From 8a454e20820426556ee004343c38876d14b72942 Mon Sep 17 00:00:00 2001 From: Matthew Gretton-Dann Date: Sat, 7 Dec 2024 07:29:03 +0000 Subject: [PATCH] Add 2024 day 7. --- bin/day2407.ml | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ bin/dune | 4 +-- 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 bin/day2407.ml diff --git a/bin/day2407.ml b/bin/day2407.ml new file mode 100644 index 0000000..d52de07 --- /dev/null +++ b/bin/day2407.ml @@ -0,0 +1,70 @@ +let ints_of_file fname = + Aoc.strings_of_file fname |> List.map (Aoc.ints_of_string ~sep:"[: ]+") + +(** [log10i i] returns the integer part of [log10 i]. [i] must be greater than + zero. *) +let log10i i = + let rec impl acc = function 0 -> acc | x -> impl (acc + 1) (x / 10) in + assert (i > 0); + impl ~-1 i + +(** [pow10 n] returns [10] raised to the [n]th power. [n] must be non-negative. +*) +let pow10 n = + let rec impl acc = function 0 -> acc | x -> impl (acc * 10) (x - 1) in + assert (n >= 0); + impl 1 n + +(** [is_valid_target1 tgt nums] checks to see if we can reach [tgt] from [nums] + using multiply and addition only. [nums] should be in reverse order to that + given in the problem. *) +let rec is_valid_target1 tgt nums = + (* 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. + *) + if tgt < 0 then false + else + match nums with + | [] -> tgt = 0 + | h :: t -> + let sum_valid = is_valid_target1 (tgt - h) t in + if tgt mod h = 0 then sum_valid || is_valid_target1 (tgt / h) t + else sum_valid + +(** [is_valid_target2 tgt nums] checks to see if we can reach [tgt] from [nums] + using multiply, addition and "merger" only. [nums] should be in reverse + order to that given in the problem. *) +let rec is_valid_target2 tgt nums = + (* Same principle as [is_valid_target1], but also noting that merger op is + only possible if the last digits of the current [tgt] are the same as the + number being examined. *) + if tgt < 0 then false + else + match nums with + | [] -> tgt = 0 + | h :: t -> + let sum_valid = is_valid_target2 (tgt - h) t in + let p = pow10 (1 + log10i h) in + let merge_valid = + if tgt mod p = h then is_valid_target2 (tgt / p) t else false + in + if tgt mod h = 0 then + sum_valid || merge_valid || is_valid_target2 (tgt / h) t + else sum_valid || merge_valid + +let check_target checker = function + | [] -> false + | h :: t -> checker h (List.rev t) + +let part checker lst = + List.filter (check_target checker) lst + |> List.map List.hd |> List.fold_left ( + ) 0 + +let _ = + Aoc.main ints_of_file + [ + (string_of_int, part is_valid_target1); + (string_of_int, part is_valid_target2); + ] diff --git a/bin/dune b/bin/dune index 01e6c77..b674f78 100644 --- a/bin/dune +++ b/bin/dune @@ -1,4 +1,4 @@ (executables - (public_names day2401 day2402 day2403 day2404 day2405 day2406) - (names day2401 day2402 day2403 day2404 day2405 day2406) + (public_names day2401 day2402 day2403 day2404 day2405 day2406 day2407) + (names day2401 day2402 day2403 day2404 day2405 day2406 day2407) (libraries str aoc))