のらりくらり

物理化学分野のポスドクです。プログラミング、読書、自転車などが好きです。

マインスイーパ?

わけあって、マインスイーパ?のプログラム書いてみた。コア部分のみだけど。初めはRubyで書いて、C++で書きなおし、間違いに気づいたため、もう一度C++で書きなおした。にしても相変わらずC++は苦手だ
最後の版のソースを載せてみる。たまにはアウトプットしないと。

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

#define LENGTH 10 	//一辺の長さ

struct cell {
	bool isOpened;
	int arroundBomb;
	bool isBomb;
	bool flag;
};

class MineSweeper {
	int bombs;	//地雷の個数
	int opened;	//開かれた個数
	struct cell field[LENGTH][LENGTH];
public:
	MineSweeper();
	void bomb_set(int n);
	int count_arrount_bombs(int x, int y);
	void print();
	int open(int x, int y);
	void setFlag(int x, int y);
	void resetFlag(int x, int y);
	void debugprint(void);
};

//座標の初期化をして、とりあえず、全部爆弾が無いことにしておく。
//コンストラクタなので、自動で呼ばれます
MineSweeper::MineSweeper()
{
	opened = 0;
	for (int i = 0; i < LENGTH; i++) {
		for(int j = 0; j < LENGTH; j++) {
			field[i][j].arroundBomb = 0;
			field[i][j].isOpened = false;
			field[i][j].isBomb = false;
			field[i][j].flag = false;
		}
	}
}

//引数nに指定された個数だけ、爆弾を仕掛ける。
//爆弾を仕掛ける座標は乱数で決める。(辺の長さで割った余りを求めれば絶対大丈夫)
void MineSweeper::bomb_set(int n)
{
	srand(time(NULL));
	bombs = n;
	for(int i = 0; i < n; i++) {
		int x = ( rand()) % LENGTH;
		int y = ( rand()) % LENGTH;
		field[x][y].isBomb = true;
	}
}

//x,yで指定された座標の周りの爆弾の個数を求める。
//もし地雷がそこに会った場合は、-1を返して、ゲームオーバーであることを知らせる、
int MineSweeper::count_arrount_bombs(int x, int y)
{
	int count = 0;
	if (field[x][y].isBomb == true)
		return -1;
	if (x < 0 || LENGTH <= x || y < 0 || LENGTH <= y )
		return -2;
	if (0 < x) {
		if (field[x-1][y].isBomb == true) {
			count++;
		}
		if (0 < y) {
			if (field[x-1][y-1].isBomb == true) {
				count++;
			}
		}
		if (y < LENGTH - 1) {
			if (field[x-1][y+1].isBomb == true) {
				count++;
			}
		}
	}
	if (x < LENGTH - 1) {
		if (field[x+1][y].isBomb == true) {
			count++;
		}
		if (0 < y) {
			if (field[x+1][y-1].isBomb == true) {
				count++;
			}
		}
		if (y < LENGTH - 1) {
			if (field[x-1][y+1].isBomb == true) {
				count++;
			}
		}
	}
	if (0 < y) {
		if (field[x][y-1].isBomb == true) {
			count++;
		}
	}
	if (y < LENGTH - 1) {
		if (field[x][y+1].isBomb == true) {
			count++;
		}
	}
	return count;
}

//デバッグ用。爆弾があるところはあると表示されるので、ゲームには使えない。
void MineSweeper::debugprint()
{
	for(int i = 0; i < LENGTH; i++) {
		for(int j = 0; j < LENGTH; j++) {
			if (field[i][j].isBomb == false) {
				printf("%c ", 'o');
			} else if (field[i][j].isBomb == true){
				printf("%c ", 'x');
			}
		}
		putchar('\n');
	}
}
//地雷があると印をつける
void MineSweeper::setFlag(int x, int y)
{
	field[x][y].flag = true;
}

//地雷の印を解除
void MineSweeper::resetFlag(int x, int y)
{
	field[x][y].flag = false;
}

//x,yの座標を開く処理
int MineSweeper::open(int x, int y)
{
	if (field[x][y].isBomb == true) {
		return -1;
	}

	field[x][y].isOpened = true;
	opened++;
	if (opened == LENGTH * LENGTH - bombs)
		return 1;
	
	field[x][y].arroundBomb = count_arrount_bombs(x, y);
	return 0;
}

//まだ開かれていない座標はoを表示、開かれている場合は、周囲の地雷の個数を表示。
void MineSweeper::print()
{
	for(int i = 0; i < LENGTH; i++) {
		for(int j = 0; j < LENGTH; j++) {
			if (field[i][j].isOpened == false) {
				if (field[i][j].flag == true) {
					printf("%c ", 'l');
				} else {
					printf("%c ", 'o');
				}
			} else {
				printf("%1d ", field[i][j].arroundBomb);
			}
		}
		putchar('\n');
	}
}
			
					

int main(void)
{
	int x, y;
	MineSweeper ms;
	ms.bomb_set(20);
	
	ms.print();
	for(;;) {
		printf("Input the value of x:");
		scanf("%1d", &x);
		printf("Input the value of y:");
		scanf("%1d", &y);
		int result = ms.open(x, y);
		if (result == -1) {
			printf("Game Over\n");
			return 0;
		} else if (result == 1) {
			printf("success!\n");
			return 0;
		} else {
			ms.print();
		}
	}
}

せっかくだし、こんなmain関数使うんじゃなくて、curses使ってみるかね