// forked from checkmate's Checkmate Vol.6 Sponser package { import flash.display.*; import flash.events.*; import flash.geom.*; import flash.text.*; import flash.net.*; import flash.system.*; import org.libspark.betweenas3.*; import org.libspark.betweenas3.tweens.ITween; import org.libspark.betweenas3.easing.*; import org.libspark.betweenas3.core.easing.*; // 締め切り前日に3時間でつくるといって5日かかった結果がこれだよ! [SWF(backgroundColor=0xbbffbb)] public class SquareGame extends Sprite { public static const IMGSRC : String = "http://assets.dev.wonderfl.net/images/related_images/e/ea/ea8a/ea8a6b1d37c2cec12e07893c66f164a9da1e92c0"; private static const N : uint = 6; // 縦の長さ private static const M : uint = 2; // 奥行き private static const SCORE_WIN : uint = 5; // 勝利するための駒数 private static const COLORS : Array = [0xffffdddd, 0xffddddff]; private static const COLORS_SELECTED : Array = [0xffff9999, 0xff9999ff]; private static const COLORS_CELL : Array = [0xffffcccc, 0xffccccff]; private var _showLayer : Shape; // セル表示用のレイヤー private var _roboLayer : Sprite; // robo表示用のレイヤー private var _robos : Array; private var _places : Array; // 座標→Robo private var _scores : Array; // スコア格納用 private var _hist : Array; // 移動履歴。要素はArray. (要素の要素は[移動差分x, 移動差分y, 移動方法]) // 座標変換行列 private var _mat : Matrix; // スクリーン座標→フィールド座標 private var _imat : Matrix; // フィールド座標→スクリーン座標 // -1 : 未スタート // 0 : 未選択, 1 : 駒選択時, 2 : 駒移動中 private var _state : int; private var _selected : Robo; // 敵アルゴリズム private var _algo : Algorithm; // スコア表示用 private var _tfScore0 : TextField; private var _tfScore1 : TextField; private var _moveTween : ITween; // 移動記述用Tween private var _leapTween : ITween = null; // 勝利のジャンプ記述用Tween private var _tf : TextField; // デバッグ用 public function SquareGame() { _state = -1; _mat = new Matrix( 180/N, 90/N, -180/N, 90/N, 465/2, 465/2-2*90/N-2*90/N ); // x * 180 / N - y * 180 / N + 465/2, // (x-2) * 90 / N + (y-2) * 90 / N + 465/2 _imat = _mat.clone(); _imat.invert(); _showLayer = new Shape(); addChild(_showLayer); _roboLayer = new Sprite(); addChild(_roboLayer); // フィールドのライン引き graphics.lineStyle(1, 0x000000); var i : int; var c1 : Point, c2 : Point; for(i = -M;i <= N+M;i++){ if(i < 0 || i > N){ c1 = trans(0, i); c2 = trans(N, i); graphics.moveTo(c1.x, c1.y); graphics.lineTo(c2.x, c2.y); c1 = trans(i, 0); c2 = trans(i, N); graphics.moveTo(c1.x, c1.y); graphics.lineTo(c2.x, c2.y); }else{ c1 = trans(-M, i); c2 = trans(N+M, i); graphics.moveTo(c1.x, c1.y); graphics.lineTo(c2.x, c2.y); c1 = trans(i, -M); c2 = trans(i, N+M); graphics.moveTo(c1.x, c1.y); graphics.lineTo(c2.x, c2.y); } } graphics.lineStyle(2, 0xcc0000); c1 = trans(0, 0); graphics.moveTo(c1.x, c1.y); c1 = trans(-M, 0); graphics.lineTo(c1.x, c1.y); c1 = trans(-M, N); graphics.lineTo(c1.x, c1.y); c1 = trans(0, N); graphics.lineTo(c1.x, c1.y); c1 = trans(0, 0); graphics.lineTo(c1.x, c1.y); c1 = trans(N, 0); graphics.moveTo(c1.x, c1.y); c1 = trans(N+M, 0); graphics.lineTo(c1.x, c1.y); c1 = trans(N+M, N); graphics.lineTo(c1.x, c1.y); c1 = trans(N, N); graphics.lineTo(c1.x, c1.y); c1 = trans(N, 0); graphics.lineTo(c1.x, c1.y); graphics.lineStyle(2, 0x0000cc); c1 = trans(0, 0); graphics.moveTo(c1.x, c1.y); c1 = trans(0, -M); graphics.lineTo(c1.x, c1.y); c1 = trans(N, -M); graphics.lineTo(c1.x, c1.y); c1 = trans(N, 0); graphics.lineTo(c1.x, c1.y); c1 = trans(0, 0); graphics.lineTo(c1.x, c1.y); c1 = trans(0, N); graphics.moveTo(c1.x, c1.y); c1 = trans(0, N+M); graphics.lineTo(c1.x, c1.y); c1 = trans(N, N+M); graphics.lineTo(c1.x, c1.y); c1 = trans(N, N); graphics.lineTo(c1.x, c1.y); c1 = trans(0, N); graphics.lineTo(c1.x, c1.y); // スコア表示の準備 _tfScore0 = new TextField(); addChild(_tfScore0); _tfScore0.autoSize = "center"; _tfScore0.x = 50; _tfScore0.y = 330; _tfScore0.defaultTextFormat = new TextFormat("Comic Sans MS", 100, 0xcc0000); _tfScore1 = new TextField(); addChild(_tfScore1); _tfScore1.autoSize = "center"; _tfScore1.x = 400; _tfScore1.y = 330; _tfScore1.defaultTextFormat = new TextFormat("Comic Sans MS", 100, 0x0000cc); // 説明用フィールド var tfInfo : TextField = new TextField(); addChild(tfInfo); tfInfo.width = 300; tfInfo.height = 100; tfInfo.x = 120; tfInfo.text = "1ターンに隣接8マス、または、\n" + "他の隣接ロボを1個飛んでどこまでもいけるよ。\n" + "赤が自軍、青はCPUが動かすよ。\n" + "先に" + SCORE_WIN + "体を対岸に入れた方の勝ちだよ!"; // roboを読み込む var loader : Loader = new Loader(); loader.contentLoaderInfo.addEventListener( Event.COMPLETE, onLoadComplete ); loader.load( new URLRequest(IMGSRC), new LoaderContext(true) ); } private function onLoadComplete(e : Event) : void { var loader : Loader = e.target.loader; var bmdRobo : BitmapData = Bitmap( loader.content ).bitmapData; loader.contentLoaderInfo.removeEventListener( Event.COMPLETE, onLoadComplete ); // roboの生成 _robos = []; for(var i : uint = 0;i < 2*N*M;i++){ var r : Robo = new Robo(bmdRobo, 8, true); _robos.push(r); r.visible = true; _roboLayer.addChild(r); } bmdRobo.dispose(); init(); _tf = new TextField(); _tf.width = 300; _tf.height = 100; // addChild(_tf); stage.addEventListener(MouseEvent.CLICK, onClick); } private function trans(x : Number, y : Number) : Point { return _mat.transformPoint(new Point(x, y)); } private function itrans(x : Number, y : Number) : Point { return _imat.transformPoint(new Point(x, y)); } // ゲームごとの初期化 private function init() : void { _showLayer.graphics.clear(); if(_leapTween != null)_leapTween.stop(); var i : int, j : int; _places = []; for(i = -M;i < N + M;i++){ _places[i] = []; for(j = -M;j < N + M;j++){ _places[i][j] = null; } } _hist = []; for(i = -M;i < N + M;i++){ _hist[i] = []; for(j = -M;j < N + M;j++){ _hist[i][j] = null; } } var p : int = 0; for(i = 1;i <= M;i++){ for(j = 0;j < N;j++){ var c : Point; // 赤軍 c = trans(0.5 - i, 0.5 + j); _robos[p].x = c.x - 10; _robos[p].y = c.y - 27; _robos[p].dir = 1; _robos[p].visible = true; _robos[p].mx = -i; _robos[p].my = j; _robos[p].side = 0; _robos[p].paint(COLORS[0]); _places[int(-i)][j] = _robos[p]; p++; // 青軍 c = trans(0.5 + j, 0.5 - i); _robos[p].x = c.x - 10; _robos[p].y = c.y - 27; _robos[p].dir = 0; _robos[p].visible = true; _robos[p].mx = j; _robos[p].my = -i; _robos[p].side = 1; _robos[p].paint(COLORS[1]); _places[j][int(-i)] = _robos[p]; p++; } } // 敵アルゴリズム _algo = new Algorithm(M, N, _robos, _places); updateScores(); _selected = null; _moveTween = null; _state = 0; } private function onClick(e : MouseEvent) : void { if(_state == -1){ init(); } // 対応する床のフィールド座標を取得 var fp : Point = itrans(mouseX, mouseY); fp.x = Math.floor(fp.x); fp.y = Math.floor(fp.y); // e.targetやフィールド座標からroboに変換 var r : Robo = e.target is Robo ? Robo(e.target) : null; if(r == null && isValid(fp.x, fp.y) && _places[fp.x][fp.y] != null){ r = _places[fp.x][fp.y]; } if(r != null && r.side != 0)r = null; switch(_state){ case 0: if(r != null){ // 味方roboが選択されたら、そのroboの移動可能範囲を表示 showMovable(r.mx, r.my, 0); _state = 1; _selected = r; _selected.paint(COLORS_SELECTED[0]); } break; case 1: if(r != null){ _selected.paint(COLORS[0]); // 味方roboが選択されたら、そのroboの移動可能範囲を表示 showMovable(r.mx, r.my, 0); _selected = r; _selected.paint(COLORS_SELECTED[0]); }else{ if(isValid(fp.x, fp.y) && _hist[fp.x][fp.y] != null){ // 移動可能範囲が選択されたら移動 var rx : Number = _selected.mx; var ry : Number = _selected.my; // 移動roboを最前面に _roboLayer.setChildIndex(_selected, _roboLayer.numChildren-1); var st : Array = []; for each(var ss : Array in _hist[fp.x][fp.y]){ rx += ss[0]; ry += ss[1]; if(ss[2] == 0){ // 歩く st.push(makeMoveTween(_selected, rx, ry)); }else{ // ジャンプする st.push(makeJumpTween(_selected, rx, ry, ss[0], ss[1])); } } _moveTween = BetweenAS3.serialTweens(st); _moveTween.onComplete = function() : void { // 移動後処理 _places[rx][ry] = _selected; _places[_selected.mx][_selected.my] = null; _selected.mx = rx; _selected.my = ry; clearMovable(); updateScores(); var jg : int = judgeEnd(); if(jg != -1){ _state = -1; leap(jg); }else{ doEnemysTurn(); } }; _moveTween.play(); _state = 2; }else{ // 移動可能範囲が選択されなければ無選択状態にする clearMovable(); } } default: break; } } // 敵のターン! private function doEnemysTurn() : void { // アルゴリズムを走らせる var res : Array = _algo.run(1); _selected = res[0]; var fp : Point = res[1]; showMovable(_selected.mx, _selected.my, 1); _selected.paint(COLORS_SELECTED[1]); var rx : Number = _selected.mx; var ry : Number = _selected.my; _roboLayer.setChildIndex(_selected, _roboLayer.numChildren-1); var st : Array = []; for each(var ss : Array in _hist[fp.x][fp.y]){ rx += ss[0]; ry += ss[1]; if(ss[2] == 0){ st.push(makeMoveTween(_selected, rx, ry)); }else{ st.push(makeJumpTween(_selected, rx, ry, ss[0], ss[1])); } } _moveTween = BetweenAS3.serialTweens(st); _moveTween.onComplete = function() : void { // 移動後処理 _places[rx][ry] = _selected; _places[_selected.mx][_selected.my] = null; _selected.mx = rx; _selected.my = ry; clearMovable(); updateScores(); var jg : int = judgeEnd(); if(jg != -1){ _state = -1; leap(jg); }else{ _state = 0; } }; _moveTween.play(); _state = 2; } // 勝利のジャンプをする private function leap(side : uint) : void { zsort(); var indLeap : uint; var i : uint; var tws : Array = []; for each(var r : Robo in _robos){ if(r.side == side){ tws.push(BetweenAS3.repeat( BetweenAS3.physicalTo(r, {y : r.y}, new PhysicalAccelerate(-Math.random() * 5 - 3, 1, 30)), 99999)); } } _leapTween = BetweenAS3.parallelTweens(tws); _leapTween.play(); } // フィールド座標でz=x+yの値に従ってroboをソート private function zsort() : void { var buf : Array = []; for each(var r : Robo in _robos){ buf.push({z : r.mx + r.my, r : r}); } buf.sortOn("z", Array.NUMERIC); for(var i : uint = 0;i < buf.length;i++){ _roboLayer.setChildIndex(buf[i].r, i); } } // 終了判定 private function judgeEnd() : int { if(_scores[0] >= SCORE_WIN)return 0; if(_scores[1] >= SCORE_WIN)return 1; return -1; } // スコアの再計算 private function updateScores() : void { _scores = [0, 0]; for(var i : uint = 0;i < M;i++){ for(var j : uint = 0;j < N;j++){ if(_places[i+N][j] != null && _places[i+N][j].side == 0)_scores[0]++; if(_places[j][i+N] != null && _places[j][i+N].side == 1)_scores[1]++; } } _tfScore0.text = "" + _scores[0]; _tfScore1.text = "" + _scores[1]; } // 移動可能範囲表示解除など private function clearMovable() : void { if(_selected != null)_selected.paint(COLORS[_selected.side]); _selected = null; _showLayer.graphics.clear(); _state = 0; } // ジャンプ移動のTweenを作成 // (tx, ty)は移動先、(dx, dy)は移動差分 private function makeJumpTween(r : Robo, tx : int, ty : int, dx : int, dy : int) : ITween { var c : Point = trans(tx + 0.5, ty + 0.5); var cont : Point = trans(tx - dx * 0.5 + 0.5 - 2, ty - dy * 0.5 + 0.5 - 2); return BetweenAS3.bezierTo(r, {x : c.x - 10, y : c.y - 27}, {x : cont.x - 10, y : cont.y - 27}, 0.5); } // 歩行移動のTweenを作成 // (tx, ty)は移動先 private function makeMoveTween(r : Robo, tx : int, ty : int) : ITween { var c : Point = trans(tx + 0.5, ty + 0.5); return BetweenAS3.to(r, {x : c.x - 10, y : c.y - 27}, 0.5); } // 移動可能範囲を探して表示 private function showMovable(x : int, y : int, side : uint) : void { _showLayer.graphics.clear(); _hist = []; var i : int, j : int; for(i = -M;i < N + M;i++){ _hist[i] = []; for(j = -M;j < N + M;j++){ _hist[i][j] = null; } } _hist[x][y] = []; for(i = -1;i <= 1;i++){ for(j = -1;j <= 1;j++){ if(i == 0 && j == 0)continue; if(isValid(x + i, y + j)){ if(_places[int(x+i)][int(y+j)] == null){ // 隣接駒がなければ _hist[int(x+i)][int(y+j)] = [[i, j, 0]]; paintCell(_showLayer.graphics, x+i, y+j, COLORS_CELL[side]); }else{ // 隣接駒があれば rec(x, y, i, j, side); } } } } } // ジャンプ移動可能範囲を探して表示 private function rec(x : int, y : int, dx : int, dy : int, side : uint) : void { if(!isValid(x + dx, y + dy))return; var jx : int = x + 2 * dx; var jy : int = y + 2 * dy; if( _places[x+dx][y+dy] != null && // 飛び越える対象が存在する isValid(jx, jy) && // ジャンプ先が有効な範囲内 _places[jx][jy] == null && // ジャンプ先が空いている (!_hist[jx][jy] || _hist[jx][jy].length > _hist[x][y].length + 1) // ジャンプ先への移動履歴がまだないか、これから作られる移動履歴より長い場合 ){ _hist[jx][jy] = _hist[x][y].concat([[2*dx, 2*dy, 1]]); paintCell(_showLayer.graphics, jx, jy, COLORS_CELL[side]); var i : int, j : int; for(i = -1;i <= 1;i++){ for(j = -1;j <= 1;j++){ if(i == -dx && j == -dy)continue; if(i == 0 && j == 0)continue; rec(jx, jy, i, j, side); } } } } // 1マスを塗る private function paintCell(g : Graphics, x : int, y : int, c : uint) : void { g.lineStyle(1, 0x000000); g.beginFill(c); var p : Point; p = trans(x, y); g.moveTo(p.x, p.y); p = trans(x, y+1); g.lineTo(p.x, p.y); p = trans(x+1, y+1); g.lineTo(p.x, p.y); p = trans(x+1, y); g.lineTo(p.x, p.y); p = trans(x, y); g.lineTo(p.x, p.y); g.endFill(); } // 移動可能範囲かどうか private function isValid(x : int, y : int) : Boolean { if(x >= -M && x < N + M && y >= 0 && y < N)return true; if(y >= -M && y < N + M && x >= 0 && x < N)return true; return false; } private function tr(...o : Array) : void { _tf.appendText(o + "\n"); } } } import flash.display.*; import flash.events.*; import flash.geom.*; class Robo extends Sprite { private var _frame:Bitmap; // フィールド座標 public var mx : int; public var my : int; public var side : int; // 0 : 味方, 1 : 敵 // 塗られている色 private var _color : uint; public var frames:Array; public var dir : int = 0; // left : 0, right : 1 private var _currentFrame:int = 0; private var _totalFrames:int = 0; private var _frameRate:int = 8; private var _autoPlay:Boolean = false; public function Robo( bmd : BitmapData, frameRate:int = 8, autoPlay:Boolean = false ){ _frameRate = frameRate; _autoPlay = autoPlay; cacheAsBitmap = true; _frame = new Bitmap(); addChild( _frame ); frames = [[], []]; const frameWidth:int = 20; const frameHeight:int = 34; var numFrames:int = bmd.width/frameWidth; for( var i:int=0; i< numFrames; ++i ) { for( var f:int=0; f<_frameRate; ++f ) { // 順向き var frame:BitmapData = new BitmapData( frameWidth, frameHeight, true, 0 ); var matrix:Matrix = new Matrix(); matrix.translate( -i*frameWidth, 0 ); frame.draw( bmd, matrix ); frames[0].push( frame ); // 逆向き var frame2:BitmapData = new BitmapData( frameWidth, frameHeight, true, 0 ); matrix = new Matrix(); matrix.translate( -(i+1)*frameWidth, 0 ); matrix.scale(-1, 1); frame2.draw( bmd, matrix ); frames[1].push( frame2 ); } } _totalFrames = frames[0].length; _color = 0xffffffff; update(); if( _autoPlay ) play(); } // 非透過の白い部分をcで塗る public function paint(c : uint) : void { for each(var frame : Array in frames){ for each(var bmd : BitmapData in frame){ bmd.threshold(bmd, bmd.rect, new Point(), "==", _color, c, 0xffffffff, true); } } _color = c; } private function update(e:Event=null):void { _frame.bitmapData = frames[dir][_currentFrame]; _currentFrame = (_currentFrame+1) % _totalFrames; } public function play():void { addEventListener( Event.ENTER_FRAME, update ); } public function stop():void { removeEventListener( Event.ENTER_FRAME, update ); } public function get currentFrame():int { return _currentFrame+1; } public function get totalFrames():int { return _totalFrames; } } class Algorithm { private var _M : uint; private var _N : uint; private var _robos : Array; private var _places : Array; public function Algorithm(M : uint, N : uint, robos : Array, places : Array) : void { _M = M; _N = N; _robos = robos; _places = places; } // [Robo, Point] public function run(side : int) : Array { var maxScore : Number = Number.MIN_VALUE; var bestMovs : Array = []; // 各roboについて for each(var r : Robo in _robos){ if(r.side == 1){ // 移動可能範囲すべてをなめて var movs : Array = enumMovable(r.mx, r.my); for each(var mov : Point in movs){ // もっとも劇的に移動する(ただしゴールに入る移動のスコアは3倍にする)移動を選ぶ var score : Number = mov.y - r.my; if(mov.y >= _N && r.my < _N)score *= 3; if(score > maxScore){ maxScore = score; bestMovs = [[r, mov]]; }else if(score == maxScore){ bestMovs.push([r, mov]); } } } } if(bestMovs.length == 0)return null; return bestMovs[int(Math.random() * bestMovs.length)]; // 同スコアの中からランダムに選択 } // 移動可能範囲の列挙(showMovableの劣化版) private function enumMovable(x : int, y : int) : Array { var i : int, j : int; var hist : Array = []; for(i = -_M;i < _N + _M;i++){ hist[i] = []; for(j = -_M;j < _N + _M;j++){ hist[i][j] = null; } } for(i = -1;i <= 1;i++){ for(j = -1;j <= 1;j++){ if(i == 0 && j == 0)continue; if(isValid(x + i, y + j)){ if(_places[int(x+i)][int(y+j)] == null){ // 隣接駒がなければ hist[int(x+i)][int(y+j)] = 1; }else{ // 隣接駒があれば rec(x, y, i, j, hist); } } } } var ret : Array = []; for(i = -_M;i < _N + _M;i++){ for(j = -_M;j < _N + _M;j++){ if(hist[i][j])ret.push(new Point(i, j)); } } return ret; } // ジャンプ移動可能範囲の列挙 private function rec(x : int, y : int, dx : int, dy : int, hist : Array) : void { if(!isValid(x + dx, y + dy))return; var jx : int = x + 2 * dx; var jy : int = y + 2 * dy; if( _places[x+dx][y+dy] != null && isValid(jx, jy) && _places[jx][jy] == null && !hist[jx][jy] ){ hist[jx][jy] = 1; var i : int, j : int; for(i = -1;i <= 1;i++){ for(j = -1;j <= 1;j++){ if(i == -dx && j == -dy)continue; if(i == 0 && j == 0)continue; rec(jx, jy, i, j, hist); } } } } private function isValid(x : int, y : int) : Boolean { if(x >= -_M && x < _N + _M && y >= 0 && y < _N)return true; if(y >= -_M && y < _N + _M && x >= 0 && x < _N)return true; return false; } } Square Game