『HTML5实现人工智能』小游戏《井字棋》发布,据说IQ上200才能赢【算法&代码讲解+资源打包下载】

时间:2022-10-25 23:28:38

一,什么是TicTacToe(井字棋)

本游戏为在下用lufylegend开发的第二款小游戏。此游戏是大家想必大家小时候都玩过,因为玩它很简单,只需要一张草稿纸和一只笔就能开始游戏,所以广受儿童欢迎。可能我说了半天,对它名字不熟悉的朋友也不懂我在说神马。那没关系,我就引用Wiki(*)的介绍作为大家对它名字的认识,顺便也勾起我们儿时的回忆:

井字棋,大陆、*又称为井字游戏、圈圈叉叉;另外也有打井游戏、OX棋的称呼,香港多称井字过三关、过三关,是种纸笔游戏。两个玩家,一个打圈(O),一个打叉(X),轮流在3乘3的格上打自己的符号,最先以横、直、斜连成一线则为胜。如果双方都下得正确无误,将得和局。这种游戏实际上是由第一位玩家所控制,第一位玩家是攻,第二位玩家是守。第一位玩家在角位行第一子的话赢面最大(见图一),第二位玩家若是在边,角位下子,第一位玩家就可以以两粒连线牵制着第二位玩家,然后制造“两头蛇”。

『HTML5实现人工智能』小游戏《井字棋》发布,据说IQ上200才能赢【算法&代码讲解+资源打包下载】

图一

二,游戏在哪里玩?

相信大家看了介绍就对井字棋有了了解。现在我用HTML5配合开源游戏引擎lufylegend开发出了这一款游戏,并实现了人工智能(AI)确保游戏中玩家能棋缝对手。

接下来是游戏在线试玩和下载源码的地址:

下载地址(含源代码):http://files.cnblogs.com/yorhom/Tic_Tac_Toe.rar

在线试玩地址:http://www.lufylegend.com/lufylegend_developers/yorhom_Tic_Tac_Toe/index.html

三,游戏截图

『HTML5实现人工智能』小游戏《井字棋》发布,据说IQ上200才能赢【算法&代码讲解+资源打包下载】

『HTML5实现人工智能』小游戏《井字棋》发布,据说IQ上200才能赢【算法&代码讲解+资源打包下载】

四,游戏引擎

本游戏运用国产的lufylegend引擎,版本为1.6.1,如果大家感兴趣可以去官网看看

lufylegend官方网站:

http://lufylegend.com/lufylegend

lufylegend API文档:

http://lufylegend.com/lufylegend/api

上面有此引擎的下载和API介绍。关于用lufylegend开发游戏的其他文章:

【HTML5游戏开发】简单的《找不同汉字版》,来考考你的眼力吧

五,算法&代码讲解

先来个游戏初始化:

init(30,"mylegend",390,420,main);

为了方便操作游戏中的一些数据,我们设定许多变量:

var backLayer,chessLayer,overLayer;
var statusText = new LTextField();
var statusContent="您先请吧……";
var matrix = [
[0,0,0],
[0,0,0],
[0,0,0]
];
var usersTurn = true;
var step = 0;
var title = "井字棋";
var introduction = ""
var infoArr = [title,introduction];

第一行是层变量;第二行是实例化的文本框对象,用来显示文字;第三行是当前显示信息的文字,比如该哪方走,哪方赢了等,会根据不同情况改变。

matrix是用来保存当前棋盘数据的数组,如果下一步棋,就会更改其中数据,顺便也说一下,为了区分【空白格子】,【我方下的位置】,【电脑下的位置】,我们用-1来代表【我方下的位置】,用0来代表【空白格子】,1来代表【电脑下的位置】;看官且记,这-1,0,1在棋盘数组中便各有了代表意义。

userTurn是用来判断玩家是否可以下棋;step是用来表示走的步数,用来判断棋盘是否下满;title,introduction还有infoArr原本是用来制作关于界面的,结果做到最后就算了,大家直接忽视掉吧。

