about summary refs log tree commit diff stats
path: root/html/life.html
diff options
context:
space:
mode:
Diffstat (limited to 'html/life.html')
-rw-r--r--html/life.html454
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>