React的井字过三关(3)

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

这是React井字棋项目的最后一篇笔记,记述AI实现。


一. 是开头都会说的原理

但凡懂一点围棋的人都知道“大场”这个概念,可以浅显地把它理解为布局时棋盘上各处的要点。棋谚“金角银边草肚皮”,就很好地说明了大场具有的特征:价值高。

比如没其他子的情况下,先手占星角位,这手棋价值大约是20目。第一手下在顶角,价值可能就1-2目。那就如果第一手占天元,价值...就不好说了。

一个棋类游戏AI实现的难度在于,每手棋的价值其实都是人类经验性的总结。算法无论是穷举或进化,都是人在教机器下棋。人的水平高,机器就学得快。反之亦然。

回到九宫格的井字棋,由于场地条件限制,极大地简化了ai的实现过程。穷举就可是最现实的办法。人只需要根据经验定义每种情况下点位的价值。教会它计算每手棋的价值,再根据价值排序,找出价值最高的一个就可以了。

只考虑一条线路上的情况,当场面出现己方的二子连线时,无疑最佳落子点是这条线的第三点(一着取胜),这手棋对于取胜来说,必然是无价的。但机器不懂人类的价值观,我们就假定这手棋的价值是10000吧。如果这条线路上对方出现二子连线,此时场面最有价值的点位就是封堵对方二子连线的位置,这手棋价值也很重要,但不能超过己方二子连线的价值,可算作1000。同理,当这条线路上,只有你的一个子,你下这手棋可以获得在这条线上的先手优势,有价值,但是比前两种情况都低,算作100。如果你下一手棋,线路上只有对方的一个子。你落子于此,既不能一步取胜,也不能封堵对方的二子连线,且不能在这条线上获得先手优势,那就是废着,昏着(如同围棋中在自己的地盘里填子一样)——尽管我们那么评价它,但这在实战中还是可能出现的,所以它的权重就是-10。

其它情况有没有考虑到呢?有比如线路一个子都没有的情况,先手肯定有一点点优势。但是在井字棋的布局里还不够不典型。就定义为0吧——这里又有坑了。

实际上的情况是,一手棋包含了2-4条线路。综合各条线路的判断下来。累加的权重就是这手棋的价值。


二. AI结构

先在外部js上定义一个ai函数;再把这个ai.js引入到header中。回到组件的页面。在Game组件的渲染方法return前写console.log(ai(lastHistory.squares))

好了,可以想象一下,的井字棋应用之前有了一个history状态。只要在电脑走棋的时候,把这个history状态发送给AI,AI遍历状态,自动找出最佳的落子点,并返回给状态。

  • 判断哪些位置能走
function ai(arr,turnToX){
var loacation=arr.map(function(item,index){
if(item===null){
return index;
}
}).filter(function(item){
return item!==undefined;
});
...
}

通过这一步,得到一个数组。这个数组代表哪些地方(一维坐标)是可以走的。

  • 确定电脑应该扮演的角色,这样才能分析——把turnToX状态传进来
var player=turnToX?'X':'O';
  • 计算能走的点位价值,sort排序返回点位。

之前所谓的ai算法实现的大坑,其实也就是这么回事。


三. 情景分析

到目前为止,拿到了可落子的点位,AI已经实现了一半。

再次请出这张棋盘图:

React的井字过三关(3)

AI就根据这张图照着写。因为不是二维坐标系,所以判断时必须细心了。

AI结构后续

	// ai函数
var oCalc={};
for(var i=0;i<arr.length;i++){
for(j=0;j<loacation.length;j++){
var index=loacation[j];
switch(index){
case 0:
case 2:
case 6:
case 8:
oCalc['loacation'+index]=judgeConner(index);
break;
case 1:
case 3:
case 5:
case 7:
oCalc['loacation'+index.toString()]=judgeSide(index);
break;
case 4:
oCalc['loacation'+index.toString()]=judgeCenter(index);
break;
}
}
}

角位是2,4,6,8。边位是1,3,5,7。中心位置为4。根据不同的情况返回不同的位置到对象oCalc中。(作为测试,可以把oCalc作为ai函数的return值)。

  • 角位,需要判定的有3条线路(横竖斜)
  • 边位,需要判断2条线路(横竖)
  • 中心位置需要判断4条线路:(横竖撇捺)

接下来就是在各个函数里var一个value,一个个对arr进行判定。

当然,有稍微简化一点的办法:意识到很多判断是重复的,比如说边角判断,线路判断2,5,8其实就是棋盘被“推倒之后的”0,1,2。根据这个思想可以定义a,b两个值,当判断的边角是0时,a=1,b=2。当判断的边角是2时,a=5,b=8。同理,当判断的边角是8时,a=7,b=6...如此往复。

处理相同权重的点位

游戏一开始,就发现所有的位置打出来,权重都是0。这也就是之前把单条线路设置为0的坑。最合理的算法应该是设置为1或10。也就是说,中心位置按理是最佳落子点,但是由于判断太繁琐,就省略了这些判断,当成是0。

事实上做这些判断是得不偿失的,因为体现“1”这个价值的点位只有在第一步时才用到。

解决方案:就是在judgeCneter函数中首先来一个判断,如果可落子点为9,直接把value设置为10并马上返回。

页面还可能会有很多相同价值的落子点,找出第一个返回就行了。

排序

当前需要根据oCalc的键值来获取该对象的属性:

var largest={
index:-1,
value:-100
};
for(var attr in oCalc){
if(oCalc[attr]>largeast.value){
largest.index=parseInt(attr.replace('loacation',''));
largest.value=oCalc[attr];
}
} console.log([player,JSON.stringify(largest)]);
return largest.index;
/*******ai函数结束*********/
}

