The Historians take you to a familiar rope bridge over a river in the middle of a jungle. The Chief isn't on this side of the bridge, though; maybe he's on the other side?
When you go to cross the bridge, you notice a group of engineers trying to repair it. (Apparently, it breaks pretty frequently.) You won't be able to cross until it's fixed.
Visit the website for the full story and puzzle description.
It's time to pull out the recursion guns. I introduced a checker logic that goes through the numbers in one line of input and tries all possible operators on the accumulated result to reach the target.
The common logic that parses the input and executes the checker was extracted into a single Solve
function, but I found it more readable to have distinct checkers for the two parts of the problem.
Everything runs in about a second, but since it's just a single line, I couldn't stand and added an optimization in Check2
to exit early when the accumulated result exceeds the target.
namespace AdventOfCode.Y2024.Day07;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
[ProblemName("Bridge Repair")]
class Solution : Solver {
public object PartOne(string input) => Filter(input, Check1).Sum();
public object PartTwo(string input) => Filter(input, Check2).Sum();
// returns those calibrations that are valid according to the checker
private IEnumerable<long> Filter(string input, Func<long,long,List<long>, bool> check) =>
from line in input.Split("\n")
let parts = Regex.Matches(line, @"\d+").Select(m=>long.Parse(m.Value))
let target = parts.First()
let nums = parts.Skip(1).ToList()
where check(target, nums[0], nums[1..])
select target;
// separate checkers provided for the two parts, these recursive functions go
// over the numbers and use all allowed operators to update the accumulated result.
// at the end of the recursion we simply check if we reached the target
private bool Check1(long target, long acc, List<long> nums) =>
nums switch {
[] => target == acc,
_ => Check1(target, acc * nums[0], nums[1..]) ||
Check1(target, acc + nums[0], nums[1..])
};
private bool Check2(long target, long acc, List<long> nums) =>
nums switch {
_ when acc > target => false, // optimization: early exit from deadend
[] => target == acc,
_ => Check2(target, long.Parse($"{acc}{nums[0]}"), nums[1..]) ||
Check2(target, acc * nums[0], nums[1..]) ||
Check2(target, acc + nums[0], nums[1..])
};
}
Please ☆ my repo if you like it!