How to solve Advent of Code 2023 – Day 2 with Python

How to solve Advent of Code 2023 – Day 2 with Python

If you missed any previous days or are looking for another year, click here for all my content about that: Advent of Code, if you want to know why you should participate and try to code the solution for yourself, click here: Advent Of Code 2022 – 7 Reasons why you should participate. If you’re here to see the solution of this year's Day 2, continue reading 😉

GitHub Repository

https://github.com/GalaxyInfernoCodes/Advent_Of_Code_2023

I will upload all of my solutions there in the form of Python notebooks (.ipynb files), but you can copy paste the code into normal .py files, too. I am uploading the examples from the task as ‘example.txt’ files to test my solution. You will have to use your own ‘input.txt’ with your supplied input since everyone gets their own and everyone needs to provide a different solution based on their input.

Day 2 Puzzle

In this blog post I’ll describe the puzzle and solution steps, but if you want to jump ahead to said solution, you can do so here: https://github.com/GalaxyInfernoCodes/Advent_Of_Code_2023/blob/main/Day01_Python/AdventOfCode_Day02_Python.ipynb

Here is the challenge, if you want to read the full puzzle: https://adventofcode.com/2023/day/2

In summary, on day 2 we made it to an island in the sky using the trebuchet from day 1. The island is without snow and while we walk to our destination an elf plays a game with us. He has a small bag with different number of cubes in it, some red, some green, some blue. To give us a chance to figure out how many of each color there are, he pulls out a subset each round and shows them to us then replaces them into the bag.

That's the input:

Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green

To parse the input I created arrays of shape [#red, #green, #blue], e.g. [4, 0, 3] for the first round of Game 1:

def parse_game(game_str: str):
    # game = "Game 1", content = "3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green"
    game, content = game_str.split(':')
    game_id = int(game.split(' ')[1])
    # content_rounds = ["3 blue, 4 red", "1 red, 2 green, 6 blue", "2 green"]
    rounds_of_game = content.split(';')
    cube_rounds = []
    for round in rounds_of_game:
        found_cubes = [0, 0, 0]
        # round example = "3 blue, 4 red" -> [["3", "blue"], ["4", "red"]]
        cube_groups = [cube_group.strip().split(' ') for cube_group in round.split(',')]
        for cube_group in cube_groups:
            if cube_group[1] == 'red':
                found_cubes[0] += int(cube_group[0])
            elif cube_group[1] == 'green':
                found_cubes[1] += int(cube_group[0])
            elif cube_group[1] == 'blue':
                found_cubes[2] += int(cube_group[0])
        cube_rounds.append(found_cubes)
    return game_id, cube_rounds

Part 1

For this part, the elf would first like to know which games would have been possible if the bag contained only 12 red cubes, 13 green cubes, and 14 blue cubes?

So for this we discard any game that exceeds this limit in any round:

def solve1(input_file: str) -> int:
    compare_cubes = [12, 13, 14]
    possible_games = []

    with open(input_file, 'r') as f:
        lines = f.readlines()
        games = [entry.strip() for entry in lines]

    for game in games:
        game_id, cube_rounds = parse_game(game)
        game_possible = True
        for round in cube_rounds:
            for round_color, limit_color in zip(round, compare_cubes):
                if round_color > limit_color:
                    game_possible = False
        if game_possible:
            possible_games.append(game_id)
    return sum(possible_games)

Some pointers:

  • The method of declaring something possible until we see one sign that disputes this (setting game_possible to False) is quite common to filter scenarios that depend on multiple conditions (like the multiple colors here)
  • The [zip](https://www.geeksforgeeks.org/zip-in-python/) function iterates through both lists at the same time. I would recommend getting familiar with it, it can be quite convenient to create neat for-loops.

Part 2

As we continue our walk, the Elf poses a second question: in each game you played, what is the fewest number of cubes of each color that could have been in the bag to make the game possible?

Thankfully, we only need a minimal adjustment to our above code to answer this:

def solve2(input_file: str) -> int:
    power_of_games = []

    with open(input_file, 'r') as f:
        lines = f.readlines()
        games = [entry.strip() for entry in lines]

    for game in games:
        minimum_cubes = [None, None, None]
        _, cube_rounds = parse_game(game)
        for round in cube_rounds:
            for color_index in range(3):
                if minimum_cubes[color_index] is None or round[color_index] > minimum_cubes[color_index]:
                    minimum_cubes[color_index] = round[color_index]
        power_of_games.append(minimum_cubes[0] * minimum_cubes[1] * minimum_cubes[2])
    return sum(power_of_games)
  • For each game we create a list of minimum_cubes (minimum needed to play, really it's more the maximum of observed cubes, so maybe the name is not ideal). We increase this whenever we see more than this number of red/green/blue cubes. Clearly, if you see 5 red cubes, there are at least 5 red cubes in the bag. And so on...
  • Don't forget to multiply the found values in each game and save them to power_of_games
  • And then add those numbers across games at the end.

Conclusion

Other than arrays in arrays, this was actually quite nice :) Parsing the rounds felt like half the battle.

I was a bit surprised we didn't need to calculate any probabilities but now that I'm thinking about it, probability theory hasn't been part of Advent of Code in any puzzles I've seen. Probably difficult to pose the question in a way that has a deterministic answer... Probably (ha) for the best.