博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
利用开源HTML5引擎lufylegend.js结合javascript实现的五子棋人机对弈
阅读量:5889 次
发布时间:2019-06-19

本文共 11637 字,大约阅读时间需要 38 分钟。

前言

    本文主要介绍利用开源引擎 开发基于Html5的游戏--五子棋,主要叙述其详细开发过程。

游戏规则

    玩过五子棋的都应该知道五子棋的规则,这里就简单介绍其规则。

    1、传统五子棋的与围棋大致相同,分为黑白两色,棋盘为15×15,棋子放置于棋盘线上。两人对局,各执一色,轮流下一子,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。    

    2、由于传统五子棋具有不公平性,而现代五子棋规则令一部分棋手望而怯步。于是产生了职业制传统五子棋,职业制传统五子棋虽然准备麻烦,但胜在简单公平,而且难度增加,久而习之,思维活跃。

规则如下:
1、准备19×19棋盘两张。
2、黑白子数目必须满足。
3、第一回合先手只能下一手,其余回合可以下连续两手。
4、后手每回合均可以下连续两手。
5、每颗子所投的棋盘没有限制。
6、只要任意一方在两个棋盘上且同一个回合上连为五子为胜。
7、若任意一方在两个棋盘上且不同一个回合上连为五子为负。
8、若任意一方在不足两个棋盘上且同一个回合上连为五子为负。
综合效应
“禁手”思维+“交换”思维+“井字游戏”原理=连珠
如:RIF规则、Sakata规则、Yamaguchi规则Tarannikov规则等。
为了方面起见,这里只考虑传统打法,也是大多数人喜欢的打法。

用到的术语

    活五:任意方向的相同颜色棋子连成5个棋子
    活四:任意方向的相同颜色棋子连成4个棋子,且两边都没有其他棋子
    冲四:任意方向的相同颜色棋子连成4个棋子,且一边没有其他棋子
    活三:任意方向的相同颜色棋子连成3个棋子,且两边都没有其他棋子
    死三:任意方向的相同颜色棋子连成3个棋子,且一边没有其他棋子
    活二:任意方向的相同颜色棋子连成2个棋子,且两边都没有其他棋子
    死二:任意方向的相同颜色棋子连成2个棋子,且一边没有其他棋子
    单一:任意方向的相同颜色棋子连成1个棋子,且两边都没有其他棋子

开发思路

     好了,废话不多说,接下来就介绍一下开发思路,首先要明确,当玩家每下一个棋子的时候,如何能教会电脑下棋,也就是如何让电脑知道往哪个地方下子。这里就需要让电脑扫描整个棋盘,分析整个棋型,通过计算得出哪个地方最有可能赢,或者最有可能阻止玩家赢。那么,就需要给每个棋型拟定一个分数,每次玩家下子的时候,就扫描棋盘,给定棋盘每个位置空子(即没有棋子的位置)的分值,然后就下分值最高的那个点。
     比如:如果这个地方如果电脑下子,可能构成活四,那么分数+5000,如果玩家可能构成活四,分数+2000。

详细过程

    我表达不是很好,说的很笼统,直接贴上代码吧。

    初始化棋盘

      
