Advent of Code 2020 day 12
Advent of Code 2020 day 12. This one was really good fun - my favourite yet. Of course, as soon as I’d finished cleaning up I had a look at some of the solutions other folks had come up, and immediately felt a bit dumb. Seems I overcooked the rotation thing with all that trigonometry. Sod ‘em: I’m still very pleased with the structure and readability of my solution!
from math import radians, sin, cos
from pathlib import Path
class Point:
FACINGS = ('north', 'east', 'south', 'west')
# +-------> +x
# |
# |
# |
# +y
def move(self, direction, distance):
if direction == 'north':
self.coordinates[1] -= distance
elif direction == 'south':
self.coordinates[1] += distance
elif direction == 'east':
self.coordinates[0] += distance
elif direction == 'west':
self.coordinates[0] -= distance
def run_instruction(self, instruction):
value = int(instruction[1:])
if instruction[0] == 'N':
self.move('north', value)
elif instruction[0] == 'S':
self.move('south', value)
elif instruction[0] == 'E':
self.move('east', value)
elif instruction[0] == 'W':
self.move('west', value)
elif instruction[0] == 'L':
self.rotate(-value)
elif instruction[0] == 'R':
self.rotate(value)
elif instruction[0] == 'F':
self.move(self.facing, value)
def normalise_rotation(self, degrees):
if degrees < 0:
return degrees + 360
else:
return degrees
def manhattan_distance(self):
return sum([abs(c) for c in self.coordinates])
class Ship(Point):
def __init__(self, version=1):
self.coordinates = [0, 0]
self.facing = 'east'
def rotate(self, degrees):
self.facing = self.FACINGS[(self.FACINGS.index(self.facing) +
int(self.normalise_rotation(degrees)/90))%4]
class Waypoint(Point):
def __init__(self):
self.coordinates = [10,-1]
self.ship = Ship()
def rotate(self, degrees):
r = radians(self.normalise_rotation(degrees))
self.coordinates = [
round(cos(r) * (self.coordinates[0] - self.ship.coordinates[0]) -
sin(r) * (self.coordinates[1] - self.ship.coordinates[1]) +
self.ship.coordinates[0]),
round(sin(r) * (self.coordinates[0] - self.ship.coordinates[0]) +
cos(r) * (self.coordinates[1] - self.ship.coordinates[1]) +
self.ship.coordinates[1])
]
def distance_from_ship(self):
return [
self.coordinates[0] - self.ship.coordinates[0],
self.coordinates[1] - self.ship.coordinates[1]
]
def run_instruction(self, instruction):
if instruction[0] == 'F':
for _ in range(int(instruction[1:])):
# Store waypoint's current distance from ship
distance = self.distance_from_ship()
# Move ship to waypoint
self.ship.coordinates = self.coordinates
# Move waypoint to new relative distance
self.coordinates = [
self.ship.coordinates[0] + distance[0],
self.ship.coordinates[1] + distance[1]
]
else:
super().run_instruction(instruction)
def get_raw_input():
return (Path(__file__).parent/'day_12_input.txt').read_text()
def parse_raw_input(raw_input):
return [line.strip() for line in raw_input.strip().splitlines()]
def part_one(input):
ship = Ship()
for instruction in input:
ship.run_instruction(instruction)
return ship.manhattan_distance()
def part_two(input):
waypoint = Waypoint()
for instruction in input:
waypoint.run_instruction(instruction)
return waypoint.ship.manhattan_distance()
if __name__ == '__main__':
print(f'Part one: {part_one(parse_raw_input(get_raw_input()))}')
print(f'Part two: {part_two(parse_raw_input(get_raw_input()))}')