Advent of Code - Day 7: Bridge Repair


Day 7 assignment

2024-12-07-generated.png

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?

-- Day 7 - Advent of Code 2024

Solution in Java

Full source can be found: in GitHub

For today I setup a small little interface Instruction to capture the instructions that we can apply on the input numbers. As well as already adding the LiteralValue implementation of this interface to capture numbers. This would help to compute the possible answers in the two parts of today.

 1interface Instruction {  
 2    long solve();  
 3}
 4
 5record LiteralValue(String number) implements Instruction {  
 6    @Override  
 7    public long solve() {  
 8        return Long.parseLong(number);  
 9    }  
10}  

Part 1

For part 1 all I had to do is create two potential aggregations for the Instruction, being the Addition and the Multiplication instruction.

 1record Addition(Instruction left, Instruction right) implements Instruction {  
 2    @Override  
 3    public long solve() {  
 4        return left.solve() + right.solve();  
 5    }
 6}  
 7  
 8record Multiplication(Instruction left, Instruction right) implements Instruction {  
 9    @Override  
10    public long solve() {  
11        return left.solve() * right.solve();  
12    } 
13}

Using these three instructions I can loop over the elements of the input text and create all potential combinations of multiply and add of all the numbers per line.

 1private List<Instruction> withPlusAndMultiply(List<Instruction> numbers) {  
 2    if (numbers.size() == 1) {  
 3        return List.of(numbers.getFirst());  
 4    }  
 5  
 6    var calculations = new ArrayList<Instruction>();  
 7    for (var partial : withPlusAndMultiply(numbers.subList(1, numbers.size()))) {  
 8        calculations.add(new Addition(numbers.getFirst(), partial));  
 9        calculations.add(new Multiplication(numbers.getFirst(), partial));  
10    }  
11    return calculations;  
12}

For each combination of the aggregations we can then check if the answer is the same as the answer in the input.

 1public void part1() {  
 2    var totalCalibrationResult = inputLoader.splitOnNewLine()  
 3            .mapToLong(line -> computeLine(line, this::withPlusAndMultiply))  
 4            .sum();  
 5  
 6    validator.part1(totalCalibrationResult);  
 7}
 8
 9private long computeLine(String line, Function<List<Instruction>, List<Instruction>> computeFunction) {  
10    var split = line.split(": ");  
11    var answer = Long.parseLong(split[0]);  
12    var numbers = Arrays.asList(split[1].split(" "));  
13    Collections.reverse(numbers);  
14  
15    return filteredPossibilities(answer, numbers, computeFunction);  
16}
17
18private long filteredPossibilities(long answer, List<String> numbers, Function<List<Instruction>, List<Instruction>> computeFunction) {  
19    var literalsInOrder = numbers.stream()  
20            .<Instruction>map(LiteralValue::new)  
21            .toList();  
22  
23    var posibilities = computeFunction.apply(literalsInOrder);  
24    log.trace("Found solutions for {}: {}", answer, posibilities);  
25    for (var possibility : posibilities) {  
26        if (possibility.solve() == answer) {  
27            log.debug("Found match for {}: {}", answer, possibility);  
28            return answer;  
29        }  
30    }  
31  
32    return 0;  
33}

Part 2

For the second part of today I added another Instruction, begin the append.

1record Append(Instruction left, Instruction right) implements Instruction {  
2    @Override  
3    public long solve() {  
4        return Long.parseLong("%d%d".formatted(right.solve(), left.solve()));  
5    }  
6}

Using the new instruction I created an additional method that would compute all potential aggregation functions withPlusAndMultiplyAndAppend and ran the input against it.

 1public void part2() {  
 2    var totalCalibrationResult = inputLoader.splitOnNewLine()  
 3            .mapToLong(line -> computeLine(line, this::withPlusAndMultiplyAndAppend))  
 4            .sum();  
 5  
 6    validator.part2(totalCalibrationResult);  
 7}
 8
 9private List<Instruction> withPlusAndMultiplyAndAppend(List<Instruction> numbers) {  
10    if (numbers.size() == 1) {  
11        return List.of(numbers.getFirst());  
12    }  
13  
14    var calculations = new ArrayList<Instruction>();  
15    for (var partial : withPlusAndMultiplyAndAppend(numbers.subList(1, numbers.size()))) {  
16        calculations.add(new Addition(numbers.getFirst(), partial));  
17        calculations.add(new Multiplication(numbers.getFirst(), partial));  
18        calculations.add(new Append(numbers.getFirst(), partial));  
19    }  
20    return calculations;  
21}

See also