3
5

GPUメモリーはまるで四次元ライフゲームのよう

Last updated at Posted at 2024-08-22

スクリーンショット 2024-08-23 043711.png

スクリーンショット 2024-08-23 043342.png

スクリーンショット 2024-08-23 043447.png

ショートストーリー: 「GPUメモリーはまるで四次元ライフゲームのよう」

東京の夜は静かで、街の喧騒が遠くに感じられるほどだった。高層ビルに囲まれた小さなワンルームに住むプログラマーの藤井健太は、窓の外の光景に目を向けることなく、パソコンの画面に集中していた。彼が取り組んでいるのは「ライフゲーム」、ある種のセルオートマトンだ。

セルオートマトンとは、簡単に言えば、グリッド上に配置されたセル(細胞)の集まりであり、各セルは「生」と「死」という2つの状態のいずれかを持っている。次の瞬間にそのセルがどうなるかは、周囲のセルがどれだけ生きているかに依存して決まる。例えば、隣接するセルが少なすぎると孤独死し、多すぎると過密で死ぬ。そして、適度な数の隣接するセルがあると、新たな生命が生まれる。この単純なルールから、無限のパターンが生まれ、時には複雑で予測不可能な動きを見せることが、このシステムの魅力だった。

藤井がこのプロジェクトに着手したのは数週間前だった。もともと大学でコンピュータサイエンスを専攻していた彼は、数学とプログラムの交差点にあるこのような抽象的な世界に強く惹かれていた。二次元のグリッド上で、生死を繰り返すセルたちの単純さとその背後にある深い複雑さに魅了され、夜な夜なコードを書き続けていた。

最初は、基本的な二次元ライフゲームを作ることが目的だった。画面上で黒と白のセルが、規則的に増減を繰り返す様子を観察しながら、藤井はその美しさに魅了された。しかし、彼の探求心はそれに留まらなかった。次第に、より高次元のシミュレーションへと挑戦したくなってきた。

ある晩、藤井はふとした思いつきで三次元に拡張することを試みた。x, y, z の三軸上でセルを配置し、立方体の中で進化するライフゲームを作り上げた。結果は想像以上に面白かった。立体的に動くカラフルなセルたちは、まるで生き物のように蠢きながらその姿を変えていった。彼はそれをさらに洗練させ、立方体が時間とともにどのように変化するのかを観察し続けた。

しかし、藤井の探求はまだ終わらなかった。「もし、時間軸を加えたらどうなるだろうか?」彼は自問した。

藤井は四次元ライフゲームに挑戦することに決めた。それは、3次元の空間に時間軸を組み込んだもので、各セルの状態が過去の状態にも影響されるというものだった。この新しいチャレンジは、これまで以上に難解で複雑なものだったが、彼はそれに夢中になった。

藤井はコーヒーを片手に、時間を忘れてコードを書き続けた。何度も何度もシミュレーションを繰り返し、過去の状態を考慮したセルの進化を見守った。彼の部屋は時間とともに変化するカラフルなセルたちの世界に満たされていった。

ついに、藤井は4次元ライフゲームの完成形を目の当たりにすることになった。目の前に広がるのは、時間と空間が織りなす一つの生命のような存在だった。彼はまるで、宇宙の創造の瞬間を目撃しているかのような感覚に陥った。

そのとき、藤井はふと気付いた。自分が目の当たりにしているこの四次元ライフゲームは、まさに現実のGPUメモリー上で行われている演算そのものに似ているのだと。GPUは並列で多数の演算プロセッサーが同時にアクセスし、同時にセルの状態を変更していく。まるで4次元のライフゲームが、リアルタイムで進行しているかのようだった。

「これが、僕の作りたかった世界だ…」藤井は静かに呟いた。

彼の心には、GPUメモリーがまるで複雑な四次元ライフゲームのように機能しているという新たな認識が深く刻まれていた。夜が明けると、藤井はまた新しい挑戦に向けて静かに目を覚ました。彼の心には、無限の次元に向かう新たなアイデアが浮かび始めていた。

2次元ライフゲーム

import os
import webbrowser
from http.server import SimpleHTTPRequestHandler, HTTPServer
import threading

