From 31c2a661c29903495a3c3d6ee144f9d4191a6dfc Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Sun, 14 Apr 2024 21:47:42 +0300 Subject: [PATCH] solve day 14 --- src/day11.zig | 2 +- src/day13.zig | 2 +- src/day14.zig | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 53 ++++++------ 4 files changed, 263 insertions(+), 27 deletions(-) create mode 100644 src/day14.zig diff --git a/src/day11.zig b/src/day11.zig index aeaf1aa..572e35d 100644 --- a/src/day11.zig +++ b/src/day11.zig @@ -195,7 +195,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result { return .{ .uint = sum_min_distances(galaxies.items) }; } -const example_input = [_][]u8{ +const example_input = [_][]const u8{ "...#......", ".......#..", "#.........", diff --git a/src/day13.zig b/src/day13.zig index 07c5cbe..8e1657e 100644 --- a/src/day13.zig +++ b/src/day13.zig @@ -208,7 +208,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result { return .{ .uint = result }; } -const example_input = &.{ +const example_input = [_][]const u8{ "#.##..##.", "..#.##.#.", "##......#", diff --git a/src/day14.zig b/src/day14.zig new file mode 100644 index 0000000..808eec6 --- /dev/null +++ b/src/day14.zig @@ -0,0 +1,233 @@ +const std = @import("std"); +const aoc = @import("./aoc.zig"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const PointU32 = @import("./point.zig").Point(u32); + +const Tile = enum { + Empty, + Rock, + Wall +}; + +const Input = struct { + tiles: std.ArrayList(Tile), + width: u32, + height: u32, + + fn init(allocator: Allocator) Input { + return Input{ + .tiles = std.ArrayList(Tile).init(allocator), + .width = 0, + .height = 0, + }; + } + + fn deinit(self: Input) void { + self.tiles.deinit(); + } + + fn get_idx(self: Input, x: u32, y: u32) usize { + assert(x < self.width); + assert(y < self.height); + return y * self.width + x; + } + + fn get(self: Input, x: u32, y: u32) Tile { + return self.tiles.items[self.get_idx(x, y)]; + } + + fn set(self: Input, x: u32, y: u32, tile: Tile) void { + self.tiles.items[self.get_idx(x, y)] = tile; + } + + fn in_bounds(self: Input, x: i32, y: i32) bool { + return (0 <= x and x < self.width) and (0 <= y and y < self.height); + } + + fn print(self: Input) void { + for (0..self.height) |y| { + for (0..self.height) |x| { + const idx = y * self.width + x; + const tile = self.tiles.items[idx]; + const symbol: u8 = switch (tile) { + .Empty => '.', + .Wall => '#', + .Rock => 'O' + }; + + std.debug.print("{c}", .{symbol}); + } + std.debug.print("\n", .{}); + } + } +}; + +fn parse_input(allocator: Allocator, lines: []const []const u8) !Input { + var parsed = Input.init(allocator); + errdefer parsed.deinit(); + parsed.width = @intCast(lines[0].len); + parsed.height = @intCast(lines.len); + + try parsed.tiles.ensureTotalCapacity(parsed.width * parsed.height); + + for (lines) |line| { + for (line) |symbol| { + const tile = switch (symbol) { + 'O' => Tile.Rock, + '.' => Tile.Empty, + '#' => Tile.Wall, + else => return error.InvalidTile + }; + + try parsed.tiles.append(tile); + } + } + + return parsed; +} + +fn slide_rock(input: Input, x: u32, y: u32, dir_x: i32, dir_y: i32) void { + assert(input.get(x, y) == .Rock); + + var new_x = x; + var new_y = y; + while (true) { + const next_x = @as(i32, @intCast(new_x)) + dir_x; + const next_y = @as(i32, @intCast(new_y)) + dir_y; + if (!input.in_bounds(next_x, next_y)) break; + if (input.get(@intCast(next_x), @intCast(next_y)) != .Empty) break; + + new_x = @intCast(next_x); + new_y = @intCast(next_y); + } + + input.set(x, y, .Empty); + input.set(new_x, new_y, .Rock); +} + +fn slide_rocks(input: Input, dir_x: i32, dir_y: i32) void { + if (dir_y != 0) { + // Column-based + for (0..input.height) |oy| { + for (0..input.width) |ox| { + const x: u32 = @intCast(ox); + const y: u32 = @intCast(@as(i32, @intCast(oy)) * (-dir_y) + @divExact(dir_y + 1, 2) * @as(i32, @intCast(input.height-1))); + if (input.get(x, y) == .Rock) { + slide_rock(input, x, y, dir_x, dir_y); + } + } + } + } else { + // Row-based + for (0..input.width) |ox| { + for (0..input.height) |oy| { + const x: u32 = @intCast(@as(i32, @intCast(ox)) * (-dir_x) + @divExact(dir_x + 1, 2) * @as(i32, @intCast(input.width-1))); + const y: u32 = @intCast(oy); + if (input.get(x, y) == .Rock) { + slide_rock(input, x, y, dir_x, dir_y); + } + } + } + } +} + +fn cycle_slide(input: Input) void { + const dirs = &.{ + .{ .x = 0, .y = -1 }, // North + .{ .x = -1, .y = 0 }, // West + .{ .x = 0, .y = 1 }, // South + .{ .x = 1, .y = 0 }, // East + }; + + inline for (dirs) |dir| { + slide_rocks(input, dir.x, dir.y); + } +} + +fn get_total_load(input: Input) u64 { + var result: u64 = 0; + for (0..input.height) |y| { + var count: u32 = 0; + for (0..input.width) |x| { + if (input.get(@intCast(x), @intCast(y)) == .Rock) { + count += 1; + } + } + result += count * (input.height - y); + } + + return result; +} + +pub fn part1(input: *aoc.Input) !aoc.Result { + const parsed = try parse_input(input.allocator, input.lines); + defer parsed.deinit(); + + slide_rocks(parsed, 0, -1); + + return .{ .uint = get_total_load(parsed) }; +} + +pub fn part2(input: *aoc.Input) !aoc.Result { + const target_cycle_count = 1000000000; + + const allocator = input.allocator; + const parsed = try parse_input(allocator, input.lines); + defer parsed.deinit(); + + var seen = std.ArrayList(std.ArrayList(Tile)).init(allocator); + defer { + for (seen.items) |tiles| { + tiles.deinit(); + } + seen.deinit(); + } + + var cycle_size: u32 = 0; + var cycle_start: u32 = 0; + var iteration: u32 = 0; + outer: while (true) { + cycle_slide(parsed); + + for (0.., seen.items) |i, tiles| { + if (std.mem.eql(Tile, tiles.items, parsed.tiles.items)) { + cycle_start = @intCast(i); + cycle_size = @intCast(iteration - i); + break :outer; + } + } + try seen.append(try parsed.tiles.clone()); + + iteration += 1; + } + + const skipped_map = Input{ + .tiles = seen.items[@mod((target_cycle_count-1) - cycle_start, cycle_size) + cycle_start], + .width = parsed.width, + .height = parsed.height + }; + + return .{ .uint = get_total_load(skipped_map) }; +} + +const example_input = [_][]const u8{ + "O....#....", + "O.OO#....#", + ".....##...", + "OO.#O....O", + ".O.....O#.", + "O.#..O.#.#", + "..O..#O..O", + ".......O..", + "#....###..", + "#OO..#....", +}; + +test "part 1 example" { + try aoc.expectAnswerUInt(part1, 136, &example_input); +} + +test "part 2 example" { + try aoc.expectAnswerUInt(part2, 64, &example_input); +} diff --git a/src/main.zig b/src/main.zig index 0a262eb..e35e139 100644 --- a/src/main.zig +++ b/src/main.zig @@ -21,19 +21,20 @@ fn create_day(comptime module: anytype) aoc.Day { } const Days = [_]aoc.Day{ - create_day(@import("day1.zig")), - create_day(@import("day2.zig")), - create_day(@import("day3.zig")), - create_day(@import("day4.zig")), - create_day(@import("day5.zig")), - create_day(@import("day6.zig")), - create_day(@import("day7.zig")), - create_day(@import("day8.zig")), - create_day(@import("day9.zig")), - create_day(@import("day10.zig")), - create_day(@import("day11.zig")), - create_day(@import("day12.zig")), - create_day(@import("day13.zig")), + create_day(@import("./day1.zig")), + create_day(@import("./day2.zig")), + create_day(@import("./day3.zig")), + create_day(@import("./day4.zig")), + create_day(@import("./day5.zig")), + create_day(@import("./day6.zig")), + create_day(@import("./day7.zig")), + create_day(@import("./day8.zig")), + create_day(@import("./day9.zig")), + create_day(@import("./day10.zig")), + create_day(@import("./day11.zig")), + create_day(@import("./day12.zig")), + create_day(@import("./day13.zig")), + create_day(@import("./day14.zig")), }; fn kilobytes(count: u32) u32 { @@ -178,16 +179,18 @@ pub fn main() !u8 { } test { - _ = @import("day1.zig"); - _ = @import("day2.zig"); - _ = @import("day3.zig"); - _ = @import("day4.zig"); - _ = @import("day5.zig"); - _ = @import("day6.zig"); - _ = @import("day7.zig"); - _ = @import("day8.zig"); - _ = @import("day9.zig"); - _ = @import("day10.zig"); - _ = @import("day11.zig"); - _ = @import("day12.zig"); + _ = @import("./day1.zig"); + _ = @import("./day2.zig"); + _ = @import("./day3.zig"); + _ = @import("./day4.zig"); + _ = @import("./day5.zig"); + _ = @import("./day6.zig"); + _ = @import("./day7.zig"); + _ = @import("./day8.zig"); + _ = @import("./day9.zig"); + _ = @import("./day10.zig"); + _ = @import("./day11.zig"); + _ = @import("./day12.zig"); + _ = @import("./day13.zig"); + _ = @import("./day14.zig"); }