初投稿です!!
こんな迷路ができました。
作ろうと思ったきっかけ
以前にHTML5とJSで、棒倒し法を使った迷路ゲームをつくって文化祭に出展したことがありました。
しかし、棒倒し法で作った迷路は解がとても単純になってしまうため、解が複雑な穴掘り法でやってみたいと思ったからです。
穴掘り法って?
■すごくざっくり言うと穴掘り法はこんなアルゴリズム (もしかしたら間違っているかも)
縦、横共に大きさが5以上で奇数の大きさのマップを作成する。
すべて壁で埋める
マップの端っこ以外で、X座標、Y座標が共に奇数の場所を無作為に選び、穴を開ける。
★端っこ以外で、X,Y座標が奇数で(今回は左上端を0,0とする)、まだ壁が残っているところがあるか確かめる
あったら:マップの端っこ以外で、X座標、Y座標が共に奇数の場所ですでに穴が開いているところを無作為に選ぶ。
| 四方の2マス先にまだ壁が残っているところがあるか確かめる
| あったら:掘れる方向中からランダムにまっすぐ2マス掘る
| なかったら:★に戻る
なかったら:迷路完成
……ですが、こんな素直に実装するのは難しいので、いろいろ工夫してやっていきます。
中身
メインのクラス
MainClass.java
packageautomaze;publicclassMainClass{publicstaticvoidmain(String[]args){Mazemaze=newMaze(21,21);//今回は試しに21×21の迷路を作る。maze.show();}}
Mazeクラス
Maze.java
packageautomaze;publicclassMaze{privateintpointX;//ブロックを置いたり消したりする目印。privateintpointY;privateintwidth;//横幅と高さ。privateintheight;privatebyte[][]map;//マップを格納する配列publicMaze(intw,inth){//コンストラクターwidth=w;height=h;if(w%2!=0&&h%2!=0&&5<=w&&5<=h){map=newbyte[width][height];make();}else{System.out.println("縦・横共に5以上の奇数で作成してください。");}}intrandomPos(intmuki){//x,y座標共に奇数なランダムな座標を返すintresult=1+2*(int)Math.floor((Math.random()*(muki-1))/2);returnresult;}privatevoidmake(){//マップを作成するpointX=randomPos(width);pointY=randomPos(height);for(inty=0;y<height;y++){//すべて壁で埋める。for(intx=0;x<width;x++){map[x][y]=1;}}map[pointX][pointY]=0;dig();}privatevoiddig(){if(isAbleContinueDig()&&map[pointX][pointY]==0){map[pointX][pointY]=0;intdirection=(int)Math.floor(Math.random()*4);switch(direction){case0:if(pointY!=1){if(map[pointX][pointY-2]==1){map[pointX][pointY-1]=0;pointY-=2;break;//u}}case1:if(pointY!=height-2){if(map[pointX][pointY+2]==1){map[pointX][pointY+1]=0;pointY+=2;break;//d}}case2:if(pointX!=1){if(map[pointX-2][pointY]==1){map[pointX-1][pointY]=0;pointX-=2;break;//l}}case3:if(pointX!=width-2){if(map[pointX+2][pointY]==1){map[pointX+1][pointY]=0;pointX+=2;break;//r}}}map[pointX][pointY]=0;dig();}elseif(isAbleDig()){pointX=randomPos(width);pointY=randomPos(height);dig();}}privatebooleanisAbleDig(){//まだ掘るところがあるか確かめるbooleanresult;intcnt=0;for(inty=0;y<height;y++){for(intx=0;x<width;x++){if(x%2!=0&&y%2!=0){if(map[x][y]!=0){cnt++;}}}}if(cnt==0){result=false;}else{result=true;}returnresult;}privatebooleanisAbleContinueDig(){//四方に掘れるところが残っているかどうか判断するif(pointY!=1){if(map[pointX][pointY-2]==1){returntrue;}}if(pointY!=height-2){if(map[pointX][pointY+2]==1){returntrue;}}if(pointX!=1){if(map[pointX-2][pointY]==1){returntrue;}}if(pointX!=width-2){if(map[pointX+2][pointY]==1){returntrue;}}returnfalse;}publicvoidshow(){for(inty=0;y<map[0].length;y++){System.out.println("");for(intx=0;x<map.length;x++){if(map[x][y]==1){System.out.print("##");}else{System.out.print(" ");}}}}publicbyte[][]getMaze(){returnmap;}}
完成して気づいたこと
もうちょっと短くできないかなぁ、と思っています。
あと、掘る関数を再帰的に呼び出してるのが原因か、ちょっと大きい迷路を作ろうとしただけでstackoverflowerrorを吐きます。
直し次第この記事も編集します。while文を使ったら何とかなりそう……、とぼんやり思っています。
気が向いたらやるかもしれないこと
- スタート・ゴールを自動で設置
- 最短経路を求める
- ゲームとしてプレイできるようにする。
私の環境
OS :Windows10
IDE:Eclipse 2020-03