接下来就是main函数,由于没有图片,所以就没有加载部分了:

function main(){
gameInit();
addText();
addLattice();
}

main调用的几个函数如下:

function gameInit(){
initLayer();
addEvent();
}
function addText(){
statusText.size = 15;
statusText.weight = "bold";
statusText.color = "white";
statusText.text = statusContent;
statusText.x = (LGlobal.width-statusText.getWidth())*0.5;
statusText.y = 393; overLayer.addChild(statusText);
}
function addLattice(){
backLayer.graphics.drawRect(10,"dimgray",[0,0,390,420],true,"dimgray");
backLayer.graphics.drawRect(10,"dimgray",[0,0,390,390],true,"lavender");
for(var i=0;i<3;i++){
backLayer.graphics.drawLine(3,"dimgray",[130*i,0,130*i,390]);
}
for(var i=0;i<3;i++){
backLayer.graphics.drawLine(3,"dimgray",[0,130*i,390,130*i]);
}
}

解释一下他们的功能。首先,gameInit是用来初始化游戏的,包括初始化层一类的东西。addText是用来加下面文字的。addLattice使用来画棋盘的。代码很简单,参照lufylegend API文档看一下就能看懂。

接下来我们来看gameInit里调用的函数:

function initLayer(){
backLayer = new LSprite();
addChild(backLayer); chessLayer = new LSprite();
backLayer.addChild(chessLayer); overLayer = new LSprite();
backLayer.addChild(overLayer);
}
function addEvent(){
backLayer.addEventListener(LMouseEvent.MOUSE_DOWN,onDown);
}

initLayer是用来实例化层的,说明了一点就是实例化LSprite。addEvent用来加点击事件。

然后接下来就来看看事件触发的onDown:

function onDown(){
var mouseX,mouseY;
mouseX = event.offsetX;
mouseY = event.offsetY; var partX = Math.floor(mouseX/130);
var partY = Math.floor(mouseY/130);
if(matrix[partX][partY]==0){
usersTurn=false;
matrix[partX][partY]=-1;
step++;
update(partX,partY); if(win(partX,partY)){
statusContent = "帅呆了,你赢啦!点击屏幕重开游戏。";
gameover();
addText();
}else if(isEnd()){
statusContent = "平局啦~~点击屏幕重开游戏。";
gameover();
addText();
}else{
statusContent = "电脑正在思考中……";
addText();
computerThink();
}
}
}

这个函数要做的就是先取出点击位置,然后根据点的位置下一颗棋,然后将在棋盘数组中相应的位置设为-1,表示是我方走的,然后判断:下了这一步棋后的胜负或者平局情况,并且调用相应的函数和显示相应的文字。判断赢,我们用win函数,代码如下:

function win(x,y){
if(Math.abs(matrix[x][0]+matrix[x][1]+matrix[x][2])==3){
return true;
}
if(Math.abs(matrix[0][y]+matrix[1][y]+matrix[2][y])==3){
return true;
}
if(Math.abs(matrix[0][0]+matrix[1][1]+matrix[2][2])==3){
return true;
}
if(Math.abs(matrix[2][0]+matrix[1][1]+matrix[0][2])==3){
return true;
}
return false;
}

首先我们判断第x行,第0,1,2列的数字相加的绝对值是否为3(由于这个函数在下面还要用到,所以我们要做得通用性,所以就用了绝对值)。为什么等于3呢?因为看官是否记得我们上面说的:-1代表【我方下的位置】,0代表【空白格子】,1代表【电脑下的位置】。但凡是下了棋的地方,值总是1或者-1,所以假如有三个同一方棋子连在一起,那这几个值加起来的绝对值一定是3。因此就返回true代表赢了。如果一直判断到最后都没有,就返回false,代表还没有赢。

我们用isEnd判断平局,代码如下:

function isEnd(){
return step>=9;
}

