うーん。これまたわかりにくいタイトルですね。要はこんな感じのパズルを作りたかったのです。

このパズルの目的は以下となります。

最初に釘をひとつだけ取り除く。
隣に色違いの釘が存在し、隣の隣に釘が存在しない場合、その釘を移動させることができる。
釘を移動させたら飛び越された釘を取り除く。
このような動作を繰り返して最下段の中央の釘のみを残して他の釘を取り除くことができたらクリアとする。

動画では単に飛び越えた釘を消すだけですが、これだと手順を覚えてしまうと誰でも簡単にできてしまうので、異なる色の釘でないと飛び越えて消すことができないというルールを追加しています。

解ける問題を生成することはできるのか?

このパズル。適当に釘を配置しておけば必ず解は存在するのでしょうか? 残念ながら解が存在しない問題も生成されてしまいます。

ただ解が存在する場合はかなりの確率で存在するので適当に問題を生成して解が存在しない場合は作り直すという方針で問題ないことがわかりました。なのでここは乱数で問題を生成することにします。

HTML部分

HTML部分を示します。

style.css

グローバル変数と定数

グローバル変数と定数を以下のように定義します。

index.js

Cellクラスの定義

釘が配置されるマスは円形です。マスの描画と隣接関係を管理できるようにするためにCellクラスを定義します。

マスの位置とインデックスは以下のようにします。row * (row + 1) / 2 + col とやれば 0 ~ 15の連続する整数でindexを割り振ることができます。

Nailクラスの定義

釘の描画とその座標を管理するためにNailクラスを定義します。

QueueStackクラスの定義

幅優先探索で必要となるQueueの役割をするQueueStackクラスを定義します。これは 8パズルでゲームをつくる で示したものとまったく同じです。

問題を生成する

問題を生成するQuestionGeneratorクラスを定義します。

コンストラクタの引数としてCellオブジェクトの配列を渡します。

何色の釘がどのマスに存在するかを文字列で表すことにします。’0’ならそこには釘は存在せず、’1′,’2′,’3’であればその色の釘が存在するものとして扱います。

マスは15個あるので文字列も15文字です。初期状態では’1′,’2′,’3’の3種類の文字がそれぞれ5個ずつ存在します。初手ではこのなかからひとつを取り除くのでこのなかから1つを’0’に置き換えます。この置き換えられた文字列が引数として渡されます。

このパズルの目的は最下段の中央の釘を残してそれ以外の釘を消してしまうことです。これは言い換えると文字列を”000000000000100″か”000000000000200″か”000000000000300″のいずれかに変えてしまうことを意味します。Solve関数は幅優先探索でこれを実現することが可能であるかどうかを調べています。また一度探索した文字列を何度も探索しないようにMapで管理しています。

GetAnswer関数は与えられた問題から釘をひとつ取り除いたパターン(全部で15種類)を生成して前述のSolve関数に渡します。そして解が存在するならその解を戻り値として返します。

Generate関数は乱数を生成してランダムに初期の釘の位置を決めて問題を生成します。そしてこれをGetAnswer関数に渡して本当に解が存在するかを調べます。解が存在するならこれを出題するために問題と解をセットにしたオブジェクトを戻り値として返します。解が存在しないなら解が存在する問題が生成させるまで処理を繰り返します。

MoveHistoryクラスの定義

途中で操作前の状態に戻せるようにMoveHistoryクラスを定義します。

どの釘をどこからどこに移動させたのか、それにともなってどの位置にあった釘が消えたのかをコンストラクタの引数として渡します。

続きは次回とします。