Прогон и отладка программы Справочная служба Турбо Паскаля
Вид материала | Документы |
Interval = 30 |
- Лекция №3. Состав и работа системы программирования Турбо Паскаль Язык программирования, 84.43kb.
- Структура программы на Паскале Система программирования Турбо Паскаль, 145.34kb.
- Структура программы на языке Турбо Паскаль Программа, написанная на языке Турбо Паскаль,, 229.09kb.
- Составлять линейные программы. Сохранять программы на диски. Оборудование, материалы,, 10.81kb.
- Структура программы в Турбо Паскаль. Простые операторы в Турбо Паскаль, 7.57kb.
- Лекция 23. Отладка и обработка исключительных ситуаций Корректность и устойчивость., 391.81kb.
- Структура программы языка Турбо Паскаль Программа на языке Турбо Паскаль имеет вид, 792.5kb.
- Книга издана при содействии Международного фонда "Культурная инициатива", 6971.93kb.
- Уроки №1-2 тема: "введение в паскаль. Среда турбо-паскаль", 120.81kb.
- Циклические программы. Структурированный тип данных. Структура сложной программы, 860.21kb.
for i := 1 to nrow do begin
GotoXY(X0,i+4);
Write(i); {Номер ряда}
GotoXY(X1,i+4);
Write(col[i]:2); {Количество фишек в ряду}
for j := 1 to ncol[i] do {Вывод ряда фишек:}
begin
GotoXY(X+2*j,i+4); if j[i] then
Write(FISH)
else
Write('.')
end
end
end; {ShowField}
Символы FISH (квадратики) выводятся через одну позицию, чтобы не сливались на экране. В те позиции, в которых ранее стояли уже снятые с поля фишки, выводится точка.
Теперь вернемся к процедуре GETPLAYERMOVE. При вводе любого очередного хода игрок должен задать два целых числа X1 и Х2. Первое из них указывает номер ряда, а второе - количество фишек, которые игрок хочет забрать из этого ряда. Программа должна проконтролировать правильность задания этих чисел: X1 должно указывать непустой ряд, Х2 не может превышать количество фишек в этом ряду. Кроме того, мы должны условиться о двух особых случаях:
- пользователь больше не хочет играть и дает команду завершить работу программы;
- пользователь хочет изменить условия игры.
Пусть ввод числа X1 =0 означает команду выхода из программы, а X1 = -1 - команду изменения условий игры. Тогда можно написать такой начальный вариант процедуры:
Procedure GetPlayerMove;
{Получает, контролирует и отображает ход игрока}
var
correctly : Boolean; {Признак правильности сделанного хода}
xl,x2 : Integer; {Вводимый ход}
begin {GetPlayerMove}
{Показываем начальное состояние игрового поля}
ShowField;
{Сообщаем, игроку правила ввода хода}
repeat
{Приглашаем игрока ввести ход}
ReadLn(xl,x2); {Вводим очередной ход}
exit := xl=0; {Контроль команды выхода}
change := xl=-l; {Контроль команды изменения}
if not (exit or change) then
{Проверить правильность хода и установить нужное значение переменной CORRECTLY. Если ход правильный, сделать нужные изменения в раскладке фишек и показать поле.}
else
correctly := true {Случай EXIT или CHANGE}
until correctly; if change then
{ Изменить условия игры }
end; {GetPlayerMove}
В этом варианте в процедуре GetPlayerMove нет описания процедуры SHOWFIELD. Сделано это не случайно: процедура ShowField может понадобиться также и при реализации процедуры SetOwnerMove, поэтому она должна быть глобальной по отношению и к GetPlayerMove, и к SetOwnerMove, т.е. ее описание должно в тексте программы предшествовать описаниям двух использующих ее процедур.
Действия
{ Сообщить игроку правила ввода хода } ,
{ Пригласить игрока ввести ход }
и
{Проверить правильность хода и установить нужное значение переменной Correctly. Если ход правильный, сделать нужные изменения в раскладке фишек и показать поле.}
не очень сложны в реализации, поэтому их можно осуществить непосредственно в теле процедуры GETPLAYERMOVE. Иное дело - изменение условий игры. Это действие полезно реализовать в отдельной процедуре GETCHANGE. С учетом этого второй вариант процедуры GETPLAYERMOVE примет такой вид:
Procedure GetPlayerMove;
{Получает, контролирует и отображает ход игрока}
const
ТЕХТ1 = 'Введите Ваш ход в формате РЯД КОЛИЧ ';
ТЕХТ01= ' (например, 2 3- взять из 2 ряда 3 фишки) ' ;
ТЕХТ2 = 'или введите 0 0 для выхода из игры; ' ; .
ТЕХТ02= '-1 0 для настройки игры';
ТЕХТЗ = 'Ваш ход: ';
Y = 20; {Номер строки для вывода сообщений}
var
correctly : Boolean; {Признак правильности сделанного хода}
xl,x2 : Integer; {Вводимый ход}
{-----------------}
Procedure GetChange;
{Устанавливает новую настройку игры (количество рядов и количество фишек в каждом ряду}
begin {GetChange}
end; {GetChange}
{-----------------}
begin {GetPlayerMove}
ShowField; {Показываем начальное состояние поля}
{Сообщить игроку правила ввода хода:}
GotoXY((80-Length(TEXT1+TEXT01)) div2,Y);
Write(TEXT1+TEXT01);
GotoXY((80-Length(TEXT2+TEXT02)) div2,Y+l);
Write(TEXT2+TEXT02);
repeat
{Пригласить игрока ввести ход:}
GotoXY(l,Y+2);
Write(TEXTS); {Выводим приглашение и стираем предыдущий ход}
GotoXY(WhereX-16,Y+2); {Курсор влево на 16 позиций}
ReadLn(xl,x2); {Вводим очередной ход}
exit := xl=0; {Контроль команды выхода}
change := xl=-l; {Контроль команды изменения}
if not (exit or change) then
begin
correctly := (xl > 0) and (xl <= nrow) and (x2 <= col[xl]) and (x2 > 0) ;
if correctly then
begin {Ход правильный:}
col[xl] := col[xl]-x2; {Изменяем раскладку фишек}
ShowField {Показываем поле}
end
else
Write(#7) {Ход неправильный: дать звуковой сигнал}
end
else
correctly := true {Случай EXIT или CHANGE}
until correctly;
if change then
GetChange end; {GetPlayerMove}
Обратите внимание: константа
ТЕХТЗ = 'Ваш ход:
имеет длинный «хвост» из пробелов (их 17), поэтому после вывода этого приглашения курсор возвращается влево на 16 позиций оператором
GotoXY(WhereX-16,Y+2); {курсор влево на 16 позиций}
(функция WHEREX возвращает текущую горизонтальную координату курсора, а функция WHEREY - его вертикальную координату). Сделано это для того, чтобы в случае, если игрок ввел неверный ход и программа повторяет вывод приглашения, пробелы в константе ТЕХТЗ затерли бы строку предыдущего ввода.
Чтобы завершить создание процедуры GETPLAYERMOVE, нужно спроектировать процедуру GETCHANGE, в которой осуществляется изменение условий игры. Я привожу текст этой процедуры без пояснений и приглашаю Вас самостоятельно разобраться в том, как она работает:
Procedure GetChange;
{Устанавливает новую настройку игры (количество рядов и количество фишек в каждом ряду}
const
tl='HACTPOЙKA ИГРЫ';
t2 ='(ввод количества рядов и количества '+'фишек в каждом ряду)';
var
correctly : Boolean;
i : Integer; begin {GetChange}
ClrScr;
GotoXY( (80 -Length (tl) ) div2,l);
Write(tl) ;
GotoXY( (80 -Length (t2) ) div2,2);
Write (t2);
repeat
GotoXYd, 3) ;
Write ( 'Введите количество рядов (максимум ', MAXROW, '):');
GotoXY(WhereX-6,WhereY) ;
ReadLn (nrow) ;
correctly := (nrow <= MAXROW) and (nrow > 1) ;
if not correctly then
Write (#7)
until correctly;
for i := 1 to nrow do
repeat
GotoXY(l,i+3) ;
Write (' ряд ',i,', количество фишек (максимум ', MAXCOL , ' ) : ' ) ;
GotoXY (WhereX - 6 , WhereY) ;
ReadLn (ncol [i] ) ;
correctly := (ncol [i] <= MAXCOL) and (ncol[i] > 0) ;
if not correctly then
Write (#7)
until correctly
end; {GetChange}
Переходим к конструированию процедуры SETOWNERMOVE, в которой программа должна проконтролировать текущую ситуацию на игровом поле и выбрать собственный ход. Работа процедуры начинается с подсчета числа непустых рядов. В зависимости от этого подсчета реализуются следующие действия:
- если все ряды пусты, значит предыдущим ходом игрок забрал последнюю фишку и он победил; нужно поздравить его с победой, усложнить игру и предложить сыграть еще раз;
- если есть только один непустой ряд, то очередной ход программы очевиден -забрать все фишки, что означает победу машины: сообщить об этом и предложить сыграть еще раз;
- если осталось два или более непустых ряда, выбрать собственный ход на основе оптимальной стратегии. Начальный вариант процедуры:
Procedure SetOwnerMove;
{Находит и отображает очередной ход программы}
{-----------------}
Function CheckField : Integer;
{Проверяет состояния игры. Возвращает 0, если нет ни одной фишки (победа игрока) , 1 - есть один ряд (победа машины) и - количество непустых рядов в остальных случаях}
begin {CheckField}
end; {CheckField}
{-----------------}
Procedure PlayerVictory;
{Поздравить игрока с победой и усложнить игру}
begin {PlayerVictory}
end; {PlayerVictory}
{-----------------}
Procedure OwnVictory;
{Победа машины}
begin {OwnVictory}
end; {OwnVictory}
{-----------------}
Procedure ChooseMove;
{Выбор очередного хода}
begin {ChooseMove}
end; {ChooseMove}
{-----------------}
begin {SetOwnerMove}
case CheckField of {Проверяем количество непустых рядов}
0 : PlayerVictory; {Все ряды пусты - победа игрока}
1 : OwnVictory; {Один непустой ряд - победа машины}
else
ChooseMove; {Выбираем очередной ход}
end; {case}
end; {SetOwnerMove}
Функция CHECKFIELD и процедуры PLAYERVICTORY и OWNVICTORY достаточно просты и их текст помещается без каких-либо пояснений в окончательный вариант программы (см. прил.5.3). Отмечу лишь, что в случае победы игрока нет смысла повторять партию заново с той же самой раскладкой фишек. Поэтому игра усложняется: в исходную раскладку добавляется еще по одной фишке в каждый ряд.
В процедуре CHOOSEMOVE анализируется позиция и выбирается очередной ход программы. Описание оптимальной стратегии уже приводилось выше. Действия программы заключаются в поиске первого слева (старшего) двоичного разряда, для которого сумма чисел нечетная. Если такой разряд не обнаружен, то текущая позиция безопасна для игрока, а значит любой ход программы сделает ее опасной. В этом случае для программы не существует оптимального выбора и она лишь убирает одну фишку из любого непустого ряда. Такая тактика означает пассивное ожидание ошибки игрока.
Если обнаружен разряд i с нечетной суммой, программа приступает к реализации оптимальной стратегии и тогда игрок обречен на поражение. Для выбора ряда, из которого следует взять фишки, программа просматривает последовательно все ряды и отыскивает тот ряд j, количество фишек в котором (в двоичном представлении) дает единицу в разряде i. Значение этого разряда для количества фишек в ряду j заменяется нулем. Затем программа продолжает подсчет суммы для оставшихся младших разрядов. Если в каком-либо из них вновь обнаружена нечетность, значение этого разряда для количества фишек в рядуj инвертируется, т.е. 0 заменяется на 1, а 1 на 0. Например, если двоичные представления числа фишек и четности сумм таковы:
число фишек в ряду j: 01001
четность сумм: 01011
(единицей указаны разряды с нечетными суммами), то в результате этой операции получим:
число фишек в ряду j: 00010
четность сумм: 00000
Таким образом, в исходном состоянии в ряду j было 1001 =9 фишек, безопасная позиция требует, чтобы в ряду осталось 0010 = 2 фишки, следовательно, из него нужно забрать 9-2 = 7 фишек.
Окончательный вариант программы представлен в прил.5.3. Попробуйте разобраться в ее деталях самостоятельно.
В программной реализации алгоритма широко используется то обстоятельство, что Ваш компьютер, как и все остальные вычислительные машины, работает с числами, представленными в двоичной системе счисления. Поэтому для получения двоичного представления числа в процедуре BITFORM оно проверяется на четность с помощью стандартной функции ODD, затем сдвигается вправо на один двоичный разряд (операция SHR), вновь осуществляется проверка на четность и т.д. до тех пор, пока не будут проверены все разряды. Максимальное число двоичных разрядов, достаточное для двоичного представления количества фишек в ряду MAXCOL=63, задается константой ВIТ=6.
Для получения суммы двоичных разрядов в процедуре CHOOSEMOVE используется суммирование разрядов по модулю 2 с помощью операции XOR. Такое суммирование дает 0, если количество единиц четное или равно нулю, и 1 - если нечетное. В этой же процедуре для инверсии двоичного разряда применяется оператор
if nbit[i] = 1 then
ncbit[j,i] := ord(ncbit[j,i]=0); {Инверсия разрядов},
в котором используется соглашение о внутреннем представлении логических величин в Турбо Паскале: 0 соответствует FALSE, а 1 - TRUE.
Тнексты программ
- Программа определения дня недели
- Определение биоритмов
- Игра НИМ
- Программа NOTEBOOK
Программа определения дня недели
{Эта программа вводит дату в формате ДЦ ММ ГГГГ к выводит на экран соответствующий этой дате день недели. Описание программы см. п. 2. 7.1.}
var
IsCorrectDate: Boolean; {Признак правильной даты}
d,m,y : Integer; {Вводимая дата - день, месяц и год}
{---------------}
Procedure InputDate (var d,m,y : Integer; var correctly : Boolean);
{Вводит в переменные d, m и у очередную дату и проверяет ее. Если дата правильная, устанавливает correctly=true, иначе correctly= false }
begin {InputDate}
Write ( 'Введите дату в формате ДД ММ ГГГГ: ');
ReadLn(d,m,y) ;
correctly := (d>=l) and (d<=31) and (m>=l)
and (m<=12) and (y>=1582) and (y<=4903)
end; {InputDate}
{----------------}
Procedure WriteDay (d,m,y : Integer) ;
const
Days_of_week : array [0..6] of String [11] =
( ' воскресенье ' , ' понедельник ' , ' вторник ' ,
' среда ' , ' четверг ' , ' пятница ' , ' суббота ' ) ;
var
с, w : Integer;
begin
if m < 3 then
begin {Месяц январь или февраль}
m := m + 10;
у := у - 1
end
else
m := m - 2; {Остальные месяцы}
с := у div 100; {Вычисляем столетие}
y := y mod 100; {Находим год в столетии}
w := abs(trunc(2.6*m-0.2)+d+y div 4+y+c div 4-2*c) mod 7;
WriteLn (Days_of_week [w] )
end;
{------------}
begin
repeat
InputDate (d,m,y, IsCorrectDate) ;
if IsCorrectDate then
WriteDay (d,m, у )
until not IsCorrectDate
end.
Определение биоритмов
{Программа для определения физической, эмоциональной и интеллектуальной активности человека. Вводится дата рождения и текущая дата. Программа вычисляет и выводит на экран общее количество дней, часов, минут и секунд, разделяющих обе даты, а также прогнозирует на месяц вперед даты, соответствующие максимуму и минимуму биоритмов. Описание программы см. п. 2. 7. 2.}
const
Size_of_Month: array [1..12] of Byte =
(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
var
d0, d,{Дни рождения и текущий}
m0, m,{Месяцы рождения и текущий}
y0, y,{Годы рождения и текущий}
dmin,{Наименее благоприятный день}
dmax,{Наиболее благоприятный день}
days: Integer;{Количество дней от рождения}
{--------------------------}
Procedure InputDates(var d0,m0,y0,d,m,y : Integer);
{Вводит дату рождения и текущую дату. Контролирует правильность дат и их непротиворечивость(текущая дата должна быть позже даты рождения)}
var
correctly: Boolean; {Признак правильного ввода}
{-------------------}
Procedure InpDate(text: String; var d,m,y: Integer);
{Выводит приглашение TEXT, вводит дату в формате ДД ММ ГГГГ и проверяет ее правильность}
const
YMIN =1800; {Минимальный правильный год}
YMAX =2000; {Максимальный правильный год}
begin {InpDate}
repeat
Write(text);
ReadLn(d,m,y);
correctly := (y >= YMIN) and (Y <= YMAX) and (m >= 1)
and (m <= 12) and (d > 0);
if correctly then
if (m = 2) and (d = 29) and (y mod 4=0)
then
{Ничего не делать: это 29 февраля високосного года!}
else
correctly := d <= Size_of_Month[m];
if not correctly then
WriteLn('Ошибка в дате!')
until correctly
end; {InpDate}
{----------------}
begin {InputDates}
repeat
InpDate('Введите дату рождения в формате ДД ММ ГГГГ:',d0,m0,y0);
InpDate(' Введите текущую дату: ', d, m, у);
{Проверяем непротиворечивость дат:}
correctly := у > у0;
if not correctly and (y = y0) then
begin
correctly := m > m0;
if not correctly and (m = m0) then
correctly := d >= d0
end
until correctly
end; {InputDates}
{-----------------}
Procedure Get_number s_of_days (d0,m0, y0,d,m, у : Integer; var days: Integer);
{Определение полного количества дней, прошедших от одной даты до другой }
{-------------------}
Procedure Variant2 ;
{Подсчет количества дней в месяцах, разделяющих обе даты }
var
mm : Integer;
begin {Variant2}
mm : = m0 ;
while mm < m do
begin
days := days + Size_of_Month[mm] ;
if (mm = 2) and (y0 mod 4=0) then
inc(days) ;
inc (mm)
end
end; {Variant2}
{---------------}
Procedure Variant3 ;
{Подсчет количества дней в месяцах и годах, разделяющих обе даты}
var
mm, yy : Integer;
begin {variant3}
mm := m0 + 1;
while mm <= 12 do {Учитываем остаток года рождения:}
begin
days := days+Size_of_Month[mm] ;
if (mm = 2) and (yO mod 4=0) then
inc (days) ;
inc (mm)
end;
yy := y0 + 1;
while yy < у do {Прибавляем разницу лет:}
begin
days := days + 365;
if yy mod 4=0 then
inc (days) ;
inc (yy)
end;
mm : = 1 ;
while mm < m do {Прибавляем начало текущего года:}
begin
days := days + Size_of_Month[mm] ;
if (y mod 4=0) and (mm = 2) then
inc (days) ;
inc (mm)
end
end; {Variant3}
{--------------------}
begin {Get_numbers_of_days}
if (y = y0) and (m = m0) then {Даты отличаются только днями:}
days := d - d0
else {Даты отличаются не только днями: }
begin
days := d + Size_of_Month[m0] - d0;
{Учитываем количество дней в текущем месяце и количество дней до конца месяца рождения}
if (y0 mod 4=0) and (m0 = 2) then
inc (days) ; {Учитываем високосный год}
if у = y0 then
Variant2 {Разница в месяцах одного и того же года}
else
Variant3 {Даты отличаются годами}
end
end; {Get_numbers_of_days}
{-------------------}
Procedure FindMaxMin(var dmin, dmax: Integer; days: Integer) ;
{Поиск критических дней}
const
TF = 2*3.1416/23.6884; {Период физической активности}
ТЕ = 2*3.1416/28.4261; {Период эмоциональной активности}
TI = 2*3.1416/33.1638; {Период интеллектуальной активности}
INTERVAL = 30;{Интервал прогноза}
var
min,{Накапливает минимум биоритмов}
max,{Накапливает максимум биоритмов}
x : Real;{Текущее значение биоритмов}
i : Integer;
begin {FindMaxMin}
max := sin(days*TF)+sin(days*TE)+sin(days*TI);
min := max;