solve_day21 :: (test: bool) {
contents := read_entire_file(ifx test then "inputs/day21_test.txt" else "inputs/day21.txt");
lines := split(contents, cast(u8) #char "\n");
monkeys: [..]Monkey;
monkey_index: Table(string, s64);
root_monkey_index, found := -1, false;
humn_monkey_index, found_humn := -1, false;
for line: lines {
parts := split(line, cast(u8) #char " ");
name := slice(parts[0], 0, parts[0].count - 1);
if name == "humn" {
found_humn = true;
humn_monkey_index = monkeys.count;
} else if name == "root" {
found = true;
root_monkey_index = monkeys.count;
}
table_set(*monkey_index, name, monkeys.count);
if parts.count > 2 {
array_add(*monkeys, .{
name = name,
calculated = false,
operation = convert_to_monkey_operation(parts[2]),
depends_on = string.[parts[1], parts[3]],
});
} else {
array_add(*monkeys, .{
name = name,
calculated = true,
operation = .NONE,
value = string_to_int(parts[1], 10, s64),
});
}
}
part1 := calc_value(*monkeys[root_monkey_index], monkeys, monkey_index);
part2 := 0;
monkey_a_index,_ := table_find(*monkey_index, monkeys[root_monkey_index].depends_on[0]);
monkey_b_index,_ := table_find(*monkey_index, monkeys[root_monkey_index].depends_on[1]);
reset_monkeys(monkeys);
monkey_a := *monkeys[monkey_a_index];
monkey_b := *monkeys[monkey_b_index];
value_a, has_humn_a := calc_value(monkey_a, monkeys, monkey_index);
value_b, has_humn_b := calc_value(monkey_b, monkeys, monkey_index);
if has_humn_a {
part2 = cast,no_check(s64) find_humn_value(monkey_a, value_b, true, monkeys, monkey_index);
} else if has_humn_b {
part2 = cast,no_check(s64) find_humn_value(monkey_b, value_a, false, monkeys, monkey_index);
} else {
assert(false, "Neither has humn?");
}
print("Part 1: %\n", part1);
print("Part 2: %\n", part2);
}
#scope_file
Monkey :: struct {
name: string;
value: s64;
calculated: bool;
operation: Monkey_Operation = .NONE;
depends_on: [2]string;
}
reset_monkeys :: (monkeys: [..]Monkey) {
for *monkeys {
if it.operation != .NONE {
it.calculated = false;
}
}
}
find_humn_value :: (monkey: Monkey, value: $T, right: bool, monkeys: [..]Monkey, monkey_index: Table) -> T {
if monkey.name == "humn" return value;
monkey_a_index,fa := table_find(*monkey_index, monkey.depends_on[0]);
monkey_b_index,fb := table_find(*monkey_index, monkey.depends_on[1]);
monkey_a := *monkeys[monkey_a_index];
monkey_b := *monkeys[monkey_b_index];
assert(fa, "Failed to find '%', %", monkey.depends_on[0], monkey.name);
assert(fb, "Failed to find '%', %", monkey.depends_on[1], monkey.name);
value_a, has_humn_a := calc_value(monkey_a, monkeys, monkey_index);
value_b, has_humn_b := calc_value(monkey_b, monkeys, monkey_index);
if has_humn_a {
rev := reverse_operation(monkey.operation, true, value, value_b, monkey.name);
return find_humn_value(monkey_a, rev, true, monkeys, monkey_index);
} else if has_humn_b {
rev := reverse_operation(monkey.operation, false, value, value_a, monkey.name);
return find_humn_value(monkey_b, rev, false, monkeys, monkey_index);
} else {
assert(false, "Neither has humn?");
}
return 0;
}
calc_value :: (monkey: *Monkey, monkeys: [..]Monkey, monkey_index: Table) -> s64, bool {
if monkey.operation == .NONE return monkey.value, monkey.name == "humn";
monkey_a_index, found_a := table_find(*monkey_index, monkey.depends_on[0]);
monkey_b_index, found_b := table_find(*monkey_index, monkey.depends_on[1]);
assert(found_a, "Failed to find monkey '%', %", monkey.depends_on[0], monkey.name);
assert(found_b, "Failed to find monkey '%' %", monkey.depends_on[1], monkey.name);
monkey_a_value, has_a_humn := calc_value(*monkeys[monkey_a_index], monkeys, monkey_index);
monkey_b_value, has_b_humn := calc_value(*monkeys[monkey_b_index], monkeys, monkey_index);
result := perform_operation(monkey.operation, monkey_a_value, monkey_b_value);
monkey.value = result;
monkey.calculated = true;
return result, has_a_humn || has_b_humn;
}
perform_operation :: (operation: Monkey_Operation, a: $T, b: T) -> T {
if operation == {
case .NONE; assert(false, "Got .NONE operation..");
case .MULT; return a * b;
case .DIV; return a / b;
case .PLUS; return a + b;
case .MINUS; return a - b;
}
return -1;
}
reverse_operation :: (operation: Monkey_Operation, right: bool, a: $T, b: T, name: string) -> T {
if operation == {
case .NONE; assert(false, "Got .NONE operation..");
case .MULT; return a / b;
case .DIV; return a * b;
case .PLUS; return a - b;
case .MINUS; if right then return a + b; else return b - a;
}
return 0;
}
convert_to_monkey_operation :: (operation: string) -> Monkey_Operation {
if operation == {
case "+";
return .PLUS;
case "-";
return .MINUS;
case "/";
return .DIV;
case "*";
return .MULT;
case;
assert(false, "Bad operation %", operation);
}
return .NONE;
}
Monkey_Operation :: enum {
NONE :: -1;
PLUS :: 0;
MINUS :: 1;
MULT :: 2;
DIV :: 3;
}