代码很简单,就是判断棋盘占满没有。

其中用到updata负责更新棋盘。代码如下:

function update(x,y){
var v = matrix[x][y];
if(v>0){
chessLayer.graphics.drawArc(10,"green",[x*130+65,y*130+65,40,0,2*Math.PI]);
}else if(v<0){
chessLayer.graphics.drawLine(20,"#CC0000",[130*x+30,130*y+30,130*(x+1)-30,130*(y+1)-30]);
chessLayer.graphics.drawLine(20,"#CC0000",[130*(x+1)-30,130*y+30,130*x+30,130*(y+1)-30]);
}
}

以上的代码也很好理解,就是先取出画的那一点是什么,如果是我方画的(在棋盘数组就是-1),在判断时,取出的值如果小于0,就画个叉叉。如果大于0也就是代表电脑画的(在棋盘数组中代表1),就画个圆。

onDown中还用到了gameOver函数,代码如下:

function gameover(){
backLayer.removeEventListener(LMouseEvent.MOUSE_DOWN,onDown);
backLayer.addEventListener(LMouseEvent.MOUSE_DOWN,function(){
chessLayer.removeAllChild();
backLayer.removeChild(chessLayer);
backLayer.removeChild(overLayer);
removeChild(backLayer);
matrix = [
[0,0,0],
[0,0,0],
[0,0,0]
];
step = 0;
main();
statusContent = "您先请吧……";
addText();
});
}

看似代码有点长,其实很简单,就是简单的移除界面上的一切对象,并且把一些值恢复为默认值。还有onDown中的computerThink函数,代码如下:

function computerThink(){
var b = best();
var x = b.x;
var y = b.y;
matrix[x][y]=1;
step++;
update(x,y); if(win(x,y)){
statusContent = "哈哈你输了!点击屏幕重开游戏。";
gameover();
addText();
}else if(isEnd()){
statusContent = "平局啦~~点击屏幕重开游戏。";
gameover();
addText();
}else{
statusContent = "该你了!!!";
addText();
}
}

首先这个函数用了best函数,这个函数会返回一个要下的位置,然后我们把在棋盘数组中相应的位置设置为1,并且把走的步数+1。然后在相应位置画上。然后判断是否赢了或者平局,或者没赢没输没平局。

best是电脑AI算法部分,代码如下:

function best(){
var bestx;
var besty;
var bestv=0;
for(var x=0;x<3;x++){
for(var y=0;y<3;y++){
if(matrix[x][y]==0){
matrix[x][y] = 1;
step++;
if(win(x,y)){
step--;
matrix[x][y] = 0;
return {'x':x,'y':y,'v':1000};
}else if(isEnd()){
step--;
matrix[x][y]=0;
return {'x':x,'y':y,'v':0};
}else{
var v=worst().v;
step--;
matrix[x][y]=0;
if(bestx==null || v>=bestv){
bestx=x;
besty=y;
bestv=v;
}
}
}
}
}
return {'x':bestx,'y':besty,'v':bestv};
}

算法的思路如下:首先我们遍历棋盘数组,然后判断遍历到的那格如果是空的(也就值是0)就先假设画上,并且将在棋盘数组中相应的位置设为1,表示电脑是下的,然后将走的步数+1。普通的操作就完了,接下来就是给下的这一步评分阶段,代码如下:

if(win(x,y)){
step--;
matrix[x][y] = 0;
return {'x':x,'y':y,'v':1000};
}else if(isEnd()){
step--;
matrix[x][y]=0;
return {'x':x,'y':y,'v':0};
}else{
var v=worst().v;
step--;
matrix[x][y]=0;
if(bestx==null || v>=bestv){
bestx=x;
besty=y;
bestv=v;
}
}

