this post was submitted on 06 Dec 2025
26 points (100.0% liked)

Advent Of Code

1199 readers
3 users here now

An unofficial home for the advent of code community on programming.dev! Other challenges are also welcome!

Advent of Code is an annual Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like.

Everybody Codes is another collection of programming puzzles with seasonal events.

EC 2025

AoC 2025

Solution Threads

M T W T F S S
1 2 3 4 5 6 7
8 9 10 11 12

Visualisations Megathread

Rules/Guidelines

Relevant Communities

Relevant Links

Credits

Icon base by Lorc under CC BY 3.0 with modifications to add a gradient

console.log('Hello World')

founded 2 years ago
MODERATORS
 

Day 6: Trash Compactor

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 29 comments
sorted by: hot top controversial new old
[–] Chais@sh.itjust.works 2 points 4 days ago

Managed to keep it compact, but boy, do I hate cephalopod math >_<

Python

from csv import reader
from functools import reduce
from itertools import pairwise
from operator import mul
from pathlib import Path
from typing import Any, List, Sequence


def _calc(values: List[str]) -> int:
    match values[-1]:
        case "+":
            return sum(map(int, values[:-1]))
        case "*":
            return reduce(mul, map(int, values[:-1]))
        case _:
            return 0


def _transpose(values: Sequence[Sequence[Any]]) -> List[List[Any]]:
    return [[values[row][col] for row in range(len(values))] for col in range(len(values[0]))]


def part_one(input: str) -> int:
    def _parse_input(input: str) -> List[List[str]]:
        return _transpose(list(map(lambda r: list(filter(None, r)), reader(input.splitlines(), delimiter=" "))))

    return sum(map(_calc, _parse_input(input)))


def part_two(input: str) -> int:
    def _parse_input(input: str) -> List[List[str]]:
        data = list(input.splitlines())
        columns = [t[0] for t in filter(lambda t: t[1] != " ", enumerate(data[-1]))] + [len(data[0])]
        numbers = [[line[a:b] for line in data[:-1]] for a, b in pairwise(columns)]
        numbers = [list(filter(None, ["".join(num).strip() for num in column])) for column in map(_transpose, numbers)]
        return list(map(lambda t: t[0] + [t[1]], zip(numbers, list(filter(None, data[-1].split(" "))))))

    return sum(map(_calc, _parse_input(input)))


if __name__ == "__main__":
    input = Path("_2025/_6/input").read_text("utf-8")
    print(part_one(input))
    print(part_two(input))
[–] skissue@programming.dev 6 points 2 weeks ago (3 children)

Uiua

I'm new to Uiua, so probably not the best way to express the solution!

ParseRows ← (
  βŠœβˆ˜βŠΈβ‰ @\n
  βœβ‡ŒΒ°βŠ‚ # Get the row of operations.
)
ParseOperations ← βŠœβŠ’βŠΈβ‰ @\s
SumCalculations ← (
  ≑(
    ⍣(β—‡/Γ—Β°@*
    | β—‡/+Β°@+
    )
  )
  /+
)

Part₁ ← (
  ParseRows
  βŠ“(ParseOperations
  | ⍉ ≑(βŠœβ‹•βŠΈβ‰ @\s) # Parse columns.
  )
  SumCalculations
)

Partβ‚‚ ← (
  ParseRows
  βŠ“(ParseOperations
  | ⊜(░≑(β‹•β–½βŠΈβ‰ @\s)) ≑/β†₯βŠΈβ‰ @\s⍉ # Parse numbers.
  )
  SumCalculations
)

&fras "6.txt"
βŠƒPart₁ Partβ‚‚
[–] Deebster@programming.dev 6 points 2 weeks ago (1 children)
[–] Quant@programming.dev 3 points 2 weeks ago

