Advent of Code 2022 day 22

Part 2 was incredibly painful. Maybe hand-coding if statements isn’t the most sensible idea.

It appears it wasn’t just me who found it painful:

Part 1

def get_card(data):
    return data.splitlines()[:-2]

def clean_card(card):
    max_col = max(len(row) for row in card)
    return [
        extend_line(row, max_col) for row in card
    ]

def extend_line(line, col):
    return line + (" "*(col-len(line)))

with open("22.txt") as f:
    data = f.read()

card = clean_card(get_card(data))
instructions = data.splitlines()[-1]

def get_instructions(instructions):
    instruction = ""
    for x in instructions:
        if x == "R" or x == "L":
            yield instruction
            instruction = ""
        instruction += x
    yield instruction

def change_direction(instruction, direction):
    rotate = instruction[0]
    if rotate == "R":
        return (int(instruction[1:]), (direction+1)%4)
    elif rotate == "L":
        return (int(instruction[1:]), (direction-1)%4)
    else:
        return (int(instruction), direction)

def get_answer(card, instructions):
    def valid(to, direction):
        row, col = to
        if row < 0:
            return valid((len(card)-1, col), direction)
        elif row == len(card):
            return valid((0, col), direction)
        elif col < 0:
            return valid((row, len(card[row])-1), direction)
        elif col >= len(card[row]):
            return valid((row, 0), direction)
        elif card[row][col] == "#":
            return False
        elif card[row][col] == ".":
            return to
        elif card[row][col] == " ":
            return next_move(to, direction)

    def next_move(start, direction):
        row, col = start
        if direction == 0:
            to = (row, col+1)
        elif direction == 1:
            to = (row+1, col)
        elif direction == 2:
            to = (row, col-1)
        elif direction == 3:
            to = (row-1, col)
        return valid(to, direction)

    def go(start, distance, direction):
        if distance == 0:
            return start
        if to := next_move(start, direction):
            return go(to, distance-1, direction)
        elif to is False:
            return start

    position = next_move((0, 0), 0)
    direction = 0
    for instruction in get_instructions(instructions):
        distance, direction = change_direction(instruction, direction)
        position = go(position, distance, direction)
    row, col = position
    return (row+1)*1000 + (col+1)*4 + direction

print(get_answer(card, instructions))

Part 2

# Not going to bother generalising
def get_face(row, col):
    if row < 50:
        if col < 100:
            return 0
        elif col < 150:
            return 1
    elif row < 100:
        return 2
    elif row < 150:
        # yes, I know, this is the wrong way round
        # I messed up.
        if col < 50:
            return 4
        return 3
    return 5

def get_cube(data):
    lines = data.splitlines()[:-2]
    cube = dict()
    for row, line in enumerate(lines):
        for col, char in enumerate(line):
            if char != " ":
                cube[(get_face(row, col), row%50, col%50)] = char
    return cube

def face_to_value(position, direction):
    face, row, col = position
    if face == 0:
        col += 50
    elif face == 1:
        col += 100
    elif face == 2:
        row += 50
        col += 50
    elif face == 3:
        row += 100
        col += 50
    elif face == 4:
        row += 100
    elif face == 5:
        row += 150
    return (row+1)*1000 + (col+1)*4 + direction % 4

with open("22.txt") as f:
    data = f.read()

cube = get_cube(data)
instructions = data.splitlines()[-1]

def get_instructions(instructions):
    instruction = ""
    for x in instructions:
        if x == "R" or x == "L":
            yield instruction
            instruction = ""
        instruction += x
    yield instruction

def change_direction(instruction, direction):
    rotate = instruction[0]
    if rotate == "R":
        return (int(instruction[1:]), (direction+1)%4)
    elif rotate == "L":
        return (int(instruction[1:]), (direction-1)%4)
    else:
        return (int(instruction), direction)

def get_answer(card, instructions):
    def next_move(start, direction):
        face, row, col = start
        if direction % 4 == 0:
            to = (face, row, col+1)
        elif direction % 4 == 1:
            to = (face, row+1, col)
        elif direction % 4 == 2:
            to = (face, row, col-1)
        elif direction % 4 == 3:
            to = (face, row-1, col)

        face, row, col = to
        new_direction = direction
        if row < 0:
            if face == 0:
                to = (5, col, 0)
                new_direction = direction+1
            elif face == 1:
                to = (5, 49, col)
            elif face == 2:
                to = (0, 49, col)
            elif face == 3:
                to = (2, 49, col)
            elif face == 4:
                to = (2, col, 0)
                new_direction = direction+1
            elif face == 5:
                to = (4, 49, col)
        elif row >= 50:
            if face == 0:
                to = (2, 0, col)
            elif face == 1:
                to = (2, col, 49)
                new_direction = direction+1
            elif face == 2:
                to = (3, 0, col)
            elif face == 3:
                to = (5, col, 49)
                new_direction = direction+1
            elif face == 4:
                to = (5, 0, col)
            elif face == 5:
                to = (1, 0, col)
        elif col < 0:
            if face == 0:
                to = (4, 49-row, 0)
                new_direction = direction+2
            elif face == 1:
                to = (0, row, 49)
            elif face == 2:
                to = (4, 0, row)
                new_direction = direction-1
            elif face == 3:
                to = (4, row, 49)
            elif face == 4:
                to = (0, 49-row, 0)
                new_direction = direction+2
            elif face == 5:
                to = (0, 0, row)
                new_direction = direction-1
        elif col >= 50:
            if face == 0:
                to = (1, row, 0)
            elif face == 1:
                to = (3, 49-row, 49)
                new_direction = direction+2
            elif face == 2:
                to = (1, 49, row)
                new_direction = direction-1
            elif face == 3:
                to = (1, 49-row, 49)
                new_direction = direction+2
            elif face == 4:
                to = (3, row, 0)
            elif face == 5:
                to = (3, 49, row)
                new_direction = direction-1

        if card[to] == "#":
            return direction % 4, False
        elif card[to] == ".":
            return new_direction % 4, to

    def go(start, distance, direction):
        if distance == 0:
            return direction, start
        new_direction, to = next_move(start, direction)
        if to:
            return go(to, distance-1, new_direction)
        elif to is False:
            return new_direction, start

    position = (0, 0, 0)
    direction = 0
    for instruction in get_instructions(instructions):
        distance, direction = change_direction(instruction, direction)
        direction, position = go(position, distance, direction)

    return face_to_value(position, direction)

print(get_answer(cube, instructions))