134 lines
3.9 KiB
OCaml
134 lines
3.9 KiB
OCaml
module IntTuple = struct
|
|
type t = int * int * int * int
|
|
|
|
let compare (x0, y0, z0, w0) (x1, y1, z1, w1) =
|
|
if x0 == x1 && y0 == y1 && z0 == z1 && w0 == w1 then 0 else 1
|
|
end
|
|
|
|
module IntTupleSet = Set.Make (IntTuple)
|
|
|
|
let find_position lines =
|
|
let rec loop list_index lines =
|
|
match lines with
|
|
| [] -> raise (Invalid_argument "not found")
|
|
| head :: tail -> (
|
|
match String.index_from_opt head 0 '^' with
|
|
| None -> loop (list_index + 1) tail
|
|
| Some i -> (list_index, i))
|
|
in
|
|
loop 0 lines
|
|
|
|
let remove_list_duplicates l =
|
|
let rec loop acc l =
|
|
match l with
|
|
| [] -> acc
|
|
| (y1, x1) :: tail ->
|
|
if List.exists (fun (y2, x2) -> x1 == x2 && y1 == y2) acc then
|
|
loop acc tail
|
|
else loop ([ (y1, x1) ] @ acc) tail
|
|
in
|
|
loop [] l
|
|
|
|
let get_direction_change direction =
|
|
match direction with
|
|
| -1, 0 -> (0, 1)
|
|
| 1, 0 -> (0, -1)
|
|
| 0, 1 -> (1, 0)
|
|
| 0, -1 -> (-1, 0)
|
|
| _ -> raise (Invalid_argument "not valid direction")
|
|
|
|
let is_out_of_bounds l (y, x) =
|
|
y < 0 || y >= List.length l || x < 0 || x >= String.length (List.nth l 0)
|
|
|
|
let is_in_loop visited (y, x) (dy, dx) =
|
|
List.exists
|
|
(fun (y1, x1, dy1, dx1) -> y1 == y && x1 == x && dy1 == dy && dx1 == dx)
|
|
visited
|
|
|
|
let positions_visited lines (y, x) =
|
|
let rec loop positions (y, x) (dy, dx) =
|
|
if is_out_of_bounds lines (y, x) then positions
|
|
else
|
|
let loop_with_pos = loop ([ (y, x) ] @ positions) in
|
|
let next_pos = (y + dy, x + dx) in
|
|
|
|
if is_out_of_bounds lines next_pos then loop_with_pos next_pos (dy, dx)
|
|
else
|
|
let next_tile = String.get (List.nth lines (y + dy)) (x + dx) in
|
|
if next_tile == '#' then
|
|
let new_dy, new_dx = get_direction_change (dy, dx) in
|
|
loop_with_pos (y, x) (new_dy, new_dx)
|
|
else loop_with_pos (y + dy, x + dx) (dy, dx)
|
|
in
|
|
loop [] (y, x) (-1, 0)
|
|
|
|
let check_loop lines (y, x) =
|
|
let rec loop positions (y, x) (dy, dx) =
|
|
if is_out_of_bounds lines (y, x) then false
|
|
else if IntTupleSet.mem (y, x, dy, dx) positions then true
|
|
else
|
|
let incremented_set = IntTupleSet.add (y, x, dy, dx) positions in
|
|
let loop_with_pos = loop incremented_set in
|
|
let next_pos = (y + dy, x + dx) in
|
|
|
|
if is_out_of_bounds lines next_pos then loop_with_pos next_pos (dy, dx)
|
|
else
|
|
let next_tile = String.get (List.nth lines (y + dy)) (x + dx) in
|
|
if next_tile == '#' then
|
|
let new_dy, new_dx = get_direction_change (dy, dx) in
|
|
loop_with_pos (y, x) (new_dy, new_dx)
|
|
else loop_with_pos (y + dy, x + dx) (dy, dx)
|
|
in
|
|
loop IntTupleSet.empty (y, x) (-1, 0)
|
|
|
|
let add_obstacle lines (y, x) =
|
|
List.mapi
|
|
(fun i ->
|
|
fun _ ->
|
|
if i == y then
|
|
let row = List.nth lines y in
|
|
let mapped_str =
|
|
String.mapi
|
|
(fun j -> fun _ -> if j == x then '#' else String.get row j)
|
|
row
|
|
in
|
|
mapped_str
|
|
else List.nth lines i)
|
|
lines
|
|
|
|
let solve lines =
|
|
let initial_position = find_position lines in
|
|
let visited =
|
|
positions_visited lines initial_position |> remove_list_duplicates
|
|
in
|
|
let part1 = List.length visited in
|
|
|
|
let x_list = List.init (List.nth lines 0 |> String.length) (fun i -> i) in
|
|
let y_list = List.init (List.length lines) (fun i -> i) in
|
|
|
|
(*
|
|
#
|
|
.#
|
|
*)
|
|
let part2 =
|
|
List.fold_left
|
|
(fun acc y ->
|
|
acc
|
|
+ List.fold_left
|
|
(fun acc x ->
|
|
let initial_y, initial_x = initial_position in
|
|
if
|
|
List.exists (fun (y1, x1) -> y1 == y && x1 == x) visited
|
|
|| (y == initial_y && x == initial_x)
|
|
then (
|
|
let new_lines = add_obstacle lines (y, x) in
|
|
print_endline (List.nth new_lines 0);
|
|
Printf.printf "%d,%d\n" (y + 1) (x + 1);
|
|
if check_loop new_lines initial_position then acc + 1 else acc)
|
|
else acc)
|
|
0 x_list)
|
|
0 y_list
|
|
in
|
|
|
|
(part1, part2)
|