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

         

Алгоритм функции Clear



Рисунок 10.23. Алгоритм функции Clear



Сначала функция clear обрабатывает текущий каталог: просматривает все файлы и удаляет те, которые надо удалить. Просмотр файлов обеспечивают функции FindFirst и FindNext . Функция FindFirst просматривает каталог, указанный при ее вызове, и записывает в структуру searchRec имя первого из найденных файлов, имя которого соответствует маске. В данной программе маска *.* , т. е. функция выбирает первый по порядку файл. Если файл найден, то выполняется проверка его расширения. Если расширение файла obj, tds или начинается со значка ~, то имя файла добавляется в поле Memo1 , а сам файл удаляется с диска. Удаляет файл функция DeleteFiie . После обработки первого файла для поиска следующего вызывается функция FindNext . После того как все файлы текущего каталога будут обработаны, функция clear проверяет, есть ли в текущем каталоге подкаталоги. Проверку выполняет функция FindFirst , которой в качестве параметра передается константа faDirectory , информирующая функцию о том, что надо искать имена каталогов, а не файлов. Если в текущем каталоге нет подкаталогов, то функция clear завершает работу. Если в текущем каталоге есть подкаталоги, то выполняется вход в подкаталог (делает это функция choir ) и вызов функции clear (для обработки подкаталога функция вызывает саму себя). Если в текущем каталоге нет необработанных каталогов, то она завершает работу и возвращает управление функции clear , которая ее вызвала и которая после этого продолжает обработку "своих" подкаталогов.

Вывод окна Обзор папок выполняет функция selectDirectory , которую вызывает функция обработки события click на кнопке Каталог. Для доступа к этой функции в текст программы надо включить директиву #include <FileCtrl.hpp>.

 



Диалоговое окно программы тестирования



Рисунок 10.1. Диалоговое окно программы тестирования




 











Доступ к файлу теста



Доступ к файлу теста



Передать имя файла теста программе тестирования можно через параметр командной строки.

При запуске программы из операционной системы при помощи команды Пуск | Выполнить параметры командной строки указывают после имени выполняемого файла программы (Рисунок 10.4).



Файл теста



Файл теста



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

Файл теста состоит из трех разделов:

 раздел заголовка;  раздел оценок;  раздел вопросов.


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

Вот пример заголовка:

История Санкт-Петербурга

Сейчас Вам будут предложены вопросы о знаменитых памятниках и архитектурных сооружениях Санкт-Петербурга. Вы должны из предложенных нескольких вариантов ответа выбрать правильный.

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

100

Отлично

85

Хорошо

60

Удовлетворительно

50

Плохо

За разделом оценок следует раздел вопросов теста.

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

Вот пример вопроса:

Архитектор Зимнего дворца

3 2 1

herm.bmp

Бартоломео

Карл Росси

Огюст Монферран

В приведенном примере к вопросу даны три варианта ответа, правильным является второй ответ (архитектор Зимнего дворца — Карл Росси). К вопросу есть иллюстрация (третье число во второй строке — единица), которая находится в файле herm.bmp.

Ниже в качестве примера приведен текст файла вопросов для контроля знания истории памятников и архитектурных сооружений Санкт-Петербурга.

История Санкт-Петербурга

Сейчас Вам будут предложены вопросы о знаменитых памятниках и архитектурных

сооружениях Санкт-Петербурга. Вы должны из предложенных нескольких вариантов ответа

выбрать правильный.

7

Вы прекрасно знаете историю Санкт-Петербурга!

6

Вы много знаете о Санкт-Петербурге, но на некоторые вопросы ответили неверно.

5

Вы недостаточно хорошо знаете историю Санкт-Петербурга?

4

Вы, вероятно, только начали знакомиться с историей Санкт-Петербурга?

Архитектор Исаакиевского собора:

3 2 1

isaak.bmp

Доменико Трезини

Опост Монферран

Карл Росси

Александровская колонна воздвигнута в 1836 году по проекту Опоста Монсреррана

как памятник, посвященный:

2 1 0

деяниям императора Александра I

подвигу русского народа в Отечественной войне 1812 года

Архитектор Зимнего дворца

