1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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
}
|