# L-15 MCS 507 Mon 25 Sep 2023 : run_game_of_life.py

"""
This script provides a basic implementation of the game of life
of John Conway, without the visualization (see lecture 29),
in order to time the cost of searching through numpy arrays.
We do as many updates as the dimension of the matrix.
"""

from time import perf_counter
import numpy as np

def neighbors(alive, i, j):
    """
    Returns #cells alive next to alive[i,j].
    """
    cnt = 0
    if(i > 0):
        if alive[i-1, j]:
            cnt = cnt + 1
        if(j > 0):
            if alive[i-1, j-1]:
                cnt = cnt + 1
        if(j < alive.shape[1]-1):
            if alive[i-1, j+1]:
                cnt = cnt + 1
    if(i < alive.shape[0]-1):
        if alive[i+1, j]:
            cnt = cnt + 1
        if(j > 0):
            if alive[i+1, j-1]:
                cnt = cnt + 1
        if(j < alive.shape[1]-1):
            if alive[i+1, j+1]:
                cnt = cnt + 1
    if(j > 0):
        if alive[i, j-1]:
            cnt = cnt + 1
    if(j < alive.shape[1]-1):
        if alive[i, j+1]:
            cnt = cnt + 1
    return cnt

def update(alive):
    """
    Applies the rules of Conway's game of life.
    """
    result = np.zeros(alive.shape, int)
    for i in range(0, alive.shape[0]):
        for j in range(0, alive.shape[1]):
            nbn = neighbors(alive, i, j)
            if(alive[i, j] == 1):
                if((nbn < 2) or (nbn > 3)):
                    result[i, j] = 0
                else:
                    result[i, j] = 1
            else:
                if(nbn == 3):
                    result[i, j] = 1
    return result

def main():
    """
    Generates a random matrix and
    applies the rules for Conway's
    game of life.
    """
    ratio = 0.2  # ratio of nonzeroes
    dim = 200    # dimension of the matrix
    alive = np.random.rand(dim, dim)
    alive = np.matrix(alive < ratio, int)
    start_time = perf_counter()
    for i in range(dim):
        alive = update(alive)
    stop_time = perf_counter()
    elapsed = stop_time - start_time
    print('elapsed time', elapsed, 'seconds')

main()