3 2 1

herm.bmp

Бартоломео Растрелли

Карл Росси

Опост Монферран

Михайловский (Инженерный) замок — жемчужина архитектуры Петербурга — построен по проекту:

3 1 0

Андрея Никифоровича Воронихина

Ивана Егоровича Старова

Василия Ивановича Баженова

Остров, на котором находится Ботанический сад, основанный императором Петром I, называется:

3 3 1

bot.bmp

Заячий

Медицинский

Аптекарский

Невский проспект получил свое название:

3 2 0

по имени реки, на которой стоит Санкт-Петербург

по имени близко расположенного монастыря, Александро-Невской лавры

в память о знаменитом полководце — Александре Невском

Скульптура знаменитого памятника Петру I выполнена:

2 1 0

Фальконе

Клодтом

Файл теста может быть подготовлен в текстовом редакторе Notepad или в Microsoft Word: В случае использования Microsoft Word при сохранении текста следует указать, что надо сохранить только текст. Для этого в диалоговом окне Сохранить в списке Тип файла следует выбрать Только текст (*.txt).

 



Флажок



Рисунок 10.13. Флажок












Форма О программе



Рисунок 10.19. Форма О программе



Вывод окна О программе выполняет функция обработки события click , которое происходит в результате выбора из меню ? команды О программе (листинг 10.9). Непосредственно вывод окна выполняет метод showModal , который выводит окно как модальный диалог. Модальный диалог перехватывает все события, адресованные другим окнам приложения, в том числе и главному. Таким образом, пока модальный диалог находится на экране, продолжить работу с приложением, которое вывело модальный диалог, нельзя.



Форма приложения



Форма приложения



На Рисунок 10.2 приведен вид формы программы тестирования.

Поле Label1 предназначено для вывода начальной информации, вопроса и результатов тестирования. Компонет image1 предназначен для вывода иллюстрации, сопровождающей вопрос. Кнопка Button1 используется для подтверждения выбора ответа и перехода к следующему вопросу.



Форма приложения



Форма приложения



Главная (стартовая) форма игры "Сапер" приведена на Рисунок 10.10.



Форма программы "Очистка диска"



Рисунок 10.22. Форма программы "Очистка диска"





Форма программы тестирования



Рисунок 10.2. Форма программы тестирования



Нетрудно заметить, что в форме нет радиокнопок — компонентов RadioButton , обеспечивающих вывод альтернативных ответов и прием ответа испытуемого. В рассматриваемой программе компоненты RadioButton будут созданы динамически, во время работы программы.

В табл. 10.1 и 10.2 приведены значения свойств формы и компонента Label1 . Значения остальных свойств этих и других компонентов можно оставить без изменений.



Главная форма программы "Сапер"



Рисунок 10.10. Главная форма программы "Сапер"



Следует обратить внимание на то, что размер формы не соответствует размеру игрового поля. Нужный размер формы будет установлен во время работы программы. Делает это функция обработки события onFormActivate , которая на основе информации о размере игрового поля (количестве клеток по вертикали и горизонтали) и размере клеток устанавливает значения свойств clientHeight и clientwidth , определяющие размер клиентской области главного окна программы.

Главное окно программы содержит компонент MainMenu 1 , который представляет собой главное меню программы. Значок компонента MainMenu находится на вкладке Standard (Рисунок 10.11).



Игра



Игра



Во время игры программа воспринимает нажатия кнопок мыши и, в соответствии с правилами игры, открывает клетки или ставит в клетки флажки.

Основную работу выполняет функция обработки события OnMouseDown (ее текст приведен в листинге 10.6). Функция получает координаты точки формы, в которой игрок щелкнул кнопкой мыши, а также информацию о том, какая кнопка была нажата. Сначала функция преобразует координаты точки, в которой игрок нажал кнопку мыши, в координаты клетки игрового поля. Затем она вносит необходимые изменения в массив Pole и, если нажата правая кнопка, вызывает функцию Flag , которая рисует в клетке флажок. Если нажата левая кнопка в клетке, в которой нет мины, то эта клетка открывается, и на экране появляется ее содержимое. Если нажата левая кнопка в клетке, в которой есть мина, то вызывается функция showPole , которая показывает все мины, в том числе и те, которые игрок не успел найти.



