"""
Implementation of the Game of Life
Scheme is similar to the SIMP/STEP standard example
Some init functions added, e.g., glider and glider gun and Cheshire cat
2010-08-28, 2017-03-27 Silvio Capobianco
"""

from simp import *

## Geometry

Y = 512
X = 512
initialize(size = [Y, X])

## Signals
onebit = SmallUInt(2)
q, p   = map(Signal, [onebit]*2)

## Local rule
def life():
    """The local rule of the Game of Life"""
    sum = q[-1,-1] + q[-1, 0] + q[-1, 1] + \
          q[ 0,-1]            + q[ 0, 1] + \
          q[ 1,-1] + q[ 1, 0] + q[ 1, 1]
    q._ = [0,0,q,1,0,0,0,0,0][sum]
    p._ = q
life_rule = Rule(life)

## Color map
red,green,blue = map(OutSignal, [UInt8]*3)

def plain():
    """Plain coloring: green = alive, black = dead"""
    green._  = q * 255
plain_rend = Renderer(Rule(plain), (red,green,blue))

def history():
    """Coloring with one-step history"""
    if   q and not p:   red._ = 255 ## Newborn
    elif q and p:     green._ = 255 ## Survivor
    elif p and not q:  blue._ = 255 ## Dead
history_rend = Renderer(Rule(history), (red,green,blue))

## User interface

ui = Console([plain_rend, history_rend])
ui.bind("STEP", life_rule)

## Random initial state

def init():
    """Uniform random initial state"""
    q[:,:] = makedist(q.shape, [1, 1])
ui.bind("I", init)

## Some classical, "funny" configs

def empty():
    """Empty initial state"""
    q[:,:] = makedist(q.shape, [1])
ui.bind("e", empty)

def glider():
    """Glider in an empty space"""
    empty()
    q[Y/2+1, X/2-1:X/2+2] = 1
    q[Y/2  , X/2+1      ] = 1
    q[Y/2-1, X/2        ] = 1
ui.bind("L", glider)

def gun():
    """Gosper's glider gun"""
    empty()
    q[Y/2        , X/2          ] = 1 ## middle
    q[Y/2-1:Y/2+2, X/2+2        ] = 1 ##
    q[Y/2        , X/2+3        ] = 1 ## part to the right
    q[Y/2-2      , X/2+1        ] = 1 ## of the middle
    q[Y/2+2      , X/2+1        ] = 1 ##
    q[Y/2-1:Y/2+2, X/2-4        ] = 1 ##
    q[Y/2-2      , X/2-3        ] = 1 ##
    q[Y/2+2      , X/2-3        ] = 1 ## part to the left
    q[Y/2-3      , X/2-2:X/2    ] = 1 ## of the middle
    q[Y/2+3      , X/2-2:X/2    ] = 1 ##
    q[Y/2-3:Y/2  , X/2+6:X/2+8  ] = 1 ##
    q[Y/2-4:Y/2+1, X/2+8        ] = 1 ## other component
    q[Y/2-5:Y/2+2, X/2+10       ] = 1 ## of the glider gun
    q[Y/2-3:Y/2  , X/2+8        ] = 0 ##
    q[Y/2-3:Y/2  , X/2+10       ] = 0 ##
    q[Y/2-1:Y/2+1, X/2-14:X/2-12] = 1 ## left block
    q[Y/2-3:Y/2-1, X/2+20:X/2+22] = 1 ## right block
ui.bind("G", gun)

def cheshire():
    """Cheshire cat: disappears leaving its smile, then a pawprint"""
    empty()
    q[Y/2        , X/2  :X/2+2] = 1 ## nose
    q[Y/2-2      , X/2-1:X/2+3] = 1 ## brow
    q[Y/2+2      , X/2-1:X/2+3] = 1 ## chin
    q[Y/2-1:Y/2+2, X/2-2      ] = 1 ## left  cheek
    q[Y/2-1:Y/2+2, X/2+3      ] = 1 ## right cheek
    q[Y/2-3      , X/2-1      ] = 1 ## left  ear
    q[Y/2-3      , X/2+2      ] = 1 ## right ear
ui.bind("C", cheshire)

## Let's go!

init()
ui.start()
