Skip to content

Commit c93641e

Browse files
committed
[2024] Add Day 11: Plutonian Pebbles
1 parent 0471eb6 commit c93641e

File tree

10 files changed

+306
-0
lines changed

10 files changed

+306
-0
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const std = @import("std");
2+
const zbench = @import("zbench");
3+
const plutonian_pebbles = @import("plutonian_pebbles");
4+
5+
const puzzle_input = @embedFile("puzzle_input");
6+
7+
// Benchmark of part 1
8+
fn task_1(_: std.mem.Allocator) void {
9+
_ = plutonian_pebbles.amount_of_stones(puzzle_input, 25) catch {};
10+
}
11+
12+
// Benchmark of part 2
13+
fn task_2(_: std.mem.Allocator) void {
14+
_ = plutonian_pebbles.amount_of_stones(puzzle_input, 75) catch {};
15+
}
16+
17+
pub fn main() !void {
18+
const stdout = std.io.getStdOut().writer();
19+
var bench = zbench.Benchmark.init(std.heap.page_allocator, .{});
20+
defer bench.deinit();
21+
22+
try bench.add("Day 11 - Task 1", task_1, .{});
23+
try bench.add("Day 11 - Task 2", task_2, .{});
24+
25+
try stdout.writeAll("\n");
26+
try bench.run(stdout);
27+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
const std = @import("std");
2+
3+
pub fn build(b: *std.Build) void {
4+
const target = b.standardTargetOptions(.{});
5+
const optimize = b.standardOptimizeOption(.{});
6+
7+
// -------------------------- Solution module --------------------------- \\
8+
const plutonian_pebbles = b.addModule("plutonian_pebbles", .{
9+
.root_source_file = b.path("src/plutonian_pebbles.zig"),
10+
});
11+
12+
// -------------------------- Main executable --------------------------- \\
13+
const plutonian_pebbles_exe = b.addExecutable(.{
14+
.name = "plutonian_pebbles",
15+
.root_source_file = b.path("src/main.zig"),
16+
.target = target,
17+
.optimize = optimize,
18+
});
19+
20+
const yazap = b.dependency("yazap", .{});
21+
plutonian_pebbles_exe.root_module.addImport("yazap", yazap.module("yazap"));
22+
plutonian_pebbles_exe.root_module.addImport("plutonian_pebbles", plutonian_pebbles);
23+
b.installArtifact(plutonian_pebbles_exe);
24+
25+
const run_cmd = b.addRunArtifact(plutonian_pebbles_exe);
26+
run_cmd.step.dependOn(b.getInstallStep());
27+
if (b.args) |args| {
28+
run_cmd.addArgs(args);
29+
}
30+
31+
const run_step = b.step("run", "Run the plutonian_pebbles (day 11) app");
32+
run_step.dependOn(&run_cmd.step);
33+
34+
// --------------------------- Example tests ---------------------------- \\
35+
const plutonian_pebbles_tests = b.addTest(.{
36+
.name = "plutonian_pebbles_tests",
37+
.root_source_file = b.path("tests/example_tests.zig"),
38+
.target = target,
39+
.optimize = optimize,
40+
});
41+
42+
plutonian_pebbles_tests.root_module.addImport("plutonian_pebbles", plutonian_pebbles);
43+
plutonian_pebbles_tests.root_module.addAnonymousImport("example_input", .{
44+
.root_source_file = b.path("input/example_input.txt"),
45+
});
46+
b.installArtifact(plutonian_pebbles_tests);
47+
48+
const test_step = b.step("test", "Run plutonian_pebbles (day 11) tests");
49+
test_step.dependOn(&b.addRunArtifact(plutonian_pebbles_tests).step);
50+
51+
// ------------------------- Puzzle benchmarks -------------------------- \\
52+
const plutonian_pebbles_benchmarks = b.addExecutable(.{
53+
.name = "plutonian_pebbles_benchmarks",
54+
.root_source_file = b.path("benchmarks/puzzle_benchmarks.zig"),
55+
.target = target,
56+
.optimize = optimize,
57+
});
58+
59+
const zbench = b.dependency("zbench", .{
60+
.target = target,
61+
.optimize = optimize,
62+
});
63+
plutonian_pebbles_benchmarks.root_module.addImport("zbench", zbench.module("zbench"));
64+
plutonian_pebbles_benchmarks.root_module.addImport("plutonian_pebbles", plutonian_pebbles);
65+
plutonian_pebbles_benchmarks.root_module.addAnonymousImport("puzzle_input", .{
66+
.root_source_file = b.path("input/puzzle_input.txt"),
67+
});
68+
b.installArtifact(plutonian_pebbles_benchmarks);
69+
70+
const benchmark_step = b.step("benchmark", "Run plutonian_pebbles (day 11) benchmarks");
71+
benchmark_step.dependOn(&b.addRunArtifact(plutonian_pebbles_benchmarks).step);
72+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.{
2+
.name = "plutonian_pebbles",
3+
.version = "0.1.0",
4+
.minimum_zig_version = "0.13.0",
5+
.dependencies = .{
6+
.yazap = .{
7+
.url = "git+https://github.com/prajwalch/yazap#c2e3122d5dd6192513ba590f229dbc535110efb8",
8+
.hash = "122054439ec36ac10987c87ae69f3b041b40b2e451af3fe3ef1fc578b3bad756a800",
9+
},
10+
.zbench = .{
11+
.url = "git+https://github.com/hendriknielaender/zBench#fb3ecae5d035091fd2392a2ec21970c06fc375fa",
12+
.hash = "122095b73930ff5d627429295c669905d85bb9b54394ddc185ad2d61295cc65819e5",
13+
},
14+
},
15+
.paths = .{
16+
"build.zig",
17+
"build.zig.zon",
18+
"src",
19+
"input",
20+
"tests",
21+
"benchmarks",
22+
},
23+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
125 17
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
6571 0 5851763 526746 23 69822 9 989
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const std = @import("std");
2+
const yazap = @import("yazap");
3+
const plutonian_pebbles = @import("plutonian_pebbles");
4+
5+
const allocator = std.heap.page_allocator;
6+
const log = std.log;
7+
const App = yazap.App;
8+
const Arg = yazap.Arg;
9+
const string = []const u8;
10+
11+
pub fn main() !void {
12+
var app = App.init(allocator, "Day 11", "Day 11: Plutonian Pebbles");
13+
defer app.deinit();
14+
15+
var cmd = app.rootCommand();
16+
cmd.setProperty(.help_on_empty_args);
17+
try cmd.addArg(Arg.singleValueOption(
18+
"filename",
19+
'f',
20+
"Input file (e.g. input/puzzle_input.txt)",
21+
));
22+
try cmd.addArg(Arg.singleValueOption(
23+
"blinks",
24+
'b',
25+
"Amount of blinks",
26+
));
27+
28+
const matches = try app.parseProcess();
29+
30+
const stdout_file = std.io.getStdOut().writer();
31+
var bw = std.io.bufferedWriter(stdout_file);
32+
const stdout = bw.writer();
33+
34+
var file_content: string = undefined;
35+
if (matches.getSingleValue("filename")) |filename| {
36+
const file = try std.fs.cwd().openFile(filename, .{});
37+
defer file.close();
38+
39+
const file_size = try file.getEndPos();
40+
const buffer: []u8 = try allocator.alloc(u8, file_size);
41+
defer allocator.free(buffer);
42+
43+
_ = try file.readAll(buffer);
44+
file_content = std.mem.Allocator.dupe(
45+
allocator,
46+
u8,
47+
std.mem.trim(u8, buffer, "\n"),
48+
) catch unreachable;
49+
} else {
50+
try app.displayHelp();
51+
return;
52+
}
53+
54+
var blinks: usize = undefined;
55+
if (matches.getSingleValue("blinks")) |blinks_str| {
56+
blinks = try std.fmt.parseInt(usize, blinks_str, 10);
57+
} else {
58+
try app.displayHelp();
59+
return;
60+
}
61+
62+
const result = plutonian_pebbles.amount_of_stones(file_content, blinks);
63+
try stdout.print("Amount of stones: {!}\n", .{result});
64+
try bw.flush();
65+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
const std = @import("std");
2+
const Allocator = std.mem.Allocator;
3+
const HashMap = std.AutoArrayHashMap;
4+
const string = []const u8;
5+
6+
/// Task 1 & 2 - Simulate the given amount of blinks and return the amount of
7+
/// stones after all blinks. Use two sets of stones which are
8+
/// swapped after each blink to reduce the amount of reallocations
9+
/// of the used Hashmaps.
10+
///
11+
/// Arguments:
12+
/// - `contents`: Input file contents.
13+
/// - `blinks`: Amount of blinks.
14+
///
15+
/// Returns:
16+
/// - The amount of stones after all blinks.
17+
pub fn amount_of_stones(contents: string, blinks: usize) !u64 {
18+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
19+
defer arena.deinit();
20+
21+
const allocator = arena.allocator();
22+
var stones = try parse(contents, allocator);
23+
var new_stones = HashMap(u64, u64).init(allocator);
24+
25+
for (0..blinks) |_| {
26+
try blink(stones, &new_stones);
27+
std.mem.swap(HashMap(u64, u64), &stones, &new_stones);
28+
}
29+
30+
var stones_count: u64 = 0;
31+
for (stones.values()) |amount| {
32+
stones_count += amount;
33+
}
34+
35+
return stones_count;
36+
}
37+
38+
// -------------------------------------------------------------------------- \\
39+
40+
/// Simulate 'one blink'. For each unique stone number apply the rules and
41+
/// increment the amounts of the new stones.
42+
///
43+
/// Arguments:
44+
/// - `stones`: Current set of stone numbers and amounts.
45+
/// - `new_stones`: The new stones after the blink.
46+
fn blink(stones: HashMap(u64, u64), new_stones: *HashMap(u64, u64)) !void {
47+
new_stones.clearRetainingCapacity();
48+
for (stones.keys()) |stone| {
49+
const amount = stones.getPtr(stone).?.*;
50+
if (stone == 0) {
51+
try increment_amount(new_stones, 1, amount);
52+
continue;
53+
}
54+
const digits = std.math.log10(stone) + 1;
55+
if (digits % 2 == 0) {
56+
const power_of_10 = try std.math.powi(u64, 10, digits / 2);
57+
const right_stone = stone % power_of_10;
58+
const left_stone = (stone - right_stone) / power_of_10;
59+
60+
try increment_amount(new_stones, right_stone, amount);
61+
try increment_amount(new_stones, left_stone, amount);
62+
} else {
63+
try increment_amount(new_stones, stone * 2024, amount);
64+
}
65+
}
66+
}
67+
68+
/// Parse the file contents into a initial map of stones with their number
69+
/// mapped to the amount of the same stone.
70+
///
71+
/// Arguments:
72+
/// - `contents`: Input file contents.
73+
/// - `allocator`: Allocator for the containers.
74+
///
75+
/// Returns:
76+
/// - Map of stone numbers to stone amounts.
77+
fn parse(contents: string, allocator: Allocator) !HashMap(u64, u64) {
78+
var stones = HashMap(u64, u64).init(allocator);
79+
80+
var numbers = std.mem.tokenize(u8, contents, " ");
81+
while (numbers.next()) |num| {
82+
try increment_amount(&stones, try std.fmt.parseInt(u64, num, 10), 1);
83+
}
84+
85+
return stones;
86+
}
87+
88+
/// Increment the amount of a given stone in the map by the given amount. Create
89+
/// a new entry if the stone didn't exist before.
90+
///
91+
/// Arguments:
92+
/// - `contents`: Input file contents.
93+
/// - `allocator`: Allocator for the containers.
94+
///
95+
/// Returns:
96+
/// - Map of stone numbers to stone amounts.
97+
fn increment_amount(stones: *HashMap(u64, u64), stone: u64, increment: u64) !void {
98+
const entry = try stones.*.getOrPutValue(stone, increment);
99+
if (entry.found_existing) {
100+
entry.value_ptr.* += increment;
101+
}
102+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const std = @import("std");
2+
const plutonian_pebbles = @import("plutonian_pebbles");
3+
const testing = std.testing;
4+
5+
// Test of part 1
6+
test "task_1" {
7+
const example_input = @embedFile("example_input");
8+
try testing.expectEqual(
9+
55312,
10+
plutonian_pebbles.amount_of_stones(example_input, 25),
11+
);
12+
}

2024/build.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,5 @@ pub fn build(b: *std.Build) void {
3838
add_subproject(b, target, optimize, test_step, benchmark_step, "06", "guard_gallivant");
3939
add_subproject(b, target, optimize, test_step, benchmark_step, "09", "disk_fragmenter");
4040
add_subproject(b, target, optimize, test_step, benchmark_step, "10", "hoof_it");
41+
add_subproject(b, target, optimize, test_step, benchmark_step, "11", "plutonian_pebbles");
4142
}

2024/build.zig.zon

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
.day_06_guard_gallivant = .{ .path = "06/guard_gallivant" },
1111
.day_09_disk_fragmenter = .{ .path = "09/disk_fragmenter" },
1212
.day_10_hoof_it = .{ .path = "10/hoof_it" },
13+
.day_11_plutonian_pebbles = .{ .path = "11/plutonian_pebbles" },
1314
},
1415
.paths = .{
1516
"build.zig",
@@ -21,5 +22,6 @@
2122
"06",
2223
"09",
2324
"10",
25+
"11",
2426
},
2527
}

0 commit comments

Comments
 (0)