## 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.

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:

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))
``````

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

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))
``````

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];
``````

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]))))
``````

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.)