原生Javascript实现五子棋

  • 棋盘绘制
  • 五子棋赢法数组
  • 五子棋人落子
  • 五子棋判断人获胜
  • 五子棋计算机落子思路
  • 五子棋计算机落子实现

棋盘绘制

1.设置居中标题
2.使用html5 canvas画布标签,居中、宽高设置为450px、阴影
3.使用js画线条,横竖都有15条线,每条线间距为30px,即棋盘宽高为420px(1430px),剩余30px平分给上下和左右各15px。
方法:画横线和竖线,使用坐标设置起始点,横线x轴都是从15 - 435,y轴是15+i
30px,竖线同上方法,画出十五条。
4.加上重置按钮,并将鼠标cursor设置为移到棋盘上和按钮上自动变成小手。

整个棋盘的绘制代码如下:
五子棋.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>五子棋</title><style>.title{text-align: center;}.chess{/* canvas与image一样虽然可以设置高和宽,但是是行内块,所以把它变成块元素,方便用margin居中 */display: block;margin: 50px auto;/* 阴影往下往右是正数,前两个值代表往下往右的偏移量,第三个代表阴影宽度,最后一个是阴影颜色 */box-shadow: 5px 5px 5px #b9b9b9,-2px -2px 2px #efefef;cursor: pointer;}div{text-align: center;}.res{padding: 10px 20px;background-color: pink;border-radius: 10px;color: white;cursor: pointer;}</style>
</head>
<body><h3 class="title">--五子棋--</h3><canvas class="chess" width="450px" height="450px"></canvas><div><a class="res">restart</a></div>
</body><script>let chess = document.getElementsByClassName('chess')[0];// 获取2d画布的上下文let cxt = chess.getContext("2d");// 线的颜色cxt.strokeStyle = "#b9b9b9";window.onload = function(){drawChessBoard();}function drawChessBoard(){for(let i=0;i<15;i++){// 从canvas画布位置开始计算// 每条横线起始点位置cxt.moveTo(15,15+i*30);// 每条横线终点位置cxt.lineTo(435,15+i*30);// 使用stroke方法连cxt.stroke();// 竖线同上cxt.moveTo(15+i*30,15);cxt.lineTo(15+i*30,435);cxt.stroke();}}</script>
</html>

五子棋赢法数组

把每一个赢法存下来,需要初始化一个三维数组,前两个位置是存棋子在棋盘的位置坐标,最后一个存的是赢法的编号,每一个赢法编号都唯一。
赢法分为横着连成5子,竖着连成5子,正斜线连成五子和反斜线连成五子。
例:
// [0,0,0]/[1,0,0]/[2,0,0]/[3,0,0]/[4,0,0] 从棋盘左上角开始横着往右五个棋子
// [1,0,1]/[2,0,1]/[2,0,1]/[3,0,1]/[5,0,1] 从棋盘左上角后面一个开始横着往右五个棋子
js代码如下:

 // 初始化数组let wins = [];for(let i=0;i<15;i++){wins[i] = [];for(let j=0;j<15;j++){wins[i][j] = [];}}// 设置赢法编号let id = 0;// 统一横线赢法,即横着连成5个子// 横坐标i的起始位置只能到11,再往后就没法赢了for(let i = 0;i<11;i++){for(let j=0;j<15;j++){for(let k = 0;k<5;k++){wins[i+k][j][id] = true}id++;}}// 统计竖线赢法for(let i = 0;i<11;i++){for(let j=0;j<15;j++){for(let k = 0;k<5;k++){wins[j][i+k][id] = true}id++;}}// 统计正斜线,从左上连到右下角for(let i=0;i<11;i++){for(let j=0;j<11;j++){for(let k=0;k<5;k++){wins[j+k][i+k][id] = true;}id++;}}// 统计反斜线,从右上连到左下角for(let i=0;i<11;i++){for(let j=14;j>=4;j--){for(let k=0;k<5;k++){wins[i+k][j-k][id] = true;}id++;}}

五子棋人落子

1.首先定义一个棋盘数组chessBoard,用来判断当前位置是否已经存在棋子
2.定义一个变量me判断是否该人下棋的回合
3.为chess画布绑定点击事件,获取x轴、y轴偏移量
4.因为棋盘每个位置的偏移量都是15,45,75等15+30*n,所以令i和j = x偏移量/30,y偏移量/30,用(i,j)来代表下落在棋盘上的位置。
5.下棋,如果棋盘上没有子并且是自己下棋的回合,就可以下棋,下完后要记得改变棋盘标记以及回合标记
6.定义下棋方法,在棋盘对应位置画个黑色的圆出来,使用canvas上下文中的arc方法画圆,并且为中间填满颜色。
示例:

代码如下:

// 定义二维数组来存储每个坐标是否已经下了棋子let chessBoard = new Array(15).fill(0).map(() => new Array(15).fill(0));// 人下棋let me = true;//用来标记人是否可以下棋let over = false;//标记游戏是否结束chess.onclick = function(e){// 游戏结束或者不是自己下棋回合就停止下棋if(over) return;if(!me) return;// 获取x轴坐标,但这个坐标也是根据canvas父元素的偏移量let x = e.offsetX;// 获取y轴坐标let y = e.offsetY;let i = Math.floor(x/30);let j = Math.floor(y/30);// 此处没有棋子则可以下棋if(chessBoard[i][j]==0&&me){oneStep(i,j,me);// 并将此处标记为已经下过棋子chessBoard[i][j] == 1;me = false;}}// 落子的方法function oneStep(i,j,me){// 画圆cxt.beginPath();// 圆心坐标、半径、从0度到2pi度画个完整的圆出来cxt.arc(15+i*30,15+j*30,13,0,2*Math.PI);cxt.closePath();let color;if(me){color = 'black';}else{color = 'white';}cxt.fillStyle=color;cxt.fill();}

五子棋判断人获胜

1.初始化一个数组myWin来获得用户在每个赢法编号上下的分值
2.在点击事件里面添加循环方法,遍历当前位置符合哪个赢法数组内容,如果在同一个赢法编号下获得5分则代表用户胜利。并标记游戏结束。
3.改变标题,代表胜利。

 // 定义二维数组来存储每个坐标是否已经下了棋子let chessBoard = new Array(15).fill(0).map(() => new Array(15).fill(0));// 人下棋let me = true;//用来标记人是否可以下棋let over = false;//标记游戏是否结束let myWin = new Array(id).fill(0);//记录用户在每个赢法编号上的分值chess.onclick = function(e){// 游戏结束或者不是自己下棋回合就停止下棋if(over) return;if(!me) return;// 获取x轴坐标,但这个坐标也是根据canvas父元素的偏移量let x = e.offsetX;// 获取y轴坐标let y = e.offsetY;let i = Math.floor(x/30);let j = Math.floor(y/30);// 此处没有棋子则可以下棋if(chessBoard[i][j]==0){oneStep(i,j,me);// 并将此处标记为已经下过棋子chessBoard[i][j] == 1;for(let k=0;k<id;k++){// 遍历每一个赢法编号,如果同一个编号的分值到达了5个代表赢了if(wins[i][j][k]){myWin[k]++;if(myWin[k]==5){title.innerHTML = '恭喜你,你赢了';over = true;}}}// me = false;}}

展示:

五子棋计算机落子思路

主要分为两部分
一、拦截用户下的棋子
二、计算机自己已经下过的棋子
给两部分分别初始化与棋盘一样大的数组myScore和computerScore
分别对两者进行分数计算,只对棋盘中的空白格位置进行计算,已经下过棋子的位置不进行考虑。
得出分值最高的空白格位置就是计算机需要落子的位置。

五子棋计算机落子实现

定义计算机落子方法computerAI
1.初始化数组myScore和computerScore来存储用户以及计算机落子在空白格位置的总分值。
2.初始化max、x、y分别存储最大分值以及最大值在棋盘的位置
3.循环遍历每一个格子,碰到空白格时去判断当前赢法编号的已落子情况,分情况进行分数增加。
4.判断大小保存最大值以及位置,调用oneStep方法实现落子,并判断游戏是否结束,分情况设置over数值。
5.在chess的点击事件里面添加语句,调用计算机落子方法。
chess点击事件里添加的部分

  // 计算机落子if(!over&&!me){computerAI();if(!over){me = !me}}

计算机落子方法:

// 计算机落子方法function computerAI(){// 计算在用户旁边落子的分值以及在计算机落子旁边下棋的分值// 计算空白子在用户所占用赢法上的分值let myScore = new Array(15).fill(0).map(() => new Array(15).fill(0));// 空白子在计算机所占用赢法上的分值let computerScore = new Array(15).fill(0).map(() => new Array(15).fill(0));let max = 0,x = 0,y = 0;//分别存储空白子分数最大值以及空白子最大值所对应的坐标for(let i=0;i<15;i++){for(let j = 0;j<15;j++){// 要判断空白位置的分值,已落子的不用管if(chessBoard[i][j]==0){for(let k=0;k<id;k++){// 代表当前赢法编号上有棋子if(wins[i][j][k]){// 用户在此编号的分值if(myWin[k] == 1){myScore[i][j] += 200;}else if(myWin[k] == 2){myScore[i][j] += 400;}else if(myWin[k] == 3){myScore[i][j] += 2000;}else if(myWin[k] == 4){myScore[i][j] += 3000;}if(computerWin[k] == 1){computerScore[i][j] += 220;}else if(computerWin[k] == 2){computerScore[i][j] += 420;}else if(computerWin[k] == 3){computerScore[i][j] += 2200;}else if(computerWin[k] == 4){computerScore[i][j] += 3200;}}}// 思路就是先去拦截用户的棋子if(myScore[i][j]>max){max = myScore[i][j];x = i;y = j;}else if(myScore[i][j]==max){// 不需要拦截用户时,去判断怎么下棋能让计算机更有利if(computerScore[i][j]>max){max = computerScore[i][j];x = i;y = j;}}if(computerScore[i][j]>max){max = computerScore[i][j];x = i;y = j;}else if(computerScore[i][j]=max){if(myScore[i][j]>max){max = myScore[i][j];x = i;y = j;}}}}}oneStep(x,y,me);chessBoard[x][y]+=1;for(let k=0;k<id;k++){if(wins[x][y][k]){computerWin[k]++;if(computerWin[k]==5){title.innerHTML = '你输了!计算机已获胜'over = true;}}}}

游戏总界面:

完整代码:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>五子棋</title><style>.title{text-align: center;}.chess{/* canvas与image一样虽然可以设置高和宽,但是是行内块,所以把它变成块元素,方便用margin居中 */display: block;margin: 50px auto;/* 阴影往下往右是正数,前两个值代表往下往右的偏移量,第三个代表阴影宽度,最后一个是阴影颜色 */box-shadow: 5px 5px 5px #b9b9b9,-2px -2px 2px #efefef;cursor: pointer;}div{text-align: center;}.res{padding: 10px 20px;background-color: pink;border-radius: 10px;color: white;cursor: pointer;}</style>
</head>
<body><h3 class="title">--五子棋--</h3><canvas class="chess" width="450px" height="450px"></canvas><div><a class="res">restart</a></div>
</body><script>let chess = document.getElementsByClassName('chess')[0];//获取chess画布let title = document.getElementsByClassName('title')[0];//获取标题// 获取2d画布的上下文let cxt = chess.getContext("2d");// 线的颜色cxt.strokeStyle = "#b9b9b9";window.onload = function(){drawChessBoard();}function drawChessBoard(){for(let i=0;i<15;i++){// 从canvas画布位置开始计算// 每条横线起始点位置cxt.moveTo(15,15+i*30);// 每条横线终点位置cxt.lineTo(435,15+i*30);// 使用stroke方法连cxt.stroke();// 竖线同上cxt.moveTo(15+i*30,15);cxt.lineTo(15+i*30,435);cxt.stroke();}}// 设置赢法数组,3维数组,前两个代表坐标位置,赢法编号// 例:// [0,0,0]/[1,0,0]/[2,0,0]/[3,0,0]/[4,0,0] 从棋盘左上角开始往右五个棋子// [1,0,1]/[2,0,1]/[2,0,1]/[3,0,1]/[5,0,1] 从棋盘左上角后面一个开始往右五个棋子// 初始化数组let wins = [];for(let i=0;i<15;i++){wins[i] = [];for(let j=0;j<15;j++){wins[i][j] = [];}}// 设置赢法编号let id = 0;// 统一横线赢法,即横着连成5个子// 横坐标i的起始位置只能到11,再往后就没法赢了for(let i = 0;i<11;i++){for(let j=0;j<15;j++){for(let k = 0;k<5;k++){wins[i+k][j][id] = true}id++;}}// 统计竖线赢法for(let i = 0;i<11;i++){for(let j=0;j<15;j++){for(let k = 0;k<5;k++){wins[j][i+k][id] = true}id++;}}// 统计正斜线,从左上连到右下角for(let i=0;i<11;i++){for(let j=0;j<11;j++){for(let k=0;k<5;k++){wins[j+k][i+k][id] = true;}id++;}}// 统计反斜线,从右上连到左下角for(let i=0;i<11;i++){for(let j=14;j>=4;j--){for(let k=0;k<5;k++){wins[i+k][j-k][id] = true;}id++;}}// 定义二维数组来存储每个坐标是否已经下了棋子let chessBoard = new Array(15).fill(0).map(() => new Array(15).fill(0));// 人下棋let me = true;//用来标记人是否可以下棋let over = false;//标记游戏是否结束let myWin = new Array(id).fill(0);//记录用户在每个赢法编号上的分值let computerWin = new Array(id).fill(0);//记录计算机在每个赢法编号上的分值chess.onclick = function(e){// 游戏结束或者不是自己下棋回合就停止下棋if(over) return;if(!me) return;// 获取x轴坐标,但这个坐标也是根据canvas父元素的偏移量let x = e.offsetX;// 获取y轴坐标let y = e.offsetY;let i = Math.floor(x/30);let j = Math.floor(y/30);// 此处没有棋子则可以下棋if(chessBoard[i][j]==0&&me){oneStep(i,j,me);// 并将此处标记为已经下过棋子chessBoard[i][j] += 1;//此处要用+1而不能用==1me = !mefor(let k=0;k<id;k++){// 遍历每一个赢法编号,如果同一个编号的分值到达了5个代表赢了if(wins[i][j][k]){myWin[k]++;if(myWin[k]==5){title.innerHTML = '恭喜你,你赢了';over = true;}}}}// 计算机落子if(!over&&!me){computerAI();if(!over){me = !me}}}// 计算机落子方法function computerAI(){// 计算在用户旁边落子的分值以及在计算机落子旁边下棋的分值// 计算空白子在用户所占用赢法上的分值let myScore = new Array(15).fill(0).map(() => new Array(15).fill(0));// 空白子在计算机所占用赢法上的分值let computerScore = new Array(15).fill(0).map(() => new Array(15).fill(0));let max = 0,x = 0,y = 0;//分别存储空白子分数最大值以及空白子最大值所对应的坐标for(let i=0;i<15;i++){for(let j = 0;j<15;j++){// 要判断空白位置的分值,已落子的不用管if(chessBoard[i][j]==0){for(let k=0;k<id;k++){// 代表当前赢法编号上有棋子if(wins[i][j][k]){// 用户在此编号的分值if(myWin[k] == 1){myScore[i][j] += 200;}else if(myWin[k] == 2){myScore[i][j] += 400;}else if(myWin[k] == 3){myScore[i][j] += 2000;}else if(myWin[k] == 4){myScore[i][j] += 3000;}if(computerWin[k] == 1){computerScore[i][j] += 220;}else if(computerWin[k] == 2){computerScore[i][j] += 420;}else if(computerWin[k] == 3){computerScore[i][j] += 2200;}else if(computerWin[k] == 4){computerScore[i][j] += 3200;}}}// 思路就是先去拦截用户的棋子if(myScore[i][j]>max){max = myScore[i][j];x = i;y = j;}else if(myScore[i][j]==max){// 不需要拦截用户时,去判断怎么下棋能让计算机更有利if(computerScore[i][j]>max){max = computerScore[i][j];x = i;y = j;}}if(computerScore[i][j]>max){max = computerScore[i][j];x = i;y = j;}else if(computerScore[i][j]=max){if(myScore[i][j]>max){max = myScore[i][j];x = i;y = j;}}}}}oneStep(x,y,me);chessBoard[x][y]+=1;for(let k=0;k<id;k++){if(wins[x][y][k]){computerWin[k]++;if(computerWin[k]==5){title.innerHTML = '你输了!计算机已获胜'over = true;}}}}// 落子的方法function oneStep(i,j,me){// 画圆cxt.beginPath();// 圆心坐标、半径、从0度到2pi度画个完整的圆出来cxt.arc(15+i*30,15+j*30,13,0,2*Math.PI);cxt.closePath();let color;if(me==true){color = 'black';}else{color = 'red';}cxt.fillStyle=color;cxt.fill();}</script>
</html>

原生Javascript实现五子棋相关推荐

  1. 原生JavaScript实现五子棋(直接上代码干货点赞收藏拿走)

    HTML页面 注释都很明确了大家好好学习 <!DOCTYPE html> <html lang="en"><head><meta char ...

  2. 分享10个原生JavaScript技巧

    首先在这里要非常感谢无私分享作品的网友们,这些代码片段主要由网友们平时分享的作品代码里面和经常去逛网站然后查看源文件收集到的.把平时网站上常用的一些实用功能代码片段通通收集起来,方便网友们学习使用,利 ...

  3. animate用法 js原生_用 原生Javascript 创建带动画的固顶导航菜单

    当我们在网页中加入一个导航菜单的时候,需要考虑很多因素.如何确定它的位置?如何定义样式?还需要保证它具有良好的响应性.又或者你想为它添加一些炫酷的动画.这时你可能会对 jQuery 感兴趣,因为它会帮 ...

  4. mysql插入ㄖ_原生JavaScript代码100个实例

    1.原生JavaScript实现字符串长度截取 function cutstr(str, len) { var temp; var icount = 0; var patrn = /[^\x00-\x ...

  5. java文件异步上传_[Java教程]原生javascript实现文件异步上传

    [Java教程]原生javascript实现文件异步上传 0 2017-10-25 19:00:06 效果图: 代码:(demo33.jsp) demo33.jsp名称文件确定 本文网址:http:/ ...

  6. [译] 原生 JavaScript 值得学习吗?答案是肯定的

    原文地址:Is Vanilla JavaScript worth learning? Absolutely. 原文作者:David Kopal 译文出自:掘金翻译计划 本文永久链接:github.co ...

  7. React jsx转换成原生JavaScript的一个例子

    jsx代码: var React = require('react'); var ReactDOM = require('react-dom'); var MyButtonController = r ...

  8. 原生希望原生JavaScript开篇

    本篇文章个人在深圳游玩的时候突然想到的...最近就有想写几篇关于原生希望的文章,所以回家到之后就奋笔疾书的写出来发布了 一直对前端技巧很有兴致,就心生了写一个专栏的动机,然后就申请了原生JavaScr ...

  9. 一些常用且实用的原生 JavaScript函数[转]

    日常开始中常用到的一些原生JavaScript函数,比较实用, 今天特地整理一下,分享给大家,希望对大家有用,会常更新,同时也欢迎大家补充. css及html方面的技巧总结,点此前往: 前端开发中一些 ...

最新文章

  1. 效果很好的asp.net的数据库访问模型(优化,封装一体化)
  2. Noticaition 1.0 正式发布了
  3. python中@修饰符用法
  4. 支持向量机原理(一)线性支持向量机
  5. node.js 代码修改 自动识别重启工具
  6. LeetCode 1111. 有效括号的嵌套深度(奇偶分离)
  7. 越是被吐槽,女博士这个群体就越强!!
  8. SQL笔记-检索出ID为Int或Long中不连续的第一个点
  9. Linux内核 eBPF基础:perf(1):perf_event在内核中的初始化
  10. 若依如何解决请求地址存在中文出现异常?
  11. VB直接播放EXE文件中的声音文件
  12. 零中频发射机设计与实现
  13. LAMP架构简介与概述 及服务安装
  14. 斗鱼实名认证 mysql_斗鱼新人主播怎么进行实名认证 斗鱼直播实名认证失败怎么办...
  15. 各省农村人均受教育年限及村委会个数(2011-2019年)
  16. 人人商城图片错乱问题
  17. csgo如何保存自己的cfg_[CS:GO]如何导出cfg文件 最新方法[已解决]
  18. 拉着你的手 歌手:谢东 专辑:笑脸
  19. Android EditText 监听回车键
  20. SQL必知必会笔记(上)

热门文章

  1. SIGCOMM 一些有意思的论文
  2. GIS理论知识(五)之地理要素的概念
  3. 【arduino】三路pid循迹小车
  4. 蚂蚁算法C语言解决,蚂蚁优化算法在解决CVRP中的应用
  5. tensorflow移植到Android端,实现物体检测自动拍照
  6. 为什么IBDP的文凭更受美国大学的青睐?
  7. 山西省初中计算机教案,山西经济出版社小学信息技术2017年最全第二册全册教案.pdf...
  8. 《关于我不自量力挑战红日靶场01却被虐的不成人样的那档事》
  9. [转载]JBoss技术支持文档
  10. 【Wps】deepin解决wps问题