Skip to content

Latest commit

 

History

History

day-8

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

Solution in Python for the day 8 puzzle of the 2019 edition of the Advent of Code annual programming challenge.

🎄🌟🌟 Space Image Format 🎄🌟🌟

🔍📖 Annotated Puzzle Statement

The Elves' spirits are lifted when they realize you have an opportunity to reboot one of their Mars rovers, and so they are curious if you would spend a brief sojourn on Mars. You land your ship near the rover.

TIL a new word: sojourn 📚

sojourn (noun):
 - a temporary stay
   Middle English: from Old French sojourner, based on Latin sub- ‘under’ + late Latin diurnum ‘day’.

When you reach the rover, you discover that it's already in the process of rebooting! It's just waiting for someone to enter a BIOS password. The Elf responsible for the rover takes a picture of the password (your puzzle input) and sends it to you via the Digital Sending Network.

Have a feeling this network reliability will turn out to be questionable.

Unfortunately, images sent via the Digital Sending Network aren't encoded with any normal encoding; instead, they're encoded in a special Space Image Format. None of the Elves seem to remember why this is the case. They send you the instructions to decode it.

All long as these images are not animated.

Images are sent as a series of digits that each represent the color of a single pixel. The digits fill each row of the image left-to-right, then move downward to the next row, filling rows top-to-bottom until every pixel of the image is filled.

So nothing too fancy for the moment.

Each image actually consists of a series of identically-sized layers that are filled in this way. So, the first digit corresponds to the top-left pixel of the first layer, the second digit corresponds to the pixel to the right of that on the same layer, and so on until the last digit, which corresponds to the bottom-right pixel of the last layer.

Are we correct assuming these layers are stacked?

For example, given an image 3 pixels wide and 2 pixels tall, the image data 123456789012 corresponds to the following image layers:

Layer 1: 123
         456

Layer 2: 789
         012

Ok this appears conspicuously easy, most likely I missed something.

The image you received is 25 pixels wide and 6 pixels tall.

To make sure the image wasn't corrupted during transmission, the Elves would like you to find the layer that contains the fewest 0 digits. On that layer, what is the number of 1 digits multiplied by the number of 2 digits?

Ok sounds fun!

📃➡ Input Contents Format

Input contents are characterized with three different attributes, listed in the table below.

Attribute Type Size Description
Width Integer Several digits Image width in pixels
Height Integer Several digits Image height in pixels
Data String Up to 15'000 Image pixels

The Python programming language comes with built-in JSON codec read methods such as json.load().

{
  "width": 3,
  "height": 2,
  "data": "123456789012"
}

⚙🚀 Implementation

💾🔍 Content Decoding

With input contents being encoded in JSON decoding them is quite trivial. It is simply a mater of calling open() and passing the results to json.load().

def load_contents(filename: str) -> map:
    contents = json.load(fp=open(filename))
    return contents

💡🙋 Puzzle Solving

The answer refers to a specific layer, meaning that the input data contents must be split into a number of layers.

... the Elves would like you to find the layer that contains the fewest 0 digits.

The data length of individual layers is known from start, making this operation easy to implement.

def splice_data(data: str, width: int, height: int) -> list[str]:
    layer_length = width * height
    assert 0 == len(data) % layer_length
    layers = list()
    for i in range(0, len(data), layer_length):
        layers.append(data[i:i + layer_length])
    return layers

The following operation consists in finding the layer with the fewest number of 0 digits. The Counter is well suited for this sort of computation.

occurences = [collection.Counter(l) for l in layers]

Last action remaining is computing the index of the layer with the least quantity of zeros. And performing the final multiplication

layer_least_zeroes = occurences.index(min(occurences))
layer_least_zeroes_occurence = Counter(layers[layer_least_zeroes])
answer = layer_least_zeroes_occurence['1'] * layer_least_zeroes_occurence['2']
Contents Answer
example.txt 1
input.txt 2064

😰🙅 Part Two

🥺👉👈 Annotated Description

Now you're ready to decode the image. The image is rendered by stacking the layers and aligning the pixels with the same positions in each layer. The digits indicate the color of the corresponding pixel: 0 is black, 1 is white, and 2 is transparent.

These transparent pixels seem interesting!

The layers are rendered with the first layer in front and the last layer in back. So, if a given position has a transparent pixel in the first and second layers, a black pixel in the third layer, and a white pixel in the fourth layer, the final image would have a black pixel at that position.

No surprises here!

For example, given an image 2 pixels wide and 2 pixels tall, the image data 0222112222120000 corresponds to the following image layers:

Layer 1: 02
         22

Layer 2: 11
         22

Layer 3: 22
         12

Layer 4: 00
         00

Then, the full image can be found by determining the top visible pixel in each position:

    The top-left pixel is black because the top layer is 0.
    The top-right pixel is white because the top layer is 2 (transparent), but the second layer is 1.
    The bottom-left pixel is white because the top two layers are 2, but the third layer is 1.
    The bottom-right pixel is black because the only visible pixel in that position is 0 (from layer 4).

So, the final image looks like this:

01 10

Sounds good.

What message is produced after decoding your image?

Message huh?! Extracting one from an image may be a though part to script...

🤔🤯 Solver Implementation

The whole process operates on a per-pixel basis, thus the first thing is to convert the list of layers into a list pixels. This constitutes a textbook usage of zip().

pixels = [''.join(l) for l in list(zip(*layers))]

Next step is flattening transparent layers.

def flatten(pixel: str) -> str:
    """Flatten layers into a single value

    :param pixel: list of layers as string
    :return: color
    """
    for layer in pixel:
        if layer != '2':
            return layer

...

pixels = list(map(flatten, layers_per_pixel))

Final operation is printing the results.

pixels = [' ' if p == '0' else '#' for p in pixels]
for i in range(0, len(pixels), contents['width']):
    print(f'{"".join(pixels[i:i+contents["width"]])}')
Contents Answer
input.txt KAUZA

🚀✨ Further Improvements

The flatten() method could be improved for handling the complete image and yield on per-pixel basis.

Still, the main improvement would be to automatically get the text from the array of pixels.