solve day 13
This commit is contained in:
parent
71d4c1bf17
commit
c36f8c6805
235
src/day13.zig
Normal file
235
src/day13.zig
Normal file
@ -0,0 +1,235 @@
|
||||
const std = @import("std");
|
||||
const aoc = @import("./aoc.zig");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Map = struct {
|
||||
tiles: std.ArrayList(bool),
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
||||
fn deinit(self: Map) void {
|
||||
self.tiles.deinit();
|
||||
}
|
||||
|
||||
fn print(self: Map) void {
|
||||
for (0..self.height) |y| {
|
||||
for (0..self.width) |x| {
|
||||
const idx = y * self.width + x;
|
||||
if (self.tiles.items[idx]) {
|
||||
std.debug.print("#", .{});
|
||||
} else {
|
||||
std.debug.print(".", .{});
|
||||
}
|
||||
}
|
||||
std.debug.print("\n", .{});
|
||||
}
|
||||
}
|
||||
|
||||
fn get(self: Map, x: u32, y: u32) bool {
|
||||
assert(x < self.width);
|
||||
assert(y < self.height);
|
||||
return self.tiles.items[y * self.width + x];
|
||||
}
|
||||
};
|
||||
|
||||
const Input = struct {
|
||||
maps: std.ArrayList(Map),
|
||||
|
||||
fn init(allocator: Allocator) Input {
|
||||
return Input{
|
||||
.maps = std.ArrayList(Map).init(allocator)
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: Input) void {
|
||||
for (self.maps.items) |map| {
|
||||
map.deinit();
|
||||
}
|
||||
self.maps.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
fn parse_map(allocator: Allocator, lines: []const []const u8) !Map {
|
||||
var width: u32 = @intCast(lines[0].len);
|
||||
var height: u32 = @intCast(lines.len);
|
||||
var tiles = try std.ArrayList(bool).initCapacity(allocator, width*height);
|
||||
errdefer tiles.deinit();
|
||||
|
||||
for (lines) |line| {
|
||||
for (line) |tile| {
|
||||
try tiles.append(tile == '#');
|
||||
}
|
||||
}
|
||||
|
||||
return Map{
|
||||
.tiles = tiles,
|
||||
.width = width,
|
||||
.height = height
|
||||
};
|
||||
}
|
||||
|
||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
||||
var parsed = Input.init(allocator);
|
||||
errdefer parsed.deinit();
|
||||
|
||||
var first_map_line: usize = 0;
|
||||
for (0.., lines) |i, line| {
|
||||
if (line.len == 0) {
|
||||
const map_lines = lines[first_map_line..i];
|
||||
try parsed.maps.append(try parse_map(allocator, map_lines));
|
||||
first_map_line = i+1;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_map_line != lines.len) {
|
||||
const map_lines = lines[first_map_line..];
|
||||
try parsed.maps.append(try parse_map(allocator, map_lines));
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
fn check_horizontal_reflection(map: Map, reflect_x: u32) bool {
|
||||
assert(reflect_x < map.width-1);
|
||||
const left_start = reflect_x;
|
||||
const right_start = reflect_x + 1;
|
||||
|
||||
const distance_to_edge = @min(map.width - right_start, left_start + 1);
|
||||
for (0..map.height) |y| {
|
||||
for (0..distance_to_edge) |offset| {
|
||||
const left_cursor = left_start - @as(u32, @intCast(offset));
|
||||
const right_cursor = right_start + @as(u32, @intCast(offset));
|
||||
|
||||
const left_tile = map.get(left_cursor , @intCast(y));
|
||||
const right_tile = map.get(right_cursor, @intCast(y));
|
||||
if (left_tile != right_tile) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn find_horizontal_reflection(map: Map, skip_x: ?u32) ?u32 {
|
||||
for (0..map.width-1) |x| {
|
||||
const x_u32: u32 = @intCast(x);
|
||||
if (x_u32 == skip_x) continue;
|
||||
if (check_horizontal_reflection(map, x_u32)) {
|
||||
return @intCast(x);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn check_vertical_reflection(map: Map, reflect_y: u32) bool {
|
||||
assert(reflect_y < map.height-1);
|
||||
const top_start = reflect_y;
|
||||
const bottom_start = reflect_y + 1;
|
||||
|
||||
const distance_to_edge = @min(map.height - bottom_start, top_start + 1);
|
||||
for (0..map.width) |x| {
|
||||
for (0..distance_to_edge) |offset| {
|
||||
const top_cursor = top_start - @as(u32, @intCast(offset));
|
||||
const bottom_cursor = bottom_start + @as(u32, @intCast(offset));
|
||||
|
||||
const top_tile = map.get(@intCast(x), top_cursor);
|
||||
const bottom_tile = map.get(@intCast(x), bottom_cursor);
|
||||
if (top_tile != bottom_tile) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn find_vertical_reflection(map: Map, skip_y: ?u32) ?u32 {
|
||||
for (0..map.height-1) |y| {
|
||||
const y_u32: u32 = @intCast(y);
|
||||
if (y_u32 == skip_y) continue;
|
||||
if (check_vertical_reflection(map, y_u32)) {
|
||||
return @intCast(y);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||
var parsed = try parse_input(input.allocator, input.lines);
|
||||
defer parsed.deinit();
|
||||
|
||||
var result: u64 = 0;
|
||||
for (parsed.maps.items) |map| {
|
||||
if (find_horizontal_reflection(map, null)) |x| {
|
||||
result += (x+1);
|
||||
}
|
||||
if (find_vertical_reflection(map, null)) |y| {
|
||||
result += 100 * (y+1);
|
||||
}
|
||||
}
|
||||
|
||||
return .{ .uint = result };
|
||||
}
|
||||
|
||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||
var parsed = try parse_input(input.allocator, input.lines);
|
||||
defer parsed.deinit();
|
||||
|
||||
var result: u64 = 0;
|
||||
for (parsed.maps.items) |map| {
|
||||
const original_reflect_x = find_horizontal_reflection(map, null);
|
||||
const original_reflect_y = find_vertical_reflection(map, null);
|
||||
|
||||
var reflect_x: ?u32 = null;
|
||||
var reflect_y: ?u32 = null;
|
||||
const tiles = map.tiles.items;
|
||||
for (0..tiles.len) |i| {
|
||||
tiles[i] = !tiles[i];
|
||||
reflect_x = find_horizontal_reflection(map, original_reflect_x);
|
||||
reflect_y = find_vertical_reflection(map, original_reflect_y);
|
||||
tiles[i] = !tiles[i];
|
||||
|
||||
if (reflect_x != null or reflect_y != null) break;
|
||||
}
|
||||
|
||||
assert(reflect_x != null or reflect_y != null);
|
||||
if (reflect_x) |x| {
|
||||
result += (x+1);
|
||||
}
|
||||
if (reflect_y) |y| {
|
||||
result += 100 * (y+1);
|
||||
}
|
||||
}
|
||||
|
||||
return .{ .uint = result };
|
||||
}
|
||||
|
||||
const example_input = &.{
|
||||
"#.##..##.",
|
||||
"..#.##.#.",
|
||||
"##......#",
|
||||
"##......#",
|
||||
"..#.##.#.",
|
||||
"..##..##.",
|
||||
"#.#.##.#.",
|
||||
"",
|
||||
"#...##..#",
|
||||
"#....#..#",
|
||||
"..##..###",
|
||||
"#####.##.",
|
||||
"#####.##.",
|
||||
"..##..###",
|
||||
"#....#..#",
|
||||
};
|
||||
|
||||
test "part 1 example" {
|
||||
try aoc.expectAnswerUInt(part1, 405, &example_input);
|
||||
}
|
||||
|
||||
test "part 2 example" {
|
||||
try aoc.expectAnswerUInt(part2, 400, &example_input);
|
||||
}
|
@ -33,6 +33,7 @@ const Days = [_]aoc.Day{
|
||||
create_day(@import("day10.zig")),
|
||||
create_day(@import("day11.zig")),
|
||||
create_day(@import("day12.zig")),
|
||||
create_day(@import("day13.zig")),
|
||||
};
|
||||
|
||||
fn kilobytes(count: u32) u32 {
|
||||
|
Loading…
Reference in New Issue
Block a user