Игра "Сапер"



Игра "Сапер"



Всем, кто работает с операционной системой Windows, хорошо знакома игра "Сапер". В этом разделе рассматривается аналогичная программа.

Пример окна программы в конце игры (после того, как игрок открыл клетку, в которой находится мина) приведен на Рисунок 10.7.



Игровое поле



Игровое поле



На разных этапах игры игровое поле выглядит по-разному. Вначале поле просто разделено на клетки. Во время игры, в результате щелчка правой кнопкой мыши, в клетке появляется флажок. Щелчок левой кнопкой тоже меняет вид клетки: клетка меняет цвет и в ней появляется цифра или мина (игра на этом заканчивается). Рассмотрим объекты, свойства и методы, обеспечивающие работу с графикой.

 



Информация о программе



Информация о программе



При выборе из меню ? команды О программе на экране должно появиться одноименное окно (Рисунок 10.16).



Клетке игрового поля соответствует элемент массива



Рисунок 10.8. Клетке игрового поля соответствует элемент массива



В начале игры каждый элемент массива, соответствующий клеткам игрового поля, может содержать число от 0 до 9. Ноль соответствует пустой клетке, рядом с которой нет мин. Клеткам, в которых нет мин, но рядом с которыми мины есть, соответствуют числа от 1 до 8. Элементы массива, соответствующие клеткам, в которых находятся мины, имеют значение 9, а элементы массива, соответствующие границе поля, содержат —3.

В качестве примера на Рисунок 10.9 изображен массив, соответствующий состоянию поля в начале игры.

В процессе игры состояние игрового поля меняется (игрок открывает клетки и ставит флажки, и, соответственно, меняются значения элементов массива. Если игрок поставил в клетку флажок, то значение соответствующего элемента массива увеличивается на 100. Например, если флажок поставлен правильно, т. е. в клетку, в которой есть мина, значение соответствующего элемента массива станет равным 109. Если флажок поставлен ошибочно (например, в пустую клетку), элемент массива будет содержать число 100. Если игрок открыл клетку, значение элемента массива увеличивается на 200. Такой способ кодирования позволяет сохранить информацию об исходном состоянии клетки.



Командная кнопка View Form



Рисунок 10.18. Командная кнопка View Form



Вид формы AboutForm после добавления необходимых компонентов приведен на Рисунок 10.19, значения ее свойств — в табл. 10.5.



Компонент MainMenu



Рисунок 10.11. Компонент MainMenu



Значок компонента MainMenu можно поместить в любое место формы, т. к. во время работы программы он не виден. Пункты меню появляются в верхней части формы в результате настройки компонента. Для настройки меню используется редактор меню, который запускается двойным щелчком левой кнопкой мыши на значке компонента или путем выбора из контекстного меню компонента команды Menu Designer . В начале работы над новым меню, сразу после добавления компонента к форме, в окне редактора находится один-единственный прямоугольник — заготовка пункта меню. Чтобы превратить эту заготовку в меню, нужно в поле Caption окна Object Inspector ввести название меню.

Если перед какой-либо буквой в названии меню ввести знак &, то во время работы программы можно будет активизировать этот пункт меню путем нажатия комбинации клавиши <Alt> и клавиши, соответствующей символу, перед которым стоит знак &. В названии меню эта буква будет подчеркнута.

Чтобы добавить в главное меню элемент, нужно в окне редактора меню выбрать последний (пустой) элемент меню и ввести название нового пункта.

Чтобы добавить в меню команду, нужно выбрать тот пункт меню, в который надо добавить команду, переместить указатель активного элемента меню в конец списка команд меню и ввести название команды.

На Рисунок 10.12 приведено окно редактора меню, в котором находится меню программы "Сапер".

После того как будет сформирована структура меню, нужно, используя окно Object Inspector , выполнить настройку элементов меню (выбрать настраиваемый пункт меню можно в окне формы приложения или из списка объектов в верхней части окна Object Inspector ). Каждый элемент меню (пункты и команды) — это объект типа TMenuitem . Свойства объектов TMenuitem (табл. 10.3) определяют вид меню во время работы программы.



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



Листинг 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 ) определяет, на каком из компонентов массива испытуемый сделал щелчок и, следовательно, какой из вариантов ответа выбран. Окончательная фиксация номера выбранного ответа и сравнение его с номером правильного ответа происходит в результате нажатия кнопки Дальше.
 


генерирует новое поле void __fastcall



Листинг 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) выводит изображение игрового поля.

Функция ShowPole выводит изображение поля



Листинг 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 .

рисует на экране клетку void



Листинг 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);
}
 


Обработка события OnMouseDown на поверхности



Листинг 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; }

рисует мину void __faatcall



Листинг 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; // убрать окно О программе }
 


TMouseButton Button, TShiftState Shift, int



Листинг 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);
}

В функцию ShellExvte надо передать



Листинг 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; }
 


щелчок на кнопке Каталог void__fastcall



Листинг 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.

во вариантов ответа int rOtv;



Листинг10.1.

Программа тестирования (заголовочный файл) #ifndef tester_H #define tester_H #include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
#include <Dialogs.hpp>
#include <Graphics.hpp>
// вопрос struct TVopros { AnsiString Vopr; // вопрос AnsiString Img; // иллюстрация (имя BMP-файла) AnsiString Otv[4]; // варианты ответа int nOtv; // кол- во вариантов ответа int rOtv; // номер правильного ответа }; // форма class TForml : public TForm { published: // IDE-managed Components TLabel *Labell; // информационное сообщение, вопрос Tlmage *Imagel; // иллюстрация к вопросу TButton *Buttonl; // кнопка OK / Дальше void__fastcall FormActivate(TObject *Sender);
void __fastcall ButtonlClick(TObject *Sender);
private: TRadioButton *RadioButton[4]; // варианты ответа - кнопки выбора void __fastcall RadioButtonClick(TObject *Sender);
// щелчок на // кнопке выбора ответа void __fastcall ShowVopros(TVopros v);
// выводит вопрос void __fastcall EraseVopros(void);
// удаляет вопрос public: __fastcall TForml (TCornponent* Owner) ; }; extern PACKAGE TForml *Forml; #endif

Массив в начале игры



Рисунок 10.9. Массив в начале игры




 











Мина



Рисунок 10.14. Мина





Начало игры



Начало игры



В начале игры надо расставить мины и для каждой клетки поля подсчитать, сколько мин находится в соседних клетках. Функция NewGame (ее текст при-, веден в листинге 10.3), решает эту задачу.



Настройка программы тестирования



Рисунок 10.5. Настройка программы тестирования на работу с файлом Peterburg.txt



Программа может получить информацию о количестве параметров командной строки, обратившись к функции paramCount . Доступ к конкретному параметру обеспечивает функция Paramstr , которой в качестве параметра передается номер параметра, значение которого надо получить. Параметры командной строки нумеруются с единицы. Следует обратить внимание, что значением paramstr(O) является полное имя выполняемого файла программы.

Ниже приведен фрагмент программы, который демонстрирует доступ к параметрам командной строки.
int n = ParamCount(); if ( n < 1) { Labell->Font->Style = TFontStyles()« fsBold; Labell->Caption = "В командной строке надо указать имя файла теста"; Buttonl->Tag = 2; return; } // открыть файл теста f = FileOpen(ParamStrd), fmOpenRead) ;
При запуске программы, использующей параметры командной строки, из среды разработки параметры нужно ввести в поле Parameters диалогового окна Run Parameters (Рисунок 10.6), которое открывается в результате выбора из меню Run команды Parameters .



Очистка диска



Очистка диска



Открыв каталог какого-либо проекта C++ Builder, можно увидеть, что помимо файлов, составляющих проект и используемых компилятором для генерации выполняемого файла (bpr-, dfm-, h- и срр-файлы), в каталоге есть файлы с расширением obj, tds и еще несколько файлов, расширение которых начинается значком ~ (Рисунок 10.20).



Окно программы "Очистка диска" в начале ее работы



Рисунок 10.21. Окно программы "Очистка диска" в начале ее работы



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

