Advent Of Code Day 4: Visualize!

05 Dec, 2019
Xebia Background Header Wave

While figuring out my convoluted solution to day 3 I really needed to visualize the wire paths to get an idea of the solution. After getting a pretty picture and tweeting it, I got more likes than I expected, and a response by someone about not having time for it. That’s what this post is for: visualizing in Python is not that hard, here’s how to do it.

I’ve used Pillow, which is a fork of PIL, the Python Image Library. After the usual install rituals all you really need to get started is this code:

from PIL import Image, ImageDraw
im ='RGB', (800, 600), (0, 0, 0))
draw = ImageDraw.Draw(im)

In this code (800, 600) is the canvas size and (0, 0, 0) is the RGB code of the background (black in this case). For puzzle purposes lines and points cover 90% of your needs:

draw.line((x1, y1, x2, y2), (255, 255, 0), width=5)
draw.ellipse((x1, y1, x2, y2), fill=(255, 0, 255)

Both line and ellipse use a tuple of two points to determine what to draw. In the case of ellipse you basically define the bounding box, so (0, 0, 10, 10) gives you a circle centered at (5, 5) with a diameter of 10. Then there’s an RGB tuple for the color, and a line width. Larger values are good to publish large images and still see some lines, a width of 1 allows precise puzzle output when you’re looking for inspiration on a solution.

That’s all there is to showing your image. On a Mac this will fire up Preview with an image.

There’s one thing that you might like to do in code, which is flipping the coordinate system. This is needed because the coordinate system of the image is to have (0, 0) in the top left with the positive x-axis to the right and the positive y-axis downwards. So why not fix this?

    def flip(point):
        return point[0], im.height - point[1]

Finally you will likely have minimum and maximum values that aren’t neatly between (0, 0) and some nice positive coordinate like (800, 800). You can fix this by translating every puzzle coordinate to an image coordinate. Given that you’ve figured out your minimum and maximum values in a tuple boundaries, we can translate all coordinates to the correct position, and let’s make sure we do the rotation right away…

min_x, min_y, max_x, max_y = boundaries
im ='RGB', (abs(max_x - min_x), abs(max_y - min_y)), (0, 0, 0))
def to_image_coords(point):
        return flip((point[0] - min_x, point[1] - min_y))

And that’s it. To prevent you having to write all this yourself I’ve created a small .py file that has a convenience class that wraps the above for you. You can find it here. I just cooked it up, so I hope that it doesn’t have too many bugs… 🙂 Have fun visualizing!


Get in touch with us to learn more about the subject and related solutions

Explore related posts