JavaScript:js实现在线五子棋人机(人人)对弈

news2025/1/12 12:21:57

在线五子棋人机对弈

全部使用前端技术,使用HTML,CSS以及JS进行实现.

棋盘在后端就是一个15*15的二维数组

页面设计

请添加图片描述

页面设计的比较粗糙

主要使用js自带的canvas画布进行绘画

HTML代码如下:

<div class="outer">
        <canvas id="canvas" height="900" width="900"></canvas>
        <div class="button">
            <div id="reset">
                <button v-on:click="reset()">重试</button>
            </div>
            <!-- <div id="check">
                <button v-on:click="check()">开始</button>
            </div> -->
            <div id="jump">
                <button v-on:click="jump()">玩家本地对战</button>
            </div>
		 </div>
</div>

CSS样式设计如下:

给canvas加了个居中和看上去像棋盘的颜色

按钮使用fixed布局跟随视口移动

#canvas {
    margin: 0 auto;
    display: block;
    margin: auto;
    background:burlywood ;
    border: 5px solid black;
    border-radius: 5px;
}

.outer {
    background: grey;
    padding: 20px;
}

.button{
    position: fixed;
    top: 100px;
    left: 100px;
}

.button button{
    color: black;
    font-size: 20px;
    background-color:powderblue
}

#reset{
    float: left;
}

#check{
    float: left;
    margin-left: 10px;
}

JS代码如下:

简而言之:就是画了一堆横线和竖线

var canvas = document.getElementById("canvas");
        var ctx = canvas.getContext("2d");
        for (let index = 0; index <= 15; index += 1) {
            ctx.moveTo(0, 60 * index);
            ctx.lineTo(900, 60 * index);
            ctx.stroke()
            ctx.moveTo(60 * index, 0);
            ctx.lineTo(60 * index, 900);
            ctx.stroke()
        };

落子

效果如下

请添加图片描述

同样使用canvas进行绘画,代码如下

 canvas.addEventListener('click', e => {
            //获取坐标距离父级的偏移量
            var { offsetX, offsetY } = e;
            //边界判断 不能点击格子外面的范围
            if (offsetX < 30 || offsetY < 30 || offsetX > 870 || offsetY > 870) {
                return;
            } else if (flag === "black") {
                let x = Math.round(e.offsetX / 60) * 60;
                let y = Math.round(e.offsetY / 60) * 60;
                if (arr[y / 60][x / 60] != 0) {
                    alert("此处已有棋子")
                    return;
                }
                arr[y / 60][x / 60] = "black";
                ctx.fillStyle = '#000';
                ctx.beginPath();
                ctx.arc(x, y, 20, 0, 2 * Math.PI)
                ctx.fill();
                ctx.closePath();
                if (test()) {
                    return;
                }
                // setTimeout(
                //     () => {
                //         clear(arrai);
                //         aitest();
                //         ai();
                //     }
                //     , 100);
                flag = "white"
            } else {
                    let x = Math.round(e.offsetX / 60) * 60;
                    let y = Math.round(e.offsetY / 60) * 60;
                    if (arr[y / 60][x / 60] != 0) {
                        alert("此处已有棋子")
                        return;
                    }
                    arr[y / 60][x / 60] = "white";
                    ctx.fillStyle = '#fff';
                    ctx.beginPath();
                    ctx.arc(x, y, 20, 0, 2 * Math.PI)
                    ctx.fill();
                    ctx.closePath();
                    test();
                flag = "black";
            }
        });

给页面的鼠标点击绑了个监听事件.

画棋子也是依靠canvas实现

就是相当于先画一个圆,再往里面填颜色.

 ctx.fillStyle = '#000';
                ctx.beginPath();
                ctx.arc(x, y, 20, 0, 2 * Math.PI)
                ctx.fill();
                ctx.closePath();

判断输赢

js逻辑判断,代码如下

