Sean McLemon | Advent of Code

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


2024-12-08 - Resonant Collinearity

(original .ipynb)

Day 8 puzzle input is a grid showing the positions of certain antennae each operating on different frequencies (same letter/digit = same frequency), mine is here). Part 1 involves finding "antinodes" (can't be bothered explaining, it's stupid) and counting how many there are. Part 2 involves adjusting how the "antinodes" are calculated and counting them again.

from collections import defaultdict
from itertools import combinations

puzzle_input_str = open("./puzzle_input/day8.txt").read()
test_input_str = """............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............"""

def parse_input(input_str):
    input_lines = input_str.splitlines()
    antennas = defaultdict(lambda:[])
    for r, row in enumerate(input_lines):
        for c, col in enumerate(row):
            if col != ".":
                antennas[col].append((r, c))
    return antennas, len(input_lines), len(input_lines[0])


def antinodes(pos_a_r, pos_a_c, pos_b_r, pos_b_c):
    dr = pos_a_r-pos_b_r
    dc = pos_a_c-pos_b_c
    return (
        (pos_a_r+dr, pos_a_c+dc),
        (pos_b_r-dr, pos_b_c-dc),
    )

    
def in_graph(max_r, max_c, r, c):
    return r >= 0 and r < max_r and c >= 0 and c < max_c

def part_one(input_str):
    all_antennas, max_r, max_c = parse_input(input_str)
    
    antinode_positions = set()
    
    for antenna_name, antennas in all_antennas.items():
        for antenna_a, antenna_b in combinations(antennas, 2):
            for antinode in antinodes(*antenna_a, *antenna_b):
                if in_graph(max_r, max_c, *antinode):
                    antinode_positions.add(antinode)
        
    return len(antinode_positions)

assert 14 == part_one(test_input_str)

print("part one:", part_one(puzzle_input_str))
part one: 291
from itertools import chain

def antinodes_2(max_r, max_c, pos_a_r, pos_a_c, pos_b_r, pos_b_c):
    dr_base = pos_a_r-pos_b_r
    dc_base = pos_a_c-pos_b_c
    
    for dr, dc in ((dr_base, dc_base), (-dr_base, -dc_base)):
        for start_r, start_c in ((pos_a_r, pos_a_c), (pos_b_r, pos_b_c)):
            r, c = start_r+dr, start_c+dc
            while in_graph(max_r, max_c, r, c):
                yield (r, c)
                r += dr
                c += dc

def part_two(input_str):
    all_antennas, max_r, max_c = parse_input(input_str)
    
    antinode_positions = set()
    for antenna_name, antennas in all_antennas.items():
        for antenna_a, antenna_b in combinations(antennas, 2):
            for antinode in antinodes_2(max_r, max_c, *antenna_a, *antenna_b):
                if in_graph(max_r, max_c, *antinode):
                    antinode_positions.add(antinode)
    return len(antinode_positions)

assert 34 == part_two(test_input_str)

print("part two:", part_two(puzzle_input_str))
part two: 1015