06
2016
05

终极版井字游戏

英文地址:https://mathwithbaddrawings.com/2013/06/16/ultimate-tic-tac-toe/

中文地址:http://blog.jobbole.com/41959/


先玩玩看吧。

获得 Adobe Flash Player

代码如下:

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};
		}
	}

}

源码打包下载

« 上一篇下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。