今回はパズルゲーム STAMP をつくります。こんな感じのゲームです。

左が初期状態、右がお題です。

2×2の領域を上書きして以下のように変更していきます。

最短手数で完成させることができない場合は不正解です。最初は2手問題からスタートして120秒の制限時間内にどれだけ解くことができるかを競うゲームです。

HTML部分

HTML部分を示します。

style.css

グローバル変数

グローバル変数ですが、大幅に減らすことにしました。これだけです。

Cellクラスの定義

Cellクラスを定義します。

マスは4×4でこのグループを上下にひとつずつ作ります。上側が現在の状態、下側がお題です。引数から描画すべき座標が決まるのでこれを求めてメンバ変数に格納しておきます。

QueueStackクラスの定義

幅優先探索をするときに使うqueueとしてQueueStackクラスを定義します。これは 最短以外は不正解!8パズルでゲームをつくる(1) と同じものです。

Questionクラスの定義

Questionクラスを定義します。

Gemeクラスの定義

Gameクラスを定義します。

コンストラクタ

コンストラクタを示します。

Cellオブジェクトの生成

マスを描画するためのCellオブジェクトを生成する処理を示します。

お題の生成

お題を生成する処理を示します。初期状態では各行に同じ種類の文字が並んでいるので、これを’0000111122223333’で表します。この状態から遷移できるすべての状態とそこまでの手数を幅優先探索ですべて取得します。そのあと各手数から1つずつ選んでこれを問題とします。

実際に幅優先探索をしてみると、最長手数は9手であることがわかります。問題が9個では少なすぎるので、このあと最長問題をシャッフルして取得された問題の後ろに追加します。初期状態と1手問題を捨てると全部で147問分取得することができます。制限時間120秒ではすべて解くことは物理的に不可能です。

更新処理

更新処理を示します。

ゲーム開始の処理

ゲーム開始の処理を示します。

お題の初期状態を表示する

現在解いている問題を初期状態に戻す処理を示します。

Questionsの先頭の要素からTextを取り出してこれをCells[1][x].ImageIndexにセットするとともに、Cells[0][x].ImageIndexも初期状態に戻しています。

選択範囲の移動

選択範囲を移動させる処理を示します。

canvasをクリックしたとき、その座標が選択範囲として描画されている矩形の上下左右であった場合はその方向に移動できるようにします。また矩形の内部がクリックされたときはスタンプを押す処理(後述)をおこないます。

以下は引数で渡された座標から選択範囲をどの方向に移動させるかを取得する関数です。

canvasがクリックされたらcanvas上の座標を引数にしてOnClickedCanvas関数が呼び出されます。

スタンプを押す処理

スタンプを押す処理を示します。

選択されている2×2の矩形で囲まれている部分に描画されるイメージを変更します。そのあとお題と比較して同じであればステージクリアです。その場合は加算される点数をスコアに加算するのですが、ここで問題発生!

加算される点数を残り時間だけから求めるとステージクリア数が増えてもあまり加点されないのでステージに応じて求めた別の値を掛け合わることにしたのですが、自分でテストプレイしたときは 2のx乗を掛けてもあまり感動できる点数にならなかったので4のx乗を掛けることにしました。すると・・・

こうなってしまいました((TT))。100億点超えってw。

指数関数なんか使わずに(残り時間 + クリアしたステージ数 × α)くらいにしておけばよかったと後悔しています(後悔先に立たず)。

クリア判定

クリア判定の処理を示します。上下の対応するマスに描画されているイメージが同じかどうかを確認しているだけです。

リセット

途中で操作を間違えたときはお題の初期状態に戻します。

ゲームオーバー処理

残り時間が 0 になったらゲームオーバーの処理をおこないます。スコアをスコアランキングに登録してプレイ中は非表示になっていたスタートボタンを再表示させます。

サーバー側の処理とスコアランキングを表示させる処理はゲーム開始以降の処理 鳩でもわかるXORパズルをつくる(2)と同じなので省略します。

ページが読み込まれたときの処理

ページが読み込まれたらGameオブジェクトを生成して、イベントリスナを追加します。

以下はレンジスライダーでボリューム調整を可能にするための当サイト定番の処理です。