( Ν‘Β° ΝœΚ–β”œβ”¬β”΄β”¬β”΄

[–] mykl@lemmy.world 4 points 2 weeks ago

Looks good. I like how you managed to use the same structure for Parts 1 and 2; that's more than I did. And yours is faster than mine too.

[–] Quant@programming.dev 2 points 2 weeks ago

One of us! One of us!

Nice use of the inversion, I always forget that's a thing

[–] Deebster@programming.dev 5 points 2 weeks ago* (last edited 2 weeks 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
[–] Quant@programming.dev 4 points 2 weeks ago

Uiua

This was fun :D

I had a fun experience just throwing the strings with both numbers and spaces at the parse function. In the online pad, everything worked out fine but running the same code on my input locally gave me a "invalid float literal" error.
I thought I'd missed some edge case in the real input again, like is often the case.
Turns out that the Uiua version I used locally had a bug that's fixed in the latest build. For once it wasn't directly my fault ^^

Run with example input

Code

$ 123 328  51 64 
$  45 64  387 23 
$   6 98  215 314
$ *   +   *   +  

# &fras "input-6.txt" β—Œ

Calc ← (
  β†˜β‚‚βŠ›βŠ‚"+*"/β—‡βŠ‚
  ≑◇⨬(/+|/Γ—)
  /+
)

P₁ ← (
  ⊜(βŠœβ–‘βŠΈβ‰ @ )βŠΈβ‰ @\n
  βŠƒβŠ£β†˜β‚‹β‚
  βŠ™(⍉≑₀◇⋕)
  Calc
)

Pβ‚‚ ← (
  βŠœβˆ˜βŠΈβ‰ @\n
  ⟜⧻
  βŠ“β‰(Λœβ†―@ )
  ⊜(βŠ™(░≑⋕)βœβ‰(βŠƒ(⊒⊣)β†˜β‚‹β‚))Β¬β€šβ‰‘βŒŸβ‰
  Calc
)

1_2 [βŠƒP₁Pβ‚‚]
≑(&p &pf $"Part _: ")

[–] lwhjp@piefed.blahaj.zone 4 points 2 weeks ago (1 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  
[–] CameronDev@programming.dev 5 points 2 weeks ago (1 children)

Ulua probably has a single character that rotates the input -90 degrees...

[–] LeixB@lemmy.world 3 points 2 weeks ago

Haskell

import Control.Arrow
import Data.Char
import Data.List
import Text.ParserCombinators.ReadP

op "*" = product
op "+" = sum

part1 s = sum $ zipWith ($) (op <$> a) (transpose $ fmap read <$> as)
  where
    (a : as) = reverse . fmap words . lines $ s

parseGroups = fst . last . readP_to_S (sepBy (endBy int eol) eol) . filter (/= ' ')
  where
    eol = char '\n'
    int = read <$> munch1 isDigit :: ReadP Int

part2 s = sum $ zipWith ($) (op <$> words a) (parseGroups . unlines $ reverse <$> transpose as)
  where
    (a : as) = reverse $ lines s

main = getContents >>= print . (part1 &&& part2)
[–] ystael@beehaw.org 3 points 2 weeks ago* (last edited 2 weeks 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))))
[–] eco_game@discuss.tchncs.de 3 points 2 weeks ago (1 children)

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

[–] chunkystyles@sopuli.xyz 3 points 2 weeks 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
}
[–] GiantTree@feddit.org 2 points 1 week ago* (last edited 1 week ago)

Kotlin

More grid stuff and two-dimensional problem solving, I like it!
The first part just requires extracting the numbers and operators, transposing the grid and summing/multiplying the numbers.
The second part is also not too hard. I just search for the numbers in the transposed grid, making sure to leave out the last column. That one might contain an operator ("+" or "*"). Remember it for later. If the entire row is made of spaces, we have finished parsing a math problem. Just remember to account for the last one! πŸ˜…
As with part one, just reduce the found problems and we're done!

This solution requires the trailing spaces to be present in the input files. I had to disable an option in my IDE to prevent it from breaking my nice solution.

Code on Github

Code

class Day06 : AOCSolution {
    override val year = 2025
    override val day = 6