首先我们判断一下如果下了这一步,是否就赢了,如果是,就先把步数改回去,并且把棋盘数组改为下这一步之前的棋盘数组(因为我们在computerThink里要改一道,所以先改回去,避免改重了),然后返回这一步的位置,并且评分为1000。最后这个过程用return来实现,return是神马,我想就不用说了吧。判断是否赢了,我们用了win函数,上面已经说过了。

但是万一下了这一步没赢怎么办,就接着判断是否下了成平局,怎么才能成平局呢?就是把整个棋盘占满且对方没有赢,自己也没有赢就是平局。由于如果别人赢了,就不会进行电脑AI,也就不会调用best函数,换句话说就是不可能进行到这一步;如果是电脑赢了,在上级判断中已经做了相应操作而且用return已经推出函数了,也不会运行到此步,因此直接判断占满没有就可以了。因此用到isEnd函数,上面也用到过,并且讲到过,这里不罗嗦。

万一上面的两种情况都不对怎么办?那就随便下个吧。但是随便下也不能乱下。因此用到了worst来选择“随便下”最好的位置。代码如下:

function worst(){
var bestx;
var besty;
var bestv = 0;
for(var x=0;x<3;x++){
for(var y=0;y<3;y++){
if(matrix[x][y] == 0){
matrix[x][y] = -1;
step++;
if(win(x,y)){
step--;
matrix[x][y] = 0;
return {'x':x,'y':y,'v':-1000};
}else if(isEnd()){
step--;
matrix[x][y]=0;
return {'x':x,'y':y,'v':0};;
}else{
var v=best().v;
step--;
matrix[x][y]=0;
if(bestx==null || v<=bestv){
bestx=x;
besty=y;
bestv=v;
}
} }
}
}
return {'x':bestx,'y':besty,'v':bestv};
}

这个函数和best是反着来的,它是假设下了某一步后,别人会赢或者平局。如果别人走那步会赢,就返回这个位置,把这个位置先占住。平局和对方赢是一样的原理,就是见哪里不对就填哪里。最后的判读是在对方不可能赢的情况下采取的,就是通过best函数取最好的。这个best函数在上面讲过了。不作解释了~~

通过worst这个函数会返回几个值,第一个和第二个是随便下的位置,最后一个是评分。在best中我们把这几个返回值接收到,并且通过评分判断这个选择是否比平局的结果还要差,再返回给computerThink这个函数来绘画布局。因此这个过程很绕。大家要搞清楚关系,搞清楚了就不难了。

『HTML5实现人工智能』小游戏《井字棋》发布,据说IQ上200才能赢【算法&代码讲解+资源打包下载】的更多相关文章

  1. &lbrack;HTML5实现人工智能&rsqb;小游戏《井字棋》发布,据说IQ上200才能赢

    一,什么是TicTacToe(井字棋)   本 游戏 为在下用lufylegend开发的第二款小游戏.此游戏是大家想必大家小时候都玩过,因为玩它很简单,只需要一张草稿纸和一只笔就能开始游戏,所以广受儿 ...

  2. Pascal小游戏 井字棋

    一个很经典的井字棋游戏 Pascal源码Chaobs奉上 注意:1.有的FP版本不支持汉语,将会出现乱码.2.别想赢电脑了,平手不错了. 井字过三关: program TicTacToe; uses ...

  3. Java 小游戏 - 井字棋 v1&period;0 (初步完成) (2018&period;4&period;16更新)

      井字棋游戏初步完成 实现功能:输入位置数据->打印棋盘->判断是否胜利->继续游戏/退出游戏 缺点:没有清屏函数   判断胜利方法太过无脑    package MYGAME; ...

  4. C&plus;&plus;小游戏——井字棋

    #include<cstdio> #include<windows.h> #include<ctime> int main() { srand(time(NULL) ...

  5. python 游戏&lpar;井字棋&rpar;

    1. 游戏思路和流程图 实现功能,现实生活中的井字棋玩法 游戏流程图 2. 使用模块和游戏提示 import random def game_info(): print('欢迎来到井字棋游戏') pr ...

  6. 『HTML5挑战经典』是英雄就下100层-开源讲座&lpar;二&rpar;危险!英雄

    本篇为<『HTML5挑战经典』是英雄就下100层-开源讲座>第二篇,需要用到开源引擎lufylegend,可以到这里下载: 下载地址:http://lufylegend.googlecod ...

  7. 基于HTML5实现五彩连珠小游戏

    今天给大家分享一款基于HTML5实现五彩连珠小游戏.这款游戏的规则:点击彩球移动到期望的位置,每移动一次,画面将随机出现3个新的彩球:当同一颜色的彩球连成5个一行或一列或一斜线时,这5个彩球同时消失, ...

  8. 井字棋小游戏(C语言)

    最近沉迷于<NetHack>.<DCSS>等字符游戏,对其很感兴趣,于是用C语言写了个字符界面的井字棋小游戏.欢迎大家指教. 编写时遇到了一些问题,我原先准备用循环,直到读取到 ...

  9. 井字棋游戏升级版 - TopTicTacToe项目 简介

    一.游戏简介 井字棋是一款世界闻名的游戏,不用我说,你一定知道它的游戏规则. 这款游戏简单易学,玩起来很有意思,不过已经证明出这款游戏如果两个玩家都足够聪明的话, 是很容易无法分出胜负的,即我们得到的 ...

