网页版井字游戏(TicTacToe)人机对战的制作(附思路和源码)
来源:互联网 发布:并查集算法 编辑:程序博客网 时间:2024/06/05 18:27
井字游戏的规则是:在一个井字格子的棋盘里下棋,横竖斜一旦三子连子,则胜。而事实上,遵循一定的规则,该游戏便能保证不败,即至少是平局。
若是两人对战,则仅需要判断“胜负平”三种状态即可,比较简单,而人机对战的难点便在于让机器立于不败之地的下棋规则。下面会重点讲解不败的思路。
先放一张游戏截图,程序演示与源码下载可以去:戳我演示或下载代码
在此先规定电脑一定是先手,如果电脑不是先手的话算法需要另外设计,但方法类似,在此暂不讨论。先说说电脑先手时保持不败的设计思路:
根据下图标出的编号来看:
前两子是有特别讲究的,不然可能会输。
第一子:只能在正中间或者四个角随机选一个,即1、3、5、7、9中任选一个。
第二子:分两种情况讨论。
若第一子在正中,即5号,那么如果玩家下在四个角(1、3、7、9),则第二子下其对角(9、7、3、1)。如果玩家下在某一行或某一列的中间格,即2、4、6、8的某一个,则第二子应下在靠近其的某个角。
比如:电脑(第1子):5
玩家(第1子):1
电脑(第2子):9
电脑(第1子):5
玩家(第1子):2
电脑(第2子):1或3(随机选择)若第一子在四个角,即1、3、7、9其中之一,则也许根据玩家的下法来判断。
若玩家下在正中央,即5号格子,那么第二步就需下第一步的对角,即9、7、3、1。 如果玩家下的不是正中央,那么第二步就下正中央。 举例:电脑(第1子):3
玩家(第1子):5
电脑(第2子):7
电脑(第1子):3
玩家(第1子):9
电脑(第2子):5
第二子以后,只需遵循一定规则即可。规则共三条,从第一条开始往下判断即可(注意一定要按顺序!!!)
1、判断电脑下某处后是否能直接胜出。即电脑下的子是否有二连珠且第三个位置还没有棋子。如果有,就往该处下棋,即可胜出,如果没有,往下。
2、判断玩家棋子是否有二连珠且对应连线的第三个位置为空位的,如果有,将该空位补上,如果没有,第三步。
3、遍历所有还没下棋的格子,对每一个格子进行判断,并统计出如果下这一格,能让几条线有二连珠且第三个位置为空位。选择统计的数量最多的一格下棋。有点抽象,拿下图举个例子,图中编号为下棋的顺序:
看第五步棋,也就是电脑的第三步,如果该棋下在第一行中间位置,能成线的条数为0,如果下在第一列中间位置,成线条数为1(第一列),而下在现在这个位置,成线条数为2(第一列与第三行)。而成线数越多,便越有优势,当成两条线时,便赢了。
下面放上源代码,核心代码有详细注释,可以直接去上面给的链接里切换编辑视图,点击右下角有地方下载。此处代码也可以直接复制即可运行:
html
<!DOCTYPE html><html><head> <meta charset="utf-8"> <title>YinyouTicTacToe</title> <link href="https://fonts.googleapis.com/css?family=Itim" rel="stylesheet"> <link rel="stylesheet" type="text/css" href="css/style.css"></head><body> <h1>Yinyou TicTacToe</h1> <div class="main"> <div id="tic-1" class="tic"> <span id="span-1" class="tic-span"></span> </div> <div id="tic-2" class="tic"> <span id="span-2" class="tic-span"></span> </div> <div id="tic-3" class="tic"> <span id="span-3" class="tic-span"></span> </div> <div id="tic-4" class="tic"> <span id="span-4" class="tic-span"></span> </div> <div id="tic-5" class="tic"> <span id="span-5" class="tic-span"></span> </div> <div id="tic-6" class="tic"> <span id="span-6" class="tic-span"></span> </div> <div id="tic-7" class="tic"> <span id="span-7" class="tic-span"></span> </div> <div id="tic-8" class="tic"> <span id="span-8" class="tic-span"></span> </div> <div id="tic-9" class="tic"> <span id="span-9" class="tic-span"></span> </div> </div> <div class="back"></div> <div class="choose"> <div class="choose-title"> <h2>TicTacToe</h2> <hr> <p>Please choose whether you wanna use? × OR O</p> </div> <div class="choose-bt"> <button id="cha">×</button> <button id="O">o</button> </div> </div> <div class="loser"> Tie! </div> <script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script> <script type="text/javascript" src="js/tic.js"></script></body></html>
css
*{ padding: 0; margin: 0; font-family: 'Itim', cursive;}html,body{ background-color: #cb4042; width: 100%; height: 96%; background-position: fixed;}h1{ text-align: center; color: #fff; font-size: 2em; margin-top: 3%;}.main{ width: 486px; height: 486px; margin: auto; margin-top: 3%; background-color: #fff; text-align: center; z-index: 5;}.tic{ display: inline-block; width: 160px; height: 160px; font-size: 1em; -webkit-text-size-adjust:100%; border:1px solid #cb4042; cursor: pointer; float: left; color: #000; position: relative;}.tic-span{ font-size: 3em; color: #cb4042; position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);}.back{ position: fixed; top: 0; width: 100%; height: 100%; background-color: #000; opacity: .5; z-index: 20;}.choose{ z-index: 21; position: absolute; top: 20%; left: 0; right: 0; margin: auto; width: 700px; color: #fff; background-color: #db4d6d; border-radius: 10px; outline: none; display: none;}.choose-title{ padding-top: 20px; text-align: center;}.choose-bt{ margin-top: 20px; margin-bottom: 15px; /* text-align: right; margin-right: 15px; */ text-align: center;}.choose-bt button{ font-family: sans-serif; width: 40px; height: 40px; font-size: 1.5em; text-align: center; background:#eee; border:1px solid #cb4042; border-radius: 50%; outline: none; color: #cb4042; margin-left: 50px; margin-right: 50px; cursor: pointer;}.choose-bt button:hover{ background-color: #fff;}.loser{ position: absolute; top: 0; height: 100%; width: 100%; text-align: center; font-size: 25em; z-index: 1; color: red; display: none;}
js
$(document).ready(function(){ var cmpt = ""; //电脑用的 var user = ""; //用户用的 var group = [0,0,0,0,0,0,0,0,0]; //记录九个棋格,0表示没下,1表示电脑,2表示玩家 $(".choose").fadeIn(1000); $("#cha").on("click",function(){ $(".choose").fadeOut(1); $(".back").fadeOut(1); cmpt = "O"; user = "×"; pcStep(); }); $("#O").on("click",function(){ $(".choose").fadeOut(1); $(".back").fadeOut(1); cmpt="×"; user="O"; pcStep(); }); //电脑下棋,只下一步 var pcStep = function(){ var step = 0; //记录当前是电脑下的第几步 for(var i in group){ if(group[i] !== 0){ //如果某格已经下过了,step++ step++; } } if(step %2 !== 0){ //如果用户还没下,就return return; } if(step === 0){ //如果电脑当前需要下第一步,因为是第一步所以不需要考虑该位置是否被别人下过的问题 var proStep = [0,2,6,8,4]; //第一步允许下的地方,四个角与中央,这里用的是从0开始 var posit = parseInt(Math.random()*5,10); //从0-4中随机生成一个数,作为proStep的下标,即随机选择一个格子下棋 group[proStep[posit]] = 1; //电脑下棋 $("#span-"+(proStep[posit]+1)).html(cmpt); judge(); return; } if(step === 2){ //如果是电脑下的第二步,分两种情况,分别是电脑第一步下了正中和四个角 if(group[4] === 1){ //如果电脑第一步下的正中,又分两种情况,对方下的是四个角还是中间 var corStep = [0,2,6,8]; //四个角在group的索引 for(var t = 0; t<4;t++){ if(group[corStep[t]] === 2){ //如果玩家下的是某一个角,那就下他对角 var posit = 0; //这里表示的是电脑要下的位置 if(corStep[t] === 0){ posit = 8; }else if(corStep[t] === 8){ posit = 0; }else if(corStep[t] === 2){ posit = 6; }else if(corStep[t] === 6){ posit = 2; } posit = parseInt(posit); group[posit] = 1; //电脑下棋 $("#span-"+(posit+1)).html(cmpt); judge(); return; } } //电脑下的不是某个角,而是在每一行或列的中间位置,电脑就下一个靠着它的角 var posit_g=[0,0]; //如果下在中间,就会有两个角靠着它,随机选一个 var posit = 0; //这就是随机选择之后的位置 if(group[1] === 2){ posit_g[0] = 0; posit_g[1] = 2; }else if(group[3] === 2){ posit_g[0] = 0; posit_g[1] = 6; }else if(group[5] === 2){ posit_g[0] = 2; posit_g[1] = 8; }else if(group[7] === 2){ posit_g[0] = 6; posit_g[1] = 8; } posit = posit_g[parseInt(Math.random()*2)]; posit = parseInt(posit); group[posit] = 1; //电脑下棋 $("#span-"+(posit+1)).html(cmpt); judge(); return; }else{ //如果电脑第一步下的不是正中,而是四个角 //分两种情况,如果对方没下正中,那么就下正中,如果对方下了正中,就下第一步的对角 if(group[4] === 0){ //玩家没下正中,则下正中 group[4] = 1; $("#span-5").html(cmpt); judge(); return; } //下第一步的对角 var posit = 0; //记录要下的从0起的位置 if(group[0] === 1){ posit = 8; }else if(group[8] === 1){ posit = 0; }else if(group[2] === 1){ posit = 6; }else if(group[6] === 1){ posit = 2; } posit = parseInt(posit); group[posit] = 1; //电脑下棋 $("#span-"+(posit+1)).html(cmpt); judge(); return; } } /*如果是第二步以后,分三步 * 1、判断自己是否可以三连珠,如果可以就连上就赢了 * 2、判断对方是否可以三连珠,如果有就堵上,不然就输了 * 3、遍历剩下的还没下的格子,看下在哪一个,能让自己一条线上存在两粒棋子且都能实现三连珠的 这样的线最多 */ //第一步 var first_arr = checkThree(1,group); if(first_arr.length !== 0){ //如果自己可以三连珠 var posit = first_arr[0]; posit = parseInt(posit); group[posit] = 1; //电脑下棋 $("#span-"+(posit+1)).html(cmpt); judge(); return; } //如果自己不能三连珠,就第二步,检查对方是否能三连珠 var second_arr = checkThree(2,group); if(second_arr.length !== 0){ //如果对方可以三连珠 //console.log(second_arr[0]); var posit = second_arr[0]; posit = parseInt(posit); group[posit] = 1; //电脑下棋 $("#span-"+(posit+1)).html(cmpt); judge(); return; } //如果自己和对方都不能三连珠,进入第三步 var third_posit = 0; var third_max = -1; for(var temp in group){ if(group[temp] === 0){ if(third_max === -1){ third_posit = temp; third_max = 0; } var ttt = [].concat(group); ttt[temp] = 1; var temp_arr = checkThree(1,ttt); if(temp_arr.length > third_max){ //如果当前点能让更多个连珠,就决定是它了 third_max = temp_arr.length; third_posit = temp; } } } group[third_posit] = 1; //电脑下棋 //这里必须先转成数字,不然会当成字符串相加,会不对 var wtf = parseInt(third_posit); wtf+=1; $("#span-"+wtf).html(cmpt); judge(); return; }; //检查是否有一点可以三连珠,参数kind如果是1,检查电脑,参数如果是2,检查玩家,如果检查到下某一点可以三连珠, //就返回该格从0开始的下标(数组形式,所有情况都在),如果没找到,返回[] //参数gp是参考的数组,正常情况下就是group,但是考虑到第三步需要判断每一个格子的,那时候要传一个临时数组了 var checkThree = function(kind,gp){ var situ = []; //用来记录所有需要返回的值 var allPossible = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]; //记录八条线 for(var i in allPossible){ var x = allPossible[i][0]; var y = allPossible[i][1]; var z = allPossible[i][2]; if((gp[x] === kind && gp[y] === kind && gp[z] === 0) || (gp[x] === 0 && gp[y] === kind && gp[z] === kind) || (gp[x] === kind && gp[y] === 0 && gp[z] === kind)){ //迭代判断吧,就不复制粘贴了 //如果满足上述条件 //console.log("Three:"+allPossible[i]); if(gp[x] === 0){ situ.push(x); continue; }else if(gp[y] === 0){ situ.push(y); continue; }else if(gp[z] === 0){ situ.push(z); continue; } } } return situ; }; //输赢平的结果显示 state取值1,2,3,1表示电脑赢,2表示玩家赢,3表示平局,a,b,c表示连起来的三格 var result = function(state,a,b,c){ if(state === 1){ console.log('lose'); $(".loser").html("LOSE!"); }else if(state === 2){ console.log('win'); $(".loser").html("WIN!"); }else if(state === 3){ console.log('tie'); $(".loser").html("TIE!"); } if(state !== 3){ $("#tic-"+a).css("background-color","#877f6c"); $("#tic-"+b).css("background-color","#877f6c"); $("#tic-"+c).css("background-color","#877f6c"); } setTimeout(function(){ if(state !== 3){ $("#tic-"+a).css("background-color","#fff"); $("#tic-"+b).css("background-color","#fff"); $("#tic-"+c).css("background-color","#fff"); } $(".loser").fadeIn(400,function(){ setTimeout(function(){ beginAgain(); },2000); }); },1500); }; //出了结果就重新开始 var beginAgain= function(){ for(var yyy = 0; yyy < 9;yyy++){ group[yyy]=0; $("#span-"+(yyy+1)).html(""); } $(".loser").fadeOut(1,function(){ pcStep(); }); } //判断输赢与和棋,一共10种情况,8负1平1还没下完 var judge = function(){ if(group[0] === group[1] && group[1] === group[2] && group[0]!== 0){ //第一行连起来了,这样写是因为格子的编号是从0开始, result(group[0],1,2,3); }else if(group[3] === group[4] && group[4] === group[5] && group[3] !== 0){ //第二行 result(group[3],4,5,6); }else if(group[6] === group[7] && group[7] === group[8] && group[6] !== 0){ //第三行 result(group[6],7,8,9); }else if(group[0] === group[3] && group[3] === group[6] && group[0] !== 0){ //第一列 result(group[0],1,4,7); }else if(group[1] === group[4] && group[4] === group[7] && group[1] !== 0){ //第二列 result(group[1],2,5,8); }else if(group[2] === group[5] && group[5] === group[8] && group[2] !== 0){ //第三列 result(group[2],3,6,9); }else if(group[0] === group[4] && group[4] === group[8] && group[0] !== 0){ //主对角线 result(group[0],1,5,9); }else if(group[2] === group[4] && group[4] === group[6] && group[2] !== 0){ //次对角线 result(group[2],3,5,7); }else{ //没分出胜负 var isTie = true; for(var i = 0; i < 9;i++){ if(group[i] === 0){ //还有格子没下,表示棋还没下完 isTie = false; } } //平局 if(isTie){ result(3,0,0,0); }else{ var step = 0; //记录当前是电脑下的第几步 for(var i in group){ if(group[i] !== 0){ //如果某格已经下过了,step++ step++; } } if(step %2 === 0 && step !== 0){ //如果用户下了,就电脑下 pcStep(); } } } }; //初始化棋盘点击事件,闭包以获取对应的o var initClick = function(i){ $("#tic-"+i).on("click",function(){ var step = 0; //记录当前是下的第几步 for(var j in group){ if(group[j] !== 0){ //如果某格已经下过了,step++ step++; } } if(step %2 === 0){ //如果电脑还没下,点了也没用 return; } if(group[i-1] === 0){ //如果没下,那么按了才有效,并将之设置为玩家下的,并判断是否和棋或者赢了 //console.log("pressed:"+i); //console.log(group); group[i-1] = 2; $("#span-"+i).html(user); judge(); } }); }; for(var i=1;i<=9;i++){ initClick(i); //初始化棋盘点击事件 }});
- 网页版井字游戏(TicTacToe)人机对战的制作(附思路和源码)
- Java坦克大战游戏(人机对战)
- Android实现五子棋游戏(二) 人机对战实现
- 回溯算法--三连游戏(人机对战)
- 如何构建自己的游戏框架并且制作游戏(一)(附源码)
- 如何构建自己的游戏框架并且制作游戏(二)(附源码)
- 简单的五子棋人机对战(delphi)
- Android游戏开发----LibGdx游戏引擎制作微信火焰效果(附源码)
- 人机对战之取火柴游戏
- 五子棋JAVA源码__支持人机、人人对战(转)
- 人机猜拳游戏(修改)
- java 五子棋之人机对战思路详解
- 我用Silverlight做的游戏(附源码)
- 贪吃蛇游戏(附源码)
- GL游戏算法(附源码)
- GL 游戏算法(附源码)
- 贪吃蛇游戏(附源码)
- Cocos2D-HTML5 MoonWarriors游戏Android编译指南(另附iOS编译指南和游戏源码)
- 【C语言】【unix c】静态库的制作和使用
- LeetCode (26)与(80) java practice
- 构造函数与析构函数是否可以抛出异常
- 关于DBLink
- 在jquery的ajax方法中的success中使用return要注意的问题
- 网页版井字游戏(TicTacToe)人机对战的制作(附思路和源码)
- BIO,NIO和AIO三者的原理及区别
- OSM
- 【机试题】六一儿童节--拼多多2018校招内推编程题
- 2017 百度之星B轮初赛(Chess, 度度熊的交易计划, 小小粉丝度度熊)
- 神经网络视频链接
- HDU 6103 Kirinriki
- 从零开始学Scala(一)——Scala环境搭建与第一行代码
- 【C语言】【unix c】动态库的制作和使用