diff --git a/day17-easy/src/main.zig b/day17-easy/src/main.zig index 15af1f7..95bf12a 100644 --- a/day17-easy/src/main.zig +++ b/day17-easy/src/main.zig @@ -44,67 +44,128 @@ fn StackList(comptime T: type, comptime capacity_type: type, comptime capacity: }; } -const Direction = enum(u2) { - Up, - Down, - Left, - Right, +fn RingQueue(comptime T: type, comptime capacity_type: type, comptime capacity: capacity_type) type { + return struct { + const Self = @This(); + mem: [capacity]T, + first: capacity_type, + next: capacity_type, + + fn add(self: *Self, value: T) void { + std.debug.assert(self.next - self.first < capacity); + self.mem[self.next % capacity] = value; + self.next += 1; + } + + fn take(self: *Self) T { + const result = self.mem[self.first % capacity]; + self.first += 1; + return result; + } + + fn isEmpty(self: *const Self) bool { + return self.next == self.first; + } + + fn init() Self { + return Self{ + .mem = undefined, + .first = 0, + .next = 0, + }; + } + }; +} + +const Direction = enum(u8) { + Horizontal, + Vertical, }; const ResultsByDirection = std.EnumArray(Direction, u32); const Results = [150][150]ResultsByDirection; -const NextCellCoordinates = StackList([2]usize, usize, 3); +const Task = packed struct(u48) { + current_heat: u16, + x: u8, + y: u8, + entry_direction: Direction, + _: u8 = 0, +}; + +const NextTasks = StackList(Task, usize, 6); + +fn getNextTasks(board: []const []const u8, task: Task) NextTasks { + var result = NextTasks.init(); + + const previous_direction: Direction = switch (task.entry_direction) { + .Vertical => .Horizontal, + .Horizontal => .Vertical, + }; -fn fillResults(board: []const []const u8, results: *Results, x: usize, y: usize, entry_direction: Direction, depth: usize) void { - //std.debug.print("Solving for {d},{d}, {any}, depth: {d}, total: {d}\n", .{ x, y, entry_direction, depth, results.*[x][y].get(entry_direction) }); - var previous_cells_list = StackList([2]usize, usize, 3).init(); - switch (entry_direction) { - .Up => { - for (1..4) |delta| { - if (x + delta < board.len) { - previous_cells_list.add(.{ x + delta, y }); + { + var previous_cells_list = StackList([2]u8, usize, 3).init(); + switch (task.entry_direction) { + .Vertical => { + inline for (1..4) |delta| { + if (task.x + delta < board.len) { + previous_cells_list.add(.{ task.x + @as(u8, delta), task.y }); + } } - } - }, - .Down => { - for (1..4) |delta| { - if (x >= delta) { - previous_cells_list.add(.{ x - delta, y }); + }, + .Horizontal => { + inline for (1..4) |delta| { + if (task.y + delta < board[task.x].len) { + previous_cells_list.add(.{ task.x, task.y + @as(u8, delta) }); + } } - } - }, - .Left => { - for (1..4) |delta| { - if (y + delta < board[x].len) { - previous_cells_list.add(.{ x, y + delta }); + }, + } + var current_heat = task.current_heat; + for (previous_cells_list.getSlice()) |previous_cell| { + current_heat += board[previous_cell[0]][previous_cell[1]]; + result.add(.{ + .current_heat = current_heat, + .x = previous_cell[0], + .y = previous_cell[1], + .entry_direction = previous_direction, + }); + } + } + + { + var previous_cells_list = StackList([2]u8, usize, 3).init(); + switch (task.entry_direction) { + .Vertical => { + inline for (1..4) |delta| { + if (task.x >= delta) { + previous_cells_list.add(.{ task.x - @as(u8, delta), task.y }); + } } - } - }, - .Right => { - for (1..4) |delta| { - if (y >= delta) { - previous_cells_list.add(.{ x, y - delta }); + }, + .Horizontal => { + inline for (1..4) |delta| { + if (task.y >= delta) { + previous_cells_list.add(.{ task.x, task.y - @as(u8, delta) }); + } } - } - }, + }, + } + var current_heat = task.current_heat; + for (previous_cells_list.getSlice()) |previous_cell| { + current_heat += board[previous_cell[0]][previous_cell[1]]; + result.add(.{ + .current_heat = current_heat, + .x = previous_cell[0], + .y = previous_cell[1], + .entry_direction = previous_direction, + }); + } } - const previous_directions: [2]Direction = switch (entry_direction) { - .Up, .Down => .{ .Left, .Right }, - .Left, .Right => .{ .Up, .Down }, - }; + //std.debug.print("Next tasks for {any}: {any}\n", .{ task, result.getSlice() }); - var current_heat = results.*[x][y].get(entry_direction); - for (previous_cells_list.getSlice()) |previous_cell| { - current_heat += board[previous_cell[0]][previous_cell[1]]; - for (previous_directions) |direction| { - if (results.*[previous_cell[0]][previous_cell[1]].get(direction) > current_heat) { - results.*[previous_cell[0]][previous_cell[1]].set(direction, current_heat); - fillResults(board, results, previous_cell[0], previous_cell[1], direction, depth + 1); - } - } - } + return result; } fn solveLines(lines: [][]u8) usize { @@ -116,26 +177,47 @@ fn solveLines(lines: [][]u8) usize { } } - const x_max = lines.len - 1; - const y_max = lines[x_max].len - 1; + const x_max = @as(u8, @intCast(lines.len - 1)); + const y_max = @as(u8, @intCast(lines[x_max].len - 1)); var results: Results = [_][150]ResultsByDirection{[_]ResultsByDirection{ResultsByDirection.initFill(total_max)} ** 150} ** 150; - results[x_max][y_max].set(.Down, lines[x_max][y_max]); - results[x_max][y_max].set(.Right, lines[x_max][y_max]); + var tasks = RingQueue(Task, usize, 1_024_576).init(); + tasks.add(.{ + .current_heat = lines[x_max][y_max], + .x = x_max, + .y = y_max, + .entry_direction = .Horizontal, + }); + tasks.add(.{ + .current_heat = lines[x_max][y_max], + .x = x_max, + .y = y_max, + .entry_direction = .Vertical, + }); - fillResults(lines, &results, x_max, y_max, .Down, 0); - fillResults(lines, &results, x_max, y_max, .Right, 0); + var i: usize = 0; + while (!tasks.isEmpty()) : (i += 1) { + //std.debug.print("Handling task {d}\n", .{i}); + var current_task = tasks.take(); + if (results[current_task.x][current_task.y].get(current_task.entry_direction) > current_task.current_heat) { + results[current_task.x][current_task.y].set(current_task.entry_direction, current_task.current_heat); + for (getNextTasks(lines, current_task).getSlice()) |next_task| { + tasks.add(next_task); + } + } + } if (false) { + std.debug.print("Total tasks done: {d}\n", .{i}); for (0..lines.len) |x| { for (0..lines[x].len) |y| { - std.debug.print("{d} ", .{@min(@min(results[x][y].get(.Up), results[x][y].get(.Down)), @min(results[x][y].get(.Left), results[x][y].get(.Right)))}); + std.debug.print("{d}/{d} ", .{ results[x][y].get(.Horizontal), results[x][y].get(.Vertical) }); } std.debug.print("\n", .{}); } } - return results[0][0].get(.Down) - lines[0][0]; + return results[0][0].get(.Vertical) - lines[0][0]; } pub fn solveAll(reader: anytype) !usize {