利用开源HTML5引擎lufylegend.js结合javascript实现的五子棋人机对弈
前言
游戏规则
玩过五子棋的都应该知道五子棋的规则,这里就简单介绍其规则。
1、传统五子棋的棋具与围棋大致相同,棋子分为黑白两色,棋盘为15×15,棋子放置于棋盘线交叉点上。两人对局,各执一色,轮流下一子,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。
2、由于传统五子棋具有不公平性,而现代五子棋禁手规则令一部分棋手望而怯步。于是产生了职业制传统五子棋,职业制传统五子棋虽然准备麻烦,但胜在简单公平,而且难度增加,久而习之,思维活跃。
用到的术语
开发思路
详细过程
初始化棋盘
//背景层、格子层、棋子层
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<BOARD_SIZE;i++){
textField = new LTextField();
textField.color = "black";
textField.x = OFFSET >> 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为AI
AI.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<BOARD_SIZE;i++){
if(array[i][y] != flag){
break;
}
count1++;
}
//上下判断
for(var i = y;i>=0;i--){
if(array[x][i] != flag){
break;
}
count2++;
}
for(var i = y+1;i<BOARD_SIZE;i++){
if(array[x][i] != flag){
break;
}
count2++;
}
//左上右下判断
for(var i = x,j=y;i>=0&&j>=0;i--,j--){
if(array[i][j] != flag){
break;
}
count3++;
}
for(var i = x+1,j=y+1;i<BOARD_SIZE&&j<BOARD_SIZE;i++,j++){
if(array[i][j] != flag){
break;
}
count3++;
}
//右上左下判断
for(var i =x,j=y;i>=0&&j<BOARD_SIZE;i--,j++){
if(array[i][j] != flag){
break;
}
count4++;
}
for(var i =x+1,j=y-1;i<BOARD_SIZE&&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;
};
最后的效果如下:
利用开源HTML5引擎lufylegend.js结合javascript实现的五子棋人机对弈的更多相关文章
- 如何在我们项目中利用开源的图表(js chart)
最近觉得应该把自己在技术上的一些心得记录在博客里面跟大家分享,一起讨论,一起成长! 这篇随笔主要为介绍chart在项目中的运用,因为在我们看到一些开源的chart时候,是使 ...
- 如何利用开源解码项目开发js视频解码的web应用 系列
介绍web上开发视频业务相关程序的技术演变历程 https://www.cnblogs.com/maoliangwu/articles/12046495.html 介绍ffmpeg asm.js we ...
- 开源HTML5游戏引擎Kiwi.js 1.0正式发布
Kiwi.js是由GameLab开发的一款全新的开源HTML5 JavaScript游戏引擎.在经过一年多的开发和测试之后,终于在日前正式发布了Kiwi.js 1.0版本. 其创始人Dan Milwa ...
- 国内开源html5游戏引擎全收录
本文引自<国内开源html5游戏引擎全收录> 游戏开发这潭水太深,英文水平太差,不敢看国外的, 而且这几年国内技术水平也挺高了不少,特别是JS方面.(我个人感觉) 最近看了几个国产的js游 ...
- html5游戏引擎-Pharse.js学习笔记(一)
1.前言 前几天随着flappy bird这样的小游戏的火爆,使我这种也曾了解过html5技术的js业余爱好者也开始关注游戏开发.研究过两个个比较成熟的html5游戏引擎,感觉用引擎还是要方便一些.所 ...
- JavaScript、jQuery、HTML5、Node.js实例大全-读书笔记1
技术很多,例子很多,只好慢慢学,慢慢实践!!现在学的这本书是[JavaScript实战----JavaScript.jQuery.HTML5.Node.js实例大全] 第 3 章 用 JavaScri ...
- HTML5游戏开发引擎Pixi.js完全入门手册(一)框架简介及框架结构分析,作者思路剖析
前言: 最近无聊在淘宝弄了个小店,打算做一个兼职.遇到一个客户,要我帮忙拷贝一个html5游戏.. 我这人有一个习惯,拿到自己没见过的东西.都会去研究一番.去网上查了下发现,资料都是英文版.感觉极度不 ...
- HTML5游戏开发引擎Pixi.js新手入门讲解
在线演示 本地下载 这篇文章中,介绍HTML5游戏引擎pixi.js的基本使用. 相关代码如下: Javascript 导入类库:(使用极客的cdn服务:http://cdn.gbtags.com) ...
- JavaScript、jQuery、HTML5、Node.js实例大全-读书笔记3
技术很多,例子很多,只好慢慢学,慢慢实践!!现在学的这本书是[JavaScript实战----JavaScript.jQuery.HTML5.Node.js实例大全] JavaScript.jQuer ...
随机推荐
- C++中的智能指针(auto_ptr)
实际上auto_ptr 仅仅是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势.使用它不必每次都手动调用delete去释放内存.当然有利也有弊,也不是全然完美的. 本 ...
- poj 2114 Boatherds 树的分治
还是利用点的分治的办法来做,统计的办法不一样了,我的做法是排序并且标记每个点属于哪颗子树. #include <iostream> #include <cstdio> #inc ...
- 2014 International Conference on Robotics and Computer Vision (ICRVC 2014)
2014机器人与计算机视觉国际会议ICRVC 与会地点:北京 与会时间:2014.10.24-26 截稿日期:2014-07-10 关于征稿: 语言:英文 主题: • Evolutionary Rob ...
- .net嵌入c#代码(投票练习)
.net嵌入c#代码(投票练习) <%@ Page Language="C#" AutoEventWireup="true" CodeFile=" ...
- docker学习笔记3:镜像操作(查找和下载)
一.查看本地镜像 只有下载后,镜像才会保存在本地(docker环境所在的主机),通过如下命令可以查看本地已经存在的镜像. 命令:dokcer images 上面命令列出本地所有已经存在的镜像,显示的信 ...
- boost 分析命令行参数
#include <boost/program_options.hpp> #include <iostream> #include <vector> using n ...
- 第1章 软件测试基本概念(Week1,3月3日)
一.对软件的认识 1. 什么是软件 2. 软件的分类 3. 软件开发的生命周期模型 (1)瀑布模型 (2)Scrum 其实对用瀑布模型这种臃肿不堪.要求严格.而无法适应软件开发周期变化的开发模型,渐渐 ...
- 基于visual Studio2013解决C语言竞赛题之1026判断排序
题目 解决代码及点评 /********************************************************************** ...
- unity3d游戏开发猜想——当程序猿老去
程序猿将代码注入生命去打造互联网的浪潮之巅.当有一天他们老了.会走向那里,会做些什么? 4.4.0" alt="" style="border:0px; ver ...
- MSSQL - 根据时间倒序删除第一行数据
delete top(1) from Tb_PaintOut where PaintNumber = (select top (1) PaintNumber from Tb_PaintOut orde ...