Advent of Code: Mull it over


Day 3 assignment

2024-12-03-generated.png

“Our computers are having issues, so I have no idea if we have any Chief Historians in stock! You’re welcome to check the warehouse, though,” says the mildly flustered shopkeeper at the North Pole Toboggan Rental Shop. The Historians head out to take a look.

Day 3 - Advent of Code 2024

Solution in Java

Full source can be found: in GitHub

Part 1

For part 1 I decided to use a regular expression (line 4) to extract all the mul(x,y) instructions of the input. For each match we find the result is converted to a number using a multiplication (line 7). Finally all the results are reduced using the sum() operation.

 1public void part1() {  
 2    var multiplication = inputLoader.string();  
 3  
 4    var sum = Pattern.compile("mul\\(([1-9][0-9]{0,2}),([1-9][0-9]{0,2})\\)")  
 5            .matcher(multiplication)  
 6            .results()  
 7            .mapToInt(m -> Integer.parseInt(m.group(1)) * Integer.parseInt(m.group(2)))  
 8            .sum();  
 9  
10    validator.part1(sum);  
11}

Part 2

In part 2 the assignment added the do() to enable parsing of the multiplications, and don't() to disable this parsing.

Updating the regular expression

My initial attempt for part 2 was to adjust the already created regular expression to also scan for the do() and don't() instructions. This however does mean that I need to check match group 0 to make sure it is either do, don't or the mul operation.

 1public void part2() {  
 2    var multiplication = inputLoader.string();  
 3  
 4    var c = 0;  
 5    var inclusion = true;  
 6    var matcher = Pattern.compile("mul\\(([1-9][0-9]{0,2}),([1-9][0-9]{0,2})\\)|(do\\(\\))|don't\\(\\)")  
 7            .matcher(multiplication);  
 8    while (matcher.hasMatch()) {  
 9        if (matcher.group(0).startsWith("do(")) {  
10            inclusion = true;  
11        } else if (matcher.group(0).startsWith("don't(")) {  
12            inclusion = false;  
13        } else if (inclusion) {  
14            c += Integer.parseInt(matcher.group(1)) * Integer.parseInt(matcher.group(2));  
15        }  
16    }  
17    validator.part2(c);  
18}

Creating a custom language parser

I was hinted by a colleague that a language parser might be faster then the regular expression, so in my second attempt for this day I also wrote a very simplified language parser.

 1public void part2() {  
 2    var multiplication = inputLoader.string();  
 3    var offset = 0;  
 4  
 5    var answer = 0;  
 6    while (offset < multiplication.length()) {  
 7        var nextDont = multiplication.indexOf("don't()", offset);  
 8        var nextMultiply = multiplication.indexOf("mul(", offset);  
 9  
10        if (nextDont < nextMultiply && nextDont > 0) {  
11            offset = multiplication.indexOf("do()", nextDont) + 4;  
12        } else if (nextMultiply > 0) {  
13            offset = nextMultiply + 4;  
14            var closing = multiplication.indexOf(')', offset);  
15            if (closing == -1 || closing - offset > 7) {  
16                // invalid not max 3 numbers and on ,  
17                continue;  
18            }  
19  
20            var separator = multiplication.indexOf(',', offset);  
21            if (separator == -1 || separator > closing) {  
22                // invalid no , inside mul function  
23                continue;  
24            }  
25  
26            var numbers = multiplication.substring(offset, closing).split(",");  
27            answer += Integer.parseInt(numbers[0]) * Integer.parseInt(numbers[1]);  
28            offset = closing;  
29        } else {  
30            // no more multiplications  
31            break;  
32        }  
33    }  
34  
35    validator.part2(answer);  
36}

To my surprise the custom parser was not faster then the previous attempt with a regular expression. So at least in Java it appears that processing of regular expressions is efficient. Or the code I wrote for the second attempt could still use some significant improvements.


See also