Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Игра ним




End

Begin

Begin

Var

Const

Var

End

Begin

End

Begin

Begin

Var

Const

End

Begin

Begin

Begin

Vаr

End

Begin

Vаr

End

Else

Begin

Else

Then

Repeat

Const

Var

Var

Const

Size_of_Month: array [1..12] of Byte =

(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

{ ------------------------------------------ }

Procedure InputDates(var d0,m0,y0,d,m,y: Integer);

.....

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

С учетом сказанного напишем следующий начальный вариант программной реализации процедуры INPUTDATES:

Procedure InputDates(var d0,m0,y0,d,m,y: Integer);

{Вводит дату рождения и текущую дату. Контролирует правильность дат и их непротиворечивость (текущая дата должна быть позже даты рождения)}

correctly: Boolean; {Признак правильного ввода}

begin {InputDates}

repeat

{Вводим и контролируем дату рождения d0,m0,y0.}

{Вводим и контролируем текущую дату d,m,y.}

{Проверяем непротиворечивость дат:}

correctly:= у > у0;

if not correctly and (у = y0) then

begin

correctly:= m > m0;

if not correctly and (m = m0) then

correctly:= d>=d0

end

until correctly

end; {InputDates}

В этой процедуре дважды выполняется одно и то же алгоритмическое действие (ввод и контроль даты). Это действие можно вынести в отдельную внутреннюю процедуру с именем INPDATE, тогда получим следующий окончательный вариант:

Procedure InputDates(var d0,m0,y0,d,m,у: Integer);

{Вводит дату рождения и текущую дату. Контролирует правильность дат и их непротиворечимость (текущая дата должна быть позже даты рождения)}

correctly: Boolean; {Признак правильного ввода}

{ ----------------------------- }

Procedure InpDate(text: String; var d,m,y: Integer);

{Выводит приглашение TEXT, вводит дату в формате ДД ММ ГГГГ и проверяет ее правильность}

YMIN = 1800; {Минимальный правильный год}

YМАХ = 2000; {Максимальный правильный год}

begin {InpDate}

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)

{Ничего не делать: это 29 февраля високосного года!}

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,y);

{Проверяем непротиворечивость дат:}

correctly:= у > у0;

if not correctly and (у = y0) then

begin

correctly:= m > m0;

if not correctly and (m = m0) then

correctly:= d>=d0

end

until correctly

end; {InputDates}

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

• месячный младенец (год и месяц обеих дат одинаков): количество дней находится простым вычитанием двух чисел;

• годовалый младенец (год обеих дат совпадает): количество дней = (остаток дней в месяце рождения) + (количество дней в текущем месяце) + (количество дней в месяцах, разделяющих обе даты);

• общий вариант (отличаются года): количество дней = (количество дней от даты рождения до конца года) + (количество дней в разделяющих даты годах) + (количество дней от начала текущего года до текущей даты).

С учетом этого составим начальный вариант программной реализации процедуры GET_NUMBERS_OF_DAYS:

Procedure Get_numbers_of_days(d0,m0, y0,d,m, у: Integer;

var days: Integer);

{Определение полного количества дней, прошедших от одной даты до другой}

{ ----------------------------- }

Procedure Variant2;

{Подсчет количества дней в месяцах, разделяющих обе даты}

begin {Variant2}

end; {Variant2}

{ ----------------------------- }

Procedure Variant3;

{Подсчет количества дней в месяцах и годах, разделяющих обе даты}

begin {Variant3}

end; {Variant3}

{ ----------------------------- }

begin {Get_numbers_of_days}

if (y = y0) and (m = m0) then {Даты отличаются только днями:}

days:= d – d0

else {Даты отличаются не только днями:}

days:= d + Size_of_Month[m0] – d0;

{Учитываем количество дней в текущем месяце и количество дней до конца месяца рождения}

if (y0 mod 4 = 0) and (m0 = 2) then

inc(days); {Учитываем високосный год}

if у = y0 then

Variant2 {Разница в месяцах одного и того же года}

Variant3 {Даты отличаются годами}

end; {Get_numbers_of_days}

В этом фрагменте используется способ связи вспомогательных процедур VARIANT2 и VARIANT3 с основной процедурой через глобальные переменные, которыми являются параметры обращения к основной процедуре. Вспомогательные процедуры удобнее всего реализовать на основе циклов WHILE:

Procedure Variant2;

{Подсчет количества дней в месяцах, разделяющих обе даты }

mm: Integer;

begin {Variant2}

mm:= m0;

while mm < m do

days:= days + Size_of_Month[mm];

if (mm = 2) and (y0 mod 4=0) then

inc(days);

inc(mm)

end; {Variant2}

{ ----------------------------- }

Procedure Variant3;

{Подсчет количества дней в месяцах и годах, разделяющих обе даты }

mm, yy: Integer;

begin {Variant3}

mm: = m0 + 1;

while mm <= 12 do {Учитываем остаток года рождения:}

days:= days+Size_of_Month[mm];

if (mm = 2) and (y0 mod 4=0) then

inc(days);

inc(mm)

end;

yy:= y0 + 1;

while yy < у do {Прибавляем разницу лет:}

