zerosleeps

Since 2010

Advent of Code 2020 day 22

Advent of Code 2020 day 22. I’ve skipped day 21 for the moment just so I could complete day 22 on day 22 (see my post about day 20 for more on that fiasco).

This one was great fun! Part one was very simple, which had me properly dreading part two but it wasn’t as bad as I feared. It took me a few attempts to understand the “if both players have at least as many cards remaining in their deck…” rule, but the example made it pretty clear.

There’s a bit of unnecessary repetition in this solution, and I can see how using OOP to model games/rounds/players would have helped me with this one, but this works so here I am.

from pathlib import Path

def get_raw_input():
    return (Path(__file__).parent/'day_22_input.txt').read_text()

def parse_raw_input(raw_input):
    return {
        int(section.splitlines()[0].strip(':').split()[-1]):
        [ int(line) for line in section.splitlines()[1:] ]
        for section in raw_input.strip().split('\n\n')
    }

def play_round(players):
    if players[1][0] > players[2][0]:
        players[1].append(players[1].pop(0))
        players[1].append(players[2].pop(0))
        return players, 1
    else:
        players[2].append(players[2].pop(0))
        players[2].append(players[1].pop(0))
        return players, 2

def part_one(input):
    players = input

    while all([len(hand) for hand in players.values()]):
        players, winner = play_round(players)

    if len(players[1]) > 0:
        winner = 1
    else:
        winner =2

    return sum([ (i + 1) * card for i, card in enumerate(reversed(players[winner])) ])

def part_two(input, recursive=False):
    players = input
    previous_rounds = []

    while all([len(hand) for hand in players.values()]):
        if players in previous_rounds:
            return 1
        else:
            previous_rounds.append({player: list(cards) for player, cards in players.items()})

        cards_drawn = {player: cards[0] for player, cards in players.items()}

        if all([ len(players[player]) > cards_drawn[player] for player in players]):
            winner = part_two({player: [card for card in cards[1:(players[player][0]+1)]] for player, cards in players.items()}, True)
            if winner == 1:
                players[1].append(players[1].pop(0))
                players[1].append(players[2].pop(0))
            else:
                players[2].append(players[2].pop(0))
                players[2].append(players[1].pop(0))
        else:
            players, winner = play_round(players)

    if len(players[1]) > 0:
        winner = 1
    else:
        winner = 2

    if recursive:
        return winner
    else:
        return sum([ (i + 1) * card for i, card in enumerate(reversed(players[winner])) ])

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()))}')