aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--aoc-2020-gleam/src/days/day11.gleam20
-rw-r--r--aoc-2020-gleam/src/days/day12.gleam123
-rw-r--r--aoc-2020-gleam/src/ext/iteratorx.gleam9
-rw-r--r--aoc-2020-gleam/src/util/dir.gleam41
-rw-r--r--aoc-2020-gleam/src/util/pos.gleam25
5 files changed, 203 insertions, 15 deletions
diff --git a/aoc-2020-gleam/src/days/day11.gleam b/aoc-2020-gleam/src/days/day11.gleam
index 1f0344a..db25317 100644
--- a/aoc-2020-gleam/src/days/day11.gleam
+++ b/aoc-2020-gleam/src/days/day11.gleam
@@ -1,6 +1,6 @@
import gleam/io
import gleam/string as str
-import gleam/iterator.{Next} as iter
+import gleam/iterator as iter
import gleam/map.{Map}
import ext/mapx
import ext/setx
@@ -48,8 +48,8 @@ fn build_grid(from input: String) -> Grid {
|> Grid
}
-fn count_near_adjacent(grid: Grid, pos: Pos) -> Int {
- pos
+fn count_near_adjacent(grid: Grid, from start: Pos) -> Int {
+ start
|> pos.neighbours8
|> setx.count(satisfying: fn(n) {
case map.get(grid.data, n) {
@@ -59,14 +59,12 @@ fn count_near_adjacent(grid: Grid, pos: Pos) -> Int {
})
}
-fn count_far_adjacent(grid: Grid, pos: Pos) -> Int {
+fn count_far_adjacent(grid: Grid, from start: 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
+ start
+ |> pos.add(d)
+ |> iterx.unfold_infinitely(pos.add(_, d))
|> iter.take(up_to: 1000)
|> iterx.filter_map(with: map.get(grid.data, _))
|> iter.first
@@ -111,9 +109,7 @@ fn is_stable(grid: Grid, settings: Settings) -> Bool {
fn stabilized_occupied(input: String, settings: Settings) -> Int {
input
|> build_grid
- |> iter.unfold(with: fn(g) {
- Next(element: g, accumulator: step_grid(g, settings))
- })
+ |> iterx.unfold_infinitely(with: step_grid(_, settings))
|> iter.find(one_that: is_stable(_, settings))
|> resx.assert_unwrap
|> count_occupied
diff --git a/aoc-2020-gleam/src/days/day12.gleam b/aoc-2020-gleam/src/days/day12.gleam
new file mode 100644
index 0000000..2986c01
--- /dev/null
+++ b/aoc-2020-gleam/src/days/day12.gleam
@@ -0,0 +1,123 @@
+import gleam/io
+import gleam/int
+import gleam/list
+import gleam/string as str
+import util/input_util
+import util/pos.{Pos}
+import util/dir.{Dir, East, North, South, West}
+
+type Instr {
+ MoveIn(dir: Dir, by: Int)
+ Turn(by: Int)
+ MoveForward(by: Int)
+}
+
+fn parse_instr(line: String) -> Instr {
+ let assert Ok(#(action, value)) = str.pop_grapheme(line)
+ let assert Ok(value) = int.parse(value)
+ case action {
+ "N" -> MoveIn(dir: North, by: value)
+ "E" -> MoveIn(dir: East, by: value)
+ "S" -> MoveIn(dir: South, by: value)
+ "W" -> MoveIn(dir: West, by: value)
+ "L" -> Turn(by: dir.degree_to_turn(-value))
+ "R" -> Turn(by: dir.degree_to_turn(value))
+ "F" -> MoveForward(by: value)
+ }
+}
+
+fn process_moves(
+ lines: List(String),
+ initial: a,
+ execute: fn(a, Instr) -> a,
+ locator: fn(a) -> Pos,
+) -> Int {
+ lines
+ |> list.map(with: parse_instr)
+ |> list.fold(from: initial, with: execute)
+ |> fn(s: a) { pos.manhattan_dist(from: pos.zero, to: locator(s)) }
+}
+
+type State1 {
+ State1(pos: Pos, dir: Dir)
+}
+
+const initial_state1 = State1(pos: pos.zero, dir: East)
+
+fn execute_instr1(prev: State1, instr: Instr) -> State1 {
+ case instr {
+ MoveIn(target, times) ->
+ State1(
+ ..prev,
+ pos: target
+ |> dir.offset
+ |> pos.mul(by: times)
+ |> pos.add(prev.pos),
+ )
+ Turn(times) ->
+ State1(
+ ..prev,
+ dir: prev.dir
+ |> dir.rotate_clockwise(by: times),
+ )
+ MoveForward(times) ->
+ State1(
+ ..prev,
+ pos: prev.dir
+ |> dir.offset
+ |> pos.mul(by: times)
+ |> pos.add(prev.pos),
+ )
+ }
+}
+
+fn part1(lines: List(String)) -> Int {
+ process_moves(lines, initial_state1, execute_instr1, fn(s) { s.pos })
+}
+
+type State2 {
+ State2(ship_pos: Pos, anchor_pos: Pos)
+}
+
+const initial_state2 = State2(ship_pos: pos.zero, anchor_pos: #(10, 1))
+
+fn execute_instr2(prev: State2, instr: Instr) -> State2 {
+ case instr {
+ MoveIn(target, times) ->
+ State2(
+ ..prev,
+ anchor_pos: target
+ |> dir.offset
+ |> pos.mul(by: times)
+ |> pos.add(prev.anchor_pos),
+ )
+ Turn(times) ->
+ State2(
+ ..prev,
+ anchor_pos: pos.rotate_around_origin(this: prev.anchor_pos, by: times),
+ )
+ MoveForward(times) ->
+ State2(
+ ..prev,
+ ship_pos: prev.anchor_pos
+ |> pos.mul(by: times)
+ |> pos.add(prev.ship_pos),
+ )
+ }
+}
+
+fn part2(lines: List(String)) -> Int {
+ process_moves(lines, initial_state2, execute_instr2, fn(s) { s.ship_pos })
+}
+
+pub fn main() -> Nil {
+ let test = input_util.read_lines("test12")
+ let assert 25 = part1(test)
+ let assert 286 = part2(test)
+
+ let input = input_util.read_lines("day12")
+ io.debug(part1(input))
+ io.debug(part2(input))
+
+ Nil
+}
diff --git a/aoc-2020-gleam/src/ext/iteratorx.gleam b/aoc-2020-gleam/src/ext/iteratorx.gleam
index 65e06f2..4e184bd 100644
--- a/aoc-2020-gleam/src/ext/iteratorx.gleam
+++ b/aoc-2020-gleam/src/ext/iteratorx.gleam
@@ -1,4 +1,4 @@
-import gleam/iterator.{Iterator} as iter
+import gleam/iterator.{Iterator, Next} as iter
pub fn length(iterator: Iterator(a)) -> Int {
iterator
@@ -23,3 +23,10 @@ pub fn filter_map(
}
})
}
+
+pub fn unfold_infinitely(from state: a, with fun: fn(a) -> a) -> Iterator(a) {
+ iter.unfold(
+ from: state,
+ with: fn(s) { Next(element: s, accumulator: fun(s)) },
+ )
+}
diff --git a/aoc-2020-gleam/src/util/dir.gleam b/aoc-2020-gleam/src/util/dir.gleam
new file mode 100644
index 0000000..6f637d9
--- /dev/null
+++ b/aoc-2020-gleam/src/util/dir.gleam
@@ -0,0 +1,41 @@
+import gleam/int
+import gleam/iterator as iter
+import ext/resultx as resx
+import ext/iteratorx as iterx
+import util/pos.{Pos}
+
+pub type Dir {
+ North
+ East
+ South
+ West
+}
+
+pub fn offset(direction: Dir) -> Pos {
+ case direction {
+ North -> #(0, 1)
+ East -> #(1, 0)
+ South -> #(0, -1)
+ West -> #(-1, 0)
+ }
+}
+
+pub fn degree_to_turn(degree: Int) -> Int {
+ resx.assert_unwrap(int.modulo(degree / 90, by: 4))
+}
+
+fn rotate_clockwise_once(direction: Dir) -> Dir {
+ case direction {
+ North -> East
+ East -> South
+ South -> West
+ West -> North
+ }
+}
+
+pub fn rotate_clockwise(this direction: Dir, by times: Int) -> Dir {
+ direction
+ |> iterx.unfold_infinitely(with: rotate_clockwise_once)
+ |> iter.at(times)
+ |> resx.assert_unwrap
+}
diff --git a/aoc-2020-gleam/src/util/pos.gleam b/aoc-2020-gleam/src/util/pos.gleam
index a060440..dd3d01d 100644
--- a/aoc-2020-gleam/src/util/pos.gleam
+++ b/aoc-2020-gleam/src/util/pos.gleam
@@ -1,9 +1,12 @@
+import gleam/int
import gleam/list
import gleam/set.{Set}
pub type Pos =
#(Int, Int)
+pub const zero = #(0, 0)
+
pub const directions8 = [
#(1, 0),
#(1, 1),
@@ -19,8 +22,26 @@ pub fn add(p1: Pos, p2: Pos) -> Pos {
#(p1.0 + p2.0, p1.1 + p2.1)
}
-pub fn neighbours8(pos: Pos) -> Set(Pos) {
+pub fn sub(p1: Pos, p2: Pos) -> Pos {
+ #(p1.0 - p2.0, p1.1 - p2.1)
+}
+
+pub fn mul(p: Pos, by scalar: Int) -> Pos {
+ #(p.0 * scalar, p.1 * scalar)
+}
+
+pub fn neighbours8(p: Pos) -> Set(Pos) {
directions8
- |> list.map(with: add(pos, _))
+ |> list.map(with: add(p, _))
|> set.from_list
}
+
+pub fn manhattan_dist(from p1: Pos, to p2: Pos) -> Int {
+ int.absolute_value(p1.0 - p2.0) + int.absolute_value(p1.1 - p2.1)
+}
+
+pub fn rotate_around_origin(this p: Pos, by times: Int) -> Pos {
+ let assert Ok(sin) = list.at([0, -1, 0, 1], times)
+ let assert Ok(cos) = list.at([1, 0, -1, 0], times)
+ #(p.0 * cos - p.1 * sin, p.0 * sin + p.1 * cos)
+}