Вид формы программы приведен на Рисунок 10.22. После того как компоненты будут добавлены в форму, необходимо выполнить их настройку — задать значения свойств (табл. 10.7).

Текст программы "Очистка диска" приведен в листинге 10.14.



Окно программы "Сапер"



Рисунок 10.7. Окно программы "Сапер"




 











Окно справочной системы программы "Сапер"



Рисунок 10.15. Окно справочной системы программы "Сапер"



Для того чтобы во время работы программы пользователь, нажав клавишу <F1>, мог получить справочную информацию, свойствам HelpFile и HelpContext главной формы надо присвоить значения, указанные в табл. 10.4.



Отображение иллюстрации



Отображение иллюстрации



Для отображения иллюстраций используется компонент image1 .

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

Очевидно, что размер области формы, которая может быть использована для вывода иллюстрации, зависит от длины (количества слов) вопроса, а также от длины и количества альтернативных ответов. Чем длиннее вопрос и ответы, тем больше места в поле формы они занимают, и тем меньше места остается для иллюстрации.

Размер и положение областей (компонентов), предназначенных для вывода вопроса, альтернативных ответов и иллюстрации, можно задать в процессе создания формы. Однако можно поступить иначе — задать размер и положение областей во время работы программы, после того как из файла будет прочитан очередной вопрос, когда будет получена информация о количестве альтернативных ответов. После того как вопрос прочитан, можно вычислить, сколько места займет текст вопроса и вариантов ответа и сколько места можно выделить для отображения иллюстрации (Рисунок 10.3).



Параметры командной строки надо ввести в поле Parameters



Рисунок 10.6. Параметры командной строки надо ввести в поле Parameters




 











Передача параметра при запуске



Рисунок 10.4. Передача параметра при запуске программы командой Пуск | Выполнить



Если запуск программы выполняется при помощи значка, изображающего программу на рабочем столе или в папке, то параметр командной строки задают в окне Свойства этого значка. Например, для настройки программы тестирования на работу с файлом теста Peterburg.txt надо раскрыть окно свойств значка (щелкнуть правой кнопкой мыши на значке и из появившегося контекстного меню выбрать команду Свойства) и в поле Объект (после имени выполняемого файла программы) ввести имя файла теста (Peterburg.txt), заключив его в двойные кавычки (Рисунок 10.5).



Правила игры и представление данных



Правила игры и представление данных



Игровое поле состоит из клеток, в каждой из которых может быть мина. Задача игрока — найти все мины и пометить их флажками.

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

В программе игровое поле представлено массивом N+2 на М+2 , где NxM — размер игрового поля. Элементы массива с номерами строк от 1 до N и номерами столбцов от 1 до М соответствуют клеткам игрового поля (Рисунок 10.8); первые и последние столбцы и строки соответствуют границе игрового поля.



Система проверки знаний



Система проверки знаний



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

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

Рассмотрим программу, которая позволяет автоматизировать процесс тестирования.

 



Содержимое каталога проекта "Проверка



Рисунок 10.20. Содержимое каталога проекта "Проверка знаний". Файлы, выделенные цветом, по окончании работы над проектом можно удалить



Файлы с расширением tds и obj создает компилятор в процессе генерации выполняемого файла. Это временные файлы. По окончании работы над проектом они не нужны, и их можно удалить, тем более что размер tds-файла может достигать нескольких мегабайт. Файлы, расширение которых начинается значком ~ , — это резервные копии соответствующих файлов проекта. По окончании работы над проектом их тоже можно удалить с диска.

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

Окно программы "Очистка диска" в начале ее работы приведено на Рисунок 10.21. Для выбора каталога, который надо "почистить", используется стандартное диалоговое окно Обзор папок, которое появляется в результате щелчка на кнопке Каталог.



Справочная информация



Справочная информация



В результате выбора в меню ? команды Справка или нажатия клавиши <F1> должна появляться справочная информация — правила игры (Рисунок 10.15).



Структура меню программы "Сапер"



Рисунок 10.12. Структура меню программы "Сапер"





Значения свойств формы



Таблица 10.1. Значения свойств формы

