aoc

My solutions for the Advent of Code
git clone https://git.tronto.net/aoc
Download | Log | Files | Refs | README

commit 19be9f44dbecdde03662be2248eda5b739d67038
parent 21e1237e93703f632253f042bd5877998d81c507
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Wed, 25 Dec 2024 00:40:50 +0100

Day 24 2024 still don't know how I managed to solve this

Diffstat:
A2024/24/Makefile | 11+++++++++++
A2024/24/clean.sh | 3+++
A2024/24/day24a.cpp | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A2024/24/day24b.cpp | 247+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M2024/learned.txt | 1+
5 files changed, 350 insertions(+), 0 deletions(-)

diff --git a/2024/24/Makefile b/2024/24/Makefile @@ -0,0 +1,11 @@ +CC=g++ -std=c++20 -g -Wall + +a: + ${CC} -o a.out day24a.cpp + ./a.out + +b: + ${CC} -o b.out day24b.cpp + ./b.out + +.PHONY: a b diff --git a/2024/24/clean.sh b/2024/24/clean.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +sed 's/XOR/^/; s/OR/|/; s/AND/\&/' diff --git a/2024/24/day24a.cpp b/2024/24/day24a.cpp @@ -0,0 +1,88 @@ +/* Use clean.sh on the input first */ + +#include <algorithm> +#include <cstdint> +#include <iostream> +#include <map> +#include <queue> +#include <ranges> +#include <set> +#include <sstream> +#include <string> +#include <string_view> +#include <vector> +using namespace std; + +int8_t state[36*36*36]; + +int d(char x) { return x >= 'a' && x <= 'z' ? x-'a' : 26+x-'0'; } +int index(char a, char b, char c) { return d(c) + 36 * (d(b) + 36 * d(a)); } +int z(int i) { return index('z', (i/10)+'0', (i%10)+'0'); } + +enum class Op { AND, OR, XOR }; + +class Gate { +public: + int x, y, z; + Op op; + + bool ready() const { + return state[x] != -1 && state[y] != -1; + } + + bool operate() const { /* Returns true if the operation flips a bit */ + auto oldstate = state[z]; + switch (op) { + case Op::AND: + state[z] = state[x] & state[y]; + break; + case Op::OR: + state[z] = state[x] | state[y]; + break; + case Op::XOR: + state[z] = state[x] ^ state[y]; + break; + } + return oldstate != state[z]; + } +}; + +Op make_op(char c) { + switch (c) { + case '&': return Op::AND; + case '|': return Op::OR; + default: return Op::XOR; + } +} + +int main() { + vector<Gate> gates; + fill(state, state + 36*36*36, -1); + string line; + + while (getline(cin, line) && line != "") + state[index(line[0], line[1], line[2])] = line[5]-'0'; + + while (getline(cin, line)) + gates.push_back(Gate { + .x = index(line[0], line[1], line[2]), + .y = index(line[6], line[7], line[8]), + .z = index(line[13], line[14], line[15]), + .op = make_op(line[4]) + }); + + for (bool flag = true; flag; ) { + flag = false; + for (auto g : gates) + if (g.ready()) + flag = flag || g.operate(); + } + + int64_t result = 0; + for (int64_t i = 0, j = 1; state[z(i)] != -1; i++, j *= 2) + result += (int64_t)state[z(i)] * j; + + cout << result << endl; + + return 0; +} diff --git a/2024/24/day24b.cpp b/2024/24/day24b.cpp @@ -0,0 +1,247 @@ +/* + +I don't know what addition circuits look like, but they should have some +kind of regular structure. I manually inspected how the first few digits +behave to get an idea. + +With some more manual inspection I noticed that the "temporary result" +gates that are results of a XOR operation always appear as terms in +exactly one XOR and one AND operation; the same thing happens for +OR-results, while AND-results appear as terms only in one OR operation. + +This excludes z-gates, which are the result of a XOR operation (except +for z45, which is the result of an OR operation) and do not appear as +terms in any other operation. And of course "errors" do not follow this +pattern either. + +So what I do is going through all gates and see if their result +satisfies the conditions above. This gives 8 candidates for the error, +but unfortunately they were not the correct answer (I tried submitting +this list 3 times). + +I thought about it a bit longer and I realized that one of the entries +in my list is rfg, which appears as the result of x00 & y00. I thought +that since this was the first digit, it could be an edge case and thus +correctly behaving differently from the others. This leaves me with +7 candidates only, so I have to find another one. + +One of the gates I had to swap (z23) was the result of an OR operation, +but should have been the result o a XOR. But any XOR-result could be +swapped with an OR-result without breaking the conditions I guessed at the +beginning. So I tried them all: for every XOR-gate I swapped the results +with my other 7 candidates in every way possible according to my rules, +and checked which of these re-arrangements gave the correct answer for +the example input. + +I found two candidates (kdf and nsr). I could have checked more inputs +to see which of the two worked, or I could just try both of them. Of +course, I choose the second option, and kdf worked. + +Did I get the gold star? Yes. Do I know how I was supposed to solve this +problem? Not a clue. But hey, I solved it without hints, a win is a win. + +*/ + +#include <algorithm> +#include <cstdint> +#include <iostream> +#include <map> +#include <queue> +#include <ranges> +#include <set> +#include <sstream> +#include <string> +#include <string_view> +#include <vector> +using namespace std; + +enum class Op { AND, OR, XOR }; + +class Gate { +public: + int x, y, z; + Op op; + + bool ready(const int8_t *state) const { + return state[x] != -1 && state[y] != -1; + } + + bool operate(int8_t *state) const { /* Returns true if the operation flips a bit */ + auto oldstate = state[z]; + switch (op) { + case Op::AND: + state[z] = state[x] & state[y]; + break; + case Op::OR: + state[z] = state[x] | state[y]; + break; + case Op::XOR: + state[z] = state[x] ^ state[y]; + break; + } + return oldstate != state[z]; + } +}; + +int8_t state[36*36*36]; +vector<Gate> gates; + +int d(char x) { return x >= 'a' && x <= 'z' ? x-'a' : 26+x-'0'; } +char h(int i) { return i >= 26 ? i-26+'0' : i+'a'; } +int index(char a, char b, char c) { return d(c) + 36 * (d(b) + 36 * d(a)); } +string name(int i) { + string r = " "; + r[0] = h(i / (36*36)); + r[1] = h((i/ 36) % 36); + r[2] = h(i % 36); + return r; +} + +int64_t digit(int i, char c) { + return index(c, (i/10)+'0', (i%10)+'0'); +} + +int64_t getnum(char c, int m) { + int64_t result = 0; + for (int64_t i = 0, j = 1; i < m; i++, j *= 2) + result += (int64_t)state[digit(i, c)] * j; + return result; +} + +Op make_op(char c) { + switch (c) { + case '&': return Op::AND; + case '|': return Op::OR; + default: return Op::XOR; + } +} + +char opname(Op op) { + switch (op) { + case Op::AND: return '&'; + case Op::OR: return '|'; + default: return '^'; + } +} + +void printop(Gate g, const vector<Op>& a) { + cout << name(g.z) << " is " << name(g.x) << " " << opname(g.op) + << " " << name(g.y) << " but appears in "; + for (auto op : a) + cout << opname(op) << " "; + cout << endl; +} + +int gg(const string& rr) { + for (unsigned i = 0; i < gates.size(); i++) + if (name(gates[i].z) == rr) + return i; + return -1; +} + +void swapgates(vector<Gate>& local_gates, vector<int> ixsa, + vector<int> iasx, int iosx, int gswap) { +} + +void run(int8_t *state, const vector<Gate>& gates) { + for (bool flag = true; flag; ) { + flag = false; + for (auto g : gates) + if (g.ready(state)) + flag = flag || g.operate(state); + } + int64_t x = getnum('x', 45); + int64_t y = getnum('y', 45); + int64_t z = getnum('z', 46); + if (x+y == z) cout << "Correct result! " << z << endl; +} + +int main() { + fill(state, state + 36*36*36, -1); + string line; + + while (getline(cin, line) && line != "") + state[index(line[0], line[1], line[2])] = line[5]-'0'; + + while (getline(cin, line)) + gates.push_back(Gate { + .x = index(line[0], line[1], line[2]), + .y = index(line[6], line[7], line[8]), + .z = index(line[13], line[14], line[15]), + .op = make_op(line[4]) + }); + +#if 0 + /* Print error candidates */ + vector<string> bad; + for (auto g : gates) { + if (name(g.z)[0] == 'z') { + if ((name(g.z) == "z45" && g.op != Op::OR) || + (name(g.z) != "z45" && g.op != Op::XOR)) { + printop(g, vector<Op>()); + bad.push_back(name(g.z)); + } + continue; + } + vector<Op> appears_in; + for (auto f : gates) + if (g.z == f.x || g.z == f.y) + appears_in.push_back(f.op); + if (g.op == Op::AND) { + if (appears_in.size() != 1 || appears_in[0] != Op::OR) { + printop(g, appears_in); + bad.push_back(name(g.z)); + } + } else { + if (appears_in.size() != 2 || + find(appears_in.begin(), appears_in.end(), Op::AND) == appears_in.end() || + find(appears_in.begin(), appears_in.end(), Op::XOR) == appears_in.end()) { + printop(g, appears_in); + bad.push_back(name(g.z)); + } + } + } + + sort(bad.begin(), bad.end()); + + for (auto b : bad) + cout << b << ","; + cout << endl; +#endif + + vector<string> is_xor_shouldbe_and = { "ckj", "rpp", "dbp" }; + vector<string> is_and_shouldbe_xor = { "fdv", "z15", "z39" }; + string is_or_shouldbe_xor = "z23"; + + vector<int> ixsa = { gg("ckj"), gg("rpp"), gg("dbp") }; + vector<int> iasx = { gg("fdv"), gg("z15"), gg("z39") }; + int iosx = gg("z23"); + + vector<vector<pair<int, int>>> swaps = { + {{ixsa[0],iasx[0]}, {ixsa[1],iasx[1]}, {ixsa[2],iasx[2]}}, + {{ixsa[0],iasx[0]}, {ixsa[1],iasx[2]}, {ixsa[2],iasx[1]}}, + {{ixsa[0],iasx[1]}, {ixsa[1],iasx[0]}, {ixsa[2],iasx[2]}}, + {{ixsa[0],iasx[1]}, {ixsa[1],iasx[2]}, {ixsa[2],iasx[0]}}, + {{ixsa[0],iasx[2]}, {ixsa[1],iasx[1]}, {ixsa[2],iasx[0]}}, + {{ixsa[0],iasx[2]}, {ixsa[1],iasx[0]}, {ixsa[2],iasx[1]}} + }; + + for (unsigned gswap = 0; gswap < gates.size(); gswap++) { + if (find(ixsa.begin(), ixsa.end(), gswap) != ixsa.end() || + find(iasx.begin(), iasx.end(), gswap) != iasx.end() || + (int)gswap == iosx || gates[gswap].op != Op::XOR) + continue; + cout << "Trying gswap " << name(gates[gswap].z) << endl; + for (auto ss : swaps) { + auto local_state = state; + auto local_gates = gates; + swap(local_gates[ss[0].first].z, local_gates[ss[0].second].z); + swap(local_gates[ss[1].first].z, local_gates[ss[1].second].z); + swap(local_gates[ss[2].first].z, local_gates[ss[2].second].z); + swap(local_gates[iosx].z, local_gates[gswap].z); + run(local_state, local_gates); + } + } + + return 0; +} diff --git a/2024/learned.txt b/2024/learned.txt @@ -11,3 +11,4 @@ List of things I learned (or refreshed) with this year's AoC. * Day 11: if(init; cond) * Day 19: more practice with std::string_view for constant string * Day 22: std::views::values for map (and keys) +* Day 24: std::fill