Source

pythonwise / life.html

Full commit
<html>
    <head>
        <title>Game Of Life</title>
        <style>
            table {
                border: 5px solid black;
            }
            td {
                width: 15px;
                height: 15px;
            }
            td.dead {
                background: green;
            }
            td.alive {
                background: red;
            }
        </style>
    </head>
    <body>
        <b>Game of Life<b>
        <table id="board">
        </table>
        <button id="run">Run</button>
        <button id="step">Step</button>
        <button id="clear">Clear</button>

    </body>
    <script src="jquery.js"></script>
    <script>
        var NUM_ROWS = 30;
        var NUM_COLS = 30;
        var DEAD = 'dead';
        var ALIVE = 'alive';
        var RUNNING = 0;
        /* We keep this for fast access since jQuery selectors
        $('#board tr:nth-child(' + row + 1 + ') td:nth-child(' + col + 1 + ')'
        are slow
        */
        var BOARD = [];

        function copy_board() {
            var board = [];
            for (var row = 0; row < NUM_ROWS; ++row) {
                var cells = [];
                for (col = 0; col < NUM_COLS; ++col) {
                    var state = get_cell(row, col).hasClass(ALIVE) ?
                                ALIVE : DEAD;
                    cells.push(state);
                }
                board.push(cells);
            }

            return board;
        }

        function cell_neighbours(row, col) {
            var uprow = (row + 1) % NUM_ROWS;
            var downrow = (row - 1) % NUM_ROWS;
            var leftcol = (col - 1) % NUM_COLS;
            var rightcol = (col + 1) % NUM_COLS;

            /* FIXME: Find a better way (since -1 % 10 => -1) */
            downrow = (downrow < 0) ? NUM_ROWS - 1 : downrow;
            leftcol = (leftcol < 0) ? NUM_COLS - 1 : leftcol;

            return [
                [uprow, leftcol], [uprow, col], [uprow, rightcol],
                [row, leftcol], [row, rightcol],
                [downrow, leftcol], [downrow, col], [downrow, rightcol]
            ];
        }

        /* http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Rules */
        function calc_new_state(current_state, num_alive) {
            if (current_state == ALIVE) {
                if (num_alive < 2) {
                    return DEAD;
                }
                else if (num_alive > 3) {
                    return DEAD;
                }
                else {
                    return ALIVE;
                }
            }
            else {
                if (num_alive == 3) {
                    return ALIVE;
                }
                else {
                    return DEAD;
                }
            }
        }


        function on_step() {
            /* Copy old board since we're going to change the current */
            var board = copy_board();

            function is_alive(cell) {
                return board[cell[0]][cell[1]] == ALIVE;
            }

            for (var row = 0; row < NUM_ROWS; ++row) {
                for (var col = 0; col < NUM_COLS; ++col) {
                    var neighbours = cell_neighbours(row, col);
                    var num_alive = $.grep(neighbours, is_alive).length;
                    var current_state = board[row][col];
                    var new_state = calc_new_state(current_state, num_alive);
                    if (new_state != current_state) {
                        toggle(row, col);
                    }
                }
            }
        }

        function run() {
            if (!RUNNING) {
                return;
            }
            on_step();
            setTimeout(run, 200);
        }

        function on_run() {
            var button = $('#run');
            if (button.text() == 'Run') {
                button.text('Stop');
                RUNNING = 1;
                run();
            }
            else {
                button.text('Run');
                RUNNING = 0;
            }
        }

        function get_cell(row, col) {
            return BOARD[row][col];
        }

        function toggle(row, col) {
            var cell = get_cell(row, col);

            if (cell.hasClass(ALIVE)) {
                var add = DEAD;
                var remove = ALIVE;
            }
            else {
                var add = ALIVE;
                var remove = DEAD;
            }

            cell.removeClass(remove).addClass(add);
        }

        function make_handler(row, col) {
            return function() {
                toggle(row, col);
            }
        }

        function initiaize_board() {
            var table = $('#board');
            for (var row = 0; row < NUM_ROWS; ++row) {
                var tr = $('<tr />');
                var cells = [];
                for (var col = 0; col < NUM_COLS; ++col) {
                    var td = $('<td />');
                    td.addClass(DEAD);
                    td.click(make_handler(row, col));
                    tr.append(td);
                    cells.push(td);
                }
                table.append(tr);
                BOARD.push(cells);
            }
        }

        function on_clear() {
            $('.' + ALIVE).removeClass(ALIVE).addClass(DEAD);
        }

        function on_ready()
        {
            initiaize_board();
            $('#step').click(on_step);
            $('#run').click(on_run);
            $('#clear').click(on_clear);
        }

        $(document).ready(on_ready);
    </script>
</html>