//背景层、格子层、棋子层var backLayer,cellLayer,chessLayer;var BOARD_SIZE = 15;//棋盘格子数量(15*15);var OFFSET = 40;//格子偏移量var CELL_WIDTH = 40;//行宽var CENTER = 8;var array = new Array();//记录每个格子是否有棋子0表示空位1表示己方棋子2表示地方棋子var isPlay = true;//是否该玩家下棋var C_STEP = 0,P_STEP = 0;//AI和玩家走的棋数function main(){	backLayer = new LSprite();	backLayer.graphics.drawRect(1,"darkorange",[0,0,LGlobal.width,LGlobal.height],true,"darkorange");	addChild(backLayer);	var textFiled;	//画棋盘周围的数字和字母	for(var i = 0;i
> 2; textField.y = OFFSET+(CELL_WIDTH*(i)); textField.font="Arial"; textField.size = 8; textField.text = BOARD_SIZE - i; backLayer.addChild(textField); textField = new LTextField(); textField.color = "black"; textField.x = OFFSET+(CELL_WIDTH*i); textField.y = ((OFFSET*3) >> 2) + OFFSET + CELL_WIDTH * (BOARD_SIZE-1); textField.font = "Arial"; textField.size = 8; textField.text = String.fromCharCode(65+i); backLayer.addChild(textField); } //AI初始化 AI.init(); //画出棋盘 drawBoard(); chessLayer = new LSprite(); backLayer.addChild(chessLayer); //按钮 //var resetButton = new LButtonSample1("重玩"); //resetButton.x = 10; //resetButton.y = 500; //backLayer.addChild(resetButton); //resetButton.addEventListener(LMouseEvent.MOUSE_UP,reset); backLayer.addEventListener(LMouseEvent.MOUSE_MOVE,onmove); backLayer.addEventListener(LMouseEvent.MOUSE_DOWN,ondown); };//function reset(){ //};//画棋盘function drawBoard(){ //画竖线条 for(var i = 0;i < BOARD_SIZE;i++){ backLayer.graphics.drawLine(2,"#000000",[i*CELL_WIDTH+OFFSET,OFFSET,i*CELL_WIDTH+OFFSET,(BOARD_SIZE-1)*CELL_WIDTH+OFFSET]); } //画横线条 for(var i = 0;i < BOARD_SIZE;i++){ backLayer.graphics.drawLine(2,"#000000",[OFFSET,i*CELL_WIDTH+OFFSET,(BOARD_SIZE-1)*CELL_WIDTH+OFFSET,i*CELL_WIDTH+OFFSET]); } //画棋盘上的小圆点 drawStar(CENTER,CENTER); drawStar((BOARD_SIZE + 1) >> 2,(BOARD_SIZE + 1) >> 2); drawStar((BOARD_SIZE + 1) >> 2,((BOARD_SIZE + 1) * 3) >> 2); drawStar(((BOARD_SIZE + 1)*3) >> 2,(BOARD_SIZE + 1) >> 2); drawStar(((BOARD_SIZE + 1)*3) >> 2,((BOARD_SIZE + 1) * 3) >> 2);};function drawStar(cx,cy){ var x = (cx - 1)*CELL_WIDTH+OFFSET; var y = (cy - 1) * CELL_WIDTH+OFFSET; backLayer.graphics.drawArc(0,"#000000",[x,y,4,0,Math.PI * 2],true,"#000000");};
//在棋盘指定位置画出黑白棋子function drawChess(cx,cy,color){	//棋子欲放入格子的中心坐标	var x = cx * CELL_WIDTH + OFFSET;	var y = cy * CELL_WIDTH + OFFSET;	var R = CELL_WIDTH >> 1;//棋子半径,为格子宽度/2	chessLayer.graphics.drawArc(0,color,[x,y,R,0,Math.PI * 2],true,color);	isPlay = false;};//画出鼠标点击后棋子欲落下的区域function drawCell(cx,cy){	if(cx >= 0 && cx < BOARD_SIZE && cy >= 0 && cy < BOARD_SIZE){		if(cellLayer){			cellLayer.die();			cellLayer.removeAllChild();			backLayer.removeChild(cellLayer);			cellLayer = null;		}		cellLayer = new LSprite();		backLayer.addChild(cellLayer);		var length = CELL_WIDTH >> 1;		cx = cx * CELL_WIDTH + OFFSET;		cy = cy * CELL_WIDTH + OFFSET;		cellLayer.graphics.drawLine(2,"red",[cx-length,cy - length,cx-length/2,cy-length]);		cellLayer.graphics.drawLine(2,"red",[cx-length,cy - length,cx-length,cy-length/2]);		cellLayer.graphics.drawLine(2,"red",[cx+length,cy - length,cx+length/2,cy-length]);		cellLayer.graphics.drawLine(2,"red",[cx+length,cy - length,cx+length,cy-length/2]);		cellLayer.graphics.drawLine(2,"red",[cx+length,cy + length,cx+length,cy+length/2]);		cellLayer.graphics.drawLine(2,"red",[cx+length,cy + length,cx+length/2,cy+length]);		cellLayer.graphics.drawLine(2,"red",[cx-length,cy + length,cx-length/2,cy+length]);		cellLayer.graphics.drawLine(2,"red",[cx-length,cy + length,cx-length,cy+length/2]);	}};
由于五子棋的核心算法就是AI部分,所以这部分初始化棋盘的代码大可不必深究,接下来就是AI算法。直接贴代码吧
var AI = AI || {};//代表每个方向AI.direction = {	TOP:1,	BOTTOM:2,	LEFT:3,	RIGHT:4,	LEFT_TOP:5,	LEFT_BOTTOM:6,	RIGHT_TOP:7,	RIGHT_BOTTOM:8};//初始化AI.init = function(){	//初始化数组为0	for(var i = 0;i < BOARD_SIZE;i++){		array[i] = new Array();		for(var j = 0;j < BOARD_SIZE;j++){			array[i][j] = 0;		}	}};//AI棋型分析AI.analysis = function(x,y){	//如果为第一步则,在玩家棋周围一格随机下棋,保证每一局棋第一步都不一样	if(P_STEP == 1){		return this.getFirstPoint(x,y);	}	var maxX = new Array(),		maxY = new Array(),		maxWeight = 0,		max = new Array(),		min = new Array(),		i, j, tem;		for (i = BOARD_SIZE - 1; i >= 0; i--) {			for (j = BOARD_SIZE; j >= 0; j--) {				if (array[i][j] !== 0) {					continue;				}				tem = this.computerWeight(i, j,2);				if (tem > maxWeight) {					maxWeight = tem;					maxX = i;					maxY = j;				}			}		}	return new Point(maxX,maxY);};//下子到i,j X方向 结果: 多少连子 两边是否截断AI.putDirectX = function (i, j, chessColor) {		var m, n,			nums = 1,			side1 = false,//两边是否被截断			side2 = false;		for (m = j - 1; m >= 0; m--) {			if (array[i][m] === chessColor) {				nums++;			}			else {				if (array[i][m] === 0) {					side1 = true;//如果为空子,则没有截断				}				break;			}		}		for (m = j + 1; m < BOARD_SIZE; m++) {			if (array[i][m] === chessColor) {				nums++;			}			else {				if (array[i][m] === 0) {					side2 = true;				}				break;			}		}		return {"nums": nums, "side1": side1, "side2": side2};	},	//下子到i,j Y方向 结果AI.putDirectY = function (i, j, chessColor) {		var m, n,			nums = 1,			side1 = false,			side2 = false;		for (m = i - 1; m >= 0; m--) {			if (array[m][j] === chessColor) {				nums++;			}			else {				if (array[m][j] === 0) {					side1 = true;				}				break;			}		}		for (m = i + 1; m < BOARD_SIZE; m++) {			if (array[m][j] === chessColor) {				nums++;			}			else {				if (array[m][j] === 0) {					side2 = true;				}				break;			}		}		return {"nums": nums, "side1": side1, "side2": side2};	},	//下子到i,j XY方向 结果AI.putDirectXY = function (i, j, chessColor) {		var m, n,			nums = 1,			side1 = false,			side2 = false;		for (m = i - 1, n = j - 1; m >= 0 && n >= 0; m--, n--) {			if (array[m][n] === chessColor) {				nums++;			}			else {				if (array[m][n] === 0) {					side1 = true;				}				break;			}		}		for (m = i + 1, n = j + 1; m < BOARD_SIZE && n < BOARD_SIZE; m++, n++) {			if (array[m][n] === chessColor) {				nums++;			}			else {				if (array[m][n] === 0) {					side2 = true;				}				break;			}		}		return {"nums": nums, "side1": side1, "side2": side2};	},AI.putDirectYX = function (i, j, chessColor) {		var m, n,			nums = 1,			side1 = false,			side2 = false;		for (m = i - 1, n = j + 1; m >= 0 && n < BOARD_SIZE; m--, n++) {			if (array[m][n] === chessColor) {				nums++;			}			else {				if (array[m][n] === 0) {					side1 = true;				}				break;			}		}		for (m = i + 1, n = j - 1; m < BOARD_SIZE && n >= 0; m++, n--) {			if (array[m][n] === chessColor) {				nums++;			}			else {				if (array[m][n] === 0) {					side2 = true;				}				break;			}		}		return {"nums": nums, "side1": side1, "side2": side2};	},//计算AI下棋权重AI.computerWeight = function(i,j,chessColor){	var weight = (BOARD_SIZE - 1) - (Math.abs(i - BOARD_SIZE >> 1) + Math.abs(j - BOARD_SIZE >> 1)), //基于棋盘位置权重(越靠近棋盘中心权重越大)	pointInfo = {},	//某点下子后连子信息	//x方向	pointInfo = this.putDirectX(i, j, chessColor);	weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true);//AI下子权重	pointInfo = this.putDirectX(i, j, chessColor-1);	weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false);//player下子权重	//y方向	pointInfo = this.putDirectY(i, j, chessColor);	weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true);//AI下子权重	pointInfo = this.putDirectY(i, j, chessColor-1);	weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false);//player下子权重	//左斜方向	pointInfo = this.putDirectXY(i, j, chessColor);	weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true);//AI下子权重	pointInfo = this.putDirectXY(i, j, chessColor-1);	weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false);//player下子权重	//右斜方向	pointInfo = this.putDirectYX(i, j, chessColor);	weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true);//AI下子权重	pointInfo = this.putDirectYX(i, j, chessColor-1);	weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false);//player下子权重	return weight;};//权重方案   活:两边为空可下子,死:一边为空AI.weightStatus = function (nums, side1, side2, isAI) {		var weight = 0;		switch (nums) {			case 1:				if (side1 && side2) {					weight = isAI ? 15 : 10;	//一				}				break;			case 2:				if (side1 && side2) {					weight = isAI ? 100 : 50;	//活二				}				else if (side1 || side2) {					weight = isAI ? 10 : 5;	//死二				}				break;			case 3:				if (side1 && side2) {					weight = isAI ? 500 : 200;	//活三				}				else if (side1 || side2) {					weight = isAI ? 30 : 20;	//死三				}				break;			case 4:				if (side1 && side2) {					weight = isAI ? 5000 : 2000;	//活四				}				else if (side1 || side2) {					weight = isAI ? 400 : 100;	//死四				}				break;			case 5:				weight = isAI ? 100000 : 10000;	//五				break;			default:				weight = isAI ? 500000 : 250000;				break;		}		return weight;	};//判断是否胜出,胜返回true否则返回false//思路:从下子的地方为中心朝4个方向判断,若连成五子,遇空子或敌方棋子则改变方向则胜出//不用全盘遍历,因为只有下子的地方才会有胜出的可能//flag标识AI还是玩家1为玩家2为AIAI.isAIWin = function(x,y,flag){	var count1 = 0;	var count2 = 0;	var count3 = 0;	var count4 = 0;	//左右判断	for(var i = x;i >= 0;i--){		if(array[i][y]!=flag){			break;		}		count1++;	}	for(var i = x+1;i
=0;i--){ if(array[x][i] != flag){ break; } count2++; } for(var i = y+1;i
=0&&j>=0;i--,j--){ if(array[i][j] != flag){ break; } count3++; } for(var i = x+1,j=y+1;i
=0&&j
=0;i++,j--){ if(array[i][j] != flag){ break; } count4++; } var win = 0;//AI是否赢了 if(count1>=5||count2>=5||count3>=5||count4>=5){ win = flag; } return win;};//AI第一步棋//参数x,y为玩家第一步棋的坐标AI.getFirstPoint = function(x,y){ var point = new Point(x,y); if(x < 3 || x > BOARD_SIZE - 3 || y < 3 || y > BOARD_SIZE - 3){ point.x = BOARD_SIZE >> 1; point.y = BOARD_SIZE >> 1; }else{ var direction = random({ min:1, max:8 }); switch(direction){ case this.direction.TOP: point.y = y - 1; break; case this.direction.BOTTOM: point.y = y + 1; break; case this.direction.LEFT: point.x = x - 1; break; case this.direction.RIGHT: point.x = x + 1; break; case this.direction.LEFT_TOP: point.x = x - 1; point.y = y - 1; break; case this.direction.LEFT_BOTTOM: point.x = x - 1; point.y = y + 1; break; case this.direction.RIGHT_TOP: point.x = x + 1; point.y = y - 1; break; case this.direction.RIGHT_BOTTOM: point.x = x + 1; point.y = y + 1; break; default: point.x = x - 1; point.y = y - 1; break; } } return point;};
function Point(x,y){	var self = this;	self.x = x;	self.y = y;};
最后的效果如下:
好了,整个五子棋就开发完成了,有任何疑问,我们可以交流交流

 

 