就是遍历棋盘,然后判断横向纵向以及斜向是否连成五子

 function test() {
            let countx = 1;
            for (let i = 0; i <= 14; i++) {
                for (let j = 0; j <= 14; j++) {
                    if (arr[i][j] !== 0 && arr[i][j] === arr[i][j + 1]) {
                        countx++;
                        if (countx == 5) {
                            alert(flag == "black" ? "黑棋获胜" : "白棋获胜");
                            setTimeout(() => location.reload(), 1000);
                            return true;
                        }
                    } else {
                        countx = 1;
                    }
                }
            }
            let county = 1;
            for (let j = 0; j <= 14; j++) {
                for (let i = 0; i <= 14; i++) {
                    if (arr[i][j] !== 0 && arr[i][j] === arr[i + 1][j]) {
                        county++;
                        if (county == 5) {
                            alert(flag == "black" ? "黑棋获胜" : "白棋获胜");
                            setTimeout(() => location.reload(), 1000);
                            return true;
                        }
                    } else {
                        county = 1;
                    }
                }
            }
            let countob = 1;
            let orii = 0;
            let orij = 0;
            for (let i = 0; i <= 14; i++) {
                for (let j = 0; j <= 14; j++) {
                    if (arr[i][j] === arr[i + 1][j + 1]) {
                        orii = i;
                        orij = j;
                        while (1 <= i <= 14 && j <= 14) {
                            if (arr[i][j] === arr[i + 1][j + 1] && arr[i][j] !== 0) {
                                countob++;
                                // console.log(countob);
                                i++;
                                j++;
                                if (countob == 5) {
                                    alert(flag == "black" ? "黑棋获胜" : "白棋获胜");
                                    setTimeout(() => location.reload(), 1000);
                                    return true;
                                }
                            } else {
                                break;
                            }
                        }
                        i = orii;
                        j = orij;
                        countob = 1;
                    }
                }
            }
            let countob1 = 1;
            let orii1 = 0;
            let orij1 = 0;
            for (let i = 1; i <= 14; i++) {
                for (let j = 0; j <= 14; j++) {
                    if (arr[i][j] === arr[i + 1][j - 1]) {
                        orii = i;
                        orij = j;
                        while (i <= 14 && 1 <= j <= 14) {
                            if (arr[i][j] === arr[i + 1][j - 1] && arr[i][j] !== 0) {
                                countob1++;
                                // console.log(countob);
                                i++;
                                j--;
                                if (countob1 == 5) {
                                    alert(flag == "black" ? "黑棋获胜" : "白棋获胜");
                                    setTimeout(() => location.reload(), 1000);
                                    return true;
                                }
                            } else {
                                break;
                            }
                        }
                        i = orii;
                        j = orij;
                        countob1 = 1;
                    }
                }
            }
            return false;
        }
到此为止,五子棋的人人对弈功能已经完全实现.

接下来是人机对弈

ai判断

逻辑就是算出棋盘上己方的最高分位置和对方的最高分位置,进行分数相加,最终的最高分位置就是最优位置.

js逻辑,代码如下:

遍历棋盘对所有位置的所有方向进行判断,得出得分

 function aitest() {
            let sum1 = 0;
            let sum2 = 0;
            for (let i = 0; i <= 14; i++) {
                for (let j = 0; j <= 14; j++) {
                    sum1 += aitestx(i, j);
                    sum1 += aitesty(i, j);
                    sum1 += aitestobl(i, j);
                    sum1 += aitestobr(i, j);
                    flag = (flag == "black") ? "white" : "black";
                    sum2 += aitestx(i, j);
                    sum2 += aitesty(i, j);
                    sum2 += aitestobl(i, j);
                    sum2 += aitestobr(i, j);
                    flag = (flag == "black") ? "white" : "black";
                    arrai[i][j] = sum1 + sum2;
                    console.log(arrai[i][j]);
                    sum1 = 0;
                    sum2 = 0;
                }
            }
        }
横向判断
function aitestx(x, y) {
            let temp = arr[x][y];
            let deadr = false;
            let deadl = false;
            let count = 1;
            for (let i = 1; i <= 5; i++) {
                if (y + i > 14) {
                    deadr = true;
                    break;
                }
                if (arr[x][y + i] != flag) {
                    if (arr[x][y + i] != 0) {
                        deadr = true;
                    }
                    break;
                } else {
                    count++;
                }
            }
            for (let i = 1; i <= 5; i++) {
                if (y - i < 0) {
                    deadl = true;
                    break;
                }
                if (arr[x][y - i] != flag) {
                    if (arr[x][y - i] != 0) {
                        deadl = true;
                    }
                    break;
                } else {
                    count++;
                }
            }
            if (deadl == true && deadr == true) {
                return 0;
            } else {
                if (count > 5) {
                    count = 5;
                    count = 5;
                }
                return (score(count, deadl == deadr ? false : true));
            }
        }
