gameoflife_utils.py

Created by naul

Created on March 11, 2023

7.82 KB


# Type y#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

# Path of ffmpeg executable for animation
plt.rcParams['animation.ffmpeg_path'] = r'/Volumes/Data/Youtube/[ffmpeg]/ffmpeg'

###############################################################################

def evolve(X):
    ''' Evolves a board of Game of Life for one turn '''
    # Dead cells as a boundary condition
    # Count neighbours
    # Alive if 3 neighbours or 2 neighbours and already alive
    
    Xi = X.astype(int)
    neigh = np.zeros(Xi.shape)
    neigh[1:-1,1:-1] = (Xi[:-2,:-2]  + Xi[:-2,1:-1] + Xi[:-2,2:] + 
                        Xi[1:-1,:-2] +                Xi[1:-1,2:]  + 
                        Xi[2:,:-2]   + Xi[2:,1:-1]  + Xi[2:,2:]) 
    
    return np.logical_or(neigh==3,np.logical_and(Xi==1,neigh==2))

###############################################################################

def get_history(B,T):
    ''' Returns the evolution of a board B after T generations '''
    history = np.zeros((T,B.shape[0], B.shape[1]),dtype=bool)
    for t in range(T):
        history[t,:,:] = B
        B = evolve(B)   
        print(t)
    return history    

###############################################################################
    
def plotcells(X, filename=False):
    ''' Plots a board of Game of Life + optionally saving the figure '''
    LW = 0.5
    if(X.shape[0]>200): 
        USE_IMSHOW = True
    else:
        USE_IMSHOW = False
        
    fig = plt.figure(figsize=(16,9),dpi=120)    
    if USE_IMSHOW == False:    
        # Light blue lines as cells boundaries
        plt.pcolor(X.T, cmap="gray_r",
                   edgecolors='cadetblue', linewidths=LW)
    else:
        plt.imshow(X[:,::-1].T, cmap="gray_r")        
    plt.gca().get_xaxis().set_visible(False)
    plt.gca().get_yaxis().set_visible(False)
    fig.tight_layout()
    
    if (filename != False): 
        plt.savefig(filename,dpi=90)
    else:
        plt.show()
    
###############################################################################
    
def makeMovie(history,filename,trim=False):
    ''' Create the movie from a history of a game of life'''
    # History is the boolean history (non inverted i.e. True = alive)
    # Inversion is done in the colormap
    # Filename should be *.mp4
    
    FIGSIZE = (16,9)
    DPI = 240
    LW = 0.5
    
    
    if(history.shape[1]>200): 
        USE_IMSHOW = True
    else:
        USE_IMSHOW = False
        
    # Trim boundaries
    if trim: 
        history = history[:,3:-3,3:-3]
    
    # Create the plot and its starting point
    print("Create initial plot")
    my_cmap = plt.get_cmap('gray_r')
    fig = plt.figure(figsize=FIGSIZE,dpi=DPI)
    ax = fig.add_subplot(111)
    
    if USE_IMSHOW == False:
    # First option : use pcolor
        pc = ax.pcolor(history[0,:,:].T, cmap=my_cmap,
                       edgecolors='cadetblue', linewidths=LW)
    else:        
    # Second option : use imshow
        im  = ax.imshow(history[0,:,::-1].T, cmap=my_cmap)
    
    cnt = ax.text(0.01, 0.99, str(0),color='red', fontsize=30,
            verticalalignment='top', horizontalalignment='left',
            transform=ax.transAxes)
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    fig.tight_layout()
    
    
    # The function as it is called at the n-th iteration
    # It directly modifies the data within the image
    def update_img(n):
        # Revert and scale from 0-1 to 0-255
        print('Frame '+str(n))
        if USE_IMSHOW == False:
            new_color = my_cmap(255*history[n,:,:].T.ravel()) 
            pc.update({'facecolors':new_color})
        else:
            im.set_data(history[n,:,::-1].T)
        #
        cnt.set_text(str(n))
    #    # if needed, can modify the field of view
    #    fov = 
    #    ax.set_xlim()
    #    ax.set_ylim()
    
        return True
    
    # Create the animation and save it
    print("Make animation")
    ani = animation.FuncAnimation(fig, update_img, history.shape[0], 
                                  interval=30) # 30ms per frame
    writer = animation.FFMpegWriter(fps=30, bitrate=5000)
    print("Save movie")
    ani.save(filename, writer = writer, dpi=DPI) 
    print("Saved")
    
