193 lines
6.6 KiB
Zig
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;
|
|
}
|