生命游戏(Game of Life):canvas实现与说明

生命游戏(Game of Life)。这款游戏由英国数学家约翰·康威(John Horton Conway)于1970年发明,并在当时的计算机界引起了极大的关注。生命游戏是一款基于元胞自动机理论的模拟游戏,它的规则简单而富有哲理。

生命游戏是一个无限的,由二维正交正方形的网格组成的计算机模拟图像。每个单元可能出现两种状态:活或死的(填充或未填充的分别)。每个单元格都与其八个相邻的单元交互,这八个单元格是水平、垂直或对角线相邻的单元格。在每个时间步上,都会发生以下变化:

1:当前元胞为存活状态时,如果周围的存活元胞数小于2或大于3,该元胞变成死亡状态。

2:当前元胞为存活状态时,如果周围有2个或3个存活元胞时,该元胞保持原样。

3:当前元胞为死亡状态时,如果周围有3个存活元胞时,该元胞变成存活状态。

在这里插入图片描述
canvas画布html代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <style>
        body {
            margin: 0;
            padding: 0;
        }

        #myCanvas {
            position: absolute;
            top: 0;
            left: 0;
        }

        #button-container {
            position: absolute;
            bottom: 0;
            left: 50%;
            transform: translateX(-50%);
            margin-bottom: 20px;
        }

        #button-container button {
            display: inline-block;
            margin: 0 10px;
            padding: 10px 20px;
            color: #fff;
            background-color: #6c8e91;
            ;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }

        #button-container button:hover {
            background-color: #6c8e91bd;
        }
    </style>
</head>

<body>
    <canvas id="myCanvas"></canvas>
    <div id="button-container">
        <button onclick="MoveToCenter()">回原点</button>
        <button onclick="Game()">开始</button>
        <button onclick="Over()">停止</button>
    </div>
</body>

</html>

网格画布js代码如下:

 // 加载纹理图片

    const myCanvas = document.querySelector("#myCanvas");
    const ctx = myCanvas.getContext("2d"); // 获取canvas上下文对象
    let clickedGrids = new Map(); // 使用哈希表存储格子位置信息
    const solidColor = "#ccc"; // 实线颜色
    const dashedColor = "#ccc"; // 虚线颜色
    const zeroinColor = "red"; // 0 点坐标颜色
    const zeroColor = "#ccc"; // 0 点坐标系颜色
    const redColor = "#53333B"; // 细胞颜色
    const pageSlicePos = {  // 坐标系的原点
        x: 0,
        y: 0,
    };
    let scale = 3; // 初始缩放比例

    myCanvas.width = window.innerWidth;
    myCanvas.height = window.innerHeight;

    myCanvas.addEventListener("mousedown", mouseDown);
    myCanvas.addEventListener("mousewheel", mouseWheel);

    // 回到坐标原点,原点居中 
    function MoveToCenter() {
        pageSlicePos.x = myCanvas.width / 2;
        pageSlicePos.y = myCanvas.height / 2;
    }

    let requestId = null;
    function draw() {
        drawLineGrid();
        requestId = requestAnimationFrame(draw);
    }
    const textureImage = new Image();
    textureImage.src = '2.png';

    //  绘制网格 scaleVal 缩放倍数
    function drawLineGrid(scaleVal = scale) {
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        ctx.fillStyle = ctx.createPattern(textureImage, "repeat");

        ctx.fillRect(0, 0, myCanvas.width, myCanvas.height);
        // 网格大小 5 表示一个单位的小正方形的基本宽高大小
        // 5 * scaleVal表示放大或缩小后的小正方形实际像素宽高大小
        const girdSize = 5 * scaleVal;
        const CanvasWidth = ctx.canvas.width;
        const CanvasHeight = ctx.canvas.height;

        ctx.save(); // 保存画布状态

        ctx.fillStyle = zeroinColor;
        ctx.fillRect(pageSlicePos.x - 3, pageSlicePos.y - 3, 6, 6);

        const canvasXHeight = CanvasHeight - pageSlicePos.y;
        const canvasYWidth = CanvasWidth - pageSlicePos.x;

        const xPageSliceTotal = Math.ceil(canvasXHeight / girdSize);
        const xRemaining = pageSlicePos.y;
        const xRemainingTotal = Math.ceil(xRemaining / girdSize);
        const yPageSliceTotal = Math.ceil(canvasYWidth / girdSize);
        const yRemaining = pageSlicePos.x;
        const yRemainingTotal = Math.ceil(yRemaining / girdSize);

        for (let i = 0; i < xPageSliceTotal; i++) {
            ctx.beginPath();
            ctx.moveTo(0, pageSlicePos.y + girdSize * i);
            ctx.lineTo(CanvasWidth, pageSlicePos.y + girdSize * i);
            ctx.strokeStyle = i === 0 ? zeroColor : i % 5 === 0 ? solidColor : dashedColor;
            ctx.stroke();
        }

        for (let i = 0; i < xRemainingTotal; i++) {
            if (i === 0) continue;
            ctx.beginPath();
            ctx.moveTo(0, pageSlicePos.y - girdSize * i);
            ctx.lineTo(CanvasWidth, pageSlicePos.y - girdSize * i);
            ctx.strokeStyle = i === 0 ? zeroColor : i % 5 === 0 ? solidColor : dashedColor;
            ctx.stroke();
        }

        for (let j = 0; j < yPageSliceTotal; j++) {
            ctx.beginPath();
            ctx.moveTo(pageSlicePos.x + girdSize * j, 0);
            ctx.lineTo(pageSlicePos.x + girdSize * j, CanvasHeight);
            ctx.strokeStyle = j === 0 ? zeroColor : j % 5 === 0 ? solidColor : dashedColor;
            ctx.stroke();
        }

        for (let j = 0; j < yRemainingTotal; j++) {
            if (j === 0) continue;
            ctx.beginPath();
            ctx.moveTo(pageSlicePos.x - girdSize * j, 0);
            ctx.lineTo(pageSlicePos.x - girdSize * j, CanvasHeight);
            ctx.strokeStyle = j === 0 ? zeroColor : j % 5 === 0 ? solidColor : dashedColor;
            ctx.stroke();
        }

        ctx.fillStyle = redColor;
        clickedGrids.forEach((value, key) => {
            const [x, y] = key.split(',').map(Number); // 将键字符串解析为坐标
            ctx.fillRect(x * girdSize + pageSlicePos.x, y * girdSize + pageSlicePos.y, girdSize, girdSize);
            //ctx.fillRect(gridPos.x * girdSize + pageSlicePos.x, gridPos.y * girdSize + pageSlicePos.y, girdSize, girdSize);
        });

        ctx.restore(); // 恢复画布状态
    }

