How to solve Advent of Code 2021 - Day 3 [Python]

How to solve Advent of Code 2021 - Day 3 [Python]

Confused? Check out my Day 1 post to read all about Advent of Code here: https://galaxyinferno.com/how-to-solve-advent-of-code-2021-day-1/

GitHub

https://github.com/GalaxyInfernoCodes/Advent_Of_Code_2021

This is where I will upload all of my Python Notebooks where I solve the challenges. I’m solving them on Google Colab, because that allows me to just quickly throw something together in the Browser on any machine I’m on. Since I’m lazy, that’s my go-to for quick and small projects. Otherwise I code in VSCode :)

Day 3 Puzzle

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

Today's puzzle deals with binary numbers and the input looks something like this:

00100
11110
10110
10111
10101
01111

Part 1

The first task was to create two new binary numbers - called the gamma and epsilon rate - from the given input lines.

For the gamma rate, you take the most common bit at each position in the strings, while for the epsilon rate you take the least common - so they are always the opposite of each other.

The first step is of course to read in the lines from a .txt file. Here I give file_name as a variable, so the code can be used for the example input that is given in the text as well as the ''real'' input I need to solve to get the stars.

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

We can then loop through all the lines in the text file at once, while creating the two new numbers. To avoid any confusions around binary numbers, we just keep them as strings and work with them char by char. The most common char is then appended to the string representing the gamma rate and the least common to the epsilon rate string. The counting is done by first creating a list of all the bits at the current position and then using the inbuilt .count(entry) method of python lists:

gamma, epsilon = '', ''
for i in range(len(diagnostics[0])):
    all_entries_at_pos = [entry[i] for entry in diagnostics]
    if all_entries_at_pos.count('0') > len(diagnostics)/2:
        gamma += '0'
        epsilon += '1'
    else:
        gamma += '1'
        epsilon += '0'

Afterwards we can get the solution by converting the gamma and epsilon rate strings to numbers by using the int() conversion call and providing the base=2 parameter to indicate that the string is representing a binary number.

int(gamma, base=2)*int(epsilon, base=2) # the power consumption = task solution

Part 2

Next, we create another two binary numbers based on some new rules. The oxygen generator rating and Co2 scrubber rating. Both of these are found by filtering out values from our text-file/diagnostic report until only one line remains, which will be the solution.

Oxygen rating

Again, we check what the most common bit at each position is. But this time we only consider the remaining values. So first we create a copy of the diagnostic report because now we will be deleting entries from it and we need the full list for the other binary number we will create.

from copy import copy

oxygen_diagnostics = copy(diagnostics)

Then we loop through the length of the provided binary numbers again, determine the most common bit and then do a list comprehension to filter out the entries that don't have this bit. When the list is only one entry long, we can stop. At the end we convert the remaining string to a number again.

Warning: check the edge cases carefully in the text, aka what to do when both bits appear equally as often. Here this means to choose '1'. But this could a common source of errors.

for i in range(len(diagnostics[0])):
    if len(oxygen_diagnostics) == 1:
        break
    all_entries_at_pos = [entry[i] for entry in oxygen_diagnostics]
    common_bit = '1' if all_entries_at_pos.count('1') >= len(oxygen_diagnostics)/2 \
                 else '0'
    oxygen_diagnostics = [entry for entry in oxygen_diagnostics 
                                    if entry[i]==common_bit]
oxygen_rating = int(oxygen_diagnostics[0], base=2)
print('oxygen', oxygen_diagnostics[0], oxygen_rating)

Co2 rating

This one is basically the same as above, except we choose the least common bit:

co2_diagnostics = copy(diagnostics)
for i in range(len(diagnostics[0])):
    if len(co2_diagnostics) == 1:
        break
    all_entries_at_pos = [entry[i] for entry in co2_diagnostics]
    least_common_bit = '0' if all_entries_at_pos.count('1') >= len(co2_diagnostics)/2 \
                       else '1'
    co2_diagnostics = [entry for entry in co2_diagnostics
                                 if entry[i]==least_common_bit]
co2_rating = int(co2_diagnostics[0], base=2)
print('co2', co2_diagnostics[0], co2_rating)

The solution is then simply to multiply both of these:

print('life support rating', oxygen_rating*co2_rating)

Let me know how you solved this or if you have a better idea on how to approach this problem :)