Игра - Сапер.

First State

Задача: создать игру Сапер;
Использованный API: GTK/GDK;
Среда разработки: Visual Studio 2008;

Я думаю, все из вас сталкивались с такой игрой, как сапер. В данной программе дано поле 20х20, количество мин вы можете выбрать на свой вкус от 10 до 99. Мины распределяются случайно по полю, затем, производится проход по всему полю. Что мы делаем:
1. Выбираем ячейку.
2. Смотрим, сколько мин находится в окрестности (конечно же, если сама ячейка не является миной).
3. записываем количество мин в окрестности в данную ячейку.

Так же имеется меню:
S - Start - начало игры, т.е. создается состояние согласно текущему количеству мин.
M - Menu - Выход в главное меню.
E - Exit - Выход из игры.

Индикатор смерти показан в правом вернем углу.
Игра заканчивается, когда либо открыты все клеточки за исключением мин, или же на всех минах стоят флажки. (Флажок ставится правой кнопкой мыши).

Основная трудность в данной задаче, это произвести открытие всех соседних пустых клеточек, при нажатии на пустую клетку. Я предложу вам 2 варианта решения этой проблемы:
1. BFS (Breadth-First Search). Реализуется, с использованием очереди.
2. DFS (Depth-First Search). Реализуется рекурсивно.

1. BFS:

/*Необходимые объявления*/
int dx[8]={0,0,1,-1,1,1,-1,-1};
int dy[8]={1,-1,1,1,0,-1,-1,0};
 
struct NODE // элемент очереди
{
 int x,y;
 NODE* next;
}node;
 
struct QUEUE // очередь
{
 NODE *begin,*end;
}queue;
 
QUEUE zq; // создаем очередь
NODE *tmp,*n; // создаем ссылки на два элемента очереди.
 
/*Вызов функции*/
zq.begin = zq.end = NULL; // наша очередь пуста
if(zq.begin == zq.end && zq.begin == NULL) // если очередь пустая
{
   zq.begin = zq.end = (NODE*)malloc(sizeof(node)); // выделяем память для первого элемента
   tmp = zq.end;
   tmp->x = y_m; tmp->y = x_m; // помещаем в x,y координаты элемента в матрице
   tmp->next = NULL; // у нас только 1 элемент, дальше ничего нет.
}
tmp = zq.begin; // начинаем сначала.
gdk_draw_zeropoint(zq.begin->x,zq.begin->y); // вызываем BFS для открытия пустых ячеек.
 
/* -BFS- */
gboolean gdk_draw_zeropoint(int i,int j)
{	
	while(tmp!=NULL) // пока не дойдем до конца списка
	{
		for(int s=0; s<8; s++) // пробегаем окрестность элемента (в общем случае с ним граничит ровно 8 элементов) 
		{
                        // если такой элемент в матрице существует, и клеточка еще не открыта
		        if((i+dx[s] >= 0) && (i+dx[s] < N) && (j+dy[s] >= 0) && (j+dy[s] < N) && (a[i+dx[s]][j+dy[s]].plstrue)) 
			{
				if(a[i+dx[s]][j+dy[s]].point == 0) // если количество мин в окрестности равно 0.
				{
					n = (NODE*)malloc(sizeof(node)); // выделяем память для нового элемента.
					n->next = NULL; // следующий за последним, пустой.
                                        // записываем координаты точки.
					n->x = i+dx[s]; 
					n->y = j+dy[s];
					zq.end->next = n; // следующий элемент в очереди за последним, это этот элемент.
					zq.end = n; // передвигаем конец очереди.
 
					gdk_draw_picture(pl,(j+dy[s])*HH,MH + (i+dx[s])*HH,HH,HH); // рисуем на экране картинку (открытой ячейки)
					a[i+dx[s]][j+dy[s]].plstrue = 0; // этот элемент мы уже открыли и обработали
				}
				else
				{
					gdk_draw_picture(pl,(j+dy[s])*HH,MH + (i+dx[s])*HH,HH,HH); // рисуем картинку (открытой ячейки)
					gettext(txt,a[i+dx[s]][j+dy[s]].point); // преобразовываем число мин в строку символов
					draw_text_in_center(txt,(j+dy[s])*HH+HH/2,(i+dx[s])*HH+HH/2+MH); // выводим число мин на экран
					a[i+dx[s]][j+dy[s]].plstrue = 0; // этот элемент мы уже открыли и обработали
				}
			}
		}
 
		zq.begin = zq.begin->next; // передвигаем начало очереди, начальный элемент мы уже обработали
		if(tmp == zq.end) break; // если достигли конца списка, конец.
		tmp = zq.begin; // tmp - начальный элемент списка.
                // записываем в i,j координаты элемента в матрице.
		i = tmp->x;
		j = tmp->y;
	}
	return TRUE;
}

2. DFS:
Для лучшего понимания, как работает рекурсия, рассмотрим псевдокод DFS, с пояснениями:

int dx[8]={0,0,1,-1,1,1,-1,-1};
int dy[8]={1,-1,1,1,0,-1,-1,0};
 
void dfs(int x,int y) // x,y - координаты данной точки в массиве.
{
 if(open[x][y]) return; // если клетка уже открыта, просто возвращаемся.
 open[x][y]; // открыть клетку (т.е. вывести её содержимое на экран)
 if(point[x][y]==0) // если количество мин в окрестности равно 0.
 {
  for(int k=0;k<8;k++) // пробегая по массивам dx,dy мы сможем поочередно пройти всех соседей данной клетки.
  {
   if(good(x+dx[k],y+dy[k])) // если координаты x+dx[k],y+dy[k] хорошие, т.е. 0<=x+dx[k]<N && 0<=y+dy[k]<N.
    dfs(x+dx[k],y+dy[k]); // вызываемся рекурсивно в данной точке.
  }
 }
}

В архиве так же содержится, доработанная и улучшенная версия данной игры. Но код улучшенной версии не предоставляется, а предоставляется готовая игра, в ознакомительных целях.

Приятной игры.

Ключевые слова: 
Сапер, игра
ВложениеРазмер
Saper.zip864.56 кб