Compare commits
2 commits
72661aa48e
...
3e070c2910
Author | SHA1 | Date | |
---|---|---|---|
3e070c2910 | |||
552cdd2d95 |
5 changed files with 429 additions and 0 deletions
21
sample/day15-sample.txt
Normal file
21
sample/day15-sample.txt
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
##########
|
||||||
|
#..O..O.O#
|
||||||
|
#......O.#
|
||||||
|
#.OO..O.O#
|
||||||
|
#..O@..O.#
|
||||||
|
#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^<<^
|
10
sample/day15-sample2.txt
Normal file
10
sample/day15-sample2.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
########
|
||||||
|
#..O.O.#
|
||||||
|
##@.O..#
|
||||||
|
#...O..#
|
||||||
|
#.#.O..#
|
||||||
|
#...O..#
|
||||||
|
#......#
|
||||||
|
########
|
||||||
|
|
||||||
|
<^^>>>vv<v>>v<<
|
9
sample/day15-sample3.txt
Normal file
9
sample/day15-sample3.txt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#######
|
||||||
|
#...#.#
|
||||||
|
#.....#
|
||||||
|
#..OO@#
|
||||||
|
#..O..#
|
||||||
|
#.....#
|
||||||
|
#######
|
||||||
|
|
||||||
|
<vv<<^^<<^^
|
163
src/day15_p1.gleam
Normal file
163
src/day15_p1.gleam
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
import gleam/dict.{type Dict}
|
||||||
|
import gleam/io
|
||||||
|
import gleam/iterator
|
||||||
|
import gleam/list.{filter, map}
|
||||||
|
import gleam/string
|
||||||
|
import stdin.{stdin}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
stdin()
|
||||||
|
|> read_input
|
||||||
|
|> calculate
|
||||||
|
|> io.debug
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate(input: #(Dict(#(Int, Int), Item), List(Movement))) -> Int {
|
||||||
|
let #(textmap, movements) = input
|
||||||
|
let #(robot, restmap) = extract_robot(textmap)
|
||||||
|
|
||||||
|
do_movements(restmap, robot, movements)
|
||||||
|
|> dict.filter(fn(_, v) { v == Box })
|
||||||
|
|> dict.fold(0, fn(acc, k, _) {
|
||||||
|
let #(x, y) = k
|
||||||
|
acc + x + y * 100
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_movements(
|
||||||
|
textmap: Dict(#(Int, Int), Item),
|
||||||
|
robot: #(Int, Int),
|
||||||
|
movements: List(Movement),
|
||||||
|
) -> Dict(#(Int, Int), Item) {
|
||||||
|
case movements {
|
||||||
|
[] -> textmap
|
||||||
|
[next, ..rest] -> {
|
||||||
|
let #(newmap, newrobot) = move_robot(textmap, robot, next)
|
||||||
|
do_movements(newmap, newrobot, rest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_robot(
|
||||||
|
textmap: Dict(#(Int, Int), Item),
|
||||||
|
robot: #(Int, Int),
|
||||||
|
movement: Movement,
|
||||||
|
) -> #(Dict(#(Int, Int), Item), #(Int, Int)) {
|
||||||
|
let next_pos = calculate_next_position(robot, movement)
|
||||||
|
case dict.get(textmap, next_pos) {
|
||||||
|
Ok(Wall) -> #(textmap, robot)
|
||||||
|
Ok(Box) ->
|
||||||
|
case try_move_box(textmap, next_pos, movement) {
|
||||||
|
Ok(new_map) -> #(new_map, next_pos)
|
||||||
|
_ -> #(textmap, robot)
|
||||||
|
}
|
||||||
|
_ -> #(textmap, next_pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_move_box(
|
||||||
|
textmap: Dict(#(Int, Int), Item),
|
||||||
|
box_pos: #(Int, Int),
|
||||||
|
movement: Movement,
|
||||||
|
) -> Result(Dict(#(Int, Int), Item), Nil) {
|
||||||
|
let next_pos = calculate_next_position(box_pos, movement)
|
||||||
|
case dict.get(textmap, next_pos) {
|
||||||
|
Ok(Wall) -> Error(Nil)
|
||||||
|
Ok(Box) ->
|
||||||
|
case try_move_box(textmap, next_pos, movement) {
|
||||||
|
Ok(new_map) ->
|
||||||
|
Ok(new_map |> dict.delete(box_pos) |> dict.insert(next_pos, Box))
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
_ -> Ok(textmap |> dict.delete(box_pos) |> dict.insert(next_pos, Box))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_next_position(
|
||||||
|
current: #(Int, Int),
|
||||||
|
movement: Movement,
|
||||||
|
) -> #(Int, Int) {
|
||||||
|
let #(x, y) = current
|
||||||
|
case movement {
|
||||||
|
Up -> #(x, y - 1)
|
||||||
|
Down -> #(x, y + 1)
|
||||||
|
Left -> #(x - 1, y)
|
||||||
|
Right -> #(x + 1, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_robot(
|
||||||
|
objects: Dict(#(Int, Int), Item),
|
||||||
|
) -> #(#(Int, Int), Dict(#(Int, Int), Item)) {
|
||||||
|
let assert [robot] =
|
||||||
|
objects |> dict.filter(fn(_, v) { v == Robot }) |> dict.keys
|
||||||
|
|
||||||
|
#(robot, objects |> dict.delete(robot))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Item {
|
||||||
|
Box
|
||||||
|
Wall
|
||||||
|
Robot
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Movement {
|
||||||
|
Up
|
||||||
|
Left
|
||||||
|
Right
|
||||||
|
Down
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_input(
|
||||||
|
it: iterator.Iterator(String),
|
||||||
|
) -> #(Dict(#(Int, Int), Item), List(Movement)) {
|
||||||
|
let #(map_lines, movement_lines) =
|
||||||
|
it
|
||||||
|
|> iterator.to_list
|
||||||
|
|> map(string.trim)
|
||||||
|
|> filter(not(string.is_empty))
|
||||||
|
|> list.split_while(fn(s) { s |> string.starts_with("#") })
|
||||||
|
|
||||||
|
let map_objects =
|
||||||
|
map_lines
|
||||||
|
|> list.index_fold(dict.new(), parse_map_line)
|
||||||
|
|
||||||
|
let movements =
|
||||||
|
movement_lines
|
||||||
|
|> string.concat
|
||||||
|
|> string.to_graphemes
|
||||||
|
|> list.map(parse_movement)
|
||||||
|
|
||||||
|
#(map_objects, movements)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_map_line(
|
||||||
|
acc: Dict(#(Int, Int), Item),
|
||||||
|
line: String,
|
||||||
|
y: Int,
|
||||||
|
) -> Dict(#(Int, Int), Item) {
|
||||||
|
line
|
||||||
|
|> string.to_graphemes
|
||||||
|
|> list.index_fold(acc, fn(a, char, x) {
|
||||||
|
case char {
|
||||||
|
"#" -> dict.insert(a, #(x, y), Wall)
|
||||||
|
"O" -> dict.insert(a, #(x, y), Box)
|
||||||
|
"@" -> dict.insert(a, #(x, y), Robot)
|
||||||
|
_ -> a
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_movement(char: String) -> Movement {
|
||||||
|
case char {
|
||||||
|
"^" -> Up
|
||||||
|
"<" -> Left
|
||||||
|
">" -> Right
|
||||||
|
"v" -> Down
|
||||||
|
_ -> panic as "invalid char"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(a: fn(a) -> Bool) {
|
||||||
|
fn(param: a) -> Bool { !a(param) }
|
||||||
|
}
|
226
src/day15_p2.gleam
Normal file
226
src/day15_p2.gleam
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
import gleam/dict.{type Dict}
|
||||||
|
import gleam/io
|
||||||
|
import gleam/iterator
|
||||||
|
import gleam/list.{Continue, Stop, filter, map}
|
||||||
|
import gleam/result
|
||||||
|
import gleam/string
|
||||||
|
import stdin.{stdin}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
stdin()
|
||||||
|
|> read_input
|
||||||
|
|> calculate
|
||||||
|
|> io.debug
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate(input: #(Dict(#(Int, Int), Item), List(Movement))) -> Int {
|
||||||
|
let #(textmap, movements) = input
|
||||||
|
let #(robot, restmap) = extract_robot(textmap)
|
||||||
|
|
||||||
|
do_movements(restmap, robot, movements)
|
||||||
|
|> dict.filter(fn(_, v) { v == Box })
|
||||||
|
|> dict.fold(0, fn(acc, k, _) {
|
||||||
|
let #(x, y) = k
|
||||||
|
acc + x + y * 100
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_movements(
|
||||||
|
textmap: Dict(#(Int, Int), Item),
|
||||||
|
robot: #(Int, Int),
|
||||||
|
movements: List(Movement),
|
||||||
|
) -> Dict(#(Int, Int), Item) {
|
||||||
|
//debug_print_map(textmap, robot)
|
||||||
|
case movements {
|
||||||
|
[] -> textmap
|
||||||
|
[next, ..rest] -> {
|
||||||
|
let #(newmap, newrobot) = move_robot(textmap, robot, next)
|
||||||
|
do_movements(newmap, newrobot, rest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hey, here's a freebie debug function :D
|
||||||
|
fn debug_print_map(textmap: Dict(#(Int, Int), Item), robot: #(Int, Int)) -> Nil {
|
||||||
|
list.range(0, 7)
|
||||||
|
|> list.each(fn(y) {
|
||||||
|
list.range(0, 20)
|
||||||
|
|> list.each(fn(x) {
|
||||||
|
let at_pos = dict.get(textmap, #(x, y))
|
||||||
|
let prev_pos = dict.get(textmap, #(x - 1, y))
|
||||||
|
case at_pos, prev_pos, robot {
|
||||||
|
Ok(Box), _, _ -> io.print("[")
|
||||||
|
_, Ok(Box), _ -> io.print("]")
|
||||||
|
Ok(Wall), _, _ | _, Ok(Wall), _ -> io.print("#")
|
||||||
|
_, _, #(rx, ry) if rx == x && ry == y -> io.print("@")
|
||||||
|
_, _, _ -> io.print(".")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
io.print("\n")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_robot(
|
||||||
|
textmap: Dict(#(Int, Int), Item),
|
||||||
|
robot: #(Int, Int),
|
||||||
|
movement: Movement,
|
||||||
|
) -> #(Dict(#(Int, Int), Item), #(Int, Int)) {
|
||||||
|
let next_pos = calculate_next_position(robot, movement)
|
||||||
|
let at_pos =
|
||||||
|
dict.get(textmap, next_pos)
|
||||||
|
|> result.or(dict.get(textmap, calculate_next_position(next_pos, Left)))
|
||||||
|
case at_pos {
|
||||||
|
Ok(Wall) -> #(textmap, robot)
|
||||||
|
Ok(Box) ->
|
||||||
|
case try_move_box(textmap, next_pos, movement) {
|
||||||
|
Ok(new_map) -> #(new_map, next_pos)
|
||||||
|
_ -> #(textmap, robot)
|
||||||
|
}
|
||||||
|
_ -> #(textmap, next_pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_move_box(
|
||||||
|
textmap: Dict(#(Int, Int), Item),
|
||||||
|
maybe_pos: #(Int, Int),
|
||||||
|
movement: Movement,
|
||||||
|
) -> Result(Dict(#(Int, Int), Item), Nil) {
|
||||||
|
let box_pos = case dict.has_key(textmap, maybe_pos) {
|
||||||
|
True -> maybe_pos
|
||||||
|
False -> calculate_next_position(maybe_pos, Left)
|
||||||
|
}
|
||||||
|
|
||||||
|
case movement {
|
||||||
|
Left | Right -> {
|
||||||
|
let next_pos = calculate_next_position(box_pos, movement)
|
||||||
|
let check_pos = calculate_next_position(next_pos, movement)
|
||||||
|
case dict.get(textmap, check_pos) {
|
||||||
|
Ok(Wall) -> Error(Nil)
|
||||||
|
Ok(Box) ->
|
||||||
|
case try_move_box(textmap, check_pos, movement) {
|
||||||
|
Ok(new_map) ->
|
||||||
|
Ok(new_map |> dict.delete(box_pos) |> dict.insert(next_pos, Box))
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
_ -> Ok(textmap |> dict.delete(box_pos) |> dict.insert(next_pos, Box))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Up | Down -> {
|
||||||
|
let #(x, y) = calculate_next_position(box_pos, movement)
|
||||||
|
let moved_map =
|
||||||
|
[#(x - 1, y), #(x, y), #(x + 1, y)]
|
||||||
|
|> filter(fn(p) { dict.has_key(textmap, p) })
|
||||||
|
|> list.fold_until(Ok(textmap), fn(acc, p) {
|
||||||
|
case dict.get(textmap, p) {
|
||||||
|
Ok(Wall) -> Stop(Error(Nil))
|
||||||
|
Ok(Box) -> {
|
||||||
|
case
|
||||||
|
result.try(acc, fn(current) {
|
||||||
|
try_move_box(current, p, movement)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Ok(new_map) -> Continue(Ok(new_map))
|
||||||
|
_ -> Stop(Error(Nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ -> Continue(acc)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
case moved_map {
|
||||||
|
Ok(new_map) ->
|
||||||
|
Ok(new_map |> dict.delete(box_pos) |> dict.insert(#(x, y), Box))
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_next_position(
|
||||||
|
current: #(Int, Int),
|
||||||
|
movement: Movement,
|
||||||
|
) -> #(Int, Int) {
|
||||||
|
let #(x, y) = current
|
||||||
|
case movement {
|
||||||
|
Up -> #(x, y - 1)
|
||||||
|
Down -> #(x, y + 1)
|
||||||
|
Left -> #(x - 1, y)
|
||||||
|
Right -> #(x + 1, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_robot(
|
||||||
|
objects: Dict(#(Int, Int), Item),
|
||||||
|
) -> #(#(Int, Int), Dict(#(Int, Int), Item)) {
|
||||||
|
let assert [robot] =
|
||||||
|
objects |> dict.filter(fn(_, v) { v == Robot }) |> dict.keys
|
||||||
|
|
||||||
|
#(robot, objects |> dict.delete(robot))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Item {
|
||||||
|
Box
|
||||||
|
Wall
|
||||||
|
Robot
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Movement {
|
||||||
|
Up
|
||||||
|
Left
|
||||||
|
Right
|
||||||
|
Down
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_input(
|
||||||
|
it: iterator.Iterator(String),
|
||||||
|
) -> #(Dict(#(Int, Int), Item), List(Movement)) {
|
||||||
|
let #(map_lines, movement_lines) =
|
||||||
|
it
|
||||||
|
|> iterator.to_list
|
||||||
|
|> map(string.trim)
|
||||||
|
|> filter(not(string.is_empty))
|
||||||
|
|> list.split_while(fn(s) { s |> string.starts_with("#") })
|
||||||
|
|
||||||
|
let map_objects =
|
||||||
|
map_lines
|
||||||
|
|> list.index_fold(dict.new(), parse_map_line)
|
||||||
|
|
||||||
|
let movements =
|
||||||
|
movement_lines
|
||||||
|
|> string.concat
|
||||||
|
|> string.to_graphemes
|
||||||
|
|> list.map(parse_movement)
|
||||||
|
|
||||||
|
#(map_objects, movements)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_map_line(
|
||||||
|
acc: Dict(#(Int, Int), Item),
|
||||||
|
line: String,
|
||||||
|
y: Int,
|
||||||
|
) -> Dict(#(Int, Int), Item) {
|
||||||
|
line
|
||||||
|
|> string.to_graphemes
|
||||||
|
|> list.index_fold(acc, fn(a, char, x) {
|
||||||
|
case char {
|
||||||
|
"#" -> dict.insert(a, #(x * 2, y), Wall)
|
||||||
|
"O" -> dict.insert(a, #(x * 2, y), Box)
|
||||||
|
"@" -> dict.insert(a, #(x * 2, y), Robot)
|
||||||
|
_ -> a
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_movement(char: String) -> Movement {
|
||||||
|
case char {
|
||||||
|
"^" -> Up
|
||||||
|
"<" -> Left
|
||||||
|
">" -> Right
|
||||||
|
"v" -> Down
|
||||||
|
_ -> panic as "invalid char"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(a: fn(a) -> Bool) {
|
||||||
|
fn(param: a) -> Bool { !a(param) }
|
||||||
|
}
|
Loading…
Reference in a new issue