Skip to content

Commit 1691bd6

Browse files
committed
Solve Day 20 Part 1
1 parent 5059628 commit 1691bd6

File tree

3 files changed

+285
-2
lines changed

3 files changed

+285
-2
lines changed

CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
77
set(CMAKE_CXX_EXTENSIONS OFF)
88

99
if (APPLE OR UNIX OR LINUX)
10-
set(WARNING_FLAGS_CXX -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wno-newline-eof -Wno-unused-function -Wno-unused-parameter)
10+
set(WARNING_FLAGS_CXX -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wshadow -Wundef -Wstrict-overflow=5 -Wsign-promo -Wcast-align -Wcast-qual -Woverloaded-virtual -Wredundant-decls -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wswitch-default -Wno-newline-eof -Wno-unused-function -Wno-unused-parameter)
1111
set(CMAKE_CXX_COMPILER "/usr/bin/clang++")
1212
endif ()
1313

@@ -34,7 +34,7 @@ if(ipo_available AND (NOT CMAKE_BUILD_TYPE MATCHES Debug) AND (NOT CMAKE_BUILD_T
3434
message("-- IPO enabled")
3535
endif()
3636

37-
set(TARGETS day-01 day-02 day-03 day-04 day-05 day-06 day-07 day-08 day-09 day-10 day-11 day-12 day-13 day-14 day-15 day-16 day-17 day-18 day-19) # Add the other days as you please.
37+
set(TARGETS day-01 day-02 day-03 day-04 day-05 day-06 day-07 day-08 day-09 day-10 day-11 day-12 day-13 day-14 day-15 day-16 day-17 day-18 day-19 day-20) # Add the other days as you please.
3838

3939
list(LENGTH TARGETS NUM_TARGETS)
4040

@@ -81,6 +81,7 @@ add_custom_target("run-all"
8181
COMMAND day-17
8282
COMMAND day-18
8383
COMMAND day-19
84+
COMMAND day-20
8485

8586
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin
8687
)

day-20/day-20.cpp

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
#include <unordered_map>
2+
#include <set>
3+
#include <array>
4+
#include <queue>
5+
#include "../aoclib/aocio.hpp"
6+
7+
/*
8+
Problem: https://adventofcode.com/2023/day/20
9+
10+
Solutions:
11+
- Part 1: 912199500
12+
- Part 2:
13+
Notes:
14+
15+
*/
16+
17+
enum class Pulse {Low, High};
18+
19+
class Network;
20+
21+
class Module
22+
{
23+
protected:
24+
std::unordered_map<std::string, Pulse> input_pulses;
25+
std::vector<std::string> outputs;
26+
Pulse state;
27+
virtual void update_state() {};
28+
29+
public:
30+
friend Network;
31+
const std::string name;
32+
33+
Module(const std::string& name, const std::vector<std::string>& outputs) : outputs(outputs), name(name)
34+
{
35+
state = Pulse::Low;
36+
};
37+
38+
virtual ~Module() = default;
39+
40+
// Returns true if the module will send a pulse.
41+
virtual bool receive_pulse(const std::string& sender_name, Pulse p)
42+
{
43+
assert(input_pulses.contains(sender_name));
44+
input_pulses.at(sender_name) = p;
45+
return true;
46+
}
47+
48+
virtual void forward_pulse(Network& network, std::set<std::string>& sends_pulse, bool print = false); // Defined after Network.
49+
};
50+
51+
class Network
52+
{
53+
public:
54+
std::unordered_map<std::string, std::unique_ptr<Module>> modules;
55+
56+
void insert_module(std::unique_ptr<Module> mod)
57+
{
58+
if (modules.contains(mod->name)) {
59+
throw std::invalid_argument("Network::insert_module: Duplicate module");
60+
}
61+
modules.insert({mod->name, std::move(mod)});
62+
update_connections();
63+
}
64+
65+
int64_t push_button(int n = 1)
66+
{
67+
int low_pulses = 0;
68+
int high_pulses = 0;
69+
70+
for (int i = 0; i < n; ++i) {
71+
low_pulses += 1;
72+
std::queue<Module*> queue;
73+
queue.push(modules.at("broadcaster").get());
74+
75+
while (!queue.empty()) {
76+
Module *mod = queue.front();
77+
queue.pop();
78+
79+
std::set<std::string> will_send_pulse;
80+
mod->forward_pulse(*this, will_send_pulse);
81+
if (mod->state == Pulse::Low) {
82+
low_pulses += mod->outputs.size();
83+
} else {
84+
high_pulses += mod->outputs.size();
85+
}
86+
for (const std::string& out_name : will_send_pulse) {
87+
assert(modules.contains(out_name));
88+
queue.push(modules.at(out_name).get());
89+
}
90+
}
91+
}
92+
return low_pulses * high_pulses;
93+
}
94+
95+
private:
96+
void update_connections()
97+
{
98+
for (auto& [input_name, input_mod] : modules) {
99+
for (const std::string& output_name : input_mod->outputs) {
100+
if (!modules.contains(output_name)) {
101+
continue;
102+
}
103+
Module *output_mod = modules.at(output_name).get();
104+
output_mod->input_pulses.insert_or_assign(input_name, Pulse::Low);
105+
}
106+
}
107+
}
108+
};
109+
110+
void Module::forward_pulse(Network& network, std::set<std::string>& will_send_pulse, bool print)
111+
{
112+
update_state();
113+
for (const std::string& output_name : outputs) {
114+
if (!network.modules.contains(output_name)) {
115+
continue;
116+
}
117+
Module *out_mod = network.modules.at(output_name).get();
118+
if (print) {
119+
std::string state_str = state == Pulse::Low ? " -low-> " : " -high-> ";
120+
std::cout << name << state_str << output_name << "\n";
121+
}
122+
bool will_send = out_mod->receive_pulse(name, state);
123+
if (will_send) {
124+
will_send_pulse.insert(output_name);
125+
}
126+
}
127+
}
128+
129+
class FlipFlop : public Module
130+
{
131+
private:
132+
bool on = false;
133+
134+
public:
135+
FlipFlop(const std::string& name, const std::vector<std::string>& outputs) : Module(name, outputs)
136+
{
137+
state = Pulse::Low;
138+
}
139+
140+
bool receive_pulse(const std::string& sender_name, Pulse p) override
141+
{
142+
if (p == Pulse::High) {
143+
return false;
144+
} else {
145+
state = on ? Pulse::Low : Pulse::High;
146+
on = !on;
147+
return true;
148+
}
149+
}
150+
151+
};
152+
153+
class Conjunction : public Module
154+
{
155+
public:
156+
Conjunction(const std::string& name, const std::vector<std::string>& outputs) : Module(name, outputs)
157+
{
158+
state = Pulse::Low;
159+
}
160+
161+
protected:
162+
void update_state() override
163+
{
164+
auto input_low = std::find_if(input_pulses.cbegin(), input_pulses.cend(), [](const auto& name_pulse) {return name_pulse.second == Pulse::Low;});
165+
bool all_inputs_high = input_low == input_pulses.cend();
166+
if (all_inputs_high) {
167+
state = Pulse::Low;
168+
} else {
169+
state = Pulse::High;
170+
}
171+
}
172+
};
173+
174+
class Broadcast : public Module
175+
{
176+
public:
177+
Broadcast(const std::string& name, const std::vector<std::string>& outputs) : Module(name, outputs)
178+
{
179+
state = Pulse::Low;
180+
}
181+
182+
bool receive_pulse(const std::string& sender_name, Pulse p) override
183+
{
184+
state = p;
185+
return true;
186+
}
187+
};
188+
189+
void parse_network(const std::vector<std::string>& lines, Network& network)
190+
{
191+
for (std::string line : lines) {
192+
aocio::str_remove_whitespace(line);
193+
if (!line.size()) {
194+
continue;
195+
}
196+
std::size_t arrow_idx = line.find("->", 0);
197+
if (arrow_idx == std::string::npos) {
198+
throw std::invalid_argument("parse_network: Missing arrow");
199+
}
200+
201+
std::string lhs = line.substr(0, arrow_idx);
202+
const std::string& rhs = line.substr(arrow_idx + 2, line.size());
203+
204+
if (lhs.size() < 2 || rhs.size() < 1) {
205+
throw std::invalid_argument("parse_network: Assignment too short");
206+
}
207+
208+
std::vector<std::string> dest_mods;
209+
aocio::line_tokenise(rhs, ",", "", dest_mods);
210+
211+
if (dest_mods.size() == 0) {
212+
throw std::invalid_argument("parse_network: No destination modules");
213+
}
214+
215+
const char module_sym = lhs.at(0);
216+
217+
if (module_sym != '%' && module_sym != '&') {
218+
if (lhs != "broadcaster") {
219+
throw std::invalid_argument("parse_network: Invalid module type");
220+
}
221+
std::unique_ptr<Module> mod = std::make_unique<Broadcast>(lhs, dest_mods);
222+
network.insert_module(std::move(mod));
223+
} else {
224+
lhs = lhs.substr(1, lhs.size());
225+
if (module_sym == '%') {
226+
std::unique_ptr<Module> mod = std::make_unique<FlipFlop>(lhs, dest_mods);
227+
network.insert_module(std::move(mod));
228+
} else {
229+
std::unique_ptr<Module> mod = std::make_unique<Conjunction>(lhs, dest_mods);
230+
network.insert_module(std::move(mod));
231+
}
232+
}
233+
}
234+
}
235+
236+
237+
int64_t part_one(const std::vector<std::string>& lines)
238+
{
239+
Network network;
240+
parse_network(lines, network);
241+
return network.push_button(1000);
242+
}
243+
244+
int64_t part_two(const std::vector<std::string>& lines)
245+
{
246+
return -1;
247+
}
248+
249+
int main()
250+
{
251+
aocio::print_day();
252+
std::vector<std::string> lines;
253+
std::string_view fname = AOC_INPUT_DIR"input.txt";
254+
bool file_loaded = aocio::file_getlines(fname, lines);
255+
if (!file_loaded) {
256+
std::cerr << "Error: " << "File '" << fname << "' not found\n";
257+
return EXIT_FAILURE;
258+
}
259+
260+
aocio::remove_leading_empty_lines(lines);
261+
if (!lines.size()) {
262+
std::cerr << "Error: " << "Input is empty";
263+
return EXIT_FAILURE;
264+
}
265+
266+
try {
267+
int64_t p1 = part_one(lines);
268+
std::cout << "Part 1: " << p1 << "\n";
269+
int64_t p2 = part_two(lines);
270+
std::cout << "Part 2: " << p2 << "\n";
271+
} catch (const std::exception& err) {
272+
std::cerr << "Error: " << err.what() << "\n";
273+
return EXIT_FAILURE;
274+
}
275+
276+
return EXIT_SUCCESS;
277+
}

day-20/input-example.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
broadcaster -> a
2+
%a -> inv, con
3+
&inv -> b
4+
%b -> con
5+
&con -> output

0 commit comments

Comments
 (0)