# HTMLコードを生成
html_content = """
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2次元ライフゲーム</title>
    <style>
        body {
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #333;
        }
        canvas {
            border: 1px solid #fff;
        }
    </style>
</head>
<body>
    <canvas id="gameCanvas"></canvas>

    <script>
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');

        const cellSize = 10;
        const gridWidth = 60;
        const gridHeight = 60;

        canvas.width = gridWidth * cellSize;
        canvas.height = gridHeight * cellSize;

        // ランダムな色を生成
        function randomColor() {
            const r = Math.floor(Math.random() * 255);
            const g = Math.floor(Math.random() * 255);
            const b = Math.floor(Math.random() * 255);
            return `rgb(${r},${g},${b})`;
        }

        // ランダムなグリッドを初期化
        function createGrid() {
            const grid = [];
            for (let y = 0; y < gridHeight; y++) {
                const row = [];
                for (let x = 0; x < gridWidth; x++) {
                    row.push(Math.random() > 0.5 ? randomColor() : null);
                }
                grid.push(row);
            }
            return grid;
        }

        // セルの生存状態を更新
        function updateGrid(grid) {
            const newGrid = grid.map(arr => [...arr]);

            for (let y = 0; y < gridHeight; y++) {
                for (let x = 0; x < gridWidth; x++) {
                    const aliveNeighbors = countAliveNeighbors(grid, x, y);

                    if (grid[y][x]) {
                        if (aliveNeighbors < 2 || aliveNeighbors > 3) {
                            newGrid[y][x] = null;
                        }
                    } else {
                        if (aliveNeighbors === 3) {
                            newGrid[y][x] = randomColor();
                        }
                    }
                }
            }

            return newGrid;
        }

        // 周囲の生きているセルを数える
        function countAliveNeighbors(grid, x, y) {
            let count = 0;
            for (let i = -1; i <= 1; i++) {
                for (let j = -1; j <= 1; j++) {
                    if (i === 0 && j === 0) continue;

                    const nx = x + i;
                    const ny = y + j;

                    if (nx >= 0 && nx < gridWidth && ny >= 0 && ny < gridHeight) {
                        if (grid[ny][nx]) {
                            count++;
                        }
                    }
                }
            }
            return count;
        }

        // グリッドを描画
        function drawGrid(grid) {
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            for (let y = 0; y < gridHeight; y++) {
                for (let x = 0; x < gridWidth; x++) {
                    if (grid[y][x]) {
                        ctx.fillStyle = grid[y][x];
                        ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
                    }
                }
            }
        }

        let grid = createGrid();

        function gameLoop() {
            grid = updateGrid(grid);
            drawGrid(grid);
            requestAnimationFrame(gameLoop);
        }

        gameLoop();
    </script>
</body>
</html>


"""

# HTMLファイルとして保存
html_file = 'galaxy_simulation.html'
with open(html_file, 'w') as file:
    file.write(html_content)

# サーバーをバックグラウンドで起動
def run_server():
    server_address = ('', 8000)
    httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
    httpd.serve_forever()

# サーバーを別スレッドで実行
thread = threading.Thread(target=run_server)
thread.daemon = True
thread.start()

# デフォルトのブラウザでHTMLファイルを開く
webbrowser.open(f'http://localhost:8000/{html_file}')

# サーバー停止まで待機
try:
    thread.join()
except KeyboardInterrupt:
    print("Server stopped.")


3次元のライフゲーム

import os
import webbrowser
from http.server import SimpleHTTPRequestHandler, HTTPServer
import threading

