/**
* Copyright hermit ( http://wonderfl.net/user/hermit )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/43Sb
*/
/**
* Tic-tac-toe, ○×ゲーム, 三目並べ.
* since 2010-06-24
*
* TODO
* [ ] レベル選択機能
* [ ] level 1: ランダムに取る
* [ ] level 2: 考えて取る
* [ ] ボタンにフォーカスしたら目立たせる(白っぽくする?)
* [ ] 枠○×を「ぼわっと光った線」で描画する
* [ ] 勝敗がついた行・列を強く光らせる
* [ ] 枡を取る時の表現を「ぼわっと光る」ものにする
* [ ] ダイアログに影をつける
* [ ] 先攻を×にする
* [ ] 得点記録
* [ ] Flash Builderから実行した時、意図せず拡大されて下にはみ出すのを解決する
* [ ] 広告を入れる
* [ ] BetweenAS3を使ってみる http://www.be-interactive.org/index.php?itemid=472
* http://wonderfl.kayac.com/code/7e3f080227a081bafbcdb09dd898b27c057cbf76
* [/] リプレイ時に左上に○と×が重なって表示されるバグを解決
* [/] 勝敗を大きく表示する
* [/] リプレイ機能
*/
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
[SWF(backgroundColor = 0)]
public class Main extends Sprite
{
private var dialog:Dialog =
new Dialog('Who marks first?',
'You', you1st, 'Me', me1st);
public function Main()
{
game = this;
addChild(board);
//addChild(msgArea);
addChild(dialog);
}
private function you1st(e:MouseEvent):void
{
removeChild(dialog);
board.clear();
board.enableBoxButtons();
}
private function me1st(e:MouseEvent):void
{
removeChild(dialog);
board.clear();
board.myTurn();
board.enableBoxButtons();
}
public function replay():void
{
addChild(dialog);
}
}
}
var game:Main;
const board:Board = new Board;
///**
// * デバッグメッセージ表示領域
// */
//const msgArea:MessageArea = new MessageArea;
// ゲーム本体 ///////////////////////////////////////////////
const T:uint = 10; // thickness 枠線、○、×の太さ
const UNIT:uint = 148; // 桝目の大きさ (stage.stageWidth - 5*2 - 10) / 3
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.filters.GlowFilter;
import flash.utils.Timer;
class Board extends Sprite
{
private const boxes:Array = [[], [], []];
private const resultMsg:ResultMsg = new ResultMsg;
public function Board()
{
drawGrid();
for (var i:uint = 0; i < 3; ++i)
for (var j:uint = 0; j < 3; ++j) {
boxes[i][j] = new Box;
boxes[i][j].x = T + UNIT * i;
boxes[i][j].y = T + UNIT * j;
addChild(boxes[i][j]);
}
}
public function clear():void
{
for each (var line:Array in boxes)
for each (var box:Box in line)
box.clear();
}
/**
* 枠を描く
*/
private function drawGrid():void
{
x = y = 5; // padding... 座標をここに書くのは変
//filters = [new BlurFilter(2, 2)];
//filters = [new GlowFilter(0xffffff)];
with (graphics) {
lineStyle(T, 0xffffff);
moveTo(T/2 + UNIT, T/2);
lineTo(T/2 + UNIT, T/2 + UNIT * 3);
moveTo(T/2 + UNIT * 2, T/2);
lineTo(T/2 + UNIT * 2, T/2 + UNIT * 3);
moveTo(T/2, T/2 + UNIT);
lineTo(T/2 + UNIT * 3, T/2 + UNIT);
moveTo(T/2, T/2 + UNIT * 2);
lineTo(T/2 + UNIT * 3, T/2 + UNIT * 2);
}
}
/**
* コンピュータが一手進める
*/
public function myTurn():void
{
// Level 0: 端から取っていく
for each (var line:Array in boxes)
for each (var box:Box in line)
if (box.status == Box.NONE) {
box.mark();
return;
}
}
public function enableBoxButtons():void
{
for each (var line:Array in boxes)
for each (var box:Box in line)
if (box.status == Box.NONE)
box.enableButton();
}
public function disableBoxButtons():void
{
for each (var line:Array in boxes)
for each (var box:Box in line)
if (box.status == Box.NONE)
box.disableButton();
}
/**
* 勝敗判定.
*
* 勝敗をbox.status形式で返す。
*/
private function check():uint
{
for (var i:uint = 0; i < 3; ++i) {
if (boxes[i][0].status != Box.NONE &&
boxes[i][0].status == boxes[i][1].status &&
boxes[i][0].status == boxes[i][2].status)
return boxes[i][0].status;
if (boxes[0][i].status != Box.NONE &&
boxes[0][i].status == boxes[1][i].status &&
boxes[0][i].status == boxes[2][i].status)
return boxes[0][i].status;
}
if (boxes[0][0].status != Box.NONE &&
boxes[0][0].status == boxes[1][1].status &&
boxes[0][0].status == boxes[2][2].status)
return boxes[0][0].status;
if (boxes[0][2].status != Box.NONE &&
boxes[0][2].status == boxes[1][1].status &&
boxes[0][2].status == boxes[2][0].status)
return boxes[0][2].status;
return Box.NONE;
}
/**
* 判定して、勝敗がついていたら結果を表示する.
*
* メソッド名がよくない。
*/
public function checkAndNext():uint
{
var result:uint = check();
switch (result) {
case Box.NONE:
return result;
case Box.COMPUTER:
resultMsg.display('You lost!');
break;
case Box.PLAYER:
resultMsg.display('You won!');
}
disableBoxButtons();
return result;
}
}
/**
* 桝目, grid.
*
* TODO
* [ ] buttonModeをクラス外から直接変更できない様にする
*/
class Box extends Sprite
{
public static const NONE:uint = 0;
public static const COMPUTER:uint = 1;
public static const PLAYER:uint = 2;
public var status:uint = NONE;
function Box()
{
clear();
//addEventListener(Event.ADDED, function():void {
// width = height = UNIT - T;
// msgArea.text = width + ', ' + height;
//});
}
public function clear():void
{
status = NONE;
with (graphics) {
lineStyle(0);
beginFill(0);
drawRect(0, 0, UNIT - T, UNIT - T);
}
}
public function enableButton():void
{
buttonMode = true;
addEventListener(MouseEvent.CLICK, clicked);
}
public function disableButton():void
{
buttonMode = false;
removeEventListener(MouseEvent.CLICK, clicked);
}
private function clicked(event:MouseEvent):void
{
// プレイヤーがこの枡を取る
disableButton();
status = PLAYER;
with (graphics) {
lineStyle(T, 0xffffff);
beginFill(0);
drawCircle(width / 2, height / 2, width / 2 - T - T);
}
if (Board(parent).checkAndNext() == NONE)
Board(parent).myTurn();
}
/**
* コンピュータがこの枡を取る
*/
public function mark():void
{
disableButton();
status = COMPUTER;
// draw cross "X"
with (graphics) {
lineStyle(T, 0xffffff);
moveTo(T * 2, T * 2);
lineTo(UNIT - T * 3, UNIT - T * 3);
moveTo(UNIT - T * 3, T * 2);
lineTo(T * 2, UNIT - T * 3);
}
Board(parent).checkAndNext();
}
}
class ResultMsg extends TextField
{
private const timer:Timer = new Timer(2000, 1);
function ResultMsg()
{
selectable = false;
defaultTextFormat =
new TextFormat("Arial", 100, 0xffffff);
autoSize = TextFieldAutoSize.LEFT;
filters = [new GlowFilter(0)];
timer.addEventListener(TimerEvent.TIMER_COMPLETE, next);
}
public function display(str:String):void
{
text = str;
board.addChild(this);
x = (parent.width - width) / 2;
y = (parent.height - height) / 2;
timer.start();
}
private function next(e:TimerEvent):void
{
parent.removeChild(this);
game.replay();
}
}
// メッセージ表示領域 ///////////////////////////////////////
/**
* メッセージ表示領域
*/
class MessageArea extends TextField
{
function MessageArea()
{
defaultTextFormat = new TextFormat("Arial", null, 0xffffff);
}
}
// ダイアログとボタン ////////////////////////////////////////
/**
* ダイアログとボタン
*/
import flash.geom.Matrix;
import flash.display.GradientType;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;
import flash.events.MouseEvent;
import flash.events.Event;
const textPoint:uint = 52; // ダイアログとボタンに使う文字の大きさ(ポイント)
class GradientBox extends Sprite
{
function GradientBox(_width:uint, _height:uint, _x:uint = 0, _y:uint = 0)
{
var m:Matrix = new Matrix();
m.createGradientBox(_width, _height,
Math.PI / 2); // rotation, 90 degrees
// createGradientBox(200, 100, Math.PI / 2)
// #=> Matrix(7.474404015029158e-18, 0.06103515625, -0.1220703125, 3.737202007514579e-18, 100, 50)
//message.text = 'm(' +
// String(m.a) + ', ' + String(m.b) + ', ' +
// String(m.c) + ', ' + String(m.d) + ',\n' +
// String(m.tx) + ', ' + String(m.ty) + ')';
with (graphics) {
beginGradientFill(GradientType.LINEAR,
[0x333333, 0x666666],
[0.8, 0.8],
[0, 80],
m);
drawRoundRect(_x, _y, _width, _height, 30, 30);
}
}
}
class Dialog extends GradientBox
{
function Dialog(msg:String, b1s:String, b1fn:Function, b2s:String, b2fn:Function)
{
// TODO
// [ ] 大きさを計算で決める
// [x] 計算して中央に配置する
// [x] 位置決定をDialogのコンストラクタに入れる
var _width:uint = 450;
var _height:uint = 300;
var _x:uint = 0;
var _y:uint = 0;
const thickness:uint = 2;
with (graphics) {
lineStyle(thickness, 0xffffff);
//drawRoundRect(0, 0, _width, _height, 30, 30);
//_width -= thickness * 2;
//_height -= thickness * 2;
//_x += thickness;
//_y += thickness;
//lineStyle(1, 0);
//drawRoundRect(3, 3, _width, _height, 30, 30);
//_width -= 2; _height -= 2;
}
super(_width, _height, _x, _y);
with (addChild(new TextField())) {
defaultTextFormat = new TextFormat("Arial", textPoint, 0xffffff);
autoSize = TextFieldAutoSize.LEFT;
text = msg;
selectable = false;
x = (parent.width - width) / 2;
y = (parent.height - height) / 3.3; // 適当
}
var b1:Button = new Button(b1s);
b1.x = 16;
b1.y = 182;
addChild(b1);
b1.addEventListener(MouseEvent.CLICK, b1fn);
var b2:Button = new Button(b2s);
b2.x = b1.x * 2 + b1.width;
b2.y = b1.y;
addChild(b2);
b2.addEventListener(MouseEvent.CLICK, b2fn);
addEventListener(Event.ADDED, function():void {
x = (stage.stageWidth - width) / 2;
y = (stage.stageHeight - height) / 2;
//msgArea.text = String('dialog.x: ' + x);
});
}
}
class Button extends GradientBox
{
function Button(s:String,
_width:uint = 200, _height:uint = 100)
{
buttonMode = true;
mouseChildren = false;
const thickness:uint = 1;
with (graphics) {
lineStyle(thickness, 0xffffff);
drawRoundRect(0, 0, _width, _height, 30, 30);
_width -= thickness * 2;
_height -= thickness * 2;
lineStyle(1, 0);
}
super(_width, _height, thickness, thickness);
with (addChild(new TextField)) {
//border = true;
//borderColor = 0x00ff00;
defaultTextFormat = new TextFormat("Arial", textPoint, 0xffffff);
autoSize = TextFieldAutoSize.LEFT;
text = s;
//selectable = false;
x = (parent.width - width) / 2;
y = (parent.height - height) / 2;
}
}
}