セグメント木をネタにゲームをつくってみました。その名も「お前もセグ木で殴ってやろうか?」

長さ8の配列のある要素を指定された値で更新することで指定された区間の和を合わせるという知的なパズル(?)です。

ではさっそく作っていきましょう。

HTML部分

HTML部分を示します。セグメント木の各ノードの値をtableタグをつかって表示させています。更新ボタンをクリックすると指定された値で各ノードが更新されていきます。下から上へむけて値が更新されていくときに殴っているかのような効果音が再生される仕様にしています。どうです、面白そうでしょう?(完全に自画自賛)

style.css

Gameクラスの定義

Gameクラスを定義します。まずはコンストラクタを示します。

問題を作る

問題を作る処理を示します。

お題となる元の配列に格納される値ですが、まずランダムに1~5の整数を生成します。そして0番目から7番目までの区間(全区間)、1番目から6番目までの区間、乱数で決めた区間の3つの区間の総和を計算します。そして配列の値の1~4個を別の値に置き換えています。3つの区間和から置き換えられる前の配列の値を当ててくださいというのが問題の趣旨です。

セグメント木の初期化

セグメント木を初期化する処理を示します。

配列の値をセグメント木の葉(この場合は index が 8 ~ 15)に格納して各ノードの値を親ノードに加算していきます。16個あるノードのうち使用するのは index が 1 ~ 15番で 0 は使いません。なぜ index 0 を飛ばすのかというとこうすることでビットシフトを用いることで容易に親ノードと左の子ノードの位置がわかるからです。

表示の初期化

お題を生成後にセグメント木と表示を初期化する処理を示します。この関数はお題生成後と[やりなおし]選択時に呼び出されます。

セグメント木の更新

セグメント木を更新する処理を示します。第一引数は配列の index、第二引数は更新する値そのものです。セグメント木の葉に更新する値を代入して根に向けて区間和の値を更新していきます。これは普通のセグメント木でおこなわれる処理とまったく同じです。更新時にどのノードの値が更新されているのかがわかるようにするのとともに殴る効果音を再生しています。このパズルゲームはセグメント木でおこなわれている処理を可視化するという教材でもあるのです(←教材っておいおい)。

ユーザーが更新ボタンをクリックしたら更新すべき値で配列の対応する要素の値を更新します。

正誤判定

ユーザーがおこなった更新が正しいかどうかを判定する処理を示します。

まず区間和を調べる処理を示します。根ノードからそれが被覆している区間と調べたい区間を被覆しているかを再帰処理で調べて総和を計算します。このとき調べたい区間を被覆している区間を強調表示してセグメント木ではどのような処理が行われているかを可視化します。

上記関数を呼び出して実際に正誤判定をする処理を示します。

お題の区間和と一致しているかどうかでチャイムとブザーを鳴らします。3つとも一致していたら正解です。正解のときはステージ数と残り時間に応じて点数が加算され、次のステージに進みます。不正解のときは強制的にお題の初期状態に戻されます。

ゲームの開始とやり直し

ゲームの開始とやり直しの処理を示します。

ゲーム開始時は、スコアとステージ数のリセットとお題の生成、[ゲームスタート]ボタンの非表示などの処理をしています。

[やり直し]が選択されたときは前述のReset関数を呼び出します。

更新処理

時間の経過とともに残り時間を減算する処理と時間切れになったらゲームオーバー処理をおこなう処理を示します。

ゲームオーバー時の処理

ゲームオーバー時の処理を示します。スコアランキングにスコアを登録します。

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

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

ページが読み込まれたときの処理を示します。Gameオブジェクトを生成してイベントリスナを追加してUpdate関数を呼び出しているだけです。レンジスライダーで効果音の音量をコントロールできるようにする処理はいつもと同じ処理です。