# HTMLコードを生成
html_content = """
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3次元ライフゲーム</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        canvas {
            display: block;
        }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        const cellSize = 1;
        const gridWidth = 30;
        const gridHeight = 30;
        const gridDepth = 30;

        // ランダムな色を生成
        function randomColor() {
            const r = Math.floor(Math.random() * 255);
            const g = Math.floor(Math.random() * 255);
            const b = Math.floor(Math.random() * 255);
            return new THREE.Color(`rgb(${r},${g},${b})`);
        }

        // ランダムなグリッドを初期化
        function createGrid() {
            const grid = [];
            for (let z = 0; z < gridDepth; z++) {
                const layer = [];
                for (let y = 0; y < gridHeight; y++) {
                    const row = [];
                    for (let x = 0; x < gridWidth; x++) {
                        row.push(Math.random() > 0.5 ? randomColor() : null);
                    }
                    layer.push(row);
                }
                grid.push(layer);
            }
            return grid;
        }

        // セルの生存状態を更新
        function updateGrid(grid) {
            const newGrid = grid.map(layer => layer.map(row => [...row]));

            for (let z = 0; z < gridDepth; z++) {
                for (let y = 0; y < gridHeight; y++) {
                    for (let x = 0; x < gridWidth; x++) {
                        const aliveNeighbors = countAliveNeighbors(grid, x, y, z);

                        if (grid[z][y][x]) {
                            if (aliveNeighbors < 2 || aliveNeighbors > 3) {
                                newGrid[z][y][x] = null;
                            }
                        } else {
                            if (aliveNeighbors === 3) {
                                newGrid[z][y][x] = randomColor();
                            }
                        }
                    }
                }
            }

            return newGrid;
        }

        // 周囲の生きているセルを数える
        function countAliveNeighbors(grid, x, y, z) {
            let count = 0;
            for (let i = -1; i <= 1; i++) {
                for (let j = -1; j <= 1; j++) {
                    for (let k = -1; k <= 1; k++) {
                        if (i === 0 && j === 0 && k === 0) continue;

                        const nx = x + i;
                        const ny = y + j;
                        const nz = z + k;

                        if (nx >= 0 && nx < gridWidth && ny >= 0 && ny < gridHeight && nz >= 0 && nz < gridDepth) {
                            if (grid[nz][ny][nx]) {
                                count++;
                            }
                        }
                    }
                }
            }
            return count;
        }

        // グリッドを描画
        function drawGrid(grid) {
            scene.clear();

            for (let z = 0; z < gridDepth; z++) {
                for (let y = 0; y < gridHeight; y++) {
                    for (let x = 0; x < gridWidth; x++) {
                        if (grid[z][y][x]) {
                            const geometry = new THREE.BoxGeometry(cellSize, cellSize, cellSize);
                            const material = new THREE.MeshBasicMaterial({ color: grid[z][y][x] });
                            const cube = new THREE.Mesh(geometry, material);
                            cube.position.set(
                                (x - gridWidth / 2) * cellSize,
                                (y - gridHeight / 2) * cellSize,
                                (z - gridDepth / 2) * cellSize
                            );
                            scene.add(cube);
                        }
                    }
                }
            }
        }

        let grid = createGrid();

        // カメラを少し斜め上から見るように設定
        camera.position.set(gridWidth, gridHeight, gridDepth);
        camera.lookAt(0, 0, 0);

        function gameLoop() {
            grid = updateGrid(grid);
            drawGrid(grid);
            renderer.render(scene, camera);
            requestAnimationFrame(gameLoop);
        }

        window.addEventListener('resize', () => {
            const width = window.innerWidth;
            const height = window.innerHeight;
            renderer.setSize(width, height);
            camera.aspect = width / height;
            camera.updateProjectionMatrix();
        });

        gameLoop();
    </script>
</body>
</html>


"""

# HTMLファイルとして保存
html_file = 'galaxy_simulation.html'
with open(html_file, 'w') as file:
    file.write(html_content)

# サーバーをバックグラウンドで起動
def run_server():
    server_address = ('', 8000)
    httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
    httpd.serve_forever()

# サーバーを別スレッドで実行
thread = threading.Thread(target=run_server)
thread.daemon = True
thread.start()

# デフォルトのブラウザでHTMLファイルを開く
webbrowser.open(f'http://localhost:8000/{html_file}')

# サーバー停止まで待機
try:
    thread.join()
except KeyboardInterrupt:
    print("Server stopped.")

4次元のライフゲーム

import os
import webbrowser
from http.server import SimpleHTTPRequestHandler, HTTPServer
import threading

# HTMLコードを生成
html_content = """
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>4次元ライフゲーム</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        canvas {
            display: block;
        }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        const cellSize = 1;
        const gridWidth = 20;
        const gridHeight = 20;
        const gridDepth = 20;
        const historyLength = 3;  // 時間軸の長さ

        // ランダムな色を生成
        function randomColor() {
            const r = Math.floor(Math.random() * 255);
            const g = Math.floor(Math.random() * 255);
            const b = Math.floor(Math.random() * 255);
            return new THREE.Color(`rgb(${r},${g},${b})`);
        }

        // グリッドを初期化 (4次元: x, y, z, t)
        function createGrid() {
            const history = [];
            for (let t = 0; t < historyLength; t++) {
                const grid = [];
                for (let z = 0; z < gridDepth; z++) {
                    const layer = [];
                    for (let y = 0; y < gridHeight; y++) {
                        const row = [];
                        for (let x = 0; x < gridWidth; x++) {
                            row.push(Math.random() > 0.5 ? randomColor() : null);
                        }
                        layer.push(row);
                    }
                    grid.push(layer);
                }
                history.push(grid);
            }
            return history;
        }

        // セルの生存状態を更新
        function updateGrid(history) {
            const newGrid = history[historyLength - 1].map(layer => layer.map(row => [...row]));

            for (let z = 0; z < gridDepth; z++) {
                for (let y = 0; y < gridHeight; y++) {
                    for (let x = 0; x < gridWidth; x++) {
                        const aliveNeighbors = countAliveNeighbors(history, x, y, z);

                        if (history[historyLength - 1][z][y][x]) {
                            if (aliveNeighbors < 2 || aliveNeighbors > 4) {
                                newGrid[z][y][x] = null;
                            }
                        } else {
                            if (aliveNeighbors === 3) {
                                newGrid[z][y][x] = randomColor();
                            }
                        }
                    }
                }
            }

            history.shift();  // 古い時間のデータを削除
            history.push(newGrid);  // 新しい時間のデータを追加

            return history;
        }

        // 周囲の生きているセルを数える
        function countAliveNeighbors(history, x, y, z) {
            let count = 0;
            for (let i = -1; i <= 1; i++) {
                for (let j = -1; j <= 1; j++) {
                    for (let k = -1; k <= 1; k++) {
                        for (let t = -1; t <= 0; t++) {
                            if (i === 0 && j === 0 && k === 0 && t === 0) continue;

                            const nx = x + i;
                            const ny = y + j;
                            const nz = z + k;
                            const nt = historyLength - 1 + t;

                            if (nx >= 0 && nx < gridWidth && ny >= 0 && ny < gridHeight && nz >= 0 && nz < gridDepth) {
                                if (history[nt][nz][ny][nx]) {
                                    count++;
                                }
                            }
                        }
                    }
                }
            }
            return count;
        }

        // グリッドを描画
        function drawGrid(grid) {
            scene.clear();

            for (let z = 0; z < gridDepth; z++) {
                for (let y = 0; y < gridHeight; y++) {
                    for (let x = 0; x < gridWidth; x++) {
                        if (grid[z][y][x]) {
                            const geometry = new THREE.BoxGeometry(cellSize, cellSize, cellSize);
                            const material = new THREE.MeshBasicMaterial({ color: grid[z][y][x] });
                            const cube = new THREE.Mesh(geometry, material);
                            cube.position.set(
                                (x - gridWidth / 2) * cellSize,
                                (y - gridHeight / 2) * cellSize,
                                (z - gridDepth / 2) * cellSize
                            );
                            scene.add(cube);
                        }
                    }
                }
            }
        }

        let history = createGrid();

        // カメラを少し斜め上から見るように設定
        camera.position.set(gridWidth * 2, gridHeight * 2, gridDepth * 2);
        camera.lookAt(0, 0, 0);

        function gameLoop() {
            history = updateGrid(history);
            drawGrid(history[historyLength - 1]);
            renderer.render(scene, camera);
            requestAnimationFrame(gameLoop);
        }

        window.addEventListener('resize', () => {
            const width = window.innerWidth;
            const height = window.innerHeight;
            renderer.setSize(width, height);
            camera.aspect = width / height;
            camera.updateProjectionMatrix();
        });

        gameLoop();
    </script>
</body>
</html>


"""

# HTMLファイルとして保存
html_file = 'galaxy_simulation.html'
with open(html_file, 'w') as file:
    file.write(html_content)

# サーバーをバックグラウンドで起動
def run_server():
    server_address = ('', 8000)
    httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
    httpd.serve_forever()

# サーバーを別スレッドで実行
thread = threading.Thread(target=run_server)
thread.daemon = True
thread.start()

# デフォルトのブラウザでHTMLファイルを開く
webbrowser.open(f'http://localhost:8000/{html_file}')

# サーバー停止まで待機
try:
    thread.join()
except KeyboardInterrupt:
    print("Server stopped.")
3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5