用js实现一款五子棋小游戏,首先把五子棋所有的功能总结一下
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL1QTN0AzMxATM3AzNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
然后是html部分, 一个canvas标签.
<body style="background: rgb(248, 204, 179);">
<canvas style="border:solid 2px red;margin: 50px;" id="can" height="600" width="600"></canvas>
<div id="ts" style="font-size: 50px;display: none;"></div>
</body>
接下来就是JS里实现五子棋的功能了。首先把棋盘绘制出来
var qijuHeight = 15;//棋盘的横线个数
var qijuWidth = 15;//棋盘的竖线个数
var qiju = [];//定义一个储存棋子位置的变量 棋局
/** @type {HTMLCanvasElement} */
var can = document.getElementById("can");//获取canvas标签
var c = can.getContext("2d");//把他转换成2d画布
//棋谱 棋局 初始化
for(var y = 0;y < qijuHeight;y++){//绘制棋盘Y上的格子
qiju.push([]);//在数组里面添加数组以充当Y轴
for (var x = 0; x < qijuWidth; x++) {//绘制棋盘X上的格子
qiju[y].push(0);//在二维数组里面添加棋子,0是空,1是黑棋,2是白棋
if(y <14 && x < 14){//
c.strokeStyle = "black";//设置格子的颜色
//绘制每个格子的位置,因为画布比棋盘大40像素,我们要把棋盘居中就需要在XY上面加20像素-
c.strokeRect(x * 40 + 20,y * 40 + 20,40,40);//绘制格子
}
}
}
棋盘就出来了
并且生成了一个名字为qiju的数组,用来存放棋子用
0代表空的格子,可以下棋(实际上棋子是下在点上的,而不是格子中心)
然后就是实现落子效果
var ssss = 0;//游戏结束的判定,1为已经结束
var hb = 1;//当前是黑棋还是白棋
can.onclick = function(e){ //鼠标点击canvas的点击事件
if(ssss == 1){//判断游戏是否已经结束的东西
return;//结束的话就中止,防止赢了之后可以继续下棋
}
var Y = parseInt(e.layerY / 40);//鼠标在画布的位置除以40就能判断鼠标点击的格子
var X = parseInt(e.layerX / 40);//鼠标在画布的位置除以40就能判断鼠标点击的格子
if(qiju[Y][X] == 0){ //如果点击的地方是空的格子,那么就把棋子绘制
qiju[Y][X] = hb;//把鼠标点击的位置,把对应数组中位置的0用当前的棋子替代;
//hb 是当前是黑棋还是白棋,1黑棋,2白棋
pd(Y,X,hb);//这个是调用判断方法,在下面,跟绘制棋子没关系
hb = hb == 1 ?2:1;//下完之后把黑棋变成白棋,白棋变成黑棋
if(ssss == 1){ //判断游戏是否已经结束的东西
return;
}
ht();//调用绘图功能
}
}
//这样就可以在数组中放置棋子了,但是canvas还没有更新,所以我们要写一个绘图的方法ht()
var color = ["green","black","white"];//颜色数组 1是黑棋,2是白棋
function ht(){//绘图的方法
c.clearRect(0, 0, can.width, can.height);//首先把canvas清除掉,重新绘制
for(var y = 0;y < qijuHeight;y++){//然后是两层for循环重新绘制棋谱,Y轴
for (var x = 0; x < qijuWidth; x++){//X轴
if(y < qijuHeight - 1 && x < qijuWidth - 1){//因为棋子是下在点上而不是格子中间,所以要少生成一个格子
c.strokeStyle = "black";//设置格子的颜色
//绘制每个格子的位置,因为画布比棋盘大40像素,我们要把棋盘居中就需要在XY上面加20像素-
c.strokeRect(x * 40 + 20,y * 40 + 20,40,40);//跟初始化一样
}
if(qiju[y][x] != 0){//判断棋局里面有下了棋子的地方
hz(x,y,15,color[qiju[y][x]])//把棋子绘制出来,数组里面是1就传黑色,是2就是白色
//调用hz()方法 在下面
}
}
}
}
//这里是绘制棋子的方法
function hz(x,y,r,co){//四个参数,xy棋子的位置,r棋子的半径,co棋子的颜色
c.beginPath();//绘制起点
c.arc((x * 40) + 20,(y * 40) + 20 ,r,0,2 * Math.PI);//绘制圆
c.shadowBlur=10;//设置阴影
c.shadowOffsetX=5; //设置阴影x长度
c.shadowOffsetY=5; //设置阴影y长度
c.shadowColor="#747371";//设置阴影颜色
c.fillStyle = co;//设置颜色
c.fill();//闭合起点
c.shadowBlur=0;//初始化阴影,不然下次调用会叠加
c.shadowOffsetX=0; //初始化阴影,
c.shadowOffsetY=0; //初始化阴影,
c.shadowColor="black";//初始化阴影,
}
绘制棋子的方法就完成了
现在就差最后一步,棋子的判定,判定棋子我是这样想的,一个棋子有八个方向,每次判断方向都是一个方向和一个相反的方向,所以我把它分为两部分,上下部分,然后就可以for循环,从左-右,左上-右下,上-下,右-上,左-下这样来,只需要循环四次。只要有5个棋子是在一个方向的,那么就获胜
每个棋子有八个方向需要判定,所以我分为上下两个部分
var fx = [[0,-1],[-1,-1],[-1,0],[-1,1]];//左 左上 上 右上 上半部分
var fx2 = [[0,1],[1,1],[1,0],[1,-1]];//左 左上 上 右上 下半部分
var lsgs = 1;//连着的个数,因为要算上下的那颗棋子,所以默认为1
var cfhs = [];//存放获胜的数组 用来获胜的时候显示用
//三个参数,位置,当前下的棋子是黑还是白
function pd(y,x,hb){//判断方法pd()
for (var i = 0; i < 4; i++) {//循环方向数组
pdfx(y,x,hb,i,1);//上半部分的判断, 1是上半部分,2是下半部分
pdfx(y,x,hb,i,2);//下半部分的判定, 因为分为两部分,所以调用两次
if(lsgs == 5){//如果连起来的个数是等于五的,那么就判定获胜
var msg = "";//定义获胜消息
msg = hb == 1? "黑棋" : "白棋";//绑定棋子对应的颜色
//把在html显示出获胜的信息
document.getElementById("ts").innerHTML = msg + "获胜!!!!!";
document.getElementById("ts").style.display = "block";
cfhs.push([y,x]);//还要把当前下的棋子的位置放进获胜数组里面
//获胜特效
for(var j = 0;j < cfhs.length;j++){//然后就是把获胜数组里面的棋子变大,观看效果好一点
hz(cfhs[j][1],cfhs[j][0],20,color[hb]);//绘制半径20的圆
}
//
ssss = 1;//游戏结束
return;
}else{//连起来不是5颗棋子的
lsgs=1;//初始化连着的棋子
cfhs = [];//初始化连着棋子的数组
}
}
}
//四个参数,xy方向,黑白棋子,哪个方向
function pdfx(y,x,hb,i,sx){//判断方向
var fxy;//当前的位置加上方向后的位置
var fxx ;//当前的位置加上方向后的位置
if(sx == 1){//上半部分
fxy = fx[i][0] + y;//加上方向后的位置
fxx = fx[i][1] + x;//加上方向后的位置
if(fxy < 0 || fxx < 0){//如果方向后的位置是棋谱外,直接放回连接的棋子个数
return lsgs;
}
}else{//下半部分
fxy = fx2[i][0] + y;//加上方向后的位置
fxx = fx2[i][1] + x;//加上方向后的位置
if(fxy > qijuHeight - 1 || fxx > qijuWidth - 1){//如果方向后的位置是棋谱外,直接放回连接的棋子个数
return lsgs;
}
}
if(qiju[fxy][fxx] == hb){//如果方向后的位置,还是跟下的棋子颜色一样,递归调用,延申方向
lsgs++;//连接个数加1
cfhs.push([fxy,fxx]);//存放连起来的数组;
pdfx(fxy,fxx,hb,i,sx)//递归
}
}
//基本上就完成了,最后再加点鼠标移动的特效
can.onmousemove = function(e){//鼠标移动事件
if(ssss == 1){//是否结束
return;
}
var Y = parseInt(e.layerY / 40);//位置
var X = parseInt(e.layerX / 40);
ht();//绘图
if(qiju[Y][X] == 0){//
hz(X,Y,20,color[hb])//绘制棋子
}
}
整个五子棋就完成了,最后最终代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS五子棋</title>
<style>
</style>
</head>
<body style="background: rgb(248, 204, 179);">
<canvas style="border:solid 2px red;margin: 50px;" id="can" height="600" width="600"></canvas>
<div id="ts" style="font-size: 50px;display: none;"></div>
<script>
var qijuHeight = 15;//棋盘的横线个数
var qijuWidth = 15;//棋盘的竖线个数
var qiju = [];//定义一个储存棋子位置的变量 棋局
/** @type {HTMLCanvasElement} */
var can = document.getElementById("can");//获取canvas标签
var c = can.getContext("2d");//把他转换成2d画布
for(var y = 0;y < qijuHeight;y++){//绘制棋盘Y上的格子
qiju.push([]);//在数组里面添加数组以充当Y轴
for (var x = 0; x < qijuWidth; x++) {//绘制棋盘X上的格子
qiju[y].push(0);//在二维数组里面添加棋子,0是空,1是黑棋,2是白棋
if(y <14 && x < 14){//
c.strokeStyle = "black";//设置格子的颜色
//绘制每个格子的位置,因为画布比棋盘大40像素,我们要把棋盘居中就需要在XY上面加20像素-
c.strokeRect(x * 40 + 20,y * 40 + 20,40,40);
}
}
}
console.log(qiju);
var ssss = 0;
var hb = 1;//当前是黑棋还是白棋
can.onclick = function(e){
if(ssss == 1){
return;
}
var Y = parseInt(e.layerY / 40);
var X = parseInt(e.layerX / 40);
if(qiju[Y][X] == 0){
qiju[Y][X] = hb;
pd(Y,X,hb);
hb = hb == 1 ?2:1;
if(ssss == 1){
return;
}
ht();
}
}
var color = ["green","black","white"];//颜色数组
function ht(){
c.clearRect(0, 0, can.width, can.height);
for(var y = 0;y < qijuHeight;y++){
for (var x = 0; x < qijuWidth; x++){
if(y <14 && x < 14){//
c.strokeStyle = "black";//设置格子的颜色
//绘制每个格子的位置,因为画布比棋盘大40像素,我们要把棋盘居中就需要在XY上面加20像素-
c.strokeRect(x * 40 + 20,y * 40 + 20,40,40);
}
if(qiju[y][x] != 0){
hz(x,y,15,color[qiju[y][x]])
}
}
}
}
function hz(x,y,r,co){
c.beginPath();
c.arc((x * 40) + 20,(y * 40) + 20 ,r,0,2 * Math.PI);
c.shadowBlur=10;
c.shadowOffsetX=5;
c.shadowOffsetY=5;
c.shadowColor="#747371";
c.fillStyle = co;
c.fill();
c.shadowBlur=0;
c.shadowOffsetX=0;
c.shadowOffsetY=0;
c.shadowColor="black";
}
var fx = [[0,-1],[-1,-1],[-1,0],[-1,1]];//左 左上 上 右上
var fx2 = [[0,1],[1,1],[1,0],[1,-1]];//左 左上 上 右上
var lsgs = 1;//连着的个数,因为要算上下的那颗棋子,所以默认为1
var cfhs = [];//存放获胜的数组
function pd(y,x,hb){
for (var i = 0; i < 4; i++) {
pdfx(y,x,hb,i,1);
pdfx(y,x,hb,i,2);
if(lsgs == 5){
var msg = "";
msg = hb == 1? "黑棋" : "白棋";
document.getElementById("ts").innerHTML = msg + "获胜!!!!!";
document.getElementById("ts").style.display = "block";
cfhs.push([y,x]);
//获胜特效
for(var j = 0;j < cfhs.length;j++){
hz(cfhs[j][1],cfhs[j][0],20,color[hb]);
}
//
ssss = 1;
return;
}else{
lsgs=1;
cfhs = [];
}
}
}
//四个参数,xy方向,黑白棋子,哪个方向,哪个部分
function pdfx(y,x,hb,i,sx){//判断方向
var fxy;//当前的位置加上方向后的位置
var fxx ;//当前的位置加上方向后的位置
if(sx == 1){//上半部分
fxy = fx[i][0] + y;//加上方向后的位置
fxx = fx[i][1] + x;//加上方向后的位置
if(fxy < 0 || fxx < 0){//如果方向后的位置是棋谱外,直接放回连接的棋子个数
return lsgs;
}
}else{//下半部分
fxy = fx2[i][0] + y;//加上方向后的位置
fxx = fx2[i][1] + x;//加上方向后的位置
if(fxy > qijuHeight - 1 || fxx > qijuWidth - 1){//如果方向后的位置是棋谱外,直接放回连接的棋子个数
return lsgs;
}
}
if(qiju[fxy][fxx] == hb){//如果方向后的位置,还是跟下的棋子颜色一样,递归调用,延申方向
lsgs++;//连接个数加1
cfhs.push([fxy,fxx]);//存放连起来的数组;
pdfx(fxy,fxx,hb,i,sx)//递归
}
}
can.onmousemove = function(e){
if(ssss == 1){
return;
}
var Y = parseInt(e.layerY / 40);
var X = parseInt(e.layerX / 40);
ht();
if(qiju[Y][X] == 0){
hz(X,Y,20,color[hb])
}
}
</script>
</body>
</html>
这样一个150行代码的五子棋就完成了。