Mull it Over
Today's challenge screamed Regex when I first saw it, mainly because whenever I see "extract parts of this string", Regex is my goto;
Basic Concept and Requirements
So we need to find all the mul(number1, number2)
and multiply these together, but ignore all other characters.
So we need to find a mechanism to find all the valid mul()
function declarations.
Part1
To do this, we can leverage the power of Regex, using the following pattern
mul\([0-9]{1,3},[0-9]{1,3}\)"
Which will match on mul(
+ any number between 0-9 , a number of 1 > 3 times
+ closing bracket.
Once we've got the mul()
matches, we can utilise regex again to pull out the numbers, parsing these and adding to the total.
Quite a simple and straightforward solution.
void Part1()
{
const string regex = @"mul\([0-9]{1,3},[0-9]{1,3}\)";
var matches = Regex.Matches(input, regex);
var total = 0;
foreach (Match match in matches)
{
var numbers = GetNumbers(match);
total += numbers[0] * numbers[1];
}
}
int[] GetNumbers(Match match)
{
var numbers = Regex.Matches(match.Value, "\\d{1,3}");
var a = int.Parse(numbers[0].Value);
var b = int.Parse(numbers[1].Value);
return [a, b];
}
Part 2
This added slightly more complicated instructions, adding the caveat that do()
and don't()
phrases will enable or disable the mil()
functions.
The best way to handle this, seemed easy, update the Regex pattern to account for do()
dont()
or mul(number, number
The Regex will now look for any of these phrases, by using the |
oprator.
We can then loop through these and using a switch
statemnt decide if we're looking at a do, dont or mil() match, and update the enabled flag accordingly.
It's then a simple check of whether its a mul()
and isEnabled
is True
, before multiplying and adding to the total.
Full Code for both solutions below
using System.Text.RegularExpressions;
var input = File.ReadAllText("./input1.txt");
// var input = @"xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))";
Part1();
Part2();
return;
void Part1()
{
const string regex = @"mul\([0-9]{1,3},[0-9]{1,3}\)";
var matches = Regex.Matches(input, regex);
var total = 0;
foreach (Match match in matches)
{
var numbers = GetNumbers(match);
total += numbers[0] * numbers[1];
}
Console.WriteLine("Total: " + total);
}
void Part2()
{
const string regex = @"do\(\)|don't\(\)|mul\([0-9]{1,3},[0-9]{1,3}\)";
var matches = Regex.Matches(input, regex);
// At the start, mul instructions are enabled
var isEnabled = true;
var total = 0;
// loop over the matches (e.g do(), dont() or mul(x, y)
foreach (Match match in matches)
{
switch (match.Value)
{
case "do()":
isEnabled = true;
break;
case "don't()":
isEnabled = false;
break;
default:
{
if (match.Value.StartsWith("mul") && isEnabled)
{
var numbers = GetNumbers(match);
total += numbers[0] * numbers[1];
}
break;
}
}
}
Console.WriteLine("Total: " + total);
}
int[] GetNumbers(Match match)
{
var numbers = Regex.Matches(match.Value, "\\d{1,3}");
var a = int.Parse(numbers[0].Value);
var b = int.Parse(numbers[1].Value);
return [a, b];
}
Python Solution Attempt
If you're new to my series, i'll re-iterate, I'm using AoC '24 to help learn and improve my existing Python skills - so all solutions will include both C# and Python attempts.
We can use the similar concepts but leverage Pythons language and functions:
import re
# Read input from file
with open("./input1.txt", "r") as file:
input_text = file.read()
# Part 1
def part1():
regex = r"mul\(\d{1,3},\d{1,3}\)"
matches = re.findall(regex, input_text)
total = 0
for match in matches:
a, b = get_numbers(match)
total += a * b
print(f"Total: {total}")
# Part 2
def part2():
regex = r"do\(\)|don't\(\)|mul\(\d{1,3},\d{1,3}\)"
matches = re.findall(regex, input_text)
is_enabled = True # At the start, mul instructions are enabled
total = 0
for match in matches:
if match == "do()":
is_enabled = True
elif match == "don't()":
is_enabled = False
elif match.startswith("mul") and is_enabled:
a, b = get_numbers(match)
total += a * b
print(f"Total: {total}")
# Helper function to extract numbers from a match
def get_numbers(match):
numbers = re.findall(r"\d{1,3}", match)
return int(numbers[0]), int(numbers[1])
# Execute parts
part1()
part2()
As always you can follow me on twitter or view the whole repo for more solutions on Github.
Top comments (0)