diff options
-rw-r--r-- | html/life.html | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/html/life.html b/html/life.html new file mode 100644 index 0000000..703b1e6 --- /dev/null +++ b/html/life.html @@ -0,0 +1,454 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Conway's Game of Life</title> + <style> + canvas { + border: 1px solid black; + } + #controls { + margin-top: 10px; + } + #controls button, #controls select { + margin-right: 10px; + } + button { + border: none; + padding: 0.25em 1em; + } + .playButton { + background-color: green; + color: white; + } + + .pauseButton { + background-color: red; + color: white; + } + </style> +</head> +<body> + <canvas id="gameCanvas" width="400" height="400"></canvas> + <div id="controls"> + <button id="playButton" class="playButton">Play</button> + <button id="pauseButton" class="pauseButton">Pause</button> + <button id="clearButton">Clear</button> + <label for="speedSlider">Speed:</label> + <input type="range" id="speedSlider" min="1" max="1000" value="500"> + <span id="speedValue">500</span> ms + <br> + <br> + <label for="patternSelect">Pattern:</label> + <select id="patternSelect"> + <option value="random">Random</option> + <option value="glider">Glider</option> + <option value="lwss">Lightweight Spaceship</option> + <option value="blinker">Blinker</option> + <option value="toad">Toad</option> + <option value="beacon">Beacon</option> + <option value="block">Block</option> + <option value="beehive">Beehive</option> + <option value="loaf">Loaf</option> + <option value="boat">Boat</option> + <option value="tub">Tub</option> + <option value="pulsar">Pulsar</option> + <option value="pentadecathlon">Pentadecathlon</option> + <option value="gosper_glider_gun">Gosper Glider Gun</option> + <option value="diehard">Diehard</option> + <option value="acorn">Acorn</option> + <option value="r_pentomino">R-pentomino</option> + <option value="blinker_pair">Blinker-pair</option> + </select> + </div> + <script> + class GameOfLife { + constructor(rows, cols) { + this.rows = rows; + this.cols = cols; + this.grid = Array.from({ length: rows }, () => Array(cols).fill(0)); + } + + countNeighbors(x, y) { + const neighborOffsets = [ + [-1, -1], [-1, 0], [-1, 1], + [0, -1], [0, 1], + [1, -1], [1, 0], [1, 1] + ]; + + return neighborOffsets.reduce((count, [dx, dy]) => { + const nx = x + dx; + const ny = y + dy; + if (nx >= 0 && ny >= 0 && nx < this.rows && ny < this.cols && this.grid[nx][ny]) { + return count + 1; + } + return count; + }, 0); + } + + step() { + const newGrid = this.grid.map((row, x) => + row.map((cell, y) => { + const neighbors = this.countNeighbors(x, y); + return neighbors === 3 || (neighbors === 2 && cell) ? 1 : 0; + }) + ); + this.grid = newGrid; + } + + clear() { + this.grid = Array.from({ length: this.rows }, () => Array(this.cols).fill(0)); + } + + toggleCell(x, y) { + if (x >= 0 && y >= 0 && x < this.cols && y < this.rows) { + this.grid[x][y] = this.grid[x][y] ? 0 : 1; + } + } + + setPattern(pattern) { + this.clear(); + switch(pattern) { + case 'glider': + this.setGlider(); + break; + case 'lwss': + this.setLWSS(); + break; + case 'blinker': + this.setBlinker(); + break; + case 'toad': + this.setToad(); + break; + case 'beacon': + this.setBeacon(); + break; + case 'block': + this.setBlock(); + break; + case 'beehive': + this.setBeehive(); + break; + case 'loaf': + this.setLoaf(); + break; + case 'boat': + this.setBoat(); + break; + case 'tub': + this.setTub(); + break; + case 'pulsar': + this.setPulsar(); + break; + case 'pentadecathlon': + this.setPentadecathlon(); + break; + case 'gosper_glider_gun': + this.setGosperGliderGun(); + break; + case 'diehard': + this.setDiehard(); + break; + case 'acorn': + this.setAcorn(); + break; + case 'r_pentomino': + this.setRPentomino(); + break; + case 'blinker_pair': + this.setBlinkerPair(); + break; + case 'random': + this.setRandom(); + break; + } + } + + setGlider() { + const pattern = [ + [0, 1, 0], + [0, 0, 1], + [1, 1, 1] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2)); + } + + setLWSS() { + const pattern = [ + [0, 1, 1, 1, 1], + [1, 0, 0, 0, 1], + [0, 0, 0, 0, 1], + [1, 0, 0, 1, 0] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2)); + } + + setBlinker() { + const pattern = [ + [1, 1, 1] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2)); + } + + setToad() { + const pattern = [ + [0, 1, 1, 1], + [1, 1, 1, 0] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2)); + } + + setBeacon() { + const pattern = [ + [1, 1, 0, 0], + [1, 1, 0, 0], + [0, 0, 1, 1], + [0, 0, 1, 1] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2)); + } + + setBlock() { + const pattern = [ + [1, 1], + [1, 1] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2)); + } + + setBeehive() { + const pattern = [ + [0, 1, 1, 0], + [1, 0, 0, 1], + [0, 1, 1, 0] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2)); + } + + setLoaf() { + const pattern = [ + [0, 1, 1, 0], + [1, 0, 0, 1], + [0, 1, 0, 1], + [0, 0, 1, 0] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2)); + } + + setBoat() { + const pattern = [ + [1, 1, 0], + [1, 0, 1], + [0, 1, 0] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2)); + } + + setTub() { + const pattern = [ + [0, 1, 0], + [1, 0, 1], + [0, 1, 0] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2)); + } + + setPulsar() { + const pattern = [ + [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1], + [1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1], + [1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0], + [1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1], + [1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1], + [1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2) - 6, Math.floor(this.cols / 2) - 6); + } + + setPentadecathlon() { + const pattern = [ + [0, 1, 0, 0, 0, 1, 0], + [1, 0, 1, 1, 1, 0, 1], + [0, 1, 0, 0, 0, 1, 0] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2) - 3); + } + + setGosperGliderGun() { + const pattern = [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2) - 9, Math.floor(this.cols / 2) - 17); + } + + setDiehard() { + const pattern = [ + [0, 0, 0, 0, 0, 0, 1], + [1, 1, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 0] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2) - 3); + } + + setAcorn() { + const pattern = [ + [0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0], + [1, 1, 0, 0, 1, 1, 1] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2) - 3); + } + + setRPentomino() { + const pattern = [ + [0, 1, 1], + [1, 1, 0], + [0, 1, 0] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2)); + } + + setBlinkerPair() { + const pattern = [ + [1, 1, 1], + [0, 0, 0], + [1, 1, 1] + ]; + this.placePattern(pattern, Math.floor(this.rows / 2), Math.floor(this.cols / 2) - 1); + } + + setRandom() { + for (let x = 0; x < this.rows; x++) { + for (let y = 0; y < this.cols; y++) { + this.grid[x][y] = Math.random() < 0.3 ? 1 : 0; + } + } + } + + placePattern(pattern, offsetX, offsetY) { + for (let x = 0; x < pattern.length; x++) { + for (let y = 0; y < pattern[x].length; y++) { + if (offsetX + x < this.rows && offsetY + y < this.cols) { + this.grid[offsetX + x][offsetY + y] = pattern[x][y]; + } + } + } + } + } + + const canvas = document.getElementById('gameCanvas'); + const ctx = canvas.getContext('2d'); + const cellSize = 20; + const rows = Math.floor(canvas.height / cellSize); + const cols = Math.floor(canvas.width / cellSize); + + const game = new GameOfLife(rows, cols); + let speed = 500; + let lastRenderTime = 0; + let isPlaying = false; + + function drawGrid() { + ctx.clearRect(0, 0, canvas.width, canvas.height); + for (let x = 0; x < game.rows; x++) { + for (let y = 0; y < game.cols; y++) { + ctx.fillStyle = game.grid[x][y] ? 'black' : 'white'; + ctx.fillRect(y * cellSize, x * cellSize, cellSize, cellSize); + ctx.strokeStyle = 'lightgrey'; + ctx.strokeRect(y * cellSize, x * cellSize, cellSize, cellSize); + } + } + } + + function simulate(currentTime) { + if (!isPlaying) { + requestAnimationFrame(simulate); + return; + } + + if (currentTime - lastRenderTime >= speed) { + game.step(); + drawGrid(); + lastRenderTime = currentTime; + } + requestAnimationFrame(simulate); + } + + function getMousePosition(event) { + const rect = canvas.getBoundingClientRect(); + const x = Math.floor((event.clientY - rect.top) / cellSize); + const y = Math.floor((event.clientX - rect.left) / cellSize); + return { x, y }; + } + + function toggleCell(event) { + const { x, y } = getMousePosition(event); + game.toggleCell(x, y); + drawGrid(); + } + + document.getElementById('speedSlider').addEventListener('input', (event) => { + speed = event.target.value; + document.getElementById('speedValue').textContent = speed; + }); + + document.getElementById('clearButton').addEventListener('click', () => { + game.clear(); + drawGrid(); + }); + + document.getElementById('playButton').addEventListener('click', function() { + isPlaying = true; + this.classList.add('playButton'); + document.getElementById('pauseButton').classList.remove('pauseButton'); + }); + + document.getElementById('pauseButton').addEventListener('click', function() { + isPlaying = false; + this.classList.add('pauseButton'); + document.getElementById('playButton').classList.remove('playButton'); + }); + + document.getElementById('patternSelect').addEventListener('change', (event) => { + game.setPattern(event.target.value); + drawGrid(); + }); + + canvas.addEventListener('click', toggleCell); + canvas.addEventListener('contextmenu', (event) => { + event.preventDefault(); + toggleCell(event); + }); + + drawGrid(); + requestAnimationFrame(simulate); + </script> +</body> +</html> |