8パズルゲームをつくろう

この連載
(れんさい)
では、Ruby(ルビー)というプログラミング言語を使って、本格的
(ほんかくてき)
なプログラミングに挑戦
(ちょうせん)
しているよ。

 前回までは、「DxRuby」というグラフィックライブラリーを使って、画像
がぞう
を表示させたり、動かしたりしてきたね。これからは勉強したことを応用して、グラフィックゲームのプログラムをつくってみよう。今回つくるのは「8パズルゲーム」だよ。

1 8パズルゲームとは?(ゲームの説明)

 8パズルゲームは、3×3の9つのセルに、1から8の数字を順に並べるゲームだ。数字がランダムに並んだセル(四角)の中に、1つだけ
から
のセルがある。このセルの上下左右のセルを移動
いどう
することで、目標
もくひょう
の状態にするパズルだ。例えば、下の図①からセルを移動させて(この状態で動かせるのは青い丸のセルだ)、②の状態にしたらパズルは完成だ。

 コンピューターゲームでの8パズルゲームでは、入れ替えるセルをマウスのクリックで選択
せんたく
し、このセルを空のセルと入れ替えてゲームを進めていく。出来るだけ少ないクリック数(セルの入れ替え数)で、セルを目標の状態にできればゲームクリアだ。
 今回は、このゲームをRubyのプログラムでつくっていこう。

8パズルゲーム(スタート時)
①ゲームスタートの時にはランダムに数字が表示されている
8パズルゲーム(ゲームクリア)
②順番通りに数字を並べられたらゲームクリア!

2 ゲーム画面をつくろう1(セルと枠の描画)

 まずは、ゲーム画面をつくる。各セルの大きさを100×100ピクセルの四角形でつくるよ。内部は赤で
りつぶす。そして、セルを横方向に3列、
たて
方向に3行の合計9つのセルを並べるよ。この部分をつくるための、Window
わく
描画
びょうが
するプログラムを考えてみよう。

 「グラフィックプログラムにチャレンジしよう」でつくったWindow枠をつくるプログラム「screen1.rb」を使って、以下の赤い枠で囲んだコードを追加する。完成したプログラムは「8puzzle1.rb」というファイル名に変えて保存しよう。

8puzzle1.rb
8puzzle1.rb

 それでは、このプログラムについて、特に追加したコードについて説明していこう。

 3行目(コメント(1) )では、セルとなるImageオブジェクトを準備
じゅんび
して、変数「cell」に代入
だいにゅう
している。

 5、6行目(コメント(2))では、セルの
はば
(cell.width)とセルの高さ(cell.height)をそれぞれ「cell_w」、「cell_h」と簡単
かんたん
変数
へんすう
にする(このあとプログラム中に何回も出てくるので、入力しやすいように簡単な変数にしている)。

 8、9行目(コメント(3))では、これらの変数を使ってゲームを表示するWindow枠の大きさをセル幅、高さの3倍にしている。数値で示したほうが簡単だと思うかも知れないけれど、セルの数を増やしたり、セルのサイズを変更するなど、プログラムを改造
かいぞう
するときに便利だ。

 11行目(コメント(4) )では、8、9行目の設定を使ってWindow枠オブジェクトをつくって、「waku」という変数に代入している。
Window.loop の中では、15行目(コメント(5) )でセルを、17行目(コメント(6) )でWindow枠を描画している。

 プログラムがで完成たら、コマンドプロンプトで実行してみよう。プログラムの実行のしかたはこの連載の記事「プログラムをつくってみよう!」を参考にしてね。次のような画面が表示されたらOKだ。

8puzzle1.rb実行画面

3 ゲーム画面をつくろう2(9つのセルと区切り線の描画)

 「8puzzle1.rb」ではセルを1つだけ描画したけれど、次は9つのセルを3行、3列で描画して、さらに各セルの間に黒で区切り線を入れて見やすくしてみよう。
 「8puzzle1.rb」に以下の赤枠で囲んでいるコードを追加して、青枠で囲んでいるコードは「8puzzle1.rb」から少し変えるよ。プログラムが完成したら、「8puzzle2.rb」というファイル名で保存しよう。

8puzzle2.rb
8puzzle2.rb

 それでは、追加したコードを中心にプログラムを解説していこう。

 2行目(コメント(1))では、9つのセルをつくるよ。「cells」という変数に配列
はいれつ
オブジェクトを代入している。セルのように、同じようなオブジェクトをまとめて扱う場合は、「画像や文字をつかいこなしてみよう!」で勉強した「配列」を使うと便利だ。

 Rubyでは、単に[ ]と記述するだけで、配列オブジェクトをつくることができて、要素
ようそ
がいくつあるかは気にしない。なお、配列は要素が複数
ふくすう
あるので、変数「cell」ではなく「cells」と複数形にするのが一般的だ。

 「9.times do」から「end」までの部分(これを「ブロック」という)は、配列「cell」に「<<」メソッドによって、配列の後ろの要素(ここでは100×100ピクセルの赤のセル)でImageオブジェクトを追加していき、これを9回繰り返している。つまり、要素(セル)の数が9つになるんだ。コードの書き方としては次のようになる。

