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.

181 lines
5.5 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 nth x = function (* find nth index of x in a list *)
| [] -> raise (Failure "Not Found")
| h :: t ->
if x = h
then if nth = 0 then 0 else 1 + find_idx (nth-1) x t
else 1 + find_idx nth x t
let rec take n l = (* take the first n elements *)
match n with
| 0 -> []
| n ->
match l with
| [] -> []
| x :: xs -> x :: (take (n-1) xs)
let rec drop n l = (* drop the first n elemnts *)
match n with
| 0 -> l
| n -> drop (n-1) @@ List.tl l
let sub_list start length = (* sub list from start *)
drop start >> take length
let range start len = (* makes a new list from start with length len *)
List.init len (fun x -> x + 0) |> List.map (fun x -> x + start)
(* --- *)
type rule = { name: string; ranges: (int * int) list }
(* type ordering = string * (int list) (* rule name and it's possible positinos *) *)
let parse_range r =
let [l; r] = String.split_on_char '-' r in
(int_of_string l, int_of_string r)
let parse_rules line = (* from "class: 1-6 or 5-7" to a rule *)
let [name; range_strings] = String.split_on_char ':' line in
let [rs1; _; rs2] = String.split_on_char ' ' (String.trim range_strings) in
{ name; ranges = [parse_range rs1; parse_range rs2] }
let read_chunks lines =
let blank1 = find_idx 0 "" lines in
let blank2 = find_idx 1 "" lines in
let rule_lines = sub_list 0 blank1 lines in
let rules = List.map parse_rules rule_lines in
let your_ticket = List.nth lines (blank2 - 1)
|> String.split_on_char ','
|> List.map int_of_string
in
let nearby_tickets = sub_list (blank2 + 2) (List.length lines - blank2 - 2) lines
|> List.map @@ String.split_on_char ','
|> List.map @@ List.map int_of_string
in
(rules, your_ticket, nearby_tickets)
(* Part 1 stuff *)
let is_value_valid rules value = (* given a list of rules and a value, is this value good *)
List.exists (fun { name; ranges } ->
List.exists (fun (l, r) ->
(value >= l) && (value <= r)
) ranges
) rules
let find_invalid_values rules tickets = (* given a list of rules and a list of tickets, find all invalid values within the tickets *)
List.map (fun ticket ->
List.filter (fun value -> not @@ is_value_valid rules value) ticket
) tickets
|> List.concat
(* Part 2 stuff *)
let find_valid_tickets rules =
List.filter (fun ticket ->
List.for_all (fun value -> is_value_valid rules value) ticket
)
let is_position_valid rule tickets position =
let values = List.map (fun t -> List.nth t position) tickets in
List.for_all (fun value ->
is_value_valid [rule] value
) values
let find_positions rule tickets =
let len = List.length (List.nth tickets 0) in
let all_positions = range 0 len in
let good_positions = List.filter (is_position_valid rule tickets) all_positions in
Printf.printf "Found good positions for rule %s: " rule.name;
List.iter (Printf.printf "%i ") good_positions;
print_newline ();
good_positions
let find_all_orderings rules tickets =
List.map (fun rule ->
let positions = find_positions rule tickets in
(rule.name, positions)
) rules
let remove_found_from_ordering found (name, positions) =
let new_pos_list = List.filter (fun p -> not @@ List.mem_assoc p found) positions in
(name, new_pos_list)
let find_ordering orderings =
print_newline ();
let rec go ords found =
if List.length orderings = List.length found
then found
else
let ords' = List.map (remove_found_from_ordering found) ords in
(* any of these only have 1 possibility? *)
let has_one = List.find (fun o -> List.length (snd o) = 1) ords' in
let uniq_pos = List.nth (snd has_one) 0 in
Printf.printf "found %s position: %i \n" (fst has_one) uniq_pos;
go ords' ((uniq_pos, fst has_one) :: found)
in
go orderings []
let find_departure_positions ords =
List.filter (fun (pos, name) ->
try String.sub name 0 9 = "departure"
with Invalid_argument _ -> false
) ords
|> List.map (fun (pos, name) -> (name, pos))
let values_at_positions positions ticket =
List.map (fun (name, pos) -> List.nth ticket pos) positions
let () =
let (rules, my_ticket, nearby) = read_file "day16.txt" |> read_chunks in
(* Part 1 *)
let invalid_nums = find_invalid_values rules nearby in
let invalid_sum = List.fold_left (+) 0 invalid_nums in
Printf.printf "invalid sum: %i\n" invalid_sum;
(* Part 2 *)
let valid_tickets = find_valid_tickets rules nearby in
let all_orderings = find_all_orderings rules valid_tickets in
let ordering = find_ordering all_orderings in
let departure_positions = find_departure_positions ordering in
print_newline ();
List.iter (fun (a, b) -> Printf.printf "%s %i\n" a b) departure_positions;
let departure_values = values_at_positions departure_positions my_ticket in
print_newline ();
List.iter (fun x -> Printf.printf "departure value %i\n" x) departure_values;
let prod = List.fold_left ( * ) 1 departure_values in
Printf.printf "\nproduct: %i" prod;