この連載
では、Ruby(ルビー)というプログラミング言語を使って、本格的
なプログラミングに挑戦
しているよ。
前回までは、「DxRuby」というグラフィックライブラリーを使って、画像
を表示させたり、動かしたりしてきたね。これからは勉強したことを応用して、グラフィックゲームのプログラムをつくってみよう。今回つくるのは「8パズルゲーム」だよ。
1 8パズルゲームとは?(ゲームの説明)
8パズルゲームは、3×3の9つのセルに、1から8の数字を順に並べるゲームだ。数字がランダムに並んだセル(四角)の中に、1つだけ空
のセルがある。このセルの上下左右のセルを移動
することで、目標
の状態にするパズルだ。例えば、下の図①からセルを移動させて(この状態で動かせるのは青い丸のセルだ)、②の状態にしたらパズルは完成だ。
コンピューターゲームでの8パズルゲームでは、入れ替えるセルをマウスのクリックで選択
し、このセルを空のセルと入れ替えてゲームを進めていく。出来るだけ少ないクリック数(セルの入れ替え数)で、セルを目標の状態にできればゲームクリアだ。
今回は、このゲームをRubyのプログラムでつくっていこう。
2 ゲーム画面をつくろう1(セルと枠の描画)
まずは、ゲーム画面をつくる。各セルの大きさを100×100ピクセルの四角形でつくるよ。内部は赤で塗
りつぶす。そして、セルを横方向に3列、縦
方向に3行の合計9つのセルを並べるよ。この部分をつくるための、Window枠
を描画
するプログラムを考えてみよう。
「グラフィックプログラムにチャレンジしよう」でつくったWindow枠をつくるプログラム「screen1.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だ。
3 ゲーム画面をつくろう2(9つのセルと区切り線の描画)
「8puzzle1.rb」ではセルを1つだけ描画したけれど、次は9つのセルを3行、3列で描画して、さらに各セルの間に黒で区切り線を入れて見やすくしてみよう。
「8puzzle1.rb」に以下の赤枠で囲んでいるコードを追加して、青枠で囲んでいるコードは「8puzzle1.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だ。
4 ゲーム画面をつくろう3(セルに数字を入れよう)
次に、8つのセルに1から8の数字を当てはめてみよう。そして残りの1つを空のセルとするよ。注意する点は、配列のインデックスは0から始まるけれど、セルの数字表示は1から始めることだ。「8puzzle2.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だ。
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」というファイル名にして保存しよう。
それでは、追加したコードを中心に説明していこう。
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 配列の順に描画しているんだ。
プログラムができたら、コマンドプロンプトでプログラムを実行しよう。空のセルのとなりのセルをクリックして、入れ替わることを確認してね。
6 ゲームのスタート状態をつくろう
8パズルゲームらしくなってきたけど、プログラムはこれで終わりではないよ。次に、パズルゲームのスタート画面をつくっていこう。
まず、ゲームのスタート画面では、セルの数字をバラバラに並べておく必要があるよね。Rubyには配列の要素をバラバラに並べ替えるために、shuffleという便利なメソッドがあるのだけれど、これを使ってセルの並びを変えると、場合によっては次のような数字の並びになってしまうことがあるんだ。
実はこの並びだと、いくらセルを動かしても、正しい並びにならない。ゲームにできない順序もあるんだ。そこでこれを避けるため、ちょっとしたテクニックを使うよ。それは、無条件にセルを並べ替えてスタート状態にするのではなくて、最初に正しい並べた状態からコンピューターにクリック動作をさせてランダムな状態をつくり、それをゲームのスタート状態にするんだ。
「8puzzle4.rb」に以下の赤枠のコードを追加しよう。完成したら「8puzzle5.rb」というファイル名にして保存してね。
それでは、追加したコードを中心に説明していこう。
42行目(コメント(1))は、ゲームのスタート状態のランダムなセルの並びをつくっている。正しいセルの並びから、先に定義したclick メソッドを1000回繰り返して、セルの並びがランダムになるようにしている。1000という数字は適当だけど、これくらい繰り返しセルを動かせばほぼランダムな並びができるだろうという数字だよ。人間がやると数分、いや数十分かかる操作が、コンピューターはあっという間に実行してしまうよ。
プログラムが完成したら、コマンドプロンプトでプログラムを実行して、8パズルゲームができるか試してみよう。ランダムなセルの位置から、マウスをクリックして正しい位置に並び替えることができたかな?
次回は、より面白いゲームなるように、いろいろな工夫をしていくよ。楽しみにしていてね。
文