Advent Of Code 2016 Day 1 Part 1: No Time for a Taxicab
Definitions:
In [1]:
#!/usr/bin/env python3
'''First, a very straightforward readable solution.
Coordinates are in tuples.'''
from math import sin, cos, radians
deflection = { # Unit of values is one degree clockwise.
'R': +90,
'L': -90,
}
NORTH = 0
def manhattan_distance(*points):
return sum(
abs(coordinate1 - coordinate2)
for coordinate1, coordinate2 in zip(*points))
def follow_route(
moves=None, # iterable of [RL]<distance> strings
# Unit of distance is 1 block.
starting_course=None, # Unit is 1 degree clockwise. 0 is North.
starting_location=(0., 0.), # (east, north) Unit is 1 block for each coordinate.
):
course = starting_course
location = starting_location
for turn_code, *distance_chars in moves:
course += deflection[turn_code]
distance = float(''.join(distance_chars))
location = tuple(
coordinate + distance * func(radians(course))
for coordinate, func in zip(location, (sin, cos)))
return location
def process_route(raw_instructions):
instructions = (s.strip() for s in raw_instructions.split(','))
starting_location = (0, 0)
destination = follow_route(
moves=instructions,
starting_course=NORTH,
starting_location=starting_location,
)
distance = round(manhattan_distance(destination, starting_location))
print('Easter Bunny HQ is {distance} blocks away.'.format(
distance=distance))
def main():
raw_routes = (
('20161202-aoc-day1part1-test', 'R5, L5, R5, R3'),
# from https://github.com/hakim89/adventofcode/raw/master/day01/part-01.py
('20161202-aoc-day1part1-long',
'''
R4, R5, L5, L5, L3, R2, R3, R2, L1, R5, R1, L5, R2, L2, L4, R3,
L1, R4, L5, R4, R3, L5, L3, R4, R2, L5, L5, R2, R3, R5, R4, R2,
R1, L1, L5, L2, L3, L4, L5, L4, L5, L1, R3, R4, R5, R3, L5, L4,
L3, L1, L4, R2, R5, R5, R4, L2, L4, R3, R1, L2, R5, L5, R1, R1,
L1, L5, L5, L2, L1, R5, R2, L4, L1, R4, R3, L3, R1, R5, L1, L4,
R2, L3, R5, R3, R1, L3'''),
)
for filename, raw_instructions in raw_routes:
with open(filename, 'w') as f:
f.write(raw_instructions)
for filename, _ in raw_routes:
with open(filename) as f:
raw_instructions = f.read()
process_route(raw_instructions)
if __name__ == '__main__':
main()
In [2]:
#!/usr/bin/env python3
'Use vector math with numpy.'
from math import sin, cos, radians
from numpy import array
deflection = { # Unit of values is one degree clockwise.
'R': +90,
'L': -90,
}
NORTH = 0
def manhattan_distance(vector):
return sum(map(abs, vector))
def follow_route(
moves=None, # iterable of [RL]<distance> strings
# Unit of distance is 1 block.
starting_course=None, # Unit is 1 degree clockwise. 0 is North.
starting_location=array([0., 0.]), # [east, north] Unit is 1 block for each coordinate.
):
course = starting_course
location = starting_location.copy()
for turn_code, *distance_chars in moves:
course += deflection[turn_code]
distance = float(''.join(distance_chars))
v = array([func(radians(course)) for func in (sin, cos)])
location += distance * v
return location
def process_route(raw_instructions):
instructions = (s.strip() for s in raw_instructions.split(','))
starting_location=array([0., 0.])
destination = follow_route(
moves=instructions,
starting_course=NORTH,
starting_location=starting_location,
)
distance = round(manhattan_distance(destination - starting_location))
print('Easter Bunny HQ is {distance} blocks away.'.format(
distance=distance))
def main():
route_filenames = (
'20161202-aoc-day1part1-test',
'20161202-aoc-day1part1-long',
)
for filename in route_filenames:
with open(filename) as f:
raw_instructions = f.read()
process_route(raw_instructions)
if __name__ == '__main__':
main()
In [3]:
import cmath
from math import radians
In [4]:
cmath.rect(10., radians(30))
Out[4]:
In [5]:
#!/usr/bin/env python3
'''Use complex numbers to simplify code.
The location calculation is particularly nice.'''
from math import radians
import cmath
deflection = { # Unit of values is one degree clockwise.
'R': +90,
'L': -90,
}
NORTH = 0
def manhattan_distance(vector):
return sum(map(abs, (vector.real, vector.imag)))
def follow_route(
moves=None, # iterable of [RL]<distance> strings
# Unit of distance is 1 block.
starting_course=None, # Unit is 1 degree clockwise. 0 is North.
starting_location=0+0j, # north + east j Unit is 1 block.
):
course = starting_course
location = starting_location
for turn_code, *distance_chars in moves:
course += deflection[turn_code]
distance = float(''.join(distance_chars))
location += cmath.rect(distance, radians(course))
return location
def process_route(raw_instructions):
instructions = (s.strip() for s in raw_instructions.split(','))
starting_location=0.+0.j
destination = follow_route(
moves=instructions,
starting_course=NORTH,
starting_location=starting_location,
)
distance = round(manhattan_distance(destination - starting_location))
print('Easter Bunny HQ is {distance} blocks away.'.format(
distance=distance))
def main():
route_filenames = (
'20161202-aoc-day1part1-test',
'20161202-aoc-day1part1-long',
)
for filename in route_filenames:
with open(filename) as f:
raw_instructions = f.read()
process_route(raw_instructions)
if __name__ == '__main__':
main()
In [6]:
#!/usr/bin/env python3
'''Use functional approach to obfuscate the code.
Look ma, no for loops.'''
from math import radians
import cmath
from itertools import accumulate, starmap, chain, islice
NORTH = 0
def manhattan_distance(vector):
return sum(map(abs, (vector.real, vector.imag)))
def deflection(move):
deflection_of_turn = { # Unit of values is one degree clockwise.
'R': +90,
'L': -90,
}
turn_code = move[:1]
return deflection_of_turn[turn_code]
def distance(move):
distance = move[1:]
return float(distance)
def follow_route(
moves=None, # sequence of [RL]<distance> strings
# Unit of distance is 1 block.
starting_course=None, # Unit is 1 degree clockwise. 0 is North.
starting_location=0+0j, # north + east j Unit is 1 block.
):
distances = map(distance, moves)
deflections = map(deflection, moves)
courses = islice(accumulate(chain(
[starting_course], deflections)), 1, None)
courses = map(radians, courses)
legs = starmap(cmath.rect, zip(distances, courses))
return starting_location + sum(legs)
def process_route(raw_instructions):
instructions = tuple(map(
lambda s: s.strip(),
raw_instructions.split(',')))
starting_location=0+0j
destination = follow_route(
moves=instructions,
starting_course=NORTH,
starting_location=starting_location,
)
distance = round(manhattan_distance(destination - starting_location))
print('Easter Bunny HQ is {distance} blocks away.'.format(
distance=distance))
def main():
route_filenames = (
'20161202-aoc-day1part1-test',
'20161202-aoc-day1part1-long',
)
list(map(
process_route,
map(lambda f: f.read(), map(open, route_filenames))))
if __name__ == '__main__':
main()
In [7]:
#!/usr/bin/env python3
'''Continue functional approach to obfuscate the code.
This time with no temporary variables in follow_route().'''
from math import radians
import cmath
from itertools import accumulate, starmap, chain, islice
NORTH = 0
def manhattan_distance(vector):
return sum(map(abs, (vector.real, vector.imag)))
def deflection(move):
deflection_of_turn = { # Unit of values is one degree clockwise.
'R': +90,
'L': -90,
}
turn_code = move[:1]
return deflection_of_turn[turn_code]
def distance(move):
distance = move[1:]
return float(distance)
def follow_route(
moves=None, # sequence of [RL]<distance> strings
# Unit of distance is 1 block.
starting_course=None, # Unit is 1 degree clockwise. 0 is North.
starting_location=0+0j, # north + east j Unit is 1 block.
):
return starting_location + sum(
starmap(
cmath.rect,
zip(
map(distance, moves),
map(radians, islice(
accumulate(chain(
[starting_course], map(deflection, moves))),
1,
None)))))
def process_route(raw_instructions):
instructions = tuple(map(
lambda s: s.strip(),
raw_instructions.split(',')))
starting_location=0+0j
destination = follow_route(
moves=instructions,
starting_course=NORTH,
starting_location=starting_location,
)
distance = round(manhattan_distance(destination - starting_location))
print('Easter Bunny HQ is {distance} blocks away.'.format(
distance=distance))
def main():
route_filenames = (
'20161202-aoc-day1part1-test',
'20161202-aoc-day1part1-long',
)
list(map(
process_route,
map(lambda f: f.read(), map(open, route_filenames))))
if __name__ == '__main__':
main()
In [8]:
#!/usr/bin/env python3
'Use lambdas for more obfuscation.'
from math import radians
import cmath
from itertools import accumulate, starmap, chain, islice
NORTH = 0
def manhattan_distance(vector):
return sum(map(abs, (vector.real, vector.imag)))
deflection = dict(map(
lambda x: (x[0][0], x[1]),
zip(
('RIGHT', 'LEFT'),
(+90, -90) # Unit of values is one degree clockwise.
)
))
def follow_route(
moves=None, # sequence of [RL]<distance> strings
# Unit of distance is 1 block.
starting_course=None, # Unit is 1 degree clockwise. 0 is North.
starting_location=0+0j, # north + east j Unit is 1 block.
):
return starting_location + sum(starmap(
cmath.rect,
zip(
map(lambda move: float(''.join(move[1:])), moves),
map(
radians,
islice(
accumulate(chain(
[starting_course],
map(lambda move: deflection[move[:1]], moves)
)),
1,
None)))))
def process_route(raw_instructions):
instructions = tuple(map(
lambda s: s.strip(),
raw_instructions.split(',')))
starting_location=0.+0.j
destination = follow_route(
moves=instructions,
starting_course=NORTH,
starting_location=starting_location,
)
distance = round(manhattan_distance(destination - starting_location))
print('Easter Bunny HQ is {distance} blocks away.'.format(
distance=distance))
def main():
route_filenames = (
'20161202-aoc-day1part1-test',
'20161202-aoc-day1part1-long',
)
list(map(
process_route,
map(lambda f: f.read(), map(open, route_filenames))))
if __name__ == '__main__':
main()
Back to trying to write clean code. Explores using namedtuple and classes based on namedtuples and complex numbers.
In [9]:
#!/usr/bin/env python3
'Use namedtuple.'
from math import sin, cos, radians
from collections import namedtuple
deflection = { # Unit of values is one degree clockwise.
'R': +90,
'L': -90,
}
NORTH = 0
Point = namedtuple('Point', ['East', 'North'])
def manhattan_distance(*points):
return sum(
abs(coordinate1 - coordinate2)
for coordinate1, coordinate2 in zip(*points))
def follow_route(
moves=None, # iterable of [RL]<distance> strings
# Unit of distance is 1 block.
starting_course=None, # Unit is 1 degree clockwise. 0 is North.
starting_location=Point(0., 0.), # Unit is 1 block for each coordinate.
):
course = starting_course
location = starting_location
for turn_code, *distance_chars in moves:
course += deflection[turn_code]
distance = float(''.join(distance_chars))
location = Point(*(
coordinate + distance * func(radians(course))
for coordinate, func in zip(location, (sin, cos))))
return location
def process_route(raw_instructions):
instructions = (s.strip() for s in raw_instructions.split(','))
starting_location = Point(0., .0)
destination = follow_route(
moves=instructions,
starting_course=NORTH,
starting_location=starting_location,
)
distance = round(manhattan_distance(destination, starting_location))
print('Easter Bunny HQ is {distance} blocks away.'.format(
distance=distance))
def main():
route_filenames = (
'20161202-aoc-day1part1-test',
'20161202-aoc-day1part1-long',
)
for filename in route_filenames:
with open(filename) as f:
raw_instructions = f.read()
process_route(raw_instructions)
if __name__ == '__main__':
main()
In [10]:
#!/usr/bin/env python3
'Use class based on namedtuple.'
from math import sin, cos, radians
from collections import namedtuple
deflection = { # Unit of values is one degree clockwise.
'R': +90,
'L': -90,
}
NORTH = 0
class Point(object):
Point = namedtuple('Point', ['North', 'East'])
def __init__(self, north, east):
self.point = self.Point(North=north, East=east)
def __len__(self):
return len(self.point)
def __getitem__(self, i):
return self.point[i]
def __str__(self):
return '{p.North}N, {p.East}E'.format(p=self.point)
def __repr__(self):
return '{p.North} North, {p.East} East'.format(p=self.point)
def __add__(self, value):
return Point(*(
c1 + c2 for c1, c2 in zip(self.point, value.point)))
def __sub__(self, value):
return Point(*(
c1 - c2 for c1, c2 in zip(self.point, value.point)))
def __round__(self, ndigits=0):
return Point(*(
round(coordinate, ndigits=ndigits)
for coordinate in self.point
))
def __abs__(self):
'manhattan distance'
return sum(map(abs, self.point))
def follow_route(
moves=None, # iterable of [RL]<distance> strings
# Unit of distance is 1 block.
starting_course=None, # Unit is 1 degree clockwise. 0 is North.
starting_location=Point(0.,0.), # Unit is 1 block for each coordinate.
):
course = starting_course
location = starting_location
for turn_code, *distance_chars in moves:
course += deflection[turn_code]
distance = float(''.join(distance_chars))
location = Point(*(
coordinate + distance * func(radians(course))
for coordinate, func in zip(location, (cos, sin))))
return location
def process_route(raw_instructions):
instructions = (s.strip() for s in raw_instructions.split(','))
starting_location = Point(0., .0)
destination = follow_route(
moves=instructions,
starting_course=NORTH,
starting_location=starting_location,
)
print('Easter Bunny HQ is {distance} blocks away.'.format(
distance=round(abs(destination - starting_location))))
def main():
route_filenames = (
'20161202-aoc-day1part1-test',
'20161202-aoc-day1part1-long',
)
for filename in route_filenames:
with open(filename) as f:
raw_instructions = f.read()
process_route(raw_instructions)
if __name__ == '__main__':
main()
In [11]:
#!/usr/bin/env python3
'Use classes based on complex numbers.'
from math import sin, cos, radians
import cmath
deflection = { # Unit of values is one degree clockwise.
'R': +90,
'L': -90,
}
NORTH = 0
class Point(object):
def __init__(self, north=None, east=None):
self.point = north + east*1j
def __str__(self):
return '{p.real}N {p.imag}E'.format(p=self.point)
def __repr__(self):
return '{p.real} North, {p.imag} East'.format(p=self.point)
def __mul__(self, value):
p = self.point * value
return Point(p.real, p.imag)
def __rmul__(self, value):
return self.__mul__(value)
def __add__(self, value):
p = self.point + value.point
return Point(p.real, p.imag)
def __sub__(self, value):
p = self.point - value.point
return Point(p.real, p.imag)
def __round__(self, ndigits=0):
return Point(
round(self.point.real, ndigits=ndigits),
round(self.point.imag, ndigits=ndigits),
)
def __abs__(self):
'manhattan distance'
return sum(map(abs, (self.point.real, self.point.imag)))
class Vector(Point):
def __init__(self, length=1., azimuth=0.):
self.point = cmath.rect(length, azimuth)
def follow_route(
moves=None, # iterable of [RL]<distance> strings
# Unit of distance is 1 block.
starting_course=None, # Unit is 1 degree clockwise. 0 is North.
starting_location=Point(0.,0.), # Unit is 1 block for each coordinate.
):
course = starting_course
location = starting_location
for turn_code, *distance_chars in moves:
course += deflection[turn_code]
distance = float(''.join(distance_chars))
location += distance * Vector(azimuth=radians(course))
# The following works also.
# location += Vector(length=distance, azimuth=radians(course))
return location
def process_route(raw_instructions):
instructions = (s.strip() for s in raw_instructions.split(','))
starting_location = Point(0., .0)
destination = follow_route(
moves=instructions,
starting_course=NORTH,
starting_location=starting_location,
)
print('Easter Bunny HQ is {distance} blocks away.'.format(
distance=round(abs(destination - starting_location))))
def main():
route_filenames = (
'20161202-aoc-day1part1-test',
'20161202-aoc-day1part1-long',
)
for filename in route_filenames:
with open(filename) as f:
raw_instructions = f.read()
process_route(raw_instructions)
if __name__ == '__main__':
main()