電子書籍の厳選無料作品が豊富!

外枠と、中の柱を建てることはできたのですが、棒を倒すことができません。
コンパイルしようとすると、停止してしまいます。
どこがまずいのか、教えてください。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
int field[13][13]={0}; //0で初期化
int dice=0;

srand((unsigned)time(NULL));

/*
9が■、0が□
外枠
*/
for(int i=0; i<13; ++i){
for(int j=0; j<13; ++j){
field[0][j]=9;
field[12][j]=9;
field[i][0]=9;
field[i][12]=9;

/*
中の柱 5本×5本
*/
for(int i=1; i<=5; ++i){
for(int j=1; j<=5; ++j){
field[i*2][j*2]=9;
}
}

/*
わかりません ><;
フリーズします・・・
*/
if(i == 2){ //2列目なら
dice=rand()%4; //サイコロで上下左右
}else if(i>=3){ //3列目以降
dice=rand()%3; //サイコロで下左右
switch(dice){
case 0:
field[i*2][j*2-1]=9;// 左
printf("■");
break;
case 1:
field[i*2+1][j*2]=9;// 下
printf("■");
break;
case 2:
field[i*2][j*2+1]=9;// 右
printf("■");
break;
case 3:
field[i*2-1][j*2]=9;// 上
printf("■");
break;
defalult: break;
}
}else{
printf("□");
}

}
printf("\n");
}
return 0;
}

質問者からの補足コメント

  • このようなポップアップが出て、停止します。

    「棒倒し法のプログラムについて教えてくださ」の補足画像1
      補足日時:2016/06/17 10:24
  • つらい・・・

    ~略~
    /*
    中の柱 5本×5本
    */
    for(int i=1; i<=5; ++i){
    for(int j=1; j<=5; ++j){
    field[i*2][j*2]=9;
    }
    }
    /*
    とりあえず、柱だけにしてみて、考えてみましたが、
    条件式がおかしいのでしょうか?
    どうすればいいのか全然わかかりません・・・。
    */
    if(field[i][j]==9){
    printf("■");
    }else{
    printf("□");
    }
    }
    printf("\n");
    }
    return 0;
    }

    「棒倒し法のプログラムについて教えてくださ」の補足画像2
      補足日時:2016/06/17 19:39

A 回答 (4件)

うーん、多分・・・まあ、ぶっちゃけ、「棒倒し法」とか初めて聞いたんで(笑)、ググりながら組み立ててみたんですが、次のようなロジックになるんじゃないでしょうか。



// ここから

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

enum Direction { RIGHT, DOWN, LEFT, UP }; /* 列挙体で方向を定義 */
enum Element { EMPTY, WALL }; /* 列挙体で壁(WALL)と空(EMPTY)を定義 */

#define MAX 13 /* 取り敢えず field の長さを指定する */

/* 関数のプロトタイプ宣言 */
void initialize_field(int array[][MAX]);
void print_field(int array[][MAX]);
int dice(int n);
void make_maze(int array[][MAX]);
void selector(int n, int i, int j, int array[][MAX]);

int main(void) {
 int field[MAX][MAX];
 srand((unsigned)time(NULL));
 /* field の初期化 */
 initialize_field(field);
 /* maze の作成 */
 make_maze(field);
 /* field の表示 */
 print_field(field);
 return 0;
}

/* field の初期化 */
void initialize_field(int array[][MAX]) {
 int i, j;
 for (i = 0; i < MAX; i++) {
  for (j = 0; j < MAX; j++) {
   /* 最初の行と最後の行は全部 WALL で埋まる */
   if (i == 0 || i == 12)
    array[i][j] = WALL;
   /* 行数(i)・列数(j) 共に偶数の場合は WALL になる */
   else if (i % 2 == 0) {
    if (j % 2 == 0)
     array[i][j] = WALL;
    else
     array[i][j] = EMPTY;
   }
   /* それ以外は行の最初と最後は WALL、それ以外は EMPTY で埋める */
   else {
    if (j == 0 || j == MAX-1)
     array[i][j] = WALL;
    else
     array[i][j] = EMPTY;
   }
  }
 }
}

/* field の表示 */
void print_field(int array[][MAX]) {
 int i, j;
 for (i = 0; i < MAX; i++) {
  for (j = 0; j < MAX; j++) {
   switch (array[i][j]) {
   case WALL:
    printf("%s", "■");
    break;
   default :
    printf("%s", "□");
    break;
   }
  }
  printf("\n");
 }
}

/* サイコロの定義 */
int dice(int n) {
 return rand()%n;
}

/* maze(迷路)作成 */
void make_maze(int array[][MAX]) {
 int i, j;
/* 端以外は行・列共に偶数の時壁なので、ループを回す初期値は2、以降カウンタは2づつ増やす */
 for (i = 2; i < MAX-1; i += 2) {
  for (j = 2; j < MAX-1; j += 2) {
   if (i == 2)
    selector(4, i, j, array);
   else {
    if (array[i][j-1] == WALL)
     selector(2, i, j, array);
    else
     selector(3, i, j, array);
   }
  }
 }
}

/* 条件でn面体サイコロを変えて配列のどこを変更するか決める */
void selector(int n, int i, int j, int array[][MAX]) {
 switch(dice(n)) {
 case RIGHT:
  array[i][j+1] = WALL;
  break;
 case DOWN:
  array[i+1][j] = WALL;
  break;
 case LEFT:
  array[i][j-1] = WALL;
  break;
 default :
  array[i-1][j] = WALL;
  break;
 }
}

// ここまで