注意largest初始状态。

这下就拿到点位了!


四. 回归React

现在可以做一个按钮,点击请求之后,直接在棋盘上反映AI所走给出来的最佳位置。

给按钮绑定一个handleClick事件,之前的handleClick参数传的是状态,,现在直接把ai的计算结果传进去。

之前的组件已经完备,状态工作正常。以至于不需要再动什么内容。

效果如下

React的井字过三关(3)

React的井字过三关(3)

留意到最后一个状态index是-1.再点击就会报错。所以handleClick开头再加一个如果参数等于-1的判断。

笔者之前用过jQuery写面向过程的井字棋(http://djtao.top/ttt/),二者的代码量差不多,但是React写的思路非常明确,错误非常好排查。深感觉到工程越大,框架优势越明显。

至此,渡劫成功。代码就不放了。

React的井字过三关(3)的更多相关文章

  1. React的井字过三关(2)

    这篇笔记是官方教程的延续笔记,所有代码基于第一篇笔记的结尾代码.旨在解决教程后面提出的五个问题. 一 . 用(X,Y)来取代原有的数字坐标 原来的数字坐标是这样的: 现在的要求是把原来的代码坐标改为二 ...

  2. React的井字过三关(1)

    React的井字过三关(1) 本文系React官方教程的Tutorial: Intro To React的笔记.由笔者用ES5语法改写. 在本篇笔记中,尝试用React构建一个可交互的井字棋游戏. 开 ...

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

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

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

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

  5. Pascal小游戏 井字棋

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

  6. 使用 Vue&period;js 改写 React 的官方教程井字棋

    React 的官方教程井字棋很好的引导初学者一步步走进 React 的世界,我想类似的教程对 Vue.js 的初学者应该也会有启发,于是使用 Vue.js 进行了改写 可以先查看最终的结果,尝试点击体 ...

  7. &lbrack;CareerCup&rsqb; 17&period;2 Tic Tac Toe 井字棋游戏

    17.2 Design an algorithm to figure out if someone has won a game oftic-tac-toe. 这道题让我们判断玩家是否能赢井字棋游戏, ...

  8. quick cocos2d-x 入门---井字棋

    学习quick cocos2d-x 第二天 ,使用quick-x 做了一个井字棋游戏 . 我假设读者已经 http://wiki.quick-x.com/doku.php?id=zh_cn阅读了这个链 ...

  9. 程序设计入门—Java语言 第五周编程题 2井字棋(5分)

    2 井字棋(5分) 题目内容: 嗯,就是视频里说的那个井字棋.视频里说了它的基本思路,现在,需要你把它全部实现出来啦. 你的程序先要读入一个整数n,范围是[3,100],这表示井字棋棋盘的边长.比如n ...

随机推荐

  1. 小tips合集

    No. 1 同一个文本文件里的行结束符如果不一致,比如有些行结束符是0D0A-Windows风格的,而有些行又是UNIX风格的0A,在这种混杂情况下,VIM将非UNIX风格的显示为^M,但如果都是0D ...

  2. &lbrack;No00009C&rsqb;Visual Studio在 解决方案资源管理器 里同步定位打开的文件

    标题的意思就是在使用VS的时候,需要我们打开编辑的文件跟解决方案的资源管理器同步显示,这样方便定位到我们在修改哪个文件. 设置如下: 工具——选项——项目和解决方案——在解决方案资源管理器中跟踪活动项 ...

  3. aop实现日志(转)

    文章来至于http://www.thinksaas.cn/group/topic/341436/ 第一:>>在spring的配置文件里增加以下配置 <!-- 支持 @AspectJ ...

  4. 调用另一个Activity

    <转>调用另一个Activity Intent对象的使用 范例说明 前一个范例介绍了如何运用切换Layout的方式,进行手机页面间的转换.如果要转换的页面并不单只是背景.颜色或文字内容的不 ...

  5. 12个常用的js正则表达式

    在这篇文章里,我已经编写了12个超有用的正则表达式,这可是WEB开发人员的最爱哦. 1.在input框中只能输入金额,其实就是只能输入最多有两位小数的数字 //第一种在input输入框限制 <i ...

  6. Gson解析POJO类中的泛型参数

    在开发Android与API交互的时候,使用Json格式传输,遇到了这样一个情况,返回数据格式POJO类如下: public class ApiResult<T> { private in ...

  7. iOS开发UI篇——Button基础

    一.简单说明 一般情况下,点击某个控件后,会做出相应反应的都是按钮 按钮的功能比较多,既能显示文字,又能显示图片,还能随时调整内部图片和文字的位置 二.按钮的三种状态 1. normal(普通状态) ...

  8. cocos2dx调度器scheduler

    / 让帧循环调用this->update(float dt)函数 // scheduleUpdate(); // 让帧循环去调用制定的函数,时间还是1/60秒 // schedule(sched ...

  9. c&plus;&plus;应用程序文件的编译过程

    这里讲下C++文件的编译过程及其中模板的编译过程: 一:一般的C++应用程序的编译过程.     一般说来,C++应用程序的编译过程分为三个阶段.模板也是一样的. 在cpp文件中展开include文件 ...

  10. AJAX(XMLHttpRequest)进行跨域请求方法详解(一)

    注意:以下代码请在Firefox 3.5.Chrome 3.0.Safari 4之后的版本中进行测试.IE8的实现方法与其他浏览不同. 跨域请求,顾名思义,就是一个站点中的资源去访问另外一个不同域名站 ...