Compare commits
	
		
			2 Commits
		
	
	
		
			4bc7815851
			...
			f271e93e63
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f271e93e63 | |||
| f66c364090 | 
| @@ -1,16 +1,35 @@ | |||||||
|  | (** [find grid c] returns the position of the character [c] in [grid]. *) | ||||||
| let find grid c = | let find grid c = | ||||||
|   match Aoc.Grid.idx_from_opt grid 0 c with |   match Aoc.Grid.idx_from_opt grid 0 c with | ||||||
|   | None -> failwith "find" |   | None -> failwith "find" | ||||||
|   | Some idx -> Aoc.Grid.pos_of_idx grid idx |   | Some idx -> Aoc.Grid.pos_of_idx grid idx | ||||||
|  |  | ||||||
|  | (** [input_of_file fname] returns a [(grid, start_pos)] pair parsed from | ||||||
|  |     [fname]. *) | ||||||
| let input_of_file fname = | let input_of_file fname = | ||||||
|   let grid = Aoc.Grid.of_file fname in |   let grid = Aoc.Grid.of_file fname in | ||||||
|   let start_pos = find grid 'S' in |   let start_pos = find grid 'S' in | ||||||
|   (grid, start_pos) |   (grid, start_pos) | ||||||
|  |  | ||||||
| let rec dijkstra visit check_end states = | (** [dijkstra visit check_end states] executes Dijkstra's algorithm. | ||||||
|  |  | ||||||
|  |     [visit cost state] is called to visit [state] with [cost]. It should mark | ||||||
|  |     [state] as visited, and return a list of [(cost, state)] pairs which contain | ||||||
|  |     new states to examine. The returned list should be sorted by [cost]. | ||||||
|  |  | ||||||
|  |     [check_end state] should return [true] if and only if [state] is an end | ||||||
|  |     state. | ||||||
|  |  | ||||||
|  |     [states] is a list of [(cost, state)] pairs ordered by [cost]. | ||||||
|  |  | ||||||
|  |     [dijkstra] returns [None] if no path is found to the destination. It returns | ||||||
|  |     [Some (cost, state, remaining_states)] if a route is found. [cost] is the | ||||||
|  |     cost of getting to [state]. [remaining_states] is a list of the remaining | ||||||
|  |     states which can be passed back to [dijkstra] if we want to find further | ||||||
|  |     paths. *) | ||||||
|  | let rec dijkstra visit check_end = | ||||||
|   let compare_costs (lhs, _) (rhs, _) = compare lhs rhs in |   let compare_costs (lhs, _) (rhs, _) = compare lhs rhs in | ||||||
|   match states with |   function | ||||||
|   | [] -> None |   | [] -> None | ||||||
|   | (cost, state) :: t -> |   | (cost, state) :: t -> | ||||||
|       if check_end state then Some (cost, state, t) |       if check_end state then Some (cost, state, t) | ||||||
| @@ -18,6 +37,8 @@ let rec dijkstra visit check_end states = | |||||||
|         let new_states = visit cost state |> List.merge compare_costs t in |         let new_states = visit cost state |> List.merge compare_costs t in | ||||||
|         dijkstra visit check_end new_states |         dijkstra visit check_end new_states | ||||||
|  |  | ||||||
|  | (** [visited_idx grid state] returns the index into the visited array for [grid] | ||||||
|  |     at a given [state]. *) | ||||||
| let visited_idx grid ((dx, dy), p) = | let visited_idx grid ((dx, dy), p) = | ||||||
|   let add = |   let add = | ||||||
|     match (dx, dy) with |     match (dx, dy) with | ||||||
| @@ -29,6 +50,9 @@ let visited_idx grid ((dx, dy), p) = | |||||||
|   in |   in | ||||||
|   (Aoc.Grid.idx_of_pos grid p * 4) + add |   (Aoc.Grid.idx_of_pos grid p * 4) + add | ||||||
|  |  | ||||||
|  | (** [visit grid visited cost state] visits [state] with [cost] in [grid]. | ||||||
|  |     [visited] is an array of visited states, and is updated as we visit. It | ||||||
|  |     returns a list of new [(cost, state)] pairs to visit. *) | ||||||
| let visit grid visited_grid cost state = | let visit grid visited_grid cost state = | ||||||
|   let (dx, dy), ((x, y) as p) = List.hd state in |   let (dx, dy), ((x, y) as p) = List.hd state in | ||||||
|   let has_visited = visited_grid.(visited_idx grid (List.hd state)) in |   let has_visited = visited_grid.(visited_idx grid (List.hd state)) in | ||||||
| @@ -42,16 +66,19 @@ let visit grid visited_grid cost state = | |||||||
|       (cost + 1000, ((dy, -dx), p) :: state); |       (cost + 1000, ((dy, -dx), p) :: state); | ||||||
|     ]) |     ]) | ||||||
|  |  | ||||||
|  | (** [has_visited grid visited_grid (cost, state)] returns true if we have | ||||||
|  |     visited the [state]. *) | ||||||
| let has_visited grid visited_grid (cost, state) = | let has_visited grid visited_grid (cost, state) = | ||||||
|   visited_grid.(visited_idx grid (List.hd state)) < cost |   visited_grid.(visited_idx grid (List.hd state)) < cost | ||||||
|  |  | ||||||
|  | (** [visit grid visited cost state] visits [state] with [cost] in [grid]. | ||||||
|  |     [visited] is an array of visited states, and is updated as we visit. It | ||||||
|  |     returns a list of new [(cost, state)] pairs to visit. *) | ||||||
| let visit_max grid visited_grid cost state = | let visit_max grid visited_grid cost state = | ||||||
|   let (dx, dy), ((x, y) as p) = List.hd state in |   let (dx, dy), ((x, y) as p) = List.hd state in | ||||||
|   if has_visited grid visited_grid (cost, state) then [] |   if has_visited grid visited_grid (cost, state) then [] | ||||||
|   else if Aoc.Grid.get_by_pos grid p = '#' then [] |   else if Aoc.Grid.get_by_pos grid p = '#' then [] | ||||||
|   else ( |   else ( | ||||||
|     Printf.printf "%d (%d, %d)\n" cost x y; |  | ||||||
|     flush stdout; |  | ||||||
|     visited_grid.(visited_idx grid (List.hd state)) <- cost; |     visited_grid.(visited_idx grid (List.hd state)) <- cost; | ||||||
|     [ |     [ | ||||||
|       (cost + 1, ((dx, dy), (x + dx, y + dy)) :: state); |       (cost + 1, ((dx, dy), (x + dx, y + dy)) :: state); | ||||||
| @@ -60,14 +87,16 @@ let visit_max grid visited_grid cost state = | |||||||
|     ] |     ] | ||||||
|     |> List.filter (fun x -> not (has_visited grid visited_grid x))) |     |> List.filter (fun x -> not (has_visited grid visited_grid x))) | ||||||
|  |  | ||||||
| let[@warning "-32"] print_point (x, y) = Printf.printf "(%d, %d)" x y | (** [check_end grid state] returns [true] if [state] is at the end location in | ||||||
|  |     [grid]. *) | ||||||
| let check_end grid state = | let check_end grid state = | ||||||
|   let _, p = List.hd state in |   let _, p = List.hd state in | ||||||
|   Aoc.Grid.get_by_pos grid p = 'E' |   Aoc.Grid.get_by_pos grid p = 'E' | ||||||
|  |  | ||||||
| let check_end2 grid _ state = check_end grid state | (** [part1 (grid, start_pos)] returns solution to part 1. | ||||||
|  |  | ||||||
|  |     This part does a simple Dijkstra algorithm over the grid finding the | ||||||
|  |     shortest path possible. *) | ||||||
| let part1 (grid, start_pos) = | let part1 (grid, start_pos) = | ||||||
|   let visited_grid = Array.make (Aoc.Grid.length grid * 4) false in |   let visited_grid = Array.make (Aoc.Grid.length grid * 4) false in | ||||||
|   match |   match | ||||||
| @@ -77,17 +106,20 @@ let part1 (grid, start_pos) = | |||||||
|   | None -> failwith "part" |   | None -> failwith "part" | ||||||
|   | Some (cost, _, _) -> cost |   | Some (cost, _, _) -> cost | ||||||
|  |  | ||||||
|  | (** [part2 (grid, start_pos)] returns solution to part 2. | ||||||
|  |  | ||||||
|  |     We reuse part 1 to find the cost of getting to the exit. Then we redo the | ||||||
|  |     Dijkstra algorithm to find all walks with that cost. | ||||||
|  |  | ||||||
|  |     Finally we get all the positions visited, de-duplicate, and count the length | ||||||
|  |     of the list. This produces the number of locations for benches. *) | ||||||
| let part2 (grid, start_pos) = | let part2 (grid, start_pos) = | ||||||
|   let cost = part1 (grid, start_pos) in |   let cost = part1 (grid, start_pos) in | ||||||
|   let visited_grid = Array.make (Aoc.Grid.length grid * 4) cost in |   let visited_grid = Array.make (Aoc.Grid.length grid * 4) cost in | ||||||
|   let rec impl acc lst = |   let rec impl acc lst = | ||||||
|     match |     match dijkstra (visit_max grid visited_grid) (check_end grid) lst with | ||||||
|       dijkstra (visit_max grid visited_grid) (check_end2 grid visited_grid) lst |  | ||||||
|     with |  | ||||||
|     | None -> acc |     | None -> acc | ||||||
|     | Some (_, states, remainder) -> |     | Some (_, states, remainder) -> impl (states :: acc) remainder | ||||||
|         List.iter (fun x -> visited_grid.(visited_idx grid x) <- 0) states; |  | ||||||
|         impl (states :: acc) remainder |  | ||||||
|   in |   in | ||||||
|   impl [] [ (0, [ ((1, 0), start_pos) ]) ] |   impl [] [ (0, [ ((1, 0), start_pos) ]) ] | ||||||
|   |> List.concat |> List.map snd |> List.sort_uniq compare |> List.length |   |> List.concat |> List.map snd |> List.sort_uniq compare |> List.length | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user