days:= days + 365;

if yy mod 4 = 0 then

inc(days);

inc(yy)

end;

mm:= 1;

while mm < m do {Прибавляем начало текущего года:}

days:= days + Size_of_Month[mm];

if (y mod 4=0) and (mm = 2) then

inc(days);

inc(mm)

end; {Variant3}

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

TF = 23.6884

ТE = 28.4261

TI = 33.1638

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

Procedure FindMaxMin(var dmin,dmax: Integer;

days: Integer);

{Поиск критических дней}

TF = 2*3.1416/23.6884; {Период физической активности}

ТЕ = 2*3.1416/28.4261; {Период эмоциональной активности}

TI = 2*3.1416/33.1638; {Период интеллектуальной активности}

INTERVAL = 30; {Интервал прогноза}

min, {Накапливает минимум биоритмов}

max, {Накапливает максимум биоритмов}

х: real; {Текущее значение биоритмов}

i: Integer;

begin {FindMaxMin}

max: = sin(days*TF)+sin(days*TE)+sin(days*TI);

min:= max; {Начальное значение минимума и максимума равно значению биоритмов для текущего дня}

dmin: = days;

dmax:= days;

for i:= 0 to INTERVAL do

x:= sin((days+i)*TF) + sin((days+i)*TE) +

sin((days+i)*TI);

if x > max then

max:= x;

dmax:= days + i

else if x < min then

min:= x;

dmin:= days + i

end;

end; {FindMaxMin}

