function Cell(x, y) 
{ 
    this.column = x; 
    this.row = y; 
 
    this.currentState = false; 
    this.nextState = false; 
    this.neighbours = 0; 
 
    this.Position = function() { return this.column + 'x' + this.row; } 
    this.IsChanged = function() { return this.nextState != this.currentState; } 
    this.SetChanged = function() { this.currentState = this.nextState; } 
    this.SetNeighbours = function(amount) 
    { 
        if (this.currentState == true) 
        { 
            if (amount != 2 && amount != 3) this.nextState = false; 
            else this.nextState = true; 
        } 
        else 
        { 
            if (amount == 3) this.nextState = true; 
            else this.nextState = false; 
        } 
 
        this.neighbours = amount; 
    }; 
} 
 
function GameOfLifeRenderer(container, columns, rows) 
{ 
    this.columns = 50; 
    this.rows = 30; 
 
    if (typeof (columns) == 'number' && columns >= 50) this.columns = columns; 
    if (typeof (rows) == 'number' && rows >= 30) this.rows = rows
 
    this.gol = new GameOfLifeLogic(this.columns, this.rows); 
 
    var htmlString = '<table id="grid" class="big">'; 
    for (var j = 0; j < this.rows; j++) 
    { 
        htmlString += '<tr>'; 
        for (var i = 0; i < this.columns; i++) 
        { 
            htmlString += '<td id="' + i + 'x' + j + '"></td>'; 
        } 
        htmlString += '</tr>'; 
    } 
 
    htmlString += '</table>'; 
 
    $('#' + container).html(htmlString); 
 
    $('#container').width($('#grid').width()); 
} 
 
GameOfLifeRenderer.prototype.SetShape = function(shapeName) 
{ 
    this.gol.LoadShape(shapeName); 
    $('#grid tbody tr td').removeClass('full'); 
    this.DrawInit(); 
} 
 
GameOfLifeRenderer.prototype.DrawInit = function() 
{ 
    this.Draw(); 
} 
 
GameOfLifeRenderer.prototype.DrawNext = function() 
{ 
    this.gol.Advance(); 
    this.Draw(); 
} 
 
GameOfLifeRenderer.prototype.Draw = function() 
{ 
    for (var i = 0; i < this.columns; i++) 
    { 
        for (var j = 0; j < this.rows; j++) 
        { 
            if (this.gol.shape[i][j].IsChanged()) 
            { 
                $('#grid tbody tr td#' + this.gol.shape[i][j].Position()).toggleClass('full'); 
                this.gol.shape[i][j].SetChanged(); 
            } 
        } 
    } 
} 
 
GameOfLifeRenderer.prototype.ClickCell = function(cell) 
{ 
    if (cell.id.indexOf('x') > -1) 
    { 
        var coords = cell.id.split('x'); 
        if (coords.length == 2) 
        { 
            var x = coords[0]; 
            var y = coords[1]; 
 
            if (!isNaN(x) && !isNaN(y)) 
            { 
                this.gol.ClickCell(x, y); 
                $(cell).toggleClass('full'); 
            } 
        } 
    } 
} 
 
function GameOfLifeLogic(cols, rows) 
{ 
    this.columns = cols; 
    this.rows = rows; 
 
    this.LoadShape('glider'); 
} 
 
GameOfLifeLogic.prototype.LoadShape = function(shapeName) 
{ 
 
    this.shape = new Array(this.columns); 
 
    for (var i = 0; i < this.columns; i++) 
    { 
        this.shape[i] = new Array(this.rows); 
        for (var j = 0; j < this.rows; j++) 
        { 
            this.shape[i][j] = new Cell(i, j); 
            this.shape[i][j].currentState = false; 
            this.shape[i][j].nextState = false; 
        } 
    } 
 
    var coords; 
    switch (shapeName) 
    { 
        case 'clear': 
            coords = []; 
            break; 
        default: 
        case 'glider': 
            coords = [[24, 13], [25, 14], [23, 15], [24, 15], [25, 15]]; 
            break; 
        case 'small exploder': 
            coords = [[24, 13], [23, 14], [24, 14], [25, 14], [23, 15], [25, 15], [24, 16]]; 
            break; 
        case 'exploder': 
            coords = [[22, 12], [24, 12], [26, 12], [22, 13], [26, 13], [22, 14], [26, 14], [22, 15], [26, 15], [22, 16], [24, 16], [26, 16]]; 
            break; 
        case '10 cell row': 
            coords = [[19, 14], [20, 14], [21, 14], [22, 14], [23, 14], [24, 14], [25, 14], [26, 14], [27, 14], [28, 14]]; 
            break; 
    } 
    this.LoadShapeByCoords(coords); 
} 
 
GameOfLifeLogic.prototype.LoadShapeByCoords = function(coords) 
{ 
    for (var i = 0; i < coords.length; i++) 
    { 
        this.shape[coords[i][0]][coords[i][1]].nextState = true; 
    } 
} 
 
GameOfLifeLogic.prototype.ClickCell = function(x, y) 
{ 
    this.shape[x][y].currentState = !this.shape[x][y].currentState; 
    this.shape[x][y].nextState = this.shape[x][y].currentState; 
} 
 
GameOfLifeLogic.prototype.Advance = function() 
{ 
    for (var i = 0; i < this.columns; i++) 
    { 
        for (var j = 0; j < this.rows; j++) 
        { 
            var neighbours = 0; 
 
            neighbours += this.GetPopulationSizeOfCell(i - 1, j - 1); 
            neighbours += this.GetPopulationSizeOfCell(i, j - 1); 
            neighbours += this.GetPopulationSizeOfCell(i + 1, j - 1); 
            neighbours += this.GetPopulationSizeOfCell(i - 1, j); 
            neighbours += this.GetPopulationSizeOfCell(i + 1, j); 
            neighbours += this.GetPopulationSizeOfCell(i - 1, j + 1); 
            neighbours += this.GetPopulationSizeOfCell(i, j + 1); 
            neighbours += this.GetPopulationSizeOfCell(i + 1, j + 1); 
 
            this.shape[i][j].SetNeighbours(neighbours); 
        } 
    } 
} 
 
GameOfLifeLogic.prototype.GetPopulationSizeOfCell = function(x, y) 
{ 
    if (x < 0 || x >= this.columns || y < 0 || y >= this.rows) return 0; 
    else 
    { 
        if (this.shape[x][y].currentState == true) return 1; 
        else return 0; 
    } 
}