Mercurial > lbo > hg > aoc22
changeset 76:2d05d3e059ce
Day 17 Part 1: Visualize trace
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sun, 31 Dec 2023 09:21:55 +0100 |
parents | 6e7829d7eee6 |
children | 85797fc052cc |
files | 2023/day17.ml |
diffstat | 1 files changed, 62 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- a/2023/day17.ml Sat Dec 30 17:49:10 2023 +0100 +++ b/2023/day17.ml Sun Dec 31 09:21:55 2023 +0100 @@ -37,12 +37,23 @@ Sexp.List (List.map elts ~f:Elt.sexp_of_t) end +module Int_pq = PrioQueue (Int) + +let () = + let q = Int_pq.empty in + let q = Int_pq.add q ~prio:1 1 in + let q = Int_pq.add q ~prio:2 2 in + let q = Int_pq.add q ~prio:0 3 in + assert (Int_pq.min_elt_exn q = 3) + module type Comparable = sig type t [@@deriving sexp, compare] end type direction = North | East | South | West [@@deriving sexp, compare, eq] -type heatloss = { value : int; min_so_far : int } [@@deriving sexp, compare] + +type heatloss = { value : int; min_so_far : int; prev : int * int } +[@@deriving sexp, compare] type field = { r : int; c : int; field : heatloss array } [@@deriving sexp, compare] @@ -58,7 +69,7 @@ module Parse = struct let parse_tile c = String.of_char c |> Int.of_string |> fun value -> - { value; min_so_far = Int.max_value } + { value; min_so_far = Int.max_value; prev = (0, 0) } let parse_line s = String.to_list s |> List.map ~f:parse_tile @@ -113,12 +124,14 @@ let initial = [ Position.initial ] let max_straight = 3 + (* Return potential neighbors at position (r, c) in direction dir *) let neighbors r c : direction -> neighbor list = function | North -> [ (r - 1, c, North); (r, c - 1, West); (r, c + 1, East) ] | East -> [ (r - 1, c, North); (r + 1, c, South); (r, c + 1, East) ] | South -> [ (r + 1, c, South); (r, c - 1, West); (r, c + 1, East) ] | West -> [ (r - 1, c, North); (r + 1, c, South); (r, c - 1, West) ] + (* Filter out neighbors that are not valid *) let valid_neighbors field Position.{ dir; straight; heatloss; _ } neighbors : neighbor list = let straight_ok = function @@ -137,6 +150,7 @@ in List.filter neighbors ~f:valid + (* From position pos, return a list of next tiles to go *) let next_options field (Position.{ r; c; dir; straight; _ } as pos) = let neighbors = neighbors r c dir in let neighbors' = valid_neighbors field pos neighbors in @@ -147,12 +161,46 @@ c = c'; dir = dir'; prev = (r, c); - straight = (if Int.(straight = 1) || equal_direction dir dir' then straight + 1 else 1); + straight = + (if Int.(straight = 1) || equal_direction dir dir' then straight + 1 + else 1); heatloss = pos.heatloss + (field_at field r' c').value; } in List.map neighbors' ~f:pos_of_neighbor + let rec trace_path ?(acc = []) field r c = + match (r, c) with + | 0, 0 -> (0, 0) :: acc + | _ -> + let entry = field_at field r c in + let r', c' = entry.prev in + trace_path ~acc:((r, c) :: acc) field r' c' + + let start_trace_path field = + let r, c = dst field in + trace_path field r c + + let visualizer_field field path = + let a = Array.map field.field ~f:(fun _ -> 0) in + let f (r, c) = a.(rc_to_ix field r c) <- 1 in + List.iter path ~f; + a + + let print_visualizer_field field r c = + let rows = Sequence.range 0 r and cols = Sequence.range 0 c in + let f r' = + Sequence.iter cols ~f:(fun c' -> printf "%d" field.((r' * r) + c')); + printf "\n" + in + Sequence.iter rows ~f + + let visualize_field field path = + let f = visualizer_field field path in + print_visualizer_field f field.r field.c + + (* Apply Dijkstra's algorithm (or something like that...) + to find the shortest path according to restrictions. *) let solve field = let dstr, dstc = dst field in let rec loop (q : Pospq.t) = @@ -160,18 +208,14 @@ else let q, pos = Pospq.remove_min_elt_exn q in if Int.equal pos.r dstr && Int.equal pos.c dstc then ( - Out_channel.printf "Visiting %s\n" - (Position.sexp_of_t pos |> Sexp.to_string_hum); + field_update field pos.r pos.c (fun v -> + { v with min_so_far = pos.heatloss; prev = pos.prev }); Some pos.heatloss) else let min_so_far = (field_at field pos.r pos.c).min_so_far in if min_so_far = Int.max_value then ( - Out_channel.printf "Visiting %s (%d vs. %d)\n" - (Position.sexp_of_t pos |> Sexp.to_string_hum) - pos.heatloss min_so_far; - field_update field pos.r pos.c (fun v -> - { v with min_so_far = pos.heatloss }); + { v with min_so_far = pos.heatloss; prev = pos.prev }); let next = next_options field pos in let q = List.fold_left next ~init:q ~f:(fun q' opt -> @@ -187,4 +231,11 @@ let inp = In_channel.(input_all stdin) in let field = Parse.parse_field inp in let part1 = Option.value_exn (Part1.solve field) in - Out_channel.printf "Part1: %d\n" part1 + let trace = Part1.start_trace_path field in + Out_channel.printf "Part1: %d\n" part1; + Out_channel.printf "Path: %s\n" + (Sexp.to_string_hum + (List.sexp_of_t + (fun (r, c) -> Sexp.List [ Int.sexp_of_t r; Int.sexp_of_t c ]) + trace)); + Part1.visualize_field field trace