ページ 11

マインスイーパについて

Posted: 2018年2月24日(土) 23:00
by 前が見えねェ
現在マインスイーパをCで作っていて、以下の点について困っています。
・82~141行目:最初のマスの指定で周囲8マスを開けた状態にする。
・143~162行目:最初に指定したマス+その周囲8マス以外に地雷を配置
・164~179行目:周りの地雷の数を示す数字(1~8)の代入
・236~246行目:地雷の入っていないマスを全て開けたかどうかの判定

このマインスイーパの仕様
・最初にクリックしたマスの周囲8マスのみ開く。
・広範囲にわたって0が連なっていても一気に解放されない。一つずつ開放。
・マスの開き方は、「(縦座標),(横座標),p」という形式。
・旗→!、?→?のチェックはマスを開くのと同様に行える。
・マスのチェックはOを用いて外せる。
・マスが解放されたかどうかはcell[][]に、地雷の有無、周りの地雷の数はcell_imformation[][]に、p,!,?,Oの状態をcell_state[][]に保存。(cell_state[][]は多分いらない)

…つまり、自分で大まかに作ったが、雑にソースを書いた為に混乱してしまっている状態です。最初から書き直しても同じことを繰り返してしまう気がするのでここをこうすればいいのでは?ということがあれば教えて頂きたいです。

・以下ソースコード

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

