flash on 2010-3-16
ちょっとしたパズルの実装
---------------------------------------------
右側と下側の丸いボタンを押すとその行またはカラムが
ひっくり返ります (順序反転および明暗逆転)
これは 4x4 ですが, 3x3 のバージョンは
最近市販されているある立体パズルと数学的に
同型です. この 4x4 版は 機構的にみて立体パズル
として実現するのは無理なのではないかと思います.
---------------------------------------------
by tenasaku: 2010年3月16日火曜日
ロジックとしてはこれでOKだが娯楽としては全然だめ
もう少し遊ぶタノシミを演出せねば...
---------------------------------------------
♥0 |
Line 238 |
Modified 2010-03-16 23:40:19 |
MIT License
archived:2017-03-10 05:04:27
ActionScript3 source code
/**
* Copyright tenasaku ( http://wonderfl.net/user/tenasaku )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/qHrw
*/
// ちょっとしたパズルの実装
// ---------------------------------------------
// 右側と下側の丸いボタンを押すとその行またはカラムが
// ひっくり返ります (順序反転および明暗逆転)
// これは 4x4 ですが, 3x3 のバージョンは
// 最近市販されているある立体パズルと数学的に
// 同型です. この 4x4 版は 機構的にみて立体パズル
// として実現するのは無理なのではないかと思います.
// ---------------------------------------------
// by tenasaku: 2010年3月16日火曜日
// ロジックとしてはこれでOKだが娯楽としては全然だめ
// もう少し遊ぶタノシミを演出せねば...
// ---------------------------------------------
package {
import flash.display.*;
import flash.events.*;
public class Main extends Sprite {
private const ROWS:int = 4; // パネルの行数
private const COLS:int = 4; // パネルのカラム数
private const BUTTONS:int = ROWS+COLS; // ボタンの個数
private const PANELS:int = ROWS*COLS;
private var perm:Array;
private var panel:Array;
private var btn:Array;
// エントリ・ポイント
public function Main():void {
var i:int;
// 長さ ROWS*COLS の順列. 最初はtrivialな順列とする.
perm = new Array;
for ( i = 0 ; i < PANELS ; ++ i ) {
perm[i] = new Number(i);
}
// ROWS行 x COLS列 のパネルを生成
panel = new Array;
for ( i = 0 ; i < PANELS ; ++ i ) {
panel[i] = new MyPanel();
this.addChild(panel[i]);
}
// BUTTONS 個のボタンを生成
btn = new Array;
for ( i = 0 ; i < BUTTONS ; ++ i ) {
btn[i] = new MyButton();
this.addChild(btn[i]);
}
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
drawButtons();
scramble();
// マウスイベントはボタンだけが受けつける
for ( i = 0 ; i < BUTTONS ; ++ i ) {
btn[i].addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
btn[i].addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
btn[i].addEventListener(MouseEvent.MOUSE_OUT, allButtonUp);
}
this.graphics.beginFill(0xffaa22);
this.graphics.drawRect(0,0,400,400);
this.graphics.endFill();
this.x = (465 - this.width)/2;
this.y = (465 - this.height)/2;
} // function Main の終わり
// マウスポインタが外れたときはすべてのボタンを上げる
private function allButtonUp(e:MouseEvent):void {
var i:int;
for ( i = 0 ; i < BUTTONS ; ++ i ) {
btn[i].up();
}
}
// マウスボタンが押されたとき
private function onMouseDown(e:MouseEvent):void {
var i:int = whichButton(e.stageX-this.x,e.stageY-this.y);
if ( i >= 0 ) { btn[i].down(); }
}
// マウスボタンが離されたとき
private function onMouseUp(e:MouseEvent):void {
var i:int = whichButton(e.stageX-this.x,e.stageY-this.y);
if ( i >= 0 ) {
if ( btn[i].isDown() ) {
if ( i < COLS ) {
changeColumn( i );
} else if ( i < COLS + ROWS ) {
changeRow( i - COLS );
}
btn[i].up();
}
}
}
// その座標はどのボタンの位置か番号で返す (ボタン上でなければ -1 を返す)
private function whichButton(X:Number,Y:Number):int {
var r:int = -1;
var i:int;
for ( i = 0 ; i < BUTTONS ; ++ i ) {
if ( (X >= btn[i].x)&&(X<btn[i].x+btn[i].width)
&& (Y >= btn[i].y)&&(Y<btn[i].y+btn[i].height) ) {
r = i;
break;
}
}
return r;
}
// ある行を変換する (左右反転&パネル裏返し)
private function changeRow(row:int):void {
var col:int;
if ( ( row >= 0 ) && ( row < ROWS ) ) {
for ( col = 0 ; col < Math.floor(COLS/2) ; ++ col ) {
swap( row*COLS+col, (row+1)*COLS-1-col );
var flag:Boolean = panel[row*COLS+col].reversed;
panel[row*COLS+col].reversed = !panel[(row+1)*COLS-1-col].reversed;
panel[(row+1)*COLS-1-col].reversed = !flag;
}
drawPanels();
}
}
// ある列を変換する (上下反転&パネル裏返し)
private function changeColumn(col:int):void {
var row:int;
if ( ( col >= 0 ) && ( col < COLS ) ) {
for ( row = 0 ; row < Math.floor(ROWS/2) ; ++ row ) {
swap( row*COLS+col, (ROWS-row-1)*COLS+col );
var flag:Boolean = panel[row*COLS+col].reversed;
panel[row*COLS+col].reversed = !panel[(ROWS-row-1)*COLS+col].reversed;
panel[(ROWS-row-1)*COLS+col].reversed = !flag;
}
drawPanels();
}
}
// 順列の二つの要素を入れ替える
private function swap(i:int,j:int):void {
if ( ( i >= 0 ) && ( i < ROWS*COLS ) ) {
if ( ( j >= 0 ) && ( j < ROWS*COLS ) ) {
if ( i != j ) {
var k:Number = perm[i];
perm[i] = perm[j];
perm[j] = k;
}
}
}
}
// 現在の状態でパネルを描画
private function drawPanels():void {
var i:int;
for ( i = 0 ; i < ROWS*COLS ; ++ i ) {
panel[i].letter = String(int(perm[i])+1);
panel[i].x = (i%COLS)*MyPanel.WIDTH;
panel[i].y = Math.floor(i/COLS)*MyPanel.HEIGHT;
panel[i].draw();
}
}
// ボタンを描画
private function drawButtons():void {
var i:int;
for ( i = 0 ; i < COLS ; ++ i ) {
btn[i].y = (MyPanel.HEIGHT-btn[i].height)/2 + ROWS*MyPanel.HEIGHT;
btn[i].x = (MyPanel.WIDTH-btn[i+COLS].width)/2 + i*MyPanel.WIDTH;
}
for ( i = 0; i < ROWS ; ++ i ) {
btn[i+COLS].x = (MyPanel.WIDTH-btn[i+COLS].width)/2 + COLS*MyPanel.WIDTH;
btn[i+COLS].y = (MyPanel.HEIGHT-btn[i+COLS].height)/2 + i*MyPanel.HEIGHT;
}
}
// パズルが完成した状態の時だけ true を返す
// 作ったけど使ってないわ (2010-03-16)
private function puzzleComplete():Boolean {
var i:int = 0;
var soFar:Boolean = true;
while ( i < ROWS*COLS ) {
if ( ( perm[i] != i ) || ( panel[i].revered ) ) {
soFar = false;
break;
}
++i;
}
return (soFar);
}
// パネルをかき回す = 問題を作る
private function scramble():void {
// n はランダムな変換の回数. 少な過ぎず多過ぎず...
var n:int = 4 + Math.floor(Math.random()*(ROWS+COLS)*1.99999999);
var i:int = 0;
while (i < n) {
var b:int = Math.floor(Math.random()*(ROWS+COLS)*0.99999999);
if ( b < COLS ) { changeColumn(b); }
else { changeRow(b-COLS); }
++ i ;
}
}
} // class Main の終わり
} // package の終わり
// 個々のパネルはこのクラスのインスタンスである
// このクラスの定義をイジればミテクレの変更が可能
import flash.text.*;
class MyPanel extends flash.display.Sprite {
public static const WIDTH:int = 80;
public static const HEIGHT:int = 80;
public static const PADDING:int = 5;
private static const HEADcolor:uint = 0x000080;
private static const TAILcolor:uint = 0x00ff80;
public var letter:String;
public var reversed:Boolean;
private var _tf:TextField;
private static const _fmt:TextFormat = new TextFormat(
"Palatino", // font name
36, // font size
0xff0000, // color
false, // bold ?
false, // italic ?
false, // underline ?
null, // link URL
null, // link target
TextFormatAlign.CENTER, // align
null, // left margin
null, // right margin
null, // indent
null // leading
);
// コンストラクタ
public function MyPanel():void {
this.letter = "?";
this.reversed = false;
_tf = new TextField();
_tf.selectable = false;
_tf.background = false;
_tf.width = WIDTH-PADDING;
_tf.height = HEIGHT-PADDING;
addChild(_tf);
this.draw();
}
// 再描画
public function draw():void {
var c:uint = (this.reversed)?TAILcolor:HEADcolor;
this.graphics.clear();
this.graphics.beginFill(c);
this.graphics.drawRect(PADDING/2,PADDING/2,WIDTH-PADDING,HEIGHT-PADDING);
this.graphics.endFill();
_tf.text = this.letter;
_tf.setTextFormat(_fmt);
_tf.x = PADDING/2;
_tf.y = PADDING/2;
}
}
// プッシュボタン(には見えない)
class MyButton extends flash.display.Sprite {
private static const W:Number = (MyPanel.WIDTH-MyPanel.PADDING)/2;
private static const H:Number = (MyPanel.HEIGHT-MyPanel.PADDING)/2;
private static const BACKcolor:uint = 0x000000;
private static const DOWNcolor:uint = 0xff6666;
private static const UPcolor:uint = 0xffffff;
private var pressed:Boolean;
public function MyButton():void {
this.up();
}
private function drawBG():void {
this.graphics.clear();
this.graphics.beginFill(BACKcolor);
this.graphics.drawEllipse(0,0,W,H);
this.graphics.endFill();
}
// 押されたときの様子
public function down():void {
this.pressed = true;
this.drawBG();
this.graphics.beginFill(DOWNcolor);
this.graphics.drawEllipse(W*0.1,H*0.1,W*0.8,H*0.8);
this.graphics.endFill();
}
// 離されたときの様子
public function up():void {
this.pressed = false;
this.drawBG();
this.graphics.beginFill(UPcolor);
this.graphics.drawEllipse(W*0.2,H*0.2,W*0.6,H*0.6);
this.graphics.endFill();
}
// 押されているかどうか
public function isDown():Boolean {
return this.pressed;
}
}