Advertisement
Guest User

Untitled

a guest
Dec 6th, 2021
875
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.68 KB | None | 0 0
  1. # aoc202104.py
  2.  
  3. import pathlib
  4. import sys
  5. from typing import Any, TypeVar
  6.  
  7. # Type annotation alias
  8. T = TypeVar('tuple[list[int], list[list[list[int]]]]')
  9.  
  10.  
  11. def intify(list_of_strings: list[str]) -> list[int]:
  12.     """ Convert a list of strings into one of ints """
  13.     return [int(c) for c in list_of_strings]
  14.  
  15.  
  16. def format_board(board: list[str]) -> list[int]:
  17.     """ Format each board from the input into int equivalents """
  18.     formatted_board = []
  19.     for row in board.splitlines():
  20.         row = row.replace('  ', ' ')
  21.         formatted_board.append(row.split())
  22.     return [intify(board) for board in formatted_board]
  23.  
  24.  
  25. def parse(puzzle_input: str) -> T:
  26.     """ Parse input """
  27.     data = puzzle_input.split('\n\n')
  28.     nums, boards = data[0].split(','), data[1:]
  29.     return (intify(nums), [format_board(board) for board in boards])
  30.  
  31.  
  32. def invert_board(board: list[list[int]]) -> list[list[int]]:
  33.     """ Switch rows and columns of board """
  34.     return [list(tupp) for tupp in list(zip(*board))]
  35.  
  36.  
  37. def winning_turns(nums: list[int], seq: list) -> int:
  38.     """ Return num of times it takes to match all nums in a sequence """
  39.     counter, turns = 0, 0
  40.     for num in nums:
  41.         turns += 1
  42.         if num in seq:
  43.             counter += 1
  44.             if counter == 5:
  45.                 return turns
  46.  
  47.  
  48. def get_scores(board: list[list[int]], nums: list[int]) -> list[int]:
  49.     """ Return a list of winning turns for a board """
  50.     return [winning_turns(nums, seq) for seq in board]
  51.  
  52.  
  53. def scores(board: list[list[int]], nums: list[int]) -> list[int]:
  54.     """ Calculate scores for board and return all values as a list """
  55.     results = []
  56.     by_row = get_scores(board, nums)
  57.     by_col = get_scores(invert_board(board), nums)
  58.     for row, col in zip(by_row, by_col):
  59.         results.append(min(row, col))
  60.     return results
  61.  
  62.  
  63. def board_scores(boards: T, nums: list[int]) -> dict[int, list[int]]:
  64.     """ Get all the quickest scores for each board """
  65.     return {
  66.         board_no: min(scores(board, nums))
  67.         for board_no, board in enumerate(boards)
  68.     }
  69.  
  70.  
  71. def flatten(t: list[list[Any]]) -> list[Any]:
  72.     """ Flatten a nested list """
  73.     return [item for sublist in t for item in sublist]
  74.  
  75.  
  76. def final_score(board: list[list[int]], played_nums: list[int]) -> int:
  77.     """ Work out final score using sets """
  78.     last_num = played_nums[-1]
  79.     board_nums = set(flatten(board))
  80.     remaining_numbers = board_nums - set(played_nums)
  81.     return sum(remaining_numbers) * last_num
  82.  
  83.  
  84. def part1(data: T) -> int:
  85.     """ Solve part 1 """
  86.     nums, boards = data[0], data[1]
  87.     board_info = board_scores(boards, nums)
  88.     # Get quickest board
  89.     quickest = min(board_info, key=board_info.get)
  90.     num_turns = board_info[quickest]
  91.     return final_score(boards[quickest], nums[:num_turns])
  92.  
  93.  
  94. def part2(data: T) -> int:
  95.     """ Solve part 2 """
  96.     nums, boards = data[0], data[1]
  97.     board_info = board_scores(boards, nums)
  98.     # Get slowest board
  99.     slowest = max(board_info, key=board_info.get)
  100.     num_turns = board_info[slowest]
  101.     return final_score(boards[slowest], nums[:num_turns])
  102.  
  103.  
  104. def solve(puzzle_input: T) -> tuple[int, int]:
  105.     """ Solve the puzzle for the given input """
  106.     data = parse(puzzle_input)
  107.     solution1 = part1(data)  # Correct answer was 58412 (with my data)
  108.     solution2 = part2(data)  # Correct answer was 10030 (with my data)
  109.  
  110.     return solution1, solution2
  111.  
  112.  
  113. if __name__ == "__main__":
  114.     for path in sys.argv[1:]:
  115.         print(f"{path}:")
  116.         puzzle_input = pathlib.Path(path).read_text().strip()
  117.         solutions = solve(puzzle_input)
  118.         print('\n'.join(str(solution) for solution in solutions))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement