C++ Builder - вопросы программирования

         

программа тестирования. Тест загружается из



Листинг 10.2.

Программа тестирования /* Универсальная . программа тестирования. Тест загружается из файла, имя которого должно быть указано в командной строке. Программа демонстрирует создание и настройку компонентов во время работы программы. */ # include <vcl.h>
#pragma hdrstop #include "tester_.h" #include <stdio.h // для доступа к функции sscanf #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1;  // форма int f; // дискриптор файла теста // имя файла теста берем из командной строки int level[4];// кол-во правильных ответов, необходимое // для достижения уровня AnsiString mes[4]; // сообщение о достижении уровня TVopros Vopros; // вопрос int otv; // номер выбранного ответа int right =0; // кол-во правильных ответов // функции, обеспечивающие чтение вопроса из файла теста int Getlnt(int f);
// читает целое int GetString(int f, AnsiString *st);
// читает строку

// конструктор __fastcall TForml::TForml(TComponent* Owner) : TFormfOwner) { int i; int left =10; // создадим радиокнопки для выбора // правильного ответа, но сделаем их невидимыми for (i =0; i < 4; i++) { // создадим радиокнопку RadioButton[i] = new TRadioButton(Forml);
// установим значения свойств RadioButton[i]->
Parent = Forml; RadioButton[i]->
Left = left; RadioButton[i]->
Width = Forml->
ClientWidth - left - 20; RadioButton[i]->
Visible = false; RadioButton[i]->
Checked = false; // зададим функцию обработки события Click RadioButton[i]->
OnClick = RadioButtonClick; } } void __fastcall TForml::FormActivate(TObject *Sender) { AnsiString st; // имя файла теста должно быть указано в командной строке int n = ParamCount();
if (  n < 1) { Labell->
Font->
Style = TFontStyles()« fsBold; Labell->
Caption = "В командной строке запуска надо задать имя файла теста"; Buttonl->
Tag = 2; return; } // открыть файл теста f = FileOpen(ParamStr(1), fmOpenRead);
if ( f == -1) { Labell->
Font->
Style = TFontStyles()« fsBold; Labell->
Caption = "Ошибка доступа к файлу теста " + ParamStr(l);
Buttonl-XTag = 2; return; } // вывести информацию о тесте GetString(f, Sst);
// прочитать название теста Forml->
Caption = st; GetString(f, sst);
// прочитать вводную информацию Labell->
Width = Forml->
ClientWidth - Labell->
Left -20; Labell->
Caption = st; Labell->
AutoSize = true; // прочитать информацию об уровнях оценки for (int i=0; i<4; i++) { level[i] = Getlnt(f);
GetString(f, &mes[i]);
} } // читает из файла очередной вопрос bool GetVopros(TVopros *v) { AnsiString st; int p;  // если р=1, то к вопросу есть иллюстрация if ( GetStringtf, &(v->
Vopr)) != 0) { // прочитать кол-во вариантов ответа,  номер правильного ответа // и признак наличия иллюстрации v->
nOtv = Getlnt(f);
v->
rOtv = Getlnt(f);
p = Getlnt(f);
if   (p)   // к вопросу есть иллюстрация GetString(f,&(v->
Img));
else v->
Img = ""; // читаем варианты ответа for(int i = 0;i < v->
nOtv; i++) { GetString(f,&(v->
0tv[i]));
} return true; } else return false; } // выводит вопрос void __fastcall TForml::ShowVopros(TVopros v) { int top; int i; // вопрос Labell->
Width = ClientWidth - Labell->
Left -20; Labell->
Caption = v.Vopr; Labell->
AutoSize = true; if   (v.Img != //к вопросу есть иллюстрация { /* определим высоту области, которую можно использовать для вывода иллюстрации */ int RegHeight = Buttonl->
Top - (Labell-ХГор + Labell->
Height +10) // область вывода вопроса - (RadioButton[l]->
Height + 10) * v.nOtv; Image1->
Top = Labell->
Top + Labell->
Height + 10; // загрузим картинку и определим ее размер Image1->
Visible = false; Image1->
AutoSize = true; Image1->
Picture->
LoadFromFile(v.Img);
if (Imagel->
Height >
RegHeight) // картинка не помещается ( Image1->
AutoSize = false; Imagel->
Height = RegHeight; Imagel->
Proportional = true; ) Image1->
Visible = true; // положение полей отсчитываем от иллюстрации top = Imagel-ХГор + Imagel->
Height + 10; ) else // положение полей отсчитываем от вопроса top = Labell-ХГор + Labell->
Height + 10; // варианты ответа for (i =0; i < v.nOtv; i++) { RadioButton[i]->
Top = top; RadioButton[i]->
Caption = v.0tv[i]; RadioButton[i]->
Visible = true; RadioButton[i]->
Checked = false; top += 20; } } // щелчок на радиокнопке выбора ответа void__fastcall TForml::RadioButtonClick(TObject *Sender) { int 1=0; while (  ! RadioButton[i]->
Checked) 1++; otv = 1+1; // ответ выбран, сделаем доступной кнопку Дальше Buttonl->
Enabled = true; } // удаляет вопрос с экрана
void __fastcall TForml::EraseVopros(void) { Imagel->
Visible = false; // скрыть поле вывода иллюстрации // скрыть поля выбора ответа for (int i=0; i <4; i++) ( RadioButton[i]->
Visible = false; RadioButton[i]->
Checked = false; } Button1->
Enabled = false; // сделать недоступной кнопку Дальше } // щелчок на кнопке ОК/Дальше/ОК void __fastcall TForml::ButtonlClick(TObject *Sender) { bool ok; // результат чтения из файла очередного вопроса switch (Buttonl-XTag) { case 0: // щелчок на кнопке ОК в начале работы программы // прочитать и вывести первый вопрос GetVopros(SVopros);
ShowVopros(Vopros);
Buttonl->
Caption = "Дальше"; Buttonl->
Enabled = false; Button1->
Tag = 1; break; case 1: // щелчок на кнопке Дальше if (otv == Vopros.rOtv) // выбран правильный ответ right++; EraseVopros(}; ok = GetVopros(SVopros);
if ( ok) ShowVopros(Vopros) ; else // вопросов больше нет { FileClose(f);
// вывести результат AnsiString st; // сообщение int i; // номер достигнутого уровня Form1->
Caption = "Результат тестирования"; st.printf("Правильных ответов: %i\n",right);
// определим оценку i = 0; // предположим, что испытуемый // ответил на все опросы while (( right < level[i]) && (i < 3)) i++; st = st + mes[i]; Labell->
Caption = st; Button1->
Caption = "OK"; Buttonl->
Enabled = true; Buttonl->
Tag =2; } break; case 2: // щелчок на кнопке OK в конце работы программы Form1->
Close(}; // завершить работу программы } } // Функция GetString читает строку из файла // значение функции — количество прочитанных символов int GetString(int f, AnsiString *st) { unsigned char buff300]; // строка (буфер) unsigned char *p = buf;  // указатель на строку int n; // кол-во прочитанных байт   (значение функции FileRead) int len =0;   // длина строки n = FileRead(f, p, 1);
while ( n != 0) { if ( *p == '\r') { n = FileRead(f, p, 1);
// прочитать '\n' break; } len++; P++; n = FileRead(f, p, 1);
} *p = '\0'; if ( len !=0) st->
printf("%s", buf);
return len; } // читает из файла целое число int Getlnt(int f) { char buf[20];  // строка (буфер) char *p = buf;  // указатель на строку int n;// кол-во прочитанных байт (значение функции FileRead) int a; // число, прочитанное из файла n = FileRead(f, p, 1);
while ( (*p >
= '0') (*p <= '9') && (n >
0)) { P++; n = FileRead(f, p, 1) ; } if ( *p == '\r') n = FileRead(f,   p,   1) // прочитать   '\n' *p =  '\0'; // изображение числа в буфере, преобразуем строку в целое sscanf(buf,"%i", &a);
return a; }
Как было сказано ранее, объявление массива компонентов не создает компоненты, а только устанавливает факт их существования. Создает и настраивает компоненты RadioButton конструктор формы (функция TForm1: : TForm1) . Непосредственное создание компонента (элемента массива) выполняет оператор RadioButton[i] = new TRadioButton(Forml)

Следующие за этим оператором инструкции обеспечивают настройку компонента. В том числе, они путем присваивания значения свойству Onclick задают функцию обработки события click . В рассматриваемой программе для обработки события click на всех компонентах RadioButton используется одна и та же функция, которая путем опроса значения свойства checked фиксирует номер выбранного ответа и делает доступной кнопку Дальше (Button1) .

После запуска программы и вывода на экран стартовой формы происходит событие onActivate . Функция обработки этого события проверяет, указан ли в командной строке параметр — имя файла теста. Реализация программы предполагает, что если имя файла теста задано без указания пути доступа к нему, т файл теста и файлы с иллюстрациями находятся в том же каталоге, что и программа тестирования. Если же путь доступа указан, то файлы с иллюстрациями должны находиться в том же каталоге, что и файл теста. Такой подход позволяет сгруппировать все файлы одного теста в одном каталоге.



Если файл теста задан, функция открывает его, читает название теста и вводную информацию и затем выводит их в диалоговое окно, причем название выводится в заголовок, а вводная информация — в поле Label1 .

Непосредственное чтение строк из файла выполняет функция Getstring . Значением функции является длина строки. Следует обратить внимание на то, что функция GetString Возвращает строку Ans iString.

После того как прочитана общая информация о тесте, программа считывает из файла теста информацию об уровнях оценки и фиксирует ее в массивах level и mes . Критерий достижения уровня (количество правильных ответов) считывает функция Getint .

После вывода информационного сообщения программа ждет, пока пользователь не нажмет кнопку Ok (Button№ ).

Командная кнопка Button№ используется:
  •  для завершения работы программы, если в командной строке не указан файл теста;
  •  для активизации процесса тестирования (после вывода информационного сообщения);
  •  для перехода к следующему вопросу (после выбора варианта ответа);
  •  для завершения работы программы (после вывода результата тестирования).


Таким образом, реакция программы на нажатие кнопки Buttonl зависит от состояния программы. Состояние программы фиксирует свойство Tag кнопки Button 1 .

После вывода информации о тесте значение свойства Tag кнопки Button1 равно нулю. Поэтому в результате щелчка на кнопке Button 1 выполняется та часть программы, которая обеспечивает вывод первого вопроса и замену находящегося на кнопке текста ОК на текст Дальше, и заменяет значение свойства Tag на единицу.

В процессе тестирования значение свойства Tag кнопки Button 1 равно единице. Поэтому функция обработки события click сравнивает номер выбранного ответа (увеличенный на единицу номер компонента RadioButton ) с номером правильного ответа, увеличивает на единицу счетчик правильных ответов (в том случае, если выбран правильный ответ) и активизирует процесс чтения следующего вопроса. Если попытка чтения очередного вопроса завершилась неудачно (это значит, что вопросы исчерпаны), функция выводит результаты тестирования, заменяет текст на командной кнопке на ОК и подготавливает операцию завершения работы программы (свойству Tag присваивает значение 2).

Чтение вопроса (вопрос, информация о количестве альтернативных ответов, номер правильного ответа и признак наличия иллюстрации, а также имя файла иллюстрации и альтернативные ответы) из файла теста выполняет функция GetVopros .

Вывод вопроса, иллюстрации и альтернативных ответов выполняет функция showVopros . Сначала функция выводит вопрос — присваивает значение свойству caption компонента Labe l1 . Затем, если к вопросу есть иллюстрация, функция вычисляет размер области, которую можно выделить для отображения иллюстрации, и загружает иллюстрацию. Если размер иллюстрации больше размера области, функция устанавливает максимально возможный размер компонента imagei и присваивает значение false свойству AutoSize и true — свойству Proportional , обеспечивая тем самым масштабирование иллюстрации. После этого функция выводит альтернативные ответы. Положение компонентов, обеспечивающих вывод альтернативных ответов, отсчитывается от нижней границы компонента image 1 , если к вопросу есть иллюстрация, или компонента Label1 , если иллюстрации нет.

Сразу после вывода вопроса кнопка Дальше (Button1) недоступна. Сделано это для того, чтобы блокировать возможность перехода к следующему вопросу, если не дан ответ на текущий. Доступной кнопку Дальше делает функция обработки события Click на одном из компонентов RadioButton .

Кроме того, функция путем сканирования (проверки значения свойства checked компонентов массива RadioButton ) определяет, на каком из компонентов массива испытуемый сделал щелчок и, следовательно, какой из вариантов ответа выбран. Окончательная фиксация номера выбранного ответа и сравнение его с номером правильного ответа происходит в результате нажатия кнопки Дальше.


Листинг 10.3.

Функция NewGame
// новая игра — генерирует новое поле void __fastcall NewGame() { // Очистим элементы массива, соответствующие отображаемым // клеткам, а в неотображаемые, по границе игрового поля, // запишем число -3. Уникальное значение клеток границы // используется функцией Open для завершения рекурсивного // процесса открытия соседних пустых клеток. int row,col; for (row=0; row <= MR+1; row++) for (col=0; col <= MC+1; col++) Pole[row][col] = -3; for (row=l; row <= MR; row++) for (col=l; col <= MC; col++) Pole[row][col] = 0; // расставим мины time_t t;   // используется генератором случайных чисел (ГСЧ) srand((unsigned) time(&t));
// инициализация ГСЧ int n = 0; // количество мин do { row = rand() % MR +1; col = randO % MC +1; if ( Pole [row] [col] ,!= 9) { Pole[row][col] = 9; n++; } } while ( n < 10);
// вычисление количества мин в соседних клетках int k; for ( row = 1; row <= MR; row++) for ( col = 1; col <= MC; col++) if ( Pole[row][col] != 9) { k =0; if ( Pole[row-1][col-1] == 9) k++; if ( Pole[row-1][col] == 9) k++; if ( Pole[row-1][col+1] == 9) k++; if ( Pole[row][col-1] = 9) k++; if ( Pole[row][col+1] == 9) k++; if ( Pole[row+1][col-1] = 9) k++; if ( Pole[row+1][col] == 9) k++; if ( Pole[row+1][col+1] == 9) k++; Pole[row][col] = k; } status =0; // начало игры nMin =0; // нет обнаруженных мин nFlag =0; // нет флагов }
После того как функция NewGame расставит мины, функция showpole (ее текст приведен в листинге 10.4) выводит изображение игрового поля.



Листинг 10.4.

Функция ShowPole // показывает поле void __fastcall TForml::ShowPole( int status) { for ( int row =1; row <= MR; row++) for ( int col = 1; col <= MC; col++) Kletka(row, col, status);
}
Функция ShowPole выводит изображение поля последовательно, клетка за клеткой. Вывод изображения отдельной клетки выполняет функция Kletka , ее текст приведен в листинге 10.5. Функция Kletka используется для вывода изображения поля в начале игры, во время игры и в ее конце. В начале игры (значение параметра status равно нулю) функция выводит только контур клетки, во время игры — количество мин в соседних клетках или флажок, а в конце она отображает исходное состояние клетки и действия пользователя. Информацию о фазе игры функция Kletka получает через параметр status .



Листинг 10.5.

Функция KLetka // рисует на экране клетку void __fastcall TForml::Kletka(int row, int col, int status) { int x = LEFT + (col-1)* W; int у = TOP + (row-1)* H; if (status == 0) // начало игры ( // клетка — серый квадрат Canvas->
Brush->
Color = clLtGray; Canvas->
Rectangle (х-1, у-1, x+W, у+Н) ; return; } // во время   (status = 1)- и в конце   (status = 2)  игры if   (   Pole[row][col]   <  100) { // клетка не открыта Canvas->
Brush->
Color = clLtGray;   // не открытые — серые Canvas->
Rectangle(х-1,у-1,x+W,у+Н);
if (status == 2 && Pole.frow] [col] == 9) Mina( x, у);
// игра закончена, показать мину return; } // клетка открыта Canvas->
Brush->
Color = clWhite;     // открытые белые Canvas->
Rectangle(x-l,y-l,x+W,y+H);
if  ( Pole[row][col] == 100) return; // клетка пустая if ( Pole[row][col] >
= 101 && Pole[row][col] <= 108) { Canvas->
Font->
Size = 14; Canvas->
Font->
Color = clBlue; Canvas->
TextOutA(x+3,y+2,IntToStr(Pole[row][col] -100));
return; } if ( Pole[row][col] >
= 200) Flag(x, y) ; if (Pole[row][col] == 109) // на этой мине подорвались! { Canvas->
Brush->
Color = clRed; Canvas->
Rectangle(x,y,x+W,y+H);
} if  ((  Pole[row][col]% 10 = 9)  && (status = 2)) Mina( x,   y);
}
 




Листинг 10.6.

Обработка события OnMouseDown на поверхности игрового поля ;
// нажатие кнопки мыши на игровом поле void _fastcall TForml::ForraMouseDown (TObject*Sender,TMouseButton Button, TShiftState Shift, int x, int y) -{ if ( status == 2) return; if ( status == 0) status = 1; x -= LEFT; у -== TOP; if (x >
0 && у >
0) { // преобразуем координаты мыши // в индексы клетки поля int row = y/H + 1; int col = x/W + 1; if (Button == mbLeft) { if ( Pole[row][col] == 9) { Pole[row][col] +=100; status -2;   // игра закончена ShowPole(status);
} else if  ( Pole[row][col] < 9) { Open(row,col);
ShowPole(status);
} } else if (Button == mbRight) { nFlag++; if ( Pole[row][col] == 9) nMin++; Pole[row][col] += 200;  // поставили флаг if (nMin == MM && nFlag = NM) { status =2;  // игра закончена ShowPole(status);
} else Kletka(row, col, status);
} } }
Функция Flag (листинг 10.7) рисует флажок. Флажок (Рисунок 10.13) состоит из четырех примитивов: линии (древко), замкнутого контура (флаг) и ломаной линии (буква "М"). Функция Flag рисует флажок, используя метод базовой точки, т. е. координаты всех точек, определяющих положение элементов рисунка, отсчитываются от базовой точки.

Функция Mina (листинг 10.8) рисует мину. Мина (Рисунок 10.14) состоит из восьми примитивов: два прямоугольника и сектор образуют корпус мины, остальные элементы рисунка — линии ("усы" и полоски на корпусе).

Обеим функциям в качестве параметров передаются координаты базовой точки рисунка и указатель на объект, на поверхности которого надо рисовать.



Листинг 10.7.

Функция Flag рисует флажок
// рисует флаг void _fastcall TForml::Flag( int x,   int y) { TPoint p[4]; // координаты флажка и нижней точки древка // точки флажка р[0].х=х+4;  р[0].у=у+4; р[1].х=х+30; р[1].у=у+12;  р[2].х=х+4;  р[2].у=у+20; // установим цвет кисти и карандаша Canvas->
Brush->
Color = clRed; Canvas->
Pen->
Color = clRed; // чтобы контур флажка был красный Canvas->
Polygon(р, 2);
  // флажок // древко Canvas->
Pen->
Color = clBlack; Canvas->
MoveTo(p[0].x, p[0].у);
Canvas->
LineTo(x+4,y+36);
TPoint m[5];         // буква М m[0].x=x+8; m[0].y=y+14; m[l].x=x+8; m[l].y=y+8; m[2].x=x+10; m[2].y=y+10; m[3].x=x+12;  m[3].y=y+8; m[4].x=x+12;  m[4].y=y+14; Canvas->
Pen->
Color = clWhite; Canvas->
Polyline(m,4}; Canvas->
Pen->
Color = clBlack; }



Листинг 10.8.

Функция Mina рисует мину // рисует мину void __faatcall TForml::Mina(int x,   int y) { Canvas->
Brush->
Color = clGreen; Canvas->
Pen->
Color = clBlack; // корпус Canvas->
Rectangle(x+16,y+26,x+24,y+30);
Canvas->
Rectangle(x+8,y+30,x+32,y+34) ; Canvas->
Pie(x+б,y+28,x+34,y+44,x+34,y+36,x+6,y+36);
// полоса на корпусе Canvas->
MoveTo(x+12,y+32);
Canvas->
LineTo(x+28,y+32);
// основание Canvas->
MoveTo(x+8,y+36);
  Canvas->
LineTo(x+32,y+36);
// вертикальный "ус" Canvas->
MoveTo(x+20,y+22);
Canvas->
LineTo(x+20,y+26);
// боковые "усы" Canvas->
MoveTo(x+8, y+30);
Canvas->
LineTo(x+6,y+28);
Canvas->
MoveTo(х+32,y+30);
Canvas->
LineTo(х+34, у+28);
}
 




Листинг 10.9.

Вывод окна О пограмме
void fastcall TForml::N4Click(TObject *Sender) { // "привяжем" окно О программе к главному окну приложения AboutFom-ХГор=Forml->
Top+ Forml->
Height/2-AboutForm->
Height/2; AboutForm->
Left=Forml->
Left+ Forml->
Width/2-AboutForm->
Width/2; AboutForm->
ShowModal(}; }
Если не предпринимать никаких усилий, то окно О программе появится в той точке экрана, в которой находилась форма во время ее разработки. Вместе с тем, можно "привязать" это окно к главному окну программы так, чтобы оно появлялось в центре главного окна. Привязка осуществляется на основании информации о текущем положении главного окна программы (свойства тор и Left ) и о размере окна О программе.

На поверхности формы О программе есть ссылка на сайт издательства . Предполагается, что в результате щелчка на ссылке в окне браузера будет открыта указанная страница. Для того чтобы это произошло, надо создать функцию обработки события onclick для компонента Labels . Значения свойств компонента Labels приведены в табл. 10.6, а текст функции обработки события — в листинге 10.10.



Листинг 10.10.

Щелчок в поле URL
void _fastcall TAboutForm::Label5Click(TObject *Sender) { /* наиболее просто передать в функцию ShellExecute строку-константу  (URL-адрес)   так,  как показано ниже: ShellExecute(AboutForm->
Handle, "open", "http:\\\\www.bhv.ru", NULL, NULL) Лучше URL- адрес брать из поля метки. В функцию ShellExecute надо передать указатель на null terminated-строку, но свойство Caption — это AnsiString. Преобразование Ansi-строки в char* выполняет метод c_str() */ // открыть файл, имя которого находится в поле Labels ShellExecute(AboutForm->
Handle,"open",Label5->
Caption.c_str(), NULL,NULL,SW_RESTORE);
}
Окно О программе закрывается в результате щелчка на кнопке ОК . Функция обработки этого события приведена ниже.
void  fastcall TAboutForm::ButtonlClick(TObject *Sender) { ModalResult = mrOk; // убрать окно О программе }
 




Листинг 10.11.

Заголовочный файл главной формы (saper_.h)
#ifndef saper_H fdefine saper_H #include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>


#include <Forms.hpp>
#include <ExtCtrls.hpp>
 # include <Menus.hpp>
class TForml : public TForm { published: TMainMenu *MainMenul; // команды главного меню TMenuItem *N1; // Новая игра TMenuItem *N2; // команда "?" // команды меню "?" TMenuItem *N3; // Справка TMenuItem *N4; // О программе void __fasteall FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y);
void __fastcall FormPaint(TObject *Sender);
void _fastcall FormCreate(TObject *Sender);
// выбор,команды в меню void  fastcall NIClick(TObject  *Sender);
   // Новая игра void __fastcall N3Click(TObject  *Sender);
   // Справка void __fastoall N4Click(TObject  *Sender);
   // О программе private: void  fastcall ShowPole(int status);
// отображает поле // отображает содержимое клетки void __fastcall Kletka (int row, int col, int status);
void __fastcall Mina (int x, int y);
  // рисует мину void __fastcall Flag( int x, int y);
  // рисует флаг public: __fastcall TForml(TComponent* Owner);
}; extern PACKAGE TForml *Forml; tendif



Листинг 10.12.

Модуль главной формы (saperXcpp), /* Игра "Сапер".  Демонстрирует работу с графикой, использование рекурсии, доступ к файлу справочной информации. */ #include <vcl.h>
#include <stdlib.h>
// для доступа к ГСЧ #include <time.h>
#linclude <stdio.h>
#pragma hdrstop #include "saper_.h" #include "saper_2.cpp" #pragma package(smart_init) #pragma resource "*.dfm" TForml *Forml; // главное окно fastcall TForml::TForml(TComponent* Owner) : TForm(Owner) { } fdefine MR 10 // кол-во клеток- по вертикали fdefine MC 10 // кол-во клеток по горизонтали idefine MM 10 // кол-во мин int Pole[MR+2][МС+2]; // минное поле // 0..8 — количество мин в соседних клетках // 9 — в клетке мина // 100..109 — клетка открыта // 200..209 — в клетку поставлен флаг int nMin;  // кол-во, найденных мин int nFlag;  // кол-во поставленных флагов int status =0; //0 — начало игры; 1 — игра; 2 — результат // смещение игрового поля относительно левого верхнего угла // поверхности формы tdefine LEFT 0  // по X Idefine ТОР 1  // по Y ttdefine W   40 // ширина клетки поля #define H   40 // высота клетки поля void __fastcall NewGameO; // новая игра — "разбрасывает" мины void __fastcall Open(int row, int col);
/* открывает текущую и соседние пустые клетки */ // нажатие кнопки мыши на игровом поле void __fastcall TForml: :FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int x, int y) { if ( status == 2) return; if ( status == 0) status = 1; x -= LEFT; у -= TOP; if (x >
0 && у >
0) { // преобразуем координаты мыши // в индексы клетки поля int row = y/H + 1; int col = x/W + 1; if (Button = mbLeft) { if ( Pole[row][col] == 9) { Pole[row][col] +=100; status =2;  // игра закончена ShowPole(status);
} else if  ( Pole[row][col] < 9) { Open(row,col);
ShowPole(status);
} } else if (Button == mbRight) { nFlag++; if ( Pole[row][col] = 9) nMin++; Pole[row][col] += 200;  // поставили флаг if (nMin == NM && nFlag == MM) { status = 2;  // игра закончена ShowPole(status);
} else Kletka(row, col, status);
} } } // Функция обработки события OnCreate обычно используется // для инициализации глобальных переменных void__fastcall TForml::FormCreate(TObject *Sender) { // В неотображаемые эл-ты массива, которые соответствуют // клеткам по границе игрового поля, запишем число -3. // Это значение используется функцией Open для завершения // рекурсивного процесса открытия соседних пустых клеток. for ( int row=0; row <= MR+1; row++) for ( int col=0; col <= MC+1; col++) Pole[row][col] = -3; NewGameO; // "разбросать" мины Forml->
ClientWidth = W*MC; Forml->
ClientHeight = H*MR+TOP+1; } // Вывод поля как результат обработки события Paint // позволяет проводить любые манипуляции с формой // во время работы программы void  fastcall TForml::FormPaint(TObject *Sender) { ShowPoletstatus);
} // Показывает поле void  fastcall TForml::ShowPole( int status) { for ( int row = 1; row <= MR; row++) for ( int col = 1; col <= MC; col++) Kletka(row, col, status);
) // рисует на экране клетку void  fastcall TForml::Kletka(int row, int col, int status) { int x = LEFT + (col-D* W; int у = TOP + (row-1)* H; if (status ==0) // начало игры { // клетка — серый квадрат Canvas->
Brush->
Color = clLtGray; Canvas->
Rectangle(x-l,y-l,x+W,y+H);
return; } // во время (status = 1) и в конце (status = 2) игр if ( Pole[row][col] < 100) { // клетка не открыта Canvas->
Brush->
Color = clLtGray;  // не открытые — серые Canvas->
Rectangle(x-l,y-l,x+W,y+H);
if (status == 2 SS Pole[row][col] = 9} Mina( x, y);
// игра закончена, показать мину return; } // клетка открыта Canvas->
Brush->
Color = clWhite;     // открытые - белые Canvas->
Rectangle(x-1,y-1,x+W,у+Н);
if  ( Pole[row][col] == 100) // клетка открыта, но она пустая return; . if ( Pole[row][col] >
= 101 && Pole[row][col] <= 108) { Canvas->
Font->
Size = 14; Canvas->
Font->
Color = clBlue; Canvas->
TextOutA(x+3,y+2,IntToStr(Pole[row][col] -100));
return; } if ( Pole[row][col] >
= 200) Flag(x, y) ; if (Pole[row][col] == 109) // на этой мине подорвались! { Canvas->
Brush->
Color = clRed; Canvas->
Rectangle(x,y,x+W,y+H);
} if (( Pole[row][col] % 10 == 9)  &&  (status == 2)) Mina( x, y);
} // рекурсивная функция открывает текущую и все соседние // клетки, в которых нет мин void  fastcall Open(int row, int col) { if (Pole[row][col] = 0) { Pole[row][col] = 100; // открываем клетки слева, справа, снизу и сверху Open(row,col-l);
Open(row-l,col);
Open(row,col+1);
Open(row+1,col);
// открываем примыкающие диагонально Open(row-l,col-l);
Open(row-l,col+l) ; Open(row+1,col-1);
Open(row+1,col+1);
} else // -3 — это граница игрового поля if (Pole[row][col] < 100 && Pole[row][col] != -3} Pole[row][col] += 100; } // новая игра — генерирует новое поле void __£astcall NewGame() { // Очистим эл-ты массива, соответствующие отображаемым // клеткам, а в неотображаемые (по границе игрового поля) // запишем число -3. Уникальное значение клеток границы // используется функцией Open для завершения рекурсивного // процесса открытия соседних пустых клеток. int row,col; for (row=0; row <= MR+1; row++) for (col=0; col <= MC+1; col++) Pole[row][col] = -3; for (row=l; row <= MR; row++) for (col=l; col <= MC; col++) Pole[row][col] = 0; // расставим мины time_t t;   // используется ГСЧ srand((unsigned) time(&t));
// инициализация ГСЧ int n = 0; // кол-во мин do { row = randO % MR +1; col = rand() % MC +1; if ( Pole[row][col] != 9) { Pole[row][col] = 9; n++; } } while ( n < 10);
// вычисление кол-ва мин в соседних клетках int k; for ( row = 1; row <= MR; row++) for ( col = 1; col <= MC; col++) if ( Pole[row][col] != 9) { k =0; if ( Pole[row-l][col-1] == 9} k++; if ( Pole[row-1][col]  == 9) k++; if ( Pole[row-1][col+1] == 9) k++; if ( Pole[row][col-1]  == 9) k++; if ( Pole[row][col+1]   == 9) k++; if ( Pole[row+1][col-1] == 9) k++; if ( Pole[row+1][col]  == 9) k++; if ( Pole[row+1][col+1] == 9) k++; Pole[row][col] = k; } status =0; // начало игры nMin =0; // нет обнаруженных мин nFlag =0; // нет флагов } // рисует мину void __fastcall TForml::Mina(int x, int y) { Canvas->
Brush->
Color = clGreen; Canvas->
Pen->
Color = clBlack; Canvas->
Rectangle(x+16,y+26,x+24,y+30) ; // корпус Canvas->
Rectangle(x+8,y+30,x+32,y+34) ; Canvas->
Pie(x+6,y+28,x+34,y+44,x+34,y+36,x+6,y+36);
// полоса на корпусе Canvas->
MoveTo(x+12,y+32);
Canvas->
LineTo(x+28,y+32);
// основание Canvas->
MoveTo(x+8,y+36);
  Canvas->
LineTo(x+32,y+36);
// вертикальный "ус" Canvas->
MoveTo(x+20,y+22);
Canvas->
LineTo(x+20,y+26);
// боковые "усы" Canvas->
MoveTo(x+8, y+30);
Canvas-XLineTo(x+6,y+28);
Canvas->
MoveTo(x+32,y+30);
Canvas->
LineTo(x+34,y+28);
} // рисует флаг void __fastoall TForml::Flag( int x, int y) { TPoint p[4]; // координаты флажка и нижней точки древка // точки флажка р[0].х=х+4;  р[0].у=у+4; р[1].х=х+30;  р[1].у=у+12; р[2].х=х+4;  р[2].у=у+20; // установим цвет кисти и карандаша Canvas->
Brush->
Color = clRed; Canvas->
Pen->
Color = clRed; // чтобы контур флажка бил красный Canvas->
Polygon(p, 2);
  // флажок // древко Canvas->
Pen->
Color = clBlack; Canvas->
MoveTo(p[0].x, p[0] .у);
Canvas->
LineTo(x+4,y+36) ; TPoint m[5];    // буква М m[0].x=x+8; m[0].y=y+14; m[l].x=x+8; m[l].y=y+8; m[2].x=x+10; m[2].y=y+10; m[3].x=x+12; m[3].y=y+8; m[4].x=x+12; m[4].y=y+14; Canvas->
Pen->
Color = clWhite; Canvas->
Polyline(m,4);
Canvas->
Pen->
Color = clBlack; } // команда главного меню Новая игра
void__fastcall TForml::NlCllck(TObject *Sender) { NewGame();
ShowPole(status);
} // выбор в меню "?" команды О программе void__fastcall TForml::N4Click(TObject *Sender) { AboutForm-ХГор = Forml->
Top + Forml->
Height/2 - AboutForm->
Height/2; AboutForm->
Left = Forml->
Left + Forml->
Width/2 - AboutForm->
Width/2; AboutForm->
ShowModal();
} // выбор в меню "?" команды Справка void __fastcall TForml::N3Click(TObject *Sender) { WinHelp(Forml->
Handle,"saper.hip",HELP_CONTEXT,1);
}



Листинг 10.13.

Модуль формы О программе (saper2_.cpp) #include <vcl.h>
#pragma hdrstop #include "saper_2.h" #pragma package(smart_init) #pragma resource "*.dfm" TAboutForm *AboutForm; __fastcall TAboutForm::TAboutForm(TComponent* Owner) : TForm(Owner) { } // Выбор URL-адреса void __fastcall TAboutForm: :Label5Click(TObject *Sender) { /*  В функцию ShellExvte надо передать указатель на null terminated строку (char*). Свойство Caption — это AnsiString. Преобразование Ansi-строки в указатель на nt-строку выполняет метод c_str() */ //, открыть файл, имя которого находится в поле Labels ShellExecute (AboutForm->
Handle,"open",Label5->
Caption.c_str(), NULL,NULL,SW_RESTORE);
} // щелчок на кнопке QK void __fastcall TAboutForm::ButtonlClick(TObject *Sender) { ModalResult = mrOk; }
 




Листинг 10.14

. Очистка диска
#include <vcl.h>
#pragma hdrstop #include "clear_.h" #include <FileCtrl.hpp>
// для доступа к SelectDirectory #pragma package(smart_init) #pragma resource "*.dfm" TForml *Forml; __fastcall TForml::TForml(TComponent* Owner) : TForm(Owner) { } AnsiString Directory; // каталог, в котором находятся проекты C++ Builder AnsiString cDir;     // текущий каталог AnsiString FileExt;  // расширение файла int n = 0;        // количество удаленных файлов // щелчок на кнопке Каталог void__fastcall TForml::ButtonlClick(TObject *Sender) { AnsiString dir;  // каталог, который выбрал пользователь if ( SelectDirectory("Выберите каталог","", dir)) { // диалог Выбор файла завершен щелчком на кнопке ОК Editl-XText = dir; Button2->
Enabled = true; // теперь кнопка Выполнить доступна }; } // удаляет ненужные файлы из текущего каталога и его подкаталогов void __fastcall Clear(void) { TSearchRec SearchRec; // информация о файле или каталоге cDir = GetCurrentDir()+"\\"; if ( FindFirst("*.*", faArchive,SearchRec) ==0) do { // проверим расширение файла int p = SearchRec.Name.Pos(".");
FileExt = SearchRec.Name.Substring(p+1,MAX_PATH);
if ( ( FileExt[1] == '-') II ( FileExt == "obj") || ( FileExt = "tds")) { Forml->
Memol->
Lines->
Add(cDir+SearchRec.Name);
DeleteFile(SearchRec.Name);
П++; } } while ( FindNext(SearchRec) == 0);
// обработка подкаталогов текущего каталога if ( FindFirst("*", faDirectory, SearchRec) == 0) do if ((SearchRec.Attr & faDirectory) = SearchRec.Attr) { // каталоги ".." и "." тоже каталоги, // но в них входить не надо !!! if (( SearchRec.Name !=".") && (SearchRec.Name != "..")) { ChDir(SearchRec.Name);
// войти в подкаталог Clear();
      // очистить каталог ChDir("..");
  / выйти из каталога }; } while ( FindNext(SearchRec) == 0);
} // щелчок на кнопке Выполнить void__fastcall TForml::Button2Click(TObject *Sender) { Memol->
Clear();
// очистить поле Memol Directory = Edit1-XText;// каталог, который выбрал пользователь ChDir(Directory);
  // войти в каталог Clear();
     // очистить текущий каталог и его подкаталоги Memol->
Lines->
Add("");
if (n) Memol->
Lines->
Add("Удалено файлов: " + IntToStr(n)}; else Memol->
Lines->
Add( "В указанном каталоге нет файлов, которые надо удалить.");
}
Основную работу (удаление файлов) выполняет рекурсивная функция clear (рекурсивной называют функцию, которая в процессе работы вызывает сама себя). Решение реализовать функцию clear как рекурсивную не случайно: функция обрабатывает каталоги компьютера, которые являются рекурсивными объектами. Рекурсивным называют объект, частично состоящий из объектов этого же типа.

Алгоритм функции clear приведен на Рисунок 10.23.

Содержание раздела