随机推荐

  1. 来份ASP&period;NET Core尝尝

    0x01.前言 学习ASP.NET Core也有一段时间了,虽说很多内容知识点还是处于一知半解的状态,但是基本的,还是 略懂一二.如果有错误,还望见谅. 本文还是和之前一样,Demo+在Linux下运 ...

  2. mobile plugin

    http://fronteed.com/iCheck/ http://spritely.net/documentation/ http://www.mobilexweb.com/blog/mobile ...

  3. Ubuntu14&period;04安装配置SVN及Trac

    还是个实习生的时候,项目管理十分欠缺,会出现很多问题,痛定思痛,决定要改变现状,养成良好的项目管理习惯,看网上工具很多,在这里尝试使用SVN作代码版本控制,使用trac作为项目管理追踪.本文采用的操作 ...

  4. 魔术方法:&lowbar;&lowbar;set、&lowbar;&lowbar;get

    __set: 在设置对象里边不能直接设置(或没有)的属性值的时候,自动去被调用 class Track { private $track_name; public function __set($na ...

  5. PL&sol;SQL学习笔记之日期时间

    一:PL/SQL时间相关类型 PL/SQL提供两个和日期时间相关的数据类型: 日期时间(Datetime)数据类型 时间间隔类型 二:日期时间类型 datetime数据类型有: DATE TIMEST ...

  6. poj1511&comma;线段树扫描线面积

    经典题,线段树扫描线其实类似区间更新,一般的做法是想象一根扫描线从上扫到下或者从左扫到右,本题的做法是从上扫到下 只要扫到了一根水平线,就将其更新到线段树对应区间中,区间和它的子区间是独立更新的 #i ...

  7. ios 耳机插入拔出检测

    [AVAudioSession sharedInstance]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@se ...

  8. IO流 简介 总结 API 案例 MD

    目录 IO 流 简介 关闭流的正确方式 关闭流的封装方法 InputStream 转 String 的方式 转换流 InputStreamReader OutputStreamWriter 测试代码 ...

  9. &lbrack;py&rsqb;django强悍的数据库接口&lpar;QuerySet API&rpar;-增删改查

    django强悍的数据库接口(QuerySet API) 4种方法插入数据 获取某个对象 filter过滤符合条件的对象 filter过滤排除某条件的对象- 支持链式多重查询 没找到排序的 - 4种方 ...

  10. 团队合作项目—(GG队)

    团队展示 一.队名:GG 二.队员信息 队员 学号 叶尚文(队长) 3116008802 蔡晓晴 3216008808 杜婷萱 3216008809 龙剑初 3116004647 于泽浩 311600 ...