canvas简单五子棋

canvas简单五子棋

效果

在这里插入图片描述

思路

  • canvans 绘制棋盘,绘制时候边缘预留棋子位置
  • 监听点击事件绘制落子并记录到字典中
  • 获胜判定,在四个方向上检测是否有足够数量的连贯棋子
    在这里插入图片描述

代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ym</title>
    <style>
        canvas {
            display: block;
            margin: 0 auto;
            border: 0
        }

        .result {
            text-align: center;
        }
        button{
            display: block;
            margin: 0 auto;
            padding: 4px 20px;
            border:0;
            color: #fff;
            outline: none;
            border-radius: 3px;
            background: #43a6ff
        }
        button:hover{
            font-weight: bold;
            cursor: pointer;
        }
    </style>
</head>
<body>
<canvas id="cv" width="200px" height="200px"></canvas>
<p class="result"></p>
<button onclick="loadPanel(400, 400,30,13)">刷新</button>
<script>

    loadPanel(400, 400,30,13);

    /**
     * 1) 点击落子并切换执子者
     * 2)以当前落子位置为基准,以‘米’字型判定,是否能构成五连及以上
     * @param w 棋盘宽度
     * @param h 棋盘高度
     * @param cs 格子尺寸
     * @param ps 棋子半径
     */
    function loadPanel(w, h, cs, ps) {
        let i, j, k;
        let chks = [[1, 0], [0, 1], [1, 1], [1, -1]];//水平,纵向,斜下,斜上 四个方向
        let successNum = 5;//赢棋标准
        let resultEl = document.querySelector('.result');

        //1)绘制棋盘,边缘应隔开棋子半径的距离
        cs = cs || 16;//默认格子宽高
        ps = ps || 4;//棋子半径
        h = h || w;//高度默认等于宽度

        let el = document.getElementById('cv');
        el.setAttribute('width', w + 'px');
        el.setAttribute('height', h + 'px');
        let context = el.getContext("2d");
        //计算棋盘分割,向下取整
        let splitX = ~~((w - 2 * ps) / cs), splitY = ~~((h - 2 * ps) / cs);

        //初始化落子位置使用字典存储,相较于数组简单且减少内存占用
        let pieces = {};
        //循环划线
        context.translate(ps, ps);
        context.beginPath();
        context.strokeStyle = '#000';
        //垂直线
        for (i = 0; i < splitX + 1; i++) {
            context.moveTo(cs * i, 0);
            context.lineTo(cs * i, splitY * cs);
            context.stroke();
        }
        //水平线
        for (j = 0; j < splitY + 1; j++) {
            context.moveTo(0, cs * j);
            context.lineTo(splitX * cs, cs * j);
            context.stroke();
        }
        context.closePath();

        let user = 0, colors = ['#000', '#fefefe'];
        el.addEventListener('click', function (e) {
            let x = e.offsetX,
                y = e.offsetY,
                //计算落子范围
                rx = ~~((x - ps) / cs) + (((x - ps) % cs <= cs / 2) ? 0 : 1),
                ry = ~~((y - ps) / cs) + (((y - ps) % cs <= cs / 2) ? 0 : 1);
            //绘制棋子
            context.beginPath();
            context.arc(cs * rx, cs * ry, ps, 2 * Math.PI, false);
            context.fillStyle = colors[user];
            context.strokeStyle = '#000';
            user ? user = 0 : user = 1;//切换执子者
            context.fill();
            context.stroke();
            context.closePath();

            //记录棋子位置
            let piece = pieces[rx + '-' + ry] = user;

            //米字型计算结果,以当前落子位置计算是否存在某个方向上具有连续的五个相同棋子
            for (j = 0; j < chks.length; j++) {
                let num = 1, chk = chks[j];
                for (i = 1; i <= 4; i++) {
                    if (pieces[(rx + chk[0] * i) + '-' + (ry + chk[1] * i)] == piece) {
                        num++
                    } else {
                        for (i = -1; i >= -4; i--) {
                            if (pieces[(rx + chk[0] * i) + '-' + (ry + chk[1] * i)] == piece) {
                                num++
                            }
                        }
                        break
                    }
                }
                if (num == successNum) {
                    resultEl.innerHTML = ['白', '黑'][user] + '方赢';
                    break;
                }
            }
        })
    }
</script>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值