update function naming style to match zig
This commit is contained in:
parent
976a41461b
commit
b59bb9c200
@ -11,7 +11,7 @@ day: u32,
|
|||||||
part: u32,
|
part: u32,
|
||||||
input_file: []u8,
|
input_file: []u8,
|
||||||
|
|
||||||
fn print_usage(program: []const u8) void {
|
fn printUsage(program: []const u8) void {
|
||||||
std.debug.print("Usage: {s} <day> <part> [input.txt] Run solution\n", .{program});
|
std.debug.print("Usage: {s} <day> <part> [input.txt] Run solution\n", .{program});
|
||||||
std.debug.print(" {s} get-input <day> [input.txt] Get input. AOC_SESSSION environment variable needs to be defined\n", .{program});
|
std.debug.print(" {s} get-input <day> [input.txt] Get input. AOC_SESSSION environment variable needs to be defined\n", .{program});
|
||||||
}
|
}
|
||||||
@ -72,7 +72,7 @@ pub fn init(allocator: Allocator) !Self {
|
|||||||
|
|
||||||
|
|
||||||
if (i < 2) {
|
if (i < 2) {
|
||||||
print_usage(program);
|
printUsage(program);
|
||||||
return error.cli;
|
return error.cli;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,11 +45,11 @@ const Tile = union(TileType) {
|
|||||||
Start: void,
|
Start: void,
|
||||||
Pipe: Pipe,
|
Pipe: Pipe,
|
||||||
|
|
||||||
fn to_char(self: Tile) u8 {
|
fn toChar(self: Tile) u8 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
.Start => 'S',
|
.Start => 'S',
|
||||||
.Ground => '.',
|
.Ground => '.',
|
||||||
.Pipe => |dirs| find_pipe_symbol(dirs[0], dirs[1]) orelse unreachable,
|
.Pipe => |dirs| findPipeSymbol(dirs[0], dirs[1]) orelse unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -67,7 +67,7 @@ const Grid = struct {
|
|||||||
fn print(self: *Grid) void {
|
fn print(self: *Grid) void {
|
||||||
for (0..self.height) |y| {
|
for (0..self.height) |y| {
|
||||||
for (0..self.width) |x| {
|
for (0..self.width) |x| {
|
||||||
const char = self.get(@intCast(x), @intCast(y)).to_char();
|
const char = self.get(@intCast(x), @intCast(y)).toChar();
|
||||||
const symbol = switch(char) {
|
const symbol = switch(char) {
|
||||||
'.' => " ",
|
'.' => " ",
|
||||||
'S' => "S",
|
'S' => "S",
|
||||||
@ -85,28 +85,28 @@ const Grid = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_idx(self: *const Grid, x: u32, y: u32) usize {
|
fn getIndex(self: *const Grid, x: u32, y: u32) usize {
|
||||||
return y * self.width + x;
|
return y * self.width + x;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(self: *const Grid, x: u32, y: u32) Tile {
|
fn get(self: *const Grid, x: u32, y: u32) Tile {
|
||||||
return self.tiles[self.get_idx(x, y)];
|
return self.tiles[self.getIndex(x, y)];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(self: *Grid, x: u32, y: u32, tile: Tile) void {
|
fn set(self: *Grid, x: u32, y: u32, tile: Tile) void {
|
||||||
self.tiles[self.get_idx(x, y)] = tile;
|
self.tiles[self.getIndex(x, y)] = tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn in_bounds(self: *const Grid, x: i32, y: i32) bool {
|
fn inBounds(self: *const Grid, x: i32, y: i32) bool {
|
||||||
return x >= 0 and y >= 0 and x < self.width and y < self.height;
|
return x >= 0 and y >= 0 and x < self.width and y < self.height;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn does_pipe_have_direction(pipe: Pipe, dir: Direction) bool {
|
fn doesPipeHaveDirection(pipe: Pipe, dir: Direction) bool {
|
||||||
return pipe[0] == dir or pipe[1] == dir;
|
return pipe[0] == dir or pipe[1] == dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_pipe_variant(dir1: Direction, dir2: Direction) ?Pipe {
|
fn findPipeVariant(dir1: Direction, dir2: Direction) ?Pipe {
|
||||||
inline for (pipe_variants) |pipe_variant| {
|
inline for (pipe_variants) |pipe_variant| {
|
||||||
const variant_dir1 = pipe_variant[1][0];
|
const variant_dir1 = pipe_variant[1][0];
|
||||||
const variant_dir2 = pipe_variant[1][1];
|
const variant_dir2 = pipe_variant[1][1];
|
||||||
@ -117,7 +117,7 @@ fn find_pipe_variant(dir1: Direction, dir2: Direction) ?Pipe {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_pipe_symbol(dir1: Direction, dir2: Direction) ?u8 {
|
fn findPipeSymbol(dir1: Direction, dir2: Direction) ?u8 {
|
||||||
inline for (pipe_variants) |pipe_variant| {
|
inline for (pipe_variants) |pipe_variant| {
|
||||||
const variant_dir1 = pipe_variant[1][0];
|
const variant_dir1 = pipe_variant[1][0];
|
||||||
const variant_dir2 = pipe_variant[1][1];
|
const variant_dir2 = pipe_variant[1][1];
|
||||||
@ -128,7 +128,7 @@ fn find_pipe_symbol(dir1: Direction, dir2: Direction) ?u8 {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_tile(char: u8) !Tile {
|
fn parseTile(char: u8) !Tile {
|
||||||
const void_value = @as(void, undefined);
|
const void_value = @as(void, undefined);
|
||||||
|
|
||||||
if (char == 'S') {
|
if (char == 'S') {
|
||||||
@ -146,7 +146,7 @@ fn parse_tile(char: u8) !Tile {
|
|||||||
return error.UnknownTile;
|
return error.UnknownTile;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !Grid {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !Grid {
|
||||||
const height: u32 = @intCast(lines.len);
|
const height: u32 = @intCast(lines.len);
|
||||||
const width: u32 = @intCast(lines[0].len);
|
const width: u32 = @intCast(lines[0].len);
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !Grid {
|
|||||||
for (0.., lines) |y, line| {
|
for (0.., lines) |y, line| {
|
||||||
for (0.., line) |x, char| {
|
for (0.., line) |x, char| {
|
||||||
const idx = y * width + x;
|
const idx = y * width + x;
|
||||||
tiles[idx] = try parse_tile(char);
|
tiles[idx] = try parseTile(char);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !Grid {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_starting_position(grid: *const Grid) ?Point {
|
fn findStartingPosition(grid: *const Grid) ?Point {
|
||||||
for (0..grid.height) |y| {
|
for (0..grid.height) |y| {
|
||||||
for (0..grid.width) |x| {
|
for (0..grid.width) |x| {
|
||||||
const tile = grid.get(@intCast(x), @intCast(y));
|
const tile = grid.get(@intCast(x), @intCast(y));
|
||||||
@ -180,7 +180,7 @@ fn find_starting_position(grid: *const Grid) ?Point {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_neighbours(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Point, 4) {
|
fn getNeighbours(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Point, 4) {
|
||||||
const offsets = .{
|
const offsets = .{
|
||||||
.{ 0, -1 },
|
.{ 0, -1 },
|
||||||
.{ 0, 1 },
|
.{ 0, 1 },
|
||||||
@ -195,17 +195,17 @@ fn get_neighbours(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Point, 4)
|
|||||||
|
|
||||||
const nx: i32 = @intCast(@as(i32, @intCast(x)) + ox );
|
const nx: i32 = @intCast(@as(i32, @intCast(x)) + ox );
|
||||||
const ny: i32 = @intCast(@as(i32, @intCast(y)) + oy );
|
const ny: i32 = @intCast(@as(i32, @intCast(y)) + oy );
|
||||||
if (grid.in_bounds(nx, ny)) {
|
if (grid.inBounds(nx, ny)) {
|
||||||
neighbours.append(Point{ .x = @intCast(nx), .y = @intCast(ny) }) catch unreachable;
|
neighbours.append(Point{ .x = @intCast(nx), .y = @intCast(ny) }) catch unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return neighbours;
|
return neighbours;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pipe_neighbours(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Point, 4) {
|
fn getPipeNeighbours(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Point, 4) {
|
||||||
var pipe_neighbours = std.BoundedArray(Point, 4).init(0) catch unreachable;
|
var pipe_neighbours = std.BoundedArray(Point, 4).init(0) catch unreachable;
|
||||||
|
|
||||||
const neighbours = get_neighbours(grid, x, y);
|
const neighbours = getNeighbours(grid, x, y);
|
||||||
const current_tile = grid.get(x, y);
|
const current_tile = grid.get(x, y);
|
||||||
assert(@as(TileType, current_tile) == TileType.Pipe);
|
assert(@as(TileType, current_tile) == TileType.Pipe);
|
||||||
const current_pipe = current_tile.Pipe;
|
const current_pipe = current_tile.Pipe;
|
||||||
@ -227,10 +227,10 @@ fn get_pipe_neighbours(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Point
|
|||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!does_pipe_have_direction(current_pipe, direction)) continue;
|
if (!doesPipeHaveDirection(current_pipe, direction)) continue;
|
||||||
|
|
||||||
const opposite_direction = direction.opposite();
|
const opposite_direction = direction.opposite();
|
||||||
if (does_pipe_have_direction(tile.Pipe, opposite_direction)) {
|
if (doesPipeHaveDirection(tile.Pipe, opposite_direction)) {
|
||||||
pipe_neighbours.append(neighbour) catch unreachable;
|
pipe_neighbours.append(neighbour) catch unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,7 +238,7 @@ fn get_pipe_neighbours(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Point
|
|||||||
return pipe_neighbours;
|
return pipe_neighbours;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_connections(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Direction, 4) {
|
fn getConnections(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Direction, 4) {
|
||||||
const directions = .{
|
const directions = .{
|
||||||
.{ Direction.Up , 0, -1 },
|
.{ Direction.Up , 0, -1 },
|
||||||
.{ Direction.Down , 0, 1 },
|
.{ Direction.Down , 0, 1 },
|
||||||
@ -255,9 +255,9 @@ fn get_connections(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Direction
|
|||||||
|
|
||||||
const nx: i32 = @intCast(@as(i32, @intCast(x)) + ox );
|
const nx: i32 = @intCast(@as(i32, @intCast(x)) + ox );
|
||||||
const ny: i32 = @intCast(@as(i32, @intCast(y)) + oy );
|
const ny: i32 = @intCast(@as(i32, @intCast(y)) + oy );
|
||||||
if (grid.in_bounds(nx, ny)) {
|
if (grid.inBounds(nx, ny)) {
|
||||||
const tile = grid.get(@intCast(nx), @intCast(ny));
|
const tile = grid.get(@intCast(nx), @intCast(ny));
|
||||||
if (@as(TileType, tile) == TileType.Pipe and does_pipe_have_direction(tile.Pipe, opposite_direction)) {
|
if (@as(TileType, tile) == TileType.Pipe and doesPipeHaveDirection(tile.Pipe, opposite_direction)) {
|
||||||
connections.append(direction) catch unreachable;
|
connections.append(direction) catch unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,8 +265,8 @@ fn get_connections(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Direction
|
|||||||
return connections;
|
return connections;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_next_pipe(grid: *const Grid, x: u32, y: u32, current_path: []Point) ?Point {
|
fn getNextPipe(grid: *const Grid, x: u32, y: u32, current_path: []Point) ?Point {
|
||||||
var next_pipes = get_pipe_neighbours(grid, x, y);
|
var next_pipes = getPipeNeighbours(grid, x, y);
|
||||||
for (next_pipes.slice()) |point| {
|
for (next_pipes.slice()) |point| {
|
||||||
if (current_path.len > 1) {
|
if (current_path.len > 1) {
|
||||||
const first_path_point = current_path[0];
|
const first_path_point = current_path[0];
|
||||||
@ -280,7 +280,7 @@ fn get_next_pipe(grid: *const Grid, x: u32, y: u32, current_path: []Point) ?Poin
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_loop(allocator: Allocator, grid: *const Grid, start: Point) !std.ArrayList(Point) {
|
fn findLoop(allocator: Allocator, grid: *const Grid, start: Point) !std.ArrayList(Point) {
|
||||||
var loop_path = std.ArrayList(Point).init(allocator);
|
var loop_path = std.ArrayList(Point).init(allocator);
|
||||||
errdefer loop_path.deinit();
|
errdefer loop_path.deinit();
|
||||||
|
|
||||||
@ -288,7 +288,7 @@ fn find_loop(allocator: Allocator, grid: *const Grid, start: Point) !std.ArrayLi
|
|||||||
|
|
||||||
var current_point = start;
|
var current_point = start;
|
||||||
while (true) {
|
while (true) {
|
||||||
const next_point = get_next_pipe(grid, current_point.x, current_point.y, loop_path.items);
|
const next_point = getNextPipe(grid, current_point.x, current_point.y, loop_path.items);
|
||||||
if (next_point == null) break;
|
if (next_point == null) break;
|
||||||
|
|
||||||
current_point = next_point.?;
|
current_point = next_point.?;
|
||||||
@ -300,17 +300,17 @@ fn find_loop(allocator: Allocator, grid: *const Grid, start: Point) !std.ArrayLi
|
|||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
var grid = try parse_input(allocator, input.lines);
|
var grid = try parseInput(allocator, input.lines);
|
||||||
defer grid.deinit();
|
defer grid.deinit();
|
||||||
|
|
||||||
const start = find_starting_position(&grid) orelse return error.StartNotFound;
|
const start = findStartingPosition(&grid) orelse return error.StartNotFound;
|
||||||
const start_connections = get_connections(&grid, start.x, start.y);
|
const start_connections = getConnections(&grid, start.x, start.y);
|
||||||
assert(start_connections.len == 2);
|
assert(start_connections.len == 2);
|
||||||
const pipe_on_start = find_pipe_variant(start_connections.buffer[0], start_connections.buffer[1]) orelse unreachable;
|
const pipe_on_start = findPipeVariant(start_connections.buffer[0], start_connections.buffer[1]) orelse unreachable;
|
||||||
|
|
||||||
grid.set(start.x, start.y, Tile{ .Pipe = pipe_on_start });
|
grid.set(start.x, start.y, Tile{ .Pipe = pipe_on_start });
|
||||||
|
|
||||||
var loop_path = try find_loop(allocator, &grid, start);
|
var loop_path = try findLoop(allocator, &grid, start);
|
||||||
defer loop_path.deinit();
|
defer loop_path.deinit();
|
||||||
|
|
||||||
const loop_length = loop_path.items.len;
|
const loop_length = loop_path.items.len;
|
||||||
@ -320,17 +320,17 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
var grid = try parse_input(allocator, input.lines);
|
var grid = try parseInput(allocator, input.lines);
|
||||||
defer grid.deinit();
|
defer grid.deinit();
|
||||||
|
|
||||||
const start = find_starting_position(&grid) orelse return error.StartNotFound;
|
const start = findStartingPosition(&grid) orelse return error.StartNotFound;
|
||||||
const start_connections = get_connections(&grid, start.x, start.y);
|
const start_connections = getConnections(&grid, start.x, start.y);
|
||||||
assert(start_connections.len == 2);
|
assert(start_connections.len == 2);
|
||||||
const pipe_on_start = find_pipe_variant(start_connections.buffer[0], start_connections.buffer[1]) orelse unreachable;
|
const pipe_on_start = findPipeVariant(start_connections.buffer[0], start_connections.buffer[1]) orelse unreachable;
|
||||||
|
|
||||||
grid.set(start.x, start.y, Tile{ .Pipe = pipe_on_start });
|
grid.set(start.x, start.y, Tile{ .Pipe = pipe_on_start });
|
||||||
|
|
||||||
var loop_path = try find_loop(allocator, &grid, start);
|
var loop_path = try findLoop(allocator, &grid, start);
|
||||||
defer loop_path.deinit();
|
defer loop_path.deinit();
|
||||||
|
|
||||||
try loop_path.append(loop_path.items[0]);
|
try loop_path.append(loop_path.items[0]);
|
||||||
|
@ -8,14 +8,14 @@ const Tile = enum {
|
|||||||
Empty,
|
Empty,
|
||||||
Galaxy,
|
Galaxy,
|
||||||
|
|
||||||
fn to_char(self: Tile) u8 {
|
fn toChar(self: Tile) u8 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
.Empty => '.',
|
.Empty => '.',
|
||||||
.Galaxy => '#',
|
.Galaxy => '#',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_char(char: u8) ?Tile {
|
fn fromChar(char: u8) ?Tile {
|
||||||
return switch (char) {
|
return switch (char) {
|
||||||
'.' => .Empty,
|
'.' => .Empty,
|
||||||
'#' => .Galaxy,
|
'#' => .Galaxy,
|
||||||
@ -38,14 +38,14 @@ const GalaxyMap = struct {
|
|||||||
for (0..self.height) |y| {
|
for (0..self.height) |y| {
|
||||||
for (0..self.width) |x| {
|
for (0..self.width) |x| {
|
||||||
const idx = y * self.width + x;
|
const idx = y * self.width + x;
|
||||||
std.debug.print("{c}", .{self.tiles[idx].to_char()});
|
std.debug.print("{c}", .{self.tiles[idx].toChar()});
|
||||||
}
|
}
|
||||||
std.debug.print("\n", .{});
|
std.debug.print("\n", .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !GalaxyMap {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !GalaxyMap {
|
||||||
const height: u32 = @intCast(lines.len);
|
const height: u32 = @intCast(lines.len);
|
||||||
const width: u32 = @intCast(lines[0].len);
|
const width: u32 = @intCast(lines[0].len);
|
||||||
var tile_map = try allocator.alloc(Tile, width * height);
|
var tile_map = try allocator.alloc(Tile, width * height);
|
||||||
@ -54,7 +54,7 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !GalaxyMap {
|
|||||||
for (0.., lines) |y, line| {
|
for (0.., lines) |y, line| {
|
||||||
for (0.., line) |x, char| {
|
for (0.., line) |x, char| {
|
||||||
const idx = width * y + x;
|
const idx = width * y + x;
|
||||||
tile_map[idx] = Tile.from_char(char) orelse .Empty;
|
tile_map[idx] = Tile.fromChar(char) orelse .Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !GalaxyMap {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_galaxies(allocator: Allocator, map: GalaxyMap) !ArrayList(Point) {
|
fn listGalaxies(allocator: Allocator, map: GalaxyMap) !ArrayList(Point) {
|
||||||
var points = ArrayList(Point).init(allocator);
|
var points = ArrayList(Point).init(allocator);
|
||||||
errdefer points.deinit();
|
errdefer points.deinit();
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ fn list_galaxies(allocator: Allocator, map: GalaxyMap) !ArrayList(Point) {
|
|||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_row_empty(map: GalaxyMap, y: u32) bool {
|
fn isRowEmpty(map: GalaxyMap, y: u32) bool {
|
||||||
for (0..map.width) |x| {
|
for (0..map.width) |x| {
|
||||||
const idx = map.width * y + x;
|
const idx = map.width * y + x;
|
||||||
if (map.tiles[idx] != .Empty) return false;
|
if (map.tiles[idx] != .Empty) return false;
|
||||||
@ -93,7 +93,7 @@ fn is_row_empty(map: GalaxyMap, y: u32) bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_column_empty(map: GalaxyMap, x: u32) bool {
|
fn isColumnEmpty(map: GalaxyMap, x: u32) bool {
|
||||||
for (0..map.height) |y| {
|
for (0..map.height) |y| {
|
||||||
const idx = map.width * y + x;
|
const idx = map.width * y + x;
|
||||||
if (map.tiles[idx] != .Empty) return false;
|
if (map.tiles[idx] != .Empty) return false;
|
||||||
@ -101,12 +101,12 @@ fn is_column_empty(map: GalaxyMap, x: u32) bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_empty_rows(allocator: Allocator, map: GalaxyMap) !ArrayList(u32) {
|
fn listEmptyRows(allocator: Allocator, map: GalaxyMap) !ArrayList(u32) {
|
||||||
var rows = ArrayList(u32).init(allocator);
|
var rows = ArrayList(u32).init(allocator);
|
||||||
errdefer rows.deinit();
|
errdefer rows.deinit();
|
||||||
|
|
||||||
for (0..map.height) |y| {
|
for (0..map.height) |y| {
|
||||||
if (is_row_empty(map, @intCast(y))) {
|
if (isRowEmpty(map, @intCast(y))) {
|
||||||
try rows.append(@intCast(y));
|
try rows.append(@intCast(y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,12 +114,12 @@ fn list_empty_rows(allocator: Allocator, map: GalaxyMap) !ArrayList(u32) {
|
|||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_empty_columns(allocator: Allocator, map: GalaxyMap) !ArrayList(u32) {
|
fn listEmptyColumns(allocator: Allocator, map: GalaxyMap) !ArrayList(u32) {
|
||||||
var columns = ArrayList(u32).init(allocator);
|
var columns = ArrayList(u32).init(allocator);
|
||||||
errdefer columns.deinit();
|
errdefer columns.deinit();
|
||||||
|
|
||||||
for (0..map.width) |x| {
|
for (0..map.width) |x| {
|
||||||
if (is_column_empty(map, @intCast(x))) {
|
if (isColumnEmpty(map, @intCast(x))) {
|
||||||
try columns.append(@intCast(x));
|
try columns.append(@intCast(x));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,11 +127,11 @@ fn list_empty_columns(allocator: Allocator, map: GalaxyMap) !ArrayList(u32) {
|
|||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_points(allocator: Allocator, points: []Point, map: GalaxyMap, amount: u32) !void {
|
fn expandPoints(allocator: Allocator, points: []Point, map: GalaxyMap, amount: u32) !void {
|
||||||
const empty_rows = try list_empty_rows(allocator, map);
|
const empty_rows = try listEmptyRows(allocator, map);
|
||||||
defer empty_rows.deinit();
|
defer empty_rows.deinit();
|
||||||
|
|
||||||
const empty_columns = try list_empty_columns(allocator, map);
|
const empty_columns = try listEmptyColumns(allocator, map);
|
||||||
defer empty_columns.deinit();
|
defer empty_columns.deinit();
|
||||||
|
|
||||||
for (points) |*p| {
|
for (points) |*p| {
|
||||||
@ -155,7 +155,7 @@ fn expand_points(allocator: Allocator, points: []Point, map: GalaxyMap, amount:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sum_min_distances(points: []Point) u64 {
|
fn sumMinDistances(points: []Point) u64 {
|
||||||
var result: u64 = 0;
|
var result: u64 = 0;
|
||||||
for (0..(points.len-1)) |i| {
|
for (0..(points.len-1)) |i| {
|
||||||
const p1 = points[i];
|
const p1 = points[i];
|
||||||
@ -171,28 +171,28 @@ fn sum_min_distances(points: []Point) u64 {
|
|||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const parsed = try parse_input(allocator, input.lines);
|
const parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var galaxies = try list_galaxies(allocator, parsed);
|
var galaxies = try listGalaxies(allocator, parsed);
|
||||||
defer galaxies.deinit();
|
defer galaxies.deinit();
|
||||||
|
|
||||||
try expand_points(allocator, galaxies.items, parsed, 1);
|
try expandPoints(allocator, galaxies.items, parsed, 1);
|
||||||
|
|
||||||
return .{ .uint = sum_min_distances(galaxies.items) };
|
return .{ .uint = sumMinDistances(galaxies.items) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const parsed = try parse_input(allocator, input.lines);
|
const parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var galaxies = try list_galaxies(allocator, parsed);
|
var galaxies = try listGalaxies(allocator, parsed);
|
||||||
defer galaxies.deinit();
|
defer galaxies.deinit();
|
||||||
|
|
||||||
try expand_points(allocator, galaxies.items, parsed, 1000000 - 1);
|
try expandPoints(allocator, galaxies.items, parsed, 1000000 - 1);
|
||||||
|
|
||||||
return .{ .uint = sum_min_distances(galaxies.items) };
|
return .{ .uint = sumMinDistances(galaxies.items) };
|
||||||
}
|
}
|
||||||
|
|
||||||
const example_input = [_][]const u8{
|
const example_input = [_][]const u8{
|
||||||
|
@ -6,7 +6,7 @@ const ArrayList = std.ArrayList;
|
|||||||
const SpringState = enum {
|
const SpringState = enum {
|
||||||
Operational, Damaged, Unknown,
|
Operational, Damaged, Unknown,
|
||||||
|
|
||||||
fn from_char(char: u8) ?SpringState {
|
fn fromChar(char: u8) ?SpringState {
|
||||||
return switch (char) {
|
return switch (char) {
|
||||||
'.' => .Operational,
|
'.' => .Operational,
|
||||||
'#' => .Damaged,
|
'#' => .Damaged,
|
||||||
@ -15,7 +15,7 @@ const SpringState = enum {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_char(self: SpringState) u8 {
|
fn toChar(self: SpringState) u8 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
.Operational => '.',
|
.Operational => '.',
|
||||||
.Damaged => '#',
|
.Damaged => '#',
|
||||||
@ -24,9 +24,9 @@ const SpringState = enum {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn print_states(states: []const SpringState) void {
|
fn printStates(states: []const SpringState) void {
|
||||||
for (states) |state| {
|
for (states) |state| {
|
||||||
std.debug.print("{c}", .{state.to_char()});
|
std.debug.print("{c}", .{state.toChar()});
|
||||||
}
|
}
|
||||||
std.debug.print("\n", .{});
|
std.debug.print("\n", .{});
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ const InputRow = struct {
|
|||||||
|
|
||||||
fn print(self: InputRow) void {
|
fn print(self: InputRow) void {
|
||||||
for (self.states) |state| {
|
for (self.states) |state| {
|
||||||
std.debug.print("{c}", .{state.to_char()});
|
std.debug.print("{c}", .{state.toChar()});
|
||||||
}
|
}
|
||||||
std.debug.print(" ", .{});
|
std.debug.print(" ", .{});
|
||||||
for (0.., self.damaged_groups) |i, group_size| {
|
for (0.., self.damaged_groups) |i, group_size| {
|
||||||
@ -75,7 +75,7 @@ const Input = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_input_row(allocator: Allocator, line: []const u8) !InputRow {
|
fn parseInputRow(allocator: Allocator, line: []const u8) !InputRow {
|
||||||
const space_idx = std.mem.indexOfScalar(u8, line, ' ') orelse return error.InvalidFormat;
|
const space_idx = std.mem.indexOfScalar(u8, line, ' ') orelse return error.InvalidFormat;
|
||||||
var states = try allocator.alloc(SpringState, space_idx);
|
var states = try allocator.alloc(SpringState, space_idx);
|
||||||
errdefer allocator.free(states);
|
errdefer allocator.free(states);
|
||||||
@ -85,7 +85,7 @@ fn parse_input_row(allocator: Allocator, line: []const u8) !InputRow {
|
|||||||
errdefer allocator.free(damaged_groups);
|
errdefer allocator.free(damaged_groups);
|
||||||
|
|
||||||
for (0.., line[0..space_idx]) |i, char| {
|
for (0.., line[0..space_idx]) |i, char| {
|
||||||
states[i] = SpringState.from_char(char) orelse return error.InvalidFormat;
|
states[i] = SpringState.fromChar(char) orelse return error.InvalidFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
var it = std.mem.splitScalar(u8, line[(space_idx+1)..], ',');
|
var it = std.mem.splitScalar(u8, line[(space_idx+1)..], ',');
|
||||||
@ -101,18 +101,18 @@ fn parse_input_row(allocator: Allocator, line: []const u8) !InputRow {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !Input {
|
||||||
var parsed = Input.init(allocator);
|
var parsed = Input.init(allocator);
|
||||||
errdefer parsed.deinit();
|
errdefer parsed.deinit();
|
||||||
|
|
||||||
for (lines) |line| {
|
for (lines) |line| {
|
||||||
try parsed.rows.append(try parse_input_row(allocator, line));
|
try parsed.rows.append(try parseInputRow(allocator, line));
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_arrangement_valid(states: []const SpringState, groups: []const u32) bool {
|
fn isArrangementValid(states: []const SpringState, groups: []const u32) bool {
|
||||||
var it = std.mem.tokenizeScalar(SpringState, states, .Operational);
|
var it = std.mem.tokenizeScalar(SpringState, states, .Operational);
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ fn is_arrangement_valid(states: []const SpringState, groups: []const u32) bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_valid_arrangements(allocator: Allocator, states: []const SpringState, groups: []const u32) !u32 {
|
fn countValidArrangements(allocator: Allocator, states: []const SpringState, groups: []const u32) !u32 {
|
||||||
var unknown_count: u32 = 0;
|
var unknown_count: u32 = 0;
|
||||||
for (states) |state| {
|
for (states) |state| {
|
||||||
if (state == .Unknown) {
|
if (state == .Unknown) {
|
||||||
@ -138,7 +138,7 @@ fn count_valid_arrangements(allocator: Allocator, states: []const SpringState, g
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (unknown_count == 0) {
|
if (unknown_count == 0) {
|
||||||
if (is_arrangement_valid(states, groups)) {
|
if (isArrangementValid(states, groups)) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
@ -170,7 +170,7 @@ fn count_valid_arrangements(allocator: Allocator, states: []const SpringState, g
|
|||||||
arrangement[index] = if (is_damaged) .Damaged else .Operational;
|
arrangement[index] = if (is_damaged) .Damaged else .Operational;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_arrangement_valid(arrangement, groups)) {
|
if (isArrangementValid(arrangement, groups)) {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,18 +191,18 @@ fn count_valid_arrangements(allocator: Allocator, states: []const SpringState, g
|
|||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const parsed = try parse_input(allocator, input.lines);
|
const parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var result: u32 = 0;
|
var result: u32 = 0;
|
||||||
for (parsed.rows.items) |row| {
|
for (parsed.rows.items) |row| {
|
||||||
result += try count_valid_arrangements(allocator, row.states, row.damaged_groups);
|
result += try countValidArrangements(allocator, row.states, row.damaged_groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{ .uint = result };
|
return .{ .uint = result };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unfold_input_row(allocator: Allocator, input_row: InputRow) !InputRow {
|
fn unfoldInputRow(allocator: Allocator, input_row: InputRow) !InputRow {
|
||||||
const multiplier = 5;
|
const multiplier = 5;
|
||||||
|
|
||||||
const state_count = input_row.states.len;
|
const state_count = input_row.states.len;
|
||||||
@ -228,12 +228,12 @@ fn unfold_input_row(allocator: Allocator, input_row: InputRow) !InputRow {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unfold_input(allocator: Allocator, input: Input) !Input {
|
fn unfoldInput(allocator: Allocator, input: Input) !Input {
|
||||||
var unfolded = Input.init(allocator);
|
var unfolded = Input.init(allocator);
|
||||||
errdefer unfolded.deinit();
|
errdefer unfolded.deinit();
|
||||||
|
|
||||||
for (input.rows.items) |row| {
|
for (input.rows.items) |row| {
|
||||||
try unfolded.rows.append(try unfold_input_row(allocator, row));
|
try unfolded.rows.append(try unfoldInputRow(allocator, row));
|
||||||
}
|
}
|
||||||
|
|
||||||
return unfolded;
|
return unfolded;
|
||||||
@ -272,7 +272,7 @@ const CacheKeyContext = struct {
|
|||||||
};
|
};
|
||||||
const Cache = std.HashMap(CacheKey, u64, CacheKeyContext, 80);
|
const Cache = std.HashMap(CacheKey, u64, CacheKeyContext, 80);
|
||||||
|
|
||||||
fn count_valid_arrangements_dp(cache: *Cache, states: []const SpringState, groups: []const u32) !u64 {
|
fn countValidArrangementsDp(cache: *Cache, states: []const SpringState, groups: []const u32) !u64 {
|
||||||
if (states.len == 0) {
|
if (states.len == 0) {
|
||||||
if (groups.len == 0) {
|
if (groups.len == 0) {
|
||||||
// If there are no springs left AND expect no more groups,
|
// If there are no springs left AND expect no more groups,
|
||||||
@ -304,7 +304,7 @@ fn count_valid_arrangements_dp(cache: *Cache, states: []const SpringState, group
|
|||||||
|
|
||||||
var result: u64 = 0;
|
var result: u64 = 0;
|
||||||
if (states[0] == .Operational or states[0] == .Unknown) {
|
if (states[0] == .Operational or states[0] == .Unknown) {
|
||||||
result += try count_valid_arrangements_dp(cache, states[1..], groups);
|
result += try countValidArrangementsDp(cache, states[1..], groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
const group_size = groups[0];
|
const group_size = groups[0];
|
||||||
@ -315,9 +315,9 @@ fn count_valid_arrangements_dp(cache: *Cache, states: []const SpringState, group
|
|||||||
) {
|
) {
|
||||||
// If the damaged group ends with an operational spring
|
// If the damaged group ends with an operational spring
|
||||||
if (group_size == states.len) {
|
if (group_size == states.len) {
|
||||||
result += try count_valid_arrangements_dp(cache, states[group_size..], groups[1..]);
|
result += try countValidArrangementsDp(cache, states[group_size..], groups[1..]);
|
||||||
} else if (states[group_size] != .Damaged) {
|
} else if (states[group_size] != .Damaged) {
|
||||||
result += try count_valid_arrangements_dp(cache, states[(group_size+1)..], groups[1..]);
|
result += try countValidArrangementsDp(cache, states[(group_size+1)..], groups[1..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,10 +327,10 @@ fn count_valid_arrangements_dp(cache: *Cache, states: []const SpringState, group
|
|||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const parsed = try parse_input(allocator, input.lines);
|
const parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
const unfolded = try unfold_input(allocator, parsed);
|
const unfolded = try unfoldInput(allocator, parsed);
|
||||||
defer unfolded.deinit();
|
defer unfolded.deinit();
|
||||||
|
|
||||||
var cache = Cache.init(allocator);
|
var cache = Cache.init(allocator);
|
||||||
@ -338,7 +338,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
|
|
||||||
var result: u64 = 0;
|
var result: u64 = 0;
|
||||||
for (unfolded.rows.items) |row| {
|
for (unfolded.rows.items) |row| {
|
||||||
result += try count_valid_arrangements_dp(&cache, row.states, row.damaged_groups);
|
result += try countValidArrangementsDp(&cache, row.states, row.damaged_groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{ .uint = result };
|
return .{ .uint = result };
|
||||||
|
@ -50,7 +50,7 @@ const Input = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_map(allocator: Allocator, lines: []const []const u8) !Map {
|
fn parseMap(allocator: Allocator, lines: []const []const u8) !Map {
|
||||||
const width: u32 = @intCast(lines[0].len);
|
const width: u32 = @intCast(lines[0].len);
|
||||||
const height: u32 = @intCast(lines.len);
|
const height: u32 = @intCast(lines.len);
|
||||||
var tiles = try std.ArrayList(bool).initCapacity(allocator, width*height);
|
var tiles = try std.ArrayList(bool).initCapacity(allocator, width*height);
|
||||||
@ -69,7 +69,7 @@ fn parse_map(allocator: Allocator, lines: []const []const u8) !Map {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !Input {
|
||||||
var parsed = Input.init(allocator);
|
var parsed = Input.init(allocator);
|
||||||
errdefer parsed.deinit();
|
errdefer parsed.deinit();
|
||||||
|
|
||||||
@ -77,20 +77,20 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
|||||||
for (0.., lines) |i, line| {
|
for (0.., lines) |i, line| {
|
||||||
if (line.len == 0) {
|
if (line.len == 0) {
|
||||||
const map_lines = lines[first_map_line..i];
|
const map_lines = lines[first_map_line..i];
|
||||||
try parsed.maps.append(try parse_map(allocator, map_lines));
|
try parsed.maps.append(try parseMap(allocator, map_lines));
|
||||||
first_map_line = i+1;
|
first_map_line = i+1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (first_map_line != lines.len) {
|
if (first_map_line != lines.len) {
|
||||||
const map_lines = lines[first_map_line..];
|
const map_lines = lines[first_map_line..];
|
||||||
try parsed.maps.append(try parse_map(allocator, map_lines));
|
try parsed.maps.append(try parseMap(allocator, map_lines));
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_horizontal_reflection(map: Map, reflect_x: u32) bool {
|
fn checkHorizontalReflection(map: Map, reflect_x: u32) bool {
|
||||||
assert(reflect_x < map.width-1);
|
assert(reflect_x < map.width-1);
|
||||||
const left_start = reflect_x;
|
const left_start = reflect_x;
|
||||||
const right_start = reflect_x + 1;
|
const right_start = reflect_x + 1;
|
||||||
@ -112,11 +112,11 @@ fn check_horizontal_reflection(map: Map, reflect_x: u32) bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_horizontal_reflection(map: Map, skip_x: ?u32) ?u32 {
|
fn findHorizontalReflection(map: Map, skip_x: ?u32) ?u32 {
|
||||||
for (0..map.width-1) |x| {
|
for (0..map.width-1) |x| {
|
||||||
const x_u32: u32 = @intCast(x);
|
const x_u32: u32 = @intCast(x);
|
||||||
if (x_u32 == skip_x) continue;
|
if (x_u32 == skip_x) continue;
|
||||||
if (check_horizontal_reflection(map, x_u32)) {
|
if (checkHorizontalReflection(map, x_u32)) {
|
||||||
return @intCast(x);
|
return @intCast(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,7 +124,7 @@ fn find_horizontal_reflection(map: Map, skip_x: ?u32) ?u32 {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_vertical_reflection(map: Map, reflect_y: u32) bool {
|
fn checkVerticalReflection(map: Map, reflect_y: u32) bool {
|
||||||
assert(reflect_y < map.height-1);
|
assert(reflect_y < map.height-1);
|
||||||
const top_start = reflect_y;
|
const top_start = reflect_y;
|
||||||
const bottom_start = reflect_y + 1;
|
const bottom_start = reflect_y + 1;
|
||||||
@ -146,11 +146,11 @@ fn check_vertical_reflection(map: Map, reflect_y: u32) bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_vertical_reflection(map: Map, skip_y: ?u32) ?u32 {
|
fn findVerticalReflection(map: Map, skip_y: ?u32) ?u32 {
|
||||||
for (0..map.height-1) |y| {
|
for (0..map.height-1) |y| {
|
||||||
const y_u32: u32 = @intCast(y);
|
const y_u32: u32 = @intCast(y);
|
||||||
if (y_u32 == skip_y) continue;
|
if (y_u32 == skip_y) continue;
|
||||||
if (check_vertical_reflection(map, y_u32)) {
|
if (checkVerticalReflection(map, y_u32)) {
|
||||||
return @intCast(y);
|
return @intCast(y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,15 +159,15 @@ fn find_vertical_reflection(map: Map, skip_y: ?u32) ?u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
var parsed = try parse_input(input.allocator, input.lines);
|
var parsed = try parseInput(input.allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var result: u64 = 0;
|
var result: u64 = 0;
|
||||||
for (parsed.maps.items) |map| {
|
for (parsed.maps.items) |map| {
|
||||||
if (find_horizontal_reflection(map, null)) |x| {
|
if (findHorizontalReflection(map, null)) |x| {
|
||||||
result += (x+1);
|
result += (x+1);
|
||||||
}
|
}
|
||||||
if (find_vertical_reflection(map, null)) |y| {
|
if (findVerticalReflection(map, null)) |y| {
|
||||||
result += 100 * (y+1);
|
result += 100 * (y+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,21 +176,21 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
var parsed = try parse_input(input.allocator, input.lines);
|
var parsed = try parseInput(input.allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var result: u64 = 0;
|
var result: u64 = 0;
|
||||||
for (parsed.maps.items) |map| {
|
for (parsed.maps.items) |map| {
|
||||||
const original_reflect_x = find_horizontal_reflection(map, null);
|
const original_reflect_x = findHorizontalReflection(map, null);
|
||||||
const original_reflect_y = find_vertical_reflection(map, null);
|
const original_reflect_y = findVerticalReflection(map, null);
|
||||||
|
|
||||||
var reflect_x: ?u32 = null;
|
var reflect_x: ?u32 = null;
|
||||||
var reflect_y: ?u32 = null;
|
var reflect_y: ?u32 = null;
|
||||||
const tiles = map.tiles.items;
|
const tiles = map.tiles.items;
|
||||||
for (0..tiles.len) |i| {
|
for (0..tiles.len) |i| {
|
||||||
tiles[i] = !tiles[i];
|
tiles[i] = !tiles[i];
|
||||||
reflect_x = find_horizontal_reflection(map, original_reflect_x);
|
reflect_x = findHorizontalReflection(map, original_reflect_x);
|
||||||
reflect_y = find_vertical_reflection(map, original_reflect_y);
|
reflect_y = findVerticalReflection(map, original_reflect_y);
|
||||||
tiles[i] = !tiles[i];
|
tiles[i] = !tiles[i];
|
||||||
|
|
||||||
if (reflect_x != null or reflect_y != null) break;
|
if (reflect_x != null or reflect_y != null) break;
|
||||||
|
@ -26,21 +26,21 @@ const Input = struct {
|
|||||||
self.tiles.deinit();
|
self.tiles.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_idx(self: Input, x: u32, y: u32) usize {
|
fn getIndex(self: Input, x: u32, y: u32) usize {
|
||||||
assert(x < self.width);
|
assert(x < self.width);
|
||||||
assert(y < self.height);
|
assert(y < self.height);
|
||||||
return y * self.width + x;
|
return y * self.width + x;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(self: Input, x: u32, y: u32) Tile {
|
fn get(self: Input, x: u32, y: u32) Tile {
|
||||||
return self.tiles.items[self.get_idx(x, y)];
|
return self.tiles.items[self.getIndex(x, y)];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(self: Input, x: u32, y: u32, tile: Tile) void {
|
fn set(self: Input, x: u32, y: u32, tile: Tile) void {
|
||||||
self.tiles.items[self.get_idx(x, y)] = tile;
|
self.tiles.items[self.getIndex(x, y)] = tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn in_bounds(self: Input, x: i32, y: i32) bool {
|
fn inBounds(self: Input, x: i32, y: i32) bool {
|
||||||
return (0 <= x and x < self.width) and (0 <= y and y < self.height);
|
return (0 <= x and x < self.width) and (0 <= y and y < self.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ const Input = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !Input {
|
||||||
var parsed = Input.init(allocator);
|
var parsed = Input.init(allocator);
|
||||||
errdefer parsed.deinit();
|
errdefer parsed.deinit();
|
||||||
parsed.width = @intCast(lines[0].len);
|
parsed.width = @intCast(lines[0].len);
|
||||||
@ -86,7 +86,7 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
|||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slide_rock(input: Input, x: u32, y: u32, dir_x: i32, dir_y: i32) void {
|
fn slideRock(input: Input, x: u32, y: u32, dir_x: i32, dir_y: i32) void {
|
||||||
assert(input.get(x, y) == .Rock);
|
assert(input.get(x, y) == .Rock);
|
||||||
|
|
||||||
var new_x = x;
|
var new_x = x;
|
||||||
@ -94,7 +94,7 @@ fn slide_rock(input: Input, x: u32, y: u32, dir_x: i32, dir_y: i32) void {
|
|||||||
while (true) {
|
while (true) {
|
||||||
const next_x = @as(i32, @intCast(new_x)) + dir_x;
|
const next_x = @as(i32, @intCast(new_x)) + dir_x;
|
||||||
const next_y = @as(i32, @intCast(new_y)) + dir_y;
|
const next_y = @as(i32, @intCast(new_y)) + dir_y;
|
||||||
if (!input.in_bounds(next_x, next_y)) break;
|
if (!input.inBounds(next_x, next_y)) break;
|
||||||
if (input.get(@intCast(next_x), @intCast(next_y)) != .Empty) break;
|
if (input.get(@intCast(next_x), @intCast(next_y)) != .Empty) break;
|
||||||
|
|
||||||
new_x = @intCast(next_x);
|
new_x = @intCast(next_x);
|
||||||
@ -105,7 +105,7 @@ fn slide_rock(input: Input, x: u32, y: u32, dir_x: i32, dir_y: i32) void {
|
|||||||
input.set(new_x, new_y, .Rock);
|
input.set(new_x, new_y, .Rock);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slide_rocks(input: Input, dir_x: i32, dir_y: i32) void {
|
fn slideRocks(input: Input, dir_x: i32, dir_y: i32) void {
|
||||||
if (dir_y != 0) {
|
if (dir_y != 0) {
|
||||||
// Column-based
|
// Column-based
|
||||||
for (0..input.height) |oy| {
|
for (0..input.height) |oy| {
|
||||||
@ -113,7 +113,7 @@ fn slide_rocks(input: Input, dir_x: i32, dir_y: i32) void {
|
|||||||
const x: u32 = @intCast(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)));
|
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) {
|
if (input.get(x, y) == .Rock) {
|
||||||
slide_rock(input, x, y, dir_x, dir_y);
|
slideRock(input, x, y, dir_x, dir_y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,14 +124,14 @@ fn slide_rocks(input: Input, dir_x: i32, dir_y: i32) void {
|
|||||||
const x: u32 = @intCast(@as(i32, @intCast(ox)) * (-dir_x) + @divExact(dir_x + 1, 2) * @as(i32, @intCast(input.width-1)));
|
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);
|
const y: u32 = @intCast(oy);
|
||||||
if (input.get(x, y) == .Rock) {
|
if (input.get(x, y) == .Rock) {
|
||||||
slide_rock(input, x, y, dir_x, dir_y);
|
slideRock(input, x, y, dir_x, dir_y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cycle_slide(input: Input) void {
|
fn cycleSlide(input: Input) void {
|
||||||
const dirs = &.{
|
const dirs = &.{
|
||||||
.{ .x = 0, .y = -1 }, // North
|
.{ .x = 0, .y = -1 }, // North
|
||||||
.{ .x = -1, .y = 0 }, // West
|
.{ .x = -1, .y = 0 }, // West
|
||||||
@ -140,11 +140,11 @@ fn cycle_slide(input: Input) void {
|
|||||||
};
|
};
|
||||||
|
|
||||||
inline for (dirs) |dir| {
|
inline for (dirs) |dir| {
|
||||||
slide_rocks(input, dir.x, dir.y);
|
slideRocks(input, dir.x, dir.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_total_load(input: Input) u64 {
|
fn getTotalLoad(input: Input) u64 {
|
||||||
var result: u64 = 0;
|
var result: u64 = 0;
|
||||||
for (0..input.height) |y| {
|
for (0..input.height) |y| {
|
||||||
var count: u32 = 0;
|
var count: u32 = 0;
|
||||||
@ -160,19 +160,19 @@ fn get_total_load(input: Input) u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const parsed = try parse_input(input.allocator, input.lines);
|
const parsed = try parseInput(input.allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
slide_rocks(parsed, 0, -1);
|
slideRocks(parsed, 0, -1);
|
||||||
|
|
||||||
return .{ .uint = get_total_load(parsed) };
|
return .{ .uint = getTotalLoad(parsed) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const target_cycle_count = 1000000000;
|
const target_cycle_count = 1000000000;
|
||||||
|
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const parsed = try parse_input(allocator, input.lines);
|
const parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var seen = std.ArrayList(std.ArrayList(Tile)).init(allocator);
|
var seen = std.ArrayList(std.ArrayList(Tile)).init(allocator);
|
||||||
@ -187,7 +187,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
var cycle_start: u32 = 0;
|
var cycle_start: u32 = 0;
|
||||||
var iteration: u32 = 0;
|
var iteration: u32 = 0;
|
||||||
outer: while (true) {
|
outer: while (true) {
|
||||||
cycle_slide(parsed);
|
cycleSlide(parsed);
|
||||||
|
|
||||||
for (0.., seen.items) |i, tiles| {
|
for (0.., seen.items) |i, tiles| {
|
||||||
if (std.mem.eql(Tile, tiles.items, parsed.tiles.items)) {
|
if (std.mem.eql(Tile, tiles.items, parsed.tiles.items)) {
|
||||||
@ -207,7 +207,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
.height = parsed.height
|
.height = parsed.height
|
||||||
};
|
};
|
||||||
|
|
||||||
return .{ .uint = get_total_load(skipped_map) };
|
return .{ .uint = getTotalLoad(skipped_map) };
|
||||||
}
|
}
|
||||||
|
|
||||||
const example_input = [_][]const u8{
|
const example_input = [_][]const u8{
|
||||||
|
@ -40,7 +40,7 @@ const HashMap = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn put(self: *HashMap, key: []const u8, value: u8) !void {
|
fn put(self: *HashMap, key: []const u8, value: u8) !void {
|
||||||
const hash = compute_hash(key);
|
const hash = computeHash(key);
|
||||||
var box = &self.boxes[hash];
|
var box = &self.boxes[hash];
|
||||||
|
|
||||||
for (box.items) |*item| {
|
for (box.items) |*item| {
|
||||||
@ -54,7 +54,7 @@ const HashMap = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn remove(self: *HashMap, key: []const u8) void {
|
fn remove(self: *HashMap, key: []const u8) void {
|
||||||
const hash = compute_hash(key);
|
const hash = computeHash(key);
|
||||||
var box = &self.boxes[hash];
|
var box = &self.boxes[hash];
|
||||||
for (0.., box.items) |i, item| {
|
for (0.., box.items) |i, item| {
|
||||||
if (std.mem.eql(u8, item.key, key)) {
|
if (std.mem.eql(u8, item.key, key)) {
|
||||||
@ -77,7 +77,7 @@ const HashMap = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !Input {
|
||||||
var result = Input.init(allocator);
|
var result = Input.init(allocator);
|
||||||
errdefer result.deinit();
|
errdefer result.deinit();
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_hash(text: []const u8) u8 {
|
fn computeHash(text: []const u8) u8 {
|
||||||
var current: u16 = 0;
|
var current: u16 = 0;
|
||||||
for (text) |symbol| {
|
for (text) |symbol| {
|
||||||
current += symbol;
|
current += symbol;
|
||||||
@ -100,12 +100,12 @@ fn compute_hash(text: []const u8) u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const parsed = try parse_input(input.allocator, input.lines);
|
const parsed = try parseInput(input.allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var result: u64 = 0;
|
var result: u64 = 0;
|
||||||
for (parsed.parts.items) |item| {
|
for (parsed.parts.items) |item| {
|
||||||
result += compute_hash(item);
|
result += computeHash(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{ .uint = result };
|
return .{ .uint = result };
|
||||||
@ -113,7 +113,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const parsed = try parse_input(input.allocator, input.lines);
|
const parsed = try parseInput(input.allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var hash_map = HashMap.init(allocator);
|
var hash_map = HashMap.init(allocator);
|
||||||
|
@ -19,7 +19,7 @@ const Tile = enum {
|
|||||||
LeftMirror,
|
LeftMirror,
|
||||||
RightMirror,
|
RightMirror,
|
||||||
|
|
||||||
fn from_u8(symbol: u8) ?Tile {
|
fn fromU8(symbol: u8) ?Tile {
|
||||||
return switch (symbol) {
|
return switch (symbol) {
|
||||||
'.' => .Empty,
|
'.' => .Empty,
|
||||||
'\\' => .LeftMirror,
|
'\\' => .LeftMirror,
|
||||||
@ -30,7 +30,7 @@ const Tile = enum {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_u8(self: Tile) u8 {
|
fn toU8(self: Tile) u8 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
.Empty => '.',
|
.Empty => '.',
|
||||||
.LeftMirror => '\\',
|
.LeftMirror => '\\',
|
||||||
@ -59,17 +59,17 @@ const Map = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_idx(self: Map, x: u32, y: u32) usize {
|
fn getIndex(self: Map, x: u32, y: u32) usize {
|
||||||
assert(x < self.width);
|
assert(x < self.width);
|
||||||
assert(y < self.height);
|
assert(y < self.height);
|
||||||
return y * self.width + x;
|
return y * self.width + x;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(self: Map, x: u32, y: u32) Tile {
|
fn get(self: Map, x: u32, y: u32) Tile {
|
||||||
return self.tiles.items[self.get_idx(x, y)];
|
return self.tiles.items[self.getIndex(x, y)];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn in_bounds(self: Map, x: i32, y: i32) bool {
|
fn inBounds(self: Map, x: i32, y: i32) bool {
|
||||||
return (0 <= x and x < self.width) and (0 <= y and y < self.height);
|
return (0 <= x and x < self.width) and (0 <= y and y < self.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ const Map = struct {
|
|||||||
for (0..self.width) |x| {
|
for (0..self.width) |x| {
|
||||||
const tile = self.get(@intCast(x), @intCast(y));
|
const tile = self.get(@intCast(x), @intCast(y));
|
||||||
|
|
||||||
std.debug.print("{c}", .{tile.to_u8()});
|
std.debug.print("{c}", .{tile.toU8()});
|
||||||
}
|
}
|
||||||
std.debug.print("\n", .{});
|
std.debug.print("\n", .{});
|
||||||
}
|
}
|
||||||
@ -94,15 +94,15 @@ const Beam = struct {
|
|||||||
dir: PointI32,
|
dir: PointI32,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !Map {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !Map {
|
||||||
const width: u32 = @intCast(lines[0].len);
|
const width: u32 = @intCast(lines[0].len);
|
||||||
const height: u32 = @intCast(lines.len);
|
const height: u32 = @intCast(lines.len);
|
||||||
const map = try Map.init(allocator, width, height);
|
const map = try Map.init(allocator, width, height);
|
||||||
|
|
||||||
for (0.., lines) |y, line| {
|
for (0.., lines) |y, line| {
|
||||||
for (0.., line) |x, symbol| {
|
for (0.., line) |x, symbol| {
|
||||||
const tile: Tile = Tile.from_u8(symbol) orelse return error.InvalidTile;
|
const tile: Tile = Tile.fromU8(symbol) orelse return error.InvalidTile;
|
||||||
const tile_idx = map.get_idx(@intCast(x), @intCast(y));
|
const tile_idx = map.getIndex(@intCast(x), @intCast(y));
|
||||||
map.tiles.items[tile_idx] = tile;
|
map.tiles.items[tile_idx] = tile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,11 +110,11 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !Map {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cast_beam(map: Map, from: PointU32, dir: PointI32) PointU32 {
|
fn castBeam(map: Map, from: PointU32, dir: PointI32) PointU32 {
|
||||||
var current: PointI32 = from.to_i32();
|
var current: PointI32 = from.toI32();
|
||||||
while (true) {
|
while (true) {
|
||||||
const next_point = current.add(dir);
|
const next_point = current.add(dir);
|
||||||
if (!map.in_bounds(next_point.x, next_point.y)) break;
|
if (!map.inBounds(next_point.x, next_point.y)) break;
|
||||||
current = next_point;
|
current = next_point;
|
||||||
|
|
||||||
const tile = map.get(@intCast(current.x), @intCast(current.y));
|
const tile = map.get(@intCast(current.x), @intCast(current.y));
|
||||||
@ -125,10 +125,10 @@ fn cast_beam(map: Map, from: PointU32, dir: PointI32) PointU32 {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return current.to_u32();
|
return current.toU32();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contains_point(comptime T: type, list: []Point(T), point: Point(T)) bool {
|
fn containsPoint(comptime T: type, list: []Point(T), point: Point(T)) bool {
|
||||||
for (list) |other| {
|
for (list) |other| {
|
||||||
if (other.eql(point)) {
|
if (other.eql(point)) {
|
||||||
return true;
|
return true;
|
||||||
@ -137,7 +137,7 @@ fn contains_point(comptime T: type, list: []Point(T), point: Point(T)) bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_energized_tiles(energy_map: []bool) u64 {
|
fn countEnergizedTiles(energy_map: []bool) u64 {
|
||||||
var result: u64 = 0;
|
var result: u64 = 0;
|
||||||
for (energy_map) |energy| {
|
for (energy_map) |energy| {
|
||||||
result += @intFromBool(energy);
|
result += @intFromBool(energy);
|
||||||
@ -145,7 +145,7 @@ fn count_energized_tiles(energy_map: []bool) u64 {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn simulate_beam(allocator: Allocator, map: Map, from_pos: PointU32, from_dir: PointI32) ![]bool {
|
fn simulateBeam(allocator: Allocator, map: Map, from_pos: PointU32, from_dir: PointI32) ![]bool {
|
||||||
var used_splits = std.ArrayList(PointU32).init(allocator);
|
var used_splits = std.ArrayList(PointU32).init(allocator);
|
||||||
defer used_splits.deinit();
|
defer used_splits.deinit();
|
||||||
|
|
||||||
@ -163,17 +163,17 @@ fn simulate_beam(allocator: Allocator, map: Map, from_pos: PointU32, from_dir: P
|
|||||||
if (is_first and map.get(beam.pos.x, beam.pos.y) != .Empty) {
|
if (is_first and map.get(beam.pos.x, beam.pos.y) != .Empty) {
|
||||||
destination = beam.pos;
|
destination = beam.pos;
|
||||||
} else {
|
} else {
|
||||||
destination = cast_beam(map, beam.pos, beam.dir);
|
destination = castBeam(map, beam.pos, beam.dir);
|
||||||
if (destination.eql(beam.pos)) continue;
|
if (destination.eql(beam.pos)) continue;
|
||||||
}
|
}
|
||||||
is_first = false;
|
is_first = false;
|
||||||
|
|
||||||
var current: PointU32 = beam.pos;
|
var current: PointU32 = beam.pos;
|
||||||
energy_map[map.get_idx(current.x, current.y)] = true;
|
energy_map[map.getIndex(current.x, current.y)] = true;
|
||||||
while (!current.eql(destination)) {
|
while (!current.eql(destination)) {
|
||||||
current.x = @intCast(@as(i32, @intCast(current.x)) + beam.dir.x);
|
current.x = @intCast(@as(i32, @intCast(current.x)) + beam.dir.x);
|
||||||
current.y = @intCast(@as(i32, @intCast(current.y)) + beam.dir.y);
|
current.y = @intCast(@as(i32, @intCast(current.y)) + beam.dir.y);
|
||||||
energy_map[map.get_idx(current.x, current.y)] = true;
|
energy_map[map.getIndex(current.x, current.y)] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tile = map.get(destination.x, destination.y);
|
const tile = map.get(destination.x, destination.y);
|
||||||
@ -202,13 +202,13 @@ fn simulate_beam(allocator: Allocator, map: Map, from_pos: PointU32, from_dir: P
|
|||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
} else if (tile == .VerticalSplit) {
|
} else if (tile == .VerticalSplit) {
|
||||||
if (!contains_point(u32, used_splits.items, destination)) {
|
if (!containsPoint(u32, used_splits.items, destination)) {
|
||||||
try beams.append(Beam{ .pos = destination, .dir = UP });
|
try beams.append(Beam{ .pos = destination, .dir = UP });
|
||||||
try beams.append(Beam{ .pos = destination, .dir = DOWN });
|
try beams.append(Beam{ .pos = destination, .dir = DOWN });
|
||||||
try used_splits.append(destination);
|
try used_splits.append(destination);
|
||||||
}
|
}
|
||||||
} else if (tile == .HorizontalSplit) {
|
} else if (tile == .HorizontalSplit) {
|
||||||
if (!contains_point(u32, used_splits.items, destination)) {
|
if (!containsPoint(u32, used_splits.items, destination)) {
|
||||||
try beams.append(Beam{ .pos = destination, .dir = LEFT });
|
try beams.append(Beam{ .pos = destination, .dir = LEFT });
|
||||||
try beams.append(Beam{ .pos = destination, .dir = RIGHT });
|
try beams.append(Beam{ .pos = destination, .dir = RIGHT });
|
||||||
try used_splits.append(destination);
|
try used_splits.append(destination);
|
||||||
@ -219,7 +219,7 @@ fn simulate_beam(allocator: Allocator, map: Map, from_pos: PointU32, from_dir: P
|
|||||||
return energy_map;
|
return energy_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_energy_map(map: Map, energy_map: []bool) void {
|
fn printEnergyMap(map: Map, energy_map: []bool) void {
|
||||||
for (0..map.height) |y| {
|
for (0..map.height) |y| {
|
||||||
for (0..map.width) |x| {
|
for (0..map.width) |x| {
|
||||||
const idx = y * map.width + x;
|
const idx = y * map.width + x;
|
||||||
@ -235,20 +235,20 @@ fn print_energy_map(map: Map, energy_map: []bool) void {
|
|||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const map = try parse_input(input.allocator, input.lines);
|
const map = try parseInput(input.allocator, input.lines);
|
||||||
defer map.deinit();
|
defer map.deinit();
|
||||||
|
|
||||||
const energy_map = try simulate_beam(allocator, map, PointU32.zero(), RIGHT);
|
const energy_map = try simulateBeam(allocator, map, PointU32.zero(), RIGHT);
|
||||||
defer allocator.free(energy_map);
|
defer allocator.free(energy_map);
|
||||||
|
|
||||||
// print_energy_map(map, energy_map);
|
// print_energy_map(map, energy_map);
|
||||||
|
|
||||||
return .{ .uint = count_energized_tiles(energy_map) };
|
return .{ .uint = countEnergizedTiles(energy_map) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const map = try parse_input(input.allocator, input.lines);
|
const map = try parseInput(input.allocator, input.lines);
|
||||||
defer map.deinit();
|
defer map.deinit();
|
||||||
|
|
||||||
var result: u64 = 0;
|
var result: u64 = 0;
|
||||||
@ -256,32 +256,32 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
for (0..map.height) |y| {
|
for (0..map.height) |y| {
|
||||||
{
|
{
|
||||||
const from = PointU32{ .x = 0, .y = @intCast(y) };
|
const from = PointU32{ .x = 0, .y = @intCast(y) };
|
||||||
const energy_map = try simulate_beam(allocator, map, from, RIGHT);
|
const energy_map = try simulateBeam(allocator, map, from, RIGHT);
|
||||||
defer allocator.free(energy_map);
|
defer allocator.free(energy_map);
|
||||||
result = @max(result, count_energized_tiles(energy_map));
|
result = @max(result, countEnergizedTiles(energy_map));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const from = PointU32{ .x = map.width-1, .y = @intCast(y) };
|
const from = PointU32{ .x = map.width-1, .y = @intCast(y) };
|
||||||
const energy_map = try simulate_beam(allocator, map, from, LEFT);
|
const energy_map = try simulateBeam(allocator, map, from, LEFT);
|
||||||
defer allocator.free(energy_map);
|
defer allocator.free(energy_map);
|
||||||
result = @max(result, count_energized_tiles(energy_map));
|
result = @max(result, countEnergizedTiles(energy_map));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (0..map.width) |x| {
|
for (0..map.width) |x| {
|
||||||
{
|
{
|
||||||
const from = PointU32{ .x = @intCast(x), .y = 0 };
|
const from = PointU32{ .x = @intCast(x), .y = 0 };
|
||||||
const energy_map = try simulate_beam(allocator, map, from, DOWN);
|
const energy_map = try simulateBeam(allocator, map, from, DOWN);
|
||||||
defer allocator.free(energy_map);
|
defer allocator.free(energy_map);
|
||||||
result = @max(result, count_energized_tiles(energy_map));
|
result = @max(result, countEnergizedTiles(energy_map));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const from = PointU32{ .x = @intCast(x), .y = map.height-1 };
|
const from = PointU32{ .x = @intCast(x), .y = map.height-1 };
|
||||||
const energy_map = try simulate_beam(allocator, map, from, UP);
|
const energy_map = try simulateBeam(allocator, map, from, UP);
|
||||||
defer allocator.free(energy_map);
|
defer allocator.free(energy_map);
|
||||||
result = @max(result, count_energized_tiles(energy_map));
|
result = @max(result, countEnergizedTiles(energy_map));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ const Direction = enum {
|
|||||||
Left,
|
Left,
|
||||||
Right,
|
Right,
|
||||||
|
|
||||||
pub fn to_offset(self: Direction) PointI8 {
|
pub fn toOffset(self: Direction) PointI8 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
.Up => PointI8{ .x = 0, .y = -1 },
|
.Up => PointI8{ .x = 0, .y = -1 },
|
||||||
.Down => PointI8{ .x = 0, .y = 1 },
|
.Down => PointI8{ .x = 0, .y = 1 },
|
||||||
@ -53,26 +53,26 @@ const HeatMap = struct {
|
|||||||
self.tiles.deinit();
|
self.tiles.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_idx(self: HeatMap, x: u32, y: u32) usize {
|
pub fn getIndex(self: HeatMap, x: u32, y: u32) usize {
|
||||||
return self.width * y + x;
|
return self.width * y + x;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn in_bounds(self: HeatMap, x: i32, y: i32) bool {
|
pub fn inBounds(self: HeatMap, x: i32, y: i32) bool {
|
||||||
return (0 <= x and x < self.width) and (0 <= y and y < self.height);
|
return (0 <= x and x < self.width) and (0 <= y and y < self.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(self: HeatMap, x: u32, y: u32, value: u8) void {
|
pub fn set(self: HeatMap, x: u32, y: u32, value: u8) void {
|
||||||
self.tiles.items[self.get_idx(x, y)] = value;
|
self.tiles.items[self.getIndex(x, y)] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(self: HeatMap, x: u32, y: u32) u8 {
|
pub fn get(self: HeatMap, x: u32, y: u32) u8 {
|
||||||
return self.tiles.items[self.get_idx(x, y)];
|
return self.tiles.items[self.getIndex(x, y)];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print(self: HeatMap) void {
|
pub fn print(self: HeatMap) void {
|
||||||
for (0..self.height) |y| {
|
for (0..self.height) |y| {
|
||||||
for (0..self.width) |x| {
|
for (0..self.width) |x| {
|
||||||
const tile_idx = self.get_idx(@intCast(x), @intCast(y));
|
const tile_idx = self.getIndex(@intCast(x), @intCast(y));
|
||||||
const symbol = self.tiles.items[tile_idx] + '0';
|
const symbol = self.tiles.items[tile_idx] + '0';
|
||||||
std.debug.print("{c}", .{symbol});
|
std.debug.print("{c}", .{symbol});
|
||||||
}
|
}
|
||||||
@ -81,7 +81,7 @@ const HeatMap = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !HeatMap {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !HeatMap {
|
||||||
const height = lines.len;
|
const height = lines.len;
|
||||||
const width = lines[0].len;
|
const width = lines[0].len;
|
||||||
var heat_map = try HeatMap.init(allocator, @intCast(width), @intCast(height));
|
var heat_map = try HeatMap.init(allocator, @intCast(width), @intCast(height));
|
||||||
@ -111,11 +111,11 @@ fn compareQueueItem(heat_map: HeatMap, a: QueueItem, b: QueueItem) std.math.Orde
|
|||||||
return std.math.order(score_a, score_b);
|
return std.math.order(score_a, score_b);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn push_to_queue(queue: *HeatPriorityQueue, heat_map: HeatMap, state: QueueItem, dir: Direction) !void {
|
inline fn pushToQueue(queue: *HeatPriorityQueue, heat_map: HeatMap, state: QueueItem, dir: Direction) !void {
|
||||||
const dir_offset = dir.to_offset();
|
const dir_offset = dir.toOffset();
|
||||||
const neighbour_x = @as(i32, @intCast(state.pos.x)) + dir_offset.x;
|
const neighbour_x = @as(i32, @intCast(state.pos.x)) + dir_offset.x;
|
||||||
const neighbour_y = @as(i32, @intCast(state.pos.y)) + dir_offset.y;
|
const neighbour_y = @as(i32, @intCast(state.pos.y)) + dir_offset.y;
|
||||||
if (!heat_map.in_bounds(neighbour_x, neighbour_y)) return; // Skip out of bounds
|
if (!heat_map.inBounds(neighbour_x, neighbour_y)) return; // Skip out of bounds
|
||||||
|
|
||||||
var consecutive_count = state.consecutive_count;
|
var consecutive_count = state.consecutive_count;
|
||||||
if (state.dir == dir) {
|
if (state.dir == dir) {
|
||||||
@ -136,7 +136,7 @@ inline fn push_to_queue(queue: *HeatPriorityQueue, heat_map: HeatMap, state: Que
|
|||||||
try queue.add(next_state);
|
try queue.add(next_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn solve_min_heat_loss(allocator: Allocator, heat_map: HeatMap, min_steps: u32, max_steps: u32) !?u32 {
|
fn solveMinHeatLoss(allocator: Allocator, heat_map: HeatMap, min_steps: u32, max_steps: u32) !?u32 {
|
||||||
var queue = HeatPriorityQueue.init(allocator, heat_map);
|
var queue = HeatPriorityQueue.init(allocator, heat_map);
|
||||||
defer queue.deinit();
|
defer queue.deinit();
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ fn solve_min_heat_loss(allocator: Allocator, heat_map: HeatMap, min_steps: u32,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state.consecutive_count < min_steps) {
|
if (state.consecutive_count < min_steps) {
|
||||||
try push_to_queue(&queue, heat_map, state, state.dir);
|
try pushToQueue(&queue, heat_map, state, state.dir);
|
||||||
} else {
|
} else {
|
||||||
for (directions) |dir| {
|
for (directions) |dir| {
|
||||||
if (state.dir == dir and state.consecutive_count == max_steps) continue;
|
if (state.dir == dir and state.consecutive_count == max_steps) continue;
|
||||||
@ -175,7 +175,7 @@ fn solve_min_heat_loss(allocator: Allocator, heat_map: HeatMap, min_steps: u32,
|
|||||||
// Skip direction, where it would need to turn around
|
// Skip direction, where it would need to turn around
|
||||||
if (state.dir == dir.inv()) continue;
|
if (state.dir == dir.inv()) continue;
|
||||||
|
|
||||||
try push_to_queue(&queue, heat_map, state, dir);
|
try pushToQueue(&queue, heat_map, state, dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,19 +185,19 @@ fn solve_min_heat_loss(allocator: Allocator, heat_map: HeatMap, min_steps: u32,
|
|||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const heat_map = try parse_input(allocator, input.lines);
|
const heat_map = try parseInput(allocator, input.lines);
|
||||||
defer heat_map.deinit();
|
defer heat_map.deinit();
|
||||||
|
|
||||||
const answer = try solve_min_heat_loss(allocator, heat_map, 0, 3);
|
const answer = try solveMinHeatLoss(allocator, heat_map, 0, 3);
|
||||||
return .{ .uint = answer.? };
|
return .{ .uint = answer.? };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const heat_map = try parse_input(allocator, input.lines);
|
const heat_map = try parseInput(allocator, input.lines);
|
||||||
defer heat_map.deinit();
|
defer heat_map.deinit();
|
||||||
|
|
||||||
const answer = try solve_min_heat_loss(allocator, heat_map, 4, 10);
|
const answer = try solveMinHeatLoss(allocator, heat_map, 4, 10);
|
||||||
return .{ .uint = answer.? };
|
return .{ .uint = answer.? };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ const Direction = enum {
|
|||||||
Left,
|
Left,
|
||||||
Right,
|
Right,
|
||||||
|
|
||||||
fn from_str(str: []const u8) ?Direction {
|
fn fromStr(str: []const u8) ?Direction {
|
||||||
if (std.mem.eql(u8, str, "U")) {
|
if (std.mem.eql(u8, str, "U")) {
|
||||||
return .Up;
|
return .Up;
|
||||||
} else if (std.mem.eql(u8, str, "D")) {
|
} else if (std.mem.eql(u8, str, "D")) {
|
||||||
@ -31,7 +31,7 @@ const Direction = enum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_offset(self: Direction) PointI32 {
|
fn toOffset(self: Direction) PointI32 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
.Up => PointI32{ .x = 0, .y = -1 },
|
.Up => PointI32{ .x = 0, .y = -1 },
|
||||||
.Down => PointI32{ .x = 0, .y = 1 },
|
.Down => PointI32{ .x = 0, .y = 1 },
|
||||||
@ -48,30 +48,30 @@ const DigInstruction = struct {
|
|||||||
|
|
||||||
const Instructions = std.ArrayList(DigInstruction);
|
const Instructions = std.ArrayList(DigInstruction);
|
||||||
|
|
||||||
fn parse_dig_instruction1(line: []const u8) !DigInstruction {
|
fn parseDigInstruction1(line: []const u8) !DigInstruction {
|
||||||
var iter = std.mem.splitScalar(u8, line, ' ');
|
var iter = std.mem.splitScalar(u8, line, ' ');
|
||||||
|
|
||||||
const dir_str = iter.next() orelse return error.MissingDirection;
|
const dir_str = iter.next() orelse return error.MissingDirection;
|
||||||
const steps_str = iter.next() orelse return error.MissingSteps;
|
const steps_str = iter.next() orelse return error.MissingSteps;
|
||||||
|
|
||||||
return DigInstruction{
|
return DigInstruction{
|
||||||
.dir = Direction.from_str(dir_str) orelse return error.InvalidDirection,
|
.dir = Direction.fromStr(dir_str) orelse return error.InvalidDirection,
|
||||||
.steps = try std.fmt.parseUnsigned(u32, steps_str, 10)
|
.steps = try std.fmt.parseUnsigned(u32, steps_str, 10)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input1(allocator: Allocator, lines: []const []const u8) !Instructions {
|
fn parseInput1(allocator: Allocator, lines: []const []const u8) !Instructions {
|
||||||
var result = Instructions.init(allocator);
|
var result = Instructions.init(allocator);
|
||||||
errdefer result.deinit();
|
errdefer result.deinit();
|
||||||
|
|
||||||
for (lines) |line| {
|
for (lines) |line| {
|
||||||
try result.append(try parse_dig_instruction1(line));
|
try result.append(try parseDigInstruction1(line));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bounds(segments: []const Segment) std.meta.Tuple(&.{ PointI32, PointI32 }) {
|
fn getBounds(segments: []const Segment) std.meta.Tuple(&.{ PointI32, PointI32 }) {
|
||||||
var upper_left = PointI32.zero();
|
var upper_left = PointI32.zero();
|
||||||
var lower_right = PointI32.zero();
|
var lower_right = PointI32.zero();
|
||||||
|
|
||||||
@ -87,13 +87,13 @@ fn get_bounds(segments: []const Segment) std.meta.Tuple(&.{ PointI32, PointI32 }
|
|||||||
return .{ upper_left, lower_right };
|
return .{ upper_left, lower_right };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_segments(allocator: Allocator, insts: Instructions) !std.ArrayList(Segment) {
|
fn getSegments(allocator: Allocator, insts: Instructions) !std.ArrayList(Segment) {
|
||||||
var segments = std.ArrayList(Segment).init(allocator);
|
var segments = std.ArrayList(Segment).init(allocator);
|
||||||
errdefer segments.deinit();
|
errdefer segments.deinit();
|
||||||
|
|
||||||
var current = PointI32.zero();
|
var current = PointI32.zero();
|
||||||
for (insts.items) |inst| {
|
for (insts.items) |inst| {
|
||||||
const step = inst.dir.to_offset().mul(@intCast(inst.steps));
|
const step = inst.dir.toOffset().mul(@intCast(inst.steps));
|
||||||
const next = current.add(step);
|
const next = current.add(step);
|
||||||
|
|
||||||
try segments.append(Segment{ .start = current, .stop = next });
|
try segments.append(Segment{ .start = current, .stop = next });
|
||||||
@ -107,7 +107,7 @@ fn get_segments(allocator: Allocator, insts: Instructions) !std.ArrayList(Segmen
|
|||||||
// GOD DAMN IT, AGAIN WITH THIS THEOROM! I THOUGH IT WAS SCANLINES!
|
// GOD DAMN IT, AGAIN WITH THIS THEOROM! I THOUGH IT WAS SCANLINES!
|
||||||
// Shoelace theorum + Pick's theorum
|
// Shoelace theorum + Pick's theorum
|
||||||
// Day 10 all over again :/
|
// Day 10 all over again :/
|
||||||
fn get_lagoon_size(allocator: Allocator, instructions: Instructions) !usize {
|
fn getLagoonSize(allocator: Allocator, instructions: Instructions) !usize {
|
||||||
var boundry_points: u32 = 0;
|
var boundry_points: u32 = 0;
|
||||||
|
|
||||||
var loop_path = std.ArrayList(PointI32).init(allocator);
|
var loop_path = std.ArrayList(PointI32).init(allocator);
|
||||||
@ -116,7 +116,7 @@ fn get_lagoon_size(allocator: Allocator, instructions: Instructions) !usize {
|
|||||||
var current = PointI32.zero();
|
var current = PointI32.zero();
|
||||||
try loop_path.append(current);
|
try loop_path.append(current);
|
||||||
for (instructions.items) |inst| {
|
for (instructions.items) |inst| {
|
||||||
const step = inst.dir.to_offset().mul(@intCast(inst.steps));
|
const step = inst.dir.toOffset().mul(@intCast(inst.steps));
|
||||||
current = current.add(step);
|
current = current.add(step);
|
||||||
boundry_points += inst.steps;
|
boundry_points += inst.steps;
|
||||||
|
|
||||||
@ -148,13 +148,13 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
var instructions = Instructions.init(allocator);
|
var instructions = Instructions.init(allocator);
|
||||||
defer instructions.deinit();
|
defer instructions.deinit();
|
||||||
for (input.lines) |line| {
|
for (input.lines) |line| {
|
||||||
try instructions.append(try parse_dig_instruction1(line));
|
try instructions.append(try parseDigInstruction1(line));
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{ .uint = try get_lagoon_size(allocator, instructions) };
|
return .{ .uint = try getLagoonSize(allocator, instructions) };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_dig_instruction2(line: []const u8) !DigInstruction {
|
fn parseDigInstruction2(line: []const u8) !DigInstruction {
|
||||||
var iter = std.mem.splitScalar(u8, line, ' ');
|
var iter = std.mem.splitScalar(u8, line, ' ');
|
||||||
|
|
||||||
_ = iter.next();
|
_ = iter.next();
|
||||||
@ -184,10 +184,10 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
var instructions = Instructions.init(allocator);
|
var instructions = Instructions.init(allocator);
|
||||||
defer instructions.deinit();
|
defer instructions.deinit();
|
||||||
for (input.lines) |line| {
|
for (input.lines) |line| {
|
||||||
try instructions.append(try parse_dig_instruction2(line));
|
try instructions.append(try parseDigInstruction2(line));
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{ .uint = try get_lagoon_size(allocator, instructions) };
|
return .{ .uint = try getLagoonSize(allocator, instructions) };
|
||||||
}
|
}
|
||||||
|
|
||||||
const example_input = [_][]const u8{
|
const example_input = [_][]const u8{
|
||||||
|
@ -17,7 +17,7 @@ const PartProperty = enum {
|
|||||||
A,
|
A,
|
||||||
S,
|
S,
|
||||||
|
|
||||||
fn from_str(text: []const u8) ?PartProperty {
|
fn fromStr(text: []const u8) ?PartProperty {
|
||||||
if (std.mem.eql(u8, text, "x")) {
|
if (std.mem.eql(u8, text, "x")) {
|
||||||
return .X;
|
return .X;
|
||||||
} else if (std.mem.eql(u8, text, "m")) {
|
} else if (std.mem.eql(u8, text, "m")) {
|
||||||
@ -31,7 +31,7 @@ const PartProperty = enum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_str(self: PartProperty) []const u8 {
|
fn toStr(self: PartProperty) []const u8 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
.X => "x",
|
.X => "x",
|
||||||
.A => "a",
|
.A => "a",
|
||||||
@ -61,7 +61,7 @@ const PropertyRange = struct {
|
|||||||
min: u16,
|
min: u16,
|
||||||
max: u16,
|
max: u16,
|
||||||
|
|
||||||
fn is_empty(self: @This()) bool {
|
fn isEmpty(self: @This()) bool {
|
||||||
return self.min > self.max;
|
return self.min > self.max;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,8 +85,8 @@ const MachinePartRange = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_empty(self: MachinePartRange) bool {
|
fn isEmpty(self: MachinePartRange) bool {
|
||||||
return self.x.is_empty() or self.m.is_empty() or self.a.is_empty() or self.s.is_empty();
|
return self.x.isEmpty() or self.m.isEmpty() or self.a.isEmpty() or self.s.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(self: MachinePartRange) u64 {
|
fn size(self: MachinePartRange) u64 {
|
||||||
@ -115,26 +115,26 @@ const Input = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn print_rule(rule: Rule) void {
|
fn printRule(rule: Rule) void {
|
||||||
switch (rule.condition) {
|
switch (rule.condition) {
|
||||||
.Always => std.debug.print("{s}", .{rule.next.slice()}),
|
.Always => std.debug.print("{s}", .{rule.next.slice()}),
|
||||||
.Less => std.debug.print("{s}<{}:{s}", .{rule.property.to_str(), rule.value, rule.next.slice()}),
|
.Less => std.debug.print("{s}<{}:{s}", .{rule.property.toStr(), rule.value, rule.next.slice()}),
|
||||||
.More => std.debug.print("{s}>{}:{s}", .{rule.property.to_str(), rule.value, rule.next.slice()})
|
.More => std.debug.print("{s}>{}:{s}", .{rule.property.toStr(), rule.value, rule.next.slice()})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_workflow(workflow: Workflow) void {
|
fn printWorkflow(workflow: Workflow) void {
|
||||||
std.debug.print("{{", .{});
|
std.debug.print("{{", .{});
|
||||||
for (0.., workflow.items) |i, rule| {
|
for (0.., workflow.items) |i, rule| {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
std.debug.print(",", .{});
|
std.debug.print(",", .{});
|
||||||
}
|
}
|
||||||
print_rule(rule);
|
printRule(rule);
|
||||||
}
|
}
|
||||||
std.debug.print("}}\n", .{});
|
std.debug.print("}}\n", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_workflow(allocator: Allocator, line: []const u8) !std.meta.Tuple(&.{ NameString, Workflow }) {
|
fn parseWorkflow(allocator: Allocator, line: []const u8) !std.meta.Tuple(&.{ NameString, Workflow }) {
|
||||||
var parsed = Workflow.init(allocator);
|
var parsed = Workflow.init(allocator);
|
||||||
errdefer parsed.deinit();
|
errdefer parsed.deinit();
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ fn parse_workflow(allocator: Allocator, line: []const u8) !std.meta.Tuple(&.{ Na
|
|||||||
|
|
||||||
try parsed.append(Rule{
|
try parsed.append(Rule{
|
||||||
.condition = condition,
|
.condition = condition,
|
||||||
.property = PartProperty.from_str(rule_str[0..cmp_idx]) orelse return error.Invalid,
|
.property = PartProperty.fromStr(rule_str[0..cmp_idx]) orelse return error.Invalid,
|
||||||
.value = try std.fmt.parseUnsigned(u16, rule_str[(cmp_idx+1)..colon], 10),
|
.value = try std.fmt.parseUnsigned(u16, rule_str[(cmp_idx+1)..colon], 10),
|
||||||
.next = try NameString.fromSlice(rule_str[(colon+1)..])
|
.next = try NameString.fromSlice(rule_str[(colon+1)..])
|
||||||
});
|
});
|
||||||
@ -174,7 +174,7 @@ fn parse_workflow(allocator: Allocator, line: []const u8) !std.meta.Tuple(&.{ Na
|
|||||||
return .{ name, parsed };
|
return .{ name, parsed };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_machine_part(line: []const u8) !MachinePart {
|
fn parseMachinePart(line: []const u8) !MachinePart {
|
||||||
if (line[0] != '{') return error.Invalid;
|
if (line[0] != '{') return error.Invalid;
|
||||||
if (line[line.len-1] != '}') return error.Invalid;
|
if (line[line.len-1] != '}') return error.Invalid;
|
||||||
|
|
||||||
@ -203,7 +203,7 @@ fn parse_machine_part(line: []const u8) !MachinePart {
|
|||||||
return machine_part;
|
return machine_part;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !Input {
|
||||||
var parsed = Input.init(allocator);
|
var parsed = Input.init(allocator);
|
||||||
errdefer parsed.deinit();
|
errdefer parsed.deinit();
|
||||||
|
|
||||||
@ -217,18 +217,18 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
|||||||
assert(lines[section_split].len == 0);
|
assert(lines[section_split].len == 0);
|
||||||
|
|
||||||
for (lines[0..section_split]) |line| {
|
for (lines[0..section_split]) |line| {
|
||||||
const row = try parse_workflow(allocator, line);
|
const row = try parseWorkflow(allocator, line);
|
||||||
try parsed.workflows.put(row[0], row[1]);
|
try parsed.workflows.put(row[0], row[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (lines[(section_split+1)..]) |line| {
|
for (lines[(section_split+1)..]) |line| {
|
||||||
try parsed.machine_parts.append(try parse_machine_part(line));
|
try parsed.machine_parts.append(try parseMachinePart(line));
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_rule(part: MachinePart, rule: Rule) ?NameString {
|
fn evalRule(part: MachinePart, rule: Rule) ?NameString {
|
||||||
if (rule.condition == .Always) {
|
if (rule.condition == .Always) {
|
||||||
return rule.next;
|
return rule.next;
|
||||||
}
|
}
|
||||||
@ -256,9 +256,9 @@ fn eval_rule(part: MachinePart, rule: Rule) ?NameString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_workflow(part: MachinePart, workflow: Workflow) NameString {
|
fn evalWorkflow(part: MachinePart, workflow: Workflow) NameString {
|
||||||
for (workflow.items) |rule| {
|
for (workflow.items) |rule| {
|
||||||
if (eval_rule(part, rule)) |name| {
|
if (evalRule(part, rule)) |name| {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -266,10 +266,10 @@ fn eval_workflow(part: MachinePart, workflow: Workflow) NameString {
|
|||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_part_accepted(part: MachinePart, workflows: std.AutoHashMap(NameString, Workflow)) !bool {
|
fn isPartAccepted(part: MachinePart, workflows: std.AutoHashMap(NameString, Workflow)) !bool {
|
||||||
var current = workflows.get(try NameString.fromSlice("in")) orelse return error.MissingInWorkflow;
|
var current = workflows.get(try NameString.fromSlice("in")) orelse return error.MissingInWorkflow;
|
||||||
while (true) {
|
while (true) {
|
||||||
const next_name = eval_workflow(part, current);
|
const next_name = evalWorkflow(part, current);
|
||||||
const next_name_str = next_name.slice();
|
const next_name_str = next_name.slice();
|
||||||
|
|
||||||
if (std.mem.eql(u8, next_name_str, "R")) {
|
if (std.mem.eql(u8, next_name_str, "R")) {
|
||||||
@ -284,12 +284,12 @@ fn is_part_accepted(part: MachinePart, workflows: std.AutoHashMap(NameString, Wo
|
|||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
var parsed = try parse_input(allocator, input.lines);
|
var parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var result: u64 = 0;
|
var result: u64 = 0;
|
||||||
for (parsed.machine_parts.items) |part| {
|
for (parsed.machine_parts.items) |part| {
|
||||||
if (try is_part_accepted(part, parsed.workflows)) {
|
if (try isPartAccepted(part, parsed.workflows)) {
|
||||||
result += part.s;
|
result += part.s;
|
||||||
result += part.x;
|
result += part.x;
|
||||||
result += part.m;
|
result += part.m;
|
||||||
@ -302,7 +302,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
var parsed = try parse_input(allocator, input.lines);
|
var parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
const State = struct {
|
const State = struct {
|
||||||
@ -338,7 +338,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
|
|
||||||
const workflow: Workflow = parsed.workflows.get(state.name).?;
|
const workflow: Workflow = parsed.workflows.get(state.name).?;
|
||||||
for (workflow.items) |rule| {
|
for (workflow.items) |rule| {
|
||||||
if (current.is_empty()) break;
|
if (current.isEmpty()) break;
|
||||||
|
|
||||||
if (rule.condition == .Always) {
|
if (rule.condition == .Always) {
|
||||||
try stack.append(State{
|
try stack.append(State{
|
||||||
|
14
src/day2.zig
14
src/day2.zig
@ -9,7 +9,7 @@ const Game = struct {
|
|||||||
set_count: u32,
|
set_count: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_cube(cube_name: []const u8) ?Cube {
|
fn parseCube(cube_name: []const u8) ?Cube {
|
||||||
if (std.mem.eql(u8, cube_name, "blue")) {
|
if (std.mem.eql(u8, cube_name, "blue")) {
|
||||||
return .Blue;
|
return .Blue;
|
||||||
} else if (std.mem.eql(u8, cube_name, "red")) {
|
} else if (std.mem.eql(u8, cube_name, "red")) {
|
||||||
@ -20,7 +20,7 @@ fn parse_cube(cube_name: []const u8) ?Cube {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_game(game: *Game, line: []const u8) void {
|
fn parseGame(game: *Game, line: []const u8) void {
|
||||||
const colon = std.mem.indexOf(u8, line, ":") orelse @panic("Invalid line, missing ':'");
|
const colon = std.mem.indexOf(u8, line, ":") orelse @panic("Invalid line, missing ':'");
|
||||||
|
|
||||||
game.set_count = 0;
|
game.set_count = 0;
|
||||||
@ -35,17 +35,17 @@ fn parse_game(game: *Game, line: []const u8) void {
|
|||||||
while (cubes.next()) |cube_count| {
|
while (cubes.next()) |cube_count| {
|
||||||
const space = std.mem.indexOf(u8, cube_count, " ") orelse @panic("Invalid format, expected space");
|
const space = std.mem.indexOf(u8, cube_count, " ") orelse @panic("Invalid format, expected space");
|
||||||
|
|
||||||
const cube = parse_cube(cube_count[(space+1)..]) orelse @panic("Invalid cube name");
|
const cube = parseCube(cube_count[(space+1)..]) orelse @panic("Invalid cube name");
|
||||||
const count = std.fmt.parseInt(u32, cube_count[0..space], 10) catch @panic("Invalid format");
|
const count = std.fmt.parseInt(u32, cube_count[0..space], 10) catch @panic("Invalid format");
|
||||||
cube_set.set(cube, count);
|
cube_set.set(cube, count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_games(allocator: Allocator, lines: []const []const u8) ![]Game {
|
fn parseGames(allocator: Allocator, lines: []const []const u8) ![]Game {
|
||||||
var games = try allocator.alloc(Game, lines.len);
|
var games = try allocator.alloc(Game, lines.len);
|
||||||
for (0.., lines) |i, line| {
|
for (0.., lines) |i, line| {
|
||||||
parse_game(&games[i], line);
|
parseGame(&games[i], line);
|
||||||
}
|
}
|
||||||
return games;
|
return games;
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
const lines = input.lines;
|
const lines = input.lines;
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
|
|
||||||
const games = try parse_games(allocator, lines);
|
const games = try parseGames(allocator, lines);
|
||||||
defer allocator.free(games);
|
defer allocator.free(games);
|
||||||
|
|
||||||
const max_red = 12;
|
const max_red = 12;
|
||||||
@ -81,7 +81,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const lines = input.lines;
|
const lines = input.lines;
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const games = try parse_games(allocator, lines);
|
const games = try parseGames(allocator, lines);
|
||||||
defer allocator.free(games);
|
defer allocator.free(games);
|
||||||
|
|
||||||
var sum: u32 = 0;
|
var sum: u32 = 0;
|
||||||
|
@ -66,12 +66,12 @@ const InternalModule = struct {
|
|||||||
inputs: []ModuleId,
|
inputs: []ModuleId,
|
||||||
outputs: []ModuleId,
|
outputs: []ModuleId,
|
||||||
|
|
||||||
fn flip_flop(self: *@This()) bool {
|
fn flipFlop(self: *@This()) bool {
|
||||||
self.state.mask = ~self.state.mask;
|
self.state.mask = ~self.state.mask;
|
||||||
return self.state.mask != 0;
|
return self.state.mask != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_input(self: *@This(), module_index: ModuleId, is_high: bool) bool {
|
fn updateInput(self: *@This(), module_index: ModuleId, is_high: bool) bool {
|
||||||
const index = std.mem.indexOfScalar(ModuleId, self.inputs, module_index);
|
const index = std.mem.indexOfScalar(ModuleId, self.inputs, module_index);
|
||||||
assert(index != null);
|
assert(index != null);
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ const InternalModule = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn append_if_new(names: *std.ArrayList(NameString), name: NameString) !void {
|
fn appendIfNew(names: *std.ArrayList(NameString), name: NameString) !void {
|
||||||
for (names.items) |other_name| {
|
for (names.items) |other_name| {
|
||||||
if (std.mem.eql(u8, other_name.slice(), name.slice())) {
|
if (std.mem.eql(u8, other_name.slice(), name.slice())) {
|
||||||
return;
|
return;
|
||||||
@ -101,9 +101,9 @@ const MachineState = struct {
|
|||||||
var names = std.ArrayList(NameString).init(allocator);
|
var names = std.ArrayList(NameString).init(allocator);
|
||||||
defer names.deinit();
|
defer names.deinit();
|
||||||
for (modules) |module| {
|
for (modules) |module| {
|
||||||
try append_if_new(&names, module.name);
|
try appendIfNew(&names, module.name);
|
||||||
for (module.outputs.items) |output| {
|
for (module.outputs.items) |output| {
|
||||||
try append_if_new(&names, output);
|
try appendIfNew(&names, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,13 +122,13 @@ const MachineState = struct {
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (modules) |module| {
|
for (modules) |module| {
|
||||||
const module_idx = self.get_name_idx(module.name).?;
|
const module_idx = self.getNameIndex(module.name).?;
|
||||||
var internal_module = &self.state[module_idx];
|
var internal_module = &self.state[module_idx];
|
||||||
|
|
||||||
internal_module.module_type = module.module_type;
|
internal_module.module_type = module.module_type;
|
||||||
internal_module.outputs = try allocator.alloc(ModuleId, module.outputs.items.len);
|
internal_module.outputs = try allocator.alloc(ModuleId, module.outputs.items.len);
|
||||||
for (0.., module.outputs.items) |output_idx, output_name| {
|
for (0.., module.outputs.items) |output_idx, output_name| {
|
||||||
const index = self.get_name_idx(output_name);
|
const index = self.getNameIndex(output_name);
|
||||||
assert(index != null);
|
assert(index != null);
|
||||||
internal_module.outputs[output_idx] = @intCast(index.?);
|
internal_module.outputs[output_idx] = @intCast(index.?);
|
||||||
}
|
}
|
||||||
@ -171,7 +171,7 @@ const MachineState = struct {
|
|||||||
self.allocator.free(self.names);
|
self.allocator.free(self.names);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_name_idx(self: MachineState, name: NameString) ?ModuleId {
|
fn getNameIndex(self: MachineState, name: NameString) ?ModuleId {
|
||||||
for (0.., self.names) |i, other_name| {
|
for (0.., self.names) |i, other_name| {
|
||||||
if (std.mem.eql(u8, other_name.slice(), name.slice())) {
|
if (std.mem.eql(u8, other_name.slice(), name.slice())) {
|
||||||
return @intCast(i);
|
return @intCast(i);
|
||||||
@ -180,7 +180,7 @@ const MachineState = struct {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_name(self: MachineState, id: ModuleId) []const u8 {
|
fn getName(self: MachineState, id: ModuleId) []const u8 {
|
||||||
return self.names[id].constSlice();
|
return self.names[id].constSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ const MachineState = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_module(allocator: Allocator, line: []const u8) !Module {
|
fn parseModule(allocator: Allocator, line: []const u8) !Module {
|
||||||
var arrow_split_iter = std.mem.splitSequence(u8, line, "->");
|
var arrow_split_iter = std.mem.splitSequence(u8, line, "->");
|
||||||
|
|
||||||
const name_with_type = arrow_split_iter.next() orelse return error.NoName;
|
const name_with_type = arrow_split_iter.next() orelse return error.NoName;
|
||||||
@ -222,12 +222,12 @@ fn parse_module(allocator: Allocator, line: []const u8) !Module {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !Input {
|
||||||
var parsed = Input.init(allocator);
|
var parsed = Input.init(allocator);
|
||||||
errdefer parsed.deinit();
|
errdefer parsed.deinit();
|
||||||
|
|
||||||
for (lines) |line| {
|
for (lines) |line| {
|
||||||
try parsed.modules.append(try parse_module(allocator, line));
|
try parsed.modules.append(try parseModule(allocator, line));
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
@ -243,7 +243,7 @@ const PulseCount = struct {
|
|||||||
high: u64
|
high: u64
|
||||||
};
|
};
|
||||||
|
|
||||||
fn simulate_pulse(pulse_queue: *std.ArrayList(Pulse), machine_state: *MachineState, pulse: Pulse) !void {
|
fn simulatePulse(pulse_queue: *std.ArrayList(Pulse), machine_state: *MachineState, pulse: Pulse) !void {
|
||||||
var to_module = &machine_state.state[pulse.to];
|
var to_module = &machine_state.state[pulse.to];
|
||||||
|
|
||||||
var output_is_high = false;
|
var output_is_high = false;
|
||||||
@ -251,9 +251,9 @@ fn simulate_pulse(pulse_queue: *std.ArrayList(Pulse), machine_state: *MachineSta
|
|||||||
output_is_high = pulse.is_high;
|
output_is_high = pulse.is_high;
|
||||||
} else if (to_module.module_type == .FlipFlop) {
|
} else if (to_module.module_type == .FlipFlop) {
|
||||||
if (pulse.is_high) return;
|
if (pulse.is_high) return;
|
||||||
output_is_high = to_module.flip_flop();
|
output_is_high = to_module.flipFlop();
|
||||||
} else if (to_module.module_type == .Conjunction) {
|
} else if (to_module.module_type == .Conjunction) {
|
||||||
output_is_high = to_module.update_input(pulse.from, pulse.is_high);
|
output_is_high = to_module.updateInput(pulse.from, pulse.is_high);
|
||||||
} else {
|
} else {
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
@ -267,7 +267,7 @@ fn simulate_pulse(pulse_queue: *std.ArrayList(Pulse), machine_state: *MachineSta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn simulate_button_press(allocator: Allocator, machine_state: *MachineState, broadcaster: ModuleId) !PulseCount {
|
fn simulateButtonPress(allocator: Allocator, machine_state: *MachineState, broadcaster: ModuleId) !PulseCount {
|
||||||
var pulses = std.ArrayList(Pulse).init(allocator);
|
var pulses = std.ArrayList(Pulse).init(allocator);
|
||||||
defer pulses.deinit();
|
defer pulses.deinit();
|
||||||
|
|
||||||
@ -289,7 +289,7 @@ fn simulate_button_press(allocator: Allocator, machine_state: *MachineState, bro
|
|||||||
low_pulse_count += 1;
|
low_pulse_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
try simulate_pulse(&pulses, machine_state, pulse);
|
try simulatePulse(&pulses, machine_state, pulse);
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{ .low = low_pulse_count, .high = high_pulse_count };
|
return .{ .low = low_pulse_count, .high = high_pulse_count };
|
||||||
@ -297,18 +297,18 @@ fn simulate_button_press(allocator: Allocator, machine_state: *MachineState, bro
|
|||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const parsed = try parse_input(allocator, input.lines);
|
const parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var machine_state = try MachineState.init(allocator, parsed.modules.items);
|
var machine_state = try MachineState.init(allocator, parsed.modules.items);
|
||||||
defer machine_state.deinit();
|
defer machine_state.deinit();
|
||||||
|
|
||||||
const broadcaster_idx = machine_state.get_name_idx(try NameString.fromSlice("broadcaster")).?;
|
const broadcaster_idx = machine_state.getNameIndex(try NameString.fromSlice("broadcaster")).?;
|
||||||
|
|
||||||
var low_count: u64 = 0;
|
var low_count: u64 = 0;
|
||||||
var high_count: u64 = 0;
|
var high_count: u64 = 0;
|
||||||
for (0..1000) |_| {
|
for (0..1000) |_| {
|
||||||
const high_low_count = try simulate_button_press(allocator, &machine_state, broadcaster_idx);
|
const high_low_count = try simulateButtonPress(allocator, &machine_state, broadcaster_idx);
|
||||||
low_count += high_low_count.low;
|
low_count += high_low_count.low;
|
||||||
high_count += high_low_count.high;
|
high_count += high_low_count.high;
|
||||||
}
|
}
|
||||||
@ -337,7 +337,7 @@ const MachineAnalysis = struct {
|
|||||||
|
|
||||||
const ModuleIdHashSet = std.AutoHashMap(ModuleId, void);
|
const ModuleIdHashSet = std.AutoHashMap(ModuleId, void);
|
||||||
|
|
||||||
fn find_coverage(allocator: Allocator, machine_state: MachineState, from: ModuleId) !ModuleIdHashSet {
|
fn findCoverage(allocator: Allocator, machine_state: MachineState, from: ModuleId) !ModuleIdHashSet {
|
||||||
var seen = ModuleIdHashSet.init(allocator);
|
var seen = ModuleIdHashSet.init(allocator);
|
||||||
errdefer seen.deinit();
|
errdefer seen.deinit();
|
||||||
|
|
||||||
@ -359,7 +359,7 @@ fn find_coverage(allocator: Allocator, machine_state: MachineState, from: Module
|
|||||||
return seen;
|
return seen;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_machine(allocator: Allocator, machine_state: MachineState, input_module: ModuleId, output_module: ModuleId) !MachineAnalysis {
|
fn analyzeMachine(allocator: Allocator, machine_state: MachineState, input_module: ModuleId, output_module: ModuleId) !MachineAnalysis {
|
||||||
const counters = try allocator.alloc(MachineCounter, machine_state.state[input_module].outputs.len);
|
const counters = try allocator.alloc(MachineCounter, machine_state.state[input_module].outputs.len);
|
||||||
errdefer allocator.free(counters);
|
errdefer allocator.free(counters);
|
||||||
|
|
||||||
@ -368,7 +368,7 @@ fn analyze_machine(allocator: Allocator, machine_state: MachineState, input_modu
|
|||||||
@memset(coverage_counts, 0);
|
@memset(coverage_counts, 0);
|
||||||
|
|
||||||
for (0.., machine_state.state[input_module].outputs) |i, counter_input| {
|
for (0.., machine_state.state[input_module].outputs) |i, counter_input| {
|
||||||
var coverage = try find_coverage(allocator, machine_state, counter_input);
|
var coverage = try findCoverage(allocator, machine_state, counter_input);
|
||||||
defer coverage.deinit();
|
defer coverage.deinit();
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -441,7 +441,7 @@ fn analyze_machine(allocator: Allocator, machine_state: MachineState, input_modu
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_counter_cycle_size(allocator: Allocator, machine_state: *MachineState, counter: MachineCounter, input_module: ModuleId) !u64 {
|
fn findCounterCycleSize(allocator: Allocator, machine_state: *MachineState, counter: MachineCounter, input_module: ModuleId) !u64 {
|
||||||
machine_state.reset();
|
machine_state.reset();
|
||||||
|
|
||||||
var pulses = std.ArrayList(Pulse).init(allocator);
|
var pulses = std.ArrayList(Pulse).init(allocator);
|
||||||
@ -463,7 +463,7 @@ fn find_counter_cycle_size(allocator: Allocator, machine_state: *MachineState, c
|
|||||||
if (pulse.to == counter.output) {
|
if (pulse.to == counter.output) {
|
||||||
if (!pulse.is_high) break :pulse_loop;
|
if (!pulse.is_high) break :pulse_loop;
|
||||||
} else {
|
} else {
|
||||||
try simulate_pulse(&pulses, machine_state, pulse);
|
try simulatePulse(&pulses, machine_state, pulse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -477,21 +477,21 @@ fn lcm(a: u64, b: u64) u64 {
|
|||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const parsed = try parse_input(allocator, input.lines);
|
const parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var machine_state = try MachineState.init(allocator, parsed.modules.items);
|
var machine_state = try MachineState.init(allocator, parsed.modules.items);
|
||||||
defer machine_state.deinit();
|
defer machine_state.deinit();
|
||||||
|
|
||||||
const broadcaster_idx = machine_state.get_name_idx(try NameString.fromSlice("broadcaster")).?;
|
const broadcaster_idx = machine_state.getNameIndex(try NameString.fromSlice("broadcaster")).?;
|
||||||
const rx_idx = machine_state.get_name_idx(try NameString.fromSlice("rx")).?;
|
const rx_idx = machine_state.getNameIndex(try NameString.fromSlice("rx")).?;
|
||||||
|
|
||||||
const analysis = try analyze_machine(allocator, machine_state, broadcaster_idx, rx_idx);
|
const analysis = try analyzeMachine(allocator, machine_state, broadcaster_idx, rx_idx);
|
||||||
defer analysis.deinit();
|
defer analysis.deinit();
|
||||||
|
|
||||||
var answer: u64 = 1;
|
var answer: u64 = 1;
|
||||||
for (analysis.counters) |counter| {
|
for (analysis.counters) |counter| {
|
||||||
const cycle_size = try find_counter_cycle_size(allocator, &machine_state, counter, broadcaster_idx);
|
const cycle_size = try findCounterCycleSize(allocator, &machine_state, counter, broadcaster_idx);
|
||||||
answer = lcm(answer, cycle_size);
|
answer = lcm(answer, cycle_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
20
src/day3.zig
20
src/day3.zig
@ -5,7 +5,7 @@ const Point = @import("./point.zig").Point;
|
|||||||
const PointU32 = Point(u32);
|
const PointU32 = Point(u32);
|
||||||
const PointList = std.ArrayList(PointU32);
|
const PointList = std.ArrayList(PointU32);
|
||||||
|
|
||||||
fn find_number_start(line: []const u8, from: u32) u32 {
|
fn findNumberStart(line: []const u8, from: u32) u32 {
|
||||||
var i = from;
|
var i = from;
|
||||||
while (i > 0) : (i -= 1) {
|
while (i > 0) : (i -= 1) {
|
||||||
if (!std.ascii.isDigit(line[i-1])) { break; }
|
if (!std.ascii.isDigit(line[i-1])) { break; }
|
||||||
@ -13,7 +13,7 @@ fn find_number_start(line: []const u8, from: u32) u32 {
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_number_end(line: []const u8, from: u32) u32 {
|
fn findNumberEnd(line: []const u8, from: u32) u32 {
|
||||||
var i = from;
|
var i = from;
|
||||||
while (i < line.len-1) : (i += 1) {
|
while (i < line.len-1) : (i += 1) {
|
||||||
if (!std.ascii.isDigit(line[i+1])) { break; }
|
if (!std.ascii.isDigit(line[i+1])) { break; }
|
||||||
@ -21,14 +21,14 @@ fn find_number_end(line: []const u8, from: u32) u32 {
|
|||||||
return i+1;
|
return i+1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_unique(list: *PointList, point: PointU32) !void {
|
fn appendUnique(list: *PointList, point: PointU32) !void {
|
||||||
for (list.items) |item| {
|
for (list.items) |item| {
|
||||||
if (item.eql(point)) { return; }
|
if (item.eql(point)) { return; }
|
||||||
}
|
}
|
||||||
try list.append(point);
|
try list.append(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_neighbouring_numbers(found_list: *PointList, lines: []const []const u8, x: usize, y: usize) !void {
|
fn findNeighbouringNumbers(found_list: *PointList, lines: []const []const u8, x: usize, y: usize) !void {
|
||||||
const neighbours = .{
|
const neighbours = .{
|
||||||
.{ -1, -1 }, .{ 0, -1 }, .{ 1, -1 },
|
.{ -1, -1 }, .{ 0, -1 }, .{ 1, -1 },
|
||||||
.{ -1, 0 }, .{ 1, 0 },
|
.{ -1, 0 }, .{ 1, 0 },
|
||||||
@ -42,8 +42,8 @@ fn find_neighbouring_numbers(found_list: *PointList, lines: []const []const u8,
|
|||||||
const nx_u32: u32 = @intCast(nx);
|
const nx_u32: u32 = @intCast(nx);
|
||||||
const ny_u32: u32 = @intCast(ny);
|
const ny_u32: u32 = @intCast(ny);
|
||||||
if (std.ascii.isDigit(lines[ny_u32][nx_u32])) {
|
if (std.ascii.isDigit(lines[ny_u32][nx_u32])) {
|
||||||
const number_start = find_number_start(lines[ny_u32], nx_u32);
|
const number_start = findNumberStart(lines[ny_u32], nx_u32);
|
||||||
try append_unique(found_list, .{ .x = number_start, .y = ny_u32 });
|
try appendUnique(found_list, .{ .x = number_start, .y = ny_u32 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,14 +60,14 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
for (line, 0..) |c, x| {
|
for (line, 0..) |c, x| {
|
||||||
if (std.ascii.isDigit(c) or c == '.') { continue; }
|
if (std.ascii.isDigit(c) or c == '.') { continue; }
|
||||||
|
|
||||||
try find_neighbouring_numbers(&numbers, lines, x, y);
|
try findNeighbouringNumbers(&numbers, lines, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sum: u32 = 0;
|
var sum: u32 = 0;
|
||||||
for (numbers.items) |number_pos| {
|
for (numbers.items) |number_pos| {
|
||||||
const line = lines[number_pos.y];
|
const line = lines[number_pos.y];
|
||||||
const number_end = find_number_end(line, number_pos.x);
|
const number_end = findNumberEnd(line, number_pos.x);
|
||||||
const number_str = line[number_pos.x..number_end];
|
const number_str = line[number_pos.x..number_end];
|
||||||
sum += try std.fmt.parseInt(u32, number_str, 10);
|
sum += try std.fmt.parseInt(u32, number_str, 10);
|
||||||
}
|
}
|
||||||
@ -87,14 +87,14 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
var numbers = PointList.init(allocator);
|
var numbers = PointList.init(allocator);
|
||||||
defer numbers.deinit();
|
defer numbers.deinit();
|
||||||
|
|
||||||
try find_neighbouring_numbers(&numbers, lines, x, y);
|
try findNeighbouringNumbers(&numbers, lines, x, y);
|
||||||
|
|
||||||
if (numbers.items.len != 2) continue;
|
if (numbers.items.len != 2) continue;
|
||||||
|
|
||||||
var gear_ratio: u32 = 1;
|
var gear_ratio: u32 = 1;
|
||||||
for (numbers.items) |number_pos| {
|
for (numbers.items) |number_pos| {
|
||||||
const number_line = lines[number_pos.y];
|
const number_line = lines[number_pos.y];
|
||||||
const number_end = find_number_end(number_line, number_pos.x);
|
const number_end = findNumberEnd(number_line, number_pos.x);
|
||||||
const number_str = number_line[number_pos.x..number_end];
|
const number_str = number_line[number_pos.x..number_end];
|
||||||
const number = try std.fmt.parseInt(u32, number_str, 10);
|
const number = try std.fmt.parseInt(u32, number_str, 10);
|
||||||
gear_ratio *= number;
|
gear_ratio *= number;
|
||||||
|
12
src/day4.zig
12
src/day4.zig
@ -12,7 +12,7 @@ const Card = struct {
|
|||||||
|
|
||||||
const CardArray = std.ArrayList(Card);
|
const CardArray = std.ArrayList(Card);
|
||||||
|
|
||||||
fn parse_numbers(result: *BoundedUintArray, text: []const u8) !void {
|
fn parseNumbers(result: *BoundedUintArray, text: []const u8) !void {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
var tokens = std.mem.tokenizeScalar(u8, text, ' ');
|
var tokens = std.mem.tokenizeScalar(u8, text, ' ');
|
||||||
while (tokens.next()) |token| : (i += 1) {
|
while (tokens.next()) |token| : (i += 1) {
|
||||||
@ -21,7 +21,7 @@ fn parse_numbers(result: *BoundedUintArray, text: []const u8) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !CardArray {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !CardArray {
|
||||||
var cards = CardArray.init(allocator);
|
var cards = CardArray.init(allocator);
|
||||||
for (lines) |line| {
|
for (lines) |line| {
|
||||||
var card = Card{
|
var card = Card{
|
||||||
@ -32,10 +32,10 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !CardArray {
|
|||||||
const bar = std.mem.indexOfScalar(u8, line, '|') orelse @panic("Invalid format");
|
const bar = std.mem.indexOfScalar(u8, line, '|') orelse @panic("Invalid format");
|
||||||
|
|
||||||
const winning_str = line[(colon+2)..(bar-1)];
|
const winning_str = line[(colon+2)..(bar-1)];
|
||||||
try parse_numbers(&card.winning, winning_str);
|
try parseNumbers(&card.winning, winning_str);
|
||||||
|
|
||||||
const mine_str = line[(bar+2)..];
|
const mine_str = line[(bar+2)..];
|
||||||
try parse_numbers(&card.mine, mine_str);
|
try parseNumbers(&card.mine, mine_str);
|
||||||
|
|
||||||
try cards.append(card);
|
try cards.append(card);
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !CardArray {
|
|||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const cards = try parse_input(allocator, input.lines);
|
const cards = try parseInput(allocator, input.lines);
|
||||||
defer cards.deinit();
|
defer cards.deinit();
|
||||||
|
|
||||||
var result: u32 = 0;
|
var result: u32 = 0;
|
||||||
@ -67,7 +67,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const cards = try parse_input(allocator, input.lines);
|
const cards = try parseInput(allocator, input.lines);
|
||||||
defer cards.deinit();
|
defer cards.deinit();
|
||||||
|
|
||||||
var multipliers = try allocator.alloc(u32, cards.items.len);
|
var multipliers = try allocator.alloc(u32, cards.items.len);
|
||||||
|
40
src/day5.zig
40
src/day5.zig
@ -21,7 +21,7 @@ const Input = struct {
|
|||||||
temperature_to_humidity: MappingSet,
|
temperature_to_humidity: MappingSet,
|
||||||
humidity_to_location : MappingSet,
|
humidity_to_location : MappingSet,
|
||||||
|
|
||||||
fn get_mappings(self: *const Input) [7]*const MappingSet {
|
fn getMappings(self: *const Input) [7]*const MappingSet {
|
||||||
return [_]*const MappingSet{
|
return [_]*const MappingSet{
|
||||||
&self.seed_to_soil,
|
&self.seed_to_soil,
|
||||||
&self.soil_to_fertilizer,
|
&self.soil_to_fertilizer,
|
||||||
@ -34,7 +34,7 @@ const Input = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_seeds(result: *SeedsArray, text: []const u8) !void {
|
fn parseSeeds(result: *SeedsArray, text: []const u8) !void {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
var tokens = std.mem.tokenizeScalar(u8, text, ' ');
|
var tokens = std.mem.tokenizeScalar(u8, text, ' ');
|
||||||
while (tokens.next()) |token| : (i += 1) {
|
while (tokens.next()) |token| : (i += 1) {
|
||||||
@ -43,7 +43,7 @@ fn parse_seeds(result: *SeedsArray, text: []const u8) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_mapping_range(line: []const u8) !MappingRange {
|
fn parseMappingRange(line: []const u8) !MappingRange {
|
||||||
const first_space = std.mem.indexOfScalar(u8, line, ' ') orelse @panic("invalid format");
|
const first_space = std.mem.indexOfScalar(u8, line, ' ') orelse @panic("invalid format");
|
||||||
const last_space = std.mem.lastIndexOfScalar(u8, line, ' ') orelse @panic("invalid format");
|
const last_space = std.mem.lastIndexOfScalar(u8, line, ' ') orelse @panic("invalid format");
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ fn parse_mapping_range(line: []const u8) !MappingRange {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(lines: []const []const u8) !Input {
|
fn parseInput(lines: []const []const u8) !Input {
|
||||||
var seeds = try SeedsArray.init(0);
|
var seeds = try SeedsArray.init(0);
|
||||||
var mapping_sets: [7]MappingSet = undefined;
|
var mapping_sets: [7]MappingSet = undefined;
|
||||||
for (&mapping_sets) |*set| {
|
for (&mapping_sets) |*set| {
|
||||||
@ -62,7 +62,7 @@ fn parse_input(lines: []const []const u8) !Input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const seeds_colon = std.mem.indexOfScalar(u8, lines[0], ':') orelse @panic("invalid format");
|
const seeds_colon = std.mem.indexOfScalar(u8, lines[0], ':') orelse @panic("invalid format");
|
||||||
try parse_seeds(&seeds, lines[0][(seeds_colon+1)..]);
|
try parseSeeds(&seeds, lines[0][(seeds_colon+1)..]);
|
||||||
|
|
||||||
var mappign_set_idx: u32 = 0;
|
var mappign_set_idx: u32 = 0;
|
||||||
for (lines[2..]) |line| {
|
for (lines[2..]) |line| {
|
||||||
@ -72,7 +72,7 @@ fn parse_input(lines: []const []const u8) !Input {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try mapping_sets[mappign_set_idx].append(try parse_mapping_range(line));
|
try mapping_sets[mappign_set_idx].append(try parseMappingRange(line));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Input{
|
return Input{
|
||||||
@ -87,35 +87,35 @@ fn parse_input(lines: []const []const u8) !Input {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_in_range(x: u64, range_start: u64, range_size: u64) bool {
|
fn isInRange(x: u64, range_start: u64, range_size: u64) bool {
|
||||||
return range_start <= x and x < (range_start + range_size);
|
return range_start <= x and x < (range_start + range_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_mapping(src: u64, mapping_set: *const MappingSet) u64 {
|
fn applyMapping(src: u64, mapping_set: *const MappingSet) u64 {
|
||||||
for (mapping_set.slice()) |mapping| {
|
for (mapping_set.slice()) |mapping| {
|
||||||
if (is_in_range(src, mapping.src_start, mapping.size)) {
|
if (isInRange(src, mapping.src_start, mapping.size)) {
|
||||||
return mapping.dest_start + (src - mapping.src_start);
|
return mapping.dest_start + (src - mapping.src_start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_seed_to_location(seed: u64, input: *const Input) u64 {
|
fn applySeedToLocation(seed: u64, input: *const Input) u64 {
|
||||||
const mapping_sequence = input.get_mappings();
|
const mapping_sequence = input.getMappings();
|
||||||
|
|
||||||
var value = seed;
|
var value = seed;
|
||||||
inline for (mapping_sequence) |mapping_set| {
|
inline for (mapping_sequence) |mapping_set| {
|
||||||
value = apply_mapping(value, mapping_set);
|
value = applyMapping(value, mapping_set);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const parsed = try parse_input(input.lines);
|
const parsed = try parseInput(input.lines);
|
||||||
|
|
||||||
var lowest_location: ?u64 = null;
|
var lowest_location: ?u64 = null;
|
||||||
for (parsed.seeds.slice()) |seed| {
|
for (parsed.seeds.slice()) |seed| {
|
||||||
const value = apply_seed_to_location(seed, &parsed);
|
const value = applySeedToLocation(seed, &parsed);
|
||||||
|
|
||||||
if (lowest_location) |location| {
|
if (lowest_location) |location| {
|
||||||
lowest_location = @min(location, value);
|
lowest_location = @min(location, value);
|
||||||
@ -127,7 +127,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
return .{ .uint = @intCast(lowest_location.?) };
|
return .{ .uint = @intCast(lowest_location.?) };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_range(mapping_set: *MappingSet, from: u64, to: u64) !void {
|
fn removeRange(mapping_set: *MappingSet, from: u64, to: u64) !void {
|
||||||
for (0.., mapping_set.slice()) |i, *range| {
|
for (0.., mapping_set.slice()) |i, *range| {
|
||||||
const range_start = range.src_start;
|
const range_start = range.src_start;
|
||||||
const range_end = range.src_start + range.size-1;
|
const range_end = range.src_start + range.size-1;
|
||||||
@ -155,7 +155,7 @@ fn remove_range(mapping_set: *MappingSet, from: u64, to: u64) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_mapping_to_range_set(src_set: *MappingSet, mapping_set: *const MappingSet) !void {
|
fn applyMappingToRangeSet(src_set: *MappingSet, mapping_set: *const MappingSet) !void {
|
||||||
var new_src_set = try MappingSet.init(0);
|
var new_src_set = try MappingSet.init(0);
|
||||||
var unmapped_ranges = try MappingSet.init(0);
|
var unmapped_ranges = try MappingSet.init(0);
|
||||||
try unmapped_ranges.appendSlice(src_set.slice());
|
try unmapped_ranges.appendSlice(src_set.slice());
|
||||||
@ -169,7 +169,7 @@ fn apply_mapping_to_range_set(src_set: *MappingSet, mapping_set: *const MappingS
|
|||||||
|
|
||||||
const src_start = src.src_start + (overlap_start - src.dest_start);
|
const src_start = src.src_start + (overlap_start - src.dest_start);
|
||||||
const overlap_size = (overlap_end - overlap_start) + 1;
|
const overlap_size = (overlap_end - overlap_start) + 1;
|
||||||
try remove_range(&unmapped_ranges, src_start, src_start+overlap_size-1);
|
try removeRange(&unmapped_ranges, src_start, src_start+overlap_size-1);
|
||||||
|
|
||||||
try new_src_set.append(MappingRange{
|
try new_src_set.append(MappingRange{
|
||||||
.src_start = src_start,
|
.src_start = src_start,
|
||||||
@ -184,7 +184,7 @@ fn apply_mapping_to_range_set(src_set: *MappingSet, mapping_set: *const MappingS
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const parsed = try parse_input(input.lines);
|
const parsed = try parseInput(input.lines);
|
||||||
|
|
||||||
var seed_to_location = try MappingSet.init(0);
|
var seed_to_location = try MappingSet.init(0);
|
||||||
for (0..(parsed.seeds.len/2)) |i| {
|
for (0..(parsed.seeds.len/2)) |i| {
|
||||||
@ -197,8 +197,8 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline for (parsed.get_mappings()) |mapping_set| {
|
inline for (parsed.getMappings()) |mapping_set| {
|
||||||
try apply_mapping_to_range_set(&seed_to_location, mapping_set);
|
try applyMappingToRangeSet(&seed_to_location, mapping_set);
|
||||||
}
|
}
|
||||||
var lowest_location: ?u64 = null;
|
var lowest_location: ?u64 = null;
|
||||||
for (seed_to_location.slice()) |range| {
|
for (seed_to_location.slice()) |range| {
|
||||||
|
12
src/day6.zig
12
src/day6.zig
@ -9,7 +9,7 @@ const Input = struct {
|
|||||||
distance: BoundedU32,
|
distance: BoundedU32,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_numbers(result: *BoundedU32, text: []const u8) !void {
|
fn parseNumbers(result: *BoundedU32, text: []const u8) !void {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
var tokens = std.mem.tokenizeScalar(u8, text, ' ');
|
var tokens = std.mem.tokenizeScalar(u8, text, ' ');
|
||||||
while (tokens.next()) |token| : (i += 1) {
|
while (tokens.next()) |token| : (i += 1) {
|
||||||
@ -18,17 +18,17 @@ fn parse_numbers(result: *BoundedU32, text: []const u8) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(lines: []const []const u8) !Input {
|
fn parseInput(lines: []const []const u8) !Input {
|
||||||
var time = try BoundedU32.init(0);
|
var time = try BoundedU32.init(0);
|
||||||
var distance = try BoundedU32.init(0);
|
var distance = try BoundedU32.init(0);
|
||||||
try parse_numbers(&time, lines[0][9..]);
|
try parseNumbers(&time, lines[0][9..]);
|
||||||
try parse_numbers(&distance, lines[1][9..]);
|
try parseNumbers(&distance, lines[1][9..]);
|
||||||
assert(time.len == distance.len);
|
assert(time.len == distance.len);
|
||||||
return Input{ .time = time, .distance = distance };
|
return Input{ .time = time, .distance = distance };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
var parsed = try parse_input(input.lines);
|
var parsed = try parseInput(input.lines);
|
||||||
|
|
||||||
var answer: u32 = 1;
|
var answer: u32 = 1;
|
||||||
for (0..parsed.time.len) |i| {
|
for (0..parsed.time.len) |i| {
|
||||||
@ -58,7 +58,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
var parsed = try parse_input(input.lines);
|
var parsed = try parseInput(input.lines);
|
||||||
|
|
||||||
var allowed_time: u64 = 0;
|
var allowed_time: u64 = 0;
|
||||||
var required_distance: u64 = 0;
|
var required_distance: u64 = 0;
|
||||||
|
42
src/day7.zig
42
src/day7.zig
@ -28,7 +28,7 @@ const CardCounts = [CARD_VARIANTS]CardIndex;
|
|||||||
const IndexArray = std.ArrayList(usize);
|
const IndexArray = std.ArrayList(usize);
|
||||||
const HandBids = std.ArrayList(HandBid);
|
const HandBids = std.ArrayList(HandBid);
|
||||||
|
|
||||||
fn hand_to_str(hand: Hand) [5]u8 {
|
fn handToStr(hand: Hand) [5]u8 {
|
||||||
var str = [1]u8{0} ** 5;
|
var str = [1]u8{0} ** 5;
|
||||||
for (0..hand.len) |i| {
|
for (0..hand.len) |i| {
|
||||||
str[i] = switch (hand[i]) {
|
str[i] = switch (hand[i]) {
|
||||||
@ -51,7 +51,7 @@ fn hand_to_str(hand: Hand) [5]u8 {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_card_index(char: u8) CardIndex {
|
fn getCardIndex(char: u8) CardIndex {
|
||||||
return switch (char) {
|
return switch (char) {
|
||||||
'A' => 12,
|
'A' => 12,
|
||||||
'K' => 11,
|
'K' => 11,
|
||||||
@ -70,7 +70,7 @@ fn get_card_index(char: u8) CardIndex {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !HandBids {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !HandBids {
|
||||||
var hand_bids = HandBids.init(allocator);
|
var hand_bids = HandBids.init(allocator);
|
||||||
|
|
||||||
for (lines) |line| {
|
for (lines) |line| {
|
||||||
@ -82,7 +82,7 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !HandBids {
|
|||||||
|
|
||||||
var hand_bid = HandBid{ .bid = bid };
|
var hand_bid = HandBid{ .bid = bid };
|
||||||
for (0..5) |i| {
|
for (0..5) |i| {
|
||||||
hand_bid.hand[i] = get_card_index(hand_str[i]);
|
hand_bid.hand[i] = getCardIndex(hand_str[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
try hand_bids.append(hand_bid);
|
try hand_bids.append(hand_bid);
|
||||||
@ -91,7 +91,7 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !HandBids {
|
|||||||
return hand_bids;
|
return hand_bids;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_cards(hand: Hand) CardCounts {
|
fn countCards(hand: Hand) CardCounts {
|
||||||
var counts = [1]CardIndex{0} ** CARD_VARIANTS;
|
var counts = [1]CardIndex{0} ** CARD_VARIANTS;
|
||||||
for (hand) |card| {
|
for (hand) |card| {
|
||||||
counts[card] += 1;
|
counts[card] += 1;
|
||||||
@ -99,8 +99,8 @@ fn count_cards(hand: Hand) CardCounts {
|
|||||||
return counts;
|
return counts;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn determine_card_strength(hand: Hand) HandStrength {
|
fn determineCardStrength(hand: Hand) HandStrength {
|
||||||
const counts = count_cards(hand);
|
const counts = countCards(hand);
|
||||||
|
|
||||||
var ones_count: u4 = 0;
|
var ones_count: u4 = 0;
|
||||||
var two_count: u4 = 0;
|
var two_count: u4 = 0;
|
||||||
@ -135,8 +135,8 @@ fn determine_card_strength(hand: Hand) HandStrength {
|
|||||||
return .Nothing;
|
return .Nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn determine_card_strength_joker(hand: Hand) HandStrength {
|
fn determineCardStrengthJoker(hand: Hand) HandStrength {
|
||||||
const joker_card = comptime get_card_index('J');
|
const joker_card = comptime getCardIndex('J');
|
||||||
|
|
||||||
var jokers = std.BoundedArray(usize, 5).init(0) catch unreachable;
|
var jokers = std.BoundedArray(usize, 5).init(0) catch unreachable;
|
||||||
var card_types = std.BoundedArray(CardIndex, 5).init(0) catch unreachable;
|
var card_types = std.BoundedArray(CardIndex, 5).init(0) catch unreachable;
|
||||||
@ -150,7 +150,7 @@ fn determine_card_strength_joker(hand: Hand) HandStrength {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (jokers.len == 0) {
|
if (jokers.len == 0) {
|
||||||
return determine_card_strength(hand);
|
return determineCardStrength(hand);
|
||||||
}
|
}
|
||||||
|
|
||||||
var joker_choices_container = [1]usize{0} ** 5;
|
var joker_choices_container = [1]usize{0} ** 5;
|
||||||
@ -166,7 +166,7 @@ fn determine_card_strength_joker(hand: Hand) HandStrength {
|
|||||||
modified_hand[jokers.get(i)] = card_types.get(joker_choices[i]);
|
modified_hand[jokers.get(i)] = card_types.get(joker_choices[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const strength = determine_card_strength(modified_hand);
|
const strength = determineCardStrength(modified_hand);
|
||||||
|
|
||||||
best_strength = @enumFromInt(@max(@intFromEnum(best_strength), @intFromEnum(strength)));
|
best_strength = @enumFromInt(@max(@intFromEnum(best_strength), @intFromEnum(strength)));
|
||||||
|
|
||||||
@ -182,7 +182,7 @@ fn determine_card_strength_joker(hand: Hand) HandStrength {
|
|||||||
return best_strength;
|
return best_strength;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compare_hands(a: *Hand, b: *Hand) bool {
|
fn compareHands(a: *Hand, b: *Hand) bool {
|
||||||
inline for (0..a.len) |i| {
|
inline for (0..a.len) |i| {
|
||||||
if (a[i] != b[i]) {
|
if (a[i] != b[i]) {
|
||||||
return a[i] > b[i];
|
return a[i] > b[i];
|
||||||
@ -191,8 +191,8 @@ fn compare_hands(a: *Hand, b: *Hand) bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compare_hands_joker(a: *Hand, b: *Hand) bool {
|
fn compareHandsJoker(a: *Hand, b: *Hand) bool {
|
||||||
const joker_card = comptime get_card_index('J');
|
const joker_card = comptime getCardIndex('J');
|
||||||
|
|
||||||
inline for (0..a.len) |i| {
|
inline for (0..a.len) |i| {
|
||||||
if (a[i] != b[i]) {
|
if (a[i] != b[i]) {
|
||||||
@ -204,7 +204,7 @@ fn compare_hands_joker(a: *Hand, b: *Hand) bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort_same_strength(hands: []Hand, indexes: []usize, cmp: *const fn(*Hand, *Hand) bool) void {
|
fn sortSameStrength(hands: []Hand, indexes: []usize, cmp: *const fn(*Hand, *Hand) bool) void {
|
||||||
for (0..(indexes.len-1)) |i| {
|
for (0..(indexes.len-1)) |i| {
|
||||||
for ((i+1)..indexes.len) |j| {
|
for ((i+1)..indexes.len) |j| {
|
||||||
const hand_a = &hands[indexes[i]];
|
const hand_a = &hands[indexes[i]];
|
||||||
@ -218,7 +218,7 @@ fn sort_same_strength(hands: []Hand, indexes: []usize, cmp: *const fn(*Hand, *Ha
|
|||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const hand_bids = try parse_input(allocator, input.lines);
|
const hand_bids = try parseInput(allocator, input.lines);
|
||||||
defer hand_bids.deinit();
|
defer hand_bids.deinit();
|
||||||
|
|
||||||
var hand_strengths = try allocator.alloc(HandStrength, hand_bids.items.len);
|
var hand_strengths = try allocator.alloc(HandStrength, hand_bids.items.len);
|
||||||
@ -229,7 +229,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
|
|
||||||
for (0..hand_bids.items.len) |i| {
|
for (0..hand_bids.items.len) |i| {
|
||||||
hands[i] = hand_bids.items[i].hand;
|
hands[i] = hand_bids.items[i].hand;
|
||||||
hand_strengths[i] = determine_card_strength(hand_bids.items[i].hand);
|
hand_strengths[i] = determineCardStrength(hand_bids.items[i].hand);
|
||||||
}
|
}
|
||||||
|
|
||||||
var order = try IndexArray.initCapacity(allocator, hand_bids.items.len);
|
var order = try IndexArray.initCapacity(allocator, hand_bids.items.len);
|
||||||
@ -249,7 +249,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sub_order.items.len != 0) {
|
if (sub_order.items.len != 0) {
|
||||||
sort_same_strength(hands, sub_order.items, compare_hands);
|
sortSameStrength(hands, sub_order.items, compareHands);
|
||||||
order.appendSliceAssumeCapacity(sub_order.items);
|
order.appendSliceAssumeCapacity(sub_order.items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -264,7 +264,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const hand_bids = try parse_input(allocator, input.lines);
|
const hand_bids = try parseInput(allocator, input.lines);
|
||||||
defer hand_bids.deinit();
|
defer hand_bids.deinit();
|
||||||
|
|
||||||
var hand_strengths = try allocator.alloc(HandStrength, hand_bids.items.len);
|
var hand_strengths = try allocator.alloc(HandStrength, hand_bids.items.len);
|
||||||
@ -275,7 +275,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
|
|
||||||
for (0..hand_bids.items.len) |i| {
|
for (0..hand_bids.items.len) |i| {
|
||||||
hands[i] = hand_bids.items[i].hand;
|
hands[i] = hand_bids.items[i].hand;
|
||||||
hand_strengths[i] = determine_card_strength_joker(hand_bids.items[i].hand);
|
hand_strengths[i] = determineCardStrengthJoker(hand_bids.items[i].hand);
|
||||||
}
|
}
|
||||||
|
|
||||||
var order = try IndexArray.initCapacity(allocator, hand_bids.items.len);
|
var order = try IndexArray.initCapacity(allocator, hand_bids.items.len);
|
||||||
@ -295,7 +295,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sub_order.items.len != 0) {
|
if (sub_order.items.len != 0) {
|
||||||
sort_same_strength(hands, sub_order.items, compare_hands_joker);
|
sortSameStrength(hands, sub_order.items, compareHandsJoker);
|
||||||
order.appendSliceAssumeCapacity(sub_order.items);
|
order.appendSliceAssumeCapacity(sub_order.items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
74
src/day8.zig
74
src/day8.zig
@ -39,7 +39,7 @@ const NodeMapLeaf = struct {
|
|||||||
nodes: std.ArrayList(NodeSplit)
|
nodes: std.ArrayList(NodeSplit)
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_instructions(allocator: Allocator, line: []const u8) !DirectionList {
|
fn parseInstructions(allocator: Allocator, line: []const u8) !DirectionList {
|
||||||
var instructions = DirectionList.init(allocator);
|
var instructions = DirectionList.init(allocator);
|
||||||
errdefer instructions.deinit();
|
errdefer instructions.deinit();
|
||||||
for (line) |c| {
|
for (line) |c| {
|
||||||
@ -53,7 +53,7 @@ fn parse_instructions(allocator: Allocator, line: []const u8) !DirectionList {
|
|||||||
return instructions;
|
return instructions;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_char_to_node_part(c: u8) NodePart {
|
fn parseCharToNodePart(c: u8) NodePart {
|
||||||
if (std.ascii.isAlphabetic(c)) {
|
if (std.ascii.isAlphabetic(c)) {
|
||||||
assert(std.ascii.isUpper(c));
|
assert(std.ascii.isUpper(c));
|
||||||
return @intCast(c - 'A' + 10);
|
return @intCast(c - 'A' + 10);
|
||||||
@ -64,17 +64,17 @@ fn parse_char_to_node_part(c: u8) NodePart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_node_label(text: []const u8) NodeLabel {
|
fn parseNodeLabel(text: []const u8) NodeLabel {
|
||||||
assert(text.len == 3);
|
assert(text.len == 3);
|
||||||
|
|
||||||
var label: NodeLabel = undefined;
|
var label: NodeLabel = undefined;
|
||||||
inline for (0..3) |i| {
|
inline for (0..3) |i| {
|
||||||
label[i] = parse_char_to_node_part(text[i]);
|
label[i] = parseCharToNodePart(text[i]);
|
||||||
}
|
}
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_nodes(allocator: Allocator, lines: []const []const u8) !NodeJunctionList {
|
fn parseNodes(allocator: Allocator, lines: []const []const u8) !NodeJunctionList {
|
||||||
var nodes = try NodeJunctionList.initCapacity(allocator, lines.len);
|
var nodes = try NodeJunctionList.initCapacity(allocator, lines.len);
|
||||||
errdefer nodes.deinit();
|
errdefer nodes.deinit();
|
||||||
|
|
||||||
@ -83,26 +83,26 @@ fn parse_nodes(allocator: Allocator, lines: []const []const u8) !NodeJunctionLis
|
|||||||
const left_str = line[7..10];
|
const left_str = line[7..10];
|
||||||
const right_str = line[12..15];
|
const right_str = line[12..15];
|
||||||
nodes.appendAssumeCapacity(NodeJunction{
|
nodes.appendAssumeCapacity(NodeJunction{
|
||||||
.from = parse_node_label(from_str),
|
.from = parseNodeLabel(from_str),
|
||||||
.left = parse_node_label(left_str),
|
.left = parseNodeLabel(left_str),
|
||||||
.right = parse_node_label(right_str),
|
.right = parseNodeLabel(right_str),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !Input {
|
||||||
const instructions = try parse_instructions(allocator, lines[0]);
|
const instructions = try parseInstructions(allocator, lines[0]);
|
||||||
errdefer instructions.deinit();
|
errdefer instructions.deinit();
|
||||||
|
|
||||||
const nodes = try parse_nodes(allocator, lines[2..]);
|
const nodes = try parseNodes(allocator, lines[2..]);
|
||||||
errdefer nodes.deinit();
|
errdefer nodes.deinit();
|
||||||
|
|
||||||
return Input{ .instructions = instructions, .nodes = nodes };
|
return Input{ .instructions = instructions, .nodes = nodes };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_map_find_branch(node_map: *NodeMap, branch_name: NodePart) ?*NodeMapBranch {
|
fn nodeMapFindBranch(node_map: *NodeMap, branch_name: NodePart) ?*NodeMapBranch {
|
||||||
for (node_map.items) |*branch| {
|
for (node_map.items) |*branch| {
|
||||||
if (branch.name == branch_name) {
|
if (branch.name == branch_name) {
|
||||||
return branch;
|
return branch;
|
||||||
@ -111,7 +111,7 @@ fn node_map_find_branch(node_map: *NodeMap, branch_name: NodePart) ?*NodeMapBran
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_leaf_find_split(node_leaf: *NodeMapLeaf, name: NodePart) ?*NodeSplit {
|
fn nodeLeafFindSplit(node_leaf: *NodeMapLeaf, name: NodePart) ?*NodeSplit {
|
||||||
for (node_leaf.nodes.items) |*split| {
|
for (node_leaf.nodes.items) |*split| {
|
||||||
if (split.name == name) {
|
if (split.name == name) {
|
||||||
return split;
|
return split;
|
||||||
@ -120,7 +120,7 @@ fn node_leaf_find_split(node_leaf: *NodeMapLeaf, name: NodePart) ?*NodeSplit {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_branch_find_leaf(node_branch: *NodeMapBranch, leaf_name: NodePart) ?*NodeMapLeaf {
|
fn nodeBranchFindLeaf(node_branch: *NodeMapBranch, leaf_name: NodePart) ?*NodeMapLeaf {
|
||||||
for (node_branch.nodes.items) |*leaf| {
|
for (node_branch.nodes.items) |*leaf| {
|
||||||
if (leaf.name == leaf_name) {
|
if (leaf.name == leaf_name) {
|
||||||
return leaf;
|
return leaf;
|
||||||
@ -129,13 +129,13 @@ fn node_branch_find_leaf(node_branch: *NodeMapBranch, leaf_name: NodePart) ?*Nod
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_node_map(allocator: Allocator, nodes: NodeJunctionList) !NodeMap {
|
fn createNodeMap(allocator: Allocator, nodes: NodeJunctionList) !NodeMap {
|
||||||
var node_map = NodeMap.init(allocator);
|
var node_map = NodeMap.init(allocator);
|
||||||
errdefer free_node_map(node_map);
|
errdefer freeNodeMap(node_map);
|
||||||
|
|
||||||
for (nodes.items) |node_junction| {
|
for (nodes.items) |node_junction| {
|
||||||
const from = node_junction.from;
|
const from = node_junction.from;
|
||||||
var node_branch_opt = node_map_find_branch(&node_map, from[0]);
|
var node_branch_opt = nodeMapFindBranch(&node_map, from[0]);
|
||||||
if (node_branch_opt == null) {
|
if (node_branch_opt == null) {
|
||||||
try node_map.append(NodeMapBranch{
|
try node_map.append(NodeMapBranch{
|
||||||
.name = from[0],
|
.name = from[0],
|
||||||
@ -145,7 +145,7 @@ fn create_node_map(allocator: Allocator, nodes: NodeJunctionList) !NodeMap {
|
|||||||
}
|
}
|
||||||
var node_branch = node_branch_opt.?;
|
var node_branch = node_branch_opt.?;
|
||||||
|
|
||||||
var node_leaf_opt = node_branch_find_leaf(node_branch, from[1]);
|
var node_leaf_opt = nodeBranchFindLeaf(node_branch, from[1]);
|
||||||
if (node_leaf_opt == null) {
|
if (node_leaf_opt == null) {
|
||||||
try node_branch.nodes.append(NodeMapLeaf{
|
try node_branch.nodes.append(NodeMapLeaf{
|
||||||
.name = from[1],
|
.name = from[1],
|
||||||
@ -165,7 +165,7 @@ fn create_node_map(allocator: Allocator, nodes: NodeJunctionList) !NodeMap {
|
|||||||
return node_map;
|
return node_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn free_node_map(node_map: NodeMap) void {
|
fn freeNodeMap(node_map: NodeMap) void {
|
||||||
for (node_map.items) |branch| {
|
for (node_map.items) |branch| {
|
||||||
for (branch.nodes.items) |leaf| {
|
for (branch.nodes.items) |leaf| {
|
||||||
leaf.nodes.deinit();
|
leaf.nodes.deinit();
|
||||||
@ -175,16 +175,16 @@ fn free_node_map(node_map: NodeMap) void {
|
|||||||
node_map.deinit();
|
node_map.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_map_lookup(node_map: *NodeMap, from: NodeLabel, direction: Direction) ?NodeLabel {
|
fn nodeMapLookup(node_map: *NodeMap, from: NodeLabel, direction: Direction) ?NodeLabel {
|
||||||
const branch_opt = node_map_find_branch(node_map, from[0]);
|
const branch_opt = nodeMapFindBranch(node_map, from[0]);
|
||||||
if (branch_opt == null) return null;
|
if (branch_opt == null) return null;
|
||||||
const branch = branch_opt.?;
|
const branch = branch_opt.?;
|
||||||
|
|
||||||
const leaf_opt = node_branch_find_leaf(branch, from[1]);
|
const leaf_opt = nodeBranchFindLeaf(branch, from[1]);
|
||||||
if (leaf_opt == null) return null;
|
if (leaf_opt == null) return null;
|
||||||
const leaf = leaf_opt.?;
|
const leaf = leaf_opt.?;
|
||||||
|
|
||||||
const split_opt = node_leaf_find_split(leaf, from[2]);
|
const split_opt = nodeLeafFindSplit(leaf, from[2]);
|
||||||
if (split_opt == null) return null;
|
if (split_opt == null) return null;
|
||||||
const split = split_opt.?;
|
const split = split_opt.?;
|
||||||
|
|
||||||
@ -196,33 +196,33 @@ fn node_map_lookup(node_map: *NodeMap, from: NodeLabel, direction: Direction) ?N
|
|||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const parsed = try parse_input(allocator, input.lines);
|
const parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var arena = std.heap.ArenaAllocator.init(allocator);
|
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
|
|
||||||
var node_map = try create_node_map(arena.allocator(), parsed.nodes);
|
var node_map = try createNodeMap(arena.allocator(), parsed.nodes);
|
||||||
defer free_node_map(node_map);
|
defer freeNodeMap(node_map);
|
||||||
|
|
||||||
var steps: u32 = 0;
|
var steps: u32 = 0;
|
||||||
var goal = comptime parse_node_label("ZZZ");
|
var goal = comptime parseNodeLabel("ZZZ");
|
||||||
var current = comptime parse_node_label("AAA");
|
var current = comptime parseNodeLabel("AAA");
|
||||||
while (!std.mem.eql(NodePart, ¤t, &goal)) {
|
while (!std.mem.eql(NodePart, ¤t, &goal)) {
|
||||||
const instruction = parsed.instructions.items[steps % parsed.instructions.items.len];
|
const instruction = parsed.instructions.items[steps % parsed.instructions.items.len];
|
||||||
current = node_map_lookup(&node_map, current, instruction) orelse @panic("dead end");
|
current = nodeMapLookup(&node_map, current, instruction) orelse @panic("dead end");
|
||||||
steps += 1;
|
steps += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{ .uint = steps };
|
return .{ .uint = steps };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_steps_needed(node_map: *NodeMap, instructions: []const Direction, start: NodeLabel) u32 {
|
fn findStepsNeeded(node_map: *NodeMap, instructions: []const Direction, start: NodeLabel) u32 {
|
||||||
var steps: u32 = 0;
|
var steps: u32 = 0;
|
||||||
var current = start;
|
var current = start;
|
||||||
while (current[2] != comptime parse_char_to_node_part('Z')) {
|
while (current[2] != comptime parseCharToNodePart('Z')) {
|
||||||
const instruction = instructions[steps % instructions.len];
|
const instruction = instructions[steps % instructions.len];
|
||||||
current = node_map_lookup(node_map, current, instruction) orelse @panic("dead end");
|
current = nodeMapLookup(node_map, current, instruction) orelse @panic("dead end");
|
||||||
steps += 1;
|
steps += 1;
|
||||||
}
|
}
|
||||||
return steps;
|
return steps;
|
||||||
@ -234,21 +234,21 @@ fn lcm(a: u64, b: u64) u64 {
|
|||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
const parsed = try parse_input(allocator, input.lines);
|
const parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var arena = std.heap.ArenaAllocator.init(allocator);
|
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
|
|
||||||
var node_map = try create_node_map(arena.allocator(), parsed.nodes);
|
var node_map = try createNodeMap(arena.allocator(), parsed.nodes);
|
||||||
defer free_node_map(node_map);
|
defer freeNodeMap(node_map);
|
||||||
|
|
||||||
var all_steps = std.ArrayList(u32).init(allocator);
|
var all_steps = std.ArrayList(u32).init(allocator);
|
||||||
defer all_steps.deinit();
|
defer all_steps.deinit();
|
||||||
|
|
||||||
for (parsed.nodes.items) |node_junction| {
|
for (parsed.nodes.items) |node_junction| {
|
||||||
if (node_junction.from[2] == comptime parse_char_to_node_part('A')) {
|
if (node_junction.from[2] == comptime parseCharToNodePart('A')) {
|
||||||
const steps = find_steps_needed(&node_map, parsed.instructions.items, node_junction.from);
|
const steps = findStepsNeeded(&node_map, parsed.instructions.items, node_junction.from);
|
||||||
try all_steps.append(steps);
|
try all_steps.append(steps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
34
src/day9.zig
34
src/day9.zig
@ -16,7 +16,7 @@ const Input = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_history(allocator: Allocator, line: []const u8) !History {
|
fn parseHistory(allocator: Allocator, line: []const u8) !History {
|
||||||
var history = History.init(allocator);
|
var history = History.init(allocator);
|
||||||
var tokens = std.mem.tokenizeScalar(u8, line, ' ');
|
var tokens = std.mem.tokenizeScalar(u8, line, ' ');
|
||||||
while (tokens.next()) |token| {
|
while (tokens.next()) |token| {
|
||||||
@ -26,19 +26,19 @@ fn parse_history(allocator: Allocator, line: []const u8) !History {
|
|||||||
return history;
|
return history;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !Input {
|
||||||
var histories = std.ArrayList(History).init(allocator);
|
var histories = std.ArrayList(History).init(allocator);
|
||||||
errdefer histories.deinit();
|
errdefer histories.deinit();
|
||||||
|
|
||||||
for (lines) |line| {
|
for (lines) |line| {
|
||||||
const history = try parse_history(allocator, line);
|
const history = try parseHistory(allocator, line);
|
||||||
try histories.append(history);
|
try histories.append(history);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Input{ .histories = histories };
|
return Input{ .histories = histories };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_all_zeros(numbers: []i32) bool {
|
fn isAllZeros(numbers: []i32) bool {
|
||||||
for (numbers) |num| {
|
for (numbers) |num| {
|
||||||
if (num != 0) {
|
if (num != 0) {
|
||||||
return false;
|
return false;
|
||||||
@ -47,34 +47,34 @@ fn is_all_zeros(numbers: []i32) bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_changes(result: []i32, numbers: []i32) void {
|
fn calcChanges(result: []i32, numbers: []i32) void {
|
||||||
assert(result.len >= numbers.len-1);
|
assert(result.len >= numbers.len-1);
|
||||||
for (0..(numbers.len-1)) |i| {
|
for (0..(numbers.len-1)) |i| {
|
||||||
result[i] = numbers[i+1] - numbers[i];
|
result[i] = numbers[i+1] - numbers[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn all_changes_of_history(allocator: Allocator, history: []i32) !HistoryChanges {
|
fn allChangesOfHistory(allocator: Allocator, history: []i32) !HistoryChanges {
|
||||||
const max_changes_len: usize = history.len-1;
|
const max_changes_len: usize = history.len-1;
|
||||||
assert(max_changes_len >= 1);
|
assert(max_changes_len >= 1);
|
||||||
|
|
||||||
var changes = HistoryChanges.init(allocator);
|
var changes = HistoryChanges.init(allocator);
|
||||||
errdefer free_history_changes(changes);
|
errdefer freeHistoryChanges(changes);
|
||||||
|
|
||||||
try changes.append(try allocator.alloc(i32, history.len-1));
|
try changes.append(try allocator.alloc(i32, history.len-1));
|
||||||
calc_changes(changes.items[0], history);
|
calcChanges(changes.items[0], history);
|
||||||
|
|
||||||
for (1..max_changes_len) |i| {
|
for (1..max_changes_len) |i| {
|
||||||
if (is_all_zeros(changes.items[i-1])) break;
|
if (isAllZeros(changes.items[i-1])) break;
|
||||||
|
|
||||||
try changes.append(try allocator.alloc(i32, history.len-i-1));
|
try changes.append(try allocator.alloc(i32, history.len-i-1));
|
||||||
calc_changes(changes.items[i], changes.items[i-1]);
|
calcChanges(changes.items[i], changes.items[i-1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn free_history_changes(changes: HistoryChanges) void {
|
fn freeHistoryChanges(changes: HistoryChanges) void {
|
||||||
for (changes.items) |row| {
|
for (changes.items) |row| {
|
||||||
changes.allocator.free(row);
|
changes.allocator.free(row);
|
||||||
}
|
}
|
||||||
@ -83,15 +83,15 @@ fn free_history_changes(changes: HistoryChanges) void {
|
|||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
var parsed = try parse_input(allocator, input.lines);
|
var parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var result: i32 = 0;
|
var result: i32 = 0;
|
||||||
for (parsed.histories.items) |history| {
|
for (parsed.histories.items) |history| {
|
||||||
const numbers = history.items;
|
const numbers = history.items;
|
||||||
|
|
||||||
const changes = try all_changes_of_history(allocator, numbers);
|
const changes = try allChangesOfHistory(allocator, numbers);
|
||||||
defer free_history_changes(changes);
|
defer freeHistoryChanges(changes);
|
||||||
|
|
||||||
var prediction: i32 = numbers[numbers.len-1];
|
var prediction: i32 = numbers[numbers.len-1];
|
||||||
for (changes.items) |change_row| {
|
for (changes.items) |change_row| {
|
||||||
@ -106,15 +106,15 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
|
|||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const allocator = input.allocator;
|
const allocator = input.allocator;
|
||||||
var parsed = try parse_input(allocator, input.lines);
|
var parsed = try parseInput(allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
var result: i32 = 0;
|
var result: i32 = 0;
|
||||||
for (parsed.histories.items) |history| {
|
for (parsed.histories.items) |history| {
|
||||||
const numbers = history.items;
|
const numbers = history.items;
|
||||||
|
|
||||||
const changes = try all_changes_of_history(allocator, numbers);
|
const changes = try allChangesOfHistory(allocator, numbers);
|
||||||
defer free_history_changes(changes);
|
defer freeHistoryChanges(changes);
|
||||||
|
|
||||||
var prediction: i32 = 0;
|
var prediction: i32 = 0;
|
||||||
for (0..changes.items.len) |i| {
|
for (0..changes.items.len) |i| {
|
||||||
|
@ -8,7 +8,7 @@ const Input = struct {
|
|||||||
fn deinit(self: Input) void { _ = self; }
|
fn deinit(self: Input) void { _ = self; }
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
fn parseInput(allocator: Allocator, lines: []const []const u8) !Input {
|
||||||
const parsed = Input {
|
const parsed = Input {
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
};
|
};
|
||||||
@ -20,14 +20,14 @@ fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||||
const parsed = try parse_input(input.allocator, input.lines);
|
const parsed = try parseInput(input.allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
return .{ .uint = 0 };
|
return .{ .uint = 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||||
const parsed = try parse_input(input.allocator, input.lines);
|
const parsed = try parseInput(input.allocator, input.lines);
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
|
|
||||||
return .{ .uint = 0 };
|
return .{ .uint = 0 };
|
||||||
|
@ -41,6 +41,7 @@ const Days = [_]aoc.Day{
|
|||||||
create_day(@import("./day18.zig")),
|
create_day(@import("./day18.zig")),
|
||||||
create_day(@import("./day19.zig")),
|
create_day(@import("./day19.zig")),
|
||||||
create_day(@import("./day20.zig")),
|
create_day(@import("./day20.zig")),
|
||||||
|
create_day(@import("./day21.zig")),
|
||||||
};
|
};
|
||||||
|
|
||||||
fn kilobytes(count: u32) u32 {
|
fn kilobytes(count: u32) u32 {
|
||||||
@ -204,4 +205,5 @@ test {
|
|||||||
_ = @import("./day18.zig");
|
_ = @import("./day18.zig");
|
||||||
_ = @import("./day19.zig");
|
_ = @import("./day19.zig");
|
||||||
_ = @import("./day20.zig");
|
_ = @import("./day20.zig");
|
||||||
|
_ = @import("./day21.zig");
|
||||||
}
|
}
|
||||||
|
@ -41,14 +41,14 @@ pub fn Point(comptime T: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_i32(self: Self) Point(i32) {
|
pub fn toI32(self: Self) Point(i32) {
|
||||||
return Point(i32){
|
return Point(i32){
|
||||||
.x = @intCast(self.x),
|
.x = @intCast(self.x),
|
||||||
.y = @intCast(self.y),
|
.y = @intCast(self.y),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_u32(self: Self) Point(u32) {
|
pub fn toU32(self: Self) Point(u32) {
|
||||||
return Point(u32){
|
return Point(u32){
|
||||||
.x = @intCast(self.x),
|
.x = @intCast(self.x),
|
||||||
.y = @intCast(self.y),
|
.y = @intCast(self.y),
|
||||||
|
Loading…
Reference in New Issue
Block a user