aoc2020/day5.ml
2020-12-05 15:13:12 +01:00

112 lines
2.9 KiB
OCaml

type half = L | H
type bounds = {lo: int; hi: int}
type result = Exact of int | Between of bounds
let avg lo hi = (lo + hi) / 2
let rec find' ?(lo=0) ~hi = function
| [] -> if lo = hi then Exact hi else Between {lo; hi}
| L::hs -> find' ~lo ~hi:(avg lo hi) hs
| H::hs -> find' ~lo:(avg (lo+1) hi) ~hi hs
exception Inexact of bounds * int option [@warn_on_literal_pattern]
let find ?index ?lo ~hi halves =
match find' ?lo ~hi halves with
| Exact x -> x
| Between p -> raise (Inexact (p, index))
type path = {rows: half list; cols: half list}
exception Unknown_char of char * int option
let path_of_string ?index str =
let revs {rows; cols} = List.{rows = rev rows; cols = rev cols} in
let put acc = function
| 'F' -> {acc with rows = L :: acc.rows}
| 'B' -> {acc with rows = H :: acc.rows}
| 'L' -> {acc with cols = L :: acc.cols}
| 'R' -> {acc with cols = H :: acc.cols}
| c -> raise (Unknown_char (c, index)) in
String.to_seq str |> Seq.fold_left put {rows = []; cols = []} |> revs
type seat = {row: int; col: int}
let seat_of_path ?index {rows; cols} =
{row = find ?index ~hi:127 rows;
col = find ?index ~hi:7 cols}
let seat_id {row; col} = (row * 8) + col
let seat_id_of_string str =
path_of_string str |> seat_of_path |> seat_id
let%test_module _ = (module struct
let str = "FBFBBFFRLR"
let rows = [L;H;L;H;H;L;L]
let row = 44
let cols = [H;L;H]
let col = 5
let path = {rows; cols}
let seat = {row; col}
let id = 357
let%test _ = find' ~hi:1 [L] = Exact 0
let%test _ = find' ~hi:1 [H] = Exact 1
let%test _ = find' ~hi:127 [] = Between {lo = 0; hi = 127}
let%test _ = find' ~hi:127 rows = Exact row
let%test _ = find' ~hi:7 cols = Exact col
let%test _ = find ~hi:127 rows = row
let%test _ = find ~hi:7 cols = col
let%test_unit _ =
try ignore (find ~hi:127 []); failwith "didn't raise"
with Inexact _ -> ()
let%test _ = path_of_string str = path
let%test _ = seat_of_path path = seat
let%test _ = seat_id seat = id
end)
let infile_seats ~of_seq input =
Bracket.infile_lines input ~line:seat_id_of_string ~of_seq
let main1 input =
infile_seats input ~of_seq:(Seq.fold_left max 0)
|> Format.printf "max id: %d\n"
let%expect_test _ =
main1 "../../data/day5";
[%expect{| max id: 855 |}]
let make_list cmp seq = List.(fast_sort cmp (of_seq seq))
let rec find_pair f = function
| [] | [_] -> None
| x::y::zs ->
match f x y with
| Some z -> Some z
| None -> find_pair f (y::zs)
let find_gap lst =
let find_gap1 x y = if x + 1 <> y then Some (x + 1) else None in
find_pair find_gap1 lst
let main2 input =
match find_gap (infile_seats input ~of_seq:(make_list Int.compare)) with
| Some x -> Format.printf "seat id: %d\n" x
| None -> raise Not_found
let%expect_test _ =
main2 "../../data/day5";
[%expect{| seat id: 552 |}]
let main = Misc.main 5 [|main1; main2|]