纵向判断
function aitesty(x, y) {
            let temp = arr[x][y];
            let deadr = false;
            let deadl = false;
            let count = 1;
            for (let i = 1; i <= 5; i++) {
                if (x + i > 14) {
                    deadr = true;
                    break;
                }
                if (arr[x + i][y] != flag) {
                    if (arr[x + i][y] != 0) {
                        deadr = true;
                    }
                    break;
                } else {
                    count++;
                }
            }
            for (let i = 1; i <= 5; i++) {
                if (x - i < 0) {
                    deadl = true;
                    break;
                }
                if (arr[x - i][y] != flag) {
                    if (arr[x - i][y] != 0) {
                        deadl = true;
                    }
                    break;
                } else {
                    count++;
                }
            }
            if (deadl == true && deadr == true) {
                return 0;
            } else {
                return (score(count, deadl == deadr ? false : true));
            }
        }
斜向判断
function aitestobl(x, y) {
            let temp = arr[x][y];
            let deadr = false;
            let deadl = false;
            let count = 1;
            for (let i = 1; i <= 5; i++) {
                if (x + i > 14 || y + i > 14) {
                    deadr = true;
                    break;
                }
                if (arr[x + i][y + i] != flag) {
                    if (arr[x + i][y + i] != 0) {
                        deadr = true;
                    }
                    break;
                } else {
                    count++;
                }
            }
            for (let i = 1; i <= 5; i++) {
                if (x - i < 0 || y - i < 0) {
                    deadl = true;
                    break;
                }
                if (arr[x - i][y - i] != flag) {
                    if (arr[x - i][y - i] != 0) {
                        deadl = true;
                    }
                    break;
                } else {
                    count++;
                }
            }
            if (deadl == true && deadr == true) {
                return 0;
            } else {
                return (score(count, deadl == deadr ? false : true));
            }
        }
反斜向判断
function aitestobr(x, y) {
            let temp = arr[x][y];
            let deadr = false;
            let deadl = false;
            let count = 1;
            for (let i = 1; i <= 5; i++) {
                if (x - i < 0 || y + i > 14) {
                    deadr = true;
                    break;
                }
                if (arr[x - i][y + i] != flag) {
                    if (arr[x - i][y + i] != 0) {
                        deadr = true;
                    }
                    break;
                } else {
                    count++;
                }
            }
            for (let i = 1; i <= 5; i++) {
                if (x + i > 14 || y - i < 0) {
                    deadl = true;
                    break;
                }
                if (arr[x + i][y - i] != flag) {
                    if (arr[x + i][y - i] != 0) {
                        deadl = true;
                    }
                    break;
                } else {
                    count++;
                }
            }
            if (deadl == true && deadr == true) {
                return 0;
            } else {
                return (score(count, deadl == deadr ? false : true));
            }
        }
根据上面方法得出的连子数调用方法算出得分返回给aitest()
function score(num, dead) {
            if (dead) {
                switch (num) {
                    case 1:
                        return 1;
                    case 2:
                        return 10;
                    case 3:
                        return 50;
                    case 4:
                        return 400;
                    case 5:
                        return 500000;
                }
            } else {
                switch (num) {
                    case 1:
                        return 5;
                    case 2:
                        return 30;
                    case 3:
                        return 250;
                    case 4:
                        return 10000;
                    case 5:
                        return 500000;
                }
            }
        }
当玩家落子时,调用回调函数settimeout()调用ai落子
setTimeout(
                    () => {
                        clear(arrai);
                        aitest();
                        ai();
                    }
                    , 100);
                flag = "white"
ai()落子函数

遍历棋盘,将分数和映射到另一个二维数组

如果发现此处已有棋子则递归调用本方法.

最后在相应位置完成落子

大功告成
 function ai() {
            let max = -1;
            let maxarr = new Array(-1, -1);
            for (let i = 1; i <= 14; i++) {
                for (let j = 1; j <= 14; j++) {
                    if (max < arrai[i][j] && arr[i][j] == 0) {
                        max = arrai[i][j];
                        maxarr[0] = i;
                        maxarr[1] = j;
                    }
                }
            }
            console.log(maxarr);
            console.log(arr);
            if (arr[maxarr[0]][maxarr[1]] != 0) {
                arrai[maxarr[0]][maxarr[1]] = -1;
                ai();
                console.log("重新来过");
                return;
            }
            console.log("max:" + max);
            console.log("max数组:" + maxarr[0] + "  " + maxarr[1]);
            x = 60 * maxarr[1];
            y = 60 * maxarr[0];
            arr[maxarr[0]][maxarr[1]] = "white";
            ctx.fillStyle = '#fff';
            ctx.beginPath();
            ctx.arc(x, y, 20, 0, 2 * Math.PI)
            ctx.fill();
            ctx.closePath();
            test();
            flag = "black";
            clear(arrai);
        }