    override fun part1(inputFile: String): String {
        val worksheet = readResourceLines(inputFile)
        // Arrange the problem in a grid and transpose it, so that the operation is the last element of each row
        val problems = worksheet.map { line -> line.trim().split(spaceSplitRegex) }.toGrid().transposed()

        val grandTotal = problems.rows().sumOf { line ->
            // Map the all but the last element to a number
            val numbers = line.mapToLongArray(0, line.lastIndex - 1, String::toLong)
            // Extract the operation
            val operation = line.last()[0]

            // Call the correct reduction
            // The "else" branch is needed for the compiler
            when (operation) {
                ADD -> numbers.sum()
                MULTIPLY -> numbers.reduce { acc, value -> acc * value }
                else -> 0
            }
        }
        return grandTotal.toString()
    }

    override fun part2(inputFile: String): String {
        val worksheet = readResourceLines(inputFile)

        // In this part the problem is more complicated and dependent on the individual characters.
        val charGrid = worksheet.map(CharSequence::toList).toCharGrid().transposed()

        val numbers = mutableListOf<Long>()
        val sb = StringBuilder(charGrid.width)

        val problems = buildList {
            // Begin with an empty operation
            // Assume the operation will be set to a valid value
            var operation = SPACE
            for (y in 0 until charGrid.height) {
                // Extract each row (transposed column)
                sb.clear().append(charGrid[y])
                // Find the bounds of the number
                val numberOffset = sb.indexNotOf(SPACE)

                if (numberOffset != -1) {
                    // A number was found, parse it and add it to the list.
                    val endIndex = sb.indexOfAny(STOP_CHARACTERS, numberOffset + 1)
                    val number = java.lang.Long.parseLong(sb, numberOffset, endIndex, 10)
                    numbers.add(number)

                    // Check whether there is an operation in the last column.
                    // IF so, that's the next relevant operation
                    val lastColumn = sb[sb.lastIndex]
                    if (lastColumn != SPACE) {
                        operation = lastColumn
                    }
                } else {
                    // No number was found, that's the separator for two calculations.
                    // Finalize the collection and clear the numbers.
                    // `toLongArray` creates a neat copy of the Longs in the list.
                    add(Problem(operation, numbers.toLongArray()))
                    numbers.clear()
                }
            }
            // Add the last remaining problem to the list
            add(Problem(operation, numbers.toLongArray()))
        }

        // Reduce all problems to their solutions and sum them up.
        val grandTotal = problems.sumOf { problem ->
            when (problem.operation) {
                ADD -> problem.numbers.sum()
                MULTIPLY -> problem.numbers.reduce { acc, value -> acc * value }
                else -> 0
            }
        }

        return grandTotal.toString()
    }

    private companion object {
        private const val ADD = '+'
        private const val MULTIPLY = '*'
        private const val SPACE = ' '
        private val STOP_CHARACTERS = charArrayOf(SPACE, ADD, MULTIPLY)

        @JvmRecord
        @Suppress("ArrayInDataClass")
        private data class Problem(val operation: Char, val numbers: LongArray)
    }

[–] Jayjader@jlai.lu 2 points 1 week ago

(Browser-based) Javascript

I got lazy and lucky writing the first part; hard-coded the number of lines / operands and it worked on the first try! I didn't factor in the final block not being padded by spaces on both sides for part 2, and so needed to test on the example code – which has 3 lines of operands instead of the problem input's 4 lines, so I had to properly solve the problem across all possible amounts of lines of input 😩.

I am very jealous of all y'all who have a transpose function available in your language of choice.

Code

function part1(inputText) {
  const [firstArgs, secondArgs, thirdArgs, fourthArgs, operators] = inputText.trim().split('\n').map(line => line.trim().split(/\s+/));
  let totalSum = 0;
  for (let i = 0; i < firstArgs.length; i++) {
    if (operators[i] === '+') {
      totalSum += Number.parseInt(firstArgs[i], 10) + Number.parseInt(secondArgs[i], 10) + Number.parseInt(thirdArgs[i], 10) + Number.parseInt(fourthArgs[i], 10);
    } else if (operators[i] === '*') {
      totalSum += Number.parseInt(firstArgs[i], 10) * Number.parseInt(secondArgs[i], 10) * Number.parseInt(thirdArgs[i], 10) * Number.parseInt(fourthArgs[i], 10);
    }
  }
  return totalSum
}

{
  const start = performance.now();
  const result = part1(document.body.textContent);
  const end = performance.now();
  console.info({
    day: 6,
    part: 1,
    time: end - start,
    result
  })
}

function part2(inputText) {
  const lines = inputText.trimEnd().split('\n');
  const interBlockIndices = [];
  for (let i = 0; i < lines[0].length; i++) {
    let allEmpty = true;
    for (let j = 0; j < lines.length; j++) {
      if (lines[j][i] !== ' ') {
        allEmpty = false;
        break;
      }
    }
    if (allEmpty) {
      interBlockIndices.push(i);
    }
  }
  interBlockIndices.push(lines[0].length);

  let totalSum = 0;
  let blockStart = 0;
  for (const interBlockIndex of interBlockIndices) {
    // compute calculation of block [blockStart, interBlockIndex - 1]
    const operands = [];
    for (let i = interBlockIndex - 1; i >= blockStart; i--) {
      // parse operands
      let operand = 0;
      for (let j = 0; j < lines.length - 1; j++) {
        if (lines[j][i] !== ' ') {
          operand *= 10;
          operand += Number.parseInt(lines[j][i], 10);
        }
      }
      operands.push(operand)
    }
    if (lines.at(-1)[blockStart] === '+') {
      totalSum += operands.reduce((accu, next) => accu + next, 0);
    } else if (lines.at(-1)[blockStart] === '*') {
      totalSum += operands.reduce((accu, next) => accu * next, 1);
    }
    //  console.debug({ totalSum, operands, blockStart, interBlockIndex });
    blockStart = interBlockIndex + 1;
  }
  return totalSum;
}

{
  const example = `123 328  51 64 
 45 64  387 23 
  6 98  215 314
*   +   *   +  
`;
  const start = performance.now();
  const result = part2(document.body.textContent);
  //   const result = part2(example);
  const end = performance.now();
  console.info({
    day: 6,
    part: 2,
    time: end - start,
    result
  })
}

[–] Pyro@programming.dev 2 points 2 weeks ago* (last edited 2 weeks 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

[–] Gobbel2000@programming.dev 2 points 2 weeks 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!();
[–] CameronDev@programming.dev 2 points 2 weeks ago

Excel

Pt1 - Its a solution, but the column width on Numbers prevents a full solution

https://docs.google.com/spreadsheets/d/e/2PACX-1vTzFApVAk9k5Q4EQhozST1HftYLgVzApgxjQx7MAl5AAE5eWEvrQnDQWmdj7J2Hyw/pub?output=ods

If I get p2 working, I'll post a new link. Dont think i will have much luck though.

[–] CameronDev@programming.dev 2 points 2 weeks ago* (last edited 2 weeks ago) (1 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);
    }

[–] Deebster@programming.dev 2 points 2 weeks ago (1 children)

Why not use .iter().sum() and .iter().product()?

[–] CameronDev@programming.dev 2 points 2 weeks ago

Because I wasn't aware of them, thanks!

[–] tranzystorek_io@beehaw.org 2 points 2 weeks ago
[–] strlcpy@lemmy.sdf.org 2 points 2 weeks 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);
}

[–] reboot6675@sopuli.xyz 2 points 2 weeks 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)
}

[–] janAkali@lemmy.sdf.org 2 points 2 weeks ago* (last edited 2 weeks 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

[–] landreville@lemmy.world 2 points 2 weeks ago

Ruby

I decided to rotate the entire input character-by-character, then parse the numbers (see the full source here)

grid = input.lines.map(&:chomp).map {|l| l.each_char.map.to_a }.to_a
transposed = Array.new(grid[0].length) { Array.new(grid.length) }

grid.each_with_index do |row, y|
  row.each_with_index do |col, x|
    transposed[x][y] = col
  end
end

vals = []
ops = []
temp_vals = []

transposed.each do |row|
  l = row.join("").strip
  temp_vals << l.scan(/\d+/).map(&:to_i).to_a[0]
  /[+*]/.match(l) { |m| ops << m.to_s.to_sym }

  if l == ""
    vals << temp_vals.compact
    temp_vals = []
  end
end

vals << temp_vals.compact unless temp_vals.empty?

vals.each_with_index.sum do |v, i|
  v.inject(ops[i])
end
[–] urandom@lemmy.world 2 points 2 weeks ago* (last edited 2 weeks ago)

Rust

Finally having some fun with iters. I think part 2 came out nicely once I figured I can rotate the whole input to get numbers that look like ours.

Q: Anyone have any tips how to reason about the intermediate types in a long iter chain? I'm currently placing dummy maps, and on occasion, even printing the values from them when I get too stuck.

the code

use std::fs::File;
use std::io::{BufReader, Lines};

#[allow(dead_code)]
pub fn part1(input: Lines<BufReader<File>>) {
    let mut input = input
        .map_while(Result::ok)
        .map(|line| {
            line.split_ascii_whitespace()
                .map(|s| s.to_string())
                .collect::<Vec<String>>()
        })
        .collect::<Vec<Vec<String>>>();

    let ops = input.pop().unwrap();
    let values = input
        .iter()
        .map(|v| {
            v.iter()
                .map(|v| v.parse::<i64>().unwrap())
                .collect::<Vec<i64>>()
        })
        .collect::<Vec<Vec<i64>>>();

    let transposed: Vec<Vec<i64>> = (0..values[0].len())
        .map(|i| values.iter().map(|row| row[i]).collect())
        .collect();

    let mut sum = 0;
    for i in 0..ops.len() {
        let op: &str = &ops[i];
        match op {
            "+" => {
                sum += transposed[i].iter().sum::<i64>();
            }
            "*" => {
                sum += transposed[i].iter().product::<i64>();
            }
            _ => panic!("Invalid operation"),
        }
    }

    println!("sum = {}", sum)
}

#[allow(dead_code)]
pub fn part2(input: Lines<BufReader<File>>) {
    let mut input = input
        .map_while(Result::ok)
        .map(|line| line.chars().collect::<Vec<char>>())
        .collect::<Vec<Vec<char>>>();

    let ops = input
        .pop()
        .unwrap()
        .iter()
        .map(|c| c.to_string())
        .filter(|s| s != " ")
        .collect::<Vec<String>>();

    let transposed: Vec<i64> = (0..input[0].len())
        .map(|i| input.iter().map(|row| row[i]).collect())
        .map(|vec: String| vec.trim().to_string())
        .map(|s| {
            if s.len() == 0 {
                0
            } else {
                s.parse::<i64>().unwrap()
            }
        })
        .collect();

    let groups = transposed
        .into_iter()
        .fold(Vec::new(), |mut acc: Vec<Vec<i64>>, num| {
            if num == 0 {
                if let Some(last) = acc.last_mut() {
                    if !last.is_empty() {
                        acc.push(Vec::new());
                    }
                } else {
                    acc.push(Vec::new());
                }
            } else {
                if acc.is_empty() {
                    acc.push(Vec::new());
                }

                acc.last_mut().unwrap().push(num);
            }

            acc
        });

    let mut sum = 0;
    for i in 0..ops.len() {
        let op: &str = &ops[i];
        match op {
            "+" => {
                sum += groups[i].iter().sum::<i64>();
            }
            "*" => {
                sum += groups[i].iter().product::<i64>();
            }
            _ => panic!("Invalid operation"),
        }
    }

    println!("sum = {}", sum)
}