Skip to content

Commit

Permalink
feat(2024): day 8 - Resonant Collinearity
Browse files Browse the repository at this point in the history
  • Loading branch information
rpidanny committed Dec 8, 2024
1 parent d0ef56f commit cdafa6b
Show file tree
Hide file tree
Showing 8 changed files with 327 additions and 12 deletions.
8 changes: 8 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"version": "0.2",
"language": "en",
"dictionaries": ["aoc-words"],
"dictionaryDefinitions": [],
"words": ["Collinearity"],
"ignorePaths": [".github", ".vscode"]
}
141 changes: 139 additions & 2 deletions 2024/Day08/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,140 @@
# --- Day 0: Template ---
# --- Day 8: Resonant Collinearity ---

To be used as template for the other days.
You find yourselves on the roof of a top-secret Easter Bunny installation.

While The Historians do their thing, you take a look at the familiar huge antenna. Much to your surprise, it seems to have been reconfigured to emit a signal that makes people 0.1% more likely to buy Easter Bunny brand Imitation Mediocre Chocolate as a Christmas gift! Unthinkable!

Scanning across the city, you find that there are actually many such antennas. Each antenna is tuned to a specific frequency indicated by a single lowercase letter, uppercase letter, or digit. You create a map (your puzzle input) of these antennas. For example:

```txt
............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............
```

The signal only applies its nefarious effect at specific antinodes based on the resonant frequencies of the antennas. In particular, an antinode occurs at any point that is perfectly in line with two antennas of the same frequency - but only when one of the antennas is twice as far away as the other. This means that for any pair of antennas with the same frequency, there are two antinodes, one on either side of them.

So, for these two antennas with frequency a, they create the two antinodes marked with #:

```txt
..........
...#......
..........
....a.....
..........
.....a....
..........
......#...
..........
..........
```

Adding a third antenna with the same frequency creates several more antinodes. It would ideally add four antinodes, but two are off the right side of the map, so instead it adds only two:

```txt
..........
...#......
#.........
....a.....
........a.
.....a....
..#.......
......#...
..........
..........
```

Antennas with different frequencies don't create antinodes; A and a count as different frequencies. However, antinodes can occur at locations that contain antennas. In this diagram, the lone antenna with frequency capital A creates no antinodes but has a lowercase-a-frequency antinode at its location:

```txt
..........
...#......
#.........
....a.....
........a.
.....a....
..#.......
......A...
..........
..........
```

The first example has antennas with two different frequencies, so the antinodes they create look like this, plus an antinode overlapping the topmost A-frequency antenna:

```txt
......#....#
...#....0...
....#0....#.
..#....0....
....0....#..
.#....A.....
...#........
#......#....
........A...
.........A..
..........#.
..........#.
```

Because the topmost A-frequency antenna overlaps with a 0-frequency antinode, there are `14` total unique locations that contain an antinode within the bounds of the map.

Calculate the impact of the signal. How many unique locations within the bounds of the map contain an antinode?

> Your puzzle answer was `364`.
## --- Part Two ---

Watching over your shoulder as you work, one of The Historians asks if you took the effects of resonant harmonics into your calculations.

Whoops!

After updating your model, it turns out that an antinode occurs at any grid position exactly in line with at least two antennas of the same frequency, regardless of distance. This means that some of the new antinodes will occur at the position of each antenna (unless that antenna is the only one of its frequency).

So, these three T-frequency antennas now create many antinodes:

```txt
T....#....
...T......
.T....#...
.........#
..#.......
..........
...#......
..........
....#.....
..........
```

In fact, the three T-frequency antennas are all exactly in line with two antennas, so they are all also antinodes! This brings the total number of antinodes in the above example to `9`.

The original example now has `34` antinodes, including the antinodes that appear on every antenna:

```txt
##....#....#
.#.#....0...
..#.#0....#.
..##...0....
....0....#..
.#...#A....#
...#..#.....
#....#.#....
..#.....A...
....#....A..
.#........#.
...#......##
```

Calculate the impact of the signal using this updated model. How many unique locations within the bounds of the map contain an antinode?

> Your puzzle answer was `1231`.
Both parts of this puzzle are complete! They provide two gold stars: `**`
50 changes: 50 additions & 0 deletions 2024/Day08/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
.A...........5........................pL..........
.................................p......L.........
......................................L...........
.......................................C..........
........v...................7...............C.....
..................................p........L......
.................vA......3........................
.......A.....3....................................
........................s....X3...................
..A......5.................9....3.................
.......8...........s.........7.............C..m...
................8......t........7.......9.........
....................o......Z.............y........
...............s.......Y.v.o......y....0..........
..................................................
..5................8.......................m...J..
5...............................0....aX...........
.V............v.s.........Z.o..7....a.............
2..........f...........P..............9...J.M.....
...............f..........P.....V......y....1.J...
...g...................o.......0l...........N..B..
..................Y...............................
......G...............f.....Z..t..............1...
............G......Z......h................B....C.
.........w....h.Y....j............a........J..y...
.............P....z..........................1....
w.......P...z...R......r8.........................
........w.........................................
.................h.G.........m............BM......
......4.....fa.................G...i....X......W..
V........4..............................tW.9...i..
............2h..............0.......tX...M........
.....z.........................l..................
.......2..........................................
..r........................Y................W...i.
.......w.........q..................i.............
.........H.2....4.................................
..........Q.....j.......M.....lrN.................
..x...H.Q.......O.....c...........................
....H.......................S.....................
.....................O..S.......6..........b......
...c.......F...Q.j.........l....T.....R...........
...........Q.F.......c.I.....1.........R....T.....
............F........I.O......r..T.............b..
..n.........q.........F.I..............T..b.......
.......n...........z..O....x.......N........b.....
.....S............................................
..........q.........cS..x4I......6................
..j.....gn.q.......x...................N...6......
...........g..n................R......B...........
12 changes: 12 additions & 0 deletions 2024/Day08/input_test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............
50 changes: 50 additions & 0 deletions 2024/Day08/resonant_collinearity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from itertools import combinations
from typing import Dict, List, Tuple