总结

一个不难的小游戏,逻辑存在一定漏洞,主要是对js也不熟悉,包括对var和let的理解等等都比较欠缺,以现在的知识储备应该是改不明白了,再多学点把这个做成线上人人对战.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1623804.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux权限维持(四)

6.inetd服务后门 inetd 是一个监听外部网络请求 ( 就是一个 socket) 的系统守护进程&#xff0c;默认情况下为 13 端口。当 inetd 接收到 一个外部请求后&#xff0c;它会根据这个请求到自己的配置文件中去找到实际处理它的程序&#xff0c;然后再把接收到的 这个socket 交给那…

B2B企业如何做好谷歌Google广告推广营销布局?

当今全球化的商业环境中&#xff0c;B2B企业要想在激烈的市场竞争中脱颖而出&#xff0c;拓展海外市场成为了必经之路。而谷歌Google广告&#xff0c;作为全球最大的在线广告平台&#xff0c;无疑是企业触达全球潜在客户的黄金钥匙。云衔科技通过专业服务助力企业轻松开户与高效…

CST初级教程 二

本教程将讲解CST Studio的视窗操控的基本操作. 3D视窗的快捷操作 动态放大与缩小&#xff08;Dynamic Zoom&#xff09; 将鼠标指针移动到CST Studio图形视窗中&#xff0c;向上滚动鼠标滚轮&#xff0c;可动太放大图形视窗中的显示内容&#xff0c;向下滚动鼠标滚轮即可动态缩…

非对称渐开线齿轮学习笔记分享

最近有小伙伴遇到了非对称渐开线齿轮的加工问题,花了些时间学习了解一下,下面是总结的学习笔记,有兴趣的朋友可以瞅瞅: 目录: 为什么要采用非对称? 非对称有什么优点? 非对称齿形如何加工? 非对称齿轮怎么测量? 非对称齿轮建模 为什么要采用非对称? 现在的传动要求…

Linux:进程创建 进程终止

Linux&#xff1a;进程创建 & 进程终止 进程创建fork写时拷贝 进程终止退出码strerrorerrno 异常信号exit 进程创建 fork fork函数可以用于在程序内部创建子进程&#xff0c;其包含在头文件<unistd.h>中&#xff0c;直接调用fork()就可以创建子进程了。 示例代码&…

【C语言】深入理解KMP算法及C语言实现

一、KMP算法简介 KMP算法&#xff08;Knuth-Morris-Pratt算法&#xff09;是一种高效的字符串匹配算法&#xff0c;由Donald Knuth、James H. Morris和 Vaughan Pratt共同发明。KMP算法的核心思想是当一次字符比较失败时&#xff0c;利用已经得到的部分匹配信息&#xff0c;将模…

JVM虚拟机监控及性能调优实战

目录 jvisualvm介绍 1. jvisualvm是JDK自带的可以远程监控内存&#xff0c;跟踪垃圾回收&#xff0c;执行时内存&#xff0c;CPU/线程分析&#xff0c;生成堆快照等的工具。 2. jvisualvm是从JDK1.6开始被继承到JDK中的。jvisualvm使用 jvisualvm监控远程服务器 开启远程监控…

【Java框架】SpringMVC(三)——异常处理,拦截器,文件上传,SSM整合

目录 异常处理解释局部异常处理全局异常 拦截器拦截器介绍作用:拦截器和过滤器之间的区别拦截器执行流程代码实现补充 文件上传依赖配置MultipartResolver编写文件上传表单页APIMultipartFileFile.separator必须对上传文件进行重命名代码示例 SpringMVC文件上传流程多文件上传 …

mybatis中<if>条件判断带数字的字符串失效问题

文章目录 一、项目背景二、真实错误原因说明三、解决方案3.1针对纯数字的字符串值场景3.2针对单个字符的字符串值场景 四、参考文献 一、项目背景 MySQL数据库使用Mybatis查询拼接select语句中进行<if>条件拼接的时候&#xff0c;发现带数字的或者带单个字母的字符串失效…

