Advent Of Code

Python solutions to the daily coding puzzles, explained

Advent of Code is an Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like. People use them as a speed contest, interview prep, company training, university coursework, practice problems, or to challenge each other.

I first participated in Advent of Code in 2018. I enjoyed working on the challenges and I learned a lot. The goal of the advent of code is to try to get on the leaderboard which is the fastest people to complete the problem when it is released at midnight.

Last year my goal was to complete all the problems. This year I will focus on writing clean code and writing more about my experience.

You can find all of these solutions on Github: lazydancer/Advent-of-Code-2019

Table of Contents

Day 1: The Tyranny of the Rocket Equation

For Part 1, we simply need to divide each module weight by 3 and substact 2. The only thing in my code that is unusally is the '//' which is an integer divide. It acts as a round down function.

For Part 2, we need to account for the extra weight of the fuel. This is done with a while loop where it looks for values which would add more fuel. When the fuel get less than 8.9999... the result will be 0.

The tyranny of the rocket equation relates to the ideal rocket equation. Which is interesting to derive. We have the follow the rule of conservation of momentum where the momentum in a closed system needs to remain constant.

All this simplifies to... mdv = v_exhaust * dm

This makes sense the change in the momentum of the ship is the change in mass of the ship times the exhaust speed

Rearranging and integrating both sides with respect to time and knowing 1/x integrates to ln(x) and ln(a) - ln(b) = ln(a/b). Finds the ideal rocket equation.

v_final - v_inital = v-exhaust * ln(m_inital / m_final)

Day 2: 1202 Program Alarm

For part 1, we need a run program which would set the pointer every 4 numbers. This is done with a simple for loop but not robust if another size of jump is needed.

For part 2, we iterate over the program to find when a result is a desired number. The inputs are changed till the result is given. With only 100 possible values for the two. Leads to only 10,000 combinations and since order matters we look at the permutations using the builtin itertools module.

Day 3: Crossed Wires

This one was done in Rust

For Part 1, we can find the path in which each wire takes, creating a vector of locations. Finding where the two wires interset is looking at where both wire end at a location. We can use a set data type to find the union, in this case a HashSet finding an intersection.

For Part 2, we look for the shortest manhattan distance along the path of the wires.

Day 4: Secure Container

Part 1, we can find adjancent cells by taking a copy, shifting it by one and comparing against the original.

Part 2, we can use a BTreeMap and count all of the numbers given finding if any are equal to 2.

Day 5: Sunny with a Chance of Asteroids

Back to python

Part 1, Using the old code from day 2, we had to introduce a new functions like input and ouput. The INPUT variable I made a global constant

Part 2, even more functions were added and we needed to separate the pointer from the for loop and introduced more functions

Day 6: Universal Orbit Map

For part 1, we can set up a map which looks at what planet it orbits. For each planet going down the chain to the base. Finding the base is done by taking the differnce between two sets. The 'guide' is a hashmap for really quick lookup values

For part 2, we can find the path of YOU and SAN back to the base element and the paths will be common until a divergent step. Add up the remain elements that aren't common to find the paths between them.

Day 7: Amplification Circuit

For part 1, we can use the itertools library and use the permutations function to get the result of the max

For part 2, I didn't get it done in the time I had. I think you need to keep the state of the program. I will come back to this, I will try to create a stateful ampifier class

Day 8: Space Image Format

For part 1, we can split the transmission into different chunks and count the layer with the fews 0 by using the min and count function

For part 2, we just send to a function which returns 1 or 0 the first layer it sees them

Day 9: Sensor Boost

After debugging for a while, I have left this one where it is. I hope to come back to this challenge

Day 10: Monitoring Station

For part one I selected an asteroid to check if it had the most lines of sight by creating an expanding box from the asteroid and using any asteroids as casting shadows on the ones behind it


