diff --git a/bin/day2407.ml b/bin/day2407.ml index d52de07..e8050c1 100644 --- a/bin/day2407.ml +++ b/bin/day2407.ml @@ -15,56 +15,63 @@ let pow10 n = 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 = +(** [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 = pow10 (1 + log10i 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. *) - if tgt < 0 then false - else + let rec impl tgts nums = match nums with - | [] -> tgt = 0 + | [] -> List.exists (( = ) 0) tgts | 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 + 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 -(** [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 +(** [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 -> checker h (List.rev t) + | h :: t -> is_valid_target h (List.rev t) checkers -let part checker lst = - List.filter (check_target checker) lst +(** [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 is_valid_target1); - (string_of_int, part is_valid_target2); + (string_of_int, part [ check_add; check_mul ]); + (string_of_int, part [ check_add; check_mul; check_cat ]); ]