diff --git a/sample/day15-sample.txt b/sample/day15-sample.txt new file mode 100644 index 0000000..84cf1fb --- /dev/null +++ b/sample/day15-sample.txt @@ -0,0 +1,21 @@ +########## +#..O..O.O# +#......O.# +#.OO..O.O# +#..O@..O.# +#O#..O...# +#O..O..O.# +#.OO.O.OO# +#....O...# +########## + +^v>^vv^v>v<>v^v<<><>>v^v^>^<<<><^ +vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<^<^^>>>^<>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^v^^<^^vv< +<>^^^^>>>v^<>vvv^>^^^vv^^>v<^^^^v<>^>vvvv><>>v^<<^^^^^ +^><^><>>><>^^<<^^v>>><^^>v>>>^v><>^v><<<>vvvv>^<><<>^>< +^>><>^v<><^vvv<^^<><^v<<<><<<^^<^>>^<<<^>>^v^>>^v>vv>^<<^v<>><<><<>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^ +<><^^>^^^<>^vv<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<> +^^>vv<^v^v^<>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<>< +v^^>>><<^^<>>^v^v^<<>^<^v^v><^<<<><<^vv>>v>v^<<^ diff --git a/sample/day15-sample2.txt b/sample/day15-sample2.txt new file mode 100644 index 0000000..8163605 --- /dev/null +++ b/sample/day15-sample2.txt @@ -0,0 +1,10 @@ +######## +#..O.O.# +##@.O..# +#...O..# +#.#.O..# +#...O..# +#......# +######## + +<^^>>>vv>v<< diff --git a/src/day15_p1.gleam b/src/day15_p1.gleam new file mode 100644 index 0000000..040cfb0 --- /dev/null +++ b/src/day15_p1.gleam @@ -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) } +}