From 5287220958764729e401c9549b5fd67dfa2a7b54 Mon Sep 17 00:00:00 2001 From: Ivan Habunek Date: Wed, 14 Dec 2022 08:58:35 +0100 Subject: [PATCH] Day 12 --- .gitignore | 1 + input/day12.example.txt | 5 ++ input/day12.txt | 41 ++++++++++ src/aoc2022.gleam | 6 +- src/aoc2022/day12.gleam | 176 ++++++++++++++++++++++++++++++++++++++++ src/aoc2022/utils.gleam | 7 ++ 6 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 input/day12.example.txt create mode 100644 input/day12.txt create mode 100644 src/aoc2022/day12.gleam diff --git a/.gitignore b/.gitignore index 170cca9..79b74d3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.ez build erl_crash.dump +/tmp/ \ No newline at end of file diff --git a/input/day12.example.txt b/input/day12.example.txt new file mode 100644 index 0000000..433e0d2 --- /dev/null +++ b/input/day12.example.txt @@ -0,0 +1,5 @@ +Sabqponm +abcryxxl +accszExk +acctuvwj +abdefghi \ No newline at end of file diff --git a/input/day12.txt b/input/day12.txt new file mode 100644 index 0000000..e54e60f --- /dev/null +++ b/input/day12.txt @@ -0,0 +1,41 @@ +abccccccaaccaaccccaaaaacccccaaaaccccccccccccccccccccccccccccccccaaaaaaaaaaaaaaaaaaaccccccccccccccccaaaccccccccccccaacccccccccccccccccccccccccccccccccccccccccaaaa +abaaaaccaaaaaccccaaaaaccccccaaaaccccccccccccccccccccaaacccccccccccaaaaaaaaaaaaaaaaaaccccccccccccccccaaaaccccccaaacaaccccccccccccccccccccccccccccccccccccccccaaaaa +abaaacccaaaaaaaacaaaaaacccccaaaaccccccccccccccccccccaaaaacccccccccaaaaaaaaaaaaaaaaacccccccccaaccccaaaaaacccccccaaaaaccccaaccccccccccccccacccccccccccccccccccaaaaa +abaaacccccaaaaaccccaaaaccccccaaacccccccccccccccccccaaaaaccccccccccaaaaaacacaaaaaacccccccccccaaccccaaaaacccccccccaaaaaaccaaaaaaccccccccccaaaccccacccccccccccaaaaaa +abaacccccaaaaaccccaacccccccccccccccaaaaacccccccccccaaaaacccccccccaaaaaaaaccaaaaaaacccccccaaaaaaaaccaaaaacccccccaaaaaaaccaaaaacccccccccccaaacccaaaccccccccccccccaa +abaaacccaaacaacccccccccccccccccccccaaaaaccccccccccccaaaaacccccccaaaaaaaaaccaaccaaacccccccaaaaaaaaccaacccccccccaaaaaaccaaaaaaccccccccccccaaaacaaaaccccccccccccccaa +abaaacccccccaaccccccccccccccccccccaaaaaaccccccccccccaaccccccaacccaaaccaaaaccccccaacccccccccaaaacccccccccccccccaacaaaccaaaaaaaccccccccccccajjjjjjjcccccccccccccccc +abcaacccccccccccccccccccccccccccccaaaaaaccccccccccccccccccccaaaaccccccaaaaccccccccccccccaacaaaaaccccccccccccccccccaaccccaaaaaacccccccccccjjjjjjjjjcccccaaaccccccc +abccccccccccccccccccccccccccccccccaaaaaaccaaccccccccccccccaaaaaacccccccaaacccccccccccaacaaaaaaaaccccccccccccccccccccccccaaccaaccccccccaiijjjjojjjjcccccaaacaccccc +abcccccccccccccccccccccccaaacccccccaaacacaaacccccccccccccccaaaaccccaaccccccccccccccccaaaaaaacccaccccccccccccccccccccccccaacccccccccccaiiijjooooojjkccaaaaaaaacccc +abccccccccccccccccccccccaaaaccccccccccaaaaaccccccccccccccccaaaaacccaaaaaccccccccccccccaaaaaacccccccccccccccccccccccccccccccccccccciiiiiiiioooooookkkcaaaaaaaacccc +abccccccccccccccccccccccaaaaccccccccccaaaaaaaacccccccccccccaacaaccaaaaacccccccaaacccaaaaaaaaccccccccccccccccccccccccccccccccccchiiiiiiiiooooouoookkkccaaaaaaccccc +abcccccccccaaccccccccccccaaaccccccccccccaaaaacccccccccccccccccccccaaaaaccccccaaaacccaaaaacaacccccccccccccaacaacccccccccccccccchhhiiiinnnooouuuuoookkkccaaaaaccccc +abcccccccccaaacccccccccccccccccccccccccaaaaacccccccccccccccccccccccaaaaacccccaaaaccccccaaccccccccccccccccaaaaacccccccccccccccchhhnnnnnnnnouuuuuuppkkkkaaaaaaccccc +abccccccaaaaaaaacccaaccccccccccccccccccaacaaccaacaaccccccccccccccccaacccccccccaaaccccccaacccccccccccccccaaaaacccccccccccccccchhhnnnnnnnnntuuxuuupppkkkkkacccccccc +abccccccaaaaaaaacacaaaacccccccccccccccccccaaccaaaaacccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaacccccccaacccccchhhnnnnttttttuxxxuuppppkkkkkcccccccc +abcccccccaaaaaaccaaaaaaccccccccccaaccccccccccaaaaaccccccccccccccccccccccccaaacccccccccccccccccccccccccccccaaaaccaaccaaacccaaahhhnnntttttttuxxxxuupppppllllccccccc +abcccccccaaaaaacccaaaacccccccccaaaaaaccccccccaaaaaacccccccccccccccccccccccaaacccccccccccccccccccccccccccccacccccaaaaaaacaaaaahhhppntttxxxxxxxxuuuuvpppplllccccccc +abcccccccaaaaaacccaaaacccccccccaaaaaacccccaaaaaaaaaccccccccccccccccccccaaaaaaaacccccccccccccccccccccaaaccccccaacaaaaaaccaaaaahhhpppttxxxxxxxxyuuvvvvvppplllcccccc +abcccccccaaccaacccaacaccaaaaccccaaaaacccccaaaaaaaaaccccccccccccccccccccaaaaaaaacccccccccccccccccccccaaacaaaaaaaccaaaaaaaaaaaaahhppptttxxxxxxyyyyyyvvvppplllcccccc +SbccccccccccccccccccccccaaaacccaaaaacccccccaaaaaaaaacaaaccccccccaacccccccaaaaaccccccccaaaaacccccccccaaaaaaaaaaaaaaaaaaaaaaaaacgggpppttxxxxEzzyyyyyvvvqqqlllcccccc +abccccccccccccccccccccccaaaacccaaaaacccccccaaaaaaaaccaaaccccccccaaacaaccaaaaaaccccccccaaaaacccccccaaaaaaaaaaaaaaaaaaaaaaaaaaacgggpppsssxxxyyyyyyvvvvvqqlllccccccc +abcccaaaccccccccccccccccaaaccccccccccccccccaaaaaaaaaaaaaccccccccaaaaaaccaaaaaacccccccaaaaaacccaaaccaaaaaccaaaaaaaaaaaacccccccccgggppssswwyyyyyyvvvvqqqqlllccccccc +abcaaaaaccccccccccccccccccccccccccccccccccaaaaaaaaaaaaacccccccaaaaaaacccaccaaacccccccaaaaaacccaaacccaaaaaaaaaaaccccaaacccaaaaacgggppsswwwyyyyyyvvqqqqqlllcccccccc +abcaaaaaaccccccccccccccccccccccccccccccccccaaccaaaaaaaaaaaccccaaaaaaacccccccccccccccccaaaaacccaaacaaaacaaaaaaaaccccaaacccaaaaacggpppsswwwywwyyyvvqqqmmmlccccccccc +abcaaaaaacccccccaacaaccccccccccccccccccccccccccaaaaaaaaaaaccccccaaaaacccccccccccccccccaaaccaaaaaaaaaaacccccccaacccccccccaaaaaacggpppsswwwwwwwwyvvqqqmmmcccccccccc +abcaaaaaccccccccaaaaaccccccccccccccccccccccccccccaaaaaaaacccccccaacaaacccccccccccccccccccccaaaaaaaaaccccccccccccccccccccaaaaaagggoossswwwwrrwwwvvqqmmmccccccccccc +abcaaaaacccccccaaaaaccccccccccccccccccccccccccccaaaaaaacccccccccaaccccccccccccccccccccccccccaaaaaaacccccccccccaaaccccccccaaaaagggooosssssrrrrwwwvqqmmmcccaacccccc +abcccccccccccccaaaaaaccccccccccccccccccccaacccccccccaaaccccccccccccccccccccccccccccccccccccccaaaaaaccccccccccccaaaaccccccaaaccgggooosssssrrrrrwwrrqmmmcccaacccccc +abcccccccccccccccaaaacccccccccccccccccccaaaacccccccacaaacccccccccccccccccccccccccccccccccccccaaaaaaacccccccccaaaaaacccccccccccgffoooooosoonrrrrrrrrmmmccaaaaacccc +abcccccccccccccccaccccccccccccccccccccccaaaacccccccaaaaacccccccccccccccccccccccccccccccccccccaaacaaacccccccccaaaaacccccccccccccfffoooooooonnnrrrrrmmmddcaaaaacccc +abccccccccccccccccccccccccccccccccccccccaaaaccccccccaaaaacccccccccccccccccccccccccaaaccccccccaacccccccccccccccaaaaaccccccccccccffffoooooonnnnnnrnnmmmdddaaaaacccc +abcccccccccccccccccccccccccccccccccccccccccccccccccaaaaaacccccccccccccccccaaaaaccaaaacccccccccccccccccccccccccaacccccccccccccccfffffffffeeeennnnnnmmdddaaaacccccc +abcccccccaaaccccccccaccccccccccccccccccccccccccccccaaaaccccccccccccaaaccccaaaaaccaaaaccccccccccccccccccccccccccccccccccccccccccccfffffffeeeeennnnnmddddaaaaaccccc +abcccaaccaaacccccaaaacccccaacccccccccccccccccccccccccaaacccccccccccaaacccaaaaaacccaaaccccccccccccccccccccccccccccccccccccccccccccccffffeeeeeeeedddddddcccaacccccc +abcccaaaaaaacccccaaaaaaccaaacccccccccccccccccccccccccccacccccccccccaaaaccaaaaaaccccccccccccccccccccccccccaacccccccccaaaccccccccccccccaaaaaaeeeeedddddcccccccccccc +abcccaaaaaacccccccaaaacccaaacaaaccccaaaacccccccccaaacaaaccccccaacccaaaacaaaaaaacccccccccccccccccccccccccaaaccccccccaaaacccccccccccccccccccaaaaeeddddccccccccccccc +abccccaaaaaaaacccaaaaaaaaaaaaaaaccccaaaacccccccccaaaaaaacccccaaacccaaaaaaaaaacccccccccccccccccccccccaaacaaaccccccccaaaaccccccccccccccccccccaaaccccccccccccccaaaca +abcccaaaaaaaaacccaacaaaaaaaaaaacccccaaaaccccccccccaaaaaacaaacaaacaaaaaaaaaacccccccccccccccaaacccccccaaaaaaaaaaccccccaaaccccccccccccccccccccaacccccccccccccccaaaaa +abcccaaaaaaaacccccccccccaaaaaacccccccaacccccccccccaaaaaaaaaaaaaaaaaaaaaaaaccccccccccccccccaaaacccccccaaaaaaaaacccccccccccccccccccccccccccccaaacccccccccccccccaaaa +abccaaaaaaacccccccccccccaaaaaaacccccccccccccccccaaaaaaaaaaaaaaaaaaaaaaaaaaacccccccccccccccaaaacccccccaaaaaaaacccccccccccccccccccccccccccccccccccccccccccccccaaaaa \ No newline at end of file diff --git a/src/aoc2022.gleam b/src/aoc2022.gleam index 746a605..1988b82 100644 --- a/src/aoc2022.gleam +++ b/src/aoc2022.gleam @@ -1,9 +1,9 @@ import gleam/io -import aoc2022/day10 +import aoc2022/day12 pub fn main() { io.print("Part 1: ") - io.debug(day10.part1()) + io.debug(day12.part1()) io.print("Part 2: ") - io.debug(day10.part2()) + io.debug(day12.part2()) } diff --git a/src/aoc2022/day12.gleam b/src/aoc2022/day12.gleam new file mode 100644 index 0000000..3b39c75 --- /dev/null +++ b/src/aoc2022/day12.gleam @@ -0,0 +1,176 @@ +//// --- Day 12: Hill Climbing Algorithm --- +//// https://adventofcode.com/2022/day/12 + +import aoc2022/utils +import gleam/int +import gleam/list +import gleam/map +import gleam/option.{None, Option, Some} +import gleam/queue +import gleam/result +import gleam/string + +type Pos { + Pos(x: Int, y: Int) +} + +type HeightMap = + map.Map(Pos, Int) + +type Visited = + map.Map(Pos, Option(Pos)) + +type Queue = + queue.Queue(Pos) + +type State { + State(end: Pos, height_map: HeightMap, queue: Queue, visited: Visited) +} + +pub fn part1() { + let #(start, end, height_map) = parse_input() + play(start, end, height_map) +} + +pub fn part2() { + let #(_start, end, height_map) = parse_input() + + height_map + |> map.filter(fn(_pos, height) { height == 0 }) + |> map.keys + |> list.map(play(_, end, height_map)) + |> list.filter(result.is_ok) + |> list.map(utils.assert_ok) + |> list.reduce(int.min) +} + +fn play(start, end, height_map) { + let visited = map.from_list([#(start, None)]) + let queue = queue.from_list([start]) + let state = State(end, height_map, queue, visited) + do_play(state) +} + +// BFS to find path from start to end +// Could be improved by A* but not really required for this one +fn do_play(state: State) -> Result(Int, Nil) { + case queue.pop_front(state.queue) { + Ok(#(current_pos, queue)) -> + case current_pos == state.end { + True -> { + let path = unravel_path(state.visited, current_pos) + Ok(list.length(path) - 1) + } + False -> { + let next = + next_positions(current_pos, state.height_map, state.visited) + let visited = add_visited(state.visited, current_pos, next) + let queue = add_queue(queue, next) + let state = State(state.end, state.height_map, queue, visited) + do_play(state) + } + } + // Queue depleted, no path found + Error(Nil) -> Error(Nil) + } +} + +fn unravel_path(visited, end_pos) -> List(Pos) { + do_unravel_path(visited, end_pos, []) +} + +fn do_unravel_path(visited, pos, path) -> List(Pos) { + case utils.assert_ok(map.get(visited, pos)) { + Some(prev_pos) -> do_unravel_path(visited, prev_pos, [pos, ..path]) + None -> [pos, ..path] + } +} + +fn add_visited(visited: Visited, current_pos: Pos, next: List(Pos)) { + list.fold( + next, + visited, + fn(visited, next_pos) { map.insert(visited, next_pos, Some(current_pos)) }, + ) +} + +fn add_queue(queue: Queue, next: List(Pos)) { + list.fold( + next, + queue, + fn(queue, next_pos) { queue.push_back(queue, next_pos) }, + ) +} + +fn next_positions(pos, height_map, visited) -> List(Pos) { + pos + |> neighbours() + // Remove out of bounds + |> list.filter(map.has_key(height_map, _)) + // Remove visited + |> list.filter(fn(neighbour) { !map.has_key(visited, neighbour) }) + // Remove neighbours too high to climb + |> list.filter(fn(neighbour) { + assert Ok(pos_height) = map.get(height_map, pos) + assert Ok(neighbour_height) = map.get(height_map, neighbour) + neighbour_height <= pos_height + 1 + }) +} + +fn neighbours(pos: Pos) -> List(Pos) { + [ + Pos(pos.x + 1, pos.y), + Pos(pos.x - 1, pos.y), + Pos(pos.x, pos.y + 1), + Pos(pos.x, pos.y - 1), + ] +} + +// ----------------------------------------------------------------------------- +// Input Parsing +// ----------------------------------------------------------------------------- + +fn parse_input() -> #(Pos, Pos, HeightMap) { + let char_map = + "day12.txt" + |> utils.read_input + |> parse_to_char_map + + let start = find_char(char_map, "S") + let end = find_char(char_map, "E") + let height_map = map.map_values(char_map, fn(_, v) { char_to_height(v) }) + #(start, end, height_map) +} + +fn char_to_height(char) { + let char = case char { + "S" -> "a" + "E" -> "z" + other -> other + } + + utils.ord(char) - 97 +} + +fn find_char(map, char) -> Pos { + assert [#(pos, _)] = + map + |> map.filter(fn(_, v) { v == char }) + |> map.to_list + + pos +} + +fn parse_to_char_map(input) { + input + |> parse_to_nested_list + |> list.flatten + |> map.from_list +} + +fn parse_to_nested_list(input) { + let rows = string.split(input, "\n") + use y, row <- list.index_map(rows) + use x, char <- list.index_map(string.to_graphemes(row)) + #(Pos(x, y), char) +} diff --git a/src/aoc2022/utils.gleam b/src/aoc2022/utils.gleam index 8caf0b5..e4623f5 100644 --- a/src/aoc2022/utils.gleam +++ b/src/aoc2022/utils.gleam @@ -1,4 +1,5 @@ import gleam/int +import gleam/io import gleam/list // Externals @@ -52,3 +53,9 @@ pub fn list_at(list: List(a), at: Int) -> Result(a, Nil) { False -> Error(Nil) } } + +pub fn inspect(term: anything, label: String) -> anything { + io.print(label) + io.print(": ") + io.debug(term) +} -- 2.45.2