int main(){
int m,n,num,q,i,j,k,l,x,y,count,end,a,b,c,sum,temporary,total;
char start[5];
char decision;
time_t time1,time2;
int cell[30][24] = {0}; //開いている 1, 開いていない 0
int cell_imformation[30][24] = {0}; //地雷 9, なし 0~8
int cell_state[30][24] = {0}; //なし 0, ! 1, ? 2, O 3
int mine[720];

do{
printf(
"----- マインスイーパ -----\n\n"
"マスの数と地雷の数を「(縦,横),地雷の数」と入力してください。\n"
"縦:9~30、横:9~24、地雷の数:10~667\n"
"(指標・初級:(9,9),10、中級:(16,16),40、上級:(30,16),99)\n\n"
"> "
);
do{
scanf("(%d,%d),%d", &m, &n, &num);
if(m < 9 || m > 30 || n < 9 || n > 24 || num < 10 || num > m * n * 0.9264){
printf("マスが規格外又は地雷の数が不適切です。");
}
} while(m < 9 || m > 30 || n < 9 || n > 24 || num < 10 || num > m * n * 0.9264);

printf(
"\n----- ルール -----\n\n"
"・マスの座標とpを入力するとマスを開きます。例:2,3,p\n"
"・開いたマスの数字は周囲8マスの地雷の数です。\n"
"・マスに地雷が入っていた場合、即時終了です。\n"
"・マスの座標と!又は?を入力した場合、そのマスにチェックを入れられます。\n"
"・マスのチェックがついているときは開くことができません。\n"
"・チェックのついているマスの座標とOを入力するとチェックを外せます。\n"
"・地雷の入っていないマスを全て開くとゲームクリアです。\n"
"・できるだけ早いクリアタイムを目指しましょう!\n\n"
"・マスの意味:\n"
"0~9:開いたマス、周りの地雷の数を示す\n"
"O:未開封のマス、地雷か数かも分からない\n"
"!,?:チェックボックスを入れたマス、開くことはできない\n"
"*:地雷、開いたら即終了\n\n"
"スタートする場合は改行してください。 > "
);
q = getchar();
printf("\n");

time(&time1);
srand((unsigned int)time(NULL));

count = 0;
end = 0;
for(i = 1; i <= 720; i++){
mine = i;
}

for(i = 0; i < m; i++){
for(j = 0; j < n; j++){
printf(" O ");
}
printf("\n\n");
}

do{
printf("> ");

do{
scanf("%d,%d,%c", &x, &y, &decision);
if(x < 0 && x > m && y < 0 && y > n && decision != 'p' && decision != '!' && decision != '?' && decision != 'O'){
printf("\n不正な入力です。\n\n> ");
}
} while(x < 0 && x > m && y < 0 && y > n && decision != 'p' && decision != '!' && decision != '?' && decision != 'O');
printf("\n");

x = x - 1;
y = y - 1;
if(decision == 'p'){
cell[x][y] == 1;
if(count == 0){
if(x > 0 && x < m && y > 0 && y < n){
for(i = x - 1; i < x + 2; i++){
for(j = y - 1; j < y + 2; j++){
cell[j] = 1;
}
}
} else if(x == 0){
if(y == 0){
for(i = x; i < x + 2; i++){
for(j = y; j < y + 2; j++){
cell[j] = 1;
}
}
} else if(y == n){
for(i = x; i < x + 2; i++){
for(j = y - 1; j < y + 1; j++){
cell[j] = 1;
}
}
} else {
for(i = x; i < x + 2; i++){
for(j = y - 1; j < y + 2; j++){
cell[j];
}
}
}
} else if(x == m){
if(y == 0){
for(i = x - 1; i < x + 1; i++){
for(j = y; j < y + 2; j++){
cell[j] = 1;
}
}
} else if(y == n){
for(i = x - 1; i < x + 1; i++){
for(j = y - 1; j < y + 1; j++){
cell[j] = 1;
}
}
} else {
for(i = x - 1; i < x + 1; i++){
for(j = y - 1; j < y + 2; j++){
cell[j] = 1;
}
}
}
} else if(y == 0){
for(i = x - 1; i < x + 2; i++){
for(j = y; j < y + 2; j++){
cell[j] = 1;
}
}
} else if(y == n){
for(i = x - 1; i < x + 2; i++){
for(j = y - 1; j < y + 1; j++){
cell[j] = 1;
}
}
}

for(i = 0; i < m * n; i++){
a = rand() % m * n;
b = rand() % m * n;
c = mine[a];
mine[a] = mine;
mine = c;
}

i = 0;
j = 0;
do{
if(mine[i] / n <= x - 1 && mine[i] / n >= x + 1 && mine[i] % n <= y - 1 && mine[i] % n >= y + 1){
temporary = mine[i];
cell_imformation[temporary / n][temporary % n] = 9;
i++;
j++;
} else {
i++;
}
} while(j < num);

for(i = 0; i < m; i++){
sum = 0;
for(j = 0; j < n; j++){

for(k = m - 1; k < m + 2; k++){
for(l = n - 1; l < n + 2; l++){
if(k != m && l != n && cell_imformation[k][l] == 9){
sum++;
}
}
}

cell_imformation[i][j] = sum;

}
}

count++;

} else {
if(cell_imformation[x][y] == 9){
printf("!!!地雷を開けてしまいました!!!\n");
end++;
}
}
} else if(decision == '!'){
cell_state[x][y] = 1;
} else if(decision == '?'){
cell_state[x][y] = 2;
} else if(decision == 'O'){
cell_state[x][y] = 3;
}

for(i = 0; i < m; i++){
for(j = 0; j < n; j++){
printf(" ");
if(cell_state[i][m] == 0){
if(cell[i][j] == 0){
printf("O");
} else {
switch(cell_imformation[i][j]){
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
printf("%d", cell_imformation[i][j]);
break;
case 9:
printf("*");
break;
default:
printf("エラー\n");
break;
}
}
} else if(cell_state[i][j]){
printf("!");
} else if(cell_state[i][j]){
printf("?");
} else if(cell_state[i][j]){
printf("O");
}
printf(" ");
}
printf("\n\n");
}

total == 0;

for(i = 0; i < m; i++){
for(j = 0; j < n; j++){
if(cell_imformation[i][j] != 9){
if(cell[i][j] == 0){
total++;
}
}
}
}

if(total == 0){
end = 2;
}

} while(end != 1 && end != 2);

time(&time2);

if(end == 1){
printf("貴方の負けです。お疲れさまでした。\n");
} else {
printf("おめでとうございます!貴方の勝ちです!\n");
}
printf("今回の経過時間は %.1f秒 でした。\n\n", difftime(time2,time1));

printf("終了する場合は0を入力してください。 > ");
scanf("%d", &q);

} while(q != 0);

return 0;

}

