Day 4: Scratchcards


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)
  • Code block support is not fully rolled out yet but likely will be in the middle of the event. Try to share solutions as both code blocks and using something such as https://topaz.github.io/paste/ or pastebin (code blocks to future proof it for when 0.19 comes out and since code blocks currently function in some apps and some instances as well if they are running a 0.19 beta)

FAQ


🔒This post will be unlocked when there is a decent amount of submissions on the leaderboard to avoid cheating for top spots

🔓 Unlocked after 8 mins

1 point
*

[JavaScript] Swapped over to javascript from rust since I want to also practice some js. Managed to get part 1 in 4 minutes and got top 400 on the global leaderboard. Second part took a bit longer and took me 13 mins since I messed up by originally trying to append to the card array. (eventually swapped to keeping track of amounts in a separate array)

Part 1
// Part 1
// ======

function part1(input) {
  const lines = input.split("\n");
  let sum = 0;

  for (const line of lines) {
    const content = line.split(":")[1];
    const winningNums = content.split("|")[0].match(/\d+/g);
    const myNums = content.split("|")[1].match(/\d+/g);

    let cardSum = 0;

    for (const num of winningNums) {
      if (myNums.includes(num)) {
        if (cardSum == 0) {
          cardSum = 1;
        } else {
          cardSum = cardSum * 2;
        }
      }
    }

    sum = sum + cardSum;
  }

  return sum;
}
Part 2
// Part 2
// ======

function part2(input) {
  let lines = input.split("\n");
  let amount = Array(lines.length).fill(1);

  for (const [i, line] of lines.entries()) {
    const content = line.split(":")[1];
    const winningNums = content.split("|")[0].match(/\d+/g);
    const myNums = content.split("|")[1].match(/\d+/g);

    let cardSum = 0;

    for (const num of winningNums) {
      if (myNums.includes(num)) {
        cardSum += 1;
      }
    }

    for (let j = 1; j <= cardSum; j++) {
      if (i + j >= lines.length) {
        break;
      }
      amount[i + j] += amount[i];
    }
  }

  return lines.reduce((acc, line, i) => {
    return acc + amount[i];
  }, 0);
}

Code Link

permalink
report
reply
1 point
*

Improvement I found afterwards:

  • Could have done a reduce on the amount array instead of the lines array since I don’t use the line value at all
permalink
report
parent
reply
4 points

Haskell

11:39 – I spent most of the time reading the scoring rules and (as usual) writing a parser…

import Control.Monad
import Data.Bifunctor
import Data.List

readCard :: String -> ([Int], [Int])
readCard =
  join bimap (map read) . second tail . break (== "|") . words . tail . dropWhile (/= ':')

countShared = length . uncurry intersect

part1 = sum . map ((\n -> if n > 0 then 2 ^ (n - 1) else 0) . countShared)

part2 = sum . foldr ((\n a -> 1 + sum (take n a) : a) . countShared) []

main = do
  input <- map readCard . lines <$> readFile "input04"
  print $ part1 input
  print $ part2 input
permalink
report
reply
2 points

Still trying to make sense of it but that part two fold is just jummy!

permalink
report
parent
reply
2 points

I’m really impressed by your part 2. And I thought my solution was short…

permalink
report
parent
reply
2 points

Not familiar with Lean4 but it looks like the same approach. High five!

permalink
report
parent
reply
3 points

Ruby

!ruby@programming.dev

Somehow took way longer on the second part than the first part trying a recursive approach and then realizing that was dumb.

https://github.com/snowe2010/advent-of-code/blob/master/ruby_aoc/2023/day04/day04.rb

permalink
report
reply
1 point
*

Edit: Sorry, should have read your code first, you made it work too. if it works it works, Recursive solutions just click for me over other solutions.

I made the recursion work, went to a depth of 24 for my input set.

