gombeのブログ

マイコンの電子工作系PIC32/KiCad/C/C++/3D/

mine sweeper(マインスイーパー)作成

マインスイーパー作りました。課題です。めんどいなあ。。。再帰を使ってかけって言われて書いたけど、再帰は必須だね。使わない縛りPlayは誰もしないと思うよ。うん。

こうやって見えても500行でほぼすべての機能を入れました。メモをしておきましょう。まずいくつかの制約事項があったのでそこから。まずFieldの配列に入れるものが決まっていて、それ道理に設計すること、再帰を使って周辺を開くこと。(当たり前なんだがそうしないとクッソ実装が大変)で、フラグも考慮してあっていると仮定して周りを開けます。まあ当たり前なんだけどフラグミスると死にます。

まずプログラムが始まると、フィールドを動的に作成します。まあ当たり前なんだけどこうしないとFieldの大きさが固定になってしまいますのでね。で、フィールドを0で埋めておいてカーソルから3*3以外のところを候補の配列にぶっこんで、シャッフルして、先頭からn個取り出します。

取り出したn個を設置する際、同時にヒントも生成します。そうすると無駄なカウントがなく、処理が早いです。生成は考えて作らないと体感速度が大幅に変わります。何よりも最初に開いたときに設置するので、その時に時間がかかると嫌ですよね(よくあるけど)。

こうしないともし80*150のフィールドで11900個の爆弾を置くと間違いなくフリーズしますというか減点されますのでね。で、こうしたら準備は完了したので周りを開きます。

周りを開く関数はフィールドと位置を引数とすると良いでしょう。もし自分が0だったら(フラグを考慮しても同様)周りを開いていきます。と言っても8方向再帰で開きましょう。これはループを使って実装すると良いでしょう。

後は飾りを実装すれば完成です。

コード例ですが、まあまあ作りこみました。

[c]

include <stdio.h>

include <stdlib.h>

include <assert.h>

include <ctype.h>

include <stdbool.h>

/ * field discription * 0-8 .....numOfBomb * 9 .....bombInHere * 10-18 .....OpenedBlock(NumOfBomb-10) * 20-29 .....flaged here(0-9) / typedef struct{ int width; int height; int numOfBombs; int **field; } field_t;

typedef struct{ int x; int y; } vector_t;

vector_t add(vector_t a,vector_t b);/add two vectors/ int distSq(vector_t a,vector_t b);/distance from 2 points/ vector_t calcMotion(char ch);/calcMotion/ void calcCursor(field_t field,char ch,vector_t cursor);/cursor motion/

int initGame(field_t field);/initialize game/ void exitGame(field_t field);/deinitialize game/ int setBombs(field_t field,vector_t cursor);/set bombs(cursor requred)/ int newField(field_t field);/make new field/ void freeField(field_t field);/free field/ int numOfFlags(field_t field,vector_t p);

void dispMessage(field_t *field);

void dispElement(int num);/display Element of map/ void dispField(field_t field,vector_t cursor);/disp map/

void open(field_t field,vector_t p);/open tile/ void putFlag(field_t field,vector_t p); bool isGameClear(field_t field); bool isGameOver(field_t *field);

void gameOverEvent(field_t field,vector_t cursor); void gameClearEvent(field_t field,vector_t cursor);

define COL_RED 1

define COL_GREEN 2

define COL_YELLOW 3

define COL_BLUE 4

define COL_PINK 5

define COL_CYAN 6

define COL_WHITE 7

define COL_RESET 9

define SET_BACKGROUND_COL(col) printf("\033[4%dm",col)

define SET_FRONTGROUND_COL(col) printf("\033[3%dm",col)

define REVERCE_BACKGROUND_FRONTGROUND_COL() printf("\033[7m")

define RESET_SETTING() printf("\033[0m")

define CLR_SCREEN() printf("\033[2J")

define SET_CURSOR(x,y) printf("\033[%d;%dH",y+1,x+1)

const struct{ char key; vector_t move; } KEYS[]={ {'4',{-1,0}}, {'8',{0,-1}}, {'6',{1,0}}, {'2',{0,1}}, {'7',{-1,-1}}, {'9',{1,-1}}, {'1',{-1,1}}, {'3',{1,1}}, };

int numOfFlagsinField;

