В начале игры нужно расставить мины, затем для каждой клетки поля подсчитать, сколько мин находится в соседних клетках. Процедура NewGame (ее текст приведен в листинге 15.3) решает эту задачу.
Листинг 15.3. Процедура NewGame
// новая игра — генерирует новое поле
procedure NewGame ();
row,col: integer; // координаты клетки (индексы массива)
n: integer; // количество поставленных мин
k: integer; // кол-во мин в соседних клетках
Begin
// Очистим эл-ты массива, соответствующие клеткам
// игрового поля
for row:=1 to MR do
for col:=1 to MC do Pole[row,col]:= 0;
// расставим мины
Randomize О; // инициализация ГСЧ
n:= 0; // кол-во мин
Repeat
row:= Random(MR) + 1;
col:= Random(MC) + 1;
if (Pole[row,col] <> 9) then begin
Pole[row,col]:= 9; n:= n+1;
end;
until (n = NM);
// для каждой клетки вычислим // кол-во мин в соседних клетках
for row:= 1 to MR do
for col:= 1 to MC do
if (Pole[row,col] <> 9) then
begin k:=0;
if Pole[row-l,col-l] = 9 then k = k + 1;
if Pole[row-1,col] =' 9 then k = k + 1;
if Pole[row-1,col+1] = 9 then k = k + 1;
if Pole[row,col-1] = 9 then k = k + 1;
if Pole[row,col+1] = 9 then k = k + 1;
if Pole[row+1,col-1] = 9 then k = k + 1;
if Pole[row+1,col] = 9 then k = k + 1;
if Pole[row+l,col+l] = 9 then k:= k + 1;
Pole[row,col]:= k;
end;
status:= 0; // начало игры
nMin:= 0; // нет обнаруженных мин
nFlag:= 0; // нет поставленных флагов
end;
После того как процедура NewGame расставит мины, процедура showPoie (ее текст приведен в листинге 15.4) выводит изображение игрового поля.
Листинг 15.4. Процедура ShowPole
// Показывает поле
Procedure ShowPoie(Canvas: TCanvas; status: integer);
Var
row,col: integer;
Begin
for row:= 1 to MR do
for col:= 1 to MC do
Kletka(Canvas, row, col, status);
end;
Процедура showPoie выводит изображение поля последовательно, клетка за клеткой. Вывод изображения отдельной клетки выполняет процедура Kletka, ее текст приведен в листинге 15.5. Процедура Kletka используется для вывода изображения поля в начале игры, во время игры и в ее конце. В начале игры (значение параметра status = 0) процедура выводит только контур клетки, во время игры — количество мин в соседних клетках или флажок, а в конце отображает исходное состояние клетки и действия пользователя. Информацию о фазе игры процедура Kletka получает через параметр status.
Листинг 15.5. Процедура Kletka
// выводит на экран изображение клетки
Procedure Kletka(Canvas: TCanvas;
row, col, status: integer);
Var
x,y: integer; // координаты области вывода
Begin
x:= (col-1)* W + I; у:= (row-1)* H + 1;
if status = 0 then begin
Canvas.Brush.Color:= clLtGray;
Canvas.Rectangle(x-1,y-1,x+W,y+H);
exit;
end;
if Pole[row,col] < 100 then
Begin
Canvas.Brush.Color:= clLtGray; // неоткрытые — серые
Canvas.Rectangle(x-1,y-l,x+W,y+H);
// если игра завершена (status = 2), то показать мины
if (status = 2) and (Pole[row,col] = 9)
then Mina(Canvas, x, y); exit; end;
// открытая клетка
Canvas.Brush.Color:= clWhite; // открытые белые
Canvas.Rectangle(x-1,y-l,x+W,y+H);
if (Pole[row,col] = 100)
then exit; // клетка открыта, но она пустая
if (Pole[row,col] >= 101)
and (Pole[row,col] <= 108)
then begin // в соседних клетках есть мины
Canvas.Font.Size:= 14;
Canvas.Font.Color:= clBlue;
Canvas.TextOut(x+3,y+2,IntToStr(Pole[row,col] -100));
exit;
end;
if (Pole[row,col] >= 200)
then Flag(Canvas, x, y);
if (Pole[row,col] = 109)
then // на этой мине подорвались!
Begin
Canvas.Brush.Color:= clRed;
Canvas.Rectangle(x-1,y-1,x+W,y+H);
end;
if ((Pole[row,col] mod 10) = 9)
and (status = 2) then
Mina(Canvas, x, y);
end;
Игра
Во время игры программа воспринимает нажатия кнопок мыши и, в соответствии с правилами игры, открывает клетки или ставит в клетки флажки.
Основную работу выполняет процедура обработки события onMouseDown (ее текст приведен в листинге 15.6). Сначала процедура преобразует координаты точки, в которой игрок нажал кнопку мыши, в координаты клетки игрового поля. Затем делает необходимые изменения в массиве Pole и, если нажата правая кнопка, рисует в клетке флажок. Если нажата левая кнопка в клетке, в которой нет мины, то эта клетка открывается, на экран выводится ее содержимое. Если нажата левая кнопка в клетке, в которой есть мина, то вызывается процедура showPole, которая показывает все мины, в том числе и те, которые игрок не успел найти.
Листинг 15.6. Обработка события OnMouseDown на поверхности игрового поля
// нажатие кнопки мыши на игровом поле
procedure TForm1.Form1MouseDown(Sender: TObject;
Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
Var
row, col: integer;
Begin
if status = 2 // игра завершена
then exit;
if status = 0 then // первый щелчок
status:= 1;
// преобразуем координаты мыши в индексы
// клетки поля
row:= Trunc(y/H) + 1;
col:= Trunc(x/W) + 1;
if Button = mbLeft then
Begin
if Pole[row,col] = 9 then
begin // открыта клетка, в которой есть мина
Pole[row,col]:= Pole[row,col] + 100;
status:= 2; // игра закончена
ShowPole(Form1.Canvas, status);