# Advent of Code 2017: introduction

Date:

Series

This post is part of the series Advent of Code 2017

It’s a common lament of mine that I don’t get to write a lot of code in my day-to-day job. I like the feeling of making something from nothing, and I often look for excuses to write bits of code, both at work and outside it.

Advent of Code is a daily series of programming challenges for the month of December, and is about to start its third annual incarnation. I discovered it too late to take part in any serious way last year, but I’m going to give it a try this year.

There are no restrictions on programming language (so of course some people delight in using esoteric languages like Brainf**k), but I think I’ll probably stick with Python for the most part. That said, I miss my Haskell days and I’m intrigued by new kids on the block Go and Rust, so I might end up throwing in a few of those on some of the simpler challenges.

I’d like to focus a bit more on how I solve the puzzles. They generally come in two parts, with the second part only being revealed after successful completion of the first part. With that in mind, test-driven development makes a lot of sense, because I can verify that I haven’t broken the solution to the first part in modifying to solve the second.

I may also take a literate programming approach with `org-mode` or Jupyter notebooks to document my solutions a bit more, and of course that will make it easier to publish solutions here so I’ll do that as much as I can make time for.

On that note, here are some solutions for 2016 that I’ve done recently as a warmup.

## Day 1: Python

Day 1 instructions

``````import numpy as np
import pytest as t
import sys

TURN = {
'L': np.array([[0,  1],
[-1, 0]]),
'R': np.array([[0, -1],
[1,  0]])
}
ORIGIN = np.array([0, 0])
NORTH = np.array([0, 1])

class Santa:
self.location = np.array(location)
self.visited = [(0,0)]

def execute_one(self, instruction):
start_loc = self.location.copy()
self.mark(start_loc, self.location)

def execute_many(self, instructions):
for i in instructions.split(','):
self.execute_one(i.strip())

def distance_from_start(self):
return sum(abs(self.location))

def mark(self, start, end):
for x in range(min(start, end), max(start, end)+1):
for y in range(min(start, end), max(start, end)+1):
if any((x, y) != start):
self.visited.append((x, y))

def find_first_crossing(self):
for i in range(1, len(self.visited)):
for j in range(i):
if self.visited[i] == self.visited[j]:
return self.visited[i]

def distance_to_first_crossing(self):
crossing = self.find_first_crossing()
if crossing is not None:
return abs(crossing) + abs(crossing)

def __str__(self):

def test_execute_one():
s = Santa(ORIGIN, NORTH)

s.execute_one('L1')
assert all(s.location == np.array([-1, 0]))

s.execute_one('L3')
assert all(s.location == np.array([-1, -3]))

s.execute_one('R3')
assert all(s.location == np.array([-4, -3]))

s.execute_one('R100')
assert all(s.location == np.array([-4, 97]))

def test_execute_many():
s = Santa(ORIGIN, NORTH)

s.execute_many('L1, L3, R3')
assert all(s.location == np.array([-4, -3]))

def test_distance():
assert Santa(ORIGIN, NORTH).distance_from_start() == 0
assert Santa((10, 10), NORTH).distance_from_start() == 20
assert Santa((-17, 10), NORTH).distance_from_start() == 27

def test_turn_left():
east = NORTH @ TURN['L']
south = east @ TURN['L']
west = south @ TURN['L']

assert all(east == np.array([-1, 0]))
assert all(south == np.array([0, -1]))
assert all(west == np.array([1, 0]))

def test_turn_right():
west = NORTH @ TURN['R']
south = west @ TURN['R']
east = south @ TURN['R']

assert all(east == np.array([-1, 0]))
assert all(south == np.array([0, -1]))
assert all(west == np.array([1, 0]))

if __name__ == '__main__':

santa = Santa(ORIGIN, NORTH)
santa.execute_many(instructions)

print(santa)
print('Distance from start:', santa.distance_from_start())
print('Distance to target: ', santa.distance_to_first_crossing())
``````

Day 2 instructions

``````module Main where

data Pos = Pos Int Int deriving (Show)

-- Magrittr-style pipe operator
(|>) :: a -> (a -> b) -> b
x |> f = f x

swapPos :: Pos -> Pos
swapPos (Pos x y) = Pos y x

clamp :: Int -> Int -> Int -> Int
clamp lower upper x | x < lower = lower
| x > upper = upper
| otherwise = x

clampH :: Pos -> Pos
clampH (Pos x y) = Pos x' y'
where y' = clamp 0 4 y
r = abs (2 - y')
x' = clamp r (4-r) x

clampV :: Pos -> Pos
clampV = swapPos . clampH . swapPos

buttonForPos :: Pos -> String
buttonForPos (Pos x y) = [buttons !! y !! x]
where buttons = ["  D  ",
" ABC ",
"56789",
" 234 ",
"  1  "]

decodeChar :: Pos -> Char -> Pos
decodeChar (Pos x y) 'R' = clampH \$ Pos (x+1) y
decodeChar (Pos x y) 'L' = clampH \$ Pos (x-1) y
decodeChar (Pos x y) 'U' = clampV \$ Pos x (y+1)
decodeChar (Pos x y) 'D' = clampV \$ Pos x (y-1)

decodeLine :: Pos -> String -> Pos
decodeLine p "" = p
decodeLine p (c:cs) = decodeLine (decodeChar p c) cs

makeCode :: String -> String
makeCode instructions = lines instructions          -- split into lines
|> scanl decodeLine (Pos 1 1) -- decode to positions
|> tail                       -- drop start position
|> concatMap buttonForPos     -- convert to buttons

main = do
input <- getContents
putStrLn \$ makeCode input
``````

## Webmentions

You can respond to this post, "Advent of Code 2017: introduction", by: liking, boosting or replying to a tweet or toot that mentions it; or sending a webmention from your own site to `https://erambler.co.uk/blog/advent-of-code-2017/`

Comments & reactions haven't loaded yet. You might have JavaScript disabled but that's cool 😎.