Skip to content

Commit

Permalink
day 8
Browse files Browse the repository at this point in the history
  • Loading branch information
vsedov committed Dec 8, 2024
1 parent b6377a9 commit 2c65407
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 71 deletions.
93 changes: 25 additions & 68 deletions src/aoc/aoc2024/day_07.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,81 +8,38 @@
from src.aoc.aoc2024 import YEAR, get_day
from src.aoc.aoc_helper import Aoc

from collections.abc import Generator, Sequence

@njit(cache=True)
def str_len(n: int) -> int:
if n == 0:
return 1
length = 0
while n > 0:
length += 1
n //= 10
return length


@njit(cache=True)
def concat_nums(a: int, b: int) -> int:
return a * (10 ** str_len(b)) + b


@njit(cache=True)
def dp_line(nums: np.ndarray, target: int, with_concat: bool) -> bool:
prev_reachable = Dict.empty(key_type=np.int64, value_type=np.bool_)
prev_reachable[nums[0]] = True
for i in range(1, len(nums)):
current_num = nums[i]
next_reachable = Dict.empty(key_type=np.int64, value_type=np.bool_)
for val in prev_reachable.keys():
new_val = val + current_num
if abs(new_val) < 10**12:
next_reachable[new_val] = True
new_val = val * current_num
if abs(new_val) < 10**12:
next_reachable[new_val] = True
if with_concat:
new_val = concat_nums(val, current_num)
if abs(new_val) < 10**12:
next_reachable[new_val] = True
prev_reachable = next_reachable
return target in prev_reachable


@njit(cache=True, parallel=True, fastmath=True)
def dp_solve_all(
targets: np.ndarray, all_nums: NumbaList[np.ndarray], with_concat: bool
) -> int:
total = 0
for i in prange(len(targets)):
if dp_line(all_nums[i], targets[i], with_concat):
total += targets[i]
return total


def solve_with_dp(txt: str, with_concat: bool = False) -> int:
lines = txt.strip().split("\n")
targets_list = []
nums_list = []
for line in lines:
target_str, nums_str = line.split(": ")
target = int(target_str)
nums = np.array([int(x) for x in nums_str.split()], dtype=np.int64)
targets_list.append(target)
nums_list.append(nums)

targets_arr = np.array(targets_list, dtype=np.int64)
typed_nums_list = NumbaList()
for arr in nums_list:
typed_nums_list.append(arr)

return dp_solve_all(targets_arr, typed_nums_list, with_concat)

def parse(txt: str) -> Generator[tuple[int, tuple[int, ...]], None, None]:
for line in txt.splitlines():
target, sep, nums = line.partition(": ")
assert sep == ": "
yield int(target), tuple(map(int, nums.split()))


def _possible_results(nums: Sequence[int], *, concat_op: bool = False) -> Generator[int, None, None]:
if len(nums) == 1:
yield nums[0]
return
*rest, last = nums
for r in _possible_results(rest, concat_op=concat_op):
yield r + last
yield r * last
if concat_op:
yield int(str(r) + str(last))


def solve(txt: str, *, concat_op: bool = False) -> int:
return sum(target for target, nums in parse(txt) if target in _possible_results(nums, concat_op=concat_op))


def part_a(txt: str) -> int:
return solve_with_dp(txt, with_concat=False)
return solve(txt)


def part_b(txt: str) -> int:
return solve_with_dp(txt, with_concat=True)
return solve(txt, concat_op=True)


def main(txt: str) -> None:
Expand Down
69 changes: 69 additions & 0 deletions src/aoc/aoc2024/day_08.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from src.aoc.aoc2024 import YEAR, get_day
from src.aoc.aoc_helper import Aoc
import numpy as np
from collections import defaultdict
import itertools

def parse(txt: str):
lines = txt.splitlines()
height, width = len(lines), len(lines[0])
scan = {}
antennas_by_freq = defaultdict(list)

