You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
92 lines
3.1 KiB
OCaml
92 lines
3.1 KiB
OCaml
(* Common util. Should probably put this in a module or something.. *)
|
|
let get_one_line file =
|
|
try Some (input_line file) with
|
|
End_of_file -> None
|
|
|
|
let get_lines file =
|
|
let rec input lines =
|
|
match get_one_line file with
|
|
Some line -> input (line :: lines)
|
|
| None -> List.rev lines
|
|
in input []
|
|
|
|
let read_file file =
|
|
let channel = open_in(file) in
|
|
get_lines channel
|
|
|
|
let explode s = (* convert a string to a list of chars *)
|
|
let rec exp i l =
|
|
if i < 0 then l else exp (i - 1) (s.[i] :: l) in
|
|
exp (String.length s - 1) [];;
|
|
|
|
let implode l = String.of_seq (List.to_seq l)
|
|
|
|
let (<<) f g x = f(g(x))
|
|
let (>>) f g x = g(f(x))
|
|
|
|
let rec find_idx x = function (* find index of x *)
|
|
| [] -> raise (Failure "Not Found")
|
|
| h :: t -> if x = h then 0 else 1 + find_idx x t
|
|
|
|
let abs x = if x < 0 then -x else x
|
|
|
|
(* --- *)
|
|
|
|
(*
|
|
type dir = North | Eath | South | West
|
|
let string_of_dir = function | North -> 'N' | Eeast -> 'E' | South -> 'S' | West -> 'W'
|
|
|
|
type action = N | E | S | W | L | R | F
|
|
let action_of_char = function | 'N' -> N | 'E' -> E | 'S' -> S | 'W' -> W | 'L' -> L | 'R' -> R | 'F' -> F
|
|
|
|
type position = int * int
|
|
type bearing = { position: position; heading: char }
|
|
|
|
(* nah fuck all that *)
|
|
*)
|
|
|
|
type instruction = { action: char; value: int }
|
|
|
|
let dirs = [('N', (0, 1)); ('E', (1, 0)); ('S', (0, -1)); ('W', (-1, 0))]
|
|
let dir_list = ['N'; 'E'; 'S'; 'W']
|
|
|
|
let parse_line = (* from string to instruction *)
|
|
explode >> function | action :: value -> { action; value = int_of_string @@ implode value }
|
|
|
|
let rec turn direction heading = function (* returns new heading, matching on degrees *)
|
|
| 0 -> heading (* doing this 90 degrees at a time *)
|
|
| d -> (* rotate through our dir_list *)
|
|
let old_idx = find_idx heading dir_list in
|
|
let idx_dx = match direction with | 'L' -> -1 | 'R' -> 1 in
|
|
let new_idx = (old_idx + 4 + idx_dx) mod 4 in (* if we fall off the dir_list in either way, wrap around with mod *)
|
|
let new_heading = List.nth dir_list new_idx in
|
|
(* Printf.printf "turning! old_idx %i idx_dx %i new_idx %i new_heading %c" old_idx idx_dx new_idx new_heading; *)
|
|
turn direction new_heading (d - 90)
|
|
|
|
let move { action; value } (x, y) heading = (* returns (new location, new heading) *)
|
|
Printf.printf "@ (%i, %i) facing %c. moving %c %i\n" x y heading action value;
|
|
match action with
|
|
| 'L' -> ((x, y), turn 'L' heading value)
|
|
| 'R' -> ((x, y), turn 'R' heading value)
|
|
| 'F' ->
|
|
let (dx, dy) = List.assoc heading dirs in
|
|
((x + (dx * value), y + (dy * value)), heading)
|
|
| dir -> (* Either N, E, S, or W. Find the (dx, dy) and apply it *)
|
|
let (dx, dy) = List.assoc dir dirs in
|
|
((x + (dx * value), y + (dy * value)), heading)
|
|
|
|
let rec travel (x, y) heading map =
|
|
match map with
|
|
| [] -> (x, y)
|
|
| line :: rest ->
|
|
let (new_position, new_heading) = move line (x, y) heading in
|
|
travel new_position new_heading rest
|
|
|
|
let manhattan (x, y) = (abs x) + abs y
|
|
|
|
let () =
|
|
let map = read_file "day12.txt" |> List.map parse_line in
|
|
|
|
let (x, y) = map |> travel (0, 0) 'E' in
|
|
Printf.printf "ending location (%i, %i). manhattan distance: %i\n" x y (manhattan (x, y));
|