forked from: Reversi
リバーシ
@mxmlc -o bin/Reversi.swf -load-config+=obj\Alltest3Config.xml
@author jc at bk-zen.com
/**
* Copyright osamX ( http://wonderfl.net/user/osamX )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/xX3L
*/
// forked from bkzen's Reversi
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.GradientType;
import flash.display.Graphics;
import flash.display.Loader;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
/**
* リバーシ
* @mxmlc -o bin/Reversi.swf -load-config+=obj\Alltest3Config.xml
* @author jc at bk-zen.com
*/
[SWF (backgroundColor = "0xFFFFFF", frameRate = "30", width = "465", height = "465")]
public class Reversi extends Sprite
{
private const phaseInit: int = 0;
private const phaseStart: int = 1;
private const phaseTurnInit: int = 5;
private const phaseTurnWait: int = 6;
private const phaseTurnStoneMove: int = 7;
private const phaseTurnChange: int = 8;
private const phaseResult: int = 50;
private const phaseEnd: int = 100;
private const STN_URL: String = "http://assets.wonderfl.net/images/related_images/b/bc/bc29/bc29dba5e4eb1b1e57129ac8dc783a8347c5c2f3";
private var phase: int = phaseInit;
private var board: Board;
private var boardBase: Shape;
private var boardView: BitmapData;
private var cellNavi: Sprite;
private var cellBtns: Array = [];
private var cellBtnFirst: CellBtn;
private var stnRect: Rectangle;
private var stnPoint: Point;
private var player1: Player;
private var player2: Player;
private var nowTurn: int;
private var pass: int;
private var infoTxt: TextField;
private var stoneBmd: BitmapData;
private var loader: Loader;
private const DEPTH:uint = LENGTH;
public function Reversi()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e: Event = null): void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
//
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComp);
loader.load(new URLRequest(STN_URL), new LoaderContext(true));
}
/**
* 石画像読み込み完了
* @param e
*/
private function onComp(e: Event): void
{
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onComp);
stoneBmd = Bitmap(loader.content).bitmapData;
make();
addEventListener(Event.ENTER_FRAME, loop);
}
/**
* 色々作る
*/
private function make():void
{
board = new Board();
boardBase = new Shape();
cellNavi = new Sprite();
var g: Graphics = boardBase.graphics;
var m: Matrix = new Matrix();
m.createGradientBox(SIZE, SIZE, 45 * Math.PI / 180);
g.beginGradientFill(GradientType.LINEAR, [0x00CC00, 0x006600], [1, 1], [0x00, 0xFF], m);
g.lineStyle(1);
var ix: int, iy: int, beforeBtn: CellBtn;
for (var i:int = 0; i < LENGTH; i++)
{
ix = (i % 8) * CEL_SIZE;
iy = (i >> 3) * CEL_SIZE;
var cellBtn: CellBtn = new CellBtn(ix, iy, i, onClickCell);
cellNavi.addChild(cellBtn);
cellBtns.push(cellBtn);
if (beforeBtn == null) cellBtnFirst = beforeBtn = cellBtn;
else beforeBtn = beforeBtn.next = cellBtn;
g.drawRect(ix, iy, CEL_SIZE, CEL_SIZE);
}
var bmp: Bitmap = new Bitmap(boardView = new BitmapData(SIZE, SIZE, true, 0x0), "auto", true);
bmp.x = boardBase.x = cellNavi.x = board.x;
bmp.y = boardBase.y = cellNavi.y = board.y;
addChild(boardBase);
addChild(bmp);
addChild(cellNavi);
stnRect = new Rectangle(0, 0, CEL_SIZE, CEL_SIZE);
stnPoint = new Point();
infoTxt = new TextField();
infoTxt.background = infoTxt.border = true;
infoTxt.width = stage.stageWidth - board.x * 2;
infoTxt.height = stage.stageHeight - SIZE - board.y * 3;
infoTxt.x = board.x;
infoTxt.y = board.y * 2 + SIZE;
addChild(infoTxt);
}
/**
* セルをクリックしたときのイベントハンドラ
* @param e
*/
private function onClickCell(e: MouseEvent):void
{
var i: int = CellBtn(e.target).index;
var cb: CellBtn = cellBtnFirst;
do { cb.visible = false; } while (cb = cb.next);
board.put(i, nowTurn);
phase = phaseTurnStoneMove;
}
private function loop(e: Event ): void
{
var c: Cell, cs: Cells, nowColor: String = "", i: int;
switch (phase)
{
case phaseInit:
/**
* 初期化
*/
boardView.lock();
boardView.fillRect(boardView.rect, 0x0);
c = board.first;
do
{
if (c.stone != STN_N)
{
stnPoint.x = c.posX;
stnPoint.y = c.posY;
stnRect.x = c.frame * CEL_SIZE;
boardView.copyPixels(stoneBmd, stnRect, stnPoint);
}
}
while (c = c.next);
boardView.unlock();
phase = phaseStart;
break;
case phaseStart:
/**
* スタート。何かもっとやることあったようなきがしたけど忘れた。
*/
nowTurn = STN_B;
pass = 0;
phase = phaseTurnInit;
log("開始");
//trace(board.numStnB + " : " + board.numStnW);
break;
case phaseTurnInit:
/**
* ターン開始。
* おける場所をハイライトする。
*/
nowColor = (nowTurn == STN_B ? "黒" : "白");
log(nowColor + "の番です", false);
cs = board.canPuts(nowTurn);
//trace(cs.length);
if (cs.length == 0)
{
pass++;
log(nowColor + "は置ける場所がないのでパスします。", false);
phase = phaseTurnChange;
if (pass == 2)
{
phase = phaseResult;
return;
}
return;
}
else
{
pass = 0;
c = cs.first;
do
{
CellBtn(cellBtns[c.index]).visible = true;
}
while (c = c.next);
}
cs.clear();
phase = phaseTurnWait;
break;
case phaseTurnWait:
if(nowTurn == STN_W){
cs = board.canPuts(nowTurn);
board.put(comMotion(STN_W), nowTurn);
var cb: CellBtn = cellBtnFirst;
do { cb.visible = false; } while (cb = cb.next);
cs.clear();
phase = phaseTurnStoneMove;
}
// 待ち
break;
case phaseTurnStoneMove:
/**
* 石を動かす
*/
boardView.lock();
boardView.fillRect(boardView.rect, 0x0);
c = board.first;
do
{
if (c.moveF != 0)
{
c.frame += c.moveF;
if (c.frame == 0 || c.frame == STN_F) c.moveF = 0;
else i++
}
if (c.stone != STN_N)
{
stnPoint.x = c.posX;
stnPoint.y = c.posY;
stnRect.x = c.frame * CEL_SIZE;
boardView.copyPixels(stoneBmd, stnRect, stnPoint);
}
}
while (c = c.next);
boardView.unlock();
if (i == 0) phase = phaseTurnChange;
break;
case phaseTurnChange:
/**
* ターン終了して相手のターンに変える。
*/
if (board.numStnB + board.numStnW == LENGTH)
{
phase = phaseResult;
return;
}
nowTurn = nowTurn ^ 3;
phase = phaseTurnInit;
break;
case phaseResult:
/**
* 結果表示
*/
log("終了です。", false);
log("黒 : 白 = " + board.numStnB + " : " + board.numStnW + " で " + (board.numStnB == board.numStnW ? "引き分けです。" : board.numStnB < board.numStnW ? "白の勝ちです。" : "黒の勝ちです。"));
phase = phaseEnd;
break;
}
}
/**
* ログ表示
* @param str
* @param clear
*/
private function log(str: String, clear: Boolean = false): void
{
if (clear) infoTxt.text = str;
else infoTxt.appendText(str + "\n");
infoTxt.scrollV = infoTxt.maxScrollV;
}
/**
* 盤面の評価 ここを変えればもっと強くなるはず 開放度理論とか使えばいいかも?
* @param bd
* @param dp
* @param stn
*/
private function eval(bd:Board, dp:uint, stn:int):Number {
var score:Number = 0;
score += stn == STN_W ? -bd.canPuts(STN_B).length * dp : bd.canPuts(STN_W).length * dp;
score += (bd.numStnW - bd.numStnB) * (LENGTH-dp);
const map:Array = bd.dataToArray(bd.toData());
const KADO:Number = 100 * dp;
if (map[ 0] != STN_N) score += map[ 0]==STN_W ? KADO : -KADO;
if (map[ 7] != STN_N) score += map[ 7]==STN_W ? KADO : -KADO;
if (map[56] != STN_N) score += map[56]==STN_W ? KADO : -KADO;
if (map[63] != STN_N) score += map[63]==STN_W ? KADO : -KADO;
return score;
}
/**
* COMはあほの子 バグ持ち?
* @param stn
*/
private function comMotion(stn:int):Number {
var cell:Cell = board.canPuts(stn).first;
var bestScore:Number = -9999999;
var bestMotion:uint = 0;
for (var i:uint = 0; i < board.canPuts(stn).length; i++) {
var tmpbd:Board = board.clone();
tmpbd.put(cell.index, stn);
var tmpScore:Number = minmax(DEPTH, tmpbd, stn^3, eval(tmpbd, DEPTH, stn));
//trace(board.toData());
//trace(tmpbd.toData());
if (tmpScore > bestScore) {
bestScore = tmpScore;
bestMotion = i;
}
tmpbd.clear();
cell = cell.next;
}
cell = board.canPuts(stn).first;
for (var j:uint = 0; j < bestMotion; j++) cell = cell.next; //cellに最善の手を代入
var ans:int = cell.index;
cell.clear();
trace("ans" + bestScore);
return ans;
}
/**
* MINMAXでゲーム木探索
* @param dp
* @param bd
* @param stn
* @param sc
*/
private function minmax(dp:uint, bd:Board, stn:int, sc:Number):Number {
if (dp <= 1 || bd.numStnB + bd.numStnW >= LENGTH) {
return sc + eval(bd, dp, stn);
}
if (bd.canPuts(stn).length == 0) return minmax(dp - 1, bd, stn ^ 3, sc + eval(bd, dp, stn));
var bestScore:Number = stn == STN_W ? -9999999 : 9999999;
for (var cell:Cell = bd.canPuts(stn).first; cell; cell = cell.next) { //最善の手をさがす
var tmpbd:Board = bd.clone();
tmpbd.put(cell.index, stn);
var tmpScore:Number = minmax(dp - 1, tmpbd, stn ^ 3, sc + eval(bd, dp, stn));
if (stn == STN_W && bestScore < tmpScore) { //俺のターン!
bestScore = tmpScore;
}else if (stn == STN_B && bestScore > tmpScore) { //プレイヤーのターン!
bestScore = tmpScore;
}
tmpbd.clear();
cell.clear();
}
//trace("stn" + stn + " dp" + dp + " len" + bd.canPuts(stn).length + " best" + bestScore);
return bestScore;
}
}
}
const STN_N: int = 0; // 何もなし
const STN_B: int = 1; // 黒
const STN_W: int = 2; // 白
const STN_F: int = 6; // 石のフレーム最後尾(0スタート)
const CEL_SIZE: int = 50; // セルのサイズ
const SIZE: int = CEL_SIZE * 8; // 盤のサイズ
const LENGTH: int = 64; // セルの総数
class Board
{
private const DEF_BOARD: Array = [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 2, 0, 0, 0,
0, 0, 0, 2, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
];
public var first: Cell;
public var cells: Array;
private var _numStnB: int;
private var _numStnW: int;
public var x: int = 5;
public var y: int = 5;
function Board(arr: Array = null)
{
if (arr == null) arr = DEF_BOARD.concat();
makeBoard(arr);
}
/**
* この場所におけるかチェック(多分使わない)
* @param index
* @return
*/
public function canPut(index: int): Boolean
{
return false;
}
/**
* 置けるセルを取得
* @param stn
* @return
*/
public function canPuts(stn: int): Cells
{
var c: Cell = first, res: Cell, tmp: Cell, l: int;
var opp: int = stn ^ 3;
do
{
if (c.stone == STN_N && checkLine(c, stn, opp))
{
if (res == null) tmp = res = c.clone();
else tmp = tmp.next = c.clone();
l++;
}
}
while (c = c.next);
return new Cells(res, l);
}
/**
* index の場所に 石をおく
* @param index
* @param stn
*/
public function put(index: int, stn: int): void
{
var cell: Cell = cells[index], c: Cell, opp: int = stn ^ 3, i: int = 1, mf: int = stn == STN_B ? -1 : 1;
cell.frame = stn == STN_B ? 0 : STN_F;
cell.stone = stn;
c = cell.tl; // 左上
if (c && c.stone == opp && checkTL(c, stn)) { do {
if (c.stone == stn) break;
c.moveF = mf, i++, c.stone = stn;
} while (c = c.tl); }
c = cell.t; // 上
if (c && c.stone == opp && checkT(c, stn) ) { do {
if (c.stone == stn) break;
c.moveF = mf, i++, c.stone = stn;
} while (c = c.t); }
c = cell.tr; // 右上
if (c && c.stone == opp && checkTR(c, stn)) { do {
if (c.stone == stn) break;
c.moveF = mf, i++, c.stone = stn;
} while (c = c.tr); }
c = cell.l; // 左
if (c && c.stone == opp && checkL(c, stn) ) { do {
if (c.stone == stn) break;
c.moveF = mf, i++, c.stone = stn;
} while (c = c.l); }
c = cell.r; // 右
if (c && c.stone == opp && checkR(c, stn) ) { do {
if (c.stone == stn) break;
c.moveF = mf, i++, c.stone = stn;
} while (c = c.r); }
c = cell.bl; // 左下
if (c && c.stone == opp && checkBL(c, stn)) { do {
if (c.stone == stn) break;
c.moveF = mf, i++, c.stone = stn;
} while (c = c.bl); }
c = cell.b; // 下
if (c && c.stone == opp && checkB(c, stn) ) { do {
if (c.stone == stn) break;
c.moveF = mf, i++, c.stone = stn;
} while (c = c.b); }
c = cell.br; // 右下
if (c && c.stone == opp && checkBR(c, stn)) { do {
if (c.stone == stn) break;
c.moveF = mf, i++, c.stone = stn;
} while (c = c.br); }
if (stn == STN_B)
{
_numStnB += i;
_numStnW -= i - 1;
}
else
{
_numStnW += i;
_numStnB -= i - 1;
}
}
/**
* どこかのラインが取れるかチェック
* @param cell
* @param stn
* @param opp
* @return
*/
private function checkLine(cell: Cell, stn: int, opp: int): Boolean
{
var c: Cell;
c = cell.tl; // 左上
if (c && c.stone == opp && checkTL(c, stn)) return true;
c = cell.t; // 上
if (c && c.stone == opp && checkT(c, stn) ) return true;
c = cell.tr; // 右上
if (c && c.stone == opp && checkTR(c, stn)) return true;
c = cell.l; // 左
if (c && c.stone == opp && checkL(c, stn) ) return true;
c = cell.r; // 右
if (c && c.stone == opp && checkR(c, stn) ) return true;
c = cell.bl; // 左下
if (c && c.stone == opp && checkBL(c, stn)) return true;
c = cell.b; // 下
if (c && c.stone == opp && checkB(c, stn) ) return true;
c = cell.br; // 左下
if (c && c.stone == opp && checkBR(c, stn)) return true;
return false;
}
// その方向に取れるのがあるかどうかをチェックする。TL(左上), T(上), TR(右上), L(左), R(右), BL(左下), B(下), BR(右)
private function checkTL(c: Cell, stn: int): Boolean { do { if (c.stone == stn) return true; else if (c.stone == STN_N) return false; } while (c = c.tl); return false; }
private function checkT( c: Cell, stn: int): Boolean { do { if (c.stone == stn) return true; else if (c.stone == STN_N) return false; } while (c = c.t ); return false; }
private function checkTR(c: Cell, stn: int): Boolean { do { if (c.stone == stn) return true; else if (c.stone == STN_N) return false; } while (c = c.tr); return false; }
private function checkL( c: Cell, stn: int): Boolean { do { if (c.stone == stn) return true; else if (c.stone == STN_N) return false; } while (c = c.l ); return false; }
private function checkR( c: Cell, stn: int): Boolean { do { if (c.stone == stn) return true; else if (c.stone == STN_N) return false; } while (c = c.r ); return false; }
private function checkBL(c: Cell, stn: int): Boolean { do { if (c.stone == stn) return true; else if (c.stone == STN_N) return false; } while (c = c.bl); return false; }
private function checkB( c: Cell, stn: int): Boolean { do { if (c.stone == stn) return true; else if (c.stone == STN_N) return false; } while (c = c.b ); return false; }
private function checkBR(c: Cell, stn: int): Boolean { do { if (c.stone == stn) return true; else if (c.stone == STN_N) return false; } while (c = c.br); return false; }
/**
* ボードの初期化
* @param arr
*/
private function makeBoard(arr: Array):void
{
var i: int, n: int = arr.length, c: Cell, tmp: Array = [], s: int;
tmp.push(first = c = new Cell(i, arr[i]));
for (i = 1; i < n; i++)
{
tmp.push(c.next = c = new Cell(i, s = arr[i]));
if (s == STN_B) _numStnB ++;
else if (s == STN_W) _numStnW ++;
}
i = 0, c = first;
do
{
if (i % 8)
{
if (i >> 3) c.tl = tmp[i - 9];
if (63 - i >> 3) c.bl = tmp[i + 7];
c.l = tmp[i - 1];
}
if (i % 8 != 7)
{
if (i >> 3) c.tr = tmp[i - 7];
if (63 - i >> 3) c.br = tmp[i + 9];
c.r = tmp[i + 1];
}
if (i >> 3) c.t = tmp[i - 8];
if (63 - i >> 3) c.b = tmp[i + 8];
i++;
}
while (c = c.next);
cells = tmp;
}
/**
* 何かに使うかもってことでとりあえず作った str から Array に変える
* @param str
* @return
*/
public function dataToArray(str: String): Array
{
return str.split(":");
}
/**
* 現在のマップをStringに変える。
* @return
*/
public function toData(): String
{
var c: Cell, res: String = "";
c = first;
do
{
res += c.stone + ":";
}
while (c = c.next);
return res.slice(0, res.length - 1);
}
/**
* 黒の数
*/
public function get numStnB(): int { return _numStnB; }
/**
* 白の数
*/
public function get numStnW(): int { return _numStnW; }
public function clear():void {
first.clear();
cells.splice(0);
}
public function clone():Board {
var bd:Board = new Board(dataToArray(toData()));
return bd;
}
}
/**
* 1マス
*/
class Cell
{
public var next: Cell;
public var tl: Cell, t: Cell, tr: Cell, l: Cell, r: Cell, bl: Cell, b: Cell, br: Cell;
public var index: int;
public var stone: int;
public var data1: Cell;
public var data2: int;
public var frame: int;
public var moveF: int;
public var posX: int, posY: int;
function Cell(i: int, s: int)
{
index = i;
stone = s;
frame = s == STN_W ? STN_F : 0;
posX = (index % 8) * CEL_SIZE;
posY = (index >> 3) * CEL_SIZE;
}
public function clone(): Cell
{
var c: Cell = new Cell(index, stone);
c.tl = tl, c.t = t, c.tr = tr, c.l = l, c.r = r, c.bl = bl, c.b = b, c.br = br;
return c;
}
/**
* Cloneしたときはこれで消さないと GC対象にならないので注意。
*/
public function clear(): void
{
tl = t = tr = l = r = bl = b = br = next = data1 = null;
}
}
/**
* マスの塊。
*/
class Cells
{
public var first: Cell;
public var length: int;
function Cells(f: Cell, l: int)
{
first = f;
length = l;
}
public function clear(): void
{
var c: Cell = first, next: Cell;
if (c == null) return;
do
{
next = c.next;
c.clear();
}
while ((c = next));
first = null;
}
}
import flash.display.Sprite;
import flash.events.MouseEvent;
/**
* ボタン。ユーザーが取れる位置のボタン。
*/
class CellBtn extends Sprite
{
public var next: CellBtn;
private var _index: int;
function CellBtn(ix: int, iy: int, i: int, handler: Function)
{
x = ix, y = iy, _index = i;
graphics.beginFill(0xFFFFFF, 0.5);
graphics.drawRect(0, 0, CEL_SIZE, CEL_SIZE);
buttonMode = true, visible = false;
addEventListener(MouseEvent.CLICK, handler);
}
public function get index(): int { return _index; }
}
/**
* プレイヤー
*/
class Player
{
protected var _stone: int;
function Player(s: int) { _stone = s; }
public function get stone(): int { return _stone; }
}