int main(int argc,char **argv){ char ch; field_t field; vector_t cursor={0,0}; bool isStarted = false;

if(argc == 1){ field.width = 15; field.height = 15; field.numOfBombs = 30; } else if(argc == 4){ field.width = atoi(argv[1]); field.height = atoi(argv[2]); field.numOfBombs = atoi(argv[3]); } else{ printf("Num of args must be 0 or 3.\n[width] [height] [num of bombs]\n"); return EXIT_FAILURE; }

dispMessage(&field);

assert(initGame(&field)==EXIT_SUCCESS);

CLR_SCREEN(); dispField(&field,&cursor); while(1){ ch = getchar(); if(isdigit(ch)&&ch!='0'&&ch!='5'){ calcCursor(&field,ch,&cursor); }

if(ch == '\n'){
  if(!isStarted){
isStarted = true;
if(setBombs(&amp;field,&amp;cursor) == EXIT_FAILURE){
  printf(&quot;cannot put bombs\n&quot;);
  break;
}
  }
  if(field.field[cursor.y][cursor.x]&lt;19) open(&amp;field,cursor); if(isGameOver(&amp;field)){ gameOverEvent(&amp;field,&amp;cursor); break; } } else if(ch == 'e')break; else if(ch == 'f'&amp;&amp;isStarted)putFlag(&amp;field,&amp;cursor); CLR_SCREEN(); dispField(&amp;field,&amp;cursor); if(isGameClear(&amp;field)&amp;&amp;isStarted){ gameClearEvent(&amp;field,&amp;cursor); break; } } exitGame(&amp;field); return EXIT_SUCCESS; } void dispMessage(field_t *field){ printf(&quot;===Mine Sweeper===\n&quot;); printf(&quot;  __\n&quot;); printf(&quot; /   \\n&quot;); printf(&quot;|/▼ ▼ \ |\n&quot;); printf(&quot;| ヽ_/ |\n&quot;); printf(&quot; \___/ \n&quot;); printf(&quot;\nPlz confirm configulation...\n&quot;); printf(&quot;[%-*s]:%d\n&quot;,15,&quot;width of field&quot;,field-&gt;width);

printf("[%-s]:%d\n",15,"height of field",field->height); printf("[%-s]:%d\n",15,"number of bombs",field->numOfBombs); printf("If you will edit these elements, you must put the argument!\n"); printf("[width] [height] [numOfBomb]\n\n"); printf("Enter to continue..."); while(getchar()!='\n');

printf("Plz confirm key settings...\n"); printf("[%-s]...%s.\n",5,"Enter","Dig (if put flag, dig around)"); printf("[%-s]...%s.\n",5,"F","Put flag"); printf("[%-s]...%s.\n",5,"4","Move cursor to the left"); printf("[%-s]...%s.\n",5,"8","Move cursor to the up"); printf("[%-s]...%s.\n",5,"6","Move cursor to the right"); printf("[%-s]...%s.\n",5,"2","Move cursor to the down"); printf("[%-s]...%s.\n",5,"7","Move cursor to the upper left"); printf("[%-s]...%s.\n",5,"9","Move cursor to the upper right"); printf("[%-s]...%s.\n",5,"1","Move cursor to the lower left"); printf("[%-s]...%s.\n",5,"3","Move cursor to the lower right"); printf("[%-*s]...%s.\n",5,"E","Exit game");

printf("\ That it! Have fun!\n\ Enter to continue..."); while(getchar()!='\n'); }

bool isGameOver(field_t *field){ int x,y; for(x=0;x<field->width;x++){ for(y=0;y<field->height;y++){ if(field->field[y][x]==19) return true; } } return false; }

bool isGameClear(field_t *field){ int x,y; for(x=0;x<field->width;x++){ for(y=0;y<field->height;y++){ if*1 return false; } } return true; }

vector_t calcMotion(char ch){ unsigned int i; for(i=0;i<sizeof(KEYS)/sizeof(KEYS[0]);i++){ if(KEYS[i].key == ch){ return KEYS[i].move; } } assert(0); return KEYS[0].move; } void calcCursor(field_t field,char ch,vector_t cursor){ cursor = add(cursor,calcMotion(ch)); if(field->width <= cursor->x){ cursor -> x = field->width-1; } else if(0 > cursor->x)cursor->x = 0; if(field->height <= cursor->y){ cursor -> y = field->height-1; } else if(0 > cursor->y)cursor->y = 0; }

bool isInField(field_t field,vector_t p){ return field->width > p->x && 0 <= p->x&& field->height > p->y && 0 <= p->y; }

int initGame(field_t *field){ int res;

assert(field->width); assert(field->height); assert(field->numOfBombs);

numOfFlagsinField = 0;

res = newField(field); if(res == EXIT_FAILURE){ return EXIT_FAILURE; }

if(system("stty -echo -icanon min 1 time 0")<0){ printf("echo setting failed\n"); return EXIT_FAILURE; }

return EXIT_SUCCESS; }

void exitGame(field_t *field){ freeField(field); if(system("stty echo -icanon min 1 time 0")<0){ printf("echo setting failed\n"); } }

void gameClearEvent(field_t field,vector_t cursor){ int x,y; for(x=0;x<field->width;x++){ for(y=0;y<field->height;y++){ if(field->field[y][x]==9) field->field[y][x]+=20; else if(field->field[y][x]<9){ field->field[y][x]+=10; } } } dispField(field,cursor); printf("Game cleard\nThank you for playing!\n"); }

void gameOverEvent(field_t field,vector_t cursor){ int x,y; for(x=0;x<field->width;x++){ for(y=0;y<field->height;y++){ if(field->field[y][x]<=9) field->field[y][x]+=10; } } dispField(field,cursor); printf("Bomb exploded!!\nYou dead。。。\n"); }

