aboutsummaryrefslogtreecommitdiff
path: root/aoc2023/src/day20/solve.gleam
diff options
context:
space:
mode:
Diffstat (limited to 'aoc2023/src/day20/solve.gleam')
-rw-r--r--aoc2023/src/day20/solve.gleam125
1 files changed, 124 insertions, 1 deletions
diff --git a/aoc2023/src/day20/solve.gleam b/aoc2023/src/day20/solve.gleam
index e589379..449cdeb 100644
--- a/aoc2023/src/day20/solve.gleam
+++ b/aoc2023/src/day20/solve.gleam
@@ -1,8 +1,131 @@
import adglent.{First, Second}
import gleam/io
+import gleam/dict.{type Dict}
+import gleam/queue.{type Queue}
+import gleam/string
+import gleam/list
+import gleam/bool
+
+type Node {
+ Broadcaster(children: List(String))
+ Flipflop(children: List(String), state: Power)
+ Conjunction(children: List(String), state: Dict(String, TonePitch))
+ Ground(children: List(String))
+}
+
+type Tone {
+ Tone(from: String, to: String, pitch: TonePitch)
+}
+
+type Power {
+ On
+ Off
+}
+
+type TonePitch {
+ Low
+ High
+}
+
+fn flip_power(p: Power) -> Power {
+ case p {
+ On -> Off
+ Off -> On
+ }
+}
+
+fn flip_flop_pitch(p: Power) -> TonePitch {
+ case p {
+ Off -> High
+ On -> Low
+ }
+}
+
+fn parse_node(input: String) -> #(String, Node) {
+ let assert [full_name, children_str] = string.split(input, on: " -> ")
+ let children = string.split(children_str, on: ", ")
+
+ case full_name {
+ "%" <> name -> #(name, Flipflop(children: children, state: Off))
+ "&" <> name -> #(name, Conjunction(children: children, state: dict.new()))
+ "broadcaster" -> #("broadcaster", Broadcaster(children: children))
+ name -> #(name, Ground([]))
+ }
+}
+
+fn to_initial_state(nodes: List(#(String, Node))) -> Dict(String, Node) {
+ let node_dict = dict.from_list(nodes)
+ let node_names = dict.keys(node_dict)
+
+ use name, node <- dict.map_values(node_dict)
+ case node {
+ Conjunction(state: _, children: chs) ->
+ node_names
+ |> list.filter(fn(n) {
+ let assert Ok(node) = dict.get(node_dict, n)
+ list.contains(node.children, any: name)
+ })
+ |> list.map(fn(n) { #(n, Low) })
+ |> dict.from_list()
+ |> fn(dict) { Conjunction(state: dict, children: chs) }
+ other -> other
+ }
+}
+
+fn press_button_once(
+ initial: #(Dict(String, Node), Int, Int),
+ queue: Queue(Tone),
+) {
+ let #(nodes, high, low) = initial
+ use <- bool.guard(queue.is_empty(queue), initial)
+ let assert Ok(#(Tone(from_name, to_name, pitch) as tone, rest)) =
+ queue.pop_front(queue)
+ io.debug(tone)
+ let assert Ok(to_node) = dict.get(nodes, to_name)
+ case to_node {
+ Broadcaster(children) ->
+ list.fold(children, rest, fn(acc, c) {
+ queue.push_back(acc, Tone(from: to_name, to: c, pitch: pitch))
+ })
+ |> press_button_once(initial, _)
+
+ Conjunction(state: state, children: children) -> todo
+
+ Flipflop(..) if pitch == High -> press_button_once(initial, rest)
+ Flipflop(state: state, children: children) -> {
+ let updated_nodes =
+ dict.insert(
+ nodes,
+ to_name,
+ Flipflop(state: flip_power(state), children: children),
+ )
+ let updated_queue =
+ list.fold(children, rest, fn(acc, c) {
+ queue.push_back(
+ acc,
+ Tone(from: to_name, to: c, pitch: flip_flop_pitch(state)),
+ )
+ })
+ press_button_once(#(updated_nodes, 0, 0), updated_queue)
+ }
+ Ground(..) -> press_button_once(initial, rest)
+ }
+}
pub fn part1(input: String) {
- todo as "Implement solution to part 1"
+ let initial_state =
+ input
+ |> string.split(on: "\n")
+ |> list.map(parse_node)
+ |> to_initial_state()
+
+ press_button_once(
+ #(initial_state, 0, 1),
+ [Tone("button", "broadcaster", Low)]
+ |> queue.from_list,
+ )
+
+ "1"
}
pub fn part2(input: String) {