Day 3: Mull It Over

Megathread guidelines

  • 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

FAQ

  • @andMoonsValue
    link
    1
    edit-2
    9 hours ago

    Python

    I’m surprised I don’t see more people taking advantage of eval I thought it was pretty slick.

    import operator 
    import re
    
    with open('input.txt', 'r') as file:
        memory = file.read()
    
    matches = re.findall("mul\(\d{1,3},\d{1,3}\)|don't\(\)|do\(\)", memory)
    
    enabled = 1
    filtered_matches = []
    for instruction in matches:
        if instruction == "don't()":
            enabled = 0
            continue
        elif instruction == "do()":
            enabled = 1
            continue
        elif enabled: 
            filtered_matches.append(instruction)
    multipled = [eval(f"operator.{x}") for x in filtered_matches]
    print(sum(multiples))
    
  • @[email protected]
    link
    fedilink
    English
    217 hours ago

    Kotlin

    Just the standard Regex stuff. I found this website to be very helpful to write the patterns. (Very useful in general)

    fun main() {
        fun part1(input: List<String>): Int =
            Regex("""mul\(\d+,\d+\)""").findAll(input.joinToString()).sumOf {
                with(Regex("""\d+""").findAll(it.value)) { this.first().value.toInt() * this.last().value.toInt() }
            }
    
        fun part2(input: List<String>): Int {
            var isMultiplyInstructionEnabled = true  // by default
            return Regex("""mul\(\d+,\d+\)|do\(\)|don't\(\)""").findAll(input.joinToString()).fold(0) { acc, instruction ->
                when (instruction.value) {
                    "do()" -> acc.also { isMultiplyInstructionEnabled = true }
                    "don't()" -> acc.also { isMultiplyInstructionEnabled = false }
                    else -> {
                        if (isMultiplyInstructionEnabled) {
                            acc + with(Regex("""\d+""").findAll(instruction.value)) { this.first().value.toInt() * this.last().value.toInt() }
                        } else acc
                    }
                }
            }
        }
    
        val testInputPart1 = readInput("Day03_test_part1")
        val testInputPart2 = readInput("Day03_test_part2")
        check(part1(testInputPart1) == 161)
        check(part2(testInputPart2) == 48)
    
        val input = readInput("Day03")
        part1(input).println()
        part2(input).println()
    }
    
    ´´´
  • @[email protected]
    link
    fedilink
    31 day ago

    Python

    Part1:

    matches = re.findall(r"(mul\((\d+),(\d+)\))", input)
    muls = [int(m[1]) * int(m[2]) for m in matches]
    print(sum(muls))
    

    Part2:

    instructions = list(re.findall(r"(do\(\)|don't\(\)|(mul\((\d+),(\d+)\)))", input)
    mul_enabled = True
    muls = 0
    
    for inst in instructions:
        if inst[0] == "don't()":
            mul_enabled = False
        elif inst[0] == "do()":
            mul_enabled = True
        elif mul_enabled:
            muls += int(inst[2]) * int(inst[3])
    
    print(muls)
    
  • bugsmith
    link
    fedilink
    222 hours ago

    Gleam

    Struggled with the second part as I am still very new to this very cool language, but got there after scrolling for some inspiration.

    import gleam/int
    import gleam/io
    import gleam/list
    import gleam/regex
    import gleam/result
    import gleam/string
    import simplifile
    
    pub fn main() {
      let assert Ok(data) = simplifile.read("input.in")
      part_one(data) |> io.debug
      part_two(data) |> io.debug
    }
    
    fn part_one(data) {
      let assert Ok(multiplication_pattern) =
        regex.from_string("mul\\(\\d{1,3},\\d{1,3}\\)")
      let assert Ok(digit_pattern) = regex.from_string("\\d{1,3},\\d{1,3}")
      let multiplications =
        regex.scan(multiplication_pattern, data)
        |> list.flat_map(fn(reg) {
          regex.scan(digit_pattern, reg.content)
          |> list.map(fn(digits) {
            digits.content
            |> string.split(",")
            |> list.map(fn(x) { x |> int.parse |> result.unwrap(0) })
            |> list.reduce(fn(a, b) { a * b })
            |> result.unwrap(0)
          })
        })
        |> list.reduce(fn(a, b) { a + b })
        |> result.unwrap(0)
    }
    
    fn part_two(data) {
      let data = "do()" <> string.replace(data, "\n", "") <> "don't()"
      let assert Ok(pattern) = regex.from_string("do\\(\\).*?don't\\(\\)")
      regex.scan(pattern, data)
      |> list.map(fn(input) { input.content |> part_one })
      |> list.reduce(fn(a, b) { a + b })
    }
    
  • @[email protected]
    link
    fedilink
    120 hours ago

    Rust

    Didn’t do anything crazy here – ended up using regex like a bunch of other folks.

    solution
    use regex::Regex;
    
    use crate::shared::util::read_lines;
    
    fn parse_mul(input: &[String]) -> (u32, u32) {
        // Lazy, but rejoin after having removed `\n`ewlines.
        let joined = input.concat();
        let re = Regex::new(r"mul\((\d+,\d+)\)|(do\(\))|(don't\(\))").expect("invalid regex");
    
        // part1
        let mut total1 = 0u32;
        // part2 -- adds `do()`s and `don't()`s
        let mut total2 = 0u32;
        let mut enabled = 1u32;
    
        re.captures_iter(&joined).for_each(|c| {
            let (_, [m]) = c.extract();
            match m {
                "do()" => enabled = 1,
                "don't()" => enabled = 0,
                _ => {
                    let product: u32 = m.split(",").map(|s| s.parse::<u32>().unwrap()).product();
                    total1 += product;
                    total2 += product * enabled;
                }
            }
        });
        (total1, total2)
    }
    
    pub fn solve() {
        let input = read_lines("inputs/day03.txt");
        let (part1_res, part2_res) = parse_mul(&input);
        println!("Part 1: {}", part1_res);
        println!("Part 2: {}", part2_res);
    }
    
    #[cfg(test)]
    mod test {
        use super::*;
    
        #[test]
        fn test_solution() {
            let test_input = vec![
                "xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))".to_string(),
            ];
            let (p1, p2) = parse_mul(&test_input);
            eprintln!("P1: {p1}, P2: {p2}");
            assert_eq!(161, p1);
            assert_eq!(48, p2);
        }
    }
    
    

    Solution on my github (Made it public now)

  • @thirteen37
    link
    English
    21 day ago

    Python

    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.

    • TunaCowboy
      link
      222 hours ago

      Sub was my first instinct too, but I got a bad answer and saw that my input had unbalanced do/don’t.

      • @thirteen37
        link
        221 hours ago

        I did wonder if that might be the case, I must have been lucky with my input.

  • @Karmmah
    link
    1
    edit-2
    21 hours ago

    Julia

    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
    
  • @RollingRagdoll
    link
    2
    edit-2
    1 day ago

    Uiua

    Part 1:

    &fras "day3/input.txt"
    /+≡/×≡⋕≡↘1regex "mul\\((\\d+),(\\d+)\\)"
    

    Part 2:

    Filter  ⍜⊜∘≡⋅""⊸⦷°□
    .&fras "day3/input.txt"
    Filterregex"don't\\(\\)?(.*?)(?:do\\(\\)|$)"
    /+≡/×≡⋕≡↘1regex "mul\\((\\d+),(\\d+)\\)"
    
  • TunaCowboy
    link
    1
    edit-2
    21 hours ago

    python

    solution
    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()
    
  • @[email protected]
    link
    fedilink
    3
    edit-2
    3 hours ago

    Factor

    : get-input ( -- corrupted-input )
      "vocab:aoc-2024/03/input.txt" utf8 file-contents ;
    
    : get-muls ( corrupted-input -- instructions )
      R/ mul\(\d+,\d+\)/ all-matching-subseqs ;
    
    : process-mul ( instruction -- n )
      R/ \d+/ all-matching-subseqs
      [ string>number ] map-product ;
    
    : solve ( corrupted-input -- n )
      get-muls [ process-mul ] map-sum ;
    
    : part1 ( -- n )
      get-input solve ;
    
    : part2 ( -- n )
      get-input
      R/ don't\(\)(.|\n)*?do\(\)/ split concat
      R/ don't\(\)(.|\n)*/ "" re-replace
      solve ;
    
  • @VegOwOtenks
    link
    English
    51 day ago

    I couldn’t figure it out in haskell, so I went with bash for the first part

    Shell

    cat example | grep -Eo "mul\([[:digit:]]{1,3},[[:digit:]]{1,3}\)" | cut -d "(" -f 2 | tr -d ")" | tr "," "*" | paste -sd+ | bc
    

    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))
    
    • @[email protected]
      link
      fedilink
      21 day ago

      Really cool trick. I did a bunch of regex matching that I’m sure I won’t remember how it works few weeks from now, this is so much readable

  • @[email protected]
    link
    fedilink
    31 day ago

    Go

    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)
    }
    
    • @[email protected]
      link
      fedilink
      English
      21 day ago

      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)
      }
      
      • @[email protected]
        link
        fedilink
        11 day ago

        Honestly this is soo much better, I’m not proud of my code at all haha. Thanks for sharing, definitely adding that | to my bag of tricks

  • @[email protected]OPM
    link
    fedilink
    61 day ago

    Sorry for the delay posting this one, Ategon seemed to have it covered, so I forgot :D I will do better.

  • @vole
    link
    English
    2
    edit-2
    1 day ago

    Raku

    sub MAIN($input) {
        grammar Muls {
            token TOP { .*? <mul>+%.*? .* }
            token mul { "mul(" <number> "," <number> ")" }
            token number { \d+ }
        }
    
        my $parsedMuls = Muls.parsefile($input);
        my @muls = $parsedMuls<mul>.map({.<number>».Int});
        my $part-one-solution = @muls.map({[*] $_.List}).sum;
        say "part 1: $part-one-solution";
    
        grammar EnabledMuls {
            token TOP { .*? [<.disabled> || <mul>]+%.*? .* }
            token mul { "mul(" <number> "," <number> ")" }
            token number { \d+ }
            token disabled { "don't()" .*? ["do()" || $] }
        }
    
        my $parsedEnabledMuls = EnabledMuls.parsefile($input);
        my @enabledMuls = $parsedEnabledMuls<mul>.map({.<number>».Int});
        my $part-two-solution = @enabledMuls.map({[*] $_.List}).sum;
        say "part 2: $part-two-solution";
    }
    

    github

  • @LeixB
    link
    51 day ago

    Haskell

    module Main where
    
    import Control.Arrow hiding ((+++))
    import Data.Char
    import Data.Functor
    import Data.Maybe
    import Text.ParserCombinators.ReadP hiding (get)
    import Text.ParserCombinators.ReadP qualified as P
    
    data Op = Mul Int Int | Do | Dont deriving (Show)
    
    parser1 :: ReadP [(Int, Int)]
    parser1 = catMaybes <$> many ((Just <$> mul) <++ (P.get $> Nothing))
    
    parser2 :: ReadP [Op]
    parser2 = catMaybes <$> many ((Just <$> operation) <++ (P.get $> Nothing))
    
    mul :: ReadP (Int, Int)
    mul = (,) <$> (string "mul(" *> (read <$> munch1 isDigit <* char ',')) <*> (read <$> munch1 isDigit <* char ')')
    
    operation :: ReadP Op
    operation = (string "do()" $> Do) +++ (string "don't()" $> Dont) +++ (uncurry Mul <$> mul)
    
    foldOp :: (Bool, Int) -> Op -> (Bool, Int)
    foldOp (_, n) Do = (True, n)
    foldOp (_, n) Dont = (False, n)
    foldOp (True, n) (Mul a b) = (True, n + a * b)
    foldOp (False, n) _ = (False, n)
    
    part1 = sum . fmap (uncurry (*)) . fst . last . readP_to_S parser1
    part2 = snd . foldl foldOp (True, 0) . fst . last . readP_to_S parser2
    
    main = getContents >>= print . (part1 &&& part2)