I've learned Python basics about two years ago and did a few personal projects, one big one that turned out to be useful in my line of work, a few small programs for my hobbies and I've also began working on this huge personal project that is currently in a state of crisis as it needs a complete reworking to fully support all the functionality I want it to be capable of. So I've written quite a lot of code, but because I am completely self-taught and rarely showed my code to professionals, I am still very uncomfortable with the end results. Currently I am doing adventofcode.com and so far so good (currently at puzzle 12). Some tasks were very easy, and others, while completed, have resulted in some weird solutions by me. I would be very grateful if someone commented on some of the weirder and/or messier ones. Here's one example with my personal input and solution.
Basically, the input (listed both below and in the link to GitHub) lists draws in its first line and then lists bingo boards (of shape 5 x 5) separated by blank lines. Each line within the board contains numbers delimited with one or two whitespaces (for alignment). As the numbers are being drawn in succession from the first line, the bingo boards cross them out. The board that has at least one column or one row with crossed numbers only wins. The task is to find the first and the last winning bingo board in the given input, calculate the sum of uncrossed numbers in each board and multiply the sum by the respective draw that made that board the winner. The result are two scores, one for the first winning board and one for the last one.
Here's the verbose description of the problem
Bingo is played on a set of boards each consisting of a 5x5 grid of numbers. Numbers are chosen at random, and the chosen number is marked on all boards on which it appears. (Numbers may not appear on all boards.) If all numbers in any row or any column of a board are marked, that board wins. (Diagonals don't count.)
The submarine has a bingo subsystem to help passengers (currently, you and the giant squid) pass the time. It automatically generates a random order in which to draw numbers and a random set of boards (your puzzle input). For example:
7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1 22 13 17 11 0 8 2 23 4 24 21 9 14 16 7 6 10 3 18 5 1 12 20 15 19 3 15 0 2 22 9 18 13 17 5 19 8 7 25 23 20 11 10 24 4 14 21 16 12 6 14 21 17 24 4 10 16 15 9 19 18 8 23 26 20 22 11 13 6 5 2 0 12 3 7 After the first five numbers are drawn (
7,4,9,5, and11), there are no winners, but the boards are marked as follows (shown here adjacent to each other to save space):
22 13 17 11 0 3 15 0 2 22 14 21 17 24 4 8 2 23 4 24 9 18 13 17 5 10 16 15 9 19 21 9 14 16 7 19 8 7 25 23 18 8 23 26 20 6 10 3 18 5 20 11 10 24 4 22 11 13 6 5 1 12 20 15 19 14 21 16 12 6 2 0 12 3 7 After the next six numbers are drawn (
17,23,2,0,14, and21), thereare still no winners:
22 13 17 11 0 3 15 0 2 22 14 21 17 24 4 8 2 23 4 24 9 18 13 17 5 10 16 15 9 19 21 9 14 16 7 19 8 7 25 23 18 8 23 26 20 6 10 3 18 5 20 11 10 24 4 22 11 13 6 5 1 12 20 15 19 14 21 16 12 6 2 0 12 3 7 Finally,
24is drawn:
22 13 17 11 0 3 15 0 2 22 14 21 17 24 4 8 2 23 4 24 9 18 13 17 5 10 16 15 9 19 21 9 14 16 7 19 8 7 25 23 18 8 23 26 20 6 10 3 18 5 20 11 10 24 4 22 11 13 6 5 1 12 20 15 19 14 21 16 12 6 2 0 12 3 7 At this point, the third board wins because it has at least one complete row or column of marked numbers (in this case, the entire top row is marked:
14 21 17 24 4).The score of the winning board can now be calculated. Start by finding the sum of all unmarked numbers on that board; in this case, the sum is
188. Then, multiply that sum by the number that was just called when the board won,24, to get the final score,188 * 24 = 4512.To guarantee victory against the giant squid, figure out which board will win first. What will your final score be if you choose that board?
Your puzzle answer was
31424.--- Part Two ---
On the other hand, it might be wise to try a different strategy: let the giant squid win.
You aren't sure how many bingo boards a giant squid could play at once, so rather than waste time counting its arms, the safe thing to do is to figure out which board will win last and choose that one. That way, no matter which boards it picks, it will win for sure.
In the above example, the second board is the last to win, which happens after
13is eventually called and its middle column is completely marked. If you were to keep playing until this point, the second board would have a sum of unmarked numbers equal to 148 for a final score of148 * 13 = 1924.Figure out which board will win last. Once it wins, what would its final score be?
Your puzzle answer was
23042.
Here's the copy of the working solution from GitHub (Sorry for lines being longer than 79 characters).
import numpy as np class Bingo: """ A class to store bingo cards and tracking whether they win """ def __init__(self, bingo): """ Create a bingo card out of a 2D-list of shape (5, 5) :param bingo: a `list` of five `list`s of length 5 """ self.lines = np.array(bingo, dtype='object') # specifying the element type in `np.ndarray` # allows storing `str` of variable length self.columns = self.lines.T # The two attributes store the same object at a right angle, allowing for easy check of both # streaking lines or columns def check(self, x): """ Check whether the current number is in the bingo card and whether that concludes the game for the card :param x: `str` number :return: `bool`, `True` if the card wins, `False` otherwise """ if x in flatten(self.lines): # if the number is in the card for i, line in enumerate(self.lines): if x in line: self.lines[i][np.where(line == x)[0][0]] += '*' # find the number and mark it by appending an asterix to the `str` value if self.check_win(): return True # if it wins, it wins return False # otherwise it doesn't def check_win(self): """ Check whether a column wins by separately collapsing lines and columns into sets of final characters in that line/column. If that set is `{'*'}`, this can only mean that every number in that line/column has been marked and the card as a whole has won :return: `bool`, `True` if the card wins, `False` otherwise """ line_lasts = [{x[-1] for x in line} for line in self.lines] column_lasts = [{x[-1] for x in column} for column in self.columns] if {'*'} in line_lasts or {'*'} in column_lasts: return True return False def count_unmarked(self): """ To determine a score of the winning card, the sum of unmarked numbers has to be calculated. :return: `int` """ unmarked = [] for x in flatten(self.lines): if x[-1] != '*': unmarked.append(int(x)) return sum(unmarked) def flatten(x): """ Flatten an iterable (`list` or `np.ndarray`, to be precise) :param x: `list` or `np.ndarray` :return: one-dimensional `list` """ if isinstance(x, list) or isinstance(x, np.ndarray): return [flatten(item) for sublist in x for item in sublist] return x # read the input and strip the cosing blank line with open('input4.txt') as f: puzzle = f.read().rstrip() # break input into lines by `'\n'` escape character and filter out empty lines lines = [x for x in puzzle.split('\n') if x] # the first line contains the draws separated by commas. Build a list of their string values draws = lines[0].split(',') # The rest of the lines are bingo cards bingos_raw = lines[1:] # Just to test a personal hypothesis of mine that every card will for sure # First, every line is split using blank spaces. The resulting 2-dimensional list is flattened and # collapsed into a set, that is then checked against the draw set. If the draw set contains the bingo set, # every bingo card will win bingo_set = set(flatten([x.split() for x in bingos_raw])) draw_set = set(draws) print(f'Every board will win: {draw_set >= bingo_set}.') # Split the raw bingo lines into sets of 5: build a new element in the list once every five `j`s and # add 5 lines from that `j` into a single 2D element of shape (5, 5) by additionally splitting each line # using whitespaces sets = [[bingos_raw[i + j].split() for i in range(5)] for j in range(len(bingos_raw)) if j % 5 == 0] # use every element in `sets` to make a bingo card bingos = [Bingo(x) for x in sets] # initialize variables to store the first and the last winner first_winner = None last_winner = None # check numbers draw by draw for draw in draws: # Check the same number against every card. If the card has one this draw, it is deleted from the list # since there's no need to mark any mor numbers there, hence the usage of a `while` loop instead of a # `for` loop. If the card is not a winner, move to the next one. If this is the last card left, this is # the last winner (since my earlier hypothesis turned out to be true, this also breaks the outer loop). # If even the first winner has not been determined, remember him. Otherwise just discard the winner and check # the next draw i = 0 while i < len(bingos): winner = bingos[i].check(draw) if not winner: i += 1 continue if len(bingos) == 1: last_winner, last_draw = bingos[0], draw break if not first_winner: first_winner, first_draw = bingos[i], draw del bingos[i] if last_winner: break # Now that the first winner and the last winner have been determined, calculate their score. # The final check is simply my personal curiosity. print(f"First winner's score is: {first_winner.count_unmarked() * int(first_draw)}.") print(f"Last winner's score is: {last_winner.count_unmarked() * int(last_draw)}, " f"last winning draw was the last overall: {last_draw == draws[-1]}.") The input file is in the link to my GitHub (link given above). Here's the direct link to the file on GitHub. It is also presented below (it contains >600 lines):
23,30,70,61,79,49,19,37,64,48,72,34,69,53,15,74,89,38,46,36,28,32,45,2,39,58,11,62,97,40,14,87,96,94,91,92,80,99,6,31,57,98,65,10,33,63,42,17,47,66,26,22,73,27,7,0,55,8,56,29,86,25,4,12,51,60,35,50,5,75,95,44,16,93,21,3,24,52,77,76,43,41,9,84,67,71,83,88,59,68,85,82,1,18,13,78,20,90,81,54 50 98 65 14 47 0 22 3 83 46 87 93 81 84 58 40 35 28 74 48 45 99 59 37 64 85 66 90 32 88 95 6 4 74 27 1 10 70 41 92 54 36 42 9 39 60 99 31 67 16 4 44 66 10 58 33 64 93 42 46 19 63 6 83 54 60 51 76 8 30 71 49 73 7 55 17 67 52 61 98 46 5 4 51 76 73 59 74 8 33 48 96 20 26 15 55 19 86 29 43 20 75 12 67 41 89 36 65 66 92 40 19 1 0 28 99 61 85 58 50 44 72 57 35 86 69 87 27 59 33 47 34 60 93 9 71 84 46 24 96 15 91 5 61 19 57 78 55 31 8 19 10 1 81 96 27 71 2 52 56 15 22 48 82 34 64 47 42 49 51 26 72 61 12 57 71 94 40 34 26 12 80 57 38 55 4 56 11 73 49 75 60 61 9 50 91 70 23 1 90 39 86 30 73 38 6 53 58 14 36 85 12 75 88 5 0 29 41 21 15 47 66 59 54 1 99 97 50 17 60 36 13 29 80 32 49 85 75 71 15 10 79 41 61 66 68 57 55 74 98 68 33 87 89 59 96 35 76 78 55 4 63 51 10 65 58 38 22 54 9 66 18 37 60 6 43 86 50 23 77 10 42 19 61 2 40 29 20 84 0 70 59 96 80 57 76 12 39 36 6 73 43 92 37 99 36 42 10 77 87 3 57 4 20 35 18 7 46 91 11 17 98 8 53 61 22 37 89 51 9 71 6 72 87 32 13 79 86 53 98 16 2 93 48 38 63 82 66 61 69 73 90 85 54 65 9 66 28 5 63 91 50 70 59 80 95 68 92 72 67 69 88 36 43 53 36 81 66 78 90 2 25 94 82 55 34 45 1 14 37 13 4 70 48 75 67 73 32 18 91 33 93 71 48 47 8 79 69 53 82 5 31 80 45 37 67 77 41 56 97 65 46 62 42 81 67 70 59 24 88 84 11 29 52 78 4 39 12 90 2 44 3 10 75 89 30 93 22 14 8 79 60 98 99 49 23 26 86 91 38 77 45 95 66 75 81 42 85 21 3 40 37 65 20 50 12 54 0 86 52 15 56 29 39 94 66 79 14 65 26 3 4 59 60 40 47 48 19 13 85 32 44 69 90 21 35 8 1 59 56 72 71 84 18 11 96 38 23 37 79 92 20 33 94 17 1 94 42 21 82 92 60 9 32 38 71 3 37 77 18 89 16 74 76 2 83 30 28 11 70 94 3 1 71 87 6 66 19 76 28 10 86 22 62 2 67 0 31 46 27 8 33 43 92 29 35 90 8 30 27 67 60 82 68 1 5 29 93 44 34 56 65 48 37 51 57 45 63 94 77 67 80 45 57 43 37 81 25 84 82 50 8 9 64 7 29 18 52 16 14 73 28 11 76 6 5 76 67 18 16 68 47 15 29 59 46 32 40 9 84 30 17 20 22 3 35 80 38 72 88 35 44 14 89 72 75 67 56 2 3 58 41 49 12 52 92 9 22 34 88 65 39 93 61 47 38 67 33 18 60 34 50 69 31 83 29 30 9 12 95 79 2 24 54 87 46 68 48 58 42 61 87 46 26 34 74 85 9 54 38 50 29 84 40 4 49 39 33 99 53 77 59 0 42 35 86 68 23 62 5 96 92 7 4 1 50 70 12 83 46 34 63 91 56 11 76 90 71 88 95 19 18 13 3 62 42 29 57 79 85 39 64 14 28 98 99 36 91 9 63 69 66 2 17 31 51 43 49 98 94 31 64 53 54 57 3 28 10 12 2 24 99 95 35 17 76 27 48 0 41 80 62 13 38 98 32 15 16 8 96 93 43 81 99 40 20 57 37 24 3 94 17 70 14 7 52 71 49 95 84 76 38 45 59 89 1 7 27 0 98 92 64 8 50 68 13 91 26 51 2 31 45 25 1 5 50 68 77 61 53 74 20 99 38 63 76 44 15 42 51 67 87 86 12 24 49 0 70 82 9 2 24 96 74 60 68 16 40 32 20 48 6 98 11 65 94 10 54 8 95 74 41 11 33 76 2 10 44 89 23 56 45 78 60 34 15 5 26 83 71 20 72 85 75 54 15 59 93 53 8 4 10 84 44 36 17 62 24 27 98 87 54 73 13 35 9 48 52 33 7 56 80 70 74 35 53 69 75 25 27 47 91 85 62 32 93 26 89 18 52 16 73 49 55 77 42 40 54 67 73 11 10 49 35 59 12 93 37 15 69 97 41 47 39 2 75 99 21 29 26 23 75 41 10 86 71 67 66 38 99 91 92 63 40 28 69 97 42 77 60 44 53 12 84 57 72 51 31 90 37 35 89 55 73 87 46 32 45 0 58 50 81 13 18 66 38 4 40 62 22 14 48 35 76 83 13 70 26 4 1 30 22 91 93 29 69 41 74 40 63 80 65 66 72 23 23 65 33 56 38 84 41 34 21 2 4 78 27 17 11 22 53 52 32 80 24 25 42 91 99 54 51 0 23 52 92 69 10 46 7 20 35 12 37 73 19 56 26 79 32 27 74 34 5 57 75 10 24 32 7 96 54 22 78 5 23 69 65 43 20 29 85 44 92 71 41 87 73 0 48 54 92 16 36 37 42 59 4 9 44 52 14 12 6 47 57 38 70 82 0 53 81 32 35 3 17 22 62 80 30 8 28 15 42 46 79 64 32 29 75 5 0 9 90 69 41 71 85 1 6 68 89 40 31 39 32 48 64 38 28 80 98 88 14 97 6 60 52 11 55 95 34 63 81 4 80 33 14 83 68 78 69 81 59 15 72 0 74 21 75 49 6 67 73 64 8 25 87 3 45 34 97 86 1 79 49 12 63 10 59 88 30 84 74 87 67 47 26 0 57 71 40 2 76 98 15 89 23 65 44 27 87 54 38 12 43 29 18 39 94 48 0 7 57 61 70 28 60 68 50 13 34 49 67 40 88 74 99 20 26 63 69 62 24 32 35 45 96 79 1 92 7 17 76 30 95 21 75 46 74 39 7 58 23 90 61 64 37 81 82 92 36 54 9 53 17 51 33 10 27 67 35 44 22 23 28 96 1 56 29 0 12 5 50 99 70 42 8 24 25 39 53 51 89 85 50 15 94 84 27 72 26 51 3 85 63 45 1 64 44 17 80 13 88 2 12 97 91 25 18 59 14 9 67 63 6 18 26 98 50 86 74 75 56 34 48 7 99 20 64 8 53 10 15 57 6 35 13 68 24 90 19 91 71 86 95 58 10 44 98 8 41 60 1 16 29 59 43 84 48 48 56 8 74 4 66 30 77 35 90 94 0 75 49 84 5 39 11 54 87 33 58 96 22 2 5 38 57 63 65 74 58 22 8 81 45 96 78 3 11 28 42 30 39 51 87 33 34 75 14 56 34 67 70 17 7 80 10 31 85 68 59 63 74 40 13 81 99 62 6 92 84 71 37 39 85 99 74 16 10 12 21 91 2 83 4 94 38 51 36 41 97 45 65 24 50 82 92 52 35 28 65 6 13 23 7 57 86 18 67 26 85 29 22 89 99 62 94 31 96 14 17 50 56 9 98 10 63 4 8 46 21 58 89 3 27 12 11 55 16 61 38 43 33 54 53 14 99 31 25 25 70 24 40 14 75 82 58 68 41 22 71 72 93 1 47 97 6 81 45 92 42 2 76 12 31 84 30 0 85 55 70 72 45 57 78 52 67 60 22 43 32 8 44 34 14 64 91 89 18 70 19 62 16 56 84 49 41 3 20 85 5 76 95 22 63 55 37 31 72 42 17 28 65 1 85 17 57 62 48 34 29 69 52 28 90 64 54 21 38 0 50 84 44 60 93 80 75 89 83 39 84 78 12 5 29 4 35 7 85 73 25 58 27 45 22 90 91 47 74 60 96 15 24 26 13 30 82 31 43 23 71 1 51 36 40 62 25 54 86 8 83 2 47 34 33 41 27 98 24 13 25 53 50 56 77 4 41 19 22 68 70 75 9 65 30 33 60 74 80 31 83 34 79 11 11 90 38 78 73 17 16 14 37 4 80 68 21 70 92 47 26 81 67 25 10 31 23 41 22 90 62 2 50 79 77 51 8 11 13 32 29 43 88 33 39 34 89 45 23 91 9 6 68 3 62 70 89 27 87 45 65 96 80 29 1 54 90 68 16 72 50 28 95 12 21 71 81 10 60 33 14 60 44 78 6 65 87 11 8 79 21 59 35 19 26 69 67 42 27 25 36 80 10 45 71 24 80 87 56 7 61 43 38 18 52 46 41 28 48 0 74 20 34 63 3 84 42 85 9 36 64 41 7 49 91 92 13 94 88 73 98 79 0 12 76 66 86 67 9 2 85 74 5 34 8 81 7 56 28 36 13 42 29 75 12 27 85 45 9 26 25 62 41 22 79 11 95 0 24 72 76 81 67 16 96 41 94 58 7 0 79 38 27 11 61 36 56 88 39 89 63 31 75 8 62 51 5 46 28 77 97 89 86 13 87 55 73 90 57 84 44 40 49 34 25 0 58 6 21 7 56 15 41 94 42 89 16 18 74 57 79 96 35 3 14 45 20 19 80 87 85 28 69 17 27 88 54 62 65 44 93 69 13 9 85 63 43 11 47 83 57 30 20 56 71 46 49 7 77 45 24 75 39 69 48 74 44 49 64 65 25 22 46 93 88 52 27 37 50 19 35 47 54 67 44 32 71 13 57 7 38 26 98 65 46 1 21 8 55 30 62 92 27 3 69 50 99 85 11 86 6 64 34 97 47 98 7 38 9 26 68 75 92 54 58 42 13 78 37 85 28 81 16 51 82 74 15 4 86 55 0 70 88 24 50 79 63 40 21 47 39 61 49 36 89 16 13 2 37 89 19 9 82 13 84 34 58 56 10 27 92 46 4 94 44 24 52 86 55 39 23 22 99 5 65 92 8 86 77 98 79 72 28 78 16 23 3 55 48 68 95 66 30 43 50 31 15 11 45 32 70 25 59 31 47 68 77 56 23 66 78 54 88 50 55 60 58 89 83 84 99 86 97 95 53 46 1 94 87 8 80 38 77 81 17 51 47 19 69 86 50 71 5 93 61 66 36 58 0 90 58 17 29 92 67 1 8 64 99 63 22 57 19 68 78 36 93 53 2 27 48 62 39 14 8 49 22 90 54 26 4 99 27 34 78 25 11 85 28 31 42 36 53 15 64 75 60 45 35 99 84 26 53 90 61 51 98 39 86 47 37 52 80 63 67 49 35 70 11 32 45 94 73 43 91 92 74 94 32 27 56 50 33 54 67 46 35 25 10 93 97 30 90 4 57 15 69 83 39 71 68 74 81 11 44 98 60 17 73 43 40 32 38 39 61 56 97 94 70 23 2 86 91 54 19 98 93 42 88 0 16 30 32 71 89 86 81 76 68 29 2 14 72 63 7 27 67 59 1 24 18 28 98 95 10 62 80 71 36 3 89 20 63 46 47 65 84 22 6 82 19 81 38 45 54 85 67 34 79 25 58 38 73 61 72 98 4 19 40 32 10 29 31 89 15 33 5 7 63 49 48 71 81 88 70 5 39 41 22 19 20 7 75 23 69 46 63 14 54 80 45 94 6 55 88 62 76 78 95 64 65 36 58 22 7 21 98 93 42 79 99 9 89 10 6 5 33 92 72