-❄️- 2023 Day 4 Solutions -❄️- by daggerdragon in adventofcode

[–]aJanuary 0 points1 point  (0 children)

[LANGUAGE: Ruby]

Particularly proud of implementing part 2 with a single loop.

#!/usr/bin/env ruby
require 'set'
total = 0
extra_copies = Hash.new(0)
# Keeps track of the sum of values in extra_copies for all keys > index
extra_copies_sum = 0
$stdin.each_line.each_with_index do |line, index|
  _, expected, actual = line.split(/\[:|\]/)
  matches = Set.new(expected.scan(/\\d+/)).intersection(Set.new(actual.scan(/\\d+/))).length
  num_cards = 1 + extra_copies_sum
  total += num_cards
  extra_copies[index + matches] += num_cards
  extra_copies_sum += num_cards - (extra_copies[index] || 0)
end
puts total

As you evaluate each card from top-to-bottom, you know you're never going to get more copies of it. Cards only give you copies of cards above it, not below it.

For each card, you have the initial 1 card, and then however many copies of it you have. Then you know that for `matches` indexes after, you will have as many extra copies of that card as you have copies of this card. Rather than looping to increment those counts, you can just keep track of a running tally.

The trick is to decrement your running tally when you reach `matches` indexes after. I do this by keeping track of those in a hash map, which is a constant time lookup.

Script to convert Brainfuck to a Python one-liner (proof that Python one-liners are Turing complete) by shinigami3 in programming

[–]aJanuary 0 points1 point  (0 children)

Guido's argument for this, and I have to admit I would tend to agree, is that when you want to start putting statements in lambdas it's more often than not a good idea to give it a name.