Square Game
締め切り前日に3時間でつくるといって5日かかった結果がこれだよ!
/**
* Copyright uwi ( http://wonderfl.net/user/uwi )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/tlE3
*/
// 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;
}
}