diff --git a/day15/build.zig b/day15/build.zig new file mode 100644 index 0000000..6af6b1e --- /dev/null +++ b/day15/build.zig @@ -0,0 +1,27 @@ +const Builder = @import("std").build.Builder; + +pub fn build(b: *Builder) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard release options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. + const mode = b.standardReleaseOptions(); + + const exe = b.addExecutable("day15", "src/main.zig"); + exe.setTarget(target); + exe.setBuildMode(mode); + exe.install(); + + const run_cmd = exe.run(); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); +} diff --git a/day15/src/main.zig b/day15/src/main.zig new file mode 100644 index 0000000..5d3ad9a --- /dev/null +++ b/day15/src/main.zig @@ -0,0 +1,203 @@ +const std = @import("std"); + +pub fn main() anyerror!void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + var gpa = &arena.allocator; + + //var f = try std.fs.cwd().openFile("input", .{}); + //var contents = try f.readToEndAlloc(gpa, std.math.maxInt(u32)); + + // Starting numbers 0,8,15,2,12,1,4 + var seed = [_]u64 {0, 8, 15, 2, 12, 1, 4}; + var game = try MemoryGame.create(gpa); + defer game.deinit(); + + try game.seed_game(seed[0..]); + try game.run_until(2020); + std.log.info("Round {} spoke {}", .{game.round-1, game.last_number}); + + // Part 2 + try game.run_until(30000000); + std.log.info("Round {} spoke {}", .{game.round-1, game.last_number}); +} + +const NumberEntry = struct { + last: u64, + second_last: ?u64 = null, +}; + +const MemoryGame = struct { + map: std.hash_map.AutoHashMap(u64, NumberEntry), + allocator: *std.mem.Allocator, + round: u64 = 1, // Starting at one to match the puzzle + last_number: u64 = 0, + + pub fn create(a: *std.mem.Allocator) !*MemoryGame { + var self = try a.create(MemoryGame); + errdefer a.destroy(self); + self.* = .{ + .map = std.hash_map.AutoHashMap(u64, NumberEntry).init(a), + .allocator = a, + }; + return self; + } + + pub fn deinit(self: *MemoryGame) void { + self.map.deinit(); + self.allocator.destroy(self); + } + + // Assumes there's not repeated number in the seed + pub fn seed_game(self: *MemoryGame, seed: []u64) !void { + for (seed) |s, k| { + try self.map.put(s, NumberEntry{.last = self.round}); + self.last_number = s; + self.round += 1; + } + } + + // Returns the number spoken, but also updates everything + pub fn do_round(self: *MemoryGame) !u64 { + // Consider last number "spoken" + // Has it been said before? + var retval : u64 = 0; + if (self.map.get(self.last_number)) |v| { + //std.log.debug("Found previously spoken number {} for round {}. Previous spoken on round {}, has a previous occurence: {}\n", + // .{self.last_number, self.round, v.last, v.second_last != null}); + if ((v.last == (self.round-1)) and (v.second_last == null)) { + // The previously spoken number was new + retval = 0; + if (self.map.get(0)) |v2| { + self.map.putAssumeCapacity(0, NumberEntry {.last = self.round, .second_last = v2.last}); + } + else { + // 0 is our new number + try self.map.put(0, NumberEntry {.last = self.round}); + } + } + else { + // The previous number was already spoken, so we speak the diff + retval = v.last - v.second_last.?; + if (self.map.get(retval)) |v2| { + self.map.putAssumeCapacity(retval, NumberEntry {.last = self.round, .second_last = v2.last}); + } + else { + try self.map.put(retval, NumberEntry {.last = self.round}); + } + } + } + else { + unreachable; + } + self.last_number = retval; + self.round += 1; + return retval; + } + + pub fn run_until(self: *MemoryGame, target_round: u64) !void { + var last : u64 = 0; + while (self.round <= target_round) { + //std.log.warn("Round {} before running do_round()", .{self.round}); + last = try self.do_round(); + //std.log.warn("Round {} spoke {}", .{self.round-1, self.last_number}); + } + } +}; + +test "036_ten_turns" { + var seed = [_]u64 {0, 3, 6}; + var game = try MemoryGame.create(std.testing.allocator); + defer game.deinit(); + try game.seed_game(seed[0..]); + if (game.map.get(0)) |value| { + std.testing.expectEqual(value.last, 1); + } + if (game.map.get(3)) |value| { + std.testing.expectEqual(value.last, 2); + } + if (game.map.get(6)) |value| { + std.testing.expectEqual(value.last, 3); + } + + var response = try game.do_round(); + std.testing.expectEqual(response, 0); // Round 4 + + response = try game.do_round(); + std.testing.expectEqual(response, 3); // Round 5 + + response = try game.do_round(); + std.testing.expectEqual(response, 3); // ROund 6 + + response = try game.do_round(); + std.testing.expectEqual(response, 1); // Round 7 + + response = try game.do_round(); + std.testing.expectEqual(response, 0); // Round 8 + + response = try game.do_round(); + std.testing.expectEqual(response, 4); // Roiund 9 + + response = try game.do_round(); + std.testing.expectEqual(response, 0); // Round 10 +} + +test "132_to_2020" { + var seed = [_]u64 {1, 3, 2}; + var game = try MemoryGame.create(std.testing.allocator); + defer game.deinit(); + try game.seed_game(seed[0..]); + try game.run_until(2020); + //std.log.warn("Last number spoken was {}", .{game.last_number}); + std.testing.expectEqual(game.last_number, 1); +} + +test "213_to_2020" { + var seed = [_]u64 {2, 1, 3}; + var game = try MemoryGame.create(std.testing.allocator); + defer game.deinit(); + try game.seed_game(seed[0..]); + try game.run_until(2020); + //std.log.warn("Last number spoken was {}", .{game.last_number}); + std.testing.expectEqual(game.last_number, 10); +} + +test "123_to_2020" { + var seed = [_]u64 {1, 2, 3}; + var game = try MemoryGame.create(std.testing.allocator); + defer game.deinit(); + try game.seed_game(seed[0..]); + try game.run_until(2020); + //std.log.warn("Last number spoken was {}", .{game.last_number}); + std.testing.expectEqual(game.last_number, 27); +} + +test "231_to_2020" { + var seed = [_]u64 {2, 3, 1}; + var game = try MemoryGame.create(std.testing.allocator); + defer game.deinit(); + try game.seed_game(seed[0..]); + try game.run_until(2020); + //std.log.warn("Last number spoken was {}", .{game.last_number}); + std.testing.expectEqual(game.last_number, 78); +} + +test "321_to_2020" { + var seed = [_]u64 {3, 2, 1}; + var game = try MemoryGame.create(std.testing.allocator); + defer game.deinit(); + try game.seed_game(seed[0..]); + try game.run_until(2020); + //std.log.warn("Last number spoken was {}", .{game.last_number}); + std.testing.expectEqual(game.last_number, 438); +} + +test "312_to_2020" { + var seed = [_]u64 {3, 1, 2}; + var game = try MemoryGame.create(std.testing.allocator); + defer game.deinit(); + try game.seed_game(seed[0..]); + try game.run_until(2020); + //std.log.warn("Last number spoken was {}", .{game.last_number}); + std.testing.expectEqual(game.last_number, 1836); +}