const unitLength = 20; let boxColor = 150; const strokeColor = 50; let columns; /* To be determined by window width */ let rows; /* To be determined by window height */ let currentBoard; let nextBoard; let o_currentBoard; let o_nextBoard; let color_white = 255; let paused_game = false; let color_dice; let color_dice_darken; function getRandomInt(max) { return Math.floor(Math.random() * max); } /** * Initialize/reset the board state */ function init(fr) { console.debug({ init: { fr } }); frameRate(fr || 10); // Attempt to refresh at starting FPS for (let i = 0; i < columns; i++) { for (let j = 0; j < rows; j++) { currentBoard[i][j] = 0; nextBoard[i][j] = 0; o_currentBoard[i][j] = { alive: 0 }; o_nextBoard[i][j] = { alive: 0 }; } } } function generate() { //Loop over every single box on the board for (let x = 0; x < columns; x++) { for (let y = 0; y < rows; y++) { // Count all living members in the Moore neighborhood(8 boxes surrounding) let neighbors = 0; let o_neighbors = 0; for (let i of [-1, 0, 1]) { for (let j of [-1, 0, 1]) { if (i == 0 && j == 0) { // the cell itself is not its own neighbor continue; } // The modulo operator is crucial for wrapping on the edge // neighbors += currentBoard[(x + i + columns) % columns][(y + j + rows) % rows]; o_neighbors += parseInt(o_currentBoard[(x + i + columns) % columns][(y + j + rows) % rows].alive); // Rules of Life if (o_currentBoard[x][y].alive == 1 && o_neighbors < 2) { // Die of Loneliness o_nextBoard[x][y] = { alive: 0 }; } else if (o_currentBoard[x][y].alive == 1 && o_neighbors > 3) { // Die of Overpopulation o_nextBoard[x][y] = { alive: 0 }; } else if (o_currentBoard[x][y].alive == 0 && o_neighbors == 3) { // New life due to Reproduction let color_idx = getRandomInt(color_dice_length); o_nextBoard[x][y] = { alive: 1, color: color_dice[color_idx], color_idx }; } else { // Stasis o_nextBoard[x][y] = o_currentBoard[x][y]; o_nextBoard[x][y].color = color(64, 64, 64); } } } } } // Swap the nextBoard to be the current Board [currentBoard, nextBoard] = [nextBoard, currentBoard]; [o_currentBoard, o_nextBoard] = [o_nextBoard, o_currentBoard]; } function setup() { console.log('setup'); // Setup logic here boxColor = color(255, 204, 0); color_dice = [ color(250, 211, 144), color(106, 137, 204), color(184, 233, 148), color(248, 194, 145), color(130, 204, 221), ]; color_dice_length = color_dice.length; /* Set the canvas to be under the element #canvas*/ // NOTES: // Let's look at some of the magic variables here. The magic variables include windowWidth, windowHeight, width and height. They are all provided by p5.js to make our life easier. // windowWidth and windowHeight are the width and height of the viewport. // width and height are the width and height of the canvas element. const canvas = createCanvas(windowWidth, windowHeight - 100); canvas.parent(document.querySelector('#canvas')); /*Calculate the number of columns and rows */ // NOTES: // We are calling createCanvas() with windowWidth and windowHeight - 100 to make a canvas that is as wide as the screen but 100 px shorter than the height. // We then use .parent() to insert our canvas element to the element with id canvas. columns = floor(width / unitLength); rows = floor(height / unitLength); /*Making both currentBoard and nextBoard 2-dimensional matrix that has (columns * rows) boxes. */ currentBoard = []; nextBoard = []; o_currentBoard = []; o_nextBoard = []; for (let i = 0; i < columns; i++) { currentBoard[i] = []; nextBoard[i] = []; o_currentBoard[i] = []; o_nextBoard[i] = []; } // Now both currentBoard and nextBoard are array of array of undefined values. init(); // Set the initial values of the currentBoard and nextBoard } function draw() { // draw logic here background(255); if (paused_game) { // } else { generate(); } for (let i = 0; i < columns; i++) { for (let j = 0; j < rows; j++) { if (o_currentBoard[i][j].alive == 1) { // let idx = getRandomInt(color_dice_length); // fill(color_dice[idx]); fill(o_currentBoard[i][j].color); } else { fill(color_white); } stroke(strokeColor); rect(i * unitLength, j * unitLength, unitLength, unitLength); } } } /** * When mouse is dragged */ function mouseDragged() { /** * If the mouse coordinate is outside the board */ if (mouseX > unitLength * columns || mouseY > unitLength * rows) { return; } const x = Math.floor(mouseX / unitLength); const y = Math.floor(mouseY / unitLength); currentBoard[x][y] = 1; o_currentBoard[x][y] = { alive: 1, color: boxColor }; fill(boxColor); stroke(strokeColor); rect(x * unitLength, y * unitLength, unitLength, unitLength); } /** * When mouse is pressed */ function mousePressed() { noLoop(); mouseDragged(); } /** * When mouse is released */ function mouseReleased() { loop(); } function windowResized() { resizeCanvas(window.innerWidth, window.innerHeight); setup(); } document.addEventListener('DOMContentLoaded', () => { document.querySelector('#frame-rate-value').innerHTML = 10; document.querySelector('#frame-rate-range').value = 10; document.querySelector('#reset-game').addEventListener('click', function () { init(10); document.querySelector('#frame-rate-value').innerHTML = 10; }); document.querySelector('#pause-game').addEventListener('click', function () { paused_game = !paused_game; if (paused_game) { document.querySelector('#game_status').innerHTML = 'paused'; } else { document.querySelector('#game_status').innerHTML = 'running'; } }); document.querySelector('#frame-rate-range').addEventListener('change', function () { let ele = document.querySelector('#frame-rate-range'); let framerate_value = ele.value; document.querySelector('#frame-rate-value').innerHTML = framerate_value; init(parseInt(framerate_value)); }); }); // Control speed of the Game of Life. // (Checkout framerate, you can use slider to control the framerate ) // Allow users to change the rules of survival. // Allow users to change the rules of reproduction. // Start/Stop the Game of life // Multiple colors of life on the same board. // Darken colors for stable life. // Random initial states // Well-known patterns of Game of Life to select from (Examples: Gosper Glider Gun, Glider, Lightweight train). // Use Keyboard to control the cursor to place the life // Resize board on windows resize (Check out windowsResized) // Switching between different styles. // Anything else that you could think of.