Свойство
Значение
Пояснение
Borderlcons.biSystemMenu
true
Есть кнопка системного меню
Border Icons.biMinimize
false
Нет кнопки Свернуть окно
Borderlcons.biMaximize
false
Нет кнопки Развернуть окно
BorderStyle
bsSingle
Тонкая граница окна, нельзя изменить размер окна




Следует обратить внимание, что несмотря



Таблица 10.2 . Значения свойств компонента Label1

Свойство
Значение
Пояснение
AutoSize
false
Запрет изменения размера поля в соответствии с его содержимым
Wordwrap
true
Разрешить перенос слов в следующую строку поля
Следует обратить внимание, что несмотря на то, что свойства BorderIcons.biMinimize и Borderlcons.biMaximize имеют значение false , кнопки Свернуть окно и Развернуть окно отображены в форме. Реальное воздействие значений этих свойств на вид окна проявляется только во время работы программы. Значение свойства BorderStyle также проявляет себя только во время работы программы.

 


Свойства объекта TMenuItem



Таблица 10.3. Свойства объекта TMenuItem

Свойство
Определяет
Name
Имя элемента меню. Используется для доступа к свойствам
Caption
Название меню или команды
Bitmap
Значок, который отображается слева от названия элемента меню
Enabled
Признак доступности элемента меню. Если значение свойства равно false , то элемент меню недоступен, при этом название меню отображается серым цветом


При выборе во время работы программы элемента меню происходит событие click . Чтобы создать процедуру обработки этого события, нужно в окне формы выбрать пункт меню и щелкнуть левой кнопкой мыши — C++ Builder создаст шаблон процедуры обработки этого события. В качестве примера ниже приведена функция обработки события click , которое возникает в результате выбора из меню ? команды Справка , N3 — это имя элемента меню, соответствующего этой команде.
// выбор в меню "?" команды Справка void __fastcall TForml::N3Click(TObject *Sender) { WinHelp(Forml->Handle,"saper.hlp",HELP_CONTEXT,1); }

 


Значения свойств главной формы



Таблица 10.4 . Значения свойств главной формы

Свойство
Значение
Пояснение
HelpFile
saper.hlp
Файл справки
HelpContext
1
Идентификатор раздела, содержимое которого отображается в результате нажатия <F1>


Для того чтобы справочная информация появилась на экране в результате выбора в меню ? команды Справка, надо создать функцию обработки события Onclick для соответствующей команды меню. Процесс создания функции обработки события для команды меню ничем не отличается от процесса создания функции обработки события для элемента управления, например, для командной кнопки: в списке объектов надо выбрать объект типа TMenuitem , для которого создается функция обработки события, а во вкладке Events — событие.

Ниже приведена функция обработки события Onclick для команды Справка меню ? .
// выбор в меню ? команды Справка void__fastcall TForml::N3Click(TObject *Sender) { WinHelp(Forml->Handle,"saper.hlp",HELP_CONTEXT,1); }
Вывод справочной информации выполняет функция winHelp , которой в качестве параметра передается идентификатор окна программы, которая запрашивает вывод справочной информации, файл справки, константу HELP_CONTEXT и идентификатор раздела, содержимое которого должно быть отражено.

 


Значения свойств формы О программе



Таблица 10.5. Значения свойств формы О программе

Свойство
Значение
Name
AboutForm
Caption
O программе
BorderStyle
bsSingle
Borderlcons.biSystemMenu
false
Borderlcons.biMininize
false
Borderlcons.biMaximize
false



Значения свойств компонента Labels



Таблица 10.6. Значения свойств компонента Labels

Свойство
Значение
Комментарий
Font.Color
clBlue
Цвет — синий
Font.Style.Underline
true
Подчеркивание
Cursor
crHandPoint
При позиционировании указателя мыши на текст указатель принимает форму руки


Для запуска браузера использована функция shellExecute , которая открывает указанный файл при помощи программы, предназначенной для работы с файлами указанного типа. Так как имя файла в данном случае представляет собой URL-адрес, то будет запущен браузер (тот, который установлен на компьютере пользователя).

