day 12


it's turtle time - part a is effectively getting a turtle to walk around in https://turtleacademy.com/. the input is a sequence where each line is a letter (N/E/S/W to move in that cardinal direction, L/R to turn, and F to move in a given direction) and a number (either the number of steps to take or the number of degrees to turn.

i've imported math because there's rotation involved, so using sin/cos is going to come in handy

# get sin and cos
import math
# input
with open('12.txt', 'r') as file:
    input = file.read()
# turn the input into a list, one element is one instruction
input_list = list(input.split('\n'))
# split into letter and number
instructions = [(str[0], int(str[1:])) for str in input_list]
# 'F10' -> ('F', 10)

the rules are slightly different for each part - for a, N/E/S/W move the ship, L/R turn the ship, and F moves the ship in the direction that it's currently facing. we start off facing east at position (0,0), so we can represent the ship - which i'm calling turtle for nostalgia reasons - by three numbers (where 90 comes from the degrees clockwise from north)

turtle = [90,0,0]

next we need to implement the instructions. i went a little unconventional (not exactly intentionally..) and had turtle[1] be the y-axis (N positive, S negative) and turtle[2] be the x-axis (E positive, W negative). managed to confuse myself in part 2 but it's all worked out fine in the end. anyway, rambling over, here's moving the thing:

for inst in instructions:
    dir = inst[0] # move NESW / turn LR / move F
    val = inst[1] # steps or degrees
    # first four are directions
    if dir == 'N':
        turtle[1] += val
    elif dir == 'E':
        turtle[2] += val
    elif dir == 'S':
        turtle[1] -= val
    elif dir == 'W':
        turtle[2] -= val
    # next two are turns
    elif dir == 'L':
        turtle[0] -= val
    elif dir == 'R':
        turtle[0] += val
    # last one is walking forward
    elif dir == 'F':
        # get the direction we're facing in degrees
        deg = turtle[0]
        # might as well over engineer
        # remember math.cos/sin take radians as an argument
        # math.radians converts deg -> rad
        # math.cos takes the cosine
        # int rounds to the nearest integer bc rounding point errors
        # (so this version only works properly with right angles)
        turtle[1] += val * int(math.cos(math.radians(deg)))
        turtle[2] += val * int(math.sin(math.radians(deg)))

we have the answer after this loop finishes - all we need to do is calculate the manhattan distance from the ship's start position to its end position. the generic way of this metric from (a,b) to (c,d) is |a - c| + |b - d|, so going from (0,0) to (x,y) is just |x| + |y|.

# from (0,0) so we just need final position
md = abs(turtle[1]) + abs(turtle[2])
print('manhattan distance = ' + str(md))

for part b, we take in the input and turn it into a list of tuples such as ('F', 10) as before. this time though, we have a waypoint (position given relative to the ship), and the instructions mean slightly different things: for N/E/S/W, we move this waypoint; for L/R, we rotate the waypoint this many degrees about the ship; and for F, we move the ship to the waypoint that many times (e.g. if the waypoint is 2 North and 5 East from the ship, and we get the instruction F 2, then we move the ship 4 North and 10 East).

we don't need the way the ship's facing anymore, so i just implemented the ship and waypoint as two lists so that they're mutable (n.b. the waypoint starts at 1 North and 10 East from the ship):

ship = [0,0]
waypoint = [1,10]

moving the waypoint (N/E/S/W) is easy, and for F we just need to move the ship to the waypoint that many times, so the main concern is rotating the waypoint around the ship. for this, i just used rotation matrices (https://en.wikipedia.org/wiki/Rotation_matrix) by implementing them as maps:

for inst in instructions:
    dir = inst[0] # move NESW / turn LR / move F
    val = inst[1] # steps or degrees
    # first four are directions
    if dir == 'N':
        waypoint[0] += val
    elif dir == 'E':
        waypoint[1] += val
    elif dir == 'S':
        waypoint[0] -= val
    elif dir == 'W':
        waypoint[1] -= val
    # next two are rotating the waypoint around the ship
    # first coord (a) is north/south, second (b) is west/east
    # c and s are cos and sin of the angle respectively
    elif dir == 'L':
        # a -> ac + bs, b -> -as + bc
        c = int(math.cos(math.radians(val)))
        s = int(math.sin(math.radians(val)))
        a = waypoint[0]
        b = waypoint[1]
        waypoint[0] = a*c + b*s
        waypoint[1] = -a*s + b*c
    elif dir == 'R':
        # a -> ac - bs, b -> as + bc
        c = int(math.cos(math.radians(val)))
        s = int(math.sin(math.radians(val)))
        a = waypoint[0]
        b = waypoint[1]
        waypoint[0] = a*c - b*s
        waypoint[1] = a*s + b*c
    # last one is moving in the direction of the waypoint
    elif dir == 'F':
        ship[0] += val * waypoint[0]
        ship[1] += val * waypoint[1]

then this just ends off as before:

md = abs(ship[0]) + abs(ship[1])
print('manhattan distance = ' + str(md))

just a small note: although these rotations technically work for any angle, and not just multiples of right angles, the only angles that can come up are multiples of right angles. as such, every time i've taken the cos/sin of an angle i've rounded it to the nearest integer to get rid of floating point errors. to generalise this to move in any direction, you'd have to remove every int() around a math.cos or math.sin

Files

12a.py 1.9 kB
Dec 12, 2020
12b.py 1.9 kB
Dec 12, 2020

Get aoc 2020

Leave a comment

Log in with itch.io to leave a comment.