Re: マインスイーパについて

Posted: 2018年2月24日(土) 23:05
by 前が見えェ
ソースコードをcodeで囲い忘れていたので囲ったのを貼り付けます。

コード:

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

int main(){
  int m,n,num,q,i,j,k,l,x,y,count,end,a,b,c,sum,temporary,total;
  char start[5];
  char decision;
  time_t time1,time2;
  int cell[30][24] = {0};                     //開いている 1, 開いていない 0
  int cell_imformation[30][24] = {0};         //地雷 9, なし 0~8
  int cell_state[30][24] = {0};               //なし 0, ! 1, ? 2, O 3
  int mine[720];

  do{
    printf(
	   "-----  マインスイーパ  -----\n\n"
	   "マスの数と地雷の数を「(縦,横),地雷の数」と入力してください。\n"
	   "縦:9~30、横:9~24、地雷の数:10~667\n"
	   "(指標・初級:(9,9),10、中級:(16,16),40、上級:(30,16),99)\n\n"
	   "> "
	   );
    do{
      scanf("(%d,%d),%d", &m, &n, &num);
      if(m < 9 || m > 30 || n < 9 || n > 24 || num < 10 || num > m * n * 0.9264){
	printf("マスが規格外又は地雷の数が不適切です。");
      }
    } while(m < 9 || m > 30 || n < 9 || n > 24 || num < 10 || num > m * n * 0.9264);

    printf(
	   "\n-----  ルール  -----\n\n"
	   "・マスの座標とpを入力するとマスを開きます。例:2,3,p\n"
	   "・開いたマスの数字は周囲8マスの地雷の数です。\n"
	   "・マスに地雷が入っていた場合、即時終了です。\n"
	   "・マスの座標と!又は?を入力した場合、そのマスにチェックを入れられます。\n"
	   "・マスのチェックがついているときは開くことができません。\n"
	   "・チェックのついているマスの座標とOを入力するとチェックを外せます。\n"
	   "・地雷の入っていないマスを全て開くとゲームクリアです。\n"
	   "・できるだけ早いクリアタイムを目指しましょう!\n\n"
	   "・マスの意味:\n"
	   "0~9:開いたマス、周りの地雷の数を示す\n"
	   "O:未開封のマス、地雷か数かも分からない\n"
	   "!,?:チェックボックスを入れたマス、開くことはできない\n"
	   "*:地雷、開いたら即終了\n\n"
	   "スタートする場合は改行してください。 > "
	   );
    q = getchar();
    printf("\n");
    
    time(&time1);
    srand((unsigned int)time(NULL));
    
    count = 0;
    end = 0;
    for(i = 1; i <= 720; i++){
      mine[i] = i;
    }

    for(i = 0; i < m; i++){
      for(j = 0; j < n; j++){
	printf(" O  ");
      }
      printf("\n\n");
    }
    
    do{
      printf("> ");
    
      do{
	scanf("%d,%d,%c", &x, &y, &decision);
	if(x < 0 && x > m && y < 0 && y > n && decision != 'p' && decision != '!' && decision != '?' && decision != 'O'){
	  printf("\n不正な入力です。\n\n> ");
	}
      } while(x < 0 && x > m && y < 0 && y > n && decision != 'p' && decision != '!' && decision != '?' && decision != 'O');
      printf("\n");

      x = x - 1;
      y = y - 1;
      if(decision == 'p'){
	cell[x][y] == 1;
	if(count == 0){
	  if(x > 0 && x < m && y > 0 && y < n){
	    for(i = x - 1; i < x + 2; i++){
	      for(j = y - 1; j < y + 2; j++){
		cell[i][j] = 1;
	      }
	    }
	  } else if(x == 0){
	    if(y == 0){
	      for(i = x; i < x + 2; i++){
		for(j = y; j < y + 2; j++){
		  cell[i][j] = 1;
		}
	      }
	    } else if(y == n){
	      for(i = x; i < x + 2; i++){
		for(j = y - 1; j < y + 1; j++){
		  cell[i][j] = 1;
		}
	      }
	    } else {
	      for(i = x; i < x + 2; i++){
		for(j = y - 1; j < y + 2; j++){
		  cell[i][j];
		}
	      }
	    }
	  } else if(x == m){
	    if(y == 0){
	      for(i = x - 1; i < x + 1; i++){
		for(j = y; j < y + 2; j++){
		  cell[i][j] = 1;
		}
	      }
	    } else if(y == n){
	      for(i = x - 1; i < x + 1; i++){
		for(j = y - 1; j < y + 1; j++){
		  cell[i][j] = 1;
		}
	      }
	    } else {
	      for(i = x - 1; i < x + 1; i++){
		for(j = y - 1; j < y + 2; j++){
		  cell[i][j] = 1;
		}
	      }
	    }
	  } else if(y == 0){
	    for(i = x - 1; i < x + 2; i++){
	      for(j = y; j < y + 2; j++){
		cell[i][j] = 1;
	      }
	    }
	  } else if(y == n){
	    for(i = x - 1; i < x + 2; i++){
	      for(j = y - 1; j < y + 1; j++){
		cell[i][j] = 1;
	      }
	    }
	  }
	  
	  for(i = 0; i < m * n; i++){
	    a = rand() % m * n;
	    b = rand() % m * n;
	    c = mine[a];
	    mine[a] = mine[b];
	    mine[b] = c;
	  }
	  
	  i = 0;
	  j = 0;
	  do{
	    if(mine[i] / n <= x - 1 && mine[i] / n >= x + 1 && mine[i] % n <= y - 1 && mine[i] % n >= y + 1){
	      temporary = mine[i];
	      cell_imformation[temporary / n][temporary % n] = 9;
	      i++;
	      j++;
	    } else {
	      i++;
	    }
	  } while(j < num);

	  for(i = 0; i < m; i++){
	    sum = 0;
	    for(j = 0; j < n; j++){

		for(k = m - 1; k < m + 2; k++){
		  for(l = n - 1; l < n + 2; l++){
		    if(k != m && l != n && cell_imformation[k][l] == 9){
		      sum++;
		    }
		  }
		}
		
		cell_imformation[i][j] = sum;
		
	    }
	  }
	  
	  count++;

	} else {
	  if(cell_imformation[x][y] == 9){
	    printf("!!!地雷を開けてしまいました!!!\n");
	    end++;
	  }
	}
      } else if(decision == '!'){
	cell_state[x][y] = 1;
      } else if(decision == '?'){
	cell_state[x][y] = 2;
      } else if(decision == 'O'){
	cell_state[x][y] = 3;
      }
      
      for(i = 0; i < m; i++){
	for(j = 0; j < n; j++){
	  printf(" ");
	  if(cell_state[i][m] == 0){
	    if(cell[i][j] == 0){
	      printf("O");
	    } else {
	      switch(cell_imformation[i][j]){
	        case 0:
	        case 1:
	        case 2:
	        case 3:
	        case 4:
	        case 5:
	        case 6:
	        case 7:
	        case 8:
		  printf("%d", cell_imformation[i][j]);
		  break;
	        case 9:
		  printf("*");
		  break;
	        default:
		  printf("エラー\n");
		  break;
	      }
	    }
	  } else if(cell_state[i][j]){
	    printf("!");
	  } else if(cell_state[i][j]){
	    printf("?");
	  } else if(cell_state[i][j]){
	    printf("O");
	  }
	  printf("  ");
	}
	printf("\n\n");
      }

      total == 0;

      for(i = 0; i < m; i++){
	for(j = 0; j < n; j++){
	  if(cell_imformation[i][j] != 9){
	    if(cell[i][j] == 0){
	      total++;
	    }
	  }
	}
      }
      
      if(total == 0){
	end = 2;
      }

    } while(end != 1 && end != 2);

    time(&time2);

    if(end == 1){
      printf("貴方の負けです。お疲れさまでした。\n");
    } else {
      printf("おめでとうございます!貴方の勝ちです!\n");
    }
    printf("今回の経過時間は %.1f秒 でした。\n\n", difftime(time2,time1));
    
    printf("終了する場合は0を入力してください。 > ");
    scanf("%d", &q);
    
  } while(q != 0);

  return 0;

}