Coursera: An Introduction to American Law 学习笔记 Week 03: Property Law

An Introduction to American Law 本文是 https://www.coursera.org/programs/career-training-for-nevadans-k7yhc/learn/american-law 这门课的学习笔记。 文章目录 An Introduction to American LawInstructors Week 03: Property LawKey Property Law TermsSupplemental Re…

LM324的输出VOL与IOL你注意过吗?

电路图 途中LMC6084 更改为LM324 故障现象 这个电路的输入输出表达式为 R30 两端电压等于0V 当J16 的4脚与2脚相等&#xff0c;等于5V&#xff08;或者4脚略大于2脚时&#xff09;7脚输出 约 500mV&#xff1b; 实际应该为0V左右才对.见下图 故障原因 上图运放输出低电平…

AI重塑数字安全,安恒信息行胜于言

有人曾言&#xff1a;所有行业都值得基于人工智能技术重做一遍。 深以为然。如今&#xff0c;数字安全产业面临着一次重要的重塑机遇。以大模型为代表的人工智能技术正深刻影响着数字安全市场格局、产品研发、技术方案以及运营服务。产业界已形成共识&#xff0c;即谁能抓住人…

Nginx+Lua+OpenResty(详解及使用)

一、 Nginx简介 Nginx是一个高性能的Web服务器和反向代理的软件。 Web服务器&#xff1a;就是运行我们web服务的容器&#xff0c;提供web功能&#xff0c;还有tomcat也提供类似的功能。 代理是软件架构和网络设计中&#xff0c;非常重要的一个概念。 二、Nginx的反向代理&…

WEB服务的配置与使用 Apache HTTPD

服务端&#xff1a;服务器将发送由状态代码和可选的响应正文组成的 响应 。状态代码指示请求是否成功&#xff0c;如果不成功&#xff0c;则指示存在哪种错误情况。这告诉客户端应该如何处理响应。较为流星的web服务器程序有&#xff1a; Apache HTTP Server 、 Nginx 客户端&a…

百度网盘svip白嫖永久手机2024最新教程

百度网盘&#xff08;原名百度云&#xff09;是百度推出的一项云存储服务&#xff0c;已覆盖主流PC和手机操作系统&#xff0c;包含Web版、Windows版、Mac版、Android版、iPhone版和Windows Phone版。用户将可以轻松将自己的文件上传到网盘上&#xff0c;并可跨终端随时随地查看…

爬虫抓取网站数据

Fiddler 配置fiddler工具结合浏览器插件 配置fiddler Tools--Options 抓包技巧 谷歌浏览器开启无痕浏览,使用SwitchyOmega配置好代理端口 Ctrl x 清理所有请求记录,可以删除指定不需要日志方便观察 设置按请求顺序 观察cookie,观察请求hesder cookie和row返回结果 Swit…

《QT实用小工具·四十二》圆形发光图像

1、概述 源码放在文章末尾 该项目实现了图像的发光效果&#xff0c;特别适合做头像&#xff0c;项目demo演示如下所示&#xff1a; 项目部分代码如下所示&#xff1a; import QtQuick 2.7 import QtGraphicalEffects 1.12Item {id: rootwidth: 80height: 80property int ra…

写Python需要养成的9个编程好习惯

以写Python代码为例&#xff0c;有以下9个编程好习惯。 1. 提前设计 写代码和写作文一样&#xff0c;需要有大纲&#xff0c;不然很容易变成"屎山"。 思考业务逻辑和代码流程&#xff0c;是动手前的准备工作&#xff0c;这上面可以花一半以上时间。 一些程序员洋…

【考研数学】全程跟张宇,《1000题》应该怎么搭配《660》《880》?

数一125学长来了&#xff0c;选题集和选老师经验满满&#xff0c;手把手教你避雷&#xff0c;直接不废话&#xff0c;三本题集很重要&#xff0c;筛选找重点事半功倍。 就老师而言&#xff0c;如果已经决定全程跟张宇老师了&#xff0c;那么就不要中途换老师&#xff01; 就题…

按照模板导出复杂样式的excel

导出excel通常使用的是apache poi,但是poi的api相当复杂&#xff0c;所以当导出的excel样式比较复杂时&#xff0c;写起来就比较头疼了&#xff0c;这里推荐使用easypoi, 可以很方便的根据模板来导出复杂excel 文档地址: 1.1 介绍 - Powered by MinDoc 我们要实现如图所示效果…