diff options
-rw-r--r-- | aoc-2020-gleam/src/days/day11.gleam | 20 | ||||
-rw-r--r-- | aoc-2020-gleam/src/days/day12.gleam | 123 | ||||
-rw-r--r-- | aoc-2020-gleam/src/ext/iteratorx.gleam | 9 | ||||
-rw-r--r-- | aoc-2020-gleam/src/util/dir.gleam | 41 | ||||
-rw-r--r-- | aoc-2020-gleam/src/util/pos.gleam | 25 |
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) +} |