Re: マインスイーパについて

Posted: 2018年2月25日(日) 23:46
by かずま
前が見えェ さんが書きました:ソースコードをcodeで囲い忘れていたので囲ったのを貼り付けます。
codeタグは使っても、タブがスペース4個に置き換え
られるので、スペース8個分としてタブを使っていると、
ご覧のようにおかしな表示になります。
前が見えねェ さんが書きました: …つまり、自分で大まかに作ったが、雑にソースを書いた為に混乱してしまっている状態です。最初から書き直しても同じことを繰り返してしまう気がするのでここをこうすればいいのでは?ということがあれば教えて頂きたいです。
<unistd.h> を include しているということは、コンパイラは gcc ですか?
でも、<unistd.h> で宣言されている関数は使っていないようですね。

gcc なら -Wall オプションを付けてコンパイルしてみましょう。
「雑なソース」であることが分かります。
・8行目の変数 start は使用されていない。
・81行目と 236行目は、== ではなく、= ですね。
・105行目は、cell[j]; ではなく、cell[j] = 1;
・224~230行目は、3つの else if を同じ式で判定しています。

どこをどうすればいいのかは、うまく説明できないので、
最初から書き直してみました。

コード:

#include <stdio.h>  // printf, fgets, sscanf, puts, putchar
#include <stdlib.h> // srand, rand
#include <string.h> // strchr
#include <time.h>   // time

