1+ #include < iostream>
2+ #include < filesystem>
3+ #include < fstream>
4+ #include < string>
5+ #include < vector>
6+ #include < benchmark/benchmark.h>
7+ #include < format>
8+ #include < aoc/2024/split.h>
9+
10+ enum Gate {
11+ AND,
12+ OR,
13+ XOR
14+ };
15+
16+ std::string gate_to_string (Gate gate) {
17+ switch (gate) {
18+ case Gate::AND:
19+ return " AND" ;
20+ case Gate::OR:
21+ return " OR" ;
22+ case Gate::XOR:
23+ return " XOR" ;
24+ default :
25+ break ;
26+ }
27+ return " unknown" ;
28+ }
29+
30+ struct Operation {
31+ Gate gate;
32+ std::string arg1;
33+ std::string arg2;
34+ std::string target;
35+ };
36+
37+ struct Data {
38+ std::map<std::string, int > gates;
39+ std::vector<Operation> operations;
40+ };
41+
42+ bool has_both_inputs (const Operation& op, const std::map<std::string, int >& gates) {
43+ return gates.at (op.arg1 ) != -1 && gates.at (op.arg2 ) != -1 ;
44+ }
45+
46+ void put_valid_gates_first (std::vector<Operation>& operations, const std::map<std::string, int >& gates, size_t offset = 0 ) {
47+ std::sort (operations.begin () + offset, operations.end (), [&](const Operation& a, const Operation& b) {
48+ int valid_args_a = 0 ;
49+ valid_args_a += gates.at (a.arg1 ) != -1 ;
50+ valid_args_a += gates.at (a.arg2 ) != -1 ;
51+ int valid_args_b = 0 ;
52+ valid_args_b += gates.at (b.arg1 ) != -1 ;
53+ valid_args_b += gates.at (b.arg2 ) != -1 ;
54+ return valid_args_a > valid_args_b;
55+ });
56+ }
57+
58+ int64_t form_number (const std::map<std::string, int >& gates) {
59+ int idx = 0 ;
60+ int64_t result = 0 ;
61+ for (auto & [gate, value] : gates) {
62+ if (gate[0 ] == ' z' ) {
63+ std::cout << std::format (" {} {}\n " , gate, value);
64+ result |= value << idx;
65+ ++idx;
66+ }
67+ }
68+ return result;
69+ }
70+
71+ int64_t part1 (const Data &data)
72+ {
73+ auto operations = data.operations ;
74+ auto gates = data.gates ;
75+
76+ for (auto & [gate, value] : gates) {
77+ std::cout << gate << " " << value << std::endl;
78+ }
79+
80+ for (auto & op : operations) {
81+ std::cout << std::format (" {} {} {} -> {}\n " , op.arg1 , gate_to_string (op.gate ), op.arg2 , op.target );
82+ }
83+ std::cout << std::endl;
84+
85+ put_valid_gates_first (operations, gates);
86+
87+ for (auto & op : operations) {
88+ std::cout << std::format (" {} {} {} -> {}\n " , op.arg1 , gate_to_string (op.gate ), op.arg2 , op.target );
89+ }
90+ std::cout << std::endl;
91+
92+ for (size_t i = 0 ; i < operations.size (); ++i) {
93+ auto & op = operations[i];
94+ if (has_both_inputs (op, gates)) {
95+ int arg1 = data.gates .at (op.arg1 );
96+ int arg2 = data.gates .at (op.arg2 );
97+ int result = 0 ;
98+ switch (op.gate ) {
99+ case Gate::AND:
100+ result = arg1 & arg2;
101+ break ;
102+ case Gate::OR:
103+ result = arg1 | arg2;
104+ break ;
105+ case Gate::XOR:
106+ result = arg1 ^ arg2;
107+ break ;
108+ default :
109+ break ;
110+ }
111+ gates[op.target ] = result;
112+ }
113+ else {
114+ put_valid_gates_first (operations, gates, i);
115+ // --i;
116+ }
117+ }
118+ for (auto & [gate, value] : gates) {
119+ std::cout << gate << " " << value << std::endl;
120+ }
121+ std::cout << std::endl;
122+ return form_number (gates);
123+ }
124+
125+ int part2 (const Data &data)
126+ {
127+ return 0 ;
128+ }
129+
130+ Data parse ()
131+ {
132+ std::ifstream file (std::filesystem::path (" inputs/day24.txt" ));
133+ if (!file.is_open ())
134+ {
135+ throw std::runtime_error (" file not found" );
136+ }
137+ std::string line;
138+ Data data;
139+
140+ bool gates = false ;
141+ while (std::getline (file, line))
142+ {
143+ if (line.empty ())
144+ {
145+ gates = true ;
146+ continue ;
147+ }
148+ if (!gates)
149+ {
150+ auto parts = split (line, " : " );
151+ data.gates [parts[0 ]] = std::stoi (parts[1 ]);
152+ }
153+ else
154+ {
155+ auto parts = split (line, " " );
156+ Operation op;
157+ op.arg1 = parts[0 ];
158+ op.arg2 = parts[2 ];
159+ op.target = parts[4 ];
160+ if (parts[1 ] == " AND" )
161+ {
162+ op.gate = Gate::AND;
163+ }
164+ else if (parts[1 ] == " OR" )
165+ {
166+ op.gate = Gate::OR;
167+ }
168+ else if (parts[1 ] == " XOR" )
169+ {
170+ op.gate = Gate::XOR;
171+ }
172+ else
173+ {
174+ throw std::runtime_error (" invalid gate" );
175+ }
176+ if (data.gates .find (op.arg1 ) == data.gates .end ()) {
177+ data.gates [op.arg1 ] = -1 ;
178+ }
179+ if (data.gates .find (op.arg2 ) == data.gates .end ()) {
180+ data.gates [op.arg2 ] = -1 ;
181+ }
182+ if (data.gates .find (op.target ) == data.gates .end ()) {
183+ data.gates [op.target ] = -1 ;
184+ }
185+ data.operations .push_back (op);
186+ }
187+ }
188+
189+ return data;
190+ }
191+
192+ class BenchmarkFixture : public benchmark ::Fixture
193+ {
194+ public:
195+ static Data data;
196+ };
197+
198+ Data BenchmarkFixture::data = parse();
199+
200+ BENCHMARK_DEFINE_F (BenchmarkFixture, Part1Benchmark)
201+ (benchmark::State &state)
202+ {
203+ for (auto _ : state)
204+ {
205+ auto s = part1 (data);
206+ benchmark::DoNotOptimize (s);
207+ }
208+ }
209+
210+ BENCHMARK_DEFINE_F (BenchmarkFixture, Part2Benchmark)
211+ (benchmark::State &state)
212+ {
213+ for (auto _ : state)
214+ {
215+ auto s = part2 (data);
216+ benchmark::DoNotOptimize (s);
217+ }
218+ }
219+
220+ BENCHMARK_REGISTER_F (BenchmarkFixture, Part1Benchmark)->Unit(benchmark::kMillisecond );
221+ BENCHMARK_REGISTER_F (BenchmarkFixture, Part2Benchmark)->Unit(benchmark::kMillisecond );
222+
223+ int main (int argc, char **argv)
224+ {
225+ Data data = parse ();
226+
227+ int64_t answer1 = 0 ;
228+ int64_t answer2 = 0 ;
229+
230+ auto first = part1 (data);
231+ std::cout << " Part 1: " << first << std::endl;
232+
233+ auto second = part2 (data);
234+ std::cout << " Part 2: " << second << std::endl;
235+
236+ first != answer1 ? throw std::runtime_error (" Part 1 incorrect" ) : nullptr ;
237+ second != answer2 ? throw std::runtime_error (" Part 2 incorrect" ) : nullptr ;
238+
239+ for (int i = 1 ; i < argc; ++i) {
240+ if (std::string (argv[i]) == " --benchmark" ) {
241+ benchmark::Initialize (&argc, argv);
242+ benchmark::RunSpecifiedBenchmarks ();
243+ return 0 ;
244+ }
245+ }
246+ }
0 commit comments