Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
Yay parsers! I've gotten quite comfortable writing these with C. Using out pointers arguments for the cursor that are only updated if the match is successful makes for easy bookkeeping.
Code
#include "common.h"
static int
parse_exact(const char **stringp, const char *expect)
{
const char *s = *stringp;
int i;
for (i=0; s[i] && expect[i] && s[i] == expect[i]; i++)
;
if (expect[i])
return 0;
*stringp = &s[i];
return 1;
}
static int
parse_int(const char **stringp, int *outp)
{
char *end;
int val;
val = (int)strtol(*stringp, &end, 10);
if (end == *stringp)
return 0;
*stringp = end;
if (outp) *outp = val;
return 1;
}
static int
parse_mul(const char **stringp, int *ap, int *bp)
{
const char *cur = *stringp;
int a,b;
if (!parse_exact(&cur, "mul(") ||
!parse_int(&cur, &a) ||
!parse_exact(&cur, ",") ||
!parse_int(&cur, &b) ||
!parse_exact(&cur, ")"))
return 0;
*stringp = cur;
if (ap) *ap = a;
if (bp) *bp = b;
return 1;
}
int
main(int argc, char **argv)
{
static char buf[32*1024];
const char *cur;
size_t nr;
int p1=0,p2=0, a,b, dont=0;
if (argc > 1)
DISCARD(freopen(argv[1], "r", stdin));
nr = fread(buf, 1, sizeof(buf), stdin);
assert(!ferror(stdin));
assert(nr != sizeof(buf));
buf[nr] = '\0';
for (cur = buf; *cur; )
if (parse_exact(&cur, "do()"))
dont = 0;
else if (parse_exact(&cur, "don't()"))
dont = 1;
else if (parse_mul(&cur, &a, &b)) {
p1 += a * b;
if (!dont) p2 += a * b;
} else
cur++;
printf("03: %d %d\n", p1, p2);
}
but this wouldn't rock anymore in the second part, so I had to resort to python for it
Python
import sys
f = "\n".join(sys.stdin.readlines())
f = f.replace("don't()", "\ndon't()\n")
f = f.replace("do()", "\ndo()\n")
import re
enabled = True
muls = []
for line in f.split("\n"):
if line == "don't()":
enabled = False
if line == "do()":
enabled = True
if enabled:
for match in re.finditer(r"mul\((\d{1,3}),(\d{1,3})\)", line):
muls.append(int(match.group(1)) * int(match.group(2)))
pass
pass
print(sum(muls))
My first insinct was similar, add line breaks to the do and dont modifiers. But I got toa caught up thinking id have to keep track of the added characters, I wound up just abusing split()-
I started poking at doing a proper lexer/parser, but then I thought about how early in AoC it is and how low the chance is that the second part will require proper parsing.
So therefore; hello regex my old friend, I've come to talk with you again.
C#
List<string> instructions = new List<string>();
public void Input(IEnumerable<string> lines)
{
foreach (var line in lines)
instructions.AddRange(Regex.Matches(line, @"mul\(\d+,\d+\)|do\(\)|don't\(\)").Select(m => m.Value));
}
public void Part1()
{
var sum = instructions.Select(mul => Regex.Match(mul, @"(\d+),(\d+)").Groups.Values.Skip(1).Select(g => int.Parse(g.Value))).Select(cc => cc.Aggregate(1, (acc, val) => acc * val)).Sum();
Console.WriteLine($"Sum: {sum}");
}
public void Part2()
{
bool enabled = true;
long sum = 0;
foreach(var inst in instructions)
{
if (inst.StartsWith("don't"))
enabled = false;
else if (inst.StartsWith("do"))
enabled = true;
else if (enabled)
sum += Regex.Match(inst, @"(\d+),(\d+)").Groups.Values.Skip(1).Select(g => int.Parse(g.Value)).Aggregate(1, (acc, val) => acc * val);
}
Console.WriteLine($"Sum: {sum}");
}
Part 1, just find the regex groups, parse to int, and done.
Part 1
func part1() {
file, _ := os.Open("input.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
re := regexp.MustCompile(`mul\(([0-9]{1,3}),([0-9]{1,3})\)`)
product := 0
for scanner.Scan() {
line := scanner.Text()
submatches := re.FindAllStringSubmatch(line, -1)
for _, s := range submatches {
a, _ := strconv.Atoi(s[1])
b, _ := strconv.Atoi(s[2])
product += (a * b)
}
}
fmt.Println(product)
}
Part 2, not so simple. Ended up doing some weird hack with a map to check if the multiplication was enabled or not. Also instead of finding regex groups I had to find the indices, and then interpret what those mean... Not very readable code I'm afraid
Part2
func part2() {
file, _ := os.Open("input.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
mulRE := regexp.MustCompile(`mul\(([0-9]{1,3}),([0-9]{1,3})\)`)
doRE := regexp.MustCompile(`do\(\)`)
dontRE := regexp.MustCompile(`don't\(\)`)
product := 0
enabled := true
for scanner.Scan() {
line := scanner.Text()
doIndices := doRE.FindAllStringIndex(line, -1)
dontIndices := dontRE.FindAllStringIndex(line, -1)
mulSubIndices := mulRE.FindAllStringSubmatchIndex(line, -1)
mapIndices := make(map[int]string)
for _, do := range doIndices {
mapIndices[do[0]] = "do"
}
for _, dont := range dontIndices {
mapIndices[dont[0]] = "dont"
}
for _, mul := range mulSubIndices {
mapIndices[mul[0]] = "mul"
}
nextMatch := 0
for i := 0; i < len(line); i++ {
val, ok := mapIndices[i]
if ok && val == "do" {
enabled = true
} else if ok && val == "dont" {
enabled = false
} else if ok && val == "mul" {
if enabled {
match := mulSubIndices[nextMatch]
a, _ := strconv.Atoi(string(line[match[2]:match[3]]))
b, _ := strconv.Atoi(string(line[match[4]:match[5]]))
product += (a * b)
}
nextMatch++
}
}
}
fmt.Println(product)
}
I also used Go - my solution for part 1 was essentially identical to yours. I went a different route for part 2 that I think ended up being simpler though.
I just prepended do() and don't() to the original regex with a |, that way it captured all 3 in order and I just looped through all the matches once and toggled the isEnabled flag accordingly.
Always interesting to see how other people tackle the same problem!
Part 2 Code
func part2() {
filePath := "input.txt"
file, _ := os.Open(filePath)
defer file.Close()
pattern := regexp.MustCompile(`do\(\)|don't\(\)|mul\((\d{1,3}),(\d{1,3})\)`)
productSum := 0
isEnabled := true
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
matches := pattern.FindAllStringSubmatch(line, -1)
for _, match := range matches {
if match[0] == "do()" {
isEnabled = true
} else if match[0] == "don't()" {
isEnabled = false
} else if isEnabled && len(match) == 3 {
n, _ := strconv.Atoi(match[1])
m, _ := strconv.Atoi(match[2])
productSum += n * m
}
}
}
fmt.Println("Total: ", productSum)
}
We can take advantage of the manageable size of the input to avoid explicit looping and mutable state; instead, construct vectors which give, for each character position in the input, the position of the most recent do() and most recent don't(); for part 2 a multiplication is enabled if the position of the most recent do() (counting start of input as 0) is greater than that of the most recent don't() (counting start of input as minus infinity).
load 'regex'
raw =: fread '3.data'
mul_matches =: 'mul\(([[:digit:]]{1,3}),([[:digit:]]{1,3})\)' rxmatches raw
NB. a b sublist y gives elements [a..b) of y
sublist =: ({~(+i.)/)~"1 _
NB. ". is number parsing
mul_results =: */"1 ". (}."2 mul_matches) sublist raw
result1 =: +/ mul_results
do_matches =: 'do\(\)' rxmatches raw
dont_matches =: 'don''t\(\)' rxmatches raw
match_indices =: (<0 0) & {"2
do_indices =: 0 , match_indices do_matches NB. start in do mode
dont_indices =: match_indices dont_matches
NB. take successive diffs, then append length from last index to end of string
run_lengths =: (}. - }:) , (((#raw) & -) @: {:)
do_map =: (run_lengths do_indices) # do_indices
dont_map =: (({. , run_lengths) dont_indices) # __ , dont_indices
enabled =: do_map > dont_map
result2 =: +/ ((match_indices mul_matches) { enabled) * mul_results
fun part1(input: String): Int {
val pattern = "mul\\((\\d{1,3}),(\\d{1,3})\\)".toRegex()
var sum = 0
pattern.findAll(input).forEach { match ->
val first = match.groups[1]?.value?.toInt()!!
val second = match.groups[2]?.value?.toInt()!!
sum += first * second
}
return sum
}
fun part2(input: String): Int {
val pattern = "mul\\((\\d{1,3}),(\\d{1,3})\\)|don't\\(\\)|do\\(\\)".toRegex()
var sum = 0
var enabled = true
pattern.findAll(input).forEach { match ->
if (match.value == "do()") enabled = true
else if (match.value == "don't()") enabled = false
else if (enabled) {
val first = match.groups[1]?.value?.toInt()!!
val second = match.groups[2]?.value?.toInt()!!
sum += first * second
}
}
return sum
}
First time writing Elixir. It's probably janky af.
I've had some help from AI to get some pointers along the way. I'm not competing in any way, just trying to learn and have fun.
~~Part 2 is currently not working, and I can't figure out why.
I'm trying to just remove everything from "don't()" to "do()" and just pass the rest through the working solution for part 1. Should work, right?
Any pointers?~~
edit; working solution:
defmodule Three do
def get_input do
File.read!("./input.txt")
end
def extract_operations(input) do
Regex.scan(~r/mul\((\d{1,3}),(\d{1,3})\)/, input)
|> Enum.map(fn [_op, num1, num2] ->
num1 = String.to_integer(num1)
num2 = String.to_integer(num2)
[num1 * num2]
end)
end
def sum_products(ops) do
List.flatten(ops)
|> Enum.filter(fn x -> is_integer(x) end)
|> Enum.sum()
end
def part1 do
extract_operations(get_input())
|> sum_products()
end
def part2 do
String.split(get_input(), ~r/don\'t\(\)[\s\S]*?do\(\)/)
|> Enum.map(&extract_operations/1)
|> sum_products()
end
end
IO.puts("part 1: #{Three.part1()}")
IO.puts("part 2: #{Three.part2()}")
Part 2 is currently not working, and I can’t figure out why. I’m trying to just remove everything from “don’t()” to “do()” and just pass the rest through the working solution for part 1. Should work, right?
I think I had the same issue. Consider what happens if there isn't a do() after a don't().
Oof, a parsing problem :/ This is some nasty-ass code. step is almost the State monad written out explicitly.
Solution
import Control.Monad
import Data.Either
import Data.List
import Text.Parsec
data Ins = Mul !Int !Int | Do | Dont
readInput :: String -> [Ins]
readInput = fromRight undefined . parse input ""
where
input = many ins <* many anyChar
ins =
choice . map try $
[ Mul <$> (string "mul(" *> arg) <*> (char ',' *> arg) <* char ')',
Do <$ string "do()",
Dont <$ string "don't()",
anyChar *> ins
]
arg = do
s <- many1 digit
guard $ length s <= 3
return $ read s
run f = snd . foldl' step (True, 0)
where
step (e, a) i =
case i of
Mul x y -> (e, if f e then a + x * y else a)
Do -> (True, a)
Dont -> (False, a)
main = do
input <- readInput <$> readFile "input03"
print $ run (const True) input
print $ run id input
Love to see you chewing through this parsing problem in Haskell, I didn't dare use Parsec because I wasn't confident enough.
Why did you decide to have a strict definition of Mul !Int !Int?
My guess is because a linter and/or HLS was suggesting it. I know HLS used to suggest making your fields strict in almost all cases. In this case I have a hunch that it slightly cuts down on memory usage because we use almost all Muls either way. So it does not need to keep the string it is parsed from in memory as part of the thunk.
But it probably makes a small/negligible difference here.
import ../aoc, re, sequtils, strutils, math
proc mulsum*(line:string):int=
let matches = line.findAll(re"mul\([0-9]{1,3},[0-9]{1,3}\)")
let pairs = matches.mapIt(it[4..^2].split(',').map(parseInt))
pairs.mapIt(it[0]*it[1]).sum
proc filter*(line:string):int=
var state = true;
var i=0
while i < line.len:
if state:
let off = line.find("don't()", i)
if off == -1:
break
result += line[i..<off].mulsum
i = off+6
state = false
else:
let on = line.find("do()", i)
if on == -1:
break
i = on+4
state = true
if state:
result += line[i..^1].mulsum
proc solve*(input:string): array[2,int] =
#part 1&2
result = [input.mulsum, input.filter]
I had a nicer solution in mind for part 2, but for some reason nre didn't want to work for me, and re couldn't give me the start/end or all results, so I ended up doing this skip/toggle approach.
Also initially I was doing it line by line out of habit from other puzzles, but then ofc the don't()s didn't propagate to the next line.
From a first glance it was obviously a regex problem.
I'm using tinyre here instead of stdlib re library just because I'm more familiar with it.
import pkg/tinyre
proc solve(input: string): AOCSolution[int, int] =
var allow = true
for match in input.match(reG"mul\(\d+,\d+\)|do\(\)|don't\(\)"):
if match == "do()": allow = true
elif match == "don't()": allow = false
else:
let m = match[4..^2].split(',')
let mult = m[0].parseInt * m[1].parseInt
result.part1 += mult
if allow: result.part2 += mult
Decided to give it a go with the nom parser (first time using this crate). Turned out quite nicely. Had some issues with the alt combinator: All alternatives have to return the same type, using a enum to wrap all options did the trick.
use crate::utils::read_lines;
pub fn solution1() {
let lines = read_lines("src/day3/input.txt");
let sum = lines
.map(|line| {
let mut sum = 0;
let mut command_bytes = Vec::new();
for byte in line.bytes() {
match (byte, command_bytes.as_slice()) {
(b')', [.., b'0'..=b'9']) => {
handle_mul(&mut command_bytes, &mut sum);
}
_ if matches_mul(byte, &command_bytes) => {
command_bytes.push(byte);
}
_ => {
command_bytes.clear();
}
}
}
sum
})
.sum::<usize>();
println!("Sum of multiplication results = {sum}");
}
pub fn solution2() {
let lines = read_lines("src/day3/input.txt");
let mut can_mul = true;
let sum = lines
.map(|line| {
let mut sum = 0;
let mut command_bytes = Vec::new();
for byte in line.bytes() {
match (byte, command_bytes.as_slice()) {
(b')', [.., b'0'..=b'9']) if can_mul => {
handle_mul(&mut command_bytes, &mut sum);
}
(b')', [b'd', b'o', b'(']) => {
can_mul = true;
command_bytes.clear();
}
(b')', [.., b't', b'(']) => {
can_mul = false;
command_bytes.clear();
}
_ if matches_do_or_dont(byte, &command_bytes)
|| matches_mul(byte, &command_bytes) =>
{
command_bytes.push(byte);
}
_ => {
command_bytes.clear();
}
}
}
sum
})
.sum::<usize>();
println!("Sum of enabled multiplication results = {sum}");
}
fn matches_mul(byte: u8, command_bytes: &[u8]) -> bool {
matches!(
(byte, command_bytes),
(b'm', [])
| (b'u', [.., b'm'])
| (b'l', [.., b'u'])
| (b'(', [.., b'l'])
| (b'0'..=b'9', [.., b'(' | b'0'..=b'9' | b','])
| (b',', [.., b'0'..=b'9'])
)
}
fn matches_do_or_dont(byte: u8, command_bytes: &[u8]) -> bool {
matches!(
(byte, command_bytes),
(b'd', [])
| (b'o', [.., b'd'])
| (b'n', [.., b'o'])
| (b'\'', [.., b'n'])
| (b'(', [.., b'o' | b't'])
| (b't', [.., b'\''])
)
}
fn handle_mul(command_bytes: &mut Vec<u8>, sum: &mut usize) {
let first_num_index = command_bytes
.iter()
.position(u8::is_ascii_digit)
.expect("Guarunteed to be there");
let comma_index = command_bytes
.iter()
.position(|&c| c == b',')
.expect("Guarunteed to be there.");
let num1 = bytes_to_num(&command_bytes[first_num_index..comma_index]);
let num2 = bytes_to_num(&command_bytes[comma_index + 1..]);
*sum += num1 * num2;
command_bytes.clear();
}
fn bytes_to_num(bytes: &[u8]) -> usize {
bytes
.iter()
.rev()
.enumerate()
.map(|(i, digit)| (*digit - b'0') as usize * 10usize.pow(i as u32))
.sum::<usize>()
}
Definitely not my prettiest code ever. It would probably look nicer if I used regex or some parsing library, but I took on the self-imposed challenge of not using third party libraries. Also, this is already further than I made it last year!
No Zalgo here! I wasted a huge amount of time by not noticing that the second part's example input was different - my code worked fine but my test failed 🤦♂️
pest.rs is lovely, although part two made my PEG a bit ugly.
I wrote matchesActual cause all of smalltalk'sstupid matchesDo: or whatever don't give you the actual match with captures, only substrings (that wasted a good 40 minutes).
Also smalltalk really needs an index operator
day3p1: input
| reg sum |
reg := 'mul\((\d\d?\d?),(\d\d?\d?)\)' asRegex.
sum := 0.
reg matchesActual: input do: [ :m | " + sum at end cause operator precedence"
sum := (m subexpression: 2) asInteger * (m subexpression: 3) asInteger + sum
].
^ sum.
day3p2: input
| reg sum do val |
reg := 'do(\:?n''t)?\(\)|mul\((\d{1,3}),(\d{1,3})\)' asRegex.
sum := 0.
do := true.
reg matchesActual: input do: [ :m |
val := m subexpression: 1.
(val at: 1) = $d ifTrue: [ do := (val size < 5) ]
ifFalse: [
do ifTrue: [
sum := (m subexpression: 2) asInteger * (m subexpression: 3) asInteger + sum.
]. ].
].
^ sum.
import re
import aoc
def setup():
return (aoc.get_lines(3), 0)
def one():
lines, acc = setup()
for line in lines:
ins = re.findall(r'mul\(\d+,\d+\)', line)
for i in ins:
p = [int(x) for x in re.findall(r'\d+', i)]
acc += p[0] * p[1]
print(acc)
def two():
lines, acc = setup()
on = 1
for line in lines:
ins = re.findall(r"do\(\)|don't\(\)|mul\(\d+,\d+\)", line)
for i in ins:
if i == "do()":
on = 1
elif i == "don't()":
on = 0
elif on:
p = [int(x) for x in re.findall(r'\d+', i)]
acc += p[0] * p[1]
print(acc)
one()
two()
def process(input, part2=False):
if part2:
input = re.sub(r'don\'t\(\).+?do\(\)', '', input) # remove everything between don't() and do()
total = [ int(i[0]) * int(i[1]) for i in re.findall(r'mul\((\d+),(\d+)\)', input) ]
return sum(total)
Given the structure of the input file, we just have to ignore everything between don't() and do(), so remove those from the instructions before processing.
I did part 2 live with the python interactive shell. I deleted all the stuff where I was just exploring ideas.
part 1:
import re
def multiply_and_add(data: "str") -> int:
digit_matches = re.findall(r"mul\(\d{0,3},\d{0,3}\)", data)
result = 0
for _ in digit_matches:
first = _.split("(")[1].split(")")[0].split(",")[0]
second = _.split("(")[1].split(")")[0].split(",")[1]
result += int(first) * int(second)
return result
with open("input") as file:
data = file.read()
answer = multiply_and_add(data)
print(answer)
part 2:
Python 3.11.2 (main, Aug 26 2024, 07:20:54) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import solution2
<re.Match object; span=(647, 651), match='do()'>
>>> from solution2 import *
>>> split_on_dont = data.split("don't()")
>>> valid = []
>>> valid.append(split_on_dont[0])
>>> for substring in split_on_dont[1:]:
... subsubstrings = substring.split("do()", maxsplit=1)
... for subsubstring in subsubstrings[1:]:
... valid.append(subsubstring)
...
>>> answer = 0
>>> for _ in valid:
... answer += multiply_and_add(_)
...
>>> answer
103811193
Regex made this one pretty straightforward. The second part additionally looks for do() and don't() in the same regex, then we do a case distinction on the match.
use regex::{Regex, Captures};
fn mul_cap(cap: Captures) -> i32 {
let a = cap.get(1).unwrap().as_str().parse::<i32>().unwrap();
let b = cap.get(2).unwrap().as_str().parse::<i32>().unwrap();
a * b
}
fn part1(input: String) {
let re = Regex::new(r"mul\((\d{1,3}),(\d{1,3})\)").unwrap();
let res = re.captures_iter(&input).map(mul_cap).sum::<i32>();
println!("{res}");
}
fn part2(input: String) {
let re = Regex::new(r"do\(\)|don't\(\)|mul\((\d{1,3}),(\d{1,3})\)").unwrap();
let mut enabled = true;
let mut res = 0;
for cap in re.captures_iter(&input) {
match cap.get(0).unwrap().as_str() {
"do()" => enabled = true,
"don't()" => enabled = false,
_ if enabled => res += mul_cap(cap),
_ => {}
}
}
println!("{res}");
}
util::aoc_main!();
I did not try to make my solution concise and kept separate code for part 1 and part 2 with test cases for both to check if I broke anything. But after struggling with Day 2 I am quite pleased to have solved Day 3 with only a little bugfixing.
function calcLineResult(line::String)
lineResult::Int = 0
enabled::Bool = true
for i=1 : length(line)
line[i]!='m' ? continue : (i<length(line) ? i+=1 : continue)
line[i]!='u' ? continue : (i<length(line) ? i+=1 : continue)
line[i]!='l' ? continue : (i<length(line) ? i+=1 : continue)
line[i]!='(' ? continue : (i<length(line) ? i+=1 : continue)
num1Str::String = ""
while line[i] in ['0','1','2','3','4','5','6','7','8','9'] #should check for length of digits < 3, but works without
num1Str = num1Str*line[i]; (i<length(line) ? i+=1 : continue)
end
line[i]!=',' ? continue : (i<length(line) ? i+=1 : continue)
num2Str::String = ""
while line[i] in ['0','1','2','3','4','5','6','7','8','9'] #should check for length of digits < 3, but works without
num2Str = num2Str*line[i]; (i<length(line) ? i+=1 : continue)
end
line[i]==')' ? lineResult+=parse(Int,num1Str)*parse(Int,num2Str) : continue
end
return lineResult
end
function calcLineResultWithEnabling(line::String,enabled::Bool)
lineResult::Int = 0
for i=1 : length(line)
if enabled && line[i] == 'm'
i<length(line) ? i += 1 : continue
line[i]!='u' ? continue : (i<length(line) ? i+=1 : continue)
line[i]!='l' ? continue : (i<length(line) ? i+=1 : continue)
line[i]!='(' ? continue : (i<length(line) ? i+=1 : continue)
num1Str::String = ""
while line[i] in ['0','1','2','3','4','5','6','7','8','9']
num1Str = num1Str*line[i]; (i<length(line) ? i+=1 : continue)
end
line[i]!=',' ? continue : (i<length(line) ? i+=1 : continue)
num2Str::String = ""
while line[i] in ['0','1','2','3','4','5','6','7','8','9']
num2Str = num2Str*line[i]; (i<length(line) ? i+=1 : continue)
end
line[i]==')' ? lineResult+=parse(Int,num1Str)*parse(Int,num2Str) : continue
elseif line[i] == 'd'
i<length(line) ? i += 1 : continue
line[i]!='o' ? continue : (i<length(line) ? i+=1 : continue)
if line[i] == '('
i<length(line) ? i += 1 : continue
line[i]==')' ? enabled=true : continue
#@info i,line[i-3:i]
elseif line[i] == 'n'
i<length(line) ? i += 1 : continue
line[i]!=''' ? continue : (i<length(line) ? i+=1 : continue)
line[i]!='t' ? continue : (i<length(line) ? i+=1 : continue)
line[i]!='(' ? continue : (i<length(line) ? i+=1 : continue)
line[i]==')' ? enabled=false : continue
#@info i,line[i-6:i]
else
nothing
end
end
end
return lineResult,enabled
end
function calcMemoryResult(inputFile::String,useEnabling::Bool)
memoryRes::Int = 0
f = open(inputFile,"r")
lines = readlines(f)
close(f)
enabled::Bool = true
for line in lines
if useEnabling
lineRes::Int,enabled = calcLineResultWithEnabling(line,enabled)
memoryRes += lineRes
else
memoryRes += calcLineResult(line)
end
end
return memoryRes
end
if abspath(PROGRAM_FILE) == @__FILE__
@info "Part 1"
@debug "checking test input"
inputFile::String = "day03InputTest"
memoryRes::Int = calcMemoryResult(inputFile,false)
try
@assert memoryRes==161
catch e
throw(ErrorException("$e memoryRes=$memoryRes"))
end
@debug "test input ok"
@debug "running real input"
inputFile::String = "day03Input"
memoryRes::Int = calcMemoryResult(inputFile,false)
try
@assert memoryRes==153469856
catch e
throw(ErrorException("$e memoryRes=$memoryRes"))
end
println("memory result: $memoryRes")
@debug "real input ok"
@info "Part 2"
@debug "checking test input"
inputFile::String = "day03InputTest"
memoryRes::Int = calcMemoryResult(inputFile,true)
try
@assert memoryRes==48
catch e
throw(ErrorException("$e memoryRes=$memoryRes"))
end
@debug "test input ok"
@debug "running real input"
inputFile::String = "day03Input"
memoryRes::Int = calcMemoryResult(inputFile,true)
try
@assert memoryRes==77055967
catch e
throw(ErrorException("$e memoryRes=$memoryRes"))
end
println("memory result: $memoryRes")
@debug "real input ok"
end
defmodule AdventOfCode.Solution.Year2024.Day03 do
def part1(input) do
Regex.scan(~r/mul\((?<l>\d+),(?<r>\d+)\)/, input, capture: ["l", "r"])
|> Stream.map(fn l -> Enum.reduce(l, 1, &(&2 * String.to_integer(&1))) end)
|> Enum.sum()
end
def part2(input) do
input |> String.replace(~r/don't\(\).*(do\(\)|$)/Us, "") |> part1
end
end
(defun p1-mult (str)
"pulls out numbers and multiplies them, assumes already filtered by size"
(let ((vals (ppcre:all-matches-as-strings "\\d+" str)))
(apply #'* (or (mapcar #'parse-integer vals) '(0)))))
(defun p1-process-line (line)
"look for mul, do the mul, and sum"
(let ((ptrn "mul\\(\\d?\\d?\\d,\\d?\\d?\\d\\)"))
(apply #'+ (mapcar #'p1-mult (ppcre:all-matches-as-strings ptrn line)))))
(defun run-p1 (file)
(let ((data (read-file file #'p1-process-line)))
(apply #'+ data)))
(defun p2-process-line (line)
"looks for mul, do, and don't"
(let ((ptrn "(mul\\(\\d?\\d?\\d,\\d?\\d?\\d\\))|(do\\(\\))|(don't\\(\\))"))
(ppcre:all-matches-as-strings ptrn line)))
(defun p2-filter (data)
"expects list containing the string tokens (mul, do, don't) from the file"
(let ((process t))
(loop for x in data
when (string= "don't()" x)
do (setf process nil)
when (string= "do()" x)
do (setf process t)
when process
sum (p1-mult x))))
(defun run-p2 (file)
(let ((data (read-file file #'p2-process-line)))
;; treat the input as one line to make processing the do's and don't's easier
(p2-filter (flatten data))))