From f9643b3881ec3eb7c48b2415ce29621df7ded666 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Mon, 15 Dec 2025 14:37:59 +0200 Subject: [PATCH] aoc2025: Implement day10 solution --- userspace/aoc2025/CMakeLists.txt | 1 + userspace/aoc2025/day10/CMakeLists.txt | 9 ++ userspace/aoc2025/day10/main.cpp | 216 +++++++++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 userspace/aoc2025/day10/CMakeLists.txt create mode 100644 userspace/aoc2025/day10/main.cpp diff --git a/userspace/aoc2025/CMakeLists.txt b/userspace/aoc2025/CMakeLists.txt index 68d2aa03..097d4f31 100644 --- a/userspace/aoc2025/CMakeLists.txt +++ b/userspace/aoc2025/CMakeLists.txt @@ -8,6 +8,7 @@ set(AOC2025_PROJECTS day7 day8 day9 + day10 full ) diff --git a/userspace/aoc2025/day10/CMakeLists.txt b/userspace/aoc2025/day10/CMakeLists.txt new file mode 100644 index 00000000..106993f6 --- /dev/null +++ b/userspace/aoc2025/day10/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SOURCES + main.cpp +) + +add_executable(aoc2025_day10 ${SOURCES}) +banan_include_headers(aoc2025_day10 ban) +banan_link_library(aoc2025_day10 libc) + +install(TARGETS aoc2025_day10 OPTIONAL) diff --git a/userspace/aoc2025/day10/main.cpp b/userspace/aoc2025/day10/main.cpp new file mode 100644 index 00000000..9bc04805 --- /dev/null +++ b/userspace/aoc2025/day10/main.cpp @@ -0,0 +1,216 @@ +#include +#include + +#include +#include +#include + +using i8 = int8_t; +using i16 = int16_t; +using i32 = int32_t; +using i64 = int64_t; + +using u8 = uint8_t; +using u16 = uint16_t; +using u32 = uint32_t; +using u64 = uint64_t; + +struct Machine +{ + u32 indicator; + BAN::Vector buttons; + BAN::Vector joltages; +}; + +static BAN::Vector parse_machines(FILE* fp) +{ + BAN::Vector machines; + + BAN::String line; + char buffer[128]; + while (fgets(buffer, sizeof(buffer), fp)) + { + MUST(line.append(buffer)); + if (line.back() != '\n') + continue; + if (line.front() != '[') + break; + + Machine machine {}; + + size_t i = 1; + while (line[i] != ']') + { + if (line[i] == '#') + machine.indicator |= 1 << (i - 1); + i++; + } + + i += 2; + + while (line[i++] == '(') + { + u32 button = 0; + + while (line[i] != ')') + { + if (line[i] == ',') + i++; + + u32 index = 0; + while (isdigit(line[i])) + index = (index * 10) + (line[i++] - '0'); + + button |= 1 << index; + } + + MUST(machine.buttons.push_back(button)); + + i += 2; + } + + i--; + + ASSERT(line[i] == '{'); + while (line[i++] != '}') + { + u32 joltage = 0; + while (isdigit(line[i])) + joltage = (joltage * 10) + (line[i++] - '0'); + MUST(machine.joltages.push_back(joltage)); + } + + MUST(machines.push_back(BAN::move(machine))); + + line.clear(); + } + + return machines; +} + +i64 part1(FILE* fp) +{ + static constexpr bool (*works_with_n_buttons)(const Machine&, size_t, size_t, u32) = + [](const Machine& machine, size_t buttons, size_t index, u32 state) -> bool + { + if (buttons == 0) + return state == machine.indicator; + for (size_t i = index; i < machine.buttons.size(); i++) + if (works_with_n_buttons(machine, buttons - 1, i + 1, state ^ machine.buttons[i])) + return true; + return false; + }; + + i64 result = 0; + + auto machines = parse_machines(fp); + for (const auto& machine : machines) + { + for (size_t i = 1; i < machine.buttons.size(); i++) + { + if (!works_with_n_buttons(machine, i, 0, 0)) + continue; + result += i; + break; + } + } + + return result; +} + +// based on https://www.reddit.com/r/adventofcode/comments/1pk87hl/2025_day_10_part_2_bifurcate_your_way_to_victory/ + +static u32 minimum_presses(const BAN::Vector>& possible_indicators, const BAN::Vector& buttons, const BAN::Vector& joltages) +{ + bool all_zeroes = true; + for (u32 joltage : joltages) + if (joltage != 0) + all_zeroes = false; + if (all_zeroes) + return 0; + + u32 indicator = 0; + for (size_t i = 0; i < joltages.size(); i++) + if (joltages[i] % 2) + indicator |= 1 << i; + + u32 result = 1'000'000; + + for (u32 button_mask : possible_indicators[indicator]) + { + size_t button_count = 0; + BAN::Vector new_joltages = joltages; + for (size_t i = 0; i < buttons.size(); i++) + { + if (!(button_mask & (1 << i))) + continue; + button_count++; + for (size_t bit = 0; bit < new_joltages.size(); bit++) + { + if (!(buttons[i] & (1 << bit))) + continue; + if (new_joltages[bit] == 0) + goto unusable; + new_joltages[bit]--; + } + } + + for (u32& joltage : new_joltages) + joltage /= 2; + result = BAN::Math::min(result, button_count + 2 * minimum_presses(possible_indicators, buttons, new_joltages)); + + unusable: + (void)0; + } + + return result; +} + +i64 part2(FILE* fp) +{ + i64 result = 0; + + auto machines = parse_machines(fp); + for (const auto& machine : machines) + { + const size_t bits = machine.joltages.size(); + + BAN::Vector> possible_indicators; + MUST(possible_indicators.resize(1u << bits)); + for (size_t button_mask = 0; button_mask < (1u << machine.buttons.size()); button_mask++) + { + u32 indicator = 0; + for (size_t button = 0; button < machine.buttons.size(); button++) + if (button_mask & (1 << button)) + indicator ^= machine.buttons[button]; + MUST(possible_indicators[indicator].push_back(button_mask)); + } + + result += minimum_presses(possible_indicators, machine.buttons, machine.joltages); + } + + return result; +} + +int main(int argc, char** argv) +{ + const char* file_path = "/usr/share/aoc2025/day10_input.txt"; + + if (argc >= 2) + file_path = argv[1]; + + FILE* fp = fopen(file_path, "r"); + if (fp == nullptr) + { + perror("fopen"); + return 1; + } + + printf("part1: %" PRId64 "\n", part1(fp)); + + fseek(fp, 0, SEEK_SET); + + printf("part2: %" PRId64 "\n", part2(fp)); + + fclose(fp); +}