Skip to content

Commit

Permalink
Day 15
Browse files Browse the repository at this point in the history
  • Loading branch information
drewolson committed Dec 15, 2024
1 parent 9a2e54e commit 6f2ce7d
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/aoc/runner/year2024.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import aoc/year2024/day11
import aoc/year2024/day12
import aoc/year2024/day13
import aoc/year2024/day14
import aoc/year2024/day15
import gleam/string

pub fn run(input: String, day: Int, part: Int) {
Expand Down Expand Up @@ -44,6 +45,8 @@ pub fn run(input: String, day: Int, part: Int) {
13, 2 -> input |> day13.part2 |> string.inspect
14, 1 -> input |> day14.part1(101, 103) |> string.inspect
14, 2 -> input |> day14.part2(101, 103) |> string.inspect
15, 1 -> input |> day15.part1 |> string.inspect
15, 2 -> input |> day15.part2 |> string.inspect
_, _ ->
"Unknown day and part for 2024: day "
<> string.inspect(day)
Expand Down
160 changes: 160 additions & 0 deletions src/aoc/year2024/day15.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import aoc/util/str
import gleam/dict.{type Dict}
import gleam/list
import gleam/result
import gleam/string

type Coord =
#(Int, Int)

type Grid =
Dict(Coord, String)

type Dir {
Up
Down
Left
Right
}

fn make_grid(str: String, f: fn(String) -> List(String)) -> Grid {
str
|> str.lines
|> list.index_map(fn(l, y) {
l
|> string.to_graphemes
|> list.flat_map(f)
|> list.index_map(fn(c, x) { #(#(x, y), c) })
})
|> list.flatten
|> dict.from_list
}

fn parse(input: String, f: fn(String) -> List(String)) -> #(Grid, List(Dir)) {
let assert Ok(#(a, b)) = string.split_once(input, "\n\n")
let dirs =
b
|> str.lines
|> list.flat_map(fn(l) { string.to_graphemes(l) })
|> list.filter_map(fn(c) {
case c {
"^" -> Ok(Up)
"v" -> Ok(Down)
"<" -> Ok(Left)
">" -> Ok(Right)
_ -> Error(Nil)
}
})
#(make_grid(a, f), dirs)
}

fn find_robot(grid: Grid) -> Coord {
grid
|> dict.to_list
|> list.find(fn(p) { p.1 == "@" })
|> result.map(fn(p) { p.0 })
|> result.unwrap(#(0, 0))
}

fn next(grid: Grid, coord: Coord, dir: Dir) -> Result(#(Coord, String), Nil) {
let #(x, y) = coord
let next_c = case dir {
Up -> #(x, y - 1)
Down -> #(x, y + 1)
Left -> #(x - 1, y)
Right -> #(x + 1, y)
}
use v <- result.map(dict.get(grid, next_c))
#(next_c, v)
}

fn push(
grid: Grid,
coord: Coord,
v: String,
rep: String,
dir: Dir,
) -> Result(Grid, Nil) {
let grid = dict.insert(grid, coord, rep)
use #(next_c, next_v) <- result.try(next(grid, coord, dir))
let grid = dict.insert(grid, next_c, v)
case next_v {
"." -> Ok(grid)
"O" -> push(grid, next_c, next_v, v, dir)
_ -> Error(Nil)
}
}

fn push2(
grid: Grid,
coord: Coord,
v: String,
rep: String,
dir: Dir,
) -> Result(Grid, Nil) {
let grid = dict.insert(grid, coord, rep)
use #(next_c, next_v) <- result.try(next(grid, coord, dir))
let grid = dict.insert(grid, next_c, v)
case next_v {
"." -> Ok(grid)
"[" | "]" if dir == Left || dir == Right ->
push2(grid, next_c, next_v, v, dir)
"[" -> {
use grid <- result.try(push2(grid, next_c, next_v, v, dir))
push2(grid, #(next_c.0 + 1, next_c.1), "]", ".", dir)
}
"]" -> {
use grid <- result.try(push2(grid, next_c, next_v, v, dir))
push2(grid, #(next_c.0 - 1, next_c.1), "[", ".", dir)
}
_ -> Error(Nil)
}
}

fn expand(c: String) -> List(String) {
case c {
"#" -> ["#", "#"]
"O" -> ["[", "]"]
"@" -> ["@", "."]
_ -> [".", "."]
}
}

pub fn part1(input: String) -> Int {
let #(grid, dirs) = parse(input, fn(c) { [c] })
let robot = find_robot(grid)
let #(grid, _) =
list.fold(dirs, #(grid, robot), fn(acc, dir) {
let #(grid, robot) = acc
let res = {
use next_g <- result.try(push(grid, robot, "@", ".", dir))
use #(next_r, _) <- result.map(next(grid, robot, dir))
#(next_g, next_r)
}
result.unwrap(res, acc)
})
grid
|> dict.filter(fn(_k, v) { v == "O" })
|> dict.keys
|> list.fold(0, fn(s, k) { s + k.0 + 100 * k.1 })
}

pub fn part2(input: String) -> Int {
let #(grid, dirs) = parse(input, expand)
let robot = find_robot(grid)
let #(grid, _) =
list.fold(dirs, #(grid, robot), fn(acc, dir) {
let #(grid, robot) = acc
let res = {
use next_g <- result.try(push2(grid, robot, "@", ".", dir))
use #(next_r, _) <- result.map(next(grid, robot, dir))
#(next_g, next_r)
}
result.unwrap(res, acc)
})

grid
|> dict.filter(fn(_k, v) { v == "[" })
|> dict.keys
|> list.fold(0, fn(s, k) { s + k.0 + 100 * k.1 })
}
52 changes: 52 additions & 0 deletions test/aoc/year2024/day15_test.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import aoc/year2024/day15
import glacier/should

const input1 = "########
#..O.O.#
##@.O..#
#...O..#
#.#.O..#
#...O..#
#......#
########
<^^>>>vv<v>>v<<"

const input2 = "##########
#..O..O.O#
#......O.#
#.OO..O.O#
#[email protected].#
#O#..O...#
#O..O..O.#
#.OO.O.OO#
#....O...#
##########
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^
"

pub fn part1_test() {
input1
|> day15.part1
|> should.equal(2028)

input2
|> day15.part1
|> should.equal(10_092)
}

pub fn part2_test() {
input2
|> day15.part2
|> should.equal(9021)
}

0 comments on commit 6f2ce7d

Please sign in to comment.