solve day 14

This commit is contained in:
Rokas Puzonas 2024-04-14 21:47:42 +03:00
parent c36f8c6805
commit 31c2a661c2
4 changed files with 263 additions and 27 deletions

View File

@ -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{
"...#......",
".......#..",
"#.........",

View File

@ -208,7 +208,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
return .{ .uint = result };
}
const example_input = &.{
const example_input = [_][]const u8{
"#.##..##.",
"..#.##.#.",
"##......#",

233
src/day14.zig Normal file
View File

@ -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);
}

View File

@ -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");
}