> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.SvZIRjMIwhS/rev.2619
 * 
 * authors: 
 *   Oliver Skanberg-Tippen
 *   

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



CellGrid grid;
int WIDTH = 33;
int HEIGHT = 20;
int CELL_SIZE = 15;

/*
 * Part of processing.js - runs once
 */
void setup()
{
    grid = new CellGrid(WIDTH, HEIGHT);
    //set the size of the canvas
    size(WIDTH * CELL_SIZE, HEIGHT * CELL_SIZE); 
    // smooth edges
    smooth();
    // limit the number of frames per second
    frameRate(5);
    // set the width of the line. 
    strokeWeight(3);
    //draw the background to 80 grey
    background(80);
}

/*
 * Part of processing.js - runs repeatedly
 */
void draw()
{
    //draw the grid
    print();
    //calculate the next step
    calcNext();
    //step the grid to its next state
    grid.stepGrid();
}

/*
 * For each cell, check the number of alive neighbours
 * if less than two (under-population) cell dies
 * if more than two (overcrowding) cell dies (this is more strict than Conway's)
 */
void calcNext()
{
    int neighbours = 0;
    for (int x = 0; x < WIDTH; x++) {
        for (int y = 0; y < HEIGHT; y++) {
            neighbours = grid.getAliveNeighbours(x, y);
            if (neighbours < 2 || neighbours > 2) {
                grid.setCellNState(x, y, false);
            } else {
                grid.setCellNState(x, y, true);
            }
        }
    }
}

/*
 * For each cell, check its state; colour (230, 230, 230) if alive
 * and 80 grey if not alive.
 */
void print()
{
    for (int x = 0; x < WIDTH; x++) {
        for (int y = 0; y < HEIGHT; y++) {
            if (grid.getCellState(x, y) == true) {
                //set fill to RGB 230, 230, 230
                fill(230, 230, 230);
                //draw the rectangle
                rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
            } else {
                fill(80);
                rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
            }
        }
    }      
}

/*
 * Part of processing.js - called if mouse pressed within the canvas
 */
void mousePressed()
{
    grid.reset();
}

class CellGrid
{
    Cell[][] grid;
    int HEIGHT;
    int WIDTH;
        
    CellGrid(int width, int height)
    {
        this.HEIGHT = height;
        this.WIDTH = width;
        //instantiate grid with set height and width
        grid = new Cell[WIDTH][HEIGHT];
        for (int x = 0; x < WIDTH; x++) {
            for (int y = 0; y < HEIGHT; y++) {
                grid[x][y] = new Cell();
            }
        }
    }
        
    boolean getCellState(int x, int y)
    {
        return grid[x][y].isAlive();
    }
        
    void setCellState(int x, int y, boolean isAlive)
    {
        grid[x][y].setAlive(isAlive);
    }
    
    /*
     * Repopulate the grid with new cells,
     */
    void reset()
    {
        for (int x = 0; x < WIDTH; x++) {
            for (int y = 0; y < HEIGHT; y++) {
                grid[x][y] = new Cell();
            }
        }
    }

    /*
    * Set the next state of a given cell
    */
    void setCellNState(int x, int y, boolean isAlive)
    {
        grid[x][y].setNAlive(isAlive);
    }
        
    /*
     * For each cell, update to next state
     */
    void stepGrid()
    {
        for (Cell[] row: grid) {
            for (Cell c: row) {
                c.step();
            }
        }
    }
        
    /*
     * Count and return the number of alive neighbours of
     * a given cell
     */
    int getAliveNeighbours(int x, int y)
    {
        int tmp = 0;
        //y-1, y+1, x-1, x+1, taking into account 'wrapping around'
        int ym, yp, xm, xp;
        if (y == 0) {
            ym = HEIGHT - 1;
            yp = 1;
        } else {
            //checking above the width and height limit will check the opposite
            //side of the grid - as if grid is 3d and wraps around
            ym = (y-1) % HEIGHT;
            yp = (y+1) % HEIGHT;
        }
        if (x == 0) {
            xm = WIDTH - 1;
            xp = 1;
        } else {
            xm = (x-1) % WIDTH;
            xp = (x+1) % WIDTH;
        }
        //increment tmp for each appropriate live cell
        tmp += getCellState(xm, ym) ? 1 : 0;
        tmp += getCellState(xm, y) ? 1 : 0;
        tmp += getCellState(xm, yp) ? 1 : 0;
        tmp += getCellState(x, ym) ? 1 : 0;
        tmp += getCellState(x, yp) ? 1 : 0;
        tmp += getCellState(xp, ym) ? 1 : 0;
        tmp += getCellState(xp, y) ? 1 : 0;
        tmp += getCellState(xp, yp) ? 1 : 0;
        return tmp;
    }
}


class Cell
{
    //alive now, alive next step
    boolean alive, nAlive;
       
    Cell()
    {
        //random chance of being alive when instantiated
        if (Math.random() < 0.03) {
            this.alive = true;
        } else {
            this.alive = false;
        }
        this.nAlive = false;
    }
        
    boolean isAlive()
    {
        return alive;
    }
        
    void setAlive(boolean state)
    {
        alive = state;
    }
        
    /*
     * Set whether alive in the next step
     */
    void setNAlive(boolean state)
    {
        nAlive = state;
    }
        
    /*
     * Step the cell to next state
     */
    void step()
    {
        alive = nAlive;
    }
}