diff --git a/exam/ex13/backend.py b/exam/ex13/backend.py new file mode 100644 index 0000000..283e74d --- /dev/null +++ b/exam/ex13/backend.py @@ -0,0 +1,64 @@ +import numpy as np + +CELL_IS_ALIVE = 0b1 +CELL_WILL_BE_ALIVE = 0b10 +CELL_WILL_BE_DEAD = 0b100 + +def is_alive(model, i, j): + return model[i][j] & CELL_IS_ALIVE + +def get_cells_around(model, i, j): + # Most common case. Handle this first. + # Fortunately the most common case is also the fastets to handle. + if(i > 2 and j > 2 and i < model.shape[0] and j < model.shape[1]): + return model[i - 2: i + 2, j - 2: j + 2] + + # Neither negative indices nor indices that overflow + # are handled properly by numpy. So basically I have to do that manually: + cells_around = np.zeros([3, 3], np.int8) + for k, m in enumerate(range(i - 1, i + 2)): + for l, n in enumerate(range(j - 1, j + 2)): + real_m = m + if(m >= model.shape[0]): + real_m = m - model.shape[0] + if(m < 0): + real_m = m + model.shape[0] + real_n = n + if(n >= model.shape[1]): + real_n = n - model.shape[1] + if(n < 0): + real_n = n + model.shape[1] + cells_around[k][l] = model[real_m][real_n] + return cells_around + +def count_living_cells_around(model, i, j): + cells_around = get_cells_around(model, i, j) + living_cells_around = (cells_around & CELL_IS_ALIVE) == 1 + living_cells_around[1][1] = False + _, counts = np.unique(living_cells_around, return_counts=True) + print(counts) + return counts[True] + + + +def handle_cell(model, i, j): + living_cells_around = count_living_cells_around(model, i, j) + if(not is_alive(model, i, j)): + if(living_cells_around == 3): + model[i][j] = CELL_WILL_BE_ALIVE + return + + if(living_cells_around > 3 or living_cells_around < 2): + model[i][j] = CELL_WILL_BE_DEAD + +def after_tick_finished(model): + # kill the cells that have died + model[(model & CELL_WILL_BE_DEAD) != 0] = 0 + + # create the new cells + model[(model & CELL_WILL_BE_ALIVE) != 0] = 1 + +def total_living_cells(model): + living_cells = (model & CELL_IS_ALIVE) == 1 + _, counts = np.unique(living_cells, return_counts=True) + return counts[True] diff --git a/exam/ex13/executor.py b/exam/ex13/executor.py new file mode 100644 index 0000000..287d53c --- /dev/null +++ b/exam/ex13/executor.py @@ -0,0 +1,11 @@ +from backend import handle_cell, after_tick_finished + +def execute_tick(model): + n, m = model.shape + + for i in range(n): + for j in range(m): + handle_cell(model, i, j) + after_tick_finished(model) + + diff --git a/exam/ex13/main.py b/exam/ex13/main.py new file mode 100644 index 0000000..bb56ed6 --- /dev/null +++ b/exam/ex13/main.py @@ -0,0 +1,25 @@ +import matplotlib.pyplot as plt +import numpy as np + +from model import prepare_model +from backend import total_living_cells +from executor import execute_tick + +def cells_alive_at_ticks(model, ticks): + for i in range(ticks): + execute_tick(model) + cell_count = total_living_cells(model) + yield i, cell_count + +plot_data = { + 0.3 : ("r", "p=0.3") + , 0.5: ("g", "p=0.5") + , 0.7: ("b", "p=0.7") +} + +for p, (color, label) in plot_data.items(): + model = prepare_model(100, p) + living_cells = np.array(list(cells_alive_at_ticks(model, 1000))) + plt.plot(living_cells[:, 0], living_cells[:, 1], color, label=label) + +plt.show() diff --git a/exam/ex13/model.py b/exam/ex13/model.py new file mode 100644 index 0000000..80ebc84 --- /dev/null +++ b/exam/ex13/model.py @@ -0,0 +1,13 @@ +import numpy as np + +def prepare_model(n, p): + """ + p must be between 0 and 1. + """ + + return (np.random.rand(n, n) < p).astype(np.int8) + + + + +