Browse Source

Adding day11

Co-Authored-By: Ryan Reed <ryanreed@noreply.gitea.ryanreed.net>
Co-Committed-By: Ryan Reed <ryanreed@noreply.gitea.ryanreed.net>
master
Ryan Reed 3 years ago
parent
commit
bf9f58e0eb
4 changed files with 211 additions and 0 deletions
  1. +10
    -0
      inputs/day11-example.txt
  2. +10
    -0
      inputs/day11.txt
  3. +155
    -0
      puzzles/day11.py
  4. +36
    -0
      tests/test_day11.py

+ 10
- 0
inputs/day11-example.txt View File

@ -0,0 +1,10 @@
5483143223
2745854711
5264556173
6141336146
6357385478
4167524645
2176841721
6882881134
4846848554
5283751526

+ 10
- 0
inputs/day11.txt View File

@ -0,0 +1,10 @@
6744638455
3135745418
4754123271
4224257161
8167186546
2268577674
7177768175
2662255275
4655343376
7852526168

+ 155
- 0
puzzles/day11.py View File

@ -0,0 +1,155 @@
"""
Advent of Code 2021 - Day 11
Run with:
python puzzles/day11.py inputs/day11.txt
"""
import pathlib
import sys
from typing import List, Tuple
from dataclasses import dataclass
@dataclass
class Octopus:
energy_level: int = 0
flash_count: int = 0
location: Tuple[int, int] = (0, 0)
last_flash: int = 0
class School:
octopi = None
step = 1
def __init__(self, data: List[str]) -> None:
self.parse(data)
self.synchronized_steps = []
def flash(self, octopus: Octopus) -> None:
octopus.energy_level = 0
octopus.flash_count += 1
octopus.last_flash = self.step
self.update_neighbors(octopus.location)
def update(self, octopus: Octopus) -> None:
if octopus.last_flash == self.step:
return None
octopus.energy_level += 1
if octopus.energy_level == 10:
self.flash(octopus)
def update_neighbors(self, location: Tuple[int, int]) -> None:
min_row = 0 if location[0] == 0 else location[0] - 1
min_col = 0 if location[1] == 0 else location[1] - 1
max_row = (
location[0] if location[0] == self.size["rows"] - 1 else location[0] + 1
)
max_col = (
location[1] if location[1] == self.size["cols"] - 1 else location[1] + 1
)
for row in range(min_row, max_row + 1):
for col in range(min_col, max_col + 1):
if row == location[0] and col == location[1]:
continue
if self.octopi[row][col].last_flash == self.step:
continue
self.update(self.octopi[row][col])
def simulate(self, steps: int = 100) -> None:
for step in range(1, steps + 1):
self.step = step
for row in self.octopi:
for octopus in row:
self.update(octopus)
def is_synchronized(self) -> bool:
"""This is inefficient and needs to be reworked"""
for row in self.octopi:
for octopi in row:
if octopi.energy_level > 0:
return False
return True
@property
def first_synchronized(self) -> int:
step = 0
while True:
step += 1
self.step = step
for row in self.octopi:
for octopus in row:
self.update(octopus)
if self.is_synchronized():
break
return step
@property
def total_flashes(self) -> int:
total = 0
for row in self.octopi:
for octopus in row:
total += octopus.flash_count
return total
def parse(self, data: List[str]) -> None:
self.octopi = []
for row_index, row in enumerate(data):
self.octopi.append(
[
Octopus(
energy_level=int(value),
location=(int(row_index), int(column_index)), # row, col
)
for column_index, value in enumerate(row)
]
)
self.size = {"rows": len(self.octopi), "cols": len(self.octopi[0])}
def part1(inputs: List[int]) -> int:
s = School(inputs)
s.simulate()
return s.total_flashes
def part2(inputs: List[int]) -> int:
s = School(inputs)
return s.first_synchronized
def parse(inputs: str) -> List[str]:
"""Parse the input string"""
return [line for line in inputs.split()]
def solve(path: str) -> Tuple[int, int]:
"""Solve the puzzle"""
puzzle_input = parse(pathlib.Path(path).read_text().strip())
part1_result = part1(puzzle_input)
part2_result = part2(puzzle_input)
return part1_result, part2_result
def main() -> None:
for path in sys.argv[1:]:
print(f"Input File: {path}")
part1_result, part2_result = solve(path)
print(f"Part 1 Result: {part1_result}")
print(f"Part 2 Result: {part2_result}")
if __name__ == "__main__":
main()

+ 36
- 0
tests/test_day11.py View File

@ -0,0 +1,36 @@
import pathlib
import pytest
import sys
ROOT_DIR = pathlib.Path(__file__).parent.parent
sys.path.append(f"{ROOT_DIR}/puzzles")
import day11 as aoc
INPUTS_DIR = f"{ROOT_DIR}/inputs"
@pytest.fixture
def example_data():
input_path = f"{INPUTS_DIR}/day11-example.txt"
return aoc.parse(pathlib.Path(input_path).read_text().strip())
@pytest.fixture
def day11_data():
input_path = f"{INPUTS_DIR}/day11.txt"
return aoc.parse(pathlib.Path(input_path).read_text().strip())
def test_example1(example_data):
assert aoc.part1(example_data) == 1656
def test_example2(example_data):
assert aoc.part2(example_data) == 195
def test_part1(day11_data):
assert aoc.part1(day11_data) == 1608
def test_part2(day11_data):
assert aoc.part2(day11_data) == 214

Loading…
Cancel
Save