画布无边界,缩放拖动js代码如下:

    // 滚轮缩放倍数, 以鼠标滚轮位置为中心缩放、放大以及边界判断
    function mouseWheel(e) {
        const x = (e.offsetX - pageSlicePos.x) / scale;
        const y = (e.offsetY - pageSlicePos.y) / scale;
        if (e.wheelDelta > 0) {
            if (scale < 10) {
                scale++;
                pageSlicePos.x -= x;
                pageSlicePos.y -= y;
            }
        } else {
            if (scale > 1) {
                scale--;
                pageSlicePos.x += x;
                pageSlicePos.y += y;
            }
        }
    }

    // 拖动 canvas 动态渲染,拖动时,动态设置 pageSlicePos 的值
    function mouseDown(e) {
        const downX = e.clientX;
        const downY = e.clientY;
        const { x, y } = pageSlicePos;

        if (event.button === 0) {
            myCanvas.style.cursor = "grabbing";
            myCanvas.onmousemove = (ev) => {
                const moveX = ev.clientX;
                const moveY = ev.clientY;
                pageSlicePos.x = x + (moveX - downX);
                pageSlicePos.y = y + (moveY - downY);
            };
        } else if (event.button === 2) {
            const gridPos = {
                x: Math.floor((e.clientX - pageSlicePos.x) / (5 * scale)),
                y: Math.floor((e.clientY - pageSlicePos.y) / (5 * scale)),
            };
            // 将格子的位置作为键,将布尔值设为true表示红色
            clickedGrids.set(`${gridPos.x},${gridPos.y}`, true);
            console.log(gridPos)
            myCanvas.onmousemove = (ev) => {
                const moveX = ev.clientX;
                const moveY = ev.clientY;
                const gridPos = {
                    x: Math.floor((moveX - pageSlicePos.x) / (5 * scale)),
                    y: Math.floor((moveY - pageSlicePos.y) / (5 * scale)),
                };
                clickedGrids.set(`${gridPos.x},${gridPos.y}`, true);
            };
        }

        myCanvas.onmouseup = (en) => {
            myCanvas.onmousemove = null;
            myCanvas.onmouseup = null;
            myCanvas.style.cursor = "";
        };
    }

    // 页面卸载时取消动画渲染
    window.addEventListener("beforeunload", () => {
        cancelAnimationFrame(requestId);
    });

    // 页面禁止鼠标右键弹出
    document.addEventListener("contextmenu", (event) => {
        event.preventDefault();
    });

    draw();
    MoveToCenter();

