DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,274 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Advent of Code 2015 - Day 3
Jules
Jules

Posted on • Updated on

Advent of Code 2015 - Day 3

I struggled to find an elegant solution to today's puzzle, but several other people did. First of all, here's Part 1:

Screenshot of Advent of Code puzzle

We are asked to keep track of Santa as he moves north, east, south, and west, and recording how many places he visits. Thinking ahead to Part 2, I was imagining we'd have to find which place he visited the most, but Eric Wastl had something else in mind today.

My approach was to create a dict, keyed on a tuple of Santa's x and y coordinates. I actually recorded how many times he visited each occasion because, who knows what's in Part 2?! My code to record his movements looks a bit clumsy compared to some of the more Pythonic solutions other people came up with, but it worked. It started off inline with the main code, but I eventually put it in its own function because of Part 2:

def increment_house(x, y, houses):
    if (x, y) in houses:
        houses[(x, y)] += 1
    else:
        houses[(x, y)] = 1

def calc_move(x, y, move):
    if move == '^':
        y += 1
    elif move == 'v':
        y -= 1
    elif move == '>':
        x += 1
    else:
        x -= 1
    return x, y

santaX = 0
santaY = 0
houses = {}
increment_house(santaX, santaY, houses)
for char in data:
    santaX, santaY = calc_move(santaX, santaY, char)
    increment_house(santaX, santaY, houses)
print(len(houses))
Enter fullscreen mode Exit fullscreen mode

A lot of code, but hopefully fairly readable, and it gets the job done, so we're on to Part 2!

Screenshot of Advent of Code puzzle

Instead of keeping count of Santa's visits, we're introduced to a new character! Now we're also tracking Santa's robot, which explains how generic calc_move() is in my code. Using the same two functions above, my Part 2 solution looks like this:

santaX = 0
santaY = 0
robX = 0
robY = 0
houses = {}
increment_house(santaX, santaY, houses)
increment_house(robX, robY, houses)
moveSanta = True
for char in data:
    if moveSanta:
        santaX, santaY = calc_move(santaX, santaY, char)
        increment_house(santaX, santaY, houses)
        moveSanta = False
    else:
        robX, robY = calc_move(robX, robY, char)
        increment_house(robX, robY, houses)
        moveSanta = True
print(len(houses))
Enter fullscreen mode Exit fullscreen mode

I've seen several examples use modulo division to decide who is moving, which would allow the code to track as many characters as you wanted, but a boolean worked well enough for two characters.

Getting more Pythonic!

Looking at other people's solutions in the megathread, I discovered that a dict could be used to move characters with only one line of code:

def calc_move(c, p):
    return { '>': (p[0] + 1, p[1]), '<': (p[0] - 1, p[1]), '^': (p[0], p[1] + 1), 'v': (p[0], p[1] - 1) }[c];
Enter fullscreen mode Exit fullscreen mode

In this code, the current coordinates are passed in as a tuple p, with the direction to move being the character c. This character is the key to four items in a dictionary, which are simultaneously looked up, and the coordinates updated accordingly. I found this really elegant; elegance seems to one of the keys to what coder find truly Pythonic.

Truly golfing!

I also found this Python one-liner, which I presume works (I haven't tested it), and despite staring at it and drinking numerous cups of coffee, I still can't understand what it's doing. Impressive, nevertheless!

len(set.union(*(set((tuple(map(sum, zip(*({">": (1, 0), "<": (-1, 0), "^": (0, 1), "v": (0, -1)}[v] for v in y[:z])))) for z in range(len(y)))) for y in (data[x::2] for x in [0, 1]))))
Enter fullscreen mode Exit fullscreen mode

I haven't used the set object to do anything other than ensure collections only contain one of each item*, so I can only guess what set.union() is doing here. Likewise, although I've seen zip() used in a lot of Python code, I've not yet dug into the documentation to find out what it really does.

(* Obviously, as it turned out, I didn't need to keep count of how many times each location was visited, so I could have used a set() for houses and saved a bit of time.)

Top comments (0)

Update Your DEV Experience Level:

Settings

Go to your customization settings to nudge your home feed to show content more relevant to your developer experience level. πŸ›