1
0
Advent-of-Code/day15/part2.zig
2024-12-15 20:39:38 +01:00

193 lines
6.6 KiB
Zig

const std = @import("std");
const print = std.debug.print;
const input = @embedFile("input");
const MAPW = 50 * 2;
const MAPH = 50;
const Cell = enum { Empty, BoxL, BoxR, Wall, Robot };
const Direction = enum { Top, Bot, Left, Right };
const Position = struct { x: usize, y: usize };
pub fn main() !void {
var map: [MAPH][MAPW]Cell = undefined;
var robot_position: Position = undefined;
for (input[0 .. MAPH * (MAPH + 1)], 0..) |c, i| {
if (c == '\n') continue;
const x = @divFloor(i, MAPH + 1);
const y = (i % (MAPH + 1)) * 2;
map[x][y] = switch (c) {
'.' => .Empty,
'#' => .Wall,
'O' => .BoxL,
'@' => .Robot,
else => unreachable,
};
map[x][y + 1] = switch (c) {
'.', '@' => .Empty,
'#' => .Wall,
'O' => .BoxR,
else => unreachable,
};
if (c == '@') robot_position = Position{ .x = x, .y = y };
}
for (input[MAPH * (MAPH + 1) ..]) |c| switch (c) {
'>' => next(&map, &robot_position, .Right),
'<' => next(&map, &robot_position, .Left),
'^' => next(&map, &robot_position, .Top),
'v' => next(&map, &robot_position, .Bot),
else => continue,
};
try std.testing.expectEqual(1521952, calculateSumPosition(map));
}
// The copy paste suck but if I want to use -1 to do dx dy, I need to use something else than usize and it fuck up the indexing
fn next(map: *[MAPH][MAPW]Cell, robot_position: *Position, direction: Direction) void {
const x = robot_position.x;
const y = robot_position.y;
switch (direction) {
.Left => switch (map[x][y - 1]) {
.Empty => {
map[x][y] = .Empty;
map[x][y - 1] = .Robot;
robot_position.*.y -= 1;
},
.Wall => {},
.BoxR, .BoxL => if (moveLeftRight(map, robot_position.*, direction)) {
map[x][y - 1] = .Robot;
map[x][y] = .Empty;
robot_position.*.y -= 1;
},
else => unreachable,
},
.Right => switch (map[x][y + 1]) {
.Empty => {
map[x][y] = .Empty;
map[x][y + 1] = .Robot;
robot_position.*.y += 1;
},
.Wall => {},
.BoxR, .BoxL => if (moveLeftRight(map, robot_position.*, direction)) {
map[x][y + 1] = .Robot;
map[x][y] = .Empty;
robot_position.*.y += 1;
},
else => unreachable,
},
.Top => switch (map[x - 1][y]) {
.Empty => {
map[x][y] = .Empty;
map[x - 1][y] = .Robot;
robot_position.*.x -= 1;
},
.Wall => {},
.BoxL, .BoxR => if (moveTopBot(map, robot_position.*, direction)) {
map[x - 1][y] = .Robot;
map[x][y] = .Empty;
robot_position.*.x -= 1;
},
else => unreachable,
},
.Bot => switch (map[x + 1][y]) {
.Empty => {
map[x][y] = .Empty;
map[x + 1][y] = .Robot;
robot_position.*.x += 1;
},
.Wall => {},
.BoxL, .BoxR => if (moveTopBot(map, robot_position.*, direction)) {
map[x + 1][y] = .Robot;
map[x][y] = .Empty;
robot_position.*.x += 1;
},
else => unreachable,
},
}
}
fn moveLeftRight(map: *[MAPH][MAPW]Cell, start: Position, direction: Direction) bool {
if (direction == .Top or direction == .Bot) @panic("Nope");
var y = start.y;
var is_right = direction == .Right;
if (direction == .Left) y -= 1;
if (direction == .Right) y += 1;
while (y != 0 and y != MAPW - 1) : ({
if (direction == .Left) y -= 1;
if (direction == .Right) y += 1;
}) {
switch (map[start.x][y]) {
.Empty => {
while (y != start.y) : ({
if (direction == .Left) y += 1;
if (direction == .Right) y -= 1;
is_right = !is_right;
}) {
map.*[start.x][y] = if (is_right) .BoxR else .BoxL;
}
return true;
},
.BoxR, .BoxL => continue,
.Wall => return false,
.Robot => unreachable,
}
}
return false;
}
fn canMoveTopBot(map: [MAPH][MAPW]Cell, start: Position, direction: Direction) bool {
if (direction == .Right or direction == .Left) @panic("Nope");
var x = start.x;
if (direction == .Top) x -= 1;
if (direction == .Bot) x += 1;
switch (map[x][start.y]) {
.Empty => return true,
.BoxR => return canMoveTopBot(map, Position{ .x = x, .y = start.y }, direction) and canMoveTopBot(map, Position{ .x = x, .y = start.y - 1 }, direction),
.BoxL => return canMoveTopBot(map, Position{ .x = x, .y = start.y }, direction) and canMoveTopBot(map, Position{ .x = x, .y = start.y + 1 }, direction),
.Wall => return false,
.Robot => unreachable,
}
}
fn moveTopBot(map: *[MAPH][MAPW]Cell, start: Position, direction: Direction) bool {
if (direction == .Right or direction == .Left) @panic("Nope");
if (!canMoveTopBot(map.*, start, direction)) return false;
const x = if (direction == .Top) start.x - 1 else start.x + 1;
switch (map[x][start.y]) {
.Empty => return true,
.BoxR => if (moveTopBot(map, Position{ .x = x, .y = start.y }, direction) and moveTopBot(map, Position{ .x = x, .y = start.y - 1 }, direction)) {
map.*[if (direction == .Top) x - 1 else x + 1][start.y] = .BoxR;
map.*[if (direction == .Top) x - 1 else x + 1][start.y - 1] = .BoxL;
map.*[x][start.y] = .Empty;
map.*[x][start.y - 1] = .Empty;
return true;
} else return false,
.BoxL => if (moveTopBot(map, Position{ .x = x, .y = start.y }, direction) and moveTopBot(map, Position{ .x = x, .y = start.y + 1 }, direction)) {
map.*[if (direction == .Top) x - 1 else x + 1][start.y] = .BoxL;
map.*[if (direction == .Top) x - 1 else x + 1][start.y + 1] = .BoxR;
map.*[x][start.y] = .Empty;
map.*[x][start.y + 1] = .Empty;
return true;
} else return false,
.Wall => return false,
.Robot => unreachable,
}
}
fn calculateSumPosition(map: [MAPH][MAPW]Cell) usize {
var total: usize = 0;
for (map, 0..) |row, x| for (row, 0..) |cell, y| switch (cell) {
.BoxL => total += x * 100 + y,
else => {},
};
return total;
}