Add day 11 visualisation

This commit is contained in:
akp 2020-12-11 20:59:21 +00:00
parent bc43258abd
commit 454d8aa3a8
No known key found for this signature in database
GPG key ID: D3E7EAA31B39637E
8 changed files with 156 additions and 33 deletions

BIN
11-seatingSystem/0.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
11-seatingSystem/1.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View file

@ -2,6 +2,12 @@
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).
### Visualisation
| Part one | Part two |
| ------------------------------ | ------------------------------ |
| ![partOne gif](0.gif?raw=true) | ![partTwo gif](1.gif?raw=true) |
<details><summary>Script output</summary>
```

View file

@ -6,6 +6,7 @@ from rich import print
from partOne import partOne
from partTwo import partTwo
import visualise
def run_tests(test_cases):
@ -59,6 +60,13 @@ if __name__ == "__main__":
print("Error: could not open input.txt")
sys.exit(-1)
if "vis" in sys.argv:
print("[green]Running visualisation....[/green]")
visualise.visualise(challenge_input)
sys.exit()
run_tests(info["testCases"])
if "debug" in sys.argv:

View file

@ -8,12 +8,27 @@ filled_seat = "#"
no_seat = "."
lookup_positions = [
(0, 1),
(1, 1),
(1, 0),
(1, -1),
(0, -1),
(-1, -1),
(-1, 0),
(-1, 1),
]
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]]:
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
next_hall = copy.deepcopy(current_hall) # this is bloody slow
hall_size = (len(next_hall[0]), len(next_hall))
@ -35,7 +50,10 @@ def iterate(current_hall: List[List[str]], neighbour_counter: Callable, get_new_
# Return copied list
return next_hall
def run(current_state:List[List[str]], neighbour_counter:Callable, get_new_state:Callable) -> int:
def run(
current_state: List[List[str]], neighbour_counter: Callable, get_new_state: Callable
) -> int:
last_state = None
while current_state != last_state:
@ -49,5 +67,5 @@ def run(current_state:List[List[str]], neighbour_counter:Callable, get_new_state
for b in a:
if b == filled_seat:
total_occupied += 1
return total_occupied
return total_occupied

View file

@ -3,7 +3,7 @@ from typing import List, Tuple
from common import *
def get_new_state(num_neighbours:int, old_state:str) -> str:
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:
@ -12,22 +12,14 @@ def get_new_state(num_neighbours:int, old_state:str) -> str:
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),
]
def count_neighbours(
hall: List[List[str]], current_pos: Tuple[int, int], hall_size: Tuple[int, int]
) -> int:
num_neighbours = 0
row, col = current_pos
for (x, y) in check_positions:
for (x, y) in lookup_positions:
test_x_pos = x + col
test_y_pos = y + row

View file

@ -3,7 +3,7 @@ from typing import List, Tuple
from common import *
def get_new_state(num_neighbours:int, old_state:str) -> str:
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:
@ -12,22 +12,14 @@ def get_new_state(num_neighbours:int, old_state:str) -> str:
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),
]
def count_neighbours(
hall: List[List[str]], current_pos: Tuple[int, int], hall_size: Tuple[int, int]
) -> int:
num_neighbours = 0
row, col = current_pos
for (x, y) in deltas:
for (x, y) in lookup_positions:
test_x_pos = col + x
test_y_pos = row + y

View file

@ -0,0 +1,107 @@
import common
import partOne
import partTwo
import os
from PIL import Image
import ffmpeg
import shutil
from typing import Callable, List
frame_counter = 1
anim_counter = 0
skipped_last = True
background_colour = "#646464"
occupied_colour = (230, 0, 0)
empty_colour = (100, 220, 100)
resize_factor = 3
def make_frame(hall: List[List[str]]):
global frame_counter, skipped_last # muahahaha
if not skipped_last:
skipped_last = True
return
else:
skipped_last = False
hall_size = (len(hall[0]), len(hall))
img = Image.new("RGB", hall_size, color=background_colour)
for x in range(hall_size[0]):
for y in range(hall_size[1]):
jts = hall[y][x]
if jts == common.no_seat:
continue
colour = None
if jts == common.open_seat:
colour = empty_colour
elif jts == common.filled_seat:
colour = occupied_colour
else:
continue
img.putpixel((x, y), colour)
new_width = int((img.width * resize_factor) / 2) * 2
new_height = int((img.height * resize_factor) / 2) * 2
img = img.resize((new_width, new_height), resample=Image.NEAREST)
img.save(
os.path.join("output", f"{anim_counter}_{str(frame_counter).zfill(4)}.png")
)
frame_counter += 1
def run(
current_state: List[List[str]],
neighbour_counter: Callable,
get_new_state: Callable,
hook: Callable,
):
last_state = None
while current_state != last_state:
hook(current_state)
last_state = current_state
current_state = common.iterate(current_state, neighbour_counter, get_new_state)
def visualise(instr: str):
global anim_counter
global frame_counter
if not os.path.exists("output"):
os.mkdir("output")
run(
common.parse(instr), partOne.count_neighbours, partOne.get_new_state, make_frame
)
anim_counter += 1
frame_counter = 0
skipped_last = True
run(
common.parse(instr), partTwo.count_neighbours, partTwo.get_new_state, make_frame
)
for i in range(anim_counter + 1):
if os.path.exists(f"{i}.gif"):
os.unlink(f"{i}.gif")
(
ffmpeg.input(
os.path.join("output", f"{i}_%04d.png"), framerate=10, start_number=0
)
.output(f"{i}.gif")
.run()
)
shutil.rmtree("output")