你可能感兴趣的文章
【狂人小白】MyBatis.001 学习巴提斯!
查看>>
全面解析C#中参数传递
查看>>
修改注册表防止SYN淹没式攻击
查看>>
WinForm窗体缩放动画
查看>>
Memcached 安装及启动脚本
查看>>
《精通VMware vSphere 6》——第2章 规划与安装 VMware ESXi 2.1规划VMware vSphere部署...
查看>>
如何安装体验 Ubuntu on Windows
查看>>
《移动App测试的22条军规》——军规5 关注用户体验
查看>>
《编程珠玑(第2版•修订版)》—第1章1.1节一次友好的对话
查看>>
《 营销数据科学: 用R和Python进行预测分析的建模技术》——第3章 锁定目标客户...
查看>>
JQuery入门(2)
查看>>
[小白技巧]在Ubuntu 14.04中,如何从Unity启动器上移除盘符图标
查看>>
POI导出JavaWeb中的table到excel下载
查看>>
只有数据分析师才能看懂的十大吐槽
查看>>
《无人机DIY》——4 制作四轴直升机I:选择 机身 
查看>>
《单页Web应用:JavaScript从前端到后端》——1.3 精心编写的单页应用的用户效益...
查看>>
记录一些React的一些细节,会不断更新
查看>>
自己常用的mixin(持续更新)
查看>>
[译] 移动技术在改善财务健康方面的作用
查看>>
播放音频的工具类
查看>>