Mercurial > lbo > hg > aoc22
changeset 77:85797fc052cc
Day 17 Part 1: Add comments
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sun, 31 Dec 2023 09:37:40 +0100 |
parents | 2d05d3e059ce |
children | ade1919a5409 |
files | 2023/day17.ml |
diffstat | 1 files changed, 43 insertions(+), 28 deletions(-) [+] |
line wrap: on
line diff
--- a/2023/day17.ml Sun Dec 31 09:21:55 2023 +0100 +++ b/2023/day17.ml Sun Dec 31 09:37:40 2023 +0100 @@ -159,9 +159,13 @@ { r = r'; c = c'; + (* Direction this position is facing when entering its tile *) dir = dir'; + (* Allow tracking of path by remembering previous tile *) prev = (r, c); + (* Number of straight tiles in a row *) straight = + (* careful, tricky! (not sure if I got this right, either *) (if Int.(straight = 1) || equal_direction dir dir' then straight + 1 else 1); heatloss = pos.heatloss + (field_at field r' c').value; @@ -169,6 +173,40 @@ in List.map neighbors' ~f:pos_of_neighbor + (* 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) = + (* No path could be found, options exhausted *) + if Pospq.is_empty q then None + else + let q, pos = Pospq.remove_min_elt_exn q in + (* Arrived at destination *) + if Int.equal pos.r dstr && Int.equal pos.c dstc then ( + field_update field pos.r pos.c (fun v -> + { v with min_so_far = pos.heatloss; prev = pos.prev }); + Some pos.heatloss) + else + (* Check if tile has been visited before *) + let min_so_far = (field_at field pos.r pos.c).min_so_far in + if min_so_far = Int.max_value then ( + (* We are first; found minimal path to tile; update cost and previous tile *) + field_update field pos.r pos.c (fun v -> + { v with min_so_far = pos.heatloss; prev = pos.prev }); + let next = next_options field pos in + (* Add all next options to the queue *) + let q = + List.fold_left next ~init:q ~f:(fun q' opt -> + Pospq.add q' ~prio:opt.heatloss opt) + in + loop q + (* Already visited this tile, skip *)) + else loop q + in + loop (List.fold_left initial ~init:Pospq.empty ~f:(Pospq.add ~prio:0)) + + (* Recursively follow `prev` links in the field array. *) let rec trace_path ?(acc = []) field r c = match (r, c) with | 0, 0 -> (0, 0) :: acc @@ -177,16 +215,19 @@ let r', c' = entry.prev in trace_path ~acc:((r, c) :: acc) field r' c' + (* Trace back the path from the destination to the start *) let start_trace_path field = let r, c = dst field in trace_path field r c + (* Create a 2D array mapping visited tiles *) 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 + (* Print the 2D array *) let print_visualizer_field field r c = let rows = Sequence.range 0 r and cols = Sequence.range 0 c in let f r' = @@ -195,36 +236,10 @@ in Sequence.iter rows ~f + (* Create and print the path on a 2D map *) 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) = - if Pospq.is_empty q then None - else - let q, pos = Pospq.remove_min_elt_exn q in - if Int.equal pos.r dstr && Int.equal pos.c dstc then ( - 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 ( - field_update field pos.r pos.c (fun v -> - { 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 -> - Pospq.add q' ~prio:opt.heatloss opt) - in - loop q) - else loop q - in - loop (List.fold_left initial ~init:Pospq.empty ~f:(Pospq.add ~prio:0)) end let () = @@ -237,5 +252,5 @@ (Sexp.to_string_hum (List.sexp_of_t (fun (r, c) -> Sexp.List [ Int.sexp_of_t r; Int.sexp_of_t c ]) - trace)); + trace)); Part1.visualize_field field trace