Game of Life主要规则,js代码如下:

    let GameOfLifeId = 0;
    function GameOfLife() { // 康威生命游戏
        let newClickedGrids = new Map(); // 使用新的哈希表存储更新后的格子位置信息

        clickedGrids.forEach((value, key) => {
            const [x, y] = key.split(',').map(Number);
            let liveNeighbors = 0;

            // 检查当前单元格的邻居
            for (let i = x - 1; i <= x + 1; i++) {
                for (let j = y - 1; j <= y + 1; j++) {
                    if (i === x && j === y) continue; // 跳过当前单元格
                    const neighborKey = `${i},${j}`;
                    if (clickedGrids.has(neighborKey)) { // 统计活着的邻居
                        liveNeighbors++;
                    } else {
                        let deadNeighborCount = 0; // 检查死亡的邻居,看看它们是否应该复活
                        for (let ni = i - 1; ni <= i + 1; ni++) {
                            for (let nj = j - 1; nj <= j + 1; nj++) {
                                if (ni === i && nj === j) continue;
                                if (clickedGrids.has(`${ni},${nj}`)) {
                                    deadNeighborCount++;
                                }
                            }
                        }
                        if (deadNeighborCount === 3) {
                            newClickedGrids.set(neighborKey, true);
                        }
                    }
                }
            }

            // 应用康威生命游戏规则
            if ((value && liveNeighbors === 2) || liveNeighbors === 3) {
                newClickedGrids.set(key, true);
            }
        });

        clickedGrids = newClickedGrids; // 更新格子位置信息
    }
    function Game() {
        GameOfLifeId = setInterval(GameOfLife, 300);
    }
    function Over() {
        clearInterval(GameOfLifeId);
    }

给出两个简单的细胞集,代码如下:


    // 原子集
    const atom = {
        glider: () => { // 滑翔机
            clickedGrids.set(`${0},${0}`, true);
            clickedGrids.set(`${0},${1}`, true);
            clickedGrids.set(`${0},${2}`, true);
            clickedGrids.set(`${1},${0}`, true);
            clickedGrids.set(`${2},${1}`, true);
        },
        piston: () => { // 活塞
            clickedGrids.set(`${-1},${-1}`, true);
            clickedGrids.set(`${-1},${-2}`, true);
            clickedGrids.set(`${-1},${1}`, true);
            clickedGrids.set(`${-1},${2}`, true);
            clickedGrids.set(`${0},${2}`, true);
            clickedGrids.set(`${0},${-2}`, true);
            clickedGrids.set(`${1},${-1}`, true);
            clickedGrids.set(`${1},${0}`, true);
            clickedGrids.set(`${1},${1}`, true);

            clickedGrids.set(`${2},${0}`, true);
            clickedGrids.set(`${3},${0}`, true);
            clickedGrids.set(`${4},${0}`, true);
            clickedGrids.set(`${4},${-1}`, true);
            clickedGrids.set(`${4},${1}`, true);

            clickedGrids.set(`${7},${1}`, true);
            clickedGrids.set(`${7},${-1}`, true);
            clickedGrids.set(`${7},${0}`, true);
            clickedGrids.set(`${9},${-1}`, true);
            clickedGrids.set(`${9},${-2}`, true);
            clickedGrids.set(`${8},${-2}`, true);
            clickedGrids.set(`${9},${1}`, true);
            clickedGrids.set(`${9},${2}`, true);
            clickedGrids.set(`${8},${2}`, true);

        },

    }

    atom.piston(); // 初始生命 - 滑翔机
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一个简单的C++实现康威生命游戏的代码。康威生命游戏是一个基于细胞自动机的零玩家游戏,它是由英国数学家约翰·康威在1970年发明的。 以下是一个简单的实现: ```c++ #include <iostream> #include <cstdlib> #include <ctime> using namespace std; const int ROW = 20; //棋盘行数 const int COL = 20; //棋盘列数 void initBoard(int board[][COL]) { srand(time(NULL)); //随机种子 for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { board[i][j] = rand() % 2; //随机生成0或1 } } } void displayBoard(int board[][COL]) { for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { if (board[i][j] == 0) { cout << " "; //死细胞用空格表示 } else { cout << "X"; //活细胞用X表示 } } cout << endl; } } int countNeighbor(int board[][COL], int row, int col) { int count = 0; for (int i = row - 1; i <= row + 1; i++) { for (int j = col - 1; j <= col + 1; j++) { if (i >= 0 && i < ROW && j >= 0 && j < COL && !(i == row && j == col)) { count += board[i][j]; } } } return count; } void updateBoard(int board[][COL]) { int newBoard[ROW][COL]; for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { int count = countNeighbor(board, i, j); if (board[i][j] == 1 && (count == 2 || count == 3)) { //活细胞周围2或3个活细胞时仍然存活 newBoard[i][j] = 1; } else if (board[i][j] == 0 && count == 3) { //死细胞周围恰好3个活细胞时复活 newBoard[i][j] = 1; } else { newBoard[i][j] = 0; //其他情况细胞死亡 } } } for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { board[i][j] = newBoard[i][j]; //更新棋盘 } } } int main() { int board[ROW][COL]; initBoard(board); while (true) { displayBoard(board); updateBoard(board); system("cls"); //清屏 } return 0; } ``` 该程序使用二维数组模拟了一个棋盘,其中每个元素代表一个细胞,0表示死细胞,1表示活细胞。`initBoard`函数用于随机初始化棋盘,`displayBoard`函数用于显示当前的棋盘状态,`countNeighbor`函数用于统计某个细胞周围的活细胞数量,`updateBoard`函数用于根据细胞周围的情况更新棋盘状态。在主函数中,程序不断显示当前的棋盘状态,并不断更新棋盘,直到用户手动退出程序为止。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值