|
|
|
@ -1,5 +1,49 @@ |
|
|
|
|
const std = @import("std"); |
|
|
|
|
|
|
|
|
|
fn StackList(comptime T: type, comptime capacity_type: type, comptime capacity: capacity_type) type { |
|
|
|
|
return struct { |
|
|
|
|
const Self = @This(); |
|
|
|
|
mem: [capacity]T, |
|
|
|
|
length: capacity_type, |
|
|
|
|
|
|
|
|
|
fn add(self: *Self, value: T) void { |
|
|
|
|
self.mem[self.length] = value; |
|
|
|
|
self.length += 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn addIfNotNull(self: *Self, nullable_value: ?T) void { |
|
|
|
|
if (nullable_value) |value| { |
|
|
|
|
self.add(value); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn has(self: *Self, needle: T) bool { |
|
|
|
|
for (0..self.length) |i| { |
|
|
|
|
if (self.mem[i] == needle) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn getMutableSlice(self: *Self) []T { |
|
|
|
|
return (&self.mem)[0..self.length]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn getSlice(self: *const Self) []const T { |
|
|
|
|
return self.mem[0..self.length]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn init() Self { |
|
|
|
|
return Self{ |
|
|
|
|
.mem = undefined, |
|
|
|
|
.length = 0, |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn RingQueueWrapper(comptime T: type, comptime size: usize) type { |
|
|
|
|
return struct { |
|
|
|
|
const Self = @This(); |
|
|
|
@ -87,6 +131,18 @@ const FloodTask = struct { |
|
|
|
|
y: TerrainCoordinate, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const Direction = enum(u8) { |
|
|
|
|
Right = 0, |
|
|
|
|
Down = 1, |
|
|
|
|
Left = 2, |
|
|
|
|
Up = 3, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const Command = packed struct(u32) { |
|
|
|
|
direction: Direction, |
|
|
|
|
length: u24, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
fn floodTerrain(terrain: *Terrain) void { |
|
|
|
|
var queue_mem: [65_536]FloodTask = undefined; |
|
|
|
|
var tasks = RingQueueWrapper(FloodTask, queue_mem.len).init(&queue_mem); |
|
|
|
@ -138,34 +194,71 @@ fn solveForTerrain(terrain: *Terrain) usize { |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn dig(terrain: *Terrain, command: []const u8) void { |
|
|
|
|
var index: usize = 2; |
|
|
|
|
const length = readNumber(usize, command, &index); |
|
|
|
|
const direction = command[0]; |
|
|
|
|
|
|
|
|
|
for (0..length) |i| { |
|
|
|
|
_ = i; |
|
|
|
|
terrain.*.x = switch (direction) { |
|
|
|
|
'R' => terrain.*.x + 1, |
|
|
|
|
'L' => terrain.*.x - 1, |
|
|
|
|
else => terrain.*.x, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
terrain.*.y = switch (direction) { |
|
|
|
|
'D' => terrain.*.y + 1, |
|
|
|
|
'U' => terrain.*.y - 1, |
|
|
|
|
else => terrain.*.y, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
//std.debug.print("digging {d},{d}\n", .{ terrain.*.x, terrain.*.y }); |
|
|
|
|
fn dig(terrain: *Terrain, command: Command) void { |
|
|
|
|
const target_x = switch (command.direction) { |
|
|
|
|
.Right => terrain.*.x + command.length, |
|
|
|
|
.Left => terrain.*.x - command.length, |
|
|
|
|
.Up, .Down => terrain.*.x, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const target_y = switch (command.direction) { |
|
|
|
|
.Down => terrain.*.y + command.length, |
|
|
|
|
.Up => terrain.*.y - command.length, |
|
|
|
|
.Left, .Right => terrain.*.y, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
while (terrain.*.x != target_x) { |
|
|
|
|
if (terrain.*.x < target_x) { |
|
|
|
|
terrain.*.x += 1; |
|
|
|
|
} else { |
|
|
|
|
terrain.*.x -= 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
terrain.*.cells[terrain.*.x][terrain.*.y] = .Dug; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
while (terrain.*.y != target_y) { |
|
|
|
|
if (terrain.*.y < target_y) { |
|
|
|
|
terrain.*.y += 1; |
|
|
|
|
} else { |
|
|
|
|
terrain.*.y -= 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
terrain.*.cells[terrain.*.x][terrain.*.y] = .Dug; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn parseCommand(command: []const u8) Command { |
|
|
|
|
var index: usize = 2; |
|
|
|
|
const length = readNumber(u24, command, &index); |
|
|
|
|
const direction: Direction = switch (command[0]) { |
|
|
|
|
'R' => .Right, |
|
|
|
|
'L' => .Left, |
|
|
|
|
'U' => .Up, |
|
|
|
|
'D' => .Down, |
|
|
|
|
else => unreachable, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return .{ |
|
|
|
|
.length = length, |
|
|
|
|
.direction = direction, |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn solveForCommands(commands: []const Command) usize { |
|
|
|
|
var terrain = Terrain.initZeroes(); |
|
|
|
|
|
|
|
|
|
for (commands) |command| { |
|
|
|
|
dig(&terrain, command); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return solveForTerrain(&terrain); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn solveAll(reader: anytype) !usize { |
|
|
|
|
var result: usize = 0; |
|
|
|
|
while (true) { |
|
|
|
|
var terrain = Terrain.initZeroes(); |
|
|
|
|
var commands = StackList(Command, usize, 1024).init(); |
|
|
|
|
|
|
|
|
|
var empty_line_reached = false; |
|
|
|
|
var line_buffer: [1000]u8 = undefined; |
|
|
|
@ -174,10 +267,10 @@ fn solveAll(reader: anytype) !usize { |
|
|
|
|
empty_line_reached = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
dig(&terrain, line); |
|
|
|
|
commands.add(parseCommand(line)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
result += solveForTerrain(&terrain); |
|
|
|
|
result += solveForCommands(commands.getSlice()); |
|
|
|
|
|
|
|
|
|
if (!empty_line_reached) { |
|
|
|
|
return result; |
|
|
|
|