Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- """Day 22 of Advent of Code 2020 Solution"""
- from collections import deque
- from operator import itemgetter
- def hand_io(file_location) -> tuple[deque[int], deque[int]]:
- with open(file_location, "r") as f:
- data = f.read().split('\n\n')
- p1 = [int(i) for i in data[0].split('\n')[1:]]
- p2 = [int(i) for i in data[1].split('\n')[1:]]
- return p1, p2
- class CrabCombat:
- def __init__(self, starting_hands):
- self.hands = {f"p{i}": deque(hand) for i, hand in enumerate(starting_hands, 1)}
- self.winner = None
- self.score = None
- def __str__(self):
- output = f""
- for k, v in self.hands.items():
- output += f"{k}:\t{v}\n"
- return output
- def __repr__(self):
- return f"{self.__class__.__name__}(starting_hands={tuple([t for t in self.hands.values()])})"
- def __iter__(self):
- return self
- def __next__(self):
- if not self.winner:
- draw = self._draw_cards()
- turn_winner = self._evaluate_turn_winner(draw)
- self._resolve_turn(draw, turn_winner)
- self._evaluate_game_win_condition()
- def _draw_cards(self):
- return {k: self.hands[k].popleft() for k in self.hands}
- def _evaluate_turn_winner(self, draw):
- return max(draw.items(), key=itemgetter(1))[0]
- def _resolve_turn(self, draw, turn_winner):
- if turn_winner == 'p1':
- self.hands['p1'].append(draw['p1'])
- self.hands['p1'].append(draw['p2'])
- else:
- self.hands['p2'].append(draw['p2'])
- self.hands['p2'].append(draw['p1'])
- def _evaluate_game_win_condition(self):
- if self.hands['p1'] and not self.hands['p2']:
- self.winner = 'p1'
- if self.hands['p2'] and not self.hands['p1']:
- self.winner = 'p2'
- def _score(self):
- score = 0
- for idx, card in enumerate(self.hands[self.winner],
- start=-len(self.hands[self.winner])):
- score += abs(idx) * card
- self.score = score
- def play(self):
- while not self.winner:
- next(self)
- self._score()
- return self.winner
- class RecursiveCombat(CrabCombat):
- def __init__(self, starting_hands):
- super().__init__(starting_hands)
- self.played = set()
- @property
- def _hands_tuple(self):
- return tuple([tuple(hand) for hand in self.hands.values()])
- def _evaluate_turn_winner(self, draw):
- if all([len(self.hands[k]) >= draw[k] for k in self.hands]):
- return self.recurse([tuple(self.hands[k])[:draw[k]] for k in draw])
- else:
- return super()._evaluate_turn_winner(draw)
- def __next__(self):
- if self._hands_tuple in self.played:
- self.winner = 'p1'
- else:
- self.played.add(self._hands_tuple)
- if not self.winner:
- draw = self._draw_cards()
- turn_winner = self._evaluate_turn_winner(draw)
- self._resolve_turn(draw, turn_winner)
- self._evaluate_game_win_condition()
- @classmethod
- def recurse(cls, starting_hands):
- recursive_game = cls(starting_hands)
- recursive_game.play()
- return recursive_game.winner
- def part_a(file_location):
- hands = hand_io(file_location)
- cc = CrabCombat(hands)
- cc.play()
- return cc.score
- def part_b(file_location):
- hands = hand_io(file_location)
- rc = RecursiveCombat(hands)
- rc.play()
- return rc.score
- if __name__ == '__main__':
- file_location = r"data/day22.txt"
- print(part_a(file_location))
- print(part_b(file_location))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement