> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.CZFhvmJx$BE/rev.1288
 * 
 * authors: 
 *   
 *   Michal Wallace
 *   Sean Siem
 *   
 *   

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



// This sketch builds on a prior work, "ConwayGameOfLife", created Michal Wallace & Sean Siem
// http://studio.sketchpad.cc/sp/pad/view/ro.9$PLBJ$HcoHs4/rev.2369



//* Conway's Game of Life Mashed Up with Go
//* (as implemented in processing by Michal Wallace on 5/19/2011)


final int kGridW = 50;
final int kGridH = 50;
final int kCellW = 10;
final int kCellH = 10;
final int kMaxAge = 15;

final int kR = 0x10;
final int kG = 0x20;
final int kB = 0x40;

int[][] gGrid = new int[kGridW][kGridH];
int[][] gNext = new int[kGridW][kGridH];

int randColor()
{
    int r = int(random(3));
    if (0 == r) return kR +1;
    if (1 == r) return kG +1;
    return kB + 1;
}

void setup()
{
    // processing could do it this way, but it  breaks sketchpad:
    //size(kGridW * kCellW, kGridH * kCellH + 50);
    size(500, 500);
    frameRate(10);
    
    // initialize to a random grid:
    for (int y = 0; y < kGridH; ++y)
    {
        for (int x = 0; x < kGridW; ++x)
        {
            gGrid[y][x] = (random(1) > 0.5) ? randColor() : 0;
            gNext[y][x] = 0;
        }
    }
}

// I want to color-code the cells by age, so the value will
// be the age of the cell.
int valueAt(int x, int y)
{
    // assume anything outside is dead
    if ((x < 0) 
       || (x >= kGridW)
       || (y < 0)
       || (y >= kGridH))
    {
        return 0;
    }
    else
    {
        return gGrid[y][x];
    }
}

int colorAt(int x, int y)
{
    int v = gGrid[y][x];
    int c = v & 0xF0;
    if (c == kR) return kR;
    if (c == kG) return kG;
    if (c == kB) return kB;
    return 0;
}


class NeighborCount
{
    int r = 0;
    int g = 0;
    int b = 0;
    int n = 0;
    
    NeighborCount(int x, int y)
    {
        count(x, y - 1);      // N
        count(x + 1, y - 1);  // NE
        count(x + 1, y);      // E
        count(x + 1, y + 1);  // SE
        count(x, y + 1);      // S
        count(x - 1, y + 1);  // SW
        count(x - 1, y);      // W
        count(x - 1, y - 1);  // NW
    }
    
    void count(int x, int y)
    {
        int v = valueAt(x, y);
        if (v > 0)
        {
            this.n += 1;
            switch(colorAt(x, y))
            {
                case kR:
                    this.r += 1;
                    break;
                case kG:
                    this.g += 1;
                    break;
                case kB:
                    this.b += 1;
                    break;
                default:
                    break;
            }
        }
    }
    
    int dominantColor()
    {
        int c = 0;
        if (this.r >= 2) { c = kR; }
        if (this.g >= 2) { c = kG; }
        if (this.b >= 2) { c = kB; }
        return c;
    }
}

void copyGrid(int[][] fromGrid, int[][] toGrid)
{
   for (y = 0; y < kGridH; ++y)
    {
        for (x = 0; x < kGridW; ++x)
        {
            toGrid[y][x] = fromGrid[y][x];
        }
    }
}

// Rules of Life:
// --------------------
// 1. die if neighbors < 2
// 2. stay alive if 2 or 3 neighbors
// 3. die if neigbors > 3
// 4. come alive if exactly 3 live neighbors
// 5. groups of cells randomly generate in one of 4 colors - RGBW
// 6. Clicking inside the gamespace colors a single cell to player's color (ATM - Green, but player will be able to select color later).
// 7. Game goal is to clear other colors from gamespace.
// 8. 




void applyRules()
{
    int x;
    int y;
    NeighborCount neighbors;
    
    for (y = 0; y < kGridH; ++y)
    {
        for (x = 0; x < kGridW; ++x)
        {
            // start with a clean copy of the grid:
            gNext[y][x] = 0;
                       
            neighbors = new NeighborCount(x, y);
            
            // if alive:
            if (valueAt(x, y) > 0)
            {
                if ((neighbors.n < 2) || (neighbors.n > 3))
                {
                    gNext[y][x] = 0; // die
                }
                else
                {
                    // get older:
                    int age = gGrid[y][x] & 0x0F;
                    int rgb = gGrid[y][x] & 0xF0;
                    age = constrain(age + 1, 0, kMaxAge);
                    gNext[y][x] = rgb + age;
                }
            }
            else if (3 == neighbors.n)
            {
                gNext[y][x] = neighbors.dominantColor() + 1; // birth
            }
        }
    }
    copyGrid(gNext, gGrid);
}


void draw()
{
    int x;
    int y;

    applyRules();

    // reset colors
    background(0);
    stroke(color(0, 0, 0));
    fill(color(255, 255, 255));
    
    // draw the grid:
    for (y = 0; y < kGridH; ++y)
    {
        for (x = 0; x < kGridW; ++x)
        {
            if (0 != gGrid[y][x])
            {
                drawCell(x, y);
            }
        }
    }
}

void drawCell(int x, int y)
{
    int cell = gGrid[y][x];
    int rgb = 0xF0 & cell;
    int age = 0x0F & cell;
    
    int shade = 256 - (8 * age);
    
    if (rgb == kR)
        fill(color(shade, 0, 0));
    else if (rgb == kG)
        fill(color(0, shade, 0));
    else if (rgb == kB)
        fill(color(0, 0, shade));
    else
        fill(color(shade, shade, shade));
        
    rect(x * kCellW, y * kCellH, kCellW, kCellH);
}


// paint with the mouse:

void mouseDragged()
{
    onMouse();
}

void mousePressed()
{
    onMouse();
}

void mouseClicked()
{
    onMouse();
}

void onMouse()
{
    int cellX = int(mouseX / kCellW);
    int cellY = int(mouseY / kCellH);
    if (cellY < kGridH)
    {
        gGrid[cellY][cellX] = kG + 1;
        drawCell(cellX, cellY);
    }
}