配列 << 要素

 15行目(コメント(2))からは、セル同士の間に区切り線を描いている。「do」の後に「|i|」という変数があるけれど、これは「ブロック変数」とよばれるものだ。このブロック変数は、「4.times」によって、繰り返しのたびに0→1→2→3と変化していくんだ。すると、「waku.line」メソッドの引数
ひきすう
に使っている変数「i」 が0→1→2→3と変化し、それに従ってx座標
ざひょう
、y座標が計算されて、縦と横に4本ずつの黒の直線がセルの間隔でセットされる。

 22行目(コメント(3))では、コメント(1)でつくった9つのセルを、3行3列に表示するよ。ここでもブロック変数「i」を使って、Window.drawメソッドの引数としてx座標、y座標を計算している。このとき、「i 」が整数
せいすう
であることを利用して、(i % 3) 、(i /3) の計算をすることにより、以下のような3行3列の組み合わせをつくっているんだ。

(0 , 0)  (1 , 0)  (2 , 0)
(0 , 1)  (1 , 1)  (2 , 1)
(0 , 2)  (1 , 2)  (2 , 2)

 この数字の組み合わせは後の説明で重要になるので、「セル番地」と呼ぶことにしよう。
  そして、Window枠内のx座標とy座標の計算をするためにそれぞれ「cell_w」(セルの幅) 、 「cell_h」(セルの高さ)をかけている。これによって、9つのcells[i] が3行3列に描画されるんだ。
  コマンドプロンプトで実行したときに、下のように表示されたらOKだ。

8puzzle2.rb実行画面

4 ゲーム画面をつくろう3(セルに数字を入れよう)

 次に、8つのセルに1から8の数字を当てはめてみよう。そして残りの1つを空のセルとするよ。注意する点は、配列のインデックスは0から始まるけれど、セルの数字表示は1から始めることだ。「8puzzle2.rb」を改造するけれど、赤い枠で囲んだコードを入力するよ。出来上がったら「8puzzle3.rb」というファイル名で保存しよう。

8puzzle3.rb
8puzzle3.rb

 それでは、追加したコードを中心にプログラムを説明していこう。 

 4行目(コメント(1))は、フォントオブジェクトをつくって、変数「font」に代入している。ここではちょっとオシャレな「Elephant」という書体を使ってみたよ。大きさは60ポイントだ。コンピューターによっては他のフォントが準備されているかもしれないので、みなさんで好みのフォントを探してみてね。

 24行目(コメント(2))では、セルの中に数字を入れている。8つのセルに1から8までの数字を入れて、残りの1つを空のセルとする。ここでもブロック変数「i 」を使っているけれど、使い方に注目してほしい。

 「cells[i] 」はそれぞれのセルのインデックス(識別子
しきべつし
)だ。そしてdraw_fontメソッドの第1、第2引数は描画するそれぞれのセルの中の座標だ。ここでは(35, 20)としたが(大体100×100サイズのセルの中央に表示されるようにしている)、フォントの大きさなどによって変わるので、セル内のどこに表示するかは調整してね。

 注意してほしいのは第3引数だ。(i + 1)はインデックスに1を足して1から始まるようにしている。また、to_s メソッドを使って、整数を文字列に変換している。もしも整数のまま使ってしまうとエラーになってしまうので注意してね。フォントの色は白(C_WHITE)にしたけれど、これも好きな色に変更してみてね。

 コマンドプロンプトで実行してみて、以下のように表示されればOKだ。

8puzzle3.rb実行画面

5 セルをクリックして入れ替えてみよう

 それでは、いよいよセルを移動させるプログラムを追加していくよ。

 ここまでで文字を入れたので、それぞれのセルを区別することができるようになった。そこでそれぞれのセルに番号をつけておこう。1番、2番、3番…としたいところだけれど、後でtimesメソッドを使うなど後の処理があるので、配列のインデックスと同じ順で、0番、1番…のようにして、これを配列「num[]」 としよう。文字を付けなかった空のセルは8番となり、num[8]となるよ。

 マウスでクリックできる(動かせる)セルは、どれでもいいというわけではなくて、空のセルの位置の上下、左右にあるセルに限られる。つまり、空のセルのセル番地を(x , y)とすると、空のセルの左のセルは(x – 1, y)、右のセルは(x + 1, y)、上のセルは(x , y – 1)、下のセルは(x , y + 1)となるね。この3つのセルがマウスでクリックできるセルになる。

 また、マウスの座標はInput.mouse_pos_x、Input.mouse_pos_y で読み取れるのだけど、この座標をセル番地の座標に変換するためには、セルの幅、高さで割り算して求めるんだ。

 それではマウスでクリックしたセルを空のセルと入れ替えるプログラムをつくってみよう。「8puzzle3.rb」に以下の赤枠のコードを追加して、「8puzzle4.rb」というファイル名にして保存しよう。

8puzzle4.rb

8puzzle4.rb

 それでは、追加したコードを中心に説明していこう。

 29行目(コメント(1))では、「num 」という配列に0から8 の整数を入れて、セルの1 から8 と空セルに対応させているよ。

 30行目(コメント(2) )からは、click メソッドを定義