char cell[30+2][24+2];  // '0'~'8':周りの地雷の数, '*':地雷
char state[30+2][24+2]; // ' ':オープン, '.':クローズ, 'x','?':チェック
int row, col, num;      // 縦、横、地雷の数

void print(void)
{
    int i, j;
    printf("    ");
    for (i = 1; i <= col; i++) printf("%3d", i);
    printf("\n     ");
    for (i = 1; i <= col; i++) printf("---");
    putchar('\n');
    for (i = 1; i <= row; i++) {
        printf("%3d:", i);
        for (j = 1; j <= col; j++)
            printf("  %c", state[i][j] == ' ' ? cell[i][j] : state[i][j]);
        putchar('\n');
    }
    putchar('\n');
}

void set_mine(int n)
{
    int i, j, x, y, m  = 0;
    for (i = 1; i <= row; i++)
        for (j = 1; j <= col; j++)
            if (state[i][j] != ' ' && cell[i][j] != '*')
                if (m++ == n) {
                    cell[i][j] = '*';
                    for (x = i - 1; x <= i + 1; x++)
                        for (y = j - 1; y <= j + 1; y++)
                            if (cell[x][y] != '*') cell[x][y]++;
                    return;
                }
}

void game(void)
{
    time_t time1, time2;
    char buf[256], decision;
    int i, j, k, x, y, first = 1, end = 0;

    printf("-----  マインスイーパ  -----\n\n"
        "マスの数と地雷の数を「縦 横 地雷の数」と入力してください。\n"
        "縦:9~30、横:9~24、地雷の数:10~667\n"
        "(指標・初級:9 9 10、中級:16 16 40、上級:30 16 99)\n\n"
        "> ");
    while (1) {
        if (!fgets(buf, sizeof buf, stdin)) return;
        if (sscanf(buf, "%d%d%d", &row, &col, &num) == 3
            && row >= 9 && row <= 30 && col >= 9 && col <= 24
            && num >= 10 && num <= row * col * 0.9264) break;
        printf("マスが規格外又は地雷の数が不適切です。\n> ");
    }
    printf("\n-----  ルール  -----\n\n"
        "・マスの座標とpを入力するとマスを開きます。例:2 3 p\n"
        "・開いたマスの数字は周囲8マスの地雷の数です。\n"
        "・マスに地雷が入っていた場合、即時終了です。\n"
        "・マスの座標とx又は?を入力した場合、そのマスにチェックを入れられます。\n"
        "・マスのチェックがついているときは開くことができません。\n"
        "・チェックのついているマスの座標と.を入力するとチェックを外せます。\n"
        "・地雷の入っていないマスを全て開くとゲームクリアです。\n"
        "・できるだけ早いクリアタイムを目指しましょう!\n\n"
        "・マスの意味:\n"
        "0~8:開いたマス、周りの地雷の数を示す\n"
        ".:未開封のマス、地雷か数かも分からない\n"
        "x,?:チェックボックスを入れたマス、開くことはできない\n"
        "*:地雷、開いたら即終了\n\n"
        "スタートする場合は改行してください。 > ");
    getchar();
    printf("\n");
    srand((unsigned) time(&time1));
    for (i = 0; i <= row + 1; i++)
        for (j = 0; j <= col + 1; j++)
            cell[i][j] = '0', state[i][j] = '.';
    print();
    do {
        printf("> ");
        while (1) {
            if (!fgets(buf, sizeof buf, stdin)) return;
            if (sscanf(buf, "%d%d %c", &x, &y, &decision) == 3
                && x > 0 && x <= row && y > 0 && y <= col
                && strchr("px?.", decision)) break;
            printf("不正な入力です。\n> ");
        }
        if (decision == 'p') {
            if (first) {
                first = 0;
                k = row * col;
                for (i = x - 1; i <= x + 1; i++)
                    for (j = y - 1; j <= y + 1; j++)
                        if (i >= 1 && i <= row && y >= 1 && y <= col)
                            state[i][j] = ' ', k--;
                for (i = 0; i < num; i++) set_mine(rand() % k--);
            }
            else if (state[x][y] == '.') {
                state[x][y] = ' ';
                if (cell[x][y] == '*')
                    end = 1, puts("!!!地雷を開けてしまいました!!!");
            }
        }
        else state[x][y] = decision;
        print();
        k = row * col;
        for (i = 1; i <= row; i++)
            for (j = 1; j <= col; j++)
                if (state[i][j] == ' ' || cell[i][j] == '*') k--;
        if (k == 0) end = 2;
    } while (end == 0);
    time(&time2);
    for (i = 1; i <= row; i++)
        for (j = 1; j <= col; j++) state[i][j] = ' ';
    print();
    if (end == 1) puts("貴方の負けです。お疲れさまでした。");
    else puts("おめでとうございます!貴方の勝ちです!");
    printf("今回の経過時間は %.1f秒 でした。\n\n", difftime(time2, time1));
}

int main(void)
{
    char buf[256];
    do {
        game();
        printf("終了する場合は 0 を入力してください。 > ");
    } while (fgets(buf, sizeof buf, stdin) && buf[0] != '0');
    return 0;
}
'0' (ゼロ)と 'O'(大文字のオー) が区別しにくく、
'1' と '!' が区別しにくいので、'O' の代わりに '.'、
'!' の代わりに 'x' にしてみました。
また、入力も ( や , などを入れるのは面倒なのでやめました。
参考になりますか?