int setBombs(field_t field,vector_t cursor){ vector_t *candidi; size_t numofcandidi;

unsigned int i = 0;

numofcandidi = field->heightfield->width-4;/widthheight-4が最大数/

candidi = (vector_t)malloc(sizeof(vector_t)numofcandidi); if(candidi == NULL)return EXIT_FAILURE; { vector_t p;

for(p.y=0;p.y&lt;field-&gt;height;p.y++){
  for(p.x=0;p.x&lt;field-&gt;width;p.x++){
if(distSq(p,*cursor)&gt;2){
  candidi[i++]=p;
}
  }
}

}

numofcandidi = (size_t)i; { vector_t tmp; int randnum; for(i=0;i<numofcandidi;i++){ randnum = rand()%numofcandidi; tmp = candidi[i]; candidi[i] = candidi[randnum]; candidi[randnum] = tmp; } }

if(numofcandidi < (unsigned int)field->numOfBombs){ printf("candidi:%lu\n",numofcandidi); return EXIT_FAILURE; } { int j; vector_t tmp; for(i = 0;i < (unsigned int)field->numOfBombs;i++){ field->field[candidi[i].y][candidi[i].x] = 9; for(j=0;j<(int)(sizeof(KEYS)/sizeof(KEYS[0]));j++){ tmp = add(KEYS[j].move,candidi[i]); if(isInField(field,&tmp)){ if(field->field[tmp.y][tmp.x]!=9){ field->field[tmp.y][tmp.x]++; } } } } } free(candidi);

return EXIT_SUCCESS; }

void putFlag(field_t field,vector_t p){ if(field->field[p->y][p->x] < 10){ field->field[p->y][p->x]+=20; numOfFlagsinField++; } else if(field->field[p->y][p->x] >= 20&&field->field[p->y][p->x] < 30) { field->field[p->y][p->x]-=20; numOfFlagsinField--; } }

void open(field_t field,vector_t p){ vector_t tmp; int i; if(field->field[p.y][p.x] < 10) field->field[p.y][p.x] += 10;/open tile/ if(field->field[p.y][p.x]==0+10|| numOfFlags(field,&p)==field->field[p.y][p.x]-10){ for(i=0;i<(int)(sizeof(KEYS)/sizeof(KEYS[0]));i++){/open around/ tmp = add(p,KEYS[i].move); if(isInField(field,&tmp)){ if(field->field[tmp.y][tmp.x]<10)/have not opended yet*/ open(field,tmp); // if(field->field[tmp.y][tmp.x] } } } }

int newField(field_t *field){ unsigned int height = field->height;

field->field = calloc(sizeof(int*),field->height);

int **fld = field->field;

if(fld==NULL)return EXIT_FAILURE;

do{ fld = calloc(sizeof(int),field->width); if(fld++==NULL)return EXIT_FAILURE; }while(--height);

return EXIT_SUCCESS; }

void freeField(field_t *field){ unsigned int height = field->height; int **fld = field->field;

do{ free(*(fld++)); }while(--height);

free(field->field); }

int numOfFlags(field_t field,vector_t p){ int i; int numOfFlags=0; vector_t tmp;

for(i=0;i<(int)(sizeof(KEYS)/sizeof(KEYS[0]));i++){ tmp = add(*p,KEYS[i].move); if(isInField(field,&tmp)){ if(field->field[tmp.y][tmp.x]>=20){ numOfFlags++; } } } return numOfFlags; }

void dispElement(int num){ assert(num>=0); if(num < 10){ printf("#"); } else if(num < 20){ if(num == 10){printf(" ");} else if(num <= 18){ printf("%d",num-10); } else if(num == 19){ SET_BACKGROUND_COL(COL_RED); printf("B"); SET_BACKGROUND_COL(COL_RESET); } } else if(num < 30){ SET_BACKGROUND_COL(COL_GREEN); printf("F"); SET_BACKGROUND_COL(COL_RESET); } else assert(0); } void dispField(field_t field,vector_t cursor){ int x,y; int **fld = field->field;

for(x=0;x<field->width;x++){ printf("+-"); } printf("+\n"); for(y=0;y<field->height;y++){ for(x=0;x<field->width;x++){ if(cursor->x==x&&y==cursor->y){ printf("["); REVERCE_BACKGROUND_FRONTGROUND_COL(); } else if(cursor->x==x-1&&y==cursor->y)printf("]"); else printf("|"); dispElement(fld[y][x]); RESET_SETTING(); } if(cursor->x==x-1&&y==cursor->y)printf("]\n"); else printf("|\n"); } for(x=0;x<field->width;x++){ printf("+-"); } printf("+\n"); printf("num of flags %d/%d\n",numOfFlagsinField,field->numOfBombs); }

vector_t add(vector_t a,vector_t b){ vector_t res;

res.x = a.x+b.x; res.y = a.y+b.y;

return res; }

int distSq(vector_t a,vector_t b){ return (a.x-b.x)(a.x-b.x)+(a.y-b.y)(a.y-b.y); }

[/c]

*1:field->field[y][x]>=20&&field->field[y][x]<=28)|| (field->field[y][x]>=0 &&field->field[y][x]<=8