Watch
1
0
Fork
You've already forked advent-of-code
0

2024 Day 21 Part 1

This commit is contained in:
Jeremy Kaplan 2024-12-24 20:13:07 -08:00
commit 4015e46237

462
2024/src/day21.zig Normal file
View file

@ -0,0 +1,462 @@
const std = @import("std");
const aoc = @import("aoc.zig");
const Allocator = std.mem.Allocator;
const List = std.ArrayList;
const Map = std.AutoHashMap;
const Set = aoc.AutoHashSet;
const PriorityQueue = std.PriorityQueue;
pub fn main() !void {
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .{};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const text = try aoc.readAll(allocator, "input/day21.txt");
defer allocator.free(text);
const codes = try aoc.splitAll(allocator, text, "\n");
defer codes.deinit();
var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
const stdout = bw.writer();
try stdout.print("{}\n", .{try part1(allocator, codes.items)});
try bw.flush();
}
const String = []const u8;
fn part1(allocator: Allocator, codes: []String) !u64 {
var sum: u64 = 0;
for (codes) |code| {
const presses = try solve(allocator, code);
sum += complexity(code, @intCast(presses));
}
return sum;
}
fn complexity(code: String, presses: u64) u64 {
const a, const n = takeInt(code).?;
if (!std.mem.eql(u8, a, "A")) unreachable;
return n * presses;
}
fn takeInt(input: []const u8) ?struct { []const u8, u64 } {
var i: usize = 0;
var n: u64 = 0;
while (i < input.len and std.ascii.isDigit(input[i])) : (i += 1) {
n *= 10;
n += input[i] - '0';
}
if (i == 0) {
// Nothing consumed
return null;
}
return .{ input[i..], n };
}
fn solve(allocator: Allocator, code: String) !usize {
// Step 0: The actual passcode (numpad)
var digits = List(Numpad).init(allocator);
defer digits.deinit();
for (code) |c| try digits.append(Numpad.parse(c));
var total: usize = 0;
var start = Numpad.a;
for (digits.items) |next| {
total += try digitCost(allocator, start, next);
start = next;
}
return total;
}
fn digitCost(allocator: Allocator, start: Numpad, end: Numpad) !usize {
var best: usize = std.math.maxInt(usize);
const digits = [_]Numpad{end};
// Step 1: Depressurized (dpad)
var robot1 = try numpadToDpad(allocator, digits[0..], start);
defer robot1.deinit();
while (try robot1.next()) |r1| {
defer r1.deinit();
// Step 2: High radiation (dpad)
var robot2 = try dpadToDpad(allocator, r1.items, Dpad.a);
defer robot2.deinit();
while (try robot2.next()) |r2| {
defer r2.deinit();
// Step 3: Very cold (dpad)
var robot3 = try dpadToDpad(allocator, r2.items, Dpad.a);
defer robot3.deinit();
while (try robot3.next()) |r3| {
defer r3.deinit();
// Step 4: Direct manipulation (no cost!)
if (r3.items.len < best) {
best = r3.items.len;
}
}
}
}
return best;
}
fn numpadToDpad(allocator: Allocator, output: []const Numpad, start: Numpad) !ButtonIterator(Numpad, Dpad) {
return try ButtonIterator(Numpad, Dpad).init(allocator, output, start);
}
fn dpadToDpad(allocator: Allocator, output: []Dpad, start: Dpad) !ButtonIterator(Dpad, Dpad) {
return try ButtonIterator(Dpad, Dpad).init(allocator, output, start);
}
fn ButtonIterator(comptime Target: type, comptime Input: type) type {
return struct {
allocator: Allocator,
stack: List(State),
const Self = @This();
const State = struct {
prev: Target,
buttons: []const Target,
inputs: List(Input),
fn deinit(self: *State) void {
self.inputs.deinit();
}
};
fn init(allocator: Allocator, buttons: []const Target, start: Target) !Self {
var stack = List(State).init(allocator);
try stack.append(State{
.prev = start,
.buttons = buttons,
.inputs = List(Input).init(allocator),
});
return Self{
.allocator = allocator,
.stack = stack,
};
}
fn deinit(self: *Self) void {
for (self.stack.items) |*s| s.deinit();
self.stack.deinit();
}
fn next(self: *Self) !?List(Input) {
while (self.stack.popOrNull()) |state| {
const buttons, var inputs = .{ state.buttons, state.inputs };
if (buttons.len == 0) {
return inputs;
}
defer inputs.deinit();
const start = state.prev;
const btn = buttons[0];
const delta = btn.pos().sub(start.pos());
var moves = try arrows(self.allocator, delta);
defer moves.deinit();
while (try moves.next()) |inp| {
defer inp.deinit();
var ii = try inputs.clone();
// Move to button (arrows)
var pos = start.pos();
for (inp.items) |i| {
pos = pos.add(i.delta());
if (!Target.valid(pos)) {
ii.deinit();
break;
}
try ii.append(i);
} else {
// Press button (A)
try ii.append(Input.a);
try self.stack.append(State{
.prev = btn,
.buttons = buttons[1..],
.inputs = ii,
});
}
}
}
return null;
}
};
}
fn arrows(allocator: Allocator, delta: Delta) !MovesIterator {
return try MovesIterator.init(allocator, delta);
}
const MovesIterator = struct {
allocator: Allocator,
stack: List(State),
const Self = @This();
const State = struct {
delta: Delta,
moves: List(Dpad),
fn deinit(self: *State) void {
self.moves.deinit();
}
};
fn init(allocator: Allocator, delta: Delta) !Self {
var stack = List(State).init(allocator);
try stack.append(State{
.delta = delta,
.moves = List(Dpad).init(allocator),
});
return Self{
.allocator = allocator,
.stack = stack,
};
}
fn deinit(self: *Self) void {
for (self.stack.items) |*s| s.deinit();
self.stack.deinit();
}
fn next(self: *Self) !?List(Dpad) {
while (self.stack.popOrNull()) |state| {
const delta, var moves = .{ state.delta, state.moves };
const dr, const dc = .{ delta.dr, delta.dc };
if (dr == 0 and dc == 0) {
return moves;
}
defer moves.deinit();
if (dr < 0) {
var m = try moves.clone();
try appendMulti(Dpad, &m, Dpad.up, @intCast(@abs(dr)));
try self.stack.append(State{
.delta = Delta.new(0, dc),
.moves = m,
});
}
if (dr > 0) {
var m = try moves.clone();
try appendMulti(Dpad, &m, Dpad.down, @intCast(@abs(dr)));
try self.stack.append(State{
.delta = Delta.new(0, dc),
.moves = m,
});
}
if (dc < 0) {
var m = try moves.clone();
try appendMulti(Dpad, &m, Dpad.left, @intCast(@abs(dc)));
try self.stack.append(State{
.delta = Delta.new(dr, 0),
.moves = m,
});
}
if (dc > 0) {
var m = try moves.clone();
try appendMulti(Dpad, &m, Dpad.right, @intCast(@abs(dc)));
try self.stack.append(State{
.delta = Delta.new(dr, 0),
.moves = m,
});
}
}
return null;
}
};
fn appendMulti(comptime T: type, l: *List(T), elt: T, n: usize) !void {
for (0..n) |_| {
try l.append(elt);
}
}
const Numpad = enum {
one,
two,
three,
four,
five,
six,
seven,
eight,
nine,
zero,
a,
const Self = @This();
fn pos(self: Self) Pos {
return switch (self) {
.seven => Pos.new(0, 0),
.eight => Pos.new(0, 1),
.nine => Pos.new(0, 2),
.four => Pos.new(1, 0),
.five => Pos.new(1, 1),
.six => Pos.new(1, 2),
.one => Pos.new(2, 0),
.two => Pos.new(2, 1),
.three => Pos.new(2, 2),
// empty at (3, 0)
.zero => Pos.new(3, 1),
.a => Pos.new(3, 2),
};
}
fn valid(p: Pos) bool {
return !(p.r == 3 and p.c == 0);
}
fn parse(c: u8) Self {
return switch (c) {
'1' => .one,
'2' => .two,
'3' => .three,
'4' => .four,
'5' => .five,
'6' => .six,
'7' => .seven,
'8' => .eight,
'9' => .nine,
'0' => .zero,
'A' => .a,
else => unreachable,
};
}
pub fn format(
self: Numpad,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
const c: u8 = switch (self) {
.one => '1',
.two => '2',
.three => '3',
.four => '4',
.five => '5',
.six => '6',
.seven => '7',
.eight => '8',
.nine => '9',
.zero => '0',
.a => 'A',
};
try writer.print("{c}", .{c});
}
};
const Dpad = enum {
up,
down,
left,
right,
a,
pub fn pos(self: Dpad) Pos {
return switch (self) {
// empty at (0, 0)
.up => Pos.new(0, 1),
.a => Pos.new(0, 2),
.left => Pos.new(1, 0),
.down => Pos.new(1, 1),
.right => Pos.new(1, 2),
};
}
fn valid(p: Pos) bool {
return !(p.r == 0 and p.c == 0);
}
pub fn delta(self: Dpad) Delta {
return switch (self) {
.up => Delta.new(-1, 0),
.down => Delta.new(1, 0),
.left => Delta.new(0, -1),
.right => Delta.new(0, 1),
.a => Delta.new(0, 0),
};
}
pub fn format(
self: Dpad,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
const c: u8 = switch (self) {
.up => '^',
.down => 'v',
.left => '<',
.right => '>',
.a => 'A',
};
try writer.print("{c}", .{c});
}
};
const Pos = struct {
r: i32,
c: i32,
fn new(r: i32, c: i32) Pos {
return Pos{ .r = r, .c = c };
}
fn add(self: Pos, d: Delta) Pos {
return .{
.r = self.r + d.dr,
.c = self.c + d.dc,
};
}
fn sub(self: Pos, other: Pos) Delta {
return .{
.dr = self.r - other.r,
.dc = self.c - other.c,
};
}
pub fn format(
self: Pos,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
try writer.print("({d}, {d})", .{ self.r, self.c });
}
};
const Delta = struct {
dr: i32,
dc: i32,
fn new(dr: i32, dc: i32) Delta {
return Delta{ .dr = dr, .dc = dc };
}
};