英文地址:https://mathwithbaddrawings.com/2013/06/16/ultimate-tic-tac-toe/
中文地址:http://blog.jobbole.com/41959/
先玩玩看吧。
代码如下:
package { import com.hanyeah.CustomMenu; import flash.display.Graphics; import flash.display.MovieClip; import flash.display.SimpleButton; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Point; public class Main extends MovieClip { public var pop:MovieClip; public var guizePop:MovieClip; public var btn:SimpleButton; private var map:MovieClip; private var miniMaps:Array; private var count:int = 81; private var enabledMaps:Array = [];//可以落子的小地图 private var callBack:Function;//落子之后回调 private var lastIndex:int = -1;//小格的落子位置,用于决定对手的大格 private var player:int; private const W:int = 100; private const H:int = 100; private const MW:int = 30; private const MH:int = 30; public function Main() { // constructor code stage ? initStage(null) : addEventListener(Event.ADDED_TO_STAGE, initStage); } private function initStage(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, initStage); var menu:CustomMenu = new CustomMenu(this, "版本:1.0.1", "作者:hanyeah", "官网:hanyeah的小站"); pop.visible = false; guizePop.visible = false; map = new MovieClip(); addChild(map); map.x = 50; map.y = 30; var i:int, j:int; miniMaps = []; for (i = 0; i < 3; i++ ) { for (j = 0; j < 3;j++ ) { var minimap:MovieClip = new MovieClip(); map.addChild(minimap); miniMaps.push(minimap); makeButton(minimap); minimap.x = i * W + 5; minimap.y = j * H + 5; minimap.id = i * 3 + j; minimap.mouseChildren = false; } } initGame(); btn.addEventListener(MouseEvent.CLICK, showGuize); } /** * 显示规则 * @param e */ private function showGuize(e:MouseEvent):void { guizePop.visible = true; addChild(guizePop); guizePop.btn.addEventListener(MouseEvent.CLICK, hideGuize); } /** * 隐藏规则 * @param e */ private function hideGuize(e:MouseEvent):void { guizePop.visible = false; guizePop.btn.removeEventListener(MouseEvent.CLICK, hideGuize); } private function initGame():void { reset(); lastIndex = -1; playerLuozi(); } /** * 用户落子 */ private function playerLuozi():void { player = 1; chooseBigMap(); callBack = aiLuozi; for (var i:int = 0; i < enabledMaps.length; i++) { var mc:MovieClip = enabledMaps[i]; mc.mouseEnabled=mc.buttonMode = true; mc.addEventListener(MouseEvent.CLICK, luoziClick); } } /** * ai落子 */ private function aiLuozi():void { player = 2; callBack = playerLuozi; chooseBigMap(); var result:Object; for (var i:int = 0; i < enabledMaps.length; i++) { var b:int = map.data[enabledMaps[i].id];//大格状态 if (b == 3) { //如果双方都已占领此格,争夺此格已经没有意义,很大概率忽略此格。 if (Math.random() > 0.1) continue; } else if (b==player) {//自己已经占领此格 if (Math.random() > 0.5) continue; } var o:Object = ai(enabledMaps[i].data); if (result) { if (o.score > result.score || (o.score == result.score && Math.random() > 0.5)) { result = o; result.mc = enabledMaps[i]; } } else { result = o; result.mc = enabledMaps[i]; } } //trace(result.score,result.index,result.mc.id); //callBack(); var mc:MovieClip = result.mc; lastIndex = result.index; mc.data[lastIndex] = 2; var i0:int = int(lastIndex / 3); var j0:int = int(lastIndex % 3); drawCha(mc.graphics,i0 * MW + 2, j0 * MH + 2, MW - 4, MH - 4,1,0x00ff00); check(mc); } /** * 检测大格、输赢 * @param mc */ private function check(mc:MovieClip):void { count--; mc.count--; // var b:int = map.data[mc.id]; var i0:int = int(mc.id / 3); var j0:int = int(mc.id % 3); if (b != player && b != 3 && checkMini(mc.data,player)) { //占领 if (b==0) { map.data[mc.id] = player; } else { map.data[mc.id] = 3; } if (player==1) { drawCircle(map.graphics, (i0 + 0.5) * W, (j0 + 0.5) * H, 45, 3, 0xff0000); } else { drawCha(map.graphics,i0 * W + 2, j0 * H + 2, W - 4, H - 4,3,0x00ff00); } if (checkBig(map.data)!=0) { pop.visible = true; addChild(pop); pop.tf.text = player == 1?"恭喜你\n你赢了":"你输了\n继续努力吧"; pop.btn.addEventListener(MouseEvent.CLICK, popClickHandler); return; } else if (count == 0) { pop.visible = true; addChild(pop); pop.tf.text = "平局"; pop.btn.addEventListener(MouseEvent.CLICK, popClickHandler); return; } } callBack(); } /** * 结束弹窗点击 * @param e */ private function popClickHandler(e:MouseEvent):void { pop.btn.removeEventListener(MouseEvent.CLICK, popClickHandler); initGame(); pop.visible = false; } /** * 画圆圈 * @param g * @param x * @param y * @param r * @param thick * @param co */ private function drawCircle(g:Graphics,x:int,y:int,r:int,thick:int,co:uint):void { g.lineStyle(thick, co); g.drawCircle(x, y, r); } /** * 画叉叉 * @param g * @param x * @param y * @param w * @param h * @param thick * @param co */ private function drawCha(g:Graphics, x:int, y:int, w:int, h:int, thick:int, co:uint):void { g.lineStyle(thick, co); /* g.moveTo(x, y); g.lineTo(x + w, y + h); g.moveTo(x + w, y); g.lineTo(x, y + h); */ g.moveTo(x+w/2, y); g.lineTo(x + w, y +h / 2); g.lineTo(x + w / 2, y + h); g.lineTo(x, y + h / 2); g.lineTo(x+w/2, y); } /** * 检查大格是否有成线的 * @param data * @return */ private function checkBig(data:Array):int { var i:int; for (i = 0; i < 8; i++) { var a:Array = lines[i]; var a0:int = data[a[0]]; var a1:int = data[a[1]]; var a2:int = data[a[2]]; if (a0 == 0) continue; if ((a0 == a1||a1==3) && (a0 == a2||a2==3)) { return a0!=3?a0:(a1!=3?a1:a2); } } return 0; } /** * 检查小格是否有成线的 * @param data * @return */ private function checkMini(data:Array,p:int):Boolean { var i:int; for (i = 0; i < 8; i++) { var a:Array = lines[i]; var a0:int = data[a[0]]; var a1:int = data[a[1]]; var a2:int = data[a[2]]; if (a0==p&&a1==p&&a2==p) return true; } return false; } /** * 选择可以走的大格 */ private function chooseBigMap():void { if (lastIndex == -1) { enabledMaps = miniMaps; return; } var mc:MovieClip = miniMaps[lastIndex]; if (mc.count == 0) { //已满 enabledMaps = miniMaps; } else if (map.data[lastIndex] == player||map.data[lastIndex] ==3) { //已占领 enabledMaps = miniMaps; } else { enabledMaps = [mc]; } } /** * 鼠标点击落子 * @param e */ private function luoziClick(e:MouseEvent):void { var mc:MovieClip = e.currentTarget as MovieClip; var i0:int = int(mc.mouseX / MW); var j0:int = int(mc.mouseY / MH); var data:Array = mc.data; var index:int = i0 * 3 + j0; if (!data[index]) { lastIndex = index; data[index] = 1; drawCircle(mc.graphics,(i0 + 0.5) * MW, (j0 + 0.5) * MH, 13,1, 0xff0000); for (var i:int = 0; i < enabledMaps.length; i++) { var mc0:MovieClip = enabledMaps[i]; mc0.removeEventListener(MouseEvent.CLICK, luoziClick); mc0.mouseEnabled=mc0.buttonMode = false; } check(mc); } } /** * 重置 */ private function reset():void { var i:int, j:int, k:int; map.graphics.clear(); drawMap(map.graphics, W, H, 2, 0x000000); map.data = arr9(); for (i = 0; i < 9; i++) { var minimap:MovieClip = miniMaps[i]; minimap.graphics.clear(); drawMap(minimap.graphics, MW, MH, 1, 0x000000); minimap.data = arr9(); minimap.count = 9; } count = 81; } /** * * @return */ private function arr9():Array { return [0, 0, 0, 0, 0, 0, 0, 0, 0]; } /** * 画井字棋棋盘 * @param g * @param w * @param h * @param thick * @param co */ private function drawMap(g:Graphics, w:int, h:int, thick:int = 1, co:uint = 0x000000):void { g.beginFill(0xffffff, 0); g.drawRect(0, 0, 3 * w, 3 * h); g.endFill(); g.lineStyle(thick, co, 1); for (var i:int = 1; i < 3; i++) { g.moveTo(i * w, 0); g.lineTo(i * w, 3 * h); g.moveTo(0, i * h); g.lineTo(3 * w, i * h); } } private function makeButton(mc:MovieClip):void { mc.addEventListener(MouseEvent.ROLL_OVER, makeButtonHandler); mc.addEventListener(MouseEvent.ROLL_OUT, makeButtonHandler); } private function makeButtonHandler(e:MouseEvent):void { var mc:MovieClip = e.currentTarget as MovieClip; switch(e.type) { case MouseEvent.ROLL_OVER: mc.alpha = 0.5; break; case MouseEvent.ROLL_OUT: mc.alpha = 1.0; break; } } //-------------------------------- //各种棋形的分数,这个关系到ai的能力 private var s0:int = 0, s1:int = 70, //000 s2:int = 1000, //002 s3:int = 100000, //022 s4:int = 80, //001 s5:int = 8000, //011 s6:int = 1; //012 //每一行或列或斜都由3个位置组成,每个位置可能有3种状态(0:空,1:用户的子,2:ai的子),3个位置共有27种组合方式,下面是各种组合的得分表。 // 000 001 002 010 011 012 020 021 022 private var scoreTable:Array = [s1, s4, s2, s4, s5, s6, s2, s6, s3, // 100 101 102 110 111 112 120 121 122 s4, s5, s6, s5, s0, s0, s6, s0, s0, // 200 201 202 210 200 212 220 221 222 s2, s6, s3, s6, s2, s0, s3, s0, s0] //行、列、斜,总共8条线。 private var lines:Array = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]]; /** * ai策略实现 * @return */ private function ai(data:Array):Object { var i:int, j:int; //创建评分表,初始得分全部为0 var scores:Array = []; for (i = 0; i < 9; i++) { scores[i] = 0; } //根据当前棋形更新评分表 //总共有8条线,每一条线的棋形必然是scoreTable中的一种,根据所属棋形,为该行中所有非空位置加分。 //比如第一行,坐标是【0,1,2】,如果该行的状态是【叉,圈,空】(即【2,1,0】),对应scoreTable中的索引为2*9+1*3+0=21,对应的分数为scoreTable[21]=1,则scores[2]+=1(因为0,1位置已经有棋子,所以得分始终为0). for (i = 0; i < 8; i++) { var b:Array = lines[i]; var c:int = data[b[0]] * 9 + data[b[1]] * 3 + data[b[2]]; for (j = 0; j < 3; j++) { var a:int = b[j]; if (data[a] == 0) scores[a] += scoreTable[c]; } } //从评分表里取出得分最高的位置,如果有并列最高,随机取 var m:int = 0; var s:int = 0; for (i = 0; i < 9; i++) { if (scores[i] > m) { s = i; m = scores[i]; } else if (scores[i] == m) { s = Math.random() > 0.5 ? i : s; } } return {"score": m, "index": s}; } } }
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。