1
+ #include " common_headers.hpp"
2
+
3
+ #include < stack>
4
+ #include < string>
5
+ #include < regex>
6
+ #include < unordered_map>
7
+ #include < unordered_set>
8
+ #include < vector>
9
+ #include < queue>
10
+
11
+
12
+ enum class Operation {AND, OR, XOR};
13
+
14
+ struct LogicExpression final {
15
+ std::string operand1;
16
+ Operation operation;
17
+ std::string operand2;
18
+ std::string output;
19
+ };
20
+
21
+ struct Program final
22
+ {
23
+ std::unordered_map<std::string, bool > gates;
24
+ std::vector<LogicExpression> exprs;
25
+ };
26
+
27
+ [[nodiscard]] bool executeOperation (bool operand1, bool operand2, Operation operation) {
28
+ switch (operation)
29
+ {
30
+ case Operation::AND:
31
+ return operand1 && operand2;
32
+ case Operation::OR:
33
+ return operand1 || operand2;
34
+ case Operation::XOR:
35
+ return operand1 != operand2;
36
+ default :
37
+ throw std::runtime_error{" unknown operation type" };
38
+ }
39
+ return false ;
40
+ }
41
+
42
+ [[nodiscard]] Operation toOperation (const std::string_view op) {
43
+ if (op == " AND" ) return Operation::AND;
44
+ if (op == " OR" ) return Operation::OR;
45
+ if (op == " XOR" ) return Operation::XOR;
46
+ throw std::runtime_error{" unknown operation type" };
47
+ }
48
+
49
+ [[nodiscard]] std::pair<std::string, bool > parseInitialGate (const std::string& line)
50
+ {
51
+ const auto pos = line.find (" :" );
52
+ if (pos == std::string::npos) {
53
+ throw std::logic_error{" input invalid" };
54
+ }
55
+ std::string gate{line.substr (0 , pos)};
56
+ bool value{line.at (pos + 2 ) == ' 1' };
57
+ return {std::move (gate), value};
58
+ }
59
+
60
+ [[nodiscard]] LogicExpression parseLogicExpression (const std::string& line)
61
+ {
62
+ std::regex pattern (R"( (\w+) (\w+) (\w+) -> (\w+))" );
63
+ std::smatch match;
64
+ if (!std::regex_match (line, match, pattern)) {
65
+ throw std::logic_error{" invalid logic expression" };
66
+ }
67
+
68
+ LogicExpression expr{
69
+ .operand1 = match[1 ].str (), // operand1
70
+ .operation = toOperation (match[2 ].str ()), // operation
71
+ .operand2 = match[3 ].str (), // operand2
72
+ .output = match[4 ].str () // result
73
+ };
74
+
75
+ return expr;
76
+ }
77
+
78
+ [[nodiscard]] Program parseInput (const std::vector<std::string>& inputVec)
79
+ {
80
+ Program program;
81
+ size_t id{};
82
+ for (; id < inputVec.size () && !inputVec[id].empty (); ++id) {
83
+ const auto [name, val] = parseInitialGate (inputVec[id]);
84
+ program.gates .emplace (name, val);
85
+ }
86
+ ++id;
87
+ for (; id < inputVec.size (); ++id) {
88
+ program.exprs .push_back (parseLogicExpression (inputVec[id]));
89
+ }
90
+ return program;
91
+ }
92
+
93
+ [[nodiscard]] size_t solveFirstPart (Program& program)
94
+ {
95
+ std::unordered_map<std::string, int > preordering;
96
+ std::unordered_map<std::string, std::vector<size_t >> operandToExpressionId;
97
+
98
+ for (size_t id = 0 ; id < program.exprs .size (); ++id) {
99
+ const auto & expr = program.exprs [id];
100
+ operandToExpressionId[expr.operand1 ].push_back (id);
101
+ operandToExpressionId[expr.operand2 ].push_back (id);
102
+
103
+ preordering[expr.output ]++;
104
+ }
105
+
106
+ std::queue<size_t > bfs;
107
+ for (size_t id = 0 ; id < program.exprs .size (); ++id) {
108
+ const auto & expr = program.exprs [id];
109
+ if (preordering[expr.operand1 ] == 0 && preordering[expr.operand2 ] == 0 ) {
110
+ bfs.push (id);
111
+ }
112
+ }
113
+
114
+ while (!bfs.empty ()) {
115
+ const auto exprId = bfs.front ();
116
+ bfs.pop ();
117
+
118
+ const LogicExpression& expr = program.exprs [exprId];
119
+ const bool operationResult = executeOperation (program.gates .at (expr.operand1 ), program.gates .at (expr.operand2 ), expr.operation );
120
+ program.gates [expr.output ] = operationResult;
121
+
122
+ if (--preordering[expr.output ] == 0 ) {
123
+ for (const size_t otherExprId : operandToExpressionId[expr.output ]) {
124
+ if (otherExprId == exprId) continue ;
125
+ const LogicExpression& otherExpr = program.exprs [otherExprId];
126
+
127
+ if (preordering.at (otherExpr.operand1 ) == 0 && preordering.at (otherExpr.operand2 ) == 0 ) {
128
+ bfs.emplace (otherExprId);
129
+ }
130
+ }
131
+ operandToExpressionId[expr.output ].clear ();
132
+ }
133
+ }
134
+
135
+ size_t zGateOutput{0 };
136
+ for (const auto & [gate, result] : program.gates ) {
137
+ if (gate[0 ] != ' z' || result == false ) continue ;
138
+ const auto gateNumber = std::stoul (gate.substr (1 ));
139
+ zGateOutput |= (size_t {1 } << gateNumber);
140
+ }
141
+ return zGateOutput;
142
+ }
143
+
144
+ void printHelp ()
145
+ {
146
+ std::cerr << " \n Usage:\n "
147
+ << " The program requires 2 args: (part1, part2) and the path to the file."
148
+ << " \n For example, ./day7 part1 data/day7.txt" ;
149
+ }
150
+
151
+ int main (int argc, char * argv[])
152
+ {
153
+ if (argc != 3 ) {
154
+ printHelp ();
155
+ return 1 ;
156
+ }
157
+
158
+ std::string_view task{argv[1 ]};
159
+ if (task != " part1" && task != " part2" ) {
160
+ std::cerr << " \n first arg can be either `part1` or `part2`\n " ;
161
+ printHelp ();
162
+ return 1 ;
163
+ }
164
+
165
+ std::vector<std::string> inputVec;
166
+ readInput (argv[2 ], std::back_inserter (inputVec));
167
+
168
+ auto program = parseInput (inputVec);
169
+
170
+ if (task == " part1" ) {
171
+ std::cout << solveFirstPart (program);
172
+ }
173
+ else {
174
+ // std::cout << solveSecondPart(program);
175
+ }
176
+
177
+ return 0 ;
178
+ }
0 commit comments