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