diff --git a/src/advent_of_code.gleam b/src/advent_of_code.gleam index 7cd2642..4d9cb78 100644 --- a/src/advent_of_code.gleam +++ b/src/advent_of_code.gleam @@ -2,6 +2,7 @@ import ceres_search as day04 import gleam/int import gleam/io import gleam/result +import guard_gallivant as day06 import historian_hysteria as day01 import mull_it_over as day03 import print_queue as day05 @@ -75,4 +76,17 @@ pub fn main() { }), Nil, ) + + // Day 06 + result.unwrap( + result.map(read("data/day6.txt"), fn(data) { + io.println( + "[6] Print Queue (Part 1): " <> int.to_string(day06.solve_a(data)), + ) + io.println( + "[6] Print Queue (Part 2): " <> int.to_string(day06.solve_b(data)), + ) + }), + Nil, + ) } diff --git a/src/guard_gallivant.gleam b/src/guard_gallivant.gleam new file mode 100644 index 0000000..038457c --- /dev/null +++ b/src/guard_gallivant.gleam @@ -0,0 +1,138 @@ +import gleam/dict.{type Dict} +import gleam/list +import gleam/pair +import gleam/result +import gleam/string +import utils/list as li + +type Entity { + Guard + Obstacle + Path + Unknown(String) +} + +type Point = + #(Int, Int) + +type Grid = + Dict(Point, Entity) + +type Direction { + Up + Down + Left + Right +} + +type State = + #(Point, Direction, Grid, List(Point)) + +fn parse_line(line, y) { + use grid, char, x <- list.index_fold(line, dict.new()) + dict.insert(grid, #(x, y), case char { + "." -> Path + "#" -> Obstacle + "^" -> Guard + uchr -> Unknown(uchr) + }) +} + +fn parse_grid(input: String) -> Grid { + input + |> string.trim() + |> string.split("\n") + |> list.map(string.to_graphemes) + |> list.index_map(parse_line) + |> list.reduce(dict.merge) + |> result.unwrap(dict.new()) +} + +fn is_guard_position(position) -> Bool { + case position { + #(_, entity) if entity == Guard -> True + _ -> False + } +} + +fn guard_position(grid: Grid) -> Result(Point, Nil) { + dict.to_list(grid) + |> list.find(is_guard_position) + |> result.map(fn(set) { pair.first(set) }) +} + +fn init_state(grid: Grid) -> State { + let assert Ok(gp) = guard_position(grid) + #(gp, Up, grid, [gp]) +} + +fn max_x(grid: Grid) -> Int { + li.max(list.map(dict.keys(grid), pair.first)) +} + +fn max_y(grid: Grid) -> Int { + li.max(list.map(dict.keys(grid), pair.second)) +} + +fn update_coordinates(position: Point, direction: Direction) -> Point { + case direction { + Up -> #(pair.first(position), pair.second(position) - 1) + Down -> #(pair.first(position), pair.second(position) + 1) + Left -> #(pair.first(position) - 1, pair.second(position)) + Right -> #(pair.first(position) + 1, pair.second(position)) + } +} + +fn is_obstacle(position: Point, grid: Grid) -> Bool { + case dict.get(grid, position) { + Ok(Obstacle) -> True + _ -> False + } +} + +fn rotate_cw(d: Direction) -> Direction { + case d { + Up -> Right + Down -> Left + Right -> Down + Left -> Up + } +} + +fn next_coordinates(pos: Point, dir: Direction, g: Grid) -> #(Point, Direction) { + let np = update_coordinates(pos, dir) + case is_obstacle(np, g) { + True -> next_coordinates(pos, rotate_cw(dir), g) + False -> #(np, dir) + } +} + +fn guard_exited(pos: Point, g: Grid) -> Bool { + let #(x, y) = pos + x > max_x(g) || x < 0 || y < 0 || y > max_y(g) +} + +fn emulate(state: State) -> State { + let #(gp, gd, g, vs) = state + let #(ngp, ngd) = next_coordinates(gp, gd, g) + case guard_exited(ngp, g) { + True -> state + False -> emulate(#(ngp, ngd, g, list.append(vs, [ngp]))) + } +} + +fn visited_count(st: State) -> Int { + let #(_, _, _, hist) = st + list.length(list.unique(hist)) +} + +pub fn solve_a(input: String) -> Int { + parse_grid(input) + |> init_state + |> emulate + |> visited_count +} + +pub fn solve_b(_input: String) -> Int { + 0 +} diff --git a/src/utils/list.gleam b/src/utils/list.gleam index 8f3f7bd..e38880a 100644 --- a/src/utils/list.gleam +++ b/src/utils/list.gleam @@ -30,3 +30,12 @@ pub fn swap(ls: List(a), ea: a, eb: a) -> List(a) { val -> val } } + +pub fn max(ls: List(Int)) -> Int { + li.fold(ls, 0, fn(cmx, cx) { + case cx { + cxx if cxx > cmx -> cxx + _ -> cmx + } + }) +} diff --git a/test/guard_gallivant_test.gleam b/test/guard_gallivant_test.gleam new file mode 100644 index 0000000..3025c7f --- /dev/null +++ b/test/guard_gallivant_test.gleam @@ -0,0 +1,21 @@ +import gleam/string +import gleeunit/should +import guard_gallivant as day05 + +fn test_data() { + string.join( + [ + "....#.....", ".........#", "..........", "..#.......", ".......#..", + "..........", ".#..^.....", "........#.", "#.........", "......#...", + ], + "\n", + ) +} + +pub fn solve_a_test() { + should.equal(day05.solve_a(test_data()), 41) +} + +pub fn solve_b_test() { + should.equal(day05.solve_b(test_data()), 0) +}