ページ 1 / 1
ダンジョンの自動生成について
Posted: 2012年5月12日(土) 21:52
by 華風えくれあ
こちらのコード( Racanhackコード解説様
http://racanhack.sourceforge.jp/rhdoc/index.html
を参考にしながらvectorなどに置き換えてコードを書いてみました。
それで実行してみたのですが、イレテーターが不正?のようなアサートウィンドウが出ました。
無理してスマートポインタなどを組み込んで実行しているのでそのせいかもしれません・・・。
どこかおかしい点は見受けられるでしょうか?ご教授お願いします。
長くなるのでスポイラーで。
► スポイラーを表示
コード:
Debug Assertion Failed!
Program:
...10\Project\ConsoleApplitation1\Debug\ConsoleApplication1.exe
File: C:\Program Files (x86)\microsoft visual studio 10.0\vc\include\vector
Line: 238
Expression: vector iterators incompatible
For information on how your program can couse an assertion
failure, see the Visual C++ documentation on asserts.
コード:
#include <cstdio>
#include <cstdlib>
#include <time.h>
#include <vector>
#include <memory>
#define MAP_WIDTH 40
#define MAP_HEIGHT 30
#define MIN_ROOM 4
#define MAX_ROOM 6
#define MIN_RECT MIN_ROOM
#define MAX_RECT MAX_ROOM
// 乱数発生関数簡易化マクロ
#define Rand(MIN,MAX) rand()%MAX+MIN
int map_i[MAP_HEIGHT][MAP_WIDTH];
struct rect_s
{
int left,right,top,bottom;
rect_s(){}
rect_s(const rect_s &){}
~rect_s(){}
};// 区画情報構造体
std::vector<std::shared_ptr<rect_s>> rect_list;
// 区画追加
rect_s *rect_add(int top,int left,int bottom,int right)
{
rect_s* rect;
rect = new rect_s();
rect->top = top;
rect->left = left;
rect->bottom = bottom;
rect->right = right;
rect_list.push_back(std::make_shared<rect_s>());
return(rect);
}
// 区画分割
void rect_split(rect_s *rect_parent)
{
rect_s *rect_child;
int split_coord;
if( (rect_parent->right - rect_parent->left <= MIN_RECT * 2) ||
(rect_parent->bottom - rect_parent->top <= MIN_RECT * 2) )
{ return ; }
// 子区画にコピー
rect_child = rect_add(rect_parent->top, rect_parent->left,rect_parent->bottom,rect_parent->right);
// 横か縦か乱数で
if(Rand(0,2)==0)
{
split_coord = Rand(rect_parent->left + MIN_RECT, rect_parent->right - MIN_RECT);
rect_parent->right = split_coord;
rect_child->left = split_coord;
rect_split(rect_parent);
rect_split(rect_child);
return;
}
else
{
split_coord = Rand(rect_parent->top + MIN_RECT, rect_parent->bottom - MIN_RECT);
rect_parent->bottom = split_coord;
rect_child->top = split_coord;
rect_split(rect_parent);
rect_split(rect_child);
return;
}
}
// 描画
void map_print( void )
{
int i,j;
int k;
std::shared_ptr<rect_s> rect;
k=0;
for(auto it=rect_list.begin();it != rect_list.end();k++,it++)
{
// イテレーターから代入できなかったため一つずつ
rect->top = rect_list.at(k)->top;
rect->left = rect_list.at(k)->left;
rect->bottom = rect_list.at(k)->bottom;
rect->right = rect_list.at(k)->right;
for(i=rect->top, j=rect->left; i<=rect->bottom; i++){ map_i[i][j] = 1; }
for(i=rect->top, j=rect->right; i<=rect->bottom; i++){ map_i[i][j] = 1; }
for(i=rect->top, j=rect->left; j<=rect->right; j++){ map_i[i][j] = 1; }
for(i=rect->bottom, j=rect->left; j<=rect->right; j++){ map_i[i][j] = 1; }
}
for(j=0;j<MAP_HEIGHT;j++)
{
for(i=0;i<MAP_WIDTH;i++)
{
if(map_i[i][j]!=0){ printf("#"); }
else { printf("."); }
}
printf("\n");
}
}
int main(void)
{
int i,j;
for(j=0;j<MAP_HEIGHT;j++)
{
for(i=0;i<MAP_WIDTH;i++)
{
map_i[i][j] = 0;
}
}
rect_list.clear();
rect_split(rect_add(0,0,MAP_WIDTH-1,MAP_HEIGHT));
map_print();
return 0;
}
Re: ダンジョンの自動生成について
Posted: 2012年5月12日(土) 22:04
by h2so5
84行目のスマートポインタはNULLポインタなのでアクセスできませんね。
あと、89行目の「イテレーターから代入できなかったため」とはどういうことでしょうか?
Re: ダンジョンの自動生成について
Posted: 2012年5月12日(土) 22:08
by 華風えくれあ
h2so5 さんが書きました:84行目のスマートポインタはNULLポインタなのでアクセスできませんね。
あと、89行目の「イテレーターから代入できなかったため」とはどういうことでしょうか?
あぁ・・・領域作るの忘れていました・・・。
それぞれに代入するための4行を書く前に、
make_sharedで行っていたのですが、謎のコンパイルエラー・・・。
以前vectorやスマートポインタ関係で書き間違い起こすと場所がどこだかわからなくなるというのがあり、
まさにそのことが起きましたので、一つ一つでいいかなと・・・。
少し書き直してみます。
Re: ダンジョンの自動生成について
Posted: 2012年5月12日(土) 22:21
by 華風えくれあ
123行目を削除したら、アサートは消えてくれました。
ですが、やはりどこかで処理が止まってしまいます。
領域は確保済みです。
Re: ダンジョンの自動生成について
Posted: 2012年5月12日(土) 22:22
by h2so5
では、どこで止まるか特定してください。
Re: ダンジョンの自動生成について
Posted: 2012年5月12日(土) 22:53
by softya(ソフト屋)
華風えくれあ さんが書きました:123行目を削除したら、アサートは消えてくれました。
ですが、やはりどこかで処理が止まってしまいます。
領域は確保済みです。
デバッガで止まった場所は特定できるはずです。
Re: ダンジョンの自動生成について
Posted: 2012年5月12日(土) 22:57
by 華風えくれあ
softya(ソフト屋) さんが書きました:華風えくれあ さんが書きました:123行目を削除したら、アサートは消えてくれました。
ですが、やはりどこかで処理が止まってしまいます。
領域は確保済みです。
デバッガで止まった場所は特定できるはずです。
デバッガの使い方がわからないのでちょっと勉強がいるかな・・・と思いました。
返信より先に自分で解決できましたのでご報告いたします。
原因は、ダムポインタとスマートポインタの混在でした。
なので種類の違う値が入り混じってしまい動作が停止したんだと思います。
なので、戻り値・引数をすべてスマートポインタで返すようにしてみました。
► スポイラーを表示
コード:
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <memory>
#define MAP_WIDTH 50
#define MAP_HEIGHT 40
#define MIN_ROOM 4
#define MAX_ROOM 6
#define MIN_RECT 8
#define MAX_RECT NULL
#define Rand(MIN,MAX) rand()%MAX+MIN
int map_i[MAP_HEIGHT][MAP_WIDTH];
struct rect_s
{
int lx,ly,hx,hy;
};// 区画情報構造体
std::vector<std::shared_ptr<rect_s>> rect_list;
std::shared_ptr<rect_s> rect_add(int lx,int ly,int hx,int hy)
{
std::shared_ptr<rect_s> rect(new rect_s);
rect->lx = lx;
rect->ly = ly;
rect->hx = hx;
rect->hy = hy;
rect_list.push_back(rect);
return(rect);
}
void rect_split(std::shared_ptr<rect_s> &rect_parent)
{
std::shared_ptr<rect_s> rect_child;
int split_coord;
if((rect_parent->hy - rect_parent->ly <= MIN_RECT*2) ||
(rect_parent->hx - rect_parent->lx <= MIN_RECT*2) )
{ return ; }
// 子区画にコピー
rect_child = rect_add(rect_parent->lx, rect_parent->ly,rect_parent->hx,rect_parent->hy);
if(Rand(0,2)==0)
{
split_coord = Rand(rect_parent->ly + MIN_RECT, rect_parent->hy - MIN_RECT);
rect_parent->hy = split_coord;
rect_child->ly = split_coord;
rect_split(rect_parent);
rect_split(rect_child);
return;
}
else
{
split_coord = Rand(rect_parent->lx + MIN_RECT, rect_parent->hx - MIN_RECT);
rect_parent->hx = split_coord;
rect_child->lx = split_coord;
rect_split(rect_parent);
rect_split(rect_child);
return;
}
}
void map_print( void )
{
int i,j;
int k;
std::shared_ptr<rect_s> rect(new rect_s);
k=0;
for(j=0;j<MAP_HEIGHT;j++)
{
for(i=0;i<MAP_WIDTH;i++)
{
if(map_i[i][j]!=0){ printf("#"); }
else { printf("."); }
}
printf("\n");
}
}
int main(void)
{
int i,j;
for(j=0;j<MAP_HEIGHT;j++)
{
for(i=0;i<MAP_WIDTH;i++)
{
map_i[i][j] = 0;
}
}
rect_split(rect_add(0,0,MAP_WIDTH-1,MAP_HEIGHT));
map_print();
return 0;
}
区画に対して1を代入する部分は少し休憩してから行いたいと思います。
Re: ダンジョンの自動生成について
Posted: 2012年5月12日(土) 23:21
by softya(ソフト屋)
何処かよくわからない場所で落ちた場合は、呼び出し履歴ウィンドウを使います。
「Visual Studio 2008での呼び出し履歴の表示 - 酢ろぐ(ch3cooh.jp)」
http://d.hatena.ne.jp/ch3cooh393/20100902/1283425268
「【ハウツー】最初にマスターしたいVisual Studioのデバッグ機能 - 応用編 (2) 呼び出し履歴ウィンドウ/ビジュアライザ | エンタープライズ | マイナビニュース」
http://news.mynavi.jp/articles/2008/08/ ... g/001.html
↑ C#の説明なので途中までしか参考にはなりませんが。
Re: ダンジョンの自動生成について
Posted: 2012年5月13日(日) 11:43
by 華風えくれあ
すいません・・・かなり危ないところを間違えていました・・・。
イテレーターから参照して代入の部分でエラーが出ていたのではなく、
下のfor文のところでした。
ここをコメントアウトしたとき実行したら右上に不正な#があったのでデバッグしてみたところ、
map_iのi分が40しかなかったのであれ?と思って宣言を見てみたらMAP_WIDTHとMAP_HEIGHTが
逆でした^^;
Re: ダンジョンの自動生成について
Posted: 2012年5月13日(日) 11:50
by softya(ソフト屋)
そういう間違いが起きるのは、
(1)int map_i[MAP_HEIGHT][MAP_WIDTH];
と定義しているからです。
(2)int map_i[MAP_WIDTH][MAP_HEIGHT];
とすれば間違うことはないはずです。
(1) の形で定義して便利なのは、直接マップデータを数値定義するときで自動生成の場合は無意味ですのでバグを減らすためにも(2)であるべきです。
あと、i,jと言う変数名もミスを誘発しやすいのでx,yを使うべきでしょう。
Re: ダンジョンの自動生成について
Posted: 2012年5月17日(木) 00:10
by 華風えくれあ
softya(ソフト屋) さんが書きました:そういう間違いが起きるのは、
(1)int map_i[MAP_HEIGHT][MAP_WIDTH];
と定義しているからです。
(2)int map_i[MAP_WIDTH][MAP_HEIGHT];
とすれば間違うことはないはずです。
(1) の形で定義して便利なのは、直接マップデータを数値定義するときで自動生成の場合は無意味ですのでバグを減らすためにも(2)であるべきです。
あと、i,jと言う変数名もミスを誘発しやすいのでx,yを使うべきでしょう。
ありがとうございます。
宣言時から間違っていたので気づくのに遅れてしまったんでしょう・・・、以後気を付けます。
さて、何事もなく表示はされるようになりました。
ですが、作られた枠が一つもなくなってしまいました。
デバッグしてもデータは分割されているようです、
どこが問題かわかりません・・・。
ここら辺が正しくないなど指摘していただけないでしょうか。
コード:
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <memory>
#include <iterator>
#define MAP_WIDTH 50
#define MAP_HEIGHT 40
#define MIN_ROOM 4
#define MAX_ROOM 6
#define MIN_RECT 8
#define MAX_RECT NULL
#define Rand(MIN,MAX) rand()%MAX+MIN
int map_i[MAP_WIDTH][MAP_HEIGHT];
struct rect_s
{
int lx,ly,hx,hy;
};// 区画情報構造体
std::vector<std::shared_ptr<rect_s>> rect_list;
std::shared_ptr<rect_s> rect_add(int lx,int ly,int hx,int hy)
{
std::shared_ptr<rect_s> rect(new rect_s);
rect->lx = lx;
rect->ly = ly;
rect->hx = hx;
rect->hy = hy;
rect_list.push_back(rect);
return(rect);
}
void rect_split(std::shared_ptr<rect_s> &rect_parent)
{
std::shared_ptr<rect_s> rect_child;
int split_coord;
if((rect_parent->hy - rect_parent->ly <= MIN_RECT*2) ||
(rect_parent->hx - rect_parent->lx <= MIN_RECT*2) )
{ return ; }
// 子区画にコピー
rect_child = rect_add(rect_parent->lx, rect_parent->ly,rect_parent->hx,rect_parent->hy);
if(Rand(0,2)==0)
{
split_coord = Rand(rect_parent->ly + MIN_RECT, rect_parent->hy - MIN_RECT);
rect_parent->hy = split_coord;
rect_child->ly = split_coord;
rect_split(rect_parent);
rect_split(rect_child);
return;
}
else
{
split_coord = Rand(rect_parent->lx + MIN_RECT, rect_parent->hx - MIN_RECT);
rect_parent->hx = split_coord;
rect_child->lx = split_coord;
rect_split(rect_parent);
rect_split(rect_child);
return;
}
}
// 描画処理
void map_print( void )
{
int x,y;
std::shared_ptr<rect_s> rect(new rect_s);
// コンテナの中身をコピー
for(auto it = rect_list.begin();it == rect_list.end();++it)
{
// 一つずつ行う
rect->lx = it->get()->lx;
rect->ly = it->get()->ly;
rect->hx = it->get()->hx;
rect->hy = it->get()->hy;
for(x = rect->lx, y = rect->ly; x <= rect->hx; x++){ map_i[x][y] = 1; }
for(x = rect->lx, y = rect->hy; x <= rect->hx; x++){ map_i[x][y] = 1; }
for(x = rect->lx, y = rect->ly; x <= rect->hy; y++){ map_i[x][y] = 1; }
for(x = rect->hx, y = rect->ly; x <= rect->hy; y++){ map_i[x][y] = 1; }
}
// 配列の中身をすべて調べ、0以外であれば#を書く
for(y=0;y<MAP_HEIGHT;y++)
{
for(x=0;x<MAP_WIDTH;x++)
{
if(map_i[x][y]!=0){ printf("#"); }
else { printf("."); }
}
printf("\n");
}
}
int main(void)
{
srand((unsigned)time(NULL));
int i,j;
for(j=0;j<MAP_HEIGHT;j++)
{
for(i=0;i<MAP_WIDTH;i++)
{
map_i[i][j] = 0;
}
}
rect_split(rect_add(0,0,MAP_WIDTH-1,MAP_HEIGHT));
map_print();
return 0;
}
Re: ダンジョンの自動生成について
Posted: 2012年5月17日(木) 01:53
by softya(ソフト屋)
gcc 4.3.4でg++ -std=c++0xでコンパイルエラーですが環境は何でしょうか?
Re: ダンジョンの自動生成について
Posted: 2012年5月17日(木) 09:36
by beatle
ぱっと見
コード:
for(auto it = rect_list.begin();it == rect_list.end();++it)
この行でit == rect_list.end()としているのが気になります。このfor文は一回も実行されませんよ。
Re: ダンジョンの自動生成について
Posted: 2012年5月17日(木) 09:41
by beatle
それから、
コード:
rect->lx = it->get()->lx;
はせっかくスマートポインタを使っていますから、生ポインタを取り出してlxにアクセスするのではなくて
コード:
rect->lx = (*it)->lx;
とする方がいいでしょうね。
Re: ダンジョンの自動生成について
Posted: 2012年5月17日(木) 10:44
by 華風えくれあ
softya(ソフト屋) さんが書きました:gcc 4.3.4でg++ -std=c++0xでコンパイルエラーですが環境は何でしょうか?
環境はVC++2010Express版です。
beatle さんが書きました:ぱっと見
コード:
for(auto it = rect_list.begin();it == rect_list.end();++it)
この行でit == rect_list.end()としているのが気になります。このfor文は一回も実行されませんよ。
なんと・・・、見落としてました、申し訳ないです。
beatle さんが書きました:それから、
コード:
rect->lx = it->get()->lx;
はせっかくスマートポインタを使っていますから、生ポインタを取り出してlxにアクセスするのではなくて
コード:
rect->lx = (*it)->lx;
とする方がいいでしょうね。
ありがとうございます、書き換えてみました。
これでデバッグしてみると、for文に不定期でアクセスエラーが起きてしまってます。
評価式がおかしいのでしょうか・・・。
コード:
/* map_print( void ) 改変点 */
int x,y;
std::shared_ptr<rect_s> rect(new rect_s);
// コンテナの中身をコピー
for(auto it = rect_list.begin();it < rect_list.end();++it)
{
// 一つずつ行う
rect->lx = (*it)->lx;
rect->ly = (*it)->ly;
rect->hx = (*it)->hx;
rect->hy = (*it)->hy;
for(x = rect->lx, y = rect->ly; x <= rect->hx; x++){ map_i[x][y] = 1; }
for(x = rect->lx, y = rect->hy; x <= rect->hx; x++){ map_i[x][y] = 1; }
for(x = rect->lx, y = rect->ly; y <= rect->hy; y++){ map_i[x][y] = 1; }
for(x = rect->hx, y = rect->ly; y <= rect->hy; y++){ map_i[x][y] = 1; }
}
Re: ダンジョンの自動生成について
Posted: 2012年5月17日(木) 11:02
by beatle
コード:
for(auto it = rect_list.begin();it < rect_list.end();++it)
これもおかしいですよ。いや、rect_listがstd::vectorだということを前提にしているならいいのですが、普通は
コード:
for(auto it = rect_list.begin();it != rect_list.end();++it)
というように、イテレータとend()の比較は!=を使います。
!=を使っておけば、rect_listがどんな型でも処理できます。これこそイテレータを使う価値があるというものです。
「アクセスエラー」というのは、具体的にどの部分でどんなエラーが出ますか?for文と言っても、外側のfor文には初期化部、条件部、更新部、実行される文と、大きく4つありますね。文の中にはまた8行の文が含まれます。
Re: ダンジョンの自動生成について
Posted: 2012年5月17日(木) 11:56
by 華風えくれあ
beatle さんが書きました:
「アクセスエラー」というのは、具体的にどの部分でどんなエラーが出ますか?for文と言っても、外側のfor文には初期化部、条件部、更新部、実行される文と、大きく4つありますね。文の中にはまた8行の文が含まれます。
おそらく、更新部のところだと思います。
デバッグでは最初の中括弧で停止しました。ですので、インクリメント直後にエラーが出たんだと思います。
外のfor文は何回か実行していたので要素数は0ではないのですが、なぜEnd()で止まってくれないのでしょうか・・・。
追記:
it != rect_list.End()には書き換えておきました。
Re: ダンジョンの自動生成について
Posted: 2012年5月17日(木) 12:05
by beatle
華風えくれあ さんが書きました:beatle さんが書きました:
「アクセスエラー」というのは、具体的にどの部分でどんなエラーが出ますか?for文と言っても、外側のfor文には初期化部、条件部、更新部、実行される文と、大きく4つありますね。文の中にはまた8行の文が含まれます。
おそらく、更新部のところだと思います。
デバッグでは最初の中括弧で停止しました。ですので、インクリメント直後にエラーが出たんだと思います。
外のfor文は何回か実行していたので要素数は0ではないのですが、なぜEnd()で止まってくれないのでしょうか・・・。
追記:
it != rect_list.End()には書き換えておきました。
大文字、小文字はC++では区別されますから注意してくださいね。
具体的なエラーメッセージなどは出ませんでしたか?
それから、なぜend()で止まっていないことが判明したのでしょうか。
for文がrect_list.size()回より多くループしていることをチェックした、とかでしょうか。
for文の条件部分に、for文のループ回数がrect_list.size()以下になっていることをチェックする式を追加したらどうでしょうか。
コード:
size_t count = 0;
for(auto it = rect_list.begin();it != rect_list.end() && count < rect_list.size();++it, ++count)
という感じに。