64 lines
2.5 KiB
OCaml
64 lines
2.5 KiB
OCaml
let ints_of_file fname =
|
|
Aoc.strings_of_file fname |> List.map (Aoc.ints_of_string ~sep:"[: ]+")
|
|
|
|
(** [check_add tgt v] Check to see if [X + v = tgt] is a valid operation. If not
|
|
returns [None] otherwise returns [Some X]. *)
|
|
let check_add tgt v = if v > tgt then None else Some (tgt - v)
|
|
|
|
(** [check_mul tgt v] Check to see if [X * v = tgt] is a valid operation. If not
|
|
returns [None] otherwise returns [Some X]. *)
|
|
let check_mul tgt v = if tgt mod v = 0 then Some (tgt / v) else None
|
|
|
|
(** [check_cat tgt v] Check to see if [X || v = tgt] is a valid operation. If
|
|
not returns [None] otherwise returns [Some X]. *)
|
|
let check_cat tgt v =
|
|
let p = Aoc.pow10 (Aoc.digits10 v) in
|
|
if tgt mod p = v then Some (tgt / p) else None
|
|
|
|
(** [is_valid_target tgt nums ops] returns [true] if we can reach [tgt] from
|
|
[nums] using [ops]. [nums] is in reverse order. *)
|
|
let is_valid_target tgt nums ops =
|
|
(* We work backwards from the target and note that:
|
|
- if we ever go negative then the route we have taken is invalid
|
|
- we can only multiply if dividing the tgt by a number results in no
|
|
remainder.
|
|
- We can concat if the last digits of tgt match the number being checked.
|
|
|
|
The recursion in impl takes in a list of current target values and the list
|
|
of numbers. It attempts every op on every potential current target with the
|
|
head of the nums list. After filtering out those ops which do not produce
|
|
a valid target we recurse to the next number, with a new list of target
|
|
numbers.
|
|
*)
|
|
let rec impl tgts nums =
|
|
match nums with
|
|
| [] -> List.exists (( = ) 0) tgts
|
|
| h :: t ->
|
|
impl
|
|
(List.map (fun tgt -> List.map (fun op -> op tgt h) ops) tgts
|
|
|> List.concat |> List.filter_map Fun.id)
|
|
t
|
|
in
|
|
impl [ tgt ] nums
|
|
|
|
(** [check_target checkers lst] returns [true] iff the given target can be
|
|
reached from its numbers. [checkers] is the list of operations that can be
|
|
used. *)
|
|
let check_target checkers = function
|
|
| [] -> false
|
|
| h :: t -> is_valid_target h (List.rev t) checkers
|
|
|
|
(** [part checkers lst] Gets the some of the targets in the list [lst] which can
|
|
be made given the input numbers. [checkers] are the operations that are
|
|
valid to be used.*)
|
|
let part checkers lst =
|
|
List.filter (check_target checkers) lst
|
|
|> List.map List.hd |> List.fold_left ( + ) 0
|
|
|
|
let _ =
|
|
Aoc.main ints_of_file
|
|
[
|
|
(string_of_int, part [ check_add; check_mul ]);
|
|
(string_of_int, part [ check_add; check_mul; check_cat ]);
|
|
]
|