ていぎ
している。引数はマウスでクリックされたセル番地のx 座標、およびy 座標、またnum がセルの番号の配列だよ。

 まずi = num.index(8) でnum 配列の中で8となる要素のインデックスをiに代入する。つまり、空のセルの位置を求めている。32行目からの right 、left 、down 、up でクリックされたセルが、空のセルに対してそれぞれ右、左、下、上にあるかどうかを判定しているよ。ここで

            a = ( b and c )

 という形になって、さらにb と c が条件文になっているのは「b がtrue 」でかつ 「c がtrue」の場合に「a がtrue 」となる。それ以外ではfalse になるよ。ただし、ここでの( )は必ず付けてね。付けないと正しい判定ができないんだ。

 そして次のif 文でこの右、左、下、上のどこかのセルがクリックされたら、このセルと空のセルのnum 配列を入れ替えていくよ。

 44行目(コメント(3))のif 文では、Input.mouse_push? メソッドによってマウスがクリックされたかどうかチェックしているよ。引数のM_LBUTTON はマウスの左(Left)ボタンを意味している。右(Right)ボタンの場合はM_RBUTTON となるよ。

 そしてif 文の次の行で先に定義したclick メソッドを呼び出して、マウスをクリックした位置とnum 配列を引数として渡している。このとき、クリックしたWindow枠での座標をcell_w、cell_h で割り算することによって、セル番地に変換しているんだ。

 わからなくなったら、もう一度コメント(2)に戻ってclick メソッドの説明を読んで、しっかり理解しておこう。

 48行目(コメント(4) )の9つのセルを描画する部分は、前のプログラムと似ているけれど、if 文があって、num[i] != 8 の行は、num[i] が8 つまり、空のセルだけ除外しているところが違うよ。これは空のセルを描画せず、セルの赤ではなく、枠の黒を残して、違いを分かりやすくしているんだ。

 さらにWindow.draw メソッドの3つ目の引数がcell[num[i]] と配列が2重になっていることに注意しよう。これによって9つのセルをnum 配列の順に描画しているんだ。

 プログラムができたら、コマンドプロンプトでプログラムを実行しよう。空のセルのとなりのセルをクリックして、入れ替わることを確認してね。

8puzzle4.rb実行画面

6 ゲームのスタート状態をつくろう

 8パズルゲームらしくなってきたけど、プログラムはこれで終わりではないよ。次に、パズルゲームのスタート画面をつくっていこう。

 まず、ゲームのスタート画面では、セルの数字をバラバラに並べておく必要があるよね。Rubyには配列の要素をバラバラに並べ替えるために、shuffleという便利なメソッドがあるのだけれど、これを使ってセルの並びを変えると、場合によっては次のような数字の並びになってしまうことがあるんだ。

セルの並び

 実はこの並びだと、いくらセルを動かしても、正しい並びにならない。ゲームにできない順序もあるんだ。そこでこれを避けるため、ちょっとしたテクニックを使うよ。それは、無条件にセルを並べ替えてスタート状態にするのではなくて、最初に正しい並べた状態からコンピューターにクリック動作をさせてランダムな状態をつくり、それをゲームのスタート状態にするんだ。

 「8puzzle4.rb」に以下の赤枠のコードを追加しよう。完成したら「8puzzle5.rb」というファイル名にして保存してね。

8puzzle5.rb
8puzzle5.rb

 それでは、追加したコードを中心に説明していこう。

 42行目(コメント(1))は、ゲームのスタート状態のランダムなセルの並びをつくっている。正しいセルの並びから、先に定義したclick メソッドを1000回繰り返して、セルの並びがランダムになるようにしている。1000という数字は適当だけど、これくらい繰り返しセルを動かせばほぼランダムな並びができるだろうという数字だよ。人間がやると数分、いや数十分かかる操作が、コンピューターはあっという間に実行してしまうよ。

 プログラムが完成したら、コマンドプロンプトでプログラムを実行して、8パズルゲームができるか試してみよう。ランダムなセルの位置から、マウスをクリックして正しい位置に並び替えることができたかな?

 次回は、より面白いゲームなるように、いろいろな工夫をしていくよ。楽しみにしていてね。

株式会社まちづくり三鷹 著者の記事一覧

1999年9月、三鷹市の第三セクターとして創立。2008年よりプログラミング教育に関連した事業を展開する中、中高生向けテキスト(『オブジェクト指向言語「Rubyプログラミング入門」』)と小学生向けテキスト(『小学生から楽しむRubyプログラミング』(日経BP社))を自社開発し、累計10,000冊以上を売り上げている。なお、開発したテキストは、全国の複数の学校や自治体等で多数採用されている。また、小・中・高校生を対象としたRubyプログラミング講座を開催し、今までに1,000人以上の受講生実績がある。 さらに、2011年からは、中高生国際Rubyプログラミングコンテスト実行委員会の構成団体として、コンテストの運営にあたっている。

最新号好評発売中!

子供の科学 2021年 10月号

CTR IMG