###############################################################################

def readRLE_OLD(filename, Bshape=(50,50), position = (10,10), rH=False,rV=False):
    
    ''' Read the RLE file and returns a binary matrix '''
    # see http://www.conwaylife.com/wiki/RLE 
    
    # Open file and cast it into a unique string
    f = open(filename,"r")
    s = ''
    while True:
        l = f.readline()
        if l == '':             # Empty indicates end of file. An empty line would be '\n'
            break
        if l[0] =='#':
            continue
        if l[0] =='x':
            continue
        s = s + l[:-1]   # To remove EOL
    f.close()
    
        
    # Create matrix
    B = np.zeros(Bshape).astype(bool)
    initX, initY = position
    
    # We parse each character and decide accordingly what to do
    # If the character is a digit, we keep going until we reach 'b' or 'o'
    curX, curY = initX, initY
    qs = ''
    for c in s:
    
        # End of file
        if c=='':  
            break
        
        # Next Line
        if c=='$':
            q = 1 if qs=='' else int(qs)
            curY += q
            curX = initX
            qs = ''
        
        # Digit (check ascii code for a digit from 0 to 9)
        if ord(c)>47 and ord(c)<58:  #
            qs = qs + c
            
        # Alive (o) or Dead (b) cell    
        if c == 'b' or c=='o':
            q = 1 if qs=='' else int(qs)
            for i in range(q):
                B[curX, curY] = False if c=='b' else True
                curX += 1
            qs = ''
    
    if rV:
        B=B[:,::-1]
    if rH:
        B=B[::-1,:]    
        
    return B.astype(bool)

###############################################################################
    
def readRLE(filename, Cshape=(50,50), position = (10,10), rH=False,rV=False,tp=False):
    
    ''' Read the RLE file and returns a binary matrix '''
    # see http://www.conwaylife.com/wiki/RLE 
    
    # Open file and cast it into a unique string
    f = open(filename,"r")
    s = ''
    while True:
        l = f.readline()
        if l == '':             # Empty indicates end of file. An empty line would be '\n'
            break
        if l[0] =='#':
            continue
        if l[0] =='x':
            continue
        s = s + l[:-1]   # To remove EOL
    f.close()
    
        
    # Create matrix
    SHAPE_MAX = (2500,2500)
    B = np.zeros(SHAPE_MAX).astype(bool)
    
    # We parse each character and decide accordingly what to do
    # If the character is a digit, we keep going until we reach 'b' or 'o'
    curX, curY = 0, 0
    qs = ''
    for c in s:
    
        # End of file
        if c=='':  
            break
        
        # Next Line
        if c=='$':
            q = 1 if qs=='' else int(qs)
            curY += q
            curX = 0
            qs = ''
        
        # Digit (check ascii code for a digit from 0 to 9)
        if ord(c)>47 and ord(c)<58:  #
            qs = qs + c
            
        # Alive (o) or Dead (b) cell    
        if c == 'b' or c=='o':
            q = 1 if qs=='' else int(qs)
            for i in range(q):
                B[curX, curY] = False if c=='b' else True
                curX += 1
            qs = ''
    
    
    
    
    
    posX, posY = position
    BshapeY=max(np.where(sum(B)>0)[0])+1
    BshapeX=max(np.where(sum(B.T)>0)[0])+1
    
    B = B[0:BshapeX,0:BshapeY]
    
    if rV:
        B=B[:,::-1]
    if rH:
        B=B[::-1,:]
    if tp:
        B=B.T
    
    C = np.zeros(Cshape)
    C[posX:(posX+B.shape[0]),posY:(posY+B.shape[1])] = np.copy(B)
    
    return C.astype(bool)our text here

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.