Sean McLemon | Advent of Code

Home | Czech | Blog | GitHub | Advent Of Code | Notes


2021-12-04 - Giant Squid

(original .ipynb)

Day 4 puzzle input is a sequence of comma-separated numbers representing the order that bingo balls are drawn, followed by a number of bingo cards (mine is here). Part 1 involves playing through the bingo game and finding the first card to have a complete row or column. Part 2 involves finding the very last one.

puzzle_input_str = open("puzzle_input/day4.txt").read()

test_input_str = """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"""



def parse_card(block):
    card = []
    for line in block.split("\n"):
        card.append(line.split())
    return card


def parse_input(input_str):
    blocks = [block for block in input_str.split("\n\n")]
    numbers = [n for n in blocks[0].split(",")]
    cards = [parse_card(card) for card in blocks[1:]]
    return (numbers, cards)

def is_winner(card):
    # check horizontals
    for line in card:
        all_true = True
        for v in line:
            all_true = all_true and v == None
        if all_true:
            return True
        
    # check verticals - there's a neater way to do this but meh
    for c in range(len(card[0])):
        all_true = True
        for r in range(len(card[c])):
            all_true = all_true and card[r][c] == None
        if all_true:
            return True
        
    return False


def update_card(card, number):
    for r, row in enumerate(card):
        for c, col in enumerate(row):
            if col == number:
                card[r][c] = None
                return


def score(card, drawn_number):
    card_total = 0
    for row in card:
        for col in row:
            if col != None:
                card_total += int(col)
                
    return int(drawn_number) * card_total
    

def nth_bingo_score(numbers, cards, n):
    winners = set()
    for number in numbers:
        for i, card in enumerate(cards):
            if i in winners:
                continue
            update_card(card, number)
            if is_winner(card):
                winners.add(i)
                if n == len(winners):
                    return score(card, number)
    
    raise Exception("No winners")
    
    
def part_one(input_str):
    numbers, cards = parse_input(input_str)
    return nth_bingo_score(numbers, cards, 1)


assert 4512 == part_one(test_input_str)
print("part one:", part_one(puzzle_input_str))
part one: 71708
def part_two(input_str):
    numbers, cards = parse_input(input_str)
    return nth_bingo_score(numbers, cards, len(cards))


assert 1924 == part_two(test_input_str)
print("part two:", part_two(puzzle_input_str))
part two: 34726