При разработке алгоритма процедуры WRITEDATES, с помощью которой на экран выводится результат работы программы, учтем, что основные сложности будут связаны с определением новой даты по начальной дате и количеству прошедших дней. Этот расчет будет повторяться дважды – для |даты пика и даты спада биоритмов, поэтому его следует вынести в отдельную процедуру WRITEDATES. Кроме того, вряд ли Вы откажетесь от возможности вывода на экран дополнительной информации о том, сколько полных дней, часов, минут и секунд разделяют дату рождения человека и текущую дату. Однако реализация этого вывода не столь проста, как это может показаться на первый взгляд. Дело в том, что диапазон возможных значений данных типа INTEGER составляет от –32768 до +32767. Средняя продолжительность жизни человека – около 70 лет, т.е. 25550 дней. Это значение еще можно представить в переменной типа INTEGER, однако часы, минуты и тем более секунды средней продолжительности жизни ко превышают этот диапазон. Чтобы получить вывод достоверных [данных, необходимо расширить диапазон значений целых чисел. Для этого Турбо Паскале предусмотрен специальный тип данных LONG I NT («длинный» целый), имеющий диапазон значений от –2147483648 до –2147483647 (см. гл. 4). Поэтому в процедуре WRITEDATES следует предусмотреть вспомогательную переменную этого типа, присвоить ей значение переменной DAYS и уже затем использовать «длинную» переменную для вычисления (и вывода) часов, минут, секунд. В результате начальный вариант процедуры WRITEDATES может быть таким:

Procedure WriteDates(dmin,dmax,days: Integer);

{Определение и вывод дат критических дней.

Вывод дополнительной информации о количестве

прожитых дней, часов, минут и секунд }

{ ------------------------------- }

Procedure WriteDate (text: String; dd: Integer);

{Определение даты для дня DD от момента рождения. В глобальных переменных d, m и у имеется текущая дата, в переменной DAYS –количество дней, прошедших от момента рождения до текущей дaты. Выводится сообщение TEXT и найденная дата в формате ДД-MEC-ГГГГ}

begin {WriteDate}

end; {WriteDate}

{ ------------------------------- }

LongDays: Longlnt; {"Длинная" целая переменная для часов,

минут и секунд }

begin {WriteDates}

LongDays: = days;

WriteLn('Прошло: ',LongDays,' дней, ',longDays*24,

' часов, ', LongDays*24*60,' минут, ',

LongDays*24*60*60,' секунд');

WriteDate('Наименее благоприятный день: ',dmin);

WriteDate('Наиболее благоприятный день: ',dmax)

end; {WriteDates}

Реализация процедуры WRITEDATE не вызывает особых сложностей:

Procedure WriteDate(text: String; dd: Integer);

Names_of_Monthes: array [1..12] of String [3] =

('янв','фев','мар','апр','мая','июн',

'июл','авг','сен','окт','ноя','дек');

d0,m0,y0,ddd: Integer;

begin {WriteDate}

d0:= d;

m0:= m;

y0:= y;

ddd:= days;

while ddd<>dd do

inc(d0); {Наращиваем число}

if (y0 mod 4 <> 0) and (d0 > Size_of_Month[m0]) or

(y0 mod 4=0) and (d0=30) then

begin {Корректируем месяц}

d0:= 1,

inc(m0);

if m0 = 13 then {Корректируем год}

m0:= 1;

inc(y0)

end;

inc(ddd)

end;

WriteLn(text,d0,'–',Names_of_Monthes[m0], '–',y0)

end; {WriteDate}

Собрав воедино отдельные части, получим полный текст программы (прил.5.2), предназначенной для определения биоритмов.

 

Ним – одна из самых старых и увлекательных математических игр. Для игры в ним необходим партнер (в ним играют вдвоем), стол и набор фишек. В качестве фишек обычно используются камешки или монетки. В наиболее известном варианте нима 12 фишек раскладываются в три ряда так, как показано на рис. 2.3.

 

 

Рис.2.3. Фишки, расположенные для игры в ним по схеме 3 – 4 – 5

 

 

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

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

Полный анализ игры с обобщением на любое число рядов с любым числом фишек в каждом ряду впервые опубликовал в 1901 г. профессор математики из Гарвардского университета Чарльз Л.Бутон, который и назвал игру «ним» от устаревшей формы английских глаголов «стянуть», «украсть». Открытая им оптимальная стратегия основана на двоичной системе счисления и довольно проста. Каждую комбинацию фишек Бутон назвал либо опасной, либо безопасной: если позиция, создавшаяся после очередного хода игрока, гарантирует ему победу, она называется безопасной, если такой гарантии нет – опасной. Бутон строго доказал, что любую опасную позицию всегда можно превратить в безопасную нужным ходом. Наоборот, если перед очередным ходом игрока уже сложилась безопасная позиция, то любой его ход превращает позицию в опасную. Таким образом, оптимальная стратегия состоит в том, чтобы каждым ходом опасную позицию превращать в безопасную и заставлять противника «портить» ее. Использование оптимальной стратегии гарантирует победу игроку только тогда, когда он открывает партию и начальная позиция фишек опасна или он делает второй ход, а начальная позиция безопасна.

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

Например, для начальной позиции по схеме 3 – 4 – 5 получим:

 

  Сума по разрядам Десятичная запись количества фишек Двоичная запись количества фишек
   

Сумма цифр в среднем столбце равна 1 – нечетному числу, что свидетельствует об опасности этой позиции. Поэтому первый игрок может сделать ее безопасной для себя, если он возьмет две фишки из первого ряда. В результате в первом ряду остается только 1 фишка (двоичное числа также 1), и сумма чисел в среднем столбце изменится на ноль.

В привычной нам десятичной системе счисления емкость каждого разряда равна 10, а для записи значений разряда используются цифры от 0 до 9. В двоичной системе счисления емкость каждого разряда равна 2, а из всех цифр используются только 0 и 1. В этой системе число записывается в виде суммы степеней двойки и при переходе от одного разряда к соседнему левому вес разряда увеличивается в 2 раза. Если нужно записать число 2 в двоичной системе, следует действовать точно так же, как при записи числа 10 в десятичной системе: записать ноль в первом (младшем) разряде и единицу – слева от него, т.е. 10 в двоичной системе означает 2 в десятичной системе. Точно так же 100 в двоичной системе означает 4 в десятичной, 1000 – 8 и т.д.

Для перевода любого целого положительного числа из десятичной системы в двоичную можно использовать прием последовательного деления числа на 2. Например, для перевода десятичного числа 11 в двоичную систему используется такая цепочка делений:

Делимое Результат Остаток
11 5 5 2 1 1 0

Если, начиная с последнего результата, остатки от деления записать в обратном порядке, получим 1011 – это и есть двоичное представление десятичного числа 11. В этом легко убедиться, записав двоичное число 1011

как сумму степеней 2:

1х23+1х22+1х21+1 = 11

Попробуем разработать программу, которая будет выполнять роль партнера в игре, причем это будет весьма опасный противник, так как он будет «знать» оптимальную стратегию и умело ею пользоваться.

Представим себе на минутку, что Вы уже создали программу и начинаете работу с ней. Как организовать удобное взаимодействие с программой? Конечно, возможно простейшее решение: Вы предварительно раскладываете на столе монетки, по запросу программы вводите в нее Ваш ход, затем читаете на экране ход программы, делаете нужные изменения в раскладке монет и т.д. Вряд ли Вас удовлетворит такая программа. Гораздо эффектнее имитировать на экране игровое поле с фишками и своеобразное табло игры, в котором сообщается об очередных ходах противников. Однако использованные ранее средства вывода данных (процедуры WRITE и WRITELN) недостаточны для этих целей, ведь с их помощью нельзя указать конкретное место на экране, куда нужно поместить выводимую информацию. Вывод этими процедурами всегда начинается с той позиции на экране, которую в данный момент занимает курсор. Следовательно, для вывода текста в нужное место экрана требуется перед обращением к этим процедурам изменить положение курсора. Для этих целей служит про­цедура GOTOXY, которая хотя и является стандартной, но располагается в отдельной библиотеке (модуле) с именем CRT. Подробнее о модулях мы поговорим в гл.9, а сейчас просто учтем, что процедуры и функции из дополнительных библиотек становятся доступны в программе, если в самом ее начале объявить об их использовании. Так, указание об использовании библиотеки CRT делается таким образом:

Uses CRT;

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

Теперь попробуем составить алгоритм главной программы. В простейшем виде он таков:

Uses CRT; {Подключение библиотеки дополнительных

процедур и функций для управления экраном}




Поделиться с друзьями:


Дата добавления: 2014-11-06; Просмотров: 381; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.117 сек.