迷路を生成してみよう3
1回動いて終わり、というプログラムの場合
コンストラクタに全ての処理を書くことも多いと思います。
毎度、管理人イガジーです。
ただ、あとでそれを extends して使おうとした場合に
処理を分離した方が良いと気づくこともあります。
昨日のプログラム例が
public static void main(String[] args) {
MazeT0 mt=new MazeT0();
mt.generate(3,4);
}
と2段構成になっているのは、実はあとで
extendsするのに備えているわけなのです。
それは後日理解して頂くとして、島ができないように
対処したプログラム例を以下に示します。
import java.util.Random;
public class MazeT1 {
Random rnd;
int xt,yt,xn,yn;
int [][]mz;
final int unset=0, wall=1, making=2;
MazeT1(){ // constructor
rnd=new Random();
}
void generate(int ynn, int xnn){
xt=xnn; yt=ynn;
xn=xt*2+3;
yn=yt*2+3;
mz=new int[yn][xn];
for (int x=0;x<xn;++x) {
mz[0][x]=mz[yn-1][x]=wall; // 外枠 上辺と下辺
}
for (int y=1;y<(yn-1);++y) {
// Arrays.fill(mz[y],unset);
mz[y][0]=mz[y][xn-1]=wall; // 外枠 左辺と右辺
}
for (int j=0;j<yt;++j){ // 各格子点から
int y=j*2+2;
for (int i=0;i<xt;++i){
int x=i*2+2;
trace(y,x); // 壁を伸ばしてゆく
}
}
mz[0][1]=mz[yn-1][xn-2]=unset; // 入り口と出口をあける
dispmz(mz); // 表示
}
void trace(int yy,int xx) {
boolean ok=true;
int x00=xx;
int y00=yy;
int dx,dy;
while (mz[yy][xx]==unset) {
mz[yy][xx]=making; // 格子点描画
dx=dy=0;
int dir=rnd.nextInt(4); // 伸ばす方向を乱数で決める
if (dir==0) dy=-1; // 上へ
else if (dir==1) dx=1; // 右へ
else if (dir==2) dy=1; // 下へ
else dx=-1; // 左へ
ok=true;
if (mz[yy+dy+dy][xx+dx+dx]==making) { // 描画中の自分に接するなら
// 方向を変える
ok=false; // 伸ばせないかもしれないと仮定する
for (int t=1;t<4;++t) {
int t2=(dir+t)%4;
dx=dy=0;
if (t2==0) dy=-1; // 上へ
else if (t2==1) dx=1; // 右へ
else if (t2==2) dy=1; // 下へ
else dx=-1; // 左へ
if (mz[yy+dy+dy][xx+dx+dx]!=making) { // 2つ先が自分ではなければ
ok=true; // 伸ばしてよい
break; // for
}
}// end for t
}// endif mz[][]==1 伸ばした先が自分の軌跡だった時の処理はここまで
if (ok) { // 伸ばしてヨシなら
xx+=dx; yy+=dy;
mz[yy][xx]=making; // 中間点描画
xx+=dx; yy+=dy; // 次の格子点
}else {
// 渦巻き的に自分の軌跡に囲まれて伸ばせない時は作りなおす
// System.out.printf("NG y=%d x=%d dy=%d dx=%d¥n",yy,xx, dy,dx ); dispmz(mz);
fixthem(unset);// やりなおし
xx=x00; yy=y00;
}
}
fixthem(wall); // 壁にする
}
void fixthem(int d) { // 壁にする/やりなおす(消す)
for (int y=0;y<yn;++y) {
for (int x=0;x<xn;++x) {
if (mz[y][x]==making) mz[y][x]=d;
}
}
}
void dispmz(int [][]zz){
for (int y=0;y<yn;++y) {
for (int x=0;x<xn;++x) {
if (zz[y][x]==wall) System.out.print("■"); // 壁
else System.out.print(" "); // 通路
}
System.out.println();
}
}
public static void main(String[] args) {
MazeT1 mt=new MazeT1();
mt.generate(3,4);
}
}
内部データ的に、unset(通路)とwall(壁)に加えて
making(作成途上の壁)を使用するようにしています。
making を伸ばしていって、wallに到達したらOKなので
making→wall に変更します。
making を伸ばしていって、袋小路的になってしまったら
making→unset に戻して、やりなおします。
making→wall や making→unset をするのが、fixthem()メソッドです。
//System.out.printf("NG y=%d x=%d dy=%d dx=%d¥n",yy,xx, dy,dx ); dispmz(mz);
のコメント(//)をはずすと、袋小路状態を確認できます。
■を並べた図は迷路っぽくないので、ちゃんと壁を線で描画したい
と感じるでしょうから、次回は上記のクラスを extends(継承)して
グラフィック描画するようにしてみましょう。
この記事へのコメントはこちら