Recursive C#
internal class Day4Task2 : IRunnable
    {
        private Regex _regex = new Regex("Card\\s*\\d*: ([\\d\\s]{2} )*\\|( [\\d\\s]{2})*");
        private Dictionary _matchCountCache = new Dictionary();
        private int _maxDepth = 0;

        public void Run()
        {
            var inputLines = File.ReadAllLines("Days/Four/Day4Input.txt");
            int sumScore = 0;

            for (int i = 0; i < inputLines.Length; i++)
            {
                sumScore += ScoreCard(i, inputLines, 0);
                Console.WriteLine("!!!" + i + "!!!");
            }

            Console.WriteLine("Sum:"+sumScore.ToString());
            Console.WriteLine("Max Recursion Depth:"+ _maxDepth.ToString());
        }

        private int ScoreCard(int lineId, string[] inputLines, int depth)
        {
            if( depth > _maxDepth )
            {
                _maxDepth = depth;
            }

            if(lineId >= inputLines.Length)
            {
                return 0;
            }

            int matchCount = 0;

            if (!_matchCountCache.ContainsKey(lineId)) {

                var winningSet = new HashSet();
                var matches = _regex.Match(inputLines[lineId]);
                foreach (Capture capture in matches.Groups[1].Captures)
                {
                    winningSet.Add(capture.Value.Trim());
                }

                foreach (Capture capture in matches.Groups[2].Captures)
                {
                    if (winningSet.Contains(capture.Value.Trim()))
                    {
                        matchCount++;
                    }
                }

                _matchCountCache[lineId] = matchCount;
            }

            matchCount = _matchCountCache[lineId];

            int totalCards = 1;
            while(matchCount > 0)
            {
                totalCards += ScoreCard(lineId+matchCount, inputLines, depth+1);
                matchCount--;
            }
            //Console.WriteLine("Finished processing id: " + lineId + " Sum is: " + totalCards);
            return totalCards;
        }
    }
permalink
report
parent
reply
3 points

(python) Much easier than day 3.

code
import pathlib

base_dir = pathlib.Path(__file__).parent
filename = base_dir / "day4_input.txt"

with open(base_dir / filename) as f:
    lines = f.read().splitlines()

score = 0

extra_cards = [0 for _ in lines]
n_cards = [1 for _ in lines]

for i, line in enumerate(lines):
    _, numbers = line.split(":")
    winning, have = numbers.split(" | ")

    winning_numbers = {int(n) for n in winning.split()}
    have_numbers = {int(n) for n in have.split()}

    have_winning_numbers = winning_numbers & have_numbers
    n_matches = len(have_winning_numbers)

    if n_matches:
        score += 2 ** (n_matches - 1)

    j = i + 1
    for _ in range(n_matches):
        if j >= len(lines):
            break
        n_cards[j] += n_cards[i]
        j += 1

answer_p1 = score
print(f"{answer_p1=}")

answer_p2 = sum(n_cards)
print(f"{answer_p2=}")
permalink
report
reply
5 points

Nim

This one was pretty simple, just parse the numbers into sets and check the size of the intersection. Part 2 just made the scoring mechanism a little more complicated.

permalink
report
reply
1 point

Hi there! Looks like you linked to a Lemmy community using a URL instead of its name, which doesn’t work well for people on different instances. Try fixing it like this: !nim@programming.dev

permalink
report
parent
reply
1 point

That’s some elegant code! Then again, I suppose that’s the beauty of nim.

permalink
report
parent
reply
3 points

I’m rather spoiled by python, so I feel like it could be more elegant. xD

But yeah, I do like how this one turned out, and nim runs a whole lot faster than python does. I really like nim’s “method call syntax”. Instead of having methods associated with an individual type, you can just call any procedure as x.f(remaining_args) to call f with x as its first argument. Makes it easy to chain procedures. Since nim is strongly typed, it’ll know which procedure you mean to use by the signature.

permalink
report
parent
reply
3 points

Aside from the general conciseness, the “universal function call syntax” is my favorite aspect of nim.

If you want to take chaining procedures to the next level, try a concatenative language like Factor (I have a day 4 solution in this thread – with no assignment to variables).

I also suggest having a look at Roc if you want a functional programming adventure, which offers great chaining syntax, a very friendly community, and is in an exciting development phase.

permalink
report
parent
reply

Advent Of Code

!advent_of_code@programming.dev

Create post

An unofficial home for the advent of code community on programming.dev!

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.

AoC 2023

Solution Threads

M T W T F S S
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25

Rules/Guidelines

  • Follow the programming.dev instance rules
  • Keep all content related to advent of code in some way
  • If what youre posting relates to a day, put in brackets the year and then day number in front of the post title (e.g. [2023 Day 10])
  • When an event is running, keep solutions in the solution megathread to avoid the community getting spammed with posts

Relevant Communities

Relevant Links

Credits

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

console.log('Hello World')

Community stats

  • 1

    Monthly active users

  • 75

    Posts

  • 857

    Comments