class ResonantCollinearity:
def __init__(self, lines: List[str]):
self.grid = [list(line) for line in lines]
self.rows, self.cols = len(self.grid), len(self.grid[0])
self.antennas = self._get_antennas()

def _get_antennas(self) -> Dict[str, List[Tuple[int, int]]]:
antennas = {}
for y, row in enumerate(self.grid):
for x, cell in enumerate(row):
if cell != ".":
antennas.setdefault(cell, []).append((x, y))
return antennas

def _valid_position(self, x: int, y: int) -> bool:
return 0 <= x < self.cols and 0 <= y < self.rows

def _generate_antinode_candidates(
self, p1: Tuple[int, int], p2: Tuple[int, int]
) -> List[Tuple[int, int]]:
x1, y1 = p1
x2, y2 = p2
return [(2 * x2 - x1, 2 * y2 - y1), (2 * x1 - x2, 2 * y1 - y2)]

def count_antinode(self) -> int:
anti_nodes = set()
for positions in self.antennas.values():
for p1, p2 in combinations(positions, 2):
for antinode in self._generate_antinode_candidates(p1, p2):
if self._valid_position(*antinode):
anti_nodes.add(antinode)
return len(anti_nodes)

def count_antinode_with_harmonics(self) -> int:
anti_nodes = set()
for positions in self.antennas.values():
for p1, p2 in combinations(positions, 2):
dx, dy = p2[0] - p1[0], p2[1] - p1[1]

for start_x, start_y, step in [(p1[0], p1[1], 1), (p2[0], p2[1], -1)]:
x, y = start_x, start_y
while self._valid_position(x, y):
anti_nodes.add((x, y))
x += step * dx
y += step * dy
return len(anti_nodes)
9 changes: 7 additions & 2 deletions 2024/Day08/solutions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from resonant_collinearity import ResonantCollinearity


def part1(inputs: list[str]) -> int:
return 1
rc = ResonantCollinearity(inputs)
return rc.count_antinode()


def part2(inputs: list[str]) -> int:
return 2
rc = ResonantCollinearity(inputs)
return rc.count_antinode_with_harmonics()
57 changes: 57 additions & 0 deletions 2024/Day08/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
def get_antinodes(grid):
# Dimensions of the grid
rows = len(grid)
cols = len(grid[0])

# Step 1: Collect all antenna positions by frequency
antennas = {}
for r in range(rows):
for c in range(cols):
char = grid[r][c]
if char != ".":
if char not in antennas:
antennas[char] = []
antennas[char].append((r, c))

# Step 2: Find and mark all antinode positions
antinodes = set()

# For each frequency group (antenna with same frequency)
for freq, positions in antennas.items():
# Compare each pair of antennas
for i in range(len(positions)):
for j in range(i + 1, len(positions)):
r1, c1 = positions[i]
r2, c2 = positions[j]

# Calculate the distance between the antennas
dr = abs(r1 - r2)
dc = abs(c1 - c2)

# If one antenna is twice as far as the other in a straight line
if (dr == 2 * dc) or (dc == 2 * dr):
# Calculate the antinode positions and add them
antinodes.add(((r1 + r2) // 2, (c1 + c2) // 2))

# Return the number of unique antinode positions
return len(antinodes)


# Example input (a simplified case from the prompt)
grid = [
"............",
"........0...",
".....0......",
".......0....",
"....0.......",
"......A.....",
"............",
"............",
"........A...",
".........A..",
"............",
"............",
]

# Calculate the number of unique antinodes
print(get_antinodes(grid))
12 changes: 4 additions & 8 deletions 2024/Day08/test_solutions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import os

import pytest
from solutions import part1, part2

from utils.inputs import get_inputs
Expand All @@ -13,18 +12,15 @@

class TestPart1:
def test_with_test_data(self):
assert part1(input_test) == 1
assert part1(input_test) == 14

@pytest.mark.skip(reason="not implemented")
def test_with_real_data(self):
assert part1(input) == 1
assert part1(input) == 364


class TestPart2:
@pytest.mark.skip(reason="not implemented")
def test_with_test_data(self):
assert part2(input_test) == 2
assert part2(input_test) == 34

@pytest.mark.skip(reason="not implemented")
def test_with_real_data(self):
assert part2(input) == 2
assert part2(input) == 1231

0 comments on commit cdafa6b

Please sign in to comment.