solve_day16 :: (test: bool) {
contents := read_entire_file(ifx test then "inputs/day16_test.txt" else "inputs/day16.txt");
lines := split(contents, cast(u8) #char "\n");
valves: [..]Valve;
valve_table: Table(u16, s64);
for line: lines {
name := slice(line, 6, 2);
valve_id := (cast(u16)name[0]) << 8 | name[1];
valve_index, found := table_find(*valve_table, valve_id);
if !found {
valve_index = add_valve(valve_id, *valves);
table_set(*valve_table, valve_id, valve_index);
}
equals_index := find_index_from_left(line, cast(u8) #char "=",);
semicolon_index := find_index_from_left(line, cast(u8) #char ";");
valves[valve_index].flow = string_to_int(slice(line, equals_index + 1, semicolon_index - equals_index - 1), 10, s64);
valve_split := split(line, "valve");
space_split := split(valve_split[1], cast(u8) #char " ");
for space_split {
if it.count != 2 && it.count != 3 continue;
connected_valve_id := (cast(u16)it[0]) << 8 | it[1];
connected_valve_index, connected_found := table_find(*valve_table, connected_valve_id);
connected_valve: *Valve;
if connected_found {
connected_valve = *valves[connected_valve_index];
} else {
connected_valve_index = add_valve(connected_valve_id, *valves);
connected_valve = *valves[connected_valve_index];
table_set(*valve_table, connected_valve_id, connected_valve_index);
}
table_set(*valves[valve_index].connections, connected_valve_id, connected_valve_index);
}
}
assert(valve_table.count == lines.count);
aa_index, ff := table_find(*valve_table, (cast(u16)65) << 8 | 65);
for *valve_1, valve_1_index: valves {
if valve_1.flow == 0 && valve_1_index != aa_index continue;
for valve_2_index: valve_1_index + 1..valves.count - 1 {
valve_2 := *valves[valve_2_index];
if valve_2.flow == 0 continue;
distance := find_shortest_route(valve_1_index, valve_2_index, valves);
array_add(*valve_1.compressed, .{ valve_2_index, distance });
array_add(*valve_2.compressed, .{ valve_1_index, distance });
}
}
print("Part 1: %\n", find_best_pressure_relief(valves, 30, 0, aa_index));
part2 := 0;
first_run, first_visited := find_best_pressure_relief(valves, 26, 0, aa_index);
second_run := find_best_pressure_relief(valves, 26, first_visited, aa_index);
print("Part 2: %, % %\n", first_run + second_run, first_run, second_run);
}
find_best_pressure_relief :: (valves: [..]Valve, time_left: s64, already_opened: u64, start_index: s64) -> s64, u64 {
deque: Deque(struct { index: s64; time_left: s64; visited: u64; pressure_relief: s64; history: [..]History; });
resize(*deque, 400_000);
defer deinit(*deque);
pressure_max := 0;
visited: u64 = 0;
start: [..]History;
deque_add_last(*deque, .{ start_index, time_left, (cast(u64)1) << start_index, 0, start });
best_fl := 0;
while deque.count > 0 {
item := deque_remove_first(*deque);
ff := item.history.count > 7 && cast(string) to_name(valves[item.history[6].index].name) == "FL" && cast(string) to_name(valves[item.history[7].index].name) == "EG";
for valves[item.index].compressed {
if item.visited & ((cast(u64)1) << it.valve_index) != 0 continue;
if item.time_left >= 0 {
visited_now := item.visited | ((cast(u64)1) << it.valve_index);
time_left_now := item.time_left - 1 - it.time;
pressure_relief := item.pressure_relief;
if already_opened & ((cast(u64)1) << it.valve_index) == 0 pressure_relief += valves[it.valve_index].flow * time_left_now;
duped: [..]History;
array_copy(*duped, item.history);
array_add(*duped, .{ it.valve_index, 1 });
if pressure_max < pressure_relief {
pressure_max = pressure_relief;
visited = visited_now;
}
deque_add_last(*deque, .{ it.valve_index, time_left_now, visited_now, pressure_relief, duped });
}
}
}
return pressure_max, visited;
}
print_history :: (history: [..]History, valves: [..]Valve) {
for history {
print("%, ", cast(string) to_name(valves[it.index].name));
}
print("\n");
}
add_valve :: (valve_id: u16, valves: *[..]Valve) -> s64 {
valve_index := valves.count;
valve := array_add(valves);
valve.name = valve_id;
return valve_index;
}
to_name :: (valve_id: u16) -> [2]u8 {
name: [2]u8;
name[0] = xx (valve_id >> 8);
name[1] = xx (valve_id & 0xFF);
return name;
}
print_visited :: (valves: [..]Valve, visited: u64) {
for 0..63 {
if 1 & (visited >> it) == 1 {
print("%, ", cast(string) to_name(valves[it].name));
}
}
}
find_shortest_route :: (from: s64, to: s64, valves: [..]Valve) -> s64 {
deque: Priority_Queue(Q_Item, (a, b) => a.steps - b.steps);
defer deinit(*deque);
start: [..]History;
visited: Table(s64, s64);
add(*deque, .{ from, 0, start });
while deque.count > 0 {
removed, item := remove_top(*deque);
valve := valves[item.index];
for v,k: valve.connections {
if v == to {
return item.steps + 1;
}
visited_index := (cast(u64)1) << cast(u64)v;
if table_contains(*visited, v) continue;
table_set(*visited, v, v);
duped: [..]History;
array_copy(*duped, item.history);
array_add(*duped, .{ v, item.steps + 1 });
add(*deque, .{ v, item.steps + 1, duped });
}
}
return -1;
}
#scope_file
Valve :: struct {
name: u16;
flow: s64;
connections: Table(u16, s64);
compressed: [..]struct { valve_index: s64; time: s64; };
}
History :: struct {
index: s64;
t: s64;
}
Q_Item :: struct {
index: s64;
steps: s64;
history: [..]History;
}