diff options
-rw-r--r-- | aoc-2020-gleam/src/days/day03.gleam | 10 | ||||
-rw-r--r-- | aoc-2020-gleam/src/days/day11.gleam | 140 | ||||
-rw-r--r-- | aoc-2020-gleam/src/ext/mapx.gleam | 16 | ||||
-rw-r--r-- | aoc-2020-gleam/src/ext/setx.gleam | 10 | ||||
-rw-r--r-- | aoc-2020-gleam/src/util/pos.gleam | 26 |
5 files changed, 194 insertions, 8 deletions
diff --git a/aoc-2020-gleam/src/days/day03.gleam b/aoc-2020-gleam/src/days/day03.gleam index 3a62419..0778eea 100644 --- a/aoc-2020-gleam/src/days/day03.gleam +++ b/aoc-2020-gleam/src/days/day03.gleam @@ -9,9 +9,7 @@ import ext/intx import ext/resultx as resx import ext/iteratorx as iterx import util/input_util - -type Pos = - #(Int, Int) +import util/pos.{Pos} const starting_pos = #(0, 0) @@ -19,10 +17,6 @@ const base_slope = #(3, 1) const all_slopes = [#(1, 1), base_slope, #(5, 1), #(7, 1), #(1, 2)] -fn add(p1: Pos, p2: Pos) -> Pos { - #(p1.0 + p2.0, p1.1 + p2.1) -} - type Area { Area(trees: Set(Pos), cycle: Int, height: Int) } @@ -68,7 +62,7 @@ fn is_valid(pos: Pos, in area: Area) -> Bool { fn tree_count(in area: Area, with slope: Pos) -> Int { starting_pos - |> iter.iterate(with: add(_, slope)) + |> iter.iterate(with: pos.add(_, slope)) |> iter.take_while(satisfying: is_valid(_, in: area)) |> iterx.count(satisfying: has_tree(in: area, at: _)) } diff --git a/aoc-2020-gleam/src/days/day11.gleam b/aoc-2020-gleam/src/days/day11.gleam new file mode 100644 index 0000000..1f0344a --- /dev/null +++ b/aoc-2020-gleam/src/days/day11.gleam @@ -0,0 +1,140 @@ +import gleam/io +import gleam/string as str +import gleam/iterator.{Next} as iter +import gleam/map.{Map} +import ext/mapx +import ext/setx +import ext/listx +import ext/resultx as resx +import ext/genericx as genx +import ext/iteratorx as iterx +import util/input_util +import util/pos.{Pos} + +type Seat { + Empty + Occupied +} + +type Grid { + Grid(data: Map(Pos, Seat)) +} + +type Settings { + Settings(threshold: Int, adjacent_counter: fn(Grid, Pos) -> Int) +} + +fn build_grid(from input: String) -> Grid { + input + |> str.split(on: "\n") + |> iter.from_list + |> iter.map(with: str.trim) + |> iter.index + |> iter.flat_map(with: fn(line) { + let #(row_index, row) = line + row + |> str.to_graphemes + |> iter.from_list + |> iter.index + |> iter.flat_map(with: fn(elem) { + let #(col_index, grapheme) = elem + case grapheme == "L" { + True -> iter.single(#(#(col_index, row_index), Empty)) + False -> iter.empty() + } + }) + }) + |> mapx.from_iter + |> Grid +} + +fn count_near_adjacent(grid: Grid, pos: Pos) -> Int { + pos + |> pos.neighbours8 + |> setx.count(satisfying: fn(n) { + case map.get(grid.data, n) { + Ok(seat) -> seat == Occupied + Error(Nil) -> False + } + }) +} + +fn count_far_adjacent(grid: Grid, pos: Pos) -> Int { + pos.directions8 + |> listx.count(satisfying: fn(d) { + iter.unfold( + from: pos.add(pos, d), + with: fn(p) { Next(element: p, accumulator: pos.add(p, d)) }, + ) + // Bigger than the largest map size + |> iter.take(up_to: 1000) + |> iterx.filter_map(with: map.get(grid.data, _)) + |> iter.first + |> genx.equals(Ok(Occupied)) + }) +} + +fn count_occupied(grid: Grid) -> Int { + grid.data + |> map.values + |> listx.count(satisfying: genx.equals(_, Occupied)) +} + +fn step_grid(prev: Grid, settings: Settings) -> Grid { + let Settings(threshold, adjacent_counter) = settings + prev.data + |> map.map_values(with: fn(pos, seat) { + let adjacent = adjacent_counter(prev, pos) + case seat { + Empty if adjacent == 0 -> Occupied + Occupied if adjacent >= threshold -> Empty + other -> other + } + }) + |> Grid +} + +fn is_stable(grid: Grid, settings: Settings) -> Bool { + let Settings(threshold, adjacent_counter) = settings + grid.data + |> mapx.to_iter + |> iter.all(satisfying: fn(entry) { + let #(pos, seat) = entry + let adjacent = adjacent_counter(grid, pos) + case seat { + Empty -> adjacent > 0 + Occupied -> adjacent < threshold + } + }) +} + +fn stabilized_occupied(input: String, settings: Settings) -> Int { + input + |> build_grid + |> iter.unfold(with: fn(g) { + Next(element: g, accumulator: step_grid(g, settings)) + }) + |> iter.find(one_that: is_stable(_, settings)) + |> resx.assert_unwrap + |> count_occupied +} + +fn part1(input: String) -> Int { + stabilized_occupied(input, Settings(4, count_near_adjacent)) +} + +fn part2(input: String) -> Int { + stabilized_occupied(input, Settings(5, count_far_adjacent)) +} + +pub fn main() -> Nil { + let test = input_util.read_text("test11") + let assert 37 = part1(test) + let assert 26 = part2(test) + + let input = input_util.read_text("day11") + io.debug(part1(input)) + io.debug(part2(input)) + + Nil +} diff --git a/aoc-2020-gleam/src/ext/mapx.gleam b/aoc-2020-gleam/src/ext/mapx.gleam new file mode 100644 index 0000000..244c1ac --- /dev/null +++ b/aoc-2020-gleam/src/ext/mapx.gleam @@ -0,0 +1,16 @@ +import gleam/map.{Map} +import gleam/iterator.{Iterator} as iter + +pub fn from_iter(iterator: Iterator(#(k, v))) -> Map(k, v) { + iter.fold( + over: iterator, + from: map.new(), + with: fn(acc, cur) { map.insert(acc, cur.0, cur.1) }, + ) +} + +pub fn to_iter(map: Map(k, v)) -> Iterator(#(k, v)) { + map + |> map.to_list + |> iter.from_list +} diff --git a/aoc-2020-gleam/src/ext/setx.gleam b/aoc-2020-gleam/src/ext/setx.gleam new file mode 100644 index 0000000..57e368e --- /dev/null +++ b/aoc-2020-gleam/src/ext/setx.gleam @@ -0,0 +1,10 @@ +import gleam/set.{Set} +import gleam/iterator as iter +import ext/iteratorx as iterx + +pub fn count(set: Set(a), satisfying predicate: fn(a) -> Bool) -> Int { + set + |> set.to_list + |> iter.from_list + |> iterx.count(satisfying: predicate) +} diff --git a/aoc-2020-gleam/src/util/pos.gleam b/aoc-2020-gleam/src/util/pos.gleam new file mode 100644 index 0000000..a060440 --- /dev/null +++ b/aoc-2020-gleam/src/util/pos.gleam @@ -0,0 +1,26 @@ +import gleam/list +import gleam/set.{Set} + +pub type Pos = + #(Int, Int) + +pub const directions8 = [ + #(1, 0), + #(1, 1), + #(0, 1), + #(-1, 1), + #(-1, 0), + #(-1, -1), + #(0, -1), + #(1, -1), +] + +pub fn add(p1: Pos, p2: Pos) -> Pos { + #(p1.0 + p2.0, p1.1 + p2.1) +} + +pub fn neighbours8(pos: Pos) -> Set(Pos) { + directions8 + |> list.map(with: add(pos, _)) + |> set.from_list +} |