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
119
|
import adglent.{First, Second}
import gleam/bool
import gleam/dict.{type Dict}
import gleam/io
import gleam/list
import gleam/result
import gleam/set.{type Set}
import utilities/array2d.{type Posn, Posn}
type Direction {
Up
Right
Down
Left
}
type Light {
Light(posn: Posn, dir: Direction)
}
fn move(l: Light) -> Light {
let Light(p, dir) = l
case dir {
Up -> Light(..l, posn: Posn(..p, r: p.r - 1))
Down -> Light(..l, posn: Posn(..p, r: p.r + 1))
Left -> Light(..l, posn: Posn(..p, c: p.c - 1))
Right -> Light(..l, posn: Posn(..p, c: p.c + 1))
}
}
fn transform(l: Light, cell: Result(String, Nil)) -> List(Light) {
use <- bool.guard(result.is_error(cell), [])
let assert Ok(c) = cell
let Light(p, dir) = l
case dir, c {
// no change
_, "." | Up, "|" | Down, "|" | Left, "-" | Right, "-" -> [l]
// diagonal mirrors
Left, "/" -> [Light(p, Down)]
Down, "/" -> [Light(p, Left)]
Right, "/" -> [Light(p, Up)]
Up, "/" -> [Light(p, Right)]
Left, "\\" -> [Light(p, Up)]
Up, "\\" -> [Light(p, Left)]
Right, "\\" -> [Light(p, Down)]
Down, "\\" -> [Light(p, Right)]
// splitters
Left, "|" | Right, "|" -> [Light(p, Up), Light(p, Down)]
Up, "-" | Down, "-" -> [Light(p, Left), Light(p, Right)]
_, _ -> panic as "unrecognized cell type"
}
}
fn energize(lights: List(Light), visited: Set(Light), grid: Dict(Posn, String)) {
let next_positions =
lights
|> list.flat_map(fn(l) {
let next = move(l)
transform(next, dict.get(grid, next.posn))
})
|> list.filter(fn(l) { !set.contains(visited, l) })
let all_visited = set.union(set.from_list(next_positions), visited)
case visited == all_visited {
True ->
set.fold(visited, set.new(), fn(acc, l) { set.insert(acc, l.posn) })
|> set.to_list
|> list.length
False -> energize(next_positions, all_visited, grid)
}
}
pub fn part1(input: String) {
let grid = array2d.parse_grid(input)
[Light(Posn(0, -1), Right)]
|> energize(set.new(), grid)
}
pub fn part2(input: String) {
let grid = array2d.parse_grid(input)
let Posn(rows, cols) = {
use acc, p <- list.fold(dict.keys(grid), Posn(0, 0))
case acc.r + acc.c > p.r + p.c {
True -> acc
False -> p
}
}
let all_starts =
list.concat([
list.map(list.range(0, rows), fn(r) { Light(Posn(r, -1), Right) }),
list.map(list.range(0, rows), fn(r) { Light(Posn(r, cols + 1), Left) }),
list.map(list.range(0, cols), fn(c) { Light(Posn(-1, c), Down) }),
list.map(list.range(0, cols), fn(c) { Light(Posn(rows + 1, c), Up) }),
])
use acc, p <- list.fold(all_starts, 0)
let energized = energize([p], set.new(), grid)
case acc > energized {
True -> acc
False -> energized
}
}
pub fn main() {
let assert Ok(part) = adglent.get_part()
let assert Ok(input) = adglent.get_input("16")
case part {
First ->
part1(input)
|> adglent.inspect
|> io.println
Second ->
part2(input)
|> adglent.inspect
|> io.println
}
}
|