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

  • @[email protected]
    link
    fedilink
    17 months ago

    Uiua

    Regex my beloved <3

    Run with example input here

    FindMul ← regex "mul\\((\\d+),(\\d+)\\)"
    
    PartOne ← (
      &rs ∞ &fo "input-3.txt"
      FindMul
      /+≡(×°⊟⋕⊏1_2)
    )
    
    IdDont ← ⊗□"don't()"♭
    
    PartTwo ← (
      &rs ∞ &fo "input-3.txt"
      regex "mul\\(\\d+,\\d+\\)|do\\(\\)|don't\\(\\)"
      ⍢(IdDont.
        ↘1⊃↘↙
        ⊗□"do()"♭.
        ⊂↘1↘
      | IdDont.
        ≠⧻,
      )
      ▽♭=0⌕□"do()".
      ≡(×°⊟⋕⊏1_2♭FindMul)♭
      /+
    )
    
    &p "Day 3:"
    &pf "Part 1: "
    &p PartOne
    &pf "Part 2: "
    &p PartTwo
    
  • @[email protected]
    link
    fedilink
    English
    2
    edit-2
    7 months 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

  • @[email protected]
    link
    fedilink
    2
    edit-2
    7 months ago

    Elixir

    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()}")
    
    
    • @[email protected]
      link
      fedilink
      English
      27 months ago

      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().

      • @[email protected]
        link
        fedilink
        17 months ago

        Ah, yes, that’s it. The lazy solution would be to add a “do()” to the end of the input, right? Haha

        • @[email protected]
          link
          fedilink
          17 months ago

          It was actually a line break that broke the regex. Changing from a “.” to “[\s\S]” fixed it.

  • @[email protected]
    link
    fedilink
    3
    edit-2
    7 months ago

    Uiua

    Part 1:

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

    Part 2:

    Filter ← ⍜⊜∘≡⋅""⊸⦷°□
    .&fras "day3/input.txt"
    ∧Filter♭regex"don't\\(\\)?(.*?)(?:do\\(\\)|$)"
    /+≡/×≡⋕≡↘1regex "mul\\((\\d+),(\\d+)\\)"
    
  • bugsmith
    link
    fedilink
    27 months 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
    English
    17 months 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
      fedilink
      27 months ago

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

  • TunaCowboy
    link
    fedilink
    1
    edit-2
    7 months 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
    1
    edit-2
    7 months 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
    
  • @[email protected]
    link
    fedilink
    37 months 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)

  • the_beber
    link
    fedilink
    English
    27 months 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()
    }
    
    ´´´
  • Deebster
    link
    fedilink
    1
    edit-2
    7 months ago

    Rust feat. pest

    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.

    part1    =  { SOI ~ (mul_expr | junk)+ ~ EOI }
    part2    =  { (enabled | disabled)+ ~ EOI }
    mul_expr =  { "mul(" ~ number ~ "," ~ number ~ ")" }
    number   =  { ASCII_DIGIT{1,3} }
    junk     = _{ ASCII }
    on       = _{ "do()" }
    off      = _{ "don't()" }
    enabled  = _{ (SOI | on) ~ (!(off) ~ (mul_expr | junk))+ }
    disabled = _{ off ~ (!(on) ~ junk)+ }
    
    use std::fs;
    
    use color_eyre::eyre;
    use pest::Parser;
    use pest_derive::Parser;
    
    #[derive(Parser)]
    #[grammar = "memory.pest"]
    pub struct MemoryParser;
    
    fn parse(input: &str, rule: Rule) -> eyre::Result<usize> {
        let sum = MemoryParser::parse(rule, input)?
            .next()
            .expect("input must be ASCII")
            .into_inner()
            .filter(|pair| pair.as_rule() == Rule::mul_expr)
            .map(|pair| {
                pair.into_inner()
                    .map(|num| num.as_str().parse::<usize>().unwrap())
                    .product::<usize>()
            })
            .sum();
        Ok(sum)
    }
    
    fn part1(filepath: &str) -> eyre::Result<usize> {
        let input = fs::read_to_string(filepath)?;
        parse(&input, Rule::part1)
    }
    
    fn part2(filepath: &str) -> eyre::Result<usize> {
        let input = fs::read_to_string(filepath)?;
        parse(&input, Rule::part2)
    }
    
    fn main() -> eyre::Result<()> {
        color_eyre::install()?;
    
        let part1 = part1("d03/input.txt")?;
        let part2 = part2("d03/input.txt")?;
        println!("Part 1: {part1}\nPart 2: {part2}");
        Ok(())
    }
    
  • @[email protected]
    link
    fedilink
    27 months 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)
    
  • @[email protected]
    link
    fedilink
    4
    edit-2
    7 months 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 ;
    
  • Zarlin
    link
    fedilink
    27 months ago

    Nim

    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.