まあ、ググりながら書いてみたんですが、どうもこう・・・ハッキリくっきり解説してるサイトが見つからなかったんですよね(笑)。
でも多分こういう事だと思います。
コメントでも書いてますが、要点は次のようなカンジです。

★最重要:「棒倒し」のループの中では行数・列数のコンビは偶数でないとならない。

まあ、最初の行と最後の行は「全部壁」なんで除外しますが、基本的にはループは2から初めて偶数飛びにしていかないといけません。
イメージ的には棒の上を飛んでくカンジでしょうか。
だから、forループを回す際に、

for (i = 0, ...
for(j = 0, ...

だとダメなんです。

for (i = 2, ...
for (j = 2, ....

じゃないといけない。
かつ、手癖で、カウンタ更新を

i++

とやっちゃうのも良くないですね。棒は必ず(偶数, 偶数)に存在するので、i += 2じゃないと「棒から落ちちゃい」ます。
そして「棒の上に立ってる」状態でサイコロを振って、その結果に従って棒を上下左右のどれかに「蹴り倒す」(つまり、多元配列の隣り合った4つの要素の「どれか」を「壁」として書き換える)わけです。

んで、棒倒し法の場合、大まかに言うと次のルールがあります。

1. 多元配列で言う2行目だけは乱数に従って上下左右どこへでも棒を蹴り倒して良い。
2. 2行目以外は棒を上に蹴ってはならない。棒は下と左右だけに蹴る事が出来るが、次の付随ルールがある。
 a. 左に棒が倒れてる場合は下と右にしか蹴れない
 b. 左に棒が倒れてなかったら下と左右どれかに蹴飛ばして構わない。

まあ、壁がどこに作れるのか、って言い換えても良いでしょうが、上のようなルールがあります。上のコードで言うと、make_mazeって関数はこのルールに従って書いています。

さて、上のコードの解説をちょっとだけしますが、ここで気づいたでしょうが、実は「棒を蹴る方向」ってのには優先度があるんですね。

1. 右と下は「いつでも」蹴られる可能性がある。
2. 左は蹴られる可能性があったり無かったりする。
3. 上はループが多元配列の2行目にいない限り蹴られる可能性は全く無い。

つまり、乱数生成でサイコロを作る場合「サイコロの面の数」さえ変えればこの状況は割に簡単に記述出来るんです。
つまり、

・上下左右どこに蹴っても良い(多元配列二行目)のケース -> rand()%4を適用すれば良い
・下と左右どこに蹴っても良いケース -> rand()%3を適用すれば良い
・下と右どっちに蹴っても良いケース->rand()%2を適用すれば良い

rand()%nは0, 1, 2...(n-1)のどれかを返すわけですから、そうなると確実に蹴られる可能性がある右と下には0や1の値を与えて、優先度に従って「適当な数」に「適当な方向」を与えてやれば良い、って事になるわけです。
そこで出てきたのが列挙体でのDirection定義なわけです。

enum Direction { RIGHT, DOWN, LEFT, UP };

これは実は

int RIGHT = 0;
int DOWN = 1;
int LEFT = 2;
int UP = 3;

って宣言と同じなんですが、まあ、メンド臭いので(笑)、こういうカタチで定義してます。
RIGHTとDOWNに関してはどっちも「必ず蹴られる可能性がある」んで0と1、どっちにしてもいいんですが、LEFTは「やや弱い蹴られる可能性」がある、UPは「殆ど蹴られる可能性が」無い、ってぇんでこういう数値割り振りをしてます(つまり、置いた順序には意味があるのです)。
こうしておけば、例えば多元配列の2行目だった場合

rand()%4 -> 0, 1, 2, 3のどれか -> RIGHT, DOWN, LEFT, UPのどれかが選ばれる

わけですし、二行目以外で、特定の(偶数, 偶数)座標の左に「棒が蹴倒されてた場合」(つまり壁があった場合)、

rand()%2 -> 0, 1のどっちか -> RIGHTかDOWNが選ばれる

と自動的に決まるわけです。
    • good
    • 0
この回答へのお礼

詳しい解説ありがとうございます。
もう少し、精査してみます。

お礼日時:2016/06/17 23:34

確かに default ラベルのスペルは間違っていますが, この質問文にあるプログラムに関して言えばそこは現在の問題とは関係ありません (dice の値は case ラベルのどれかと一致するためそもそも default に落ちない).



プログラムを
#include <stdio.h>

int main(void)
{
int field[13][13]={0}; //0で初期化

/*
9が■、0が□
外枠
*/
for(int i=0; i<13; ++i){
for(int j=0; j<13; ++j){

if(i>=3){ //3列目以降
field[i*2][j*2-1]=9;// 左
printf("■");
}

}
printf("\n");
}
return 0;
}
まで圧縮したら気づくかな?
    • good
    • 0

うん、#1さんが言う通りですか。



>とりあえず
>× defalult: break;
>○ default: break;

そこがおかしいと、例えばclangでコンパイルして実行すると、

~ $ ./a.out
□□□□□□□□□□□□□
□□□□□□□□□□□□□

■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
segmentation fault (core dumped)

とセグメンテーションフォールトが起きるんですが、そこを直すと

~ $ ./a.out
□□□□□□□□□□□□□
□□□□□□□□□□□□□

■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■
■■■■■■■■■■■■■

とセグフォが起きないんで、デフォルトがタイポでフツーに動かなかったんですかね。
    • good
    • 0

> コンパイルしようとすると、停止してしまいます。



エラーメッセージが表示されると思いますが、まずそれを提示すべきではないでしょうか?

とりあえず
× defalult: break;
○ default: break;
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!