前回、迷路の作成方法を記事にしました。
今回は、実際に作ってみたいと思います。
今回自分が作るのは、【穴掘り法】でやってみたいと思います。
GUIの作成
今回のGUIは下記の通りにします。
Labelを縦横7×7で配置します。
ラベルの命名規則は、左上を(0,0)とし、右にX方向/下にY方向とします。
フローを作る
まずは、ざっくりとフロー表を作ります。
フロー表を作る事で、必要な関数を整理する事ができるので、作るようにしましょう。
ただ慣れてきている人は、必要ないかもしれません。
ざっくり2ステップです。
下記、関数を作っていきましょう。
①迷路初期化 ②迷路作成
必要な関数を作る(初期化)
まずは、初期化を作りましょう。
GUIに設置したLabel『lblMazeX*_Y*』のバックカラー(BackColor)を全てグレー(Gray)にします。
命名規則の決めているラベルなので、XYの数値からラベルを取得できるように関数を作ります。
private Label lblMaze(int x, int y)
{
return (Label)this.Controls["lblMazeX" + x.ToString() + "_Y" + y.ToString()];
}
これで、XYの値が分かれば、ラベルを取得することが出来ます。
では、本題の【初期化】です。
private int Maze_X = 7; //迷路のXの数
private int Maze_Y = 7; //迷路のYの数
private void Initialize_Maze()
{
for (int x = 0; x < Maze_X; x++)
{
for (int y = 0; y < Maze_Y; y++)
{
lblMaze(x, y).BackColor = Color.Gray;
}
}
}
XYのラベルの個数が分からない為、変数として定義しましょう。
(後々は、可変できるようにします。)
先ほど作った関数【lblMaze(int x,int y)】が普通のラベル用に使われてシンプルです!!
必要な関数を作る(迷路自動生成)
では、本題の迷路自動作成に入ります。
【穴掘り法】をするにあたり、ざっくりフローの確認です。
では、作っていきます。
List lstHole = new List(); private void Create_Maze() { //1,3,5からランダムを選択したい(0,2,4,6は壁の可能性あり) Random rd = new Random(); int posX = 2 * (rd.Next(0, Maze_X / 2)) + 1; int posY = 2 * (rd.Next(0, Maze_Y / 2)) + 1; lblMaze(posX, posY).BackColor = Color.White; //穴を掘ったリストを作る。 lstHole.Clear(); lstHole.Add(new Point(posX, posY)); do { //新着が見えるように、ウェイト Application.DoEvents(); System.Threading.Thread.Sleep(100); int direct = rd.Next(); int result = Digging(direct, ref posX, ref posY); if (result != 0) { if (result == -1) { MessageBox.Show("異常"); return; } int idx = rd.Next(0, lstHole.Count); posX = lstHole[idx].X; posY = lstHole[idx].Y; continue; } //全て穴を掘ったか確認する。 bool res = Check_Hole(); if (res == true) { //(奇数,奇数)座標が全てが白になっている。 break; } lstHole.Add(new Point(posX, posY)); } while (true); //最後にスタートとゴールを(固定) lblMaze(0, 1).BackColor = Color.LightBlue; lblMaze(Maze_X - 1, Maze_Y - 2).BackColor = Color.LightPink; }
初期位置は、X/Yともに奇数座標の時は必ず道になるので、座標が奇数/奇数になるようにします。
XYの数を半分にした(小数点切り捨て)した範囲でランダムを取ります。
2n+1で必ず奇数になるようにします。※ここでのnがランダムの数字
方向を決め、いざ穴を掘ります。【関数:Digging(int 方向,ref int x ,ref int y)】
そして、穴を掘り終わったかのチェックをします【関数:Check_Hole】
戻りがTrueの時にループを抜けます。
そして、ループを抜けたら、スタートの位置とゴールの位置を作ります。
各関数を記載
【関数:Digging(int 方向,ref int x ,ref int y)】
private int Digging(int direct, ref int posX, ref int posY)
{
direct = direct % 4; //余りを取得して方向を決定 0:↑ 1:→ 2:↓ 3:←
//範囲外でないことを確認
//([Maze_X/Maze_Y]は個数なので[posX/posY]は0から始まるので、個数にするために+1する)
if ((Maze_X < posX + 1) || (Maze_Y < posY + 1))
{
return -1;
}
//現在のラベル
Label lbl_Current = lblMaze(posX, posY);
Label lbl_Process, lbl_Target;
int beforeX = posX;
int beforeY = posY;
Get_GotoLabel(direct, ref posX, ref posY, out lbl_Process, out lbl_Target);
//移動先がラベル無ければ、終了
if (lbl_Target == null)
{
posX = beforeX;
posY = beforeY;
return 1;
}
else if(lbl_Target.BackColor == Color.Gray && lbl_Process.BackColor == Color.Gray)
{
lbl_Process.BackColor = Color.White;
lbl_Target.BackColor = Color.White;
}
return 0;
}
穴を掘るときは2マス分進みます。
その為、1マス先と2マス先のLabelのマスを取得する必要があります。
それ用に作ったのが【関数:GetGotoLabel】です。
private void Get_GotoLabel(
int direct, ref int posX, ref int posY,
out Label lbl_Process, out Label lbl_Target)
{
direct = direct % 4; //余りを取得して方向を決定 0:↑ 1:→ 2:↓ 3:←
//移動先のラベル
switch (direct)
{
case 0: //↑
lbl_Process = lblMaze(posX, posY - 1);
lbl_Target = lblMaze(posX, posY - 2);
posY = posY - 2;
break;
case 1: //→
lbl_Process = lblMaze(posX + 1, posY);
lbl_Target = lblMaze(posX + 2, posY);
posX = posX + 2;
break;
case 2: //↓
lbl_Process = lblMaze(posX, posY + 1);
lbl_Target = lblMaze(posX, posY + 2);
posY = posY + 2;
break;
case 3:
lbl_Process = lblMaze(posX - 1, posY);
lbl_Target = lblMaze(posX - 2, posY);
posX = posX - 2;
break;
default:
lbl_Process = null;
lbl_Target = null;
break;
}
}
最後にチェック用の関数です。
【関数:Check_Hole】
private bool Check_Hole()
{
for (int x = 1; x < Maze_X; x = x + 2)
{
for (int y = 1; y < Maze_Y; y = y + 2)
{
if (lblMaze(x, y).BackColor == Color.Gray)
{
return false;
}
}
}
return true;
}
チェック用の関数ではXY座標がともに奇数の座標が白抜きになっているかチェックし、1つでもグレーであれば、まだ穴は掘れるとして、抜けます。
逆に考えるとループが最後まで続いたら、全てが白抜きになったという事になるので、迷路作成終了です。
これで必要な関数が揃ったので、【作成】のクリックを作っていきましょう。
作成ボタンの処理を作る
関数が全て揃ったので、あとは、フロー通り並べるだけです。
private void btnCreate_Click(object sender, EventArgs e)
{
Initialize_Maze();
Create_Maze();
MessageBox.Show("終了");
}
完成です。では、実際に動かしてみます。
4回動かして、4回とも違う迷路になっています。
まとめ
今回の記事では、穴掘り法で迷路を作成する事でした。参考になればと思います。
実はまだ完成ではありません。
今回の目的は【子供に遊んでもらえるアプリケーション】です。
操作するプレーヤーを置いてゴールまで操作してもらえるように作りましょう。
業務でプログラミング(C#/VB/Python)を作っている。
挫折を何回も繰り返し、幾度の壁を乗り越えてきた。
乗り越えてきた事を忘れないように記録に残す。
同じ思いをしている人への情報提供になれたらと思う。
基本は初心者に向けたプログラムの情報を提供する。
コメント