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

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 966,904 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 1
Jules
Jules

Posted on • Updated on

Advent of Code 2015 - Day 1

This is a series of posts showing how I solve each of the Advent of Code (AoC) challenges from 2015 to the present day, in Python. The idea is to force myself to practice coding, and to look at ways to improve. After I complete each challenge, I'll look at other people's submissions to see if there are more Pythonic ways to have solved the puzzle, and what tiny solutions golfers may have come up with.

Pythonic Code

There is a way of writing Python that uses the language in the way it is intended to be used, as opposed to what is simply syntactically correct. I came to Python from many years coding in some form or other of Basic, so a lot of my Python can simply read like translated Basic. After many years of dabbling, I still have lots to learn about iterators, for example. I love finding other Python solutions to the Advent puzzles that give me new insights into the language.

Golfing

However challenging the Advent puzzles can be, there will always be a subset of coders that wants to write the absolutely shortest code. This isn't always the most efficient code, and would often be useless in production as it tends to be pretty opaque, and therefore hard to maintain. However, it can be a joy to stumble across a single line of code that solves a puzzle you wrote twenty lines for.

Spoilers?

There are solution megathreads on Reddit for every AoC puzzle, in virtually every language you can imagine. And the puzzles I'm starting out with are almost seven years old, so I really don't think I'm spoiling anything for anyone.

Day 1

On with the puzzles then, each of which is in two parts. Solving part one unlocks part two, and you receive a star for solving each part. The aim at the end of Advent, then, is to have 50 stars. There is usually a file of test data, which serves as the input for both parts of the puzzle. This means that the first bit of coding in virtually every solution is to load the puzzle data from a file.

The file will often be a single (very long) string, which can be loaded like this:

with open('input.txt') as f:
    data = f.read()
Enter fullscreen mode Exit fullscreen mode

Alternatively, the file will contain several lines of data, which I tend to load into a list like this:

with open('input.txt') as f:
    data = [line for line in f]
Enter fullscreen mode Exit fullscreen mode

Part 1

The puzzles are generally written in the style of a story about Santa and his elves; here's how the scene is set on Day 1:

Image description

My first thought was to simply loop through the string (for char in data), but I remembered that Python's string library can count instances of one string inside another:

floor = data.count('(') - data.count(')')
Enter fullscreen mode Exit fullscreen mode

And that was the end of Part 1, which in fairness had been billed as a simple puzzle! When I looked at other solutions for more Pythonic approaches, the main alternative was to use the replace() function of strings to replace '(' with '+1' and ')' with '-1' and run eval() on the resulting string:

eval(data.replace('(', '+1').data(')', '-1'))
Enter fullscreen mode Exit fullscreen mode

Part 2

The second part does much the same thing, but we need to stop when we reach the basement:

Image description

To my mind, this meant looping through the characters, exiting the loop when we hit the basement:

floor = 0
pos = 0
for char in data:
    pos += 1
    if char == '(':
        floor += 1
    else:
        floor -= 1
    if floor == -1:
        break
print(str(pos))
Enter fullscreen mode Exit fullscreen mode

This certainly worked, and ran quickly enough (<4ms) not to worry too much how much it could be improved...

In golfing terms though, this stood out to me, from a Reddit user:

list(itertools.accumulate([1 if b == '(' else -1 for _
   x in data])).index(-1) + 1
Enter fullscreen mode Exit fullscreen mode

I've not seen the accumulate() function before, but what it seems to be doing here is going through the whole string, storing the floor number in a new list as we go through the old list, then finding the index (+1) of the first time we hit -1. It's a neat one liner, but potentially a bit wasteful if we hit the basement in the first couple of steps in a long set of instructions...

Top comments (0)

Take a look at this:

Settings

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