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

top 50 comments

sorted by: hot top controversial new old
[–] [S] 6 points 2 years ago

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

  • source
  • [–] 5 points 2 years ago (1 child)

    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)
    
  • source
  • hideshow 2 child comments
  • [–] 5 points 2 years ago (3 children)

    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))
    
  • source
  • hideshow 6 child comments
  • [–] 5 points 2 years ago* (last edited 2 years ago) (1 child)

    C

    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);
    }
    

    https://github.com/sjmulder/aoc/blob/master/2024/c/day03.c

  • source
  • hideshow 2 child comments
  • [–] 2 points 2 years ago

    Got the code a little shorter:

    Code

    #include "common.h"
    
    static int
    parse_mul(const char **stringp, int *ap, int *bp)
    {
    	const char *cur = *stringp, *end;
    
    	if (strncmp(cur, "mul(", 4)) { return 0; } cur += 4;
    	*ap = (int)strtol(cur, (char **)&end, 10);
    	if (end == cur)  { return 0; } cur = end;
    	if (*cur != ',') { return 0; } cur += 1;
    	*bp = (int)strtol(cur, (char **)&end, 10);
    	if (end == cur)  { return 0; } cur = end;
    	if (*cur != ')') { return 0; } cur += 1;
    
    	*stringp = cur;
    	return 1;
    }
    
    int
    main(int argc, char **argv)
    {
    	static char buf[32*1024];
    	const char *p;
    	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 (p = buf; *p; )
    		if (parse_mul(&p, &a, &b)) { p1 += a*b; p2 += a*b*!dont; }
    		else if (!strncmp(p, "do()", 4))    { dont = 0; p += 4; }
    		else if (!strncmp(p, "don't()", 7)) { dont = 1; p += 7; }
    		else p++;
    
    	printf("03: %d %d\n", p1, p2);
    }
    

  • source
  • parent
  • [–] 5 points 2 years ago* (1 child)

    Nim

    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 pair = match[4..^2].split(',')
          let mult = pair[0].parseInt * pair[1].parseInt
          result.part1 += mult
          if allow: result.part2 += mult
    

    Codeberg repo

  • source
  • hideshow 2 child comments
  • [–] 4 points 2 years ago* (last edited 2 years 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 ;
    
  • source
  • [–] 4 points 2 years ago

    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}");
    }
    

  • source
  • [–] 4 points 2 years ago* (last edited 2 years ago)

    Uiua

    Uses experimental feature of fold to track the running state of do/don't.

    [edit] Slightly re-written to make it less painful :-) Try it online!

    # Experimental!
    DataP₁       ← $ xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))
    DataPβ‚‚       ← $ xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))
    GetMul       ← $ mul\((\d{1,3}),(\d{1,3})\)
    GetMulDoDont ← $ mul\(\d{1,3},\d{1,3}\)|do\(\)|don\'t\(\)
    
    &p/+≑(/Γ—β‰‘β‹•β†˜1)regexGetMul DataP₁ # Part 1
    
    # Build an accumulator to track running state of do/don't
    Filter ← β†˜1βŠ‚:∧(⍣(0 Β°"don"|1 Β°"do("|.β—Œ)) :1≑(↙3Β°β–‘)
    β‰‘βŠ’ regex GetMulDoDont DataPβ‚‚
    β–½βŠΈβ‰‘β—‡(≍"mul"↙3)β–½βŠΈFilter      # Apply Filter, remove the spare 'do's
    &p/+≑◇(/Γ—β‰‘β—‡β‹•β†˜1⊒regexGetMul) # Get the digits and multiply, sum.
    
  • source
  • [–] 4 points 2 years ago (1 child)

    Haskell

    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
    

  • source
  • hideshow 2 child comments
  • [–] 2 points 2 years ago (1 child)

    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?

  • source
  • parent
  • hideshow 2 child comments
  • [–] 3 points 2 years ago (1 child)

    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.

  • source
  • parent
  • hideshow 2 child comments
  • [–] 4 points 2 years 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)
    
  • source
  • [–] 3 points 2 years 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)

  • source
  • [–] 3 points 2 years ago* (last edited 2 years ago) (1 child)

    Kotlin

    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
    }
    
  • source
  • hideshow 2 child comments
  • [–] 3 points 2 years ago (1 child)

    Rust with nom parser

    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 memmap2::Mmap;
    use nom::{
        branch::alt, bytes::complete::*, character::complete::*, combinator::map, multi::many_till,
        sequence::tuple, AsBytes, IResult,
    };
    
    #[derive(Debug)]
    enum Token {
        Do,
        Dont,
        Mul(u64, u64),
    }
    
    fn main() -> anyhow::Result<()> {
        let file = std::fs::File::open("input.txt")?;
        let mmap = unsafe { Mmap::map(&file)? };
    
        let mut sum_part1 = 0;
        let mut sum_part2 = 0;
        let mut enabled = true;
    
        let mut cursor = mmap.as_bytes();
        while let Ok(token) = parse(cursor) {
            match token.1 .1 {
                Token::Do => enabled = true,
                Token::Dont => enabled = false,
                Token::Mul(left, right) => {
                    let prod = left * right;
                    sum_part1 += prod;
                    if enabled {
                        sum_part2 += prod;
                    }
                }
            }
    
            cursor = token.0;
        }
    
        println!("part1: {} part2: {}", sum_part1, sum_part2);
    
        Ok(())
    }
    
    type ParseResult<'a> =
        Result<(&'a [u8], (Vec<char>, Token)), nom::Err<nom::error::Error<&'a [u8]>>>;
    
    fn parse(input: &[u8]) -> ParseResult {
        many_till(
            anychar,
            alt((
                map(doit, |_| Token::Do),
                map(dont, |_| Token::Dont),
                map(mul, |el| Token::Mul(el.2, el.4)),
            )),
        )(input)
    }
    
    fn doit(input: &[u8]) -> IResult<&[u8], &[u8]> {
        tag("do()")(input)
    }
    
    fn dont(input: &[u8]) -> IResult<&[u8], &[u8]> {
        tag("don't()")(input)
    }
    
    type ParsedMulResult<'a> = (&'a [u8], &'a [u8], u64, &'a [u8], u64, &'a [u8]);
    
    fn mul(input: &[u8]) -> IResult<&[u8], ParsedMulResult> {
        tuple((tag("mul"), tag("("), u64, tag(","), u64, tag(")")))(input)
    }
    
  • source
  • hideshow 2 child comments
  • [–] 3 points 2 years ago (1 child)

    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)
    }
    

  • source
  • hideshow 2 child comments
  • [–] 3 points 2 years ago (1 child)

    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)
    }
    

  • source
  • parent
  • hideshow 2 child comments
  • [–] 3 points 2 years ago

    J

    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 =: (&lt;0 0) &amp; {"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) &amp; -) @: {:)
    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
    
  • source
  • [+] 3 points 2 years ago*
    [–] 3 points 2 years 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+)\\)"
    
  • source
  • [–] 2 points 2 years 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

  • source
  • [–] 2 points 2 years ago (1 child)

    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.

  • source
  • hideshow 2 child comments
  • [–] 2 points 2 years ago* (last edited 2 years ago) (1 child)

    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()}")
    
    
  • source
  • hideshow 2 child comments
  • [–] 2 points 2 years ago (1 child)

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

  • source
  • parent
  • hideshow 2 child comments
  • [–] 2 points 2 years 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 })
    }
    
  • source
  • [–] 2 points 2 years ago (1 child)

    Rust

    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!();
    
  • source
  • hideshow 2 child comments
  • [–] [S] 1 point 2 years ago

    Our part1's are basically identical, but for part 2 I just regex deleted everything between dont() anddo() `, after sanitizing out the newlines.

    I guess it would have failed if my test data had a mul after the last dont, but it worked out, so good enough :)

  • source
  • parent
  • [–] 2 points 2 years ago*

    Python

    After a bunch of fiddling yesterday and today I finally managed to arrive at a regex-only solution for part 2. That re.DOTALL is crucial here.

    import re
    from pathlib import Path
    
    
    def parse_input_one(input: str) -> list[tuple[int]]:
        p = re.compile(r"mul\((\d{1,3}),(\d{1,3})\)")
        return [(int(m[0]), int(m[1])) for m in p.findall(input)]
    
    
    def parse_input_two(input: str) -> list[tuple[int]]:
        p = re.compile(r"don't\(\).*?do\(\)|mul\((\d{1,3}),(\d{1,3})\)", re.DOTALL)
        return [(int(m[0]), int(m[1])) for m in p.findall(input) if m[0] and m[1]]
    
    
    def part_one(input: str) -> int:
        pairs = parse_input_one(input)
        return sum(map(lambda v: v[0] * v[1], pairs))
    
    
    def part_two(input: str) -> int:
        pairs = parse_input_two(input)
        return sum(map(lambda v: v[0] * v[1], pairs))
    
    
    if __name__ == "__main__":
        input = Path("input").read_text("utf-8")
        print(part_one(input))
        print(part_two(input))
    
  • source
  • [–] 2 points 2 years 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.

  • source
  • [–] 2 points 2 years 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()
    }
    
    ´´´
    
  • source
  • [–] 1 point 2 years 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
    
  • source
  • [+] 1 point 2 years ago* (last edited 1 year ago)
    [–] 1 point 2 years ago

    Rust

    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!

  • source
  • [–] 1 point 2 years ago* (last edited 2 years 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()
    

  • source
  • [–] 1 point 2 years ago* (last edited 2 years 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
    
  • source
  • [–] 1 point 2 years ago* (last edited 2 years 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(())
    }
    
  • source
  • 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
    
  • source
  • load more comments
    view more: next β€Ί