game-of-life-controlnet / game_of_life.py
jerpint's picture
first commit
ea2f505
import numpy as np
from scipy import signal
from PIL import Image
from utils import generate_image_from_grid
class GameOfLife:
def __init__(self):
"""Initialize the game.
dim: dimensions of the board
p: probability of a cell being dead at init
seed: (optional) for reproducibility, set to None for a new random state
init_state: (optional) a np.array grid to start the game with
"""
self.kernel = [
[1, 1, 1],
[1, 0, 1],
[1, 1, 1],
]
self.kernel = np.ones((3, 3))
self.kernel[1, 1] = 0
self.game_history = []
self.state = None
self.step_counter = 0
def set_random_state(self, dim=(100, 100), p=0.5, seed=None):
if seed:
np.random.seed(seed)
self.state = (np.random.random(dim) < p).astype("int")
self.game_history.append(self.state.copy())
def set_empty_state(self, dim=(100, 100)):
self.state = np.zeros(dim)
self.game_history.append(self.state.copy())
def set_state_from_array(self, array):
self.state = array.copy()
self.game_history.append(self.state.copy())
def count_neighbors(self):
"""
Count the number of live neighbors each cell in self.state has with convolutions.
"""
self.neighbors = signal.convolve2d(
self.state, self.kernel, boundary="fill", fillvalue=0, mode="same"
).astype("int")
def place_blob(self, blob, i, j):
"""Place a blob at coordinates i,j
blob: ndarray of zeros and ones
i: int
j: int
"""
try:
self.state[i : i + blob.shape[0], j : j + blob.shape[1]] = blob
except:
print("Check bounds of box vs size of game!")
def step(self):
"""Update the game based on conway game of life rules"""
# Count the number of neighbors via convolution
self.count_neighbors()
# Copy of initial state
self.new_state = self.state
# Rebirth if cell is dead and has three live neighbors
self.new_state += np.logical_and(self.neighbors == 3, self.state == 0)
# Death if cell has less than 2 neighbors
self.new_state -= np.logical_and(self.neighbors < 2, self.state == 1)
# Death if cell has more than 3 neighbors
self.new_state -= np.logical_and(self.neighbors > 3, self.state == 1)
# Update game state
self.state = self.new_state
# Save state to history
self.game_history.append(self.state.copy())
# Update step counter
self.step_counter += 1
def generate_n_steps(self, n):
for _ in range(n):
self.step()
if np.array_equal(self.game_history[-1], self.game_history[-2]):
# If the game is stable, break
break
def generate_image(self, grid: np.array, img_size: int = 512) -> Image:
return generate_image_from_grid(grid, img_size)