Sunday, April 22, 2012

Conway's Game of Life

So this dude, Conway, made a "zero-player" game because he's a mathematician and that's a good enough reason. He thinks a living cell dies when it's lonely or overpopulated. Humans just get severely annoyed.
Anyway, I implemented this game in C++ and OpenGL(glut) for a low-input-high-output semester project. Some call me lazy and I respect the opinions of others.
Constructive criticism is welcome.

// g++ ConwayWork.cpp -lglut
#include <iostream>
#include <GL/glut.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define ROW 100
#define COLUMN 100
int x_pos = 0, y_pos = 0, W, H;
bool gen = false;
void timerDraw(int foo);
void initGL()        //Initialize with the background color
{
   glClearColor(0.08, 0.08, 0.08, 1.0);
}
class Life
{
private:
    bool state;        //True is alive. False is dead.
public:
    Life()
    {
        state = false;
    }
    void make()
    {
        state = true;
    }
    void kill()
    {
        state = false;
    }
    bool isAlive()
    {
        return state;
    }
    friend void mouse(int button, int state, int x, int y);
    friend void keyboard(unsigned char a,int b,int c);
};
class World
{
private:
    Life colony[COLUMN][ROW];
public:
    World()
    {
        colony[3][3].make();
        colony[4][4].make();
        colony[5][4].make();
        colony[5][3].make();
        colony[5][2].make();
    }
    void populateRandom()
    {
        for(int i = 0; i < COLUMN; i++)
            for(int j = 0; j < ROW; j++)
                if(rand()%2)
                    colony[i][j].make();
                else
                    colony[i][j].kill();
    }
    void clear()
    {
        for(int i = 0; i < COLUMN; i++)
            for(int j = 0; j < ROW; j++)
                colony[i][j].kill();
    }
    void judgment()
    {
        World theDeparted;
        for(int i = 0; i < COLUMN; i++)
            for(int j = 0; j < ROW; j++)
                if(!colony[i][j].isAlive() && neighbours(i, j) == 3)    //If dead and 3 neighbours, create.
                    theDeparted.colony[i][j].make();
                else if(colony[i][j].isAlive() && (neighbours(i, j) == 2 || neighbours(i, j) == 3) )    //If alive and two or 3 neighbours, continue to live.
                    theDeparted.colony[i][j].make();
                else                    //Anything else, kill.
                    theDeparted.colony[i][j].kill();
        *this = theDeparted;
    }
    int neighbours(int i, int j)
    {
        int n = 0;
        if(i > 0 && j > 0 && i < COLUMN-1 && j < ROW-1)        //Inside
        {
            if(colony[i-1][j-1].isAlive())    //Down left
                n++;
            if(colony[i-1][j].isAlive())        //Left
                n++;
            if(colony[i-1][j+1].isAlive())    //Up left
                n++;
            if(colony[i][j+1].isAlive())        //Up
                n++;
            if(colony[i+1][j+1].isAlive())    //Up right
                n++;
            if(colony[i+1][j].isAlive())        //Right
                n++;
            if(colony[i+1][j-1].isAlive())    //Down right
                n++;
            if(colony[i][j-1].isAlive())        //Down
                n++;
            return n;
        }
        else if(i == 0 && j == 0)        //Lower left corner
        {
            if(colony[i][j+1].isAlive())        //Up
                n++;
            if(colony[i+1][j+1].isAlive())    //Up right
                n++;
            if(colony[i+1][j].isAlive())        //Right
                n++;
            return n;
        }
        else if(i == 0 && j == ROW-1)        //Top left corner
        {
            if(colony[i+1][j-1].isAlive())    //Down right
                n++;
            if(colony[i][j-1].isAlive())        //Down
                n++;
            if(colony[i+1][j].isAlive())        //Right
                n++;
            return n;
        }
        else if(i == COLUMN-1 && j == ROW-1)    //Top right corner
        {
            if(colony[i-1][j-1].isAlive())    //Down left
                n++;
            if(colony[i-1][j].isAlive())        //Left
                n++;
            if(colony[i][j-1].isAlive())        //Down
                n++;
            return n;
        }
        else if(i == COLUMN-1 && j == 0)    //Lower right corner
        {
            if(colony[i-1][j].isAlive())        //Left
                n++;
            if(colony[i-1][j+1].isAlive())    //Up left
                n++;
            if(colony[i][j+1].isAlive())        //Up
                n++;
            return n;
        }
        else if(i == 0)            //Left column
        {
            if(colony[i][j+1].isAlive())        //Up
                n++;
            if(colony[i+1][j+1].isAlive())    //Up right
                n++;
            if(colony[i+1][j].isAlive())        //Right
                n++;
            if(colony[i+1][j-1].isAlive())    //Down right
                n++;
            if(colony[i][j-1].isAlive())        //Down
                n++;
            return n;
        }
        else if(j == 0)        //Bottom row
        {
            if(colony[i-1][j].isAlive())        //Left
                n++;
            if(colony[i-1][j+1].isAlive())    //Up left
                n++;
            if(colony[i][j+1].isAlive())        //Up
                n++;
            if(colony[i+1][j+1].isAlive())    //Up right
                n++;
            if(colony[i+1][j].isAlive())        //Right
                n++;
            return n;
        }
        else if(i == COLUMN-1)        //Right column
        {
            if(colony[i-1][j-1].isAlive())    //Down left
                n++;
            if(colony[i-1][j].isAlive())        //Left
                n++;
            if(colony[i-1][j+1].isAlive())    //Up left
                n++;
            if(colony[i][j+1].isAlive())        //Up
                n++;
            if(colony[i][j-1].isAlive())        //Down
                n++;
            return n;
        }
        else if(j == ROW-1)        //Top row
        {
            if(colony[i-1][j-1].isAlive())    //Down left
                n++;
            if(colony[i-1][j].isAlive())        //Left
                n++;
            if(colony[i+1][j].isAlive())        //Right
                n++;
            if(colony[i+1][j-1].isAlive())    //Down right
                n++;
            if(colony[i][j-1].isAlive())        //Down
                n++;
            return n;
        }
    }
    friend void draw();
    friend void mouse(int button, int state, int x, int y);
    friend void keyboard(unsigned char a,int b,int c);
};
    World pluto;    //Create object.

