diff options
author | Tomasz Chojnacki <tomaszchojnacki2001@gmail.com> | 2023-08-03 20:41:12 +0200 |
---|---|---|
committer | Tomasz Chojnacki <tomaszchojnacki2001@gmail.com> | 2023-08-03 20:41:12 +0200 |
commit | 02598d252c0e9093384ec53e46445df1a783feda (patch) | |
tree | 8456b190d82205587cceb22f1fcb351a21b87769 | |
parent | f5a3414d089bbfc06c6afe4f2e083600fd229dc5 (diff) | |
download | gleam_aoc2020-02598d252c0e9093384ec53e46445df1a783feda.tar.gz gleam_aoc2020-02598d252c0e9093384ec53e46445df1a783feda.zip |
Finish day 23
-rw-r--r-- | aoc-2020-gleam/src/days/day23.gleam | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/aoc-2020-gleam/src/days/day23.gleam b/aoc-2020-gleam/src/days/day23.gleam new file mode 100644 index 0000000..8b58fa7 --- /dev/null +++ b/aoc-2020-gleam/src/days/day23.gleam @@ -0,0 +1,118 @@ +import gleam/io +import gleam/int +import gleam/bool +import gleam/string as str +import gleam/function as fun +import gleam/iterator as iter +import gleam/map.{Map} +import gleam/list.{Continue, Stop} +import ext/resultx as resx +import ext/iteratorx as iterx + +fn parse_input(input: String) -> List(Int) { + input + |> str.to_graphemes + |> list.map(with: fun.compose(int.parse, resx.assert_unwrap)) +} + +fn play(input: String, bound: Int, moves: Int) -> Map(Int, Int) { + let assert [first, ..tail] = parse_input(input) + let assert Ok(second) = list.first(tail) + + tail + |> list.append(case bound >= 10 { + True -> list.range(from: 10, to: bound) + False -> [] + }) + |> build_map(map.from_list([#(first, second)]), first) + |> move(first, moves, bound) +} + +fn move( + cups: Map(Int, Int), + current: Int, + round: Int, + bound: Int, +) -> Map(Int, Int) { + use <- bool.guard(when: round == 0, return: cups) + + // Remove nodes from source + let assert Ok(first) = map.get(cups, current) + let assert Ok(second) = map.get(cups, first) + let assert Ok(third) = map.get(cups, second) + let assert Ok(rest) = map.get(cups, third) + let cups = map.insert(into: cups, for: current, insert: rest) + + // Insert nodes at destination + let assert Ok(before) = + iter.iterate(from: current - 1, with: int.subtract(_, 1)) + |> iter.map(with: fn(n) { resx.assert_unwrap(int.modulo(n - 1, bound)) + 1 }) + |> iter.find(one_that: fn(key) { + !list.contains([first, second, third], key) + }) + let assert Ok(after) = map.get(cups, before) + let cups = + cups + |> map.insert(for: before, insert: first) + |> map.insert(for: third, insert: after) + + cups + |> move( + cups + |> map.get(current) + |> resx.assert_unwrap, + round - 1, + bound, + ) +} + +fn build_map(list: List(Int), cups: Map(Int, Int), first: Int) -> Map(Int, Int) { + case list { + [] -> map.new() + [head] -> map.insert(into: cups, for: head, insert: first) + [head, ..tail] -> { + let assert Ok(second) = list.first(tail) + build_map(tail, map.insert(into: cups, for: head, insert: second), first) + } + } +} + +fn to_result_string(cups: Map(Int, Int)) -> String { + iterx.unfold_infinitely( + from: 1, + with: fn(key) { resx.assert_unwrap(map.get(cups, key)) }, + ) + |> iter.drop(1) + |> iter.fold_until( + from: "", + with: fn(acc, key) { + use <- bool.guard(when: key == 1, return: Stop(acc)) + Continue(acc <> int.to_string(key)) + }, + ) +} + +fn part1(input: String) -> String { + input + |> play(9, 100) + |> to_result_string +} + +fn part2(input: String) -> Int { + let finished = play(input, 1_000_000, 10_000_000) + let assert Ok(first) = map.get(finished, 1) + let assert Ok(second) = map.get(finished, first) + first * second +} + +pub fn main() -> Nil { + let test = "389125467" + let assert "67384529" = part1(test) + let assert 149_245_887_792 = part2(test) + + let input = "925176834" + io.println(part1(input)) + io.debug(part2(input)) + + Nil +} |