Наиболее просто передать URL-адрес в функцию shellExecute можно как строку-константу, например:
ShellExecute(AboutForm->Handle, "open", "http:\\\\www.bhv.ru", NULL, NULL, SW_RESTORE);
Но лучше URL-адрес брать из поля метки. В функцию sheiiExute надо передать указатель на обычную строку, т. е. завершающуюся нулевым символом. Однако свойство caption — это Ansistring . Преобразование строки Ansi в указатель на null terminated string выполняет метод c_str () .



Настройка компонентов



Таблица 10.7. Настройка компонентов

Компонент
Свойство
Значение
Комментарий
Form1
BorderStyle
bsSingle
Тонкая граница окна. Во время работы программы нельзя изменить размер окна, захватив границу мышью
Form1
Borderlcins .biMaximize
false
Во время работы программы в заголовке окна нет кнопки Развернуть
Button2
Enabled
false
В начале работы программы кнопка Выполнить недоступна
Memol
ScrollBars
ssVertical
Есть вертикальная полоса прокрутки



Текст программы



Текст программы



После того как будет создана форма программы, можно приступить к кодированию (набору текста). Сначала надо внести дополнения в объявление формы (листинг 10.1) — объявить массив компонентов RadioButton , функцию обработки события click на кнопке выбора ответа и функции, обеспечивающие отображение и удаление вопроса. Следует обратить внимание на то, что объявление массива компонентов RadioButton (указателей на компоненты) только устанавливает факт существования компонентов, сами же компоненты будут созданы в начале работы программы. Делает это конструктор формы. Он же задает функции обработки события click для компонентов массива. Другой важный момент, на который следует обратить внимание, это объявление функций swowVopros и EraseVopros как методов объекта Form1 . Это сделано для того, чтобы обеспечить этим функциям прямой доступ к компонентам формы.

Текст модуля главной формы приведен в листинге 10.2.



Текст программы



Текст программы



Полный текст программы "Сапер" приведен ниже: в листингах 10.11 и 10.12 — заголовочный файл и модуль главной формы; в листинге 10.13 — модуль формы О программе.



Требования к программе



Требования к программе



В результате анализа используемых на практике методик тестирования были сформулированы следующие требования к программе:

программа должна обеспечить работу с тестом произвольной длины, т. е. не должно быть ограничения на количество вопросов в тесте;  вопрос может сопровождаться иллюстрацией;  для каждого вопроса может быть до четырех возможных вариантов ответа со своей оценкой в баллах;  результат тестирования должен быть отнесен к одному из четырех уровней, например: "отлично", "хорошо", "удовлетворительно" или "плохо";  вопросы теста должны находиться в текстовом файле;  в программе должна быть заблокирована возможность возврата к предыдущему вопросу. Если вопрос предложен, то на него должен быть дан ответ.


На Рисунок 10.1 приведен пример окна программы тестирования во время ее работы.



Выбрать нужную форму можно в списке окна View Form



Рисунок 10.17. Выбрать нужную форму можно в списке окна View Form












Выбрав ссылку можно активизировать



Рисунок 10.16. Выбрав ссылку, можно активизировать браузер и перейти на страницу издательства



Чтобы программа во время своей работы могла вывести на экран окно, отличное от главного (стартового), нужно добавить в проект форму. Делается это выбором из меню File команды New form . В результате выполнения команды New form в проект добавляется новая форма и соответствующий ей модуль.

Если в проекте несколько форм, то для того чтобы получить доступ к нужной форме и, соответственно, к модулю, надо выбрать имя нужной формы в списке диалогового окна View Form (Рисунок 10.17), которое становится доступным в результате щелчка на командной кнопке View Form (Рисунок 10.18) или нажатия комбинации клавиш <Shift>+<F12>.



Вычисление размера области вывода иллюстрации



Рисунок 10.3. Вычисление размера области вывода иллюстрации



Если реальный размер иллюстрации превышает размер области, выделенной для ее отображения, то необходимо выполнить масштабирование иллюстрации. Для этого надо сначала присвоить максимально возможные значения свойствам width и Height , а затем — присвоить значение true свойству Proportional . Следует обратить внимание, что для того чтобы масштабирование было выполнено без искажения, значение свойства stretc должно быть false .