オブジェクト指向でないコードで書いた15パズルの完成
ボタンを動かすコードを書く
前回は15パズルのデザインを作成しました。今回は実際にパズルを実行するプログラムを書きましょう。このパズルに必要な機能はいくつかありますが、まず「タイルを動かす」プログラムから作っていくことにします。
15パズルのデザインを表示している「デザイナ」で「12」のボタンをダブルクリックします。すると次のようなコードができます。
private void button12_Click(object sender, EventArgs e) { }
ここにコードを書くと、「12」のボタンであるbutton12をクリックしたときに実行することができます。画面(その上にあるボタンを含みます)をクリックすることは「イベント」であり、その「クリックイベント」を捕捉するものが「イベントハンドラ」です。
初心者向けの書籍では、ここでできたコードを「イベントハンドラ」と呼ぶことが多いですが、厳密には「イベントハンドラによって実行されるメソッド」です。厳密な意味での「イベントハンドラ」のコード、クリックイベントを捕捉するコードはVisual Studioが自動的に作ってくれていて、表面上は見えなくなっています。
このようにVisual Studioにはプログラマーの操作により必要なコードを自動生成する機能があり、またそれをバックグラウンドに隠して見えなくし、肝心なコードだけを表示して開発しやすくする機能を持っているのです。
では、ボタンを動かすコードを書きましょう。この状態で「12」のボタンをクリックすると、空きスペースに移動することになりますが、実際はそこに見えない「16」のボタンがあるので、「16」のボタンと位置を交換することになります。ボタンの位置はプロパティのTopとLeftで決まります。一時的に値を保持する変数swapPosを作り、それぞれのTopとLeftを交換します。コードは次のようになります。
private void button12_Click(object sender, EventArgs e) { int swapPos; swapPos = button12.Top; button12.Top = button16.Top; button16.Top = swapPos; swapPos = button12.Left; button12.Left = button16.Left; button16.Left = swapPos; }
Visual Studioで緑の三角「開始」ボタンをクリックするか、「デバッグ」メニューの「デバッグの開始」でビルドしてプログラムを実行します。パズルが表示されたら「12」のタイルをクリックすると、タイルは空白の場所に移動します。
しかし、これだけでは困ります。タイルを動かすことができるのは、空白の場所である「16」のタイルと接している場合だけです。そこでタイルを動かす条件を決めます。タイルを動かせる条件を「横に接している場合」と「縦に接している場合」の2つの場合に分けて考えます。「横に接している場合」は、両方のタイルのTopの値が同じで、Leftの値の差が100です。「縦に接している場合」はタイルのLeftの値が同じで、Topの値の差が100です。それぞれ条件式にして、交換するコードを内側に記述します。コードは次のようになります。
private void button12_Click(object sender, EventArgs e) { int swapPos; if (button12.Left == button16.Left & (Math.Abs(button12.Top - button16.Top) == 100)) { swapPos = button12.Top; button12.Top = button16.Top; button16.Top = swapPos; } if (button12.Top == button16.Top & (Math.Abs(button12.Left - button16.Left) == 100)) { swapPos = button12.Left; button12.Left = button16.Left; button16.Left = swapPos; } }
これでボタン「12」を正しい条件で動かすことができるようになりました。ほかのボタンも動かせるようにするには、同じコードを残りの14個のボタンに書かなければいけません。まずボタンをクリックしてイベントハンドラとイベントハンドラによって実行されるメソッドを自動的に作り、内側にコードを書きます。コードはコピーできますが、それぞれ対応するボタンの名前を変更しなければいけません。これは根気のいる繰り返し作業になります。またTopとLeftの値を入れ替えるときに使う一時的な変数swapPosの定義は、コードの先頭に移しておきましょう。次の位置です。
namespace SampleProject { public partial class Form1 : Form { public Form1() { InitializeComponent(); } int swapPos; private void button1_Click(object sender, EventArgs e) { (以下略)
完成を判定するコードを書く
次にパズルの完成を判定するコードを書きます。パズルの完成はどう判定すればよいでしょう。タイルの位置で判断すればよさそうです。ボタン「1」から「4」のTopの値は0、「5」から「8」のTopは100、のようにTopの値を判定し、ボタン「1」「5」「9」「13」はLeftの値が0、「2」「6」「10」「14」はLeftの値が100というように、すべてのボタンのTopとLeftの値を判定するメソッドを作ります。すべてのTopとLeftの値が判定できれば「Complete!」のメッセージボックスを表示することにしましょう。コードは次のようになります。
private void complete() { if(button1.Top == 0 & button2.Top == 0 & button3.Top == 0 & button4.Top == 0) { if (button5.Top == 100 & button6.Top == 100 & button7.Top == 100 & button8.Top == 100) { if (button9.Top == 200 & button10.Top == 200 & button11.Top == 200 & button12.Top == 200) { if (button13.Top == 300 & button14.Top == 300 & button15.Top == 300 & button16.Top == 300) { if (button1.Left == 0 & button5.Left == 0 & button9.Left == 0 & button13.Left == 0) { if (button2.Left == 100 & button6.Left == 100 & button10.Left == 100 & button14.Left == 100) { if (button3.Left == 200 & button7.Left == 200 & button11.Left == 200 & button15.Left == 200) { if (button4.Left == 300 & button8.Left == 300 & button12.Left == 300 & button16.Top == 300) { MessageBox.Show("Complete!"); } } } } } } } } }
このメソッドは、タイルを動かすたびに判定したいので、すべてのボタンのクリックイベントで実行されるメソッドの最後でcomplete()メソッドを呼び出します。たとえばボタン「1」のクリックイベントで実行されるメソッドは次のようになります。
private void button1_Click(object sender, EventArgs e) { if (button1.Left == button16.Left & (Math.Abs(button1.Top - button16.Top) == 100)) { swapPos = button1.Top; button1.Top = button16.Top; button16.Top = swapPos; } if (button1.Top == button16.Top & (Math.Abs(button1.Left - button16.Left) == 100)) { swapPos = button1.Left; button1.Left = button16.Left; button16.Left = swapPos; } complete(); }
ここまでできたら、一度Visual Studioで緑の三角「開始」ボタンをクリックするか、「デバッグ」メニューの「デバッグの開始」でビルドしてプログラムを実行しましょう。パズルが表示されたら「12」か「15」のタイルをクリックして動かし、もう一度クリックして元の位置に戻します。完成の「Complete!」のメッセージボックスが表示されればプログラムは正しく動いています。
タイルをランダムに並べ替えてゲームを初期化するコードを書く
ゲームを開始する際にはタイルがランダムになっている必要があります。そこでプログラムの実行時に、まずタイルをランダムに交換するコードを実行することにしましょう。プログラムの実行時に実行したいコードは、フォームのLoadイベントで実行するようにすればいいでしょう。Loadイベントハンドラとイベントハンドラによって実行されるメソッドを作るには、デザイナでフォームの何もないところをダブルクリックします。このプログラムではフォームの内側はすでにボタンで敷き詰められているので、フォームの上部、タイトルバーをダブルクリックするとよいでしょう。次のようなコードが表示されます。
private void Form1_Load(object sender, EventArgs e) { }
ボタンをダブルクリックして生成するコードと同様に、ここで見えているのは「Loadイベントハンドラによって実行されるメソッド」です。「Loadイベントハンドラ」は別のところに生成していて隠されています。ここにコードを書くと、フォームがロードされたときに実行させることができます。
タイルをランダムに交換するには、Randomクラスを使って乱数を作り、その番号のボタンが「16」のボタンと位置を交換するようにします。対象のタイルは「1」から「15」の15枚なので、乱数は1から15を作り、case文で分岐するようにします。交換するコードは各ボタンのクリックイベントハンドラによって実行されるメソッドをコピーして使います。サンプルコードは次のようになります。
private void Form1_Load(object sender, EventArgs e) { Random r = new System.Random(); for (int i=1 ; i<2000 ; i++) { int j = r.Next(1,15); switch (j) { case 1: if (button1.Left == button16.Left & (Math.Abs(button1.Top - button16.Top) == 100)) { swapPos = button1.Top; button1.Top = button16.Top; button16.Top = swapPos; } if (button1.Top == button16.Top & (Math.Abs(button1.Left - button16.Left) == 100)) { swapPos = button1.Left; button1.Left = button16.Left; button16.Left = swapPos; } break; case 2: (以下、ボタン2以降の交換処理が続く)
これで15パズルが完成しました。コード全体は次のようになります。Visual Studioで緑の三角「開始」ボタンをクリックするか、「デバッグ」メニューの「デバッグの開始」でビルドしてプログラムを実行しましょう。デバッグのときはForm1_Loadメソッドにあるボタンの位置交換の繰り返し回数を減らしておくのがよいかもしれません。「for (int i=1 ; i<2000 ; i++)」のiの値を2000から20程度に変更すればよいでしょう。そうすればデバッグ時には少しのタイル移動でパズルが完成します。2000のままだとタイルが大きく動いてしまい、テストのたびにひたすら15パズルを解かされることになりますからね。
ここまでのサンプルコードは以下のようになります。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace SampleProject { public partial class Form1 : Form { public Form1() { InitializeComponent(); } int swapPos; private void button1_Click(object sender, EventArgs e) { if (button1.Left == button16.Left & (Math.Abs(button1.Top - button16.Top) == 100)) { swapPos = button1.Top; button1.Top = button16.Top; button16.Top = swapPos; } if (button1.Top == button16.Top & (Math.Abs(button1.Left - button16.Left) == 100)) { swapPos = button1.Left; button1.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button2_Click(object sender, EventArgs e) { if (button2.Left == button16.Left & (Math.Abs(button2.Top - button16.Top) == 100)) { swapPos = button2.Top; button2.Top = button16.Top; button16.Top = swapPos; } if (button2.Top == button16.Top & (Math.Abs(button2.Left - button16.Left) == 100)) { swapPos = button2.Left; button2.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button3_Click(object sender, EventArgs e) { if (button3.Left == button16.Left & (Math.Abs(button3.Top - button16.Top) == 100)) { swapPos = button3.Top; button3.Top = button16.Top; button16.Top = swapPos; } if (button3.Top == button16.Top & (Math.Abs(button3.Left - button16.Left) == 100)) { swapPos = button3.Left; button3.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button4_Click(object sender, EventArgs e) { if (button4.Left == button16.Left & (Math.Abs(button4.Top - button16.Top) == 100)) { swapPos = button4.Top; button4.Top = button16.Top; button16.Top = swapPos; } if (button4.Top == button16.Top & (Math.Abs(button4.Left - button16.Left) == 100)) { swapPos = button4.Left; button4.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button5_Click(object sender, EventArgs e) { if (button5.Left == button16.Left & (Math.Abs(button5.Top - button16.Top) == 100)) { swapPos = button5.Top; button5.Top = button16.Top; button16.Top = swapPos; } if (button5.Top == button16.Top & (Math.Abs(button5.Left - button16.Left) == 100)) { swapPos = button5.Left; button5.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button6_Click(object sender, EventArgs e) { if (button6.Left == button16.Left & (Math.Abs(button6.Top - button16.Top) == 100)) { swapPos = button6.Top; button6.Top = button16.Top; button16.Top = swapPos; } if (button6.Top == button16.Top & (Math.Abs(button6.Left - button16.Left) == 100)) { swapPos = button6.Left; button6.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button7_Click(object sender, EventArgs e) { if (button7.Left == button16.Left & (Math.Abs(button7.Top - button16.Top) == 100)) { swapPos = button7.Top; button7.Top = button16.Top; button16.Top = swapPos; } if (button7.Top == button16.Top & (Math.Abs(button7.Left - button16.Left) == 100)) { swapPos = button7.Left; button7.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button8_Click(object sender, EventArgs e) { if (button8.Left == button16.Left & (Math.Abs(button8.Top - button16.Top) == 100)) { swapPos = button8.Top; button8.Top = button16.Top; button16.Top = swapPos; } if (button8.Top == button16.Top & (Math.Abs(button8.Left - button16.Left) == 100)) { swapPos = button8.Left; button8.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button9_Click(object sender, EventArgs e) { if (button9.Left == button16.Left & (Math.Abs(button9.Top - button16.Top) == 100)) { swapPos = button9.Top; button9.Top = button16.Top; button16.Top = swapPos; } if (button9.Top == button16.Top & (Math.Abs(button9.Left - button16.Left) == 100)) { swapPos = button9.Left; button9.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button10_Click(object sender, EventArgs e) { if (button10.Left == button16.Left & (Math.Abs(button10.Top - button16.Top) == 100)) { swapPos = button10.Top; button10.Top = button16.Top; button16.Top = swapPos; } if (button10.Top == button16.Top & (Math.Abs(button10.Left - button16.Left) == 100)) { swapPos = button10.Left; button10.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button11_Click(object sender, EventArgs e) { if (button11.Left == button16.Left & (Math.Abs(button11.Top - button16.Top) == 100)) { swapPos = button11.Top; button11.Top = button16.Top; button16.Top = swapPos; } if (button11.Top == button16.Top & (Math.Abs(button11.Left - button16.Left) == 100)) { swapPos = button11.Left; button11.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button12_Click(object sender, EventArgs e) { if (button12.Left == button16.Left & (Math.Abs(button12.Top - button16.Top) == 100)) { swapPos = button12.Top; button12.Top = button16.Top; button16.Top = swapPos; } if (button12.Top == button16.Top & (Math.Abs(button12.Left - button16.Left) == 100)) { swapPos = button12.Left; button12.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button13_Click(object sender, EventArgs e) { if (button13.Left == button16.Left & (Math.Abs(button13.Top - button16.Top) == 100)) { swapPos = button13.Top; button13.Top = button16.Top; button16.Top = swapPos; } if (button13.Top == button16.Top & (Math.Abs(button13.Left - button16.Left) == 100)) { swapPos = button13.Left; button13.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button14_Click(object sender, EventArgs e) { if (button14.Left == button16.Left & (Math.Abs(button14.Top - button16.Top) == 100)) { swapPos = button14.Top; button14.Top = button16.Top; button16.Top = swapPos; } if (button14.Top == button16.Top & (Math.Abs(button14.Left - button16.Left) == 100)) { swapPos = button14.Left; button14.Left = button16.Left; button16.Left = swapPos; } complete(); } private void button15_Click(object sender, EventArgs e) { if (button15.Left == button16.Left & (Math.Abs(button15.Top - button16.Top) == 100)) { swapPos = button15.Top; button15.Top = button16.Top; button16.Top = swapPos; } if (button15.Top == button16.Top & (Math.Abs(button15.Left - button16.Left) == 100)) { swapPos = button15.Left; button15.Left = button16.Left; button16.Left = swapPos; } complete(); } private void complete() { if(button1.Top == 0 & button2.Top == 0 & button3.Top == 0 & button4.Top == 0) { if (button5.Top == 100 & button6.Top == 100 & button7.Top == 100 & button8.Top == 100) { if (button9.Top == 200 & button10.Top == 200 & button11.Top == 200 & button12.Top == 200) { if (button13.Top == 300 & button14.Top == 300 & button15.Top == 300 & button16.Top == 300) { if (button1.Left == 0 & button5.Left == 0 & button9.Left == 0 & button13.Left == 0) { if (button2.Left == 100 & button6.Left == 100 & button10.Left == 100 & button14.Left == 100) { if (button3.Left == 200 & button7.Left == 200 & button11.Left == 200 & button15.Left == 200) { if (button4.Left == 300 & button8.Left == 300 & button12.Left == 300 & button16.Top == 300) MessageBox.Show("Complete!"); } } } } } } } } private void Form1_Load(object sender, EventArgs e) { Random r = new System.Random(); for (int i=1 ; i<2000 ; i++) { int j = r.Next(1,15); switch (j) { case 1: if (button1.Left == button16.Left & (Math.Abs(button1.Top - button16.Top) == 100)) { swapPos = button1.Top; button1.Top = button16.Top; button16.Top = swapPos; } if (button1.Top == button16.Top & (Math.Abs(button1.Left - button16.Left) == 100)) { swapPos = button1.Left; button1.Left = button16.Left; button16.Left = swapPos; } break; case 2: if (button2.Left == button16.Left & (Math.Abs(button2.Top - button16.Top) == 100)) { swapPos = button2.Top; button2.Top = button16.Top; button16.Top = swapPos; } if (button2.Top == button16.Top & (Math.Abs(button2.Left - button16.Left) == 100)) { swapPos = button2.Left; button2.Left = button16.Left; button16.Left = swapPos; } break; case 3: if (button3.Left == button16.Left & (Math.Abs(button3.Top - button16.Top) == 100)) { swapPos = button3.Top; button3.Top = button16.Top; button16.Top = swapPos; } if (button3.Top == button16.Top & (Math.Abs(button3.Left - button16.Left) == 100)) { swapPos = button3.Left; button3.Left = button16.Left; button16.Left = swapPos; } break; case 4: if (button4.Left == button16.Left & (Math.Abs(button4.Top - button16.Top) == 100)) { swapPos = button4.Top; button4.Top = button16.Top; button16.Top = swapPos; } if (button4.Top == button16.Top & (Math.Abs(button4.Left - button16.Left) == 100)) { swapPos = button4.Left; button4.Left = button16.Left; button16.Left = swapPos; } break; case 5: if (button5.Left == button16.Left & (Math.Abs(button5.Top - button16.Top) == 100)) { swapPos = button5.Top; button5.Top = button16.Top; button16.Top = swapPos; } if (button5.Top == button16.Top & (Math.Abs(button5.Left - button16.Left) == 100)) { swapPos = button5.Left; button5.Left = button16.Left; button16.Left = swapPos; } break; case 6: if (button6.Left == button16.Left & (Math.Abs(button6.Top - button16.Top) == 100)) { swapPos = button6.Top; button6.Top = button16.Top; button16.Top = swapPos; } if (button6.Top == button16.Top & (Math.Abs(button6.Left - button16.Left) == 100)) { swapPos = button6.Left; button6.Left = button16.Left; button16.Left = swapPos; } break; case 7: if (button7.Left == button16.Left & (Math.Abs(button7.Top - button16.Top) == 100)) { swapPos = button7.Top; button7.Top = button16.Top; button16.Top = swapPos; } if (button7.Top == button16.Top & (Math.Abs(button7.Left - button16.Left) == 100)) { swapPos = button7.Left; button7.Left = button16.Left; button16.Left = swapPos; } break; case 8: if (button8.Left == button16.Left & (Math.Abs(button8.Top - button16.Top) == 100)) { swapPos = button8.Top; button8.Top = button16.Top; button16.Top = swapPos; } if (button8.Top == button16.Top & (Math.Abs(button8.Left - button16.Left) == 100)) { swapPos = button8.Left; button8.Left = button16.Left; button16.Left = swapPos; } break; case 9: if (button9.Left == button16.Left & (Math.Abs(button9.Top - button16.Top) == 100)) { swapPos = button9.Top; button9.Top = button16.Top; button16.Top = swapPos; } if (button9.Top == button16.Top & (Math.Abs(button9.Left - button16.Left) == 100)) { swapPos = button9.Left; button9.Left = button16.Left; button16.Left = swapPos; } break; case 10: if (button10.Left == button16.Left & (Math.Abs(button10.Top - button16.Top) == 100)) { swapPos = button10.Top; button10.Top = button16.Top; button16.Top = swapPos; } if (button10.Top == button16.Top & (Math.Abs(button10.Left - button16.Left) == 100)) { swapPos = button10.Left; button10.Left = button16.Left; button16.Left = swapPos; } break; case 11: if (button11.Left == button16.Left & (Math.Abs(button11.Top - button16.Top) == 100)) { swapPos = button11.Top; button11.Top = button16.Top; button16.Top = swapPos; } if (button11.Top == button16.Top & (Math.Abs(button11.Left - button16.Left) == 100)) { swapPos = button11.Left; button11.Left = button16.Left; button16.Left = swapPos; } break; case 12: if (button12.Left == button16.Left & (Math.Abs(button12.Top - button16.Top) == 100)) { swapPos = button12.Top; button12.Top = button16.Top; button16.Top = swapPos; } if (button12.Top == button16.Top & (Math.Abs(button12.Left - button16.Left) == 100)) { swapPos = button12.Left; button12.Left = button16.Left; button16.Left = swapPos; } break; case 13: if (button13.Left == button16.Left & (Math.Abs(button13.Top - button16.Top) == 100)) { swapPos = button13.Top; button13.Top = button16.Top; button16.Top = swapPos; } if (button13.Top == button16.Top & (Math.Abs(button13.Left - button16.Left) == 100)) { swapPos = button13.Left; button13.Left = button16.Left; button16.Left = swapPos; } break; case 14: if (button14.Left == button16.Left & (Math.Abs(button14.Top - button16.Top) == 100)) { swapPos = button14.Top; button14.Top = button16.Top; button16.Top = swapPos; } if (button14.Top == button16.Top & (Math.Abs(button14.Left - button16.Left) == 100)) { swapPos = button14.Left; button14.Left = button16.Left; button16.Left = swapPos; } break; case 15: if (button15.Left == button16.Left & (Math.Abs(button15.Top - button16.Top) == 100)) { swapPos = button15.Top; button15.Top = button16.Top; button16.Top = swapPos; } if (button15.Top == button16.Top & (Math.Abs(button15.Left - button16.Left) == 100)) { swapPos = button15.Left; button15.Left = button16.Left; button16.Left = swapPos; } break; } } } } }
次回は、上記のコードの問題点を確認しながらオブジェクト指向によるプログラミングを始めてみましょう。