Skip to content

Instantly share code, notes, and snippets.

@ndbn

ndbn/15.zig Secret

Last active December 15, 2024 12:43
Show Gist options
  • Save ndbn/a2eb9d1059147a10722470b24858d9bf to your computer and use it in GitHub Desktop.
Save ndbn/a2eb9d1059147a10722470b24858d9bf to your computer and use it in GitHub Desktop.
AoC 2024 Day 15
const std = @import("std");
const input_file_name = "inputs/15.txt";
const input_data = blk: {
@setEvalBranchQuota(300_000);
var result: []const []const u8 = &.{};
const input_file = @embedFile(input_file_name);
var token = std.mem.splitSequence(u8, input_file, "\r\n");
while (token.next()) |val| {
result = result ++ [1][]const u8{val};
}
break :blk result;
};
const rows = input_data.len;
const cols = input_data[0].len;
//
const divider = blk: {
@setEvalBranchQuota(300_000);
for (input_data, 0..) |data, i| {
if (data.len == 0) break :blk i;
}
unreachable;
};
const MOVES = input_data[divider + 1 ..];
const MOVES_ROWS = MOVES.len;
const MOVES_COLS = MOVES[0].len;
const Vec2 = struct {
r: isize,
c: isize,
pub fn add(self: Vec2, drdc: [2]i8) Vec2 {
return .{
.r = self.r + drdc[0],
.c = self.c + drdc[1],
};
}
pub fn left(self: Vec2) Vec2 {
return .{
.r = self.r,
.c = self.c - 1,
};
}
pub fn right(self: Vec2) Vec2 {
return .{
.r = self.r,
.c = self.c + 1,
};
}
};
const Move = [2]i8;
fn charToMove(c: u8) Move {
return switch (c) {
'>' => .{ 0, 1 },
'<' => .{ 0, -1 },
'^' => .{ -1, 0 },
'v' => .{ 1, 0 },
else => unreachable,
};
}
fn writeMap(map: anytype, pos: Vec2, c: u8) void {
map[@intCast(pos.r)][@intCast(pos.c)] = c;
}
fn readMap(map: anytype, pos: Vec2) u8 {
return map[@intCast(pos.r)][@intCast(pos.c)];
}
fn ptrMap(map: anytype, pos: Vec2) *u8 {
return &map[@intCast(pos.r)][@intCast(pos.c)];
}
fn swapByteMap(map: anytype, pos1: Vec2, pos2: Vec2) void {
std.mem.swap(u8, ptrMap(map, pos1), ptrMap(map, pos2));
}
fn part1() !isize {
var result: isize = 0;
result = 0;
//
var MAP: [divider][cols]u8 = undefined;
for (input_data[0..divider], 0..) |line, i| {
MAP[i] = line[0..cols].*;
}
const START: Vec2 = blk2: {
for (0..MAP.len) |r| {
for (0..MAP[r].len) |c| {
if (MAP[r][c] == '@') {
break :blk2 .{ .r = @intCast(r), .c = @intCast(c) };
}
}
}
unreachable;
};
var robot_coord: Vec2 = START;
for (0..MOVES_ROWS) |mr| {
for (0..MOVES_COLS) |mc| {
// read move instruction
const dmove: [2]i8 = charToMove(MOVES[mr][mc]);
// Calc next coordinates
const next_coord = robot_coord.add(dmove);
// MAP char at new
const b = readMap(MAP, next_coord);
if (b == '.') {
// move robot
swapByteMap(&MAP, robot_coord, next_coord);
robot_coord = next_coord;
} else if (b == '#') {
// nothing to do, it's a wall
} else if (b == 'O') {
var times: u32 = 0;
var slide_coord = next_coord;
while (readMap(MAP, slide_coord) == 'O') {
times += 1;
slide_coord = slide_coord.add(dmove);
}
const what_after_boxes = readMap(MAP, slide_coord);
if (what_after_boxes == '.') {
// can move package of boxes
const reversed_dmove = .{ dmove[0] * -1, dmove[1] * -1 };
var caret = slide_coord;
times += 1; // add 1 for robot moves also
while (times > 0) : (times -= 1) {
const prev_caret = caret.add(reversed_dmove);
swapByteMap(&MAP, caret, prev_caret);
caret = prev_caret; //move caret
}
robot_coord = caret.add(dmove); //new robot coordinates
}
}
}
}
//print map / calc result
for (0..MAP.len) |r| {
// std.debug.print("{s}\n", .{MAP[r]}); // print map
for (MAP[r], 0..) |b, c| {
if (b == 'O') {
const r2: u32 = @intCast(r);
const c2: u32 = @intCast(c);
result += 100 * r2 + c2;
}
}
}
return result;
}
fn part2() !isize {
var result: isize = 0;
result = 0;
//
// change map
var MAP: [divider][cols * 2]u8 = undefined;
for (input_data[0..divider], 0..) |line, i| {
for (line[0..cols], 0..) |c, j| {
if (c == '#') {
MAP[i][2 * j] = '#';
MAP[i][2 * j + 1] = '#';
} else if (c == 'O') {
MAP[i][2 * j] = '[';
MAP[i][2 * j + 1] = ']';
} else if (c == '.') {
MAP[i][2 * j] = '.';
MAP[i][2 * j + 1] = '.';
} else if (c == '@') {
MAP[i][2 * j] = '@';
MAP[i][2 * j + 1] = '.';
}
}
}
const START: Vec2 = blk2: {
for (0..MAP.len) |r| {
for (0..MAP[r].len) |c| {
if (MAP[r][c] == '@') {
break :blk2 .{ .r = @intCast(r), .c = @intCast(c) };
}
}
}
unreachable;
};
var robot_coord: Vec2 = START;
for (0..MOVES_ROWS) |mr| {
for (0..MOVES_COLS) |mc| {
// Debug
// for (0..MAP.len) |r| {
// std.debug.print("{c}: {s}\n", .{ MOVES[mr][mc], MAP[r] }); // print map
// }
// while (true) {
// const stdin = std.io.getStdIn().reader();
// const b = try stdin.readByte();
// if (b == '\n') break;
// }
// read move instruction
const dmove: [2]i8 = charToMove(MOVES[mr][mc]);
// Calc next coordinates
const next_coord = robot_coord.add(dmove);
// MAP char at new
const b = readMap(&MAP, next_coord);
if (b == '.') {
// move robot
swapByteMap(&MAP, robot_coord, next_coord);
robot_coord = next_coord;
} else if (b == '#') {
// nothing to do, it's a wall
} else if (b == '[' or b == ']') {
if (dmove[0] == 0) { // left or right - stright line, work same as part1
var times: u32 = 0;
var slide_coord = next_coord;
while (true) {
const c = readMap(&MAP, slide_coord);
if (!(c == ']' or c == '[')) break;
times += 1;
slide_coord = slide_coord.add(dmove);
}
const what_after_boxes = readMap(&MAP, slide_coord);
if (what_after_boxes == '.') {
// can move package of boxes
const reversed_dmove = .{ dmove[0] * -1, dmove[1] * -1 };
var caret = slide_coord;
times += 1; // add 1 for robot moves also
while (times > 0) : (times -= 1) {
const prev_caret = caret.add(reversed_dmove);
swapByteMap(&MAP, caret, prev_caret);
caret = prev_caret; //move caret
}
robot_coord = caret.add(dmove); //new robot coordinates
}
} else {
// up and down moves
const MapType = @TypeOf(MAP);
const check_map = struct {
pub fn check_map_fn(map: *MapType, pos: Vec2, dir: [2]i8) bool {
const c = readMap(map, pos);
if (c == '.') return true;
if (c == '#') return false;
if (c == '[') return check_map_fn(map, pos.right().add(dir), dir) and check_map_fn(map, pos.add(dir), dir);
if (c == ']') return check_map_fn(map, pos.left().add(dir), dir) and check_map_fn(map, pos.add(dir), dir);
if (c == '@') return false;
// std.debug.print("unreachable {c}\n", .{c});
unreachable;
}
}.check_map_fn;
const move_map = struct {
pub fn move_map_fn(map: *MapType, pos: Vec2, dir: [2]i8, second_part: bool) bool {
const c = readMap(map, pos);
if (c == '.') {
const reversed_dir = .{ dir[0] * -1, dir[1] * -1 };
const prev_pos = pos.add(reversed_dir);
swapByteMap(map, pos, prev_pos);
return true;
}
if (c == '#') return false;
if (c == '[') return move_map_fn(map, pos.add(dir), dir, second_part) and move_map_fn(map, pos.right(), dir, second_part);
if (c == ']') return move_map_fn(map, pos.add(dir), dir, second_part) and move_map_fn(map, pos.left(), dir, second_part);
unreachable;
}
}.move_map_fn;
const next_map_coord = robot_coord.add(dmove);
if (check_map(&MAP, next_map_coord, dmove)) {
_ = move_map(&MAP, next_map_coord, dmove, false);
robot_coord = robot_coord.add(dmove);
}
}
}
}
}
//print map / calc result
for (0..MAP.len) |r| {
// std.debug.print("{s}\n", .{MAP[r]}); // print map
for (MAP[r], 0..) |b, c| {
if (b == '[') {
const r2: u32 = @intCast(r);
const c2: u32 = @intCast(c);
result += 100 * r2 + c2;
}
}
}
return result;
}
pub fn main() !void {
var timer = try std.time.Timer.start();
const part1_result = try part1();
std.debug.print("part 1 {d} {d}ms\n", .{ part1_result, timer.lap() / std.time.ns_per_ms });
const part2_result = try part2();
std.debug.print("part 2 {d} {d}ms\n", .{ part2_result, timer.lap() / std.time.ns_per_ms });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment