Article first posted on the Playground Advent Calendar!
Below, [wikipedia](https://ja.wikipedia.org/wiki/%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B2%E3%83%BC%E3 Quoted from% 83% A0). ~~ I think it's easier to understand if you jump to the link than to read the quote below w ~~
In the life game, the subsequent state is determined only by the initial state. There is a grid like a grid, and one grid is called a cell. Each cell has eight neighboring cells. Each cell has two states, "life" and "death", and the state of the next step (generation) of a cell is determined by the state of the surrounding eight cells in the current generation.
The life and death of a cell follows the following rules.
birth If there are exactly three living cells adjacent to a dead cell, the next generation will be born. ** Survival ** If there are two or three living cells adjacent to a living cell, they will survive in the next generation. ** Depopulated ** If there is less than one living cell adjacent to a living cell, it will die due to depopulation. ** Overcrowding ** If there are 4 or more living cells adjacent to a living cell, it will die due to overcrowding. Below is an example of life and death in the next step in the central cell. Live cells are indicated by ■, and dead cells are indicated by □.
import matplotlib.pyplot as plt
import numpy as np
n = 50
#Create a random cell
cells = np.random.randint(0, 2, (n, n), dtype=bool)
A random selection of either True
or False
creates an n × n matrix of elements. ("Life" for True
," Death "for False
)
(If you increase it to about n = 500
, it will become heavy ...)
sum_around = lambda i, j : cells[max(0, i-1):i+2, max(0, j-1):j+2].sum() - cells[i, j]
First, a 3 × 3 matrix centered on the ʻi row
j column component of the matrix is extracted, and the sum of the elements of the matrix is obtained. Subtracting the center from the result gives the number of "raw" cells adjacent to the ʻi
row j
column component. (Calculated as True = 1, False = 0
)
max
functionWhen you want to specify the cell to the left (or above) of the cell in the 0th row (or 0th column), even if ʻi -1 = -1, then
max (0, -1) = 0` Therefore, only the area inside the wall can be referred to. Python seems to be okay if the index exceeds the size of the matrix, and I didn't have to do the same for the nth row (or nth column).
def update(old):
sum_around = lambda i, j : cells[max(0, i-1):i+2, max(0, j-1):j+2].sum() - cells[i, j]
around = np.fromfunction(np.vectorize(sum_around), old.shape)
new = np.where(old , ((2 <= around) & (around <= 3)), (around == 3))
return new
By putting the sum_around
function in the first argument of the np.fromfunction
function, we create a new matrix ʻaroundwhere ** the value of each element = the number of adjacent" raw "cells **. When each element is
True, that is, when it is a" raw "cell, the judgment result of ** survival / depopulation / overcrowding ** is returned, and when it is
False, that is, when it is a "death" cell, the judgment result is returned. Returns the judgment result of ** birth **. The return value is a
bool` type, which will be the next generation cell as it is.
np.vectorize
functionIt seems that the function of the first argument needs to be a universal function (ufunc).
Parameters :
function : callable The function is called with N parameters, where N is the rank of shape. Each parameter represents the coordinates of the array varying along a specific axis. For example, if shape were (2, 2), then the parameters would be array([[0, 0], [1, 1]]) and array([[0, 1], [0, 1]]) numpy.fromfunction
Since the argument of the sum_around
function assumes the ʻint type, it is ufunced with
np.vectorizeso that it can receive the
ndarray` type.
while True:
# for _ in range(200): #If you want to specify the number of times
cells = update(cells)
plt.imshow(cells)
plt.pause(.01) # 0.Updated every 01 seconds
plt.cla() #Without this, it gets heavier and heavier
from matplotlib.colors import LinearSegmentedColormap
colors = ['green', 'red']
xmas = LinearSegmentedColormap.from_list('xmas', colors)
norm = plt.Normalize(0, 1)
plt.tick_params(labelbottom=False, labelleft=False,
bottom=False, left=False)
You can change to the specified color by changing the inside of the while statement to plt.imshow (cells, cmap = xmas, norm = norm)
.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
n = 15
cells = np.zeros([n, n], dtype=bool)
#Galaxy
cells[3:9, 3:5] = True
cells[3:5, 6:12] = True
cells[-5:-3, 3:9] = True
cells[6:12, -5:-3] = True
#random
# cells = np.random.randint(0, 2, (n, n), dtype=bool)
def update(old):
sum_around = lambda i, j : cells[max(0, i-1):i+2, max(0, j-1):j+2].sum() - cells[i, j]
around = np.fromfunction(np.vectorize(sum_around), old.shape, dtype=int)
new = np.where(old , ((2 <= around) & (around <= 3)), (around == 3))
return new
colors = ['green', 'red']
xmas = LinearSegmentedColormap.from_list('xmas', colors)
norm = plt.Normalize(0, 1)
plt.tick_params(labelbottom=False, labelleft=False,
bottom=False, left=False)
# for _ in range(200):
while True:
cells = update(cells)
plt.imshow(cells, cmap=xmas, norm=norm)
plt.pause(.01)
plt.cla()
It's probably the first time since I started programming that something I made by myself from scratch worked ... I did it (urgent recruitment: verbalization of joy)
Recommended Posts