[Advent of Code]( is an annual programming competition to complete 25 challenges in December. It's a way grow as a programmer, through solving problems and reveiwing others code. In this article I will cover two things I learned about this year and try to convince you to join next years competition.

1. Assembly Language

I have always be interested in how computers work, *see* [building a transistor from scratch]( I have never had the chance to program in assembly. That changed on [Day 16](, the challenge was to run a new programming language called elf code. Elf code looked almost identical to assembly.

4 0 2 0
13 2 0 2
2 2 3 2
4 3 2 3
3 3 2 2
13 2 1 2
6 2 1 1
10 1 2 3
4 2 3 0
13 1 0 1
2 1 1 1

The first number is the opcode, which could represent 1 of 16 operations, 'add', 'mul', 'mov'. The second and third were inputs and the forth was what register to use for the output.

First, I wrote the code in python. It turned out way too slow, even with pypy and a better data structure. I got it down to 1/10th of the original but after 3 hours, I stopped it.

To solve it, you need to watch how the elf code is being executed, find the pattern and simplify the logic. I was curious if this could be completed by running right on the hardware.

Using an intermeddiate step to C, Below is a snippet of the assembly. ';' is a comment. The 'l01', 'l02', 'l03' and 'l07' are labels. For example the first command is jmp l17, which is jump to label l17.

      jmp       l17           ;  L00: goto *jump_table[0+16+1]; //addi 3 16 3
l01:  mov       r11, 1        ;  L01: reg[1] = 1; //seti 1 2 1
l02:  mov       r12, 1        ;  L02: reg[2] = 1; //seti 1 1 2
l03:  mov       rax, r11      ;  L03: reg[5] = reg[1] * reg[2]; //mulr 1 2 5
      mov       rbx, r12      ;
      imul      rax, rbx      ;
      mov       r15, rax      ;
      cmp       r15, r14      ;  L04: reg[5] = reg[5] == reg[4]; //eqrr 5 4 5
      sete      al            ;  Grab the flag
      movzx     r15, al       ;  Move the flag to r15 extends with zeros
      je        l07           ;  L05: goto *jump_table[reg[5] + 5 + 1]; //addr 5 3 3
      jmp       l08           ;  L06: goto *jump_table[6 + 1 + 1]; //addi 3 1 3
l07:  add       r10, r11      ;  L07: reg[0] = reg[1] + reg[0]; //addr 1 0 0

In the end the assembly was not fast enough, even being roughly 20x faster than my original python implementation.

Along the way I learned x86-64 commands, registers, flags and jump commands. I also learned why assembly is not written. This code ran about as fast as the C code that I made but not being portable between different computers and being somewhat hard to follow. Below is the code to write a single character to the screen.

      mov       [value], r10     
      mov       rax, 1           ; system call for write
      mov       rdi, 1           ; file handle 1 is stdout
      mov       rsi, value       ; address of string to output
      mov       rdx, 8           ; number of bytes
      syscall                    ; invoke operating system to do the write
      mov       rax, 60          ; system call for exit
      xor       rdi, rdi         ; exit code 0
      syscall                    ; invoke operating system to exit 

All the code can be found on my [github](

2. Python Imports

Most of the time I avoid python imports, I found the default data types do the job. During these challenges, I have come to love defaultdict, copy, dequeue. Also networkx for building networks and re for regular expressions

defaultdict - Having a set value for when nothing exist is a very useful feature

somedict = {}
print(somedict[3]) # KeyError

someddict = defaultdict(int)
print(someddict[3]) # print int(), thus 0

copy - Because python calls list by reference this is useful to keep things functional

import copy

dequeue - A double-ended queue, this is useful for when rotation is needed with a rotate function

d = deque(xrange(10))
**networkx** - This was useful for all types of graph equations. Shortest path was used many times
graph = networkx.Graph()
# ...adding nodes...
result = networkx.shortest_path(graph)

re - This is a very popular library I have tried to avoid it.

Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.

list(map(int, re.findall(r'-?\d+', line)))

During the challenge, I read as much as I coded. Reading other peoples code was enlightening. You saw how they approached the problem and what trade-offs they chose.

Thank you Eric Wastl for creating advent of code and everybody that shared their solutions online. I learned a lot.