108 lines
2.3 KiB
Gleam
108 lines
2.3 KiB
Gleam
import gleam/dict.{type Dict}
|
|
import gleam/io
|
|
import gleam/iterator.{Done, Next}
|
|
import gleam/list.{filter, fold, map}
|
|
import gleam/pair
|
|
import gleam/result
|
|
import gleam/string
|
|
import stdin.{stdin}
|
|
|
|
pub fn main() {
|
|
stdin()
|
|
|> read_input
|
|
|> calculate_resonance
|
|
|> io.debug
|
|
}
|
|
|
|
fn calculate_resonance(chart: Map) -> Int {
|
|
let Map(w, h, antennas) = chart
|
|
|
|
antennas
|
|
|> dict.fold([], fn(acc, _, coords) {
|
|
let antinodes =
|
|
coords
|
|
|> ordered_combinations
|
|
|> map(fn(coord) {
|
|
let #(dx, dy) = resolve_antinode_frequency(coord)
|
|
// Spawn antinodes until out ot bounds
|
|
coord
|
|
|> pair.first
|
|
|> iterator.unfold(fn(pos) {
|
|
let #(x, y) = pos
|
|
let newpos = #(x + dx, y + dy)
|
|
case newpos {
|
|
#(tx, ty) if tx < 0 || tx >= w || ty < 0 || ty >= h -> Done
|
|
_ -> Next(newpos, newpos)
|
|
}
|
|
})
|
|
|> iterator.to_list
|
|
})
|
|
|> list.flatten
|
|
|
|
[coords, antinodes, acc] |> list.flatten
|
|
})
|
|
|> list.unique
|
|
|> list.length
|
|
}
|
|
|
|
fn resolve_antinode_frequency(
|
|
coords: #(#(Int, Int), #(Int, Int)),
|
|
) -> #(Int, Int) {
|
|
let #(#(ax, ay), #(bx, by)) = coords
|
|
|
|
#(ax - bx, ay - by)
|
|
}
|
|
|
|
fn ordered_combinations(items: List(a)) -> List(#(a, a)) {
|
|
items
|
|
|> list.combination_pairs
|
|
|> map(fn(x) { [x, pair.swap(x)] })
|
|
|> list.flatten
|
|
}
|
|
|
|
pub type Map {
|
|
Map(width: Int, height: Int, antennas: Dict(String, List(#(Int, Int))))
|
|
}
|
|
|
|
fn read_input(it: iterator.Iterator(String)) -> Map {
|
|
let maptxt =
|
|
it
|
|
|> iterator.to_list
|
|
|> map(string.trim)
|
|
|> filter(not(string.is_empty))
|
|
|
|
let assert Ok(width) = list.first(maptxt) |> result.map(string.length)
|
|
let height = list.length(maptxt)
|
|
let antennas =
|
|
maptxt
|
|
|> list.index_fold([], find_antennas)
|
|
|> fold(dict.new(), fn(acc, v) {
|
|
let #(group, coord) = v
|
|
let current =
|
|
acc
|
|
|> dict.get(group)
|
|
|> result.unwrap([])
|
|
dict.insert(acc, group, [coord, ..current])
|
|
})
|
|
|
|
Map(width, height, antennas)
|
|
}
|
|
|
|
fn find_antennas(
|
|
acc: List(#(String, #(Int, Int))),
|
|
line: String,
|
|
y: Int,
|
|
) -> List(#(String, #(Int, Int))) {
|
|
line
|
|
|> string.to_graphemes
|
|
|> list.index_fold(acc, fn(cur, chr, x) {
|
|
case chr {
|
|
"." -> cur
|
|
c -> [#(c, #(x, y)), ..cur]
|
|
}
|
|
})
|
|
}
|
|
|
|
fn not(a: fn(a) -> Bool) {
|
|
fn(param: a) -> Bool { !a(param) }
|
|
}
|