英文地址: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};
}
}
}
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。