[–] 3 points 11 months ago* (last edited 11 months ago)

Ironically for Lisp, a good chunk of the work here is type conversion, because strings, vectors, multidimensional arrays, characters, and numbers don't have implicit conversions between them; you have to specify what you want explicitly. I also found it easier to manually transpose the character array for part 2 rather than traverse in column-major order, because that makes the relationship between input and output data structure more transparent.

(ql:quickload :str)
(ql:quickload :array-operations)

(defun parse-line-1 (line)
  (let ((broken-line (str:split " " (str:collapse-whitespaces (str:trim line)))))
    (mapcar #'(lambda (s)
                (cond ((equal s "+") #'+)
                      ((equal s "*") #'*)
                      (t (parse-integer s))))
            broken-line)))

(defun read-inputs-1 (filename)
  (let* ((input-lines (uiop:read-file-lines filename)))
    (mapcar #'parse-line-1 input-lines)))

(defun main-1 (filename)
  (let* ((problems (read-inputs-1 filename))
         (arguments (apply #'mapcar #'list (butlast problems))))
    (reduce #'+ (mapcar #'apply (car (last problems)) arguments))))

(defun parse-operands-2 (lines)
  (let* ((initial-rows (length lines))
         (initial-cols (length (car lines)))
         (flat-chars (make-array (list (* initial-rows initial-cols))
                                 :initial-contents (apply #'concatenate 'string lines)))
         (box-chars (make-array (list initial-rows initial-cols) :displaced-to flat-chars))
         (transposed-chars (aops:each-index (i j) (aref box-chars j i))))
    (loop for cv across (aops:split transposed-chars 1)
          for s = (str:trim (coerce cv 'string))
          collect (if (zerop (length s)) nil (parse-integer s)))))

(defun list-split (xs sep &optional (predicate #'equal))
  (let ((current nil)
        (result nil))
    (loop for x in xs
          do (if (funcall predicate x sep)
                 (progn
                   (setf result (cons (reverse current) result))
                   (setf current nil))
                 (setf current (cons x current)))
          finally (setf result (cons (reverse current) result)))
    (reverse result)))

(defun main-2 (filename)
  (let* ((lines (uiop:read-file-lines filename))
         (operators (parse-line-1 (car (last lines))))
         (operands (parse-operands-2 (butlast lines))))
    (loop for rator in operators
          for rands in (list-split operands nil)
          sum (apply rator rands))))
  • source
  • [–] 2 points 11 months ago

    I also thought about trying to rotate, but not for very long. Mine would be a bit simpler if I'd done what you did and build the number string and then check if it's blank.

    fun main() {
        val input = getInput(6)
        val output = parseInput2(input)
        var total = 0L
        for ((numbers, operator) in output) {
            when (operator) {
                '+' -> { total += numbers.sum() }
                '*' -> { total += numbers.reduce { acc, number -> acc * number }}
            }
        }
        println(getElapsedTime())
        println(total)
    }
    
    fun parseInput2(input: String): List<Pair<List<Long>, Char>> {
        val rows = input.lines()
            .filter { it.isNotBlank() }
            .map { it.toCharArray() }
        val output: MutableList<Pair<List<Long>, Char>> = mutableListOf()
        val numberRowCount = rows.size - 1
        var isNewProblem = true
        var currentNumbers: MutableList<Long> = mutableListOf()
        var operator = ' '
        for (column in rows[0].indices) {
            if (!isNewProblem && isColumnEmpty(rows, column)) {
                isNewProblem = true
                output.add(currentNumbers to operator)
                continue
            }
            if (isNewProblem) {
                isNewProblem = false
                currentNumbers = mutableListOf()
                operator = rows.last()[column]
            }
            var number = ""
            for (row in 0..<numberRowCount) {
                if (rows[row][column] != ' ') {
                    number += rows[row][column]
                }
            }
            currentNumbers.add(number.toLong())
        }
        if (!isNewProblem) {
            output.add(currentNumbers to operator)
        }
        return output
    }
    
    fun isColumnEmpty(rows: List<CharArray>, column: Int): Boolean {
        for (i in rows.indices) {
            if (rows[i][column] != ' ') {
                return false
            }
        }
        return true
    }
    
  • source
  • parent
  • context
  • [–] 2 points 11 months ago* (last edited 11 months ago)

    My original solution for part 1 was just removing the last digit, get the highest number, cut off everything up to and including that first number, get the highest number again.
    Once I did part 2 I realized I can just throw in a loop, cut off parts of the end so there's enough numbers left for the subsequent iterations and keep the rest the same.
    Now it works for any number of batteries and all you'd need to change is the number after Total! :D

    Online pad: AoC-2025-D3

    You can even use your own input by uploading a file (make sure it's using LF line endings only with a trailing one at the end) and replacing the example input with this: &rs inf &fo "input-file.txt"

    Code

    $ 987654321111111
    $ 811111111111119
    $ 234234234234278
    $ 818181911112111
    βŠœβˆ˜βŠΈβ‰ @\n
    
    Max ← βŠ’βŠΈβ–
    
    Jolt! ← (
      Β―^
      ""
      β₯(βŠ™(‚⊑Maxβ—‘β†˜+₁
          βŠ™(βŠ™β†˜β€šβ‹…βˆ˜+₁))
        βŠ‚
      )^
      βŠ™β‹…β—Œ
    )
    
    Total! ← (
      ≑(β‹•Jolt!^)
      /+
    )
    
    PartOne ← Total!2
    PartTwo ← Total!12
    
    ⊸PartOne
    &pf "Part One: "
    &p
    PartTwo
    &pf "Part Two: "
    &p
    

  • source
  • [–] 2 points 11 months ago

    Go

    Part 2: Read the whole input in a rune matrix. Scan it column by column, store the numbers as you go, ignoring all spaces, and store the operand when you find it. When you hit an empty column or the end, do the operation and add it to the total.

    spoiler

    func part2() {
    	// file, _ := os.Open("sample.txt")
    	file, _ := os.Open("input.txt")
    	defer file.Close()
    	scanner := bufio.NewScanner(file)
    
    	chars := [][]rune{}
    	for scanner.Scan() {
    		chars = append(chars, []rune(scanner.Text()))
    	}
    
    	m := len(chars)
    	n := len(chars[0])
    	var op rune
    	nums := []int{}
    	total := 0
    
    	for j := range n {
    		current := []rune{}
    		for i := range m {
    			if chars[i][j] == '+' || chars[i][j] == '*' {
    				op = chars[i][j]
    			} else if chars[i][j] != ' ' {
    				current = append(current, chars[i][j])
    			}
    		}
    		if len(current) > 0 {
    			x, _ := strconv.Atoi(string(current))
    			nums = append(nums, x)
    		}
    		if len(current) == 0 || j == n-1 {
    			result := 0
    			if op == '*' {
    				result = 1
    			}
    			for _, x := range nums {
    				if op == '+' {
    					result = result + x
    				} else {
    					result = result * x
    				}
    			}
    			total += result
    			nums = []int{}
    		}
    	}
    
    	fmt.Println(total)
    }
    

  • source
  • [–] 2 points 11 months ago

    C

    Well so much for reading a grid of ints in part 1! For part 2, initially I reworked the parsing to read into a big buffer, but then thought it would be fun to try and use memory-mapped I/O as not to use any more memory than strictly necessary for the final version:

    Code

    #include <stdio.h>
    #include <stdlib.h>
    #include <inttypes.h>
    #include <ctype.h>
    #include <assert.h>
    #include <sys/mman.h>
    #include <unistd.h>
    #include <err.h>
    
    #define GH	5
    
    int
    main()
    {
    	char *data, *g[GH], *p;
    	uint64_t p1=0,p2=0, acc;
    	int len, h=0, i, x,y, val;
    	char op;
    
    	if ((len = (int)lseek(0, 0, SEEK_END)) == -1)
    		err(1, "<stdin>");
    	if (!(data = mmap(NULL, len, PROT_READ, MAP_SHARED, 0, 0)))
    		err(1, "<stdin>");
    	for (i=0; i<len; i++)
    		if (!i || data[i-1]=='\n') {
    			assert(h < GH);
    			g[h++] = data+i;
    		}
    
    	for (x=0; g[h-1]+x < data+len; x++) {
    		if ((op = g[h-1][x]) != '+' && op != '*')
    			continue;
    
    		for (acc = op=='*', y=0; y<h-1; y++) {
    			val = atoi(&g[y][x]);
    			acc = op=='+' ? acc+val : acc*val;
    		}
    
    		p1 += acc;
    
    		for (acc = op=='*', i=0; ; i++) {
    			for (val=0, y=0; y<h-1; y++) {
    				p = &g[y][x+i];
    				if (p < g[y+1] && isdigit(*p))
    					val = val*10 + *p-'0';
    			}
    			if (!val)
    				break;
    			acc = op=='+' ? acc+val : acc*val;
    		}
    
    		p2 += acc;
    	}
    
    	printf("06: %"PRIu64" %"PRIu64"\n", p1, p2);
    }
    

  • source
  • [–] 3 points 11 months ago (1 child)

    Kotlin

    I'm not fully happy with my parsing today, but oh well. I also thought about just plain building the grid and then rotating it, but "normal input parsing" works too.

    Solution

    class Day06 : Puzzle {
    
        val numsPartOne = mutableListOf<MutableList<Long>>()
        val numsPartTwo = mutableListOf<List<Long>>()
        val ops = mutableListOf<(Long, Long) -> Long>()
    
        override fun readFile() {
            val input = readInputFromFile("src/main/resources/a2025/day06.txt")
            val lines = input.lines().filter { it.isNotBlank() }
    
            // parse part1 input
            for (line in lines.dropLast(1)) {
                for ((c, num) in line.trim().split(" +".toRegex()).withIndex()) {
                    if (numsPartOne.getOrNull(c) == null) numsPartOne.add(mutableListOf())
                    numsPartOne[c].add(num.toLong())
                }
            }
    
            // parse part2 input
            var numList = mutableListOf<Long>()
            for (c in 0..<lines.maxOf { it.length }) {
                var numStr = ""
                for (r in 0..<lines.size - 1) {
                    numStr += lines[r].getOrElse(c) { ' ' }
                }
                if (numStr.isBlank()) {
                    numsPartTwo.add(numList)
                    numList = mutableListOf()
                } else {
                    numList.add(numStr.trim().toLong())
                }
            }
            numsPartTwo.add(numList)
    
            // parse operators
            ops.addAll(
                lines.last().split(" +".toRegex())
                    .map { it.trim()[0] }
                    .map {
                        when (it) {
                            '*' -> { a: Long, b: Long -> a * b }
                            '+' -> { a: Long, b: Long -> a + b }
                            else -> throw IllegalArgumentException("Unknown operator: $it")
                        }
                    }
            )
        }
    
        override fun solvePartOne(): String {
            return numsPartOne.mapIndexed { c, list -> list.reduce { a, b -> ops[c](a, b) } }.sum().toString()
        }
    
        override fun solvePartTwo(): String {
            return numsPartTwo.mapIndexed { c, list -> list.reduce { a, b -> ops[c](a, b) } }.sum().toString()
        }
    }
    

    full code on Codeberg

  • source
  • [–] 5 points 11 months ago* (last edited 11 months ago)

    nushell

    I was afk when the puzzle went up so I had another go at doing it on my phone in Turmux with my shell's scripting language. It's quite nice how your shell is also a REPL so you can build up the answer in pieces, although I wrote a file for the second part.

    Phone screenshot of my solution being developed

    open input.txt | str replace --all --regex ' +' ' ' |
            lines | each { $in | str trim } | to text |
            from csv --noheaders --separator ' ' |
            reverse | transpose --ignore-titles |
            each {
                    |list| transpose | skip 1 | if $list.column0 == '+' { math sum } else { math product }
            } |
            math sum
    

    Part 2

    let input = open input.txt | lines | each { $in | split chars }
    let last_row = ($input | length) - 1
    let last_col = ($input | first | length) - 1
    
    mut op = ' '
    mut numbers = []
    mut grand_tot = 0
    for x in $last_col..0 {
      if $op == '=' {
        $op = ' '
        continue
      }
      let n = 0..($last_row - 1) | each { |y| $input | get $y | get $x } | str join | into int
      $numbers = ($numbers | append $n)
    
      $op = $input | get $last_row | get $x
      if $op != ' ' {
        $grand_tot += $numbers | if $op == '+' { math sum } else { math product }
        $numbers = []
        $op = '='
      }
    }
    $grand_tot
    
  • source
  • [–] 2 points 11 months ago

    Rust

    Mainly difficult parsing today.

    View on github

    fn part1(input: String) {
        let mut nums: Vec<Vec<u64>> = Vec::new();
        let mut mul: Vec<bool> = Vec::new();
        for l in input.lines() {
            if l.chars().next().unwrap().is_ascii_digit() {
                let row = l
                    .split_ascii_whitespace()
                    .map(|s| s.parse::<u64>().unwrap())
                    .collect();
                nums.push(row);
            } else {
                mul = l.split_ascii_whitespace().map(|s| s == "*").collect();
            }
        }
        let mut sum = 0;
        for (idx, op_mul) in mul.iter().enumerate() {
            let col = nums.iter().map(|row| row[idx]);
            sum += if *op_mul {
                col.reduce(|acc, n| acc * n)
            } else {
                col.reduce(|acc, n| acc + n)
            }
            .unwrap();
        }
        println!("{sum}");
    }
    
    fn part2(input: String) {
        let grid: Vec<&[u8]> = input.lines().map(|l| l.as_bytes()).collect();
        let n_rows = grid.len() - 1; // Not counting operator row
        let mut op_mul = grid[n_rows][0] == b'*';
        let mut cur = if op_mul { 1 } else { 0 };
        let mut sum = 0;
        for x in 0..grid[0].len() {
            let digits: Vec<u8> = (0..n_rows).map(|y| grid[y][x]).collect();
            if digits.iter().all(|d| *d == b' ') {
                sum += cur;
                op_mul = grid[n_rows][x + 1] == b'*';
                cur = if op_mul { 1 } else { 0 };
                continue;
            }
            let n = String::from_utf8(digits)
                .unwrap()
                .trim()
                .parse::<u64>()
                .unwrap();
            if op_mul {
                cur *= n;
            } else {
                cur += n;
            }
        }
        sum += cur;
        println!("{sum}");
    }
    
    util::aoc_main!();
    
  • source
  • [–] 2 points 11 months ago*

    Go

    Damn, I actually reeaally enjoyed this one! I didn't expect the twist of part 2, but somehow it wasn't that hard to manage.

    Here is my modern solution:

    day06.go

    package main
    
    import (
    	"aoc/utils"
    	"fmt"
    	"regexp"
    	"slices"
    	"strconv"
    	"strings"
    )
    
    type operation int
    
    func (o operation) compute(values []int) int {
    	switch o {
    	case add:
    		sum := 0
    		for _, val := range values {
    			sum += val
    		}
    		return sum
    	case mul:
    		product := 1
    		for _, val := range values {
    			product *= val
    		}
    		return product
    	}
    
    	return 0
    }
    
    const (
    	add operation = iota
    	mul
    )
    
    var allOperationSymbols = []string{"+", "*"}
    
    func operationFromSymbol(sym string) operation {
    	switch sym {
    	case "+":
    		return add
    	case "*":
    		return mul
    	default:
    		panic(fmt.Sprintf("wtf is a %s?", sym))
    	}
    }
    
    type problems struct {
    	values     [][]int
    	operations []operation
    }
    
    func (p *problems) feed(column string) {
    	last := string(column[len(column)-1])
    	done := false
    
    	if slices.Contains(allOperationSymbols, last) {
    		p.operations = append(p.operations, operationFromSymbol(last))
    		column = column[:len(column)-1]
    		done = true
    	}
    
    	val, _ := strconv.Atoi(strings.TrimSpace(column))
    	idx := len(p.values) - 1
    	p.values[idx] = append(p.values[idx], val)
    
    	if done {
    		p.values = append(p.values, []int{})
    	}
    }
    
    func (p *problems) addLine(line string) (done bool) {
    	parts := strings.Split(line, " ")
    	parts = slices.DeleteFunc(parts, func(elem string) bool {
    		return elem == ""
    	})
    
    	if slices.Contains(allOperationSymbols, parts[0]) {
    		p.operations = make([]operation, len(parts))
    		for idx, sym := range parts {
    			p.operations[idx] = operationFromSymbol(sym)
    		}
    		done = true
    	} else {
    		if len(p.values) == 0 {
    			lenparts := len(parts)
    			p.values = make([][]int, lenparts)
    			for idx := range lenparts {
    				p.values[idx] = []int{}
    			}
    		}
    
    		for idx, part := range parts {
    			num, _ := strconv.Atoi(part)
    			p.values[idx] = append(p.values[idx], num)
    		}
    		done = false
    	}
    
    	return done
    }
    
    func (p problems) solve() []int {
    	solutions := make([]int, len(p.values))
    
    	for idx, values := range p.values {
    		op := p.operations[idx]
    		solutions[idx] = op.compute(values)
    	}
    
    	return solutions
    }
    
    func stepOne(input chan string) (int, error) {
    	modernProblems := problems{}
    	for line := range input {
    		done := modernProblems.addLine(line)
    		if done {
    			break
    		}
    	}
    
    	modernSolutions := modernProblems.solve()
    	sum := 0
    	for _, solution := range modernSolutions {
    		sum += solution
    	}
    
    	return sum, nil
    }
    
    func transposeInputChan(input chan string) []string {
    	lines := [][]rune{}
    	for line := range input {
    		lines = append(lines, []rune(line))
    	}
    
    	linecount := len(lines)
    	columncount := len(lines[0])
    	transposed := make([][]rune, columncount)
    
    	for idx := range transposed {
    		transposed[idx] = make([]rune, linecount)
    	}
    
    	for row, line := range lines {
    		for col, char := range line {
    			transposed[col][row] = char
    		}
    	}
    
    	columns := make([]string, len(transposed))
    	for idx, col := range transposed {
    		columns[idx] = string(col)
    	}
    
    	return columns
    }
    
    func stepTwo(input chan string) (int, error) {
    	transposedInput := transposeInputChan(input)
    	slices.Reverse(transposedInput)
    
    	// problem-set with one empty problem.
    	modernProblems := problems{
    		values: [][]int{[]int{}},
    	}
    
    	for _, column := range transposedInput {
    		if matched, _ := regexp.MatchString("^\\s*$", column); matched {
    			continue
    		}
    
    		modernProblems.feed(column)
    	}
    
    	// Remove last useless empty problem.
    	modernProblems.values = modernProblems.values[:len(modernProblems.values)-1]
    
    	modernSolutions := modernProblems.solve()
    	sum := 0
    	for _, solution := range modernSolutions {
    		sum += solution
    	}
    
    	return sum, nil
    }
    
    func main() {
    	inputFile := utils.FilePath("day06.txt")
    	utils.RunStep(utils.ONE, inputFile, stepOne)
    	utils.RunStep(utils.TWO, inputFile, stepTwo)
    }
    

  • source
  • [–] 2 points 11 months ago* (last edited 11 months ago)

    Nim

    The hardest part was reading the part 2 description. I literally looked at it for minutes trying to understand where the problem numbers come from and how they're related to the example input. But then it clicked.

    The next roadblock was that my template was stripping whitespace at the end of the last line, making parsing a lot harder. I've replaced strip() with strip(chars={'\n'}) to keep the trailing space intact.

    Runtime: 1.4 ms 618 ΞΌs

    view code

    type
      AOCSolution[T,U] = tuple[part1: T, part2: U]
    
    proc solve(input: string): AOCSolution[int, int] =
      let lines = input.splitLines()
      let numbers = lines[0..^2]
      let ops = lines[^1]
    
      block p1:
        let numbers = numbers.mapIt(it.splitWhiteSpace().mapIt(parseInt it))
        let ops = ops.splitWhitespace()
        for x in 0 .. numbers[0].high:
          var res = numbers[0][x]
          for y in 1 .. numbers.high:
            case ops[x]
            of "*": res *= numbers[y][x]
            of "+": res += numbers[y][x]
          result.part1 += res
    
      block p2:
        var problems: seq[(char, Slice[int])]
        var ind = 0
        while ind < ops.len:
          let len = ops.skipWhile({' '}, ind+1)
          problems.add (ops[ind], ind .. ind + len - (if ind+len < ops.high: 1 else: 0))
          ind += len + 1
    
        for (op, cols) in problems:
          var res = 0
          for x in cols:
            var num = ""
            for y in 0 .. numbers.high:
              num &= numbers[y][x]
    
            if res == 0:
              res = parseInt num.strip
            else:
              case op
              of '*': res *= parseInt num.strip
              of '+': res += parseInt num.strip
              else: discard
    
          result.part2 += res
    

    Full solution at Codeberg: solution.nim

  • source
  • [–] 5 points 11 months ago (4 children)

    Haskell

    There's probably a really clever way of abstracting just the difference between the two layouts.

    import Data.Char (isSpace)  
    import Data.List (transpose)  
    import Data.List.Split (splitWhen)  
    
    op '+' = sum  
    op '*' = product  
    
    part1 =  
      sum  
        . map ((op . head . last) <*> (map read . init))  
        . (transpose . map words . lines)  
    
    part2 =  
      sum  
        . map ((op . last . last) <*> map (read . init))  
        . (splitWhen (all isSpace) . reverse . transpose . lines)  
    
    main = do  
      input <- readFile "input06"  
      print $ part1 input  
      print $ part2 input  
    
  • source
  • [–] 2 points 11 months ago*

    Python

    For part1, regex is king. Thought about rotating the grid for part2, but going column by column is simple enough.

    view code

    import re
    from operator import add, mul
    
    def part1(data: str):
        # split into row, but do not split into cells yet
        rows = data.splitlines()
        m = len(rows)   # number of rows
    
        # initialize the result and operator arrays
        # the operators array will store the operator function for each column block
        # the result array will store the initial value for each column block
        operators = []
        res = []
        # using regex to skip variable number of spaces
        for symbol in re.findall(r'\S', rows[-1]):
            if symbol == '+':
                operators.append(add)
                res.append(0)
            elif symbol == '*':
                operators.append(mul)
                res.append(1)
    
        n = len(res)    # number of columns
        # iterate through each row, except the last one
        for i in range(m-1):
            # use regex to find all numbers in the row
            for j, num in enumerate(map(int, re.findall(r'\d+', rows[i]))):
                # apply the operator to update the result for the appropriate column
                res[j] = operators[j](res[j], num)
    
        return sum(res)
    
    def part2(data: str):
        # completely split into grid
        grid = [list(line) for line in data.splitlines()]
        m, n = len(grid), len(grid[0])
        
        res = 0
        
        curr = None     # current value of the block
        op = None       # operator for the block
        for j in range(n):
            if curr is None:
                # we just started a new block
                # update the current value and operator based on the symbol
                symbol = grid[-1][j]
                curr = 0 if symbol == '+' else 1
                op = add if symbol == '+' else mul
            
            # read the number from the column
            num = 0
            for i in range(m-1):
                if grid[i][j] != ' ':
                    num = num * 10 + int(grid[i][j])
            # if there is no number, we are at the end of a block
            if num == 0:
                # add the block value to the result
                #   and reset the current value and operator
                res += curr
                curr = None
                op = None
                continue
            
            # otherwise, update the current value using the operator
            curr = op(curr, num)
        # finally, don't forget to add the last block value that's being tracked
        res += curr
    
        return res
    
    sample = """123 328  51 64 
     45 64  387 23 
      6 98  215 314
    *   +   *   +  """
    assert part1(sample) == 4277556
    assert part2(sample) == 3263827
    

  • source
  • [–] [S] 2 points 11 months ago* (last edited 11 months ago) (2 children)

    Rust

    Pt1 easy, pt2 cry

    edit: Updated with more iterring :)

    view code

        #[test]
        fn test_y2025_day6_part1() {
            let input = include_str!("../../input/2025/day_6.txt");
            let lines = input.lines().collect::<Vec<&str>>();
            let nr = lines[0..4]
                .iter()
                .map(|l| {
                    l.split_whitespace()
                        .map(|s| s.parse::<usize>().unwrap())
                        .collect::<Vec<usize>>()
                })
                .collect::<Vec<Vec<usize>>>();
            let operations = lines[4].split_whitespace().collect::<Vec<&str>>();
    
            let total = operations
                .iter()
                .enumerate()
                .map(|(i, op)| match *op {
                    "*" => [0, 1, 2, 3].iter().map(|j| nr[*j][i]).product::<usize>(),
                    "+" => [0, 1, 2, 3].iter().map(|j| nr[*j][i]).sum::<usize>(),
                    _ => panic!("Unknown operation {}", op),
                })
                .sum::<usize>();
            assert_eq!(4412382293768, total);
            println!("Total: {}", total);
        }
    
        #[test]
        fn test_y2025_day6_part2() {
            let input = std::fs::read_to_string("input/2025/day_6.txt").unwrap();
            let lines = input
                .lines()
                .map(|s| s.chars().collect::<Vec<char>>())
                .collect::<Vec<Vec<char>>>();
            let mut i = lines[0].len();
    
            let mut numbers = vec![];
            let mut total = 0;
            while i > 0 {
                i -= 1;
                let number = [0, 1, 2, 3]
                    .iter()
                    .filter_map(|j| lines[*j][i].to_digit(10))
                    .fold(0, |acc, x| acc * 10 + x);
                if number == 0 {
                    continue;
                }
                numbers.push(number as usize);
                match lines[4][i] {
                    '*' => {
                        total += numbers.iter().product::<usize>();
                        numbers.clear();
                    }
                    '+' => {
                        total += numbers.iter().sum::<usize>();
                        numbers.clear();
                    }
                    ' ' => {}
                    _ => panic!("Unknown operation {}", lines[4][i]),
                }
            }
            assert_eq!(7858808482092, total);
            println!("Total: {}", total);
        }
    

  • source
  • [–] 2 points 11 months ago*

    Edit: looking at your code, I had forgotten about .indices. That would have made this a little easier to write.

    I completely forgot to do the puzzle yesterday somehow. I struggled a bit on this one for a while because I'd used a <= 4 where I should have used a < 4. Just a complete brainfart of thinking, "It needs to be 4 or less". I wasted more time on that than I'd like to admit.

    My first stab at this set all of the adjacency counts to 0, and that lead to a few rolls that had no rolls adjacent to them staying on the map by accident.

    const val toiletPaperRoll = '@'
    const val adjacentLimit = 4
    var height = 0
    var width = 0
    
    fun main() {
        val input = getInput(4)
        val map = parseInput1(input)
        height = map.size
        width = map[0].size
        val adjacencyMap = createAdjacencyMap(map)
        var anyRemoved = true
        var count = 0
        while (anyRemoved) {
            anyRemoved = false
            for (y in 0..<height) {
                for (x in 0..<width) {
                    if (adjacencyMap[y][x] in 0..<adjacentLimit) {
                        count++
                        anyRemoved = true
                        removeToiletPaperRoll(adjacencyMap, x, y)
                    }
                }
            }
        }
        println(count)
    }
    
    fun parseInput1(input: String): List<List<Char>> = input
        .lines()
        .filter { it.isNotBlank() }
        .map { it.toCharArray().toList() }
    
    fun createAdjacencyMap(map: List<List<Char>>): List<MutableList<Int>> {
        val adjacencyMap = List(height) { MutableList(width) { -1 } }
        for (y in 0..<height) {
            for (x in 0..<width) {
                if (map[y][x] != toiletPaperRoll) {
                    continue
                }
                adjacencyMap[y][x]++
                for (y2 in y - 1..y + 1) {
                    for (x2 in x - 1..x + 1) {
                        if (y == y2 && x == x2 || (y2 < 0 || x2 < 0 || y2 >= height || x2 >= width)) {
                            continue
                        }
                        if (map[y2][x2] == toiletPaperRoll) {
                            adjacencyMap[y2][x2]++
                        }
                    }
                }
            }
        }
        return adjacencyMap
    }
    
    fun removeToiletPaperRoll(adjacencyMap: List<MutableList<Int>>, x: Int, y: Int) {
        adjacencyMap[y][x] = -1
        for (y2 in y - 1..y + 1) {
            for (x2 in x - 1..x + 1) {
                if (y == y2 && x == x2 || (y2 < 0 || x2 < 0 || y2 >= height || x2 >= width)) {
                    continue
                }
                if (adjacencyMap[y2][x2] >= adjacentLimit) {
                    adjacencyMap[y2][x2]--
                }
            }
        }
    }
    
  • source
  • parent
  • context
  • view more: β€Ή prev next β€Ί