void draw()
{
    static double color1 = 0.03, color2 = 1, color3 = 0;    //Colors for decoration only.
    glClear(GL_COLOR_BUFFER_BIT);
    for(int i = 0; i < COLUMN; i++)
    {
        for(int j = 0; j < ROW; j++)
            if(pluto.colony[i][j].isAlive())
            {
                glBegin(GL_POLYGON);        //Draw life square, size 10.
                glColor3d(1, color2-color1, color1+color2);
                glVertex2d(i*10,j*10);
                glVertex2d(i*10+10,j*10);
                glVertex2d(i*10+10,10*j-10);
                glVertex2d(i*10,j*10-10);
                glEnd();
                //cout<<"@";        //Cout for command line output, this slows the program considerably.
            }
            //else  
                //cout<<" ";
    //    cout<<endl;
    }

    glBegin(GL_POLYGON);        //Draw keyboard square size 10.
    glColor3d(1, color3, 0);
    glVertex2d(x_pos*10,y_pos*10);
    glVertex2d(x_pos*10+10,y_pos*10);
    glVertex2d(x_pos*10+10,10*y_pos-10);
    glVertex2d(x_pos*10,y_pos*10-10);
    glEnd();
    glutSwapBuffers();
    if(gen)
    {
        pluto.judgment();        //Show generation changes.
        color1 += 0.01;
        color2 -= 0.01;
        color3 = 1;
    }
    else
        color3 = 0;
    glutTimerFunc(200,timerDraw,0);
}
void reshape(int w, int h)
{
    W = w;        //W and H for use by mousefunction only.
    H = h;
    glClear(GL_COLOR_BUFFER_BIT);
    glViewport(0,0,w,h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-10.0,COLUMN*10+10,-10.0,ROW*10+10,-1,1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
void mouse(int button, int state, int x, int y)
{
    //Origin of window is top left, origin of OpenGL is bottom left. Y values thus corrected.
    if(button == GLUT_LEFT_BUTTON && state == GLUT_UP && x*COLUMN/W > 0 && x*COLUMN/W < COLUMN && (H-y)*ROW/H > 0 && (H-y)*ROW/H < ROW)
    {
        if(pluto.colony[x*COLUMN/W][(H-y)*ROW/H].isAlive())
            pluto.colony[x*COLUMN/W][(H-y)*ROW/H].kill();
        else
            pluto.colony[x*COLUMN/W][(H-y)*ROW/H].make();
    }
}
void keyboard(unsigned char a,int b,int c)
{
    switch(a)
    {
    case 'w': case 'W':
        if(y_pos < ROW-1)
            y_pos++;
        break;
    case 'a': case'A':
        if(x_pos > 0)
            x_pos--;
        break;
    case 's': case 'S':
        if(y_pos > 0)
            y_pos--;
        break;
    case 'd': case 'D':
        if(x_pos < COLUMN-1)
            x_pos++;
        break;
    case 'x': case 'X':
        if(pluto.colony[x_pos][y_pos].isAlive())
            pluto.colony[x_pos][y_pos].kill();
        else
            pluto.colony[x_pos][y_pos].make();
        break;
    case ' ':
        gen = !gen;
        break;
    case 'r': case 'R':
        pluto.populateRandom();
        break;
    case 'c': case 'C':
        pluto.clear();
        break;
    default:
        break;
    }
}
void timerDraw(int foo)
{
    glutMouseFunc(mouse);
    glutKeyboardFunc(keyboard);
    glutPostRedisplay();
}

int main(int argc, char** argv)        //Mandatory arguments for OpenGL
{
    srand (time(NULL));        //Seed the Random number generator.
    cout<<"Use WASD keys for navigation, 'x' to alter state, 'r' for random population, 'c' to clear, Space to start.\nMouse can also be used, although it is slightly inaccurate.\n";
    glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutCreateWindow("CONWAY");
    glutReshapeFunc(reshape);
    glutDisplayFunc(draw);
    glutSwapBuffers();
    initGL();
    glutMainLoop();
    return 0;
}