Day 11 (Python)
This commit is contained in:
parent
4ced6104ab
commit
bc43258abd
7 changed files with 283 additions and 27 deletions
4
.github/README.md
vendored
4
.github/README.md
vendored
|
@ -15,7 +15,7 @@ Puzzle inputs and descriptions are not included in this repository. You'll have
|
||||||
<!-- PARSE START -->
|
<!-- PARSE START -->
|
||||||
|
|
||||||
| Day | | Python | Go |
|
| Day | | Python | Go |
|
||||||
| --------------------------- | ----------------------------- | ------------------------------------- | --------------------------------- |
|
| --------------------------- | ------------------------------ | ------------------------------------- | --------------------------------- |
|
||||||
| [1](/01-reportRepair) | ![Completed][check] | [Link](/01-reportRepair/python) | [Link](/01-reportRepair/go) |
|
| [1](/01-reportRepair) | ![Completed][check] | [Link](/01-reportRepair/python) | [Link](/01-reportRepair/go) |
|
||||||
| [2](/02-passwordPhilosophy) | ![Completed][check] | [Link](/02-passwordPhilosophy/python) | [Link](/02-passwordPhilosophy/go) |
|
| [2](/02-passwordPhilosophy) | ![Completed][check] | [Link](/02-passwordPhilosophy/python) | [Link](/02-passwordPhilosophy/go) |
|
||||||
| [3](/03-tobogganTrajectory) | ![Completed][check] | [Link](/03-tobogganTrajectory/python) | [Link](/03-tobogganTrajectory/go) |
|
| [3](/03-tobogganTrajectory) | ![Completed][check] | [Link](/03-tobogganTrajectory/python) | [Link](/03-tobogganTrajectory/go) |
|
||||||
|
@ -26,7 +26,7 @@ Puzzle inputs and descriptions are not included in this repository. You'll have
|
||||||
| [8](/08-handheldHalting) | ![Completed][check] | [Link](/08-handheldHalting/python) | [Link](/08-handheldHalting/go) |
|
| [8](/08-handheldHalting) | ![Completed][check] | [Link](/08-handheldHalting/python) | [Link](/08-handheldHalting/go) |
|
||||||
| [9](/09-encodingError) | ![Completed][check] | [Link](/09-encodingError/python) | [Link](/09-encodingError/go) |
|
| [9](/09-encodingError) | ![Completed][check] | [Link](/09-encodingError/python) | [Link](/09-encodingError/go) |
|
||||||
| [10](/10-adapterArray) | ![Completed][check] | [Link](/10-adapterArray/python) | [Link](/10-adapterArray/go) |
|
| [10](/10-adapterArray) | ![Completed][check] | [Link](/10-adapterArray/python) | [Link](/10-adapterArray/go) |
|
||||||
| 11 | ![Not yet attempted][pending] | | |
|
| [11](/11-seatingSystem) | ![Partially complete][partial] | [Link](/11-seatingSystem/python) | |
|
||||||
| 12 | | | |
|
| 12 | | | |
|
||||||
| 13 | | | |
|
| 13 | | | |
|
||||||
| 14 | | | |
|
| 14 | | | |
|
||||||
|
|
21
11-seatingSystem/README.md
Normal file
21
11-seatingSystem/README.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# [Day 11: Seating System](https://adventofcode.com/2020/day/11)
|
||||||
|
|
||||||
|
This is a [cellular automata](https://en.wikipedia.org/wiki/Cellular_automaton) that (in part one) uses the [Moore neighbourhood](https://en.wikipedia.org/wiki/Moore_neighborhood).
|
||||||
|
|
||||||
|
<details><summary>Script output</summary>
|
||||||
|
|
||||||
|
```
|
||||||
|
❯ python .\python\
|
||||||
|
AoC 2020: day 11 - Seating System
|
||||||
|
Python 3.8.5
|
||||||
|
|
||||||
|
Test cases
|
||||||
|
1.1 pass
|
||||||
|
2.1 pass
|
||||||
|
|
||||||
|
Answers
|
||||||
|
Part 1: 2283
|
||||||
|
Part 2: 2054
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
19
11-seatingSystem/info.json
Normal file
19
11-seatingSystem/info.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"year": "2020",
|
||||||
|
"day": "11",
|
||||||
|
"title": "Seating System",
|
||||||
|
"testCases": {
|
||||||
|
"one": [
|
||||||
|
{
|
||||||
|
"input": "L.LL.LL.LL\nLLLLLLL.LL\nL.L.L..L..\nLLLL.LL.LL\nL.LL.LL.LL\nL.LLLLL.LL\n..L.L.....\nLLLLLLLLLL\nL.LLLLLL.L\nL.LLLLL.LL\n",
|
||||||
|
"expected": 37
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"two": [
|
||||||
|
{
|
||||||
|
"input": "L.LL.LL.LL\nLLLLLLL.LL\nL.L.L..L..\nLLLL.LL.LL\nL.LL.LL.LL\nL.LLLLL.LL\n..L.L.....\nLLLLLLLLLL\nL.LLLLLL.L\nL.LLLLL.LL\n",
|
||||||
|
"expected": 26
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
69
11-seatingSystem/python/__main__.py
Normal file
69
11-seatingSystem/python/__main__.py
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import json
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from rich import print
|
||||||
|
|
||||||
|
from partOne import partOne
|
||||||
|
from partTwo import partTwo
|
||||||
|
|
||||||
|
|
||||||
|
def run_tests(test_cases):
|
||||||
|
do_tests = True
|
||||||
|
if len(test_cases) == 0:
|
||||||
|
do_tests = False
|
||||||
|
elif len(test_cases["one"]) == 0 and len(test_cases["two"]) == 0:
|
||||||
|
do_tests = False
|
||||||
|
|
||||||
|
if not do_tests:
|
||||||
|
print("Info: no test cases specified. Skipping tests\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Test cases")
|
||||||
|
|
||||||
|
def rt(tcs, f, n):
|
||||||
|
for i, tc in enumerate(tcs):
|
||||||
|
print(f"{n}.{i+1} ", end="")
|
||||||
|
expectedInt = tc["expected"]
|
||||||
|
result = f(str(tc["input"]))
|
||||||
|
if result == expectedInt:
|
||||||
|
print("[green]pass[/green]")
|
||||||
|
else:
|
||||||
|
print(f"[red]fail[/red] (got {result}, expected {expectedInt})")
|
||||||
|
|
||||||
|
rt(test_cases["one"], partOne, 1)
|
||||||
|
rt(test_cases["two"], partTwo, 2)
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
info = open("info.json").read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("Error: could not open info.json")
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
info = json.loads(info)
|
||||||
|
|
||||||
|
year = info["year"]
|
||||||
|
day = info["day"]
|
||||||
|
title = info["title"]
|
||||||
|
|
||||||
|
print(f"[yellow]AoC {year}[/yellow]: day {day} - {title}")
|
||||||
|
print(f"Python {platform.python_version()}\n")
|
||||||
|
|
||||||
|
try:
|
||||||
|
challenge_input = open("input.txt").read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("Error: could not open input.txt")
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
run_tests(info["testCases"])
|
||||||
|
|
||||||
|
if "debug" in sys.argv:
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
print("Answers")
|
||||||
|
print("Part 1:", partOne(challenge_input))
|
||||||
|
print("Part 2:", partTwo(challenge_input))
|
53
11-seatingSystem/python/common.py
Normal file
53
11-seatingSystem/python/common.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import copy
|
||||||
|
import time
|
||||||
|
from typing import Callable, List
|
||||||
|
|
||||||
|
|
||||||
|
open_seat = "L"
|
||||||
|
filled_seat = "#"
|
||||||
|
no_seat = "."
|
||||||
|
|
||||||
|
|
||||||
|
def parse(instr: str) -> List[List[str]]:
|
||||||
|
return [[char for char in x] for x in instr.strip().split("\n")]
|
||||||
|
|
||||||
|
def iterate(current_hall: List[List[str]], neighbour_counter: Callable, get_new_state: Callable) -> List[List[str]]:
|
||||||
|
# Copy hall
|
||||||
|
next_hall = copy.deepcopy(current_hall) # this is bloody slow
|
||||||
|
|
||||||
|
hall_size = (len(next_hall[0]), len(next_hall))
|
||||||
|
|
||||||
|
# Iterate each chair space
|
||||||
|
for col in range(hall_size[0]):
|
||||||
|
for row in range(hall_size[1]):
|
||||||
|
current_pos = current_hall[row][col]
|
||||||
|
|
||||||
|
# If it's the floor, there's nothing we can do with this spot
|
||||||
|
if current_pos == no_seat:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Count number of adjacent seats
|
||||||
|
num_neighbours = neighbour_counter(current_hall, (row, col), hall_size)
|
||||||
|
|
||||||
|
# Execute rules on copied list based on that count
|
||||||
|
next_hall[row][col] = get_new_state(num_neighbours, next_hall[row][col])
|
||||||
|
|
||||||
|
# Return copied list
|
||||||
|
return next_hall
|
||||||
|
|
||||||
|
def run(current_state:List[List[str]], neighbour_counter:Callable, get_new_state:Callable) -> int:
|
||||||
|
last_state = None
|
||||||
|
|
||||||
|
while current_state != last_state:
|
||||||
|
last_state = current_state
|
||||||
|
current_state = iterate(current_state, neighbour_counter, get_new_state)
|
||||||
|
|
||||||
|
# at this point, we've ended up with two identical seating arrangements
|
||||||
|
# let's count the number of occupied seats
|
||||||
|
total_occupied = 0
|
||||||
|
for a in current_state:
|
||||||
|
for b in a:
|
||||||
|
if b == filled_seat:
|
||||||
|
total_occupied += 1
|
||||||
|
|
||||||
|
return total_occupied
|
42
11-seatingSystem/python/partOne.py
Normal file
42
11-seatingSystem/python/partOne.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
|
from common import *
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_state(num_neighbours:int, old_state:str) -> str:
|
||||||
|
if num_neighbours == 0:
|
||||||
|
return filled_seat
|
||||||
|
elif num_neighbours >= 4 and old_state == filled_seat:
|
||||||
|
return open_seat
|
||||||
|
|
||||||
|
return old_state
|
||||||
|
|
||||||
|
|
||||||
|
def count_neighbours(hall:List[List[str]], current_pos:Tuple[int, int], hall_size:Tuple[int, int]) -> int:
|
||||||
|
num_neighbours = 0
|
||||||
|
check_positions = [
|
||||||
|
(0, 1),
|
||||||
|
(1, 1),
|
||||||
|
(1, 0),
|
||||||
|
(1, -1),
|
||||||
|
(0, -1),
|
||||||
|
(-1, -1),
|
||||||
|
(-1, 0),
|
||||||
|
(-1, 1),
|
||||||
|
]
|
||||||
|
|
||||||
|
row, col = current_pos
|
||||||
|
|
||||||
|
for (x, y) in check_positions:
|
||||||
|
test_x_pos = x + col
|
||||||
|
test_y_pos = y + row
|
||||||
|
|
||||||
|
if 0 <= test_x_pos < hall_size[0] and 0 <= test_y_pos < hall_size[1]:
|
||||||
|
if hall[test_y_pos][test_x_pos] == filled_seat:
|
||||||
|
num_neighbours += 1
|
||||||
|
|
||||||
|
return num_neighbours
|
||||||
|
|
||||||
|
|
||||||
|
def partOne(instr: str) -> int:
|
||||||
|
return run(parse(instr), count_neighbours, get_new_state)
|
52
11-seatingSystem/python/partTwo.py
Normal file
52
11-seatingSystem/python/partTwo.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
|
from common import *
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_state(num_neighbours:int, old_state:str) -> str:
|
||||||
|
if num_neighbours == 0:
|
||||||
|
return filled_seat
|
||||||
|
elif num_neighbours >= 5 and old_state == filled_seat:
|
||||||
|
return open_seat
|
||||||
|
|
||||||
|
return old_state
|
||||||
|
|
||||||
|
|
||||||
|
def count_neighbours(hall:List[List[str]], current_pos:Tuple[int, int], hall_size:Tuple[int, int]) -> int:
|
||||||
|
num_neighbours = 0
|
||||||
|
deltas = [
|
||||||
|
(0, 1),
|
||||||
|
(1, 1),
|
||||||
|
(1, 0),
|
||||||
|
(1, -1),
|
||||||
|
(0, -1),
|
||||||
|
(-1, -1),
|
||||||
|
(-1, 0),
|
||||||
|
(-1, 1),
|
||||||
|
]
|
||||||
|
|
||||||
|
row, col = current_pos
|
||||||
|
|
||||||
|
for (x, y) in deltas:
|
||||||
|
|
||||||
|
test_x_pos = col + x
|
||||||
|
test_y_pos = row + y
|
||||||
|
|
||||||
|
while 0 <= test_x_pos < hall_size[0] and 0 <= test_y_pos < hall_size[1]:
|
||||||
|
|
||||||
|
test_location = hall[test_y_pos][test_x_pos]
|
||||||
|
|
||||||
|
if test_location == filled_seat:
|
||||||
|
num_neighbours += 1
|
||||||
|
|
||||||
|
if test_location in [open_seat, filled_seat]:
|
||||||
|
break
|
||||||
|
|
||||||
|
test_x_pos += x
|
||||||
|
test_y_pos += y
|
||||||
|
|
||||||
|
return num_neighbours
|
||||||
|
|
||||||
|
|
||||||
|
def partTwo(instr: str) -> int:
|
||||||
|
return run(parse(instr), count_neighbours, get_new_state)
|
Loading…
Add table
Add a link
Reference in a new issue