Published on

Advent of Code 2023: Day 4 Solution

Authors
  • avatar
    Name
    Tinker Assist
    Twitter

tailwind-nextjs-banner

Advent of Code 2023 - Day 4 - Scratchcards

Table of Contents

Part 1 Solution

We'll need to begin by reading the puzzle input

Read the Puzzle Input File

We will save our puzzle input in "day4_input.txt" and then read from that file.

file = open("day4_input.txt", "r")
input = file.read()

We will then break up the input by line:

input_list = input.split('\n')

Create Solution structure

points = 0
for card in input_list:
    # We will add to our points here
print(points)

Get Winning Numbers and Numbers You Have

To retrieve all of the winning values from our input line, we need to do the following:

  • Get all characters to the right of the :
card.split(':')[1]
  • Get all remaining characters to the left of the |
card.split(':')[1].split('|')[0]
  • Seperate values using space characters as the delimeter
card.split(':')[1].split('|')[0].split(' ')
  • Turn each value from the list into an integer
[int(i) for i in card.split(':')[1].split('|')[0].split(' ')]
  • Remove all stray spaces and null values in our list
[int(i) for i in card.split(':')[1].split('|')[0].split(' ') if i.isdigit()]

We can use the same methodology for the Numbers You Have, but instead read all values after the | by indexing 1 instead of 0.

[int(i) for i in card.split(':')[1].split('|')[1].split(' ') if i.isdigit()]

It is messy, but it works :)


So now, our code looks as follows

points = 0
for card in input_list:
    winning_nums = [int(i) for i in card.split(':')[1].split('|')[0].split(' ') if i.isdigit()]
    your_nums = [int(i) for i in card.split(':')[1].split('|')[1].split(' ') if i.isdigit()]

print(points)

Compare Your Numbers to Winning Numbers

Now we will loop through each number in the list of Your Numbers and identify those that are in the Winning Numbers. If the number at any given index is in the Winning Numbers, we will increment a variable winners

winners = 0
for your_num in your_nums:
    winners += 1 if your_num in winning_nums else 0

Count the Points:

Now, we need to add to our points variable based on how many winners were found on our card. We can do this as follows

points += 2**(winners-1) if winners > 0 else 0

And that's it!

The Code

points = 0
for card in input_list:
    winning_nums = [int(i) for i in card.split(':')[1].split('|')[0].split(' ') if i.isdigit()]
    your_nums = [int(i) for i in card.split(':')[1].split('|')[1].split(' ') if i.isdigit()]

    winners = 0
    for your_num in your_nums:
        winners += 1 if your_num in winning_nums else 0
    
    points += 2**(winners-1) if winners > 0 else 0
            
print(points)

Part 2 Solution

Create Solution Structure

We can Recycle much of the code from our part 1 solution. Let's start with the following and build from there

for card in input_list:
    winning_nums = [int(i) for i in card.split(':')[1].split('|')[0].split(' ') if i.isdigit()]
    your_nums = [int(i) for i in card.split(':')[1].split('|')[1].split(' ') if i.isdigit()]

    winners = 0
    for your_num in your_nums:
        winners += 1 if your_num in winning_nums else 0

In this part, we will need to reference the index of a given input line. We can update our input read for loop as follows to get the index

for ind_card, card in enumerate(input_list):

Additionally, let's add a list that we can use to track how many copies of each scratchcard we have at any given time. We can do this above our for loop

cards = [1]*len(input_list)

Counting Card copies

After we have collected the number of winners on a given card, we can go through each subsequent card and distribute copies. Remember, we must distribute one copy for every copy of the card we just scratched.

for i in range(0,winners):
    cards[ind_card+i+1] += cards[ind_card]

However, we must be sure not to distribute copies for indexes beyond the length of our input. Thus, we need to modify as follows

for i in range(0,winners):
    if ind_card+i+1 < len(input_list):
        cards[ind_card+i+1] += cards[ind_card]

That's it! Now we can print the sum of all of our cards. The full solution is below.

The Code

# part 2
cards = [1]*len(input_list)
for ind_card, card in enumerate(input_list):
    winning_nums = [int(i) for i in card.split(':')[1].split('|')[0].split(' ') if i.isdigit()]
    your_nums = [int(i) for i in card.split(':')[1].split('|')[1].split(' ') if i.isdigit()]

    winners = 0
    for your_num in your_nums:
        winners += 1 if your_num in winning_nums else 0
        
    for i in range(0,winners):
        if ind_card+i+1 < len(input_list):
            cards[ind_card+i+1] += cards[ind_card]

print(sum(cards))

Full code

Get the full code here.

Other Good Solution Resources