for y, line in enumerate(lines):
for x, c in enumerate(line):
if c != '.':
pos = x + y * 1j
scan[pos] = c
antennas_by_freq[c].append(pos)

return scan, antennas_by_freq

def part_a(txt: str) -> int:
scan, antennas_by_freq = parse(txt)
antinode_locations = set()

for freq, antennas in antennas_by_freq.items():
for a, b in itertools.combinations(antennas, 2):
diff = a - b
if abs(diff) > 0:
antinode_locations.add(a + diff) # Forward antinode
antinode_locations.add(b - diff) # Backward antinode

lines = txt.splitlines()
height, width = len(lines), len(lines[0])
valid_antinodes = sum(1 for pos in antinode_locations
if 0 <= pos.real < width and 0 <= pos.imag < height)

return valid_antinodes

def part_b(txt: str) -> int:
scan, antennas_by_freq = parse(txt)
lines = txt.splitlines()
height, width = len(lines), len(lines[0])
max_dim = max(height, width)
antinode_locations = set()

for freq, antennas in antennas_by_freq.items():
if len(antennas) >= 2:
antinode_locations.update(antennas)

for a, b in itertools.combinations(antennas, 2):
diff = a - b
if abs(diff) > 0:
for i in range(-max_dim, max_dim + 1):
antinode_locations.add(a + i * diff)
antinode_locations.add(b + i * diff)

valid_antinodes = sum(1 for pos in antinode_locations
if 0 <= pos.real < width and 0 <= pos.imag < height)

return valid_antinodes

def main(txt: str) -> None:
print("part_a: ", part_a(txt))
print("part_b: ", part_b(txt))

if __name__ == "__main__":
aoc = Aoc(day=get_day(), years=YEAR)
aoc.run(main, submit=False, part='both', readme_update=True, profile=True, analyze_complexity=True)
2 changes: 1 addition & 1 deletion src/aoc/aoc2024/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
| 05 | [Print Queue](https://adventofcode.com/2024/day/5) ||||
| 06 | [Guard Gallivant](https://adventofcode.com/2024/day/6) ||||
| 07 | [Bridge Repair](https://adventofcode.com/2024/day/7) ||||
| 08 | [?](https://adventofcode.com/2024/day/8) | :x: | :x: | :x: |
| 08 | [Resonant Collinearity](https://adventofcode.com/2024/day/8) | | | |
| 09 | [?](https://adventofcode.com/2024/day/9) | :x: | :x: | :x: |
| 10 | [?](https://adventofcode.com/2024/day/10) | :x: | :x: | :x: |
| 11 | [?](https://adventofcode.com/2024/day/11) | :x: | :x: | :x: |
Expand Down
4 changes: 2 additions & 2 deletions src/aoc/aoc_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,8 @@ def run(
readme_update: bool = False,
profile: bool = False,
analyze_complexity: bool = False,
warmups: int = 10,
repeats: int = 10,
warmups: int = 1000,
repeats: int = 1000,
runs: int = 1000,
) -> Dict[str, PerformanceMetrics]:
"""Main execution method with enhanced performance analysis options."""
Expand Down
23 changes: 23 additions & 0 deletions tests/aoc2024/2024_day_07_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import pytest

from src.aoc.aoc2024 import day_07 as d

TEST_INPUT = """
190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20
""".strip()


def test_a() -> None:
assert d.part_a(TEST_INPUT) == 3749


def test_b() -> None:
assert d.part_b(TEST_INPUT) == 11387
39 changes: 39 additions & 0 deletions tests/aoc2024/2024_day_08_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

import pytest

from src.aoc.aoc2024 import day_08 as d

TEST_INPUT = """
............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............
""".strip()

TEST_INPUT_2 = """
T....#....
...T......
.T....#...
.........#
..#.......
..........
...#......
..........
....#.....
..........
""" .strip()
def test_a() -> None:
assert d.part_a(TEST_INPUT) == 14


def test_b() -> None:
assert d.part_b(TEST_INPUT) == 34

0 comments on commit 2c65407

Please sign in to comment.