Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Самоучитель игры на Паскале. АВС и немного Турбо
.pdf
факториала. Но нас это не спасёт – компьютер всё равно считает слишком
быстро.
Другой вариант – посчитать один и тот же факториал не один раз, а
десять. Или сто. Лично у меня ненулевые значения появились при
миллионе повторений, а программа приобрела вот такой вид:
N:=15;
skoka:=1000000;
{--------------- обычный способ ------------------}
DT:=CurrentDateTime;
time0:=DT.hour*60*60*1000 + DT.minute*60*1000 + DT.second*1000
+ DT.milliseconds;
for i:=1 to sko
rez:=OldFact(N);
DT:=CurrentDateTime;
time1:=DT.hour*60*60*1000 + DT.minute*60*1000 + DT.second*1000
+ DT.milliseconds;
Writeln( 'обычный способ. время = ', time1-time0);
{--------------- рекурсивный способ ------------------}
DT:=CurrentDateTime;
time0:=DT.hour*60*60*1000 + DT.minute*60*1000 + DT.second*1000
+ DT.milliseconds;
for i:=1 to skoka do
rez:=NewFact(N);
DT:=CurrentDateTime;
time1:=DT.hour*60*60*1000 + DT.minute*60*1000 + DT.second*1000
+ DT.milliseconds;
Writeln( 'рекурсивный способ. время = ', time1-time0);
ka do
231

Writeln(N,'!=',rez);
Померяйте время. Получите результат. Подумайте. Сделайте выводы.
А теперь хочу, чтобы с графикой. Нарисуйте часы. С тремя стрелками –
часовой, минутной и секундной. И чтобы ходили, разумеется. И тикали.
Поглядим сначала на часы стоящие. Мы знаем время, в часах, минутах и
секундах. Задача - нарисовать часы и три стрелки в соответствующем
положении. Са
конец линии – центр окружности
здесь нас подкарауливает математика в лице геометрии и тригонометрии.
Теперь поглядим на часы идущие. Стрелки должны двигаться. Когда?
Когда изменилось время. Самая быстрая стрелка у нас секундная, значит
передвигать стрелки надо, когда изменилось число секунд. Как мы можем
узнать, когда изменилось число секун
– бесконечно отслеживать, который час (“который ча
фразеологизм, отслеживать будем секунды). Как только изменилось число
секунд – перерисовываем стрелки. Часовую стрелку и минутную мы при
этом, как правило, будем перерисовывать совершенно зря – часы и
минуты, скорее всего, не изменились – но куда деваться? И вообще,
компьютер железный, а мы устали.
В результате имеем чт
Нарисовать циферблат
Запомнить время
Цикл пока не надое
Если время изменилось
Стереть стрелки
Нарисовать стрелки
Запомнить время
Теперь обещанные геометрия с тригонометрией. Определяем константы,
все целые. Центр цифер
секундной стрелок – rh
естественно.
ми часы – окружность, Circle. Стрелки – линии. Один
, другой конец – где, собственно? А вот
д? Нам доступен только один способ
с” здесь просто
о-то такое:
ст
блата – cX, cY. Длины часовой, минутной,
, rm, rs. Всё измеряется в экранных точках,
232

Чтобы нарисовать стрелку, надо провести линию. Один конец линии –
центр циферблата, другой конец линии надо рассчитать. Вот формулы,
для случая, когда центр циферблата совпадает с началом координат:
X = R*Sin(α)
Y = R*Cos(α).
R – это, понятно, радиус, то есть длина стрелки. α – угол между
вертикалью и нашей стрелкой. С углом имеем некоторые нюан
с секундной стрелки. В любой момент на
секундах. Соображаем, что когда стрелка указывает на цифру три, иными
словами прошло пятнадцать секунд, стрелка образует прямой угол с
вертикалью. То есть, пятнадцать секунд соответствуют девяноста
градусам. Один секунда – пятнадцать градусов. Умножаем секунды на
пятнадцать, получаем угол в градусах. Это ничего, что я так неторопливо
объясняю?
Угол, как верно подмечено, в градусах. Но функция Sin на входе жуёт
только углы в радианах. Вспоминаем, что 2π радиан это 180 градусов.
Добавляем в ко
koef = 3.14159/180;
и записываем свеженькую формула для расчёта координат конца стрелки
x:=cX + Round(rs*Sin(6*s*koef));
y:=cY - Round(rs*Cos(6*s*koef));
S – количество секунд. Ко
почему в од
С минутной стрелкой получаем абсолютно тоже самое, с заменой секунд
на минуты:
x:=cX + Round(rm*Sin(6*m*koef));
y:=cY - Round(rm*Cos(6*m*koef));
С часовой придётся подумать, но совсем немного. Во-первых, девяносто
градусов соответствуют трём часам, то есть коэффициент пересчёта будет
нстанты коэффициент пересчёта
ординаты центра (cX,cY) мы учли. Подумайте,
ном случае плюс, а в другом минус.
м известен не угол, а время в
сы. Начнём
233

равен тридцати. Во-вторых, что три часа, что пятнадцать, циферблат у нас
только один и только на двенадцать часов. Учитывая вышесказанное,
получаем:
if h>12
then h:=h-12;
x:=cX + Round(rh*Sin(30*h*koef));
y:=cY - Round(rh*Cos(30*h*koef));
А теперь все вместе и хором! В смысле бодренько дописываем ещё
недописанное, и получаем вот такую программу:
program Clock;
uses
GraphABC, Utils, Crt;
const
cX=320;
cY=200;
rs=150;
rm=120;
rh=100;
koef=3.14159/180;
var
mode, driver
DT : DateTime;
mOld, hOld, sOld : real;
h : real;
x,y : integer;
i,j : integer;
begin
SetPenWidth( 3);
SetPenColor(clBlack);
Circle(cX,cY, rs+5);
sOld:=0;
repeat
DT:=CurrentDateTime;
: integer;
234

if DT.second <> sOld then begin
{ seconds }
SetPenWidth( 2);
SetPenColor(clWhite);
x:=cX + Round(rs*Sin(6*sOld*koef));
y:=cY - Round(rs*Cos(6*sOld*koef));
Line(cX,cY, x,y);
SetPenColor(clRed);
x:=cX + Round(rs*Sin(6*DT.second*koef));
y:=cY - Round(rs*Cos(6*DT.second*koef));
Line(cX,cY, x,y);
sOld:=DT.second;
{ munites }
SetPenWidth( 3);
SetPenColor(clWhite);
x:=cX + Round(rm*Sin(6*mOld*koef));
y:=cY - Round(rm*Cos(6*mOld*koef));
Line(cX,cY, x,y);
SetPenColor(clBlue);
x:=cX + Round(rm*Sin(6*DT.minute*koef));
y:=cY - Round(rm*Cos(6*DT.minute*koef));
Line(cX,cY, x,y);
mOld:=DT.minute;
{ hours }
h:=DT.hour;
if h>12
then h:=h-12;
SetPenWidth( 5);
SetpenColor(clWhite);
x:=cX + Round(rh*Sin(30*hOld*koef));
y:=cY - Round(rh*Cos(30*hOld*koef));
Line(cX,cY, x,y);
SetPenColor(clGreen);
x:=cX + Round(rh*Sin(30*h*koef));
y:=cY - Round(rh*Cos(30*h*koef));
235

Line(cX,cY, x,y);
hOld:=DT.hour;
end;
until KeyPressed;
end.
Проверьте. Добейтесь, чтобы часы тикали.
Проверили? Я человек честный, и как честный человек, честно
признаюсь, что в программе ошибка. Поставьте компьютерные часы на
12.50 или что-то вроде этого и посмотрите, где часовая стрелка. Правда
ведь, она должна быть немного в другом месте? Нашли ошибку?
Подсказ
Строку
h:=DT.hour;
Заменить на
h:=(DT.hour
Ну а зачем и почему, вы уж сами догадайтесь.
А сейчас, дети, мы выучим плохие слова, которые вы не должны
произносить ни в коем случае.
Или начнём по другому.
Они есть! И никуда от этого не деться. Всякие страшные злые вещи. И
лучше, если вы узнаете о них от ме
Страшных злых вещей несколько. Нет, их, конечно совершенно
немереное количество, это я расскажу о нескольких.
Начнём с не очень страшного. Задача – найти первый чётный элемент
массива. Мы её уже решили, вот так:
if ((a[i] mod 2) = 0) and (indexEven=0)
then indexEven:=i;
ываю:
*60 + DT.minute)/60;
Страшная сила
ня.
indexEven:=0;
for i:=1 to N do begin
end;
236

Что бросается в глаза? Первый чётный элемент мы уже давно нашли, но
продолжаем перебирать массив до конца. При этом мы вынуждены
производить дополнительную проверку, чтобы не испортить уже
найденный индекс. Некузяво как-то.
Проблема решается волшебным словом Break:
indexEven:=0;
for i:=1 to N do begin
if (a[i] mod 2) = 0 then begin
indexEven:=i;
Break;
end;
end;
Break прекращает выполнение цикла, вн
есть, если перв
цикл и выполнится. С одной стороны, это хорошо – даже обосновывать не
надо, почему. С другой стороны, это хорошо только при правильно
организованной программе – возможно, вы попутно делали с массивом
что-то ещё и в своей на
И по
вторю ещё раз, а Вы прочтите внимательно - Break прекращает
выполнение цикла, внутри которого он находится. Только этого цикла.
Если циклы у нас вложенные, то прекратится выполнение только
внутреннего. Если бы мы искали первый чётный элемент двумерного
массива, такой приём не сработал бы. Это несколько ограничивает
полезность Break.
Зато Break отлично работает и с циклами вида whi
В общем, пользуйтесь на здоровье, но помните…
Следующий экспонат кунсткамеры гораздо противнее. Называется
Continue. Действует он чуть-чуть наоборот. Цикл выполняется ровно
столько раз, сколько предполагалось изначально. Но вот внутри цикла,
всё что находится после Continue выпо
Иллюстрировать не бу
законченным злом. По сути, Continue – эмулятор Того, О Чём Нельзя
Говорить (в приличном программистском обществе). Я и не буду.
Приговор – забыть немедленно.
ым чётным элементом оказался третий, то ровно три раза
ивности ожидали полного прохода по массиву.
ду, поскольку считаю эту команду безусловным и
утри которого он находится. То
le и repeat-until.
лняться не будет.
237

Теперь два схожих на лицо монстрика из другой экологической ниши.
Первый - FillMem. Назначение этого ужаса – заполнить кусок памяти
вс111 равно чем. Точнее – параметров у процедуры три. Первый – всё
равно что, то есть любого типа. Второй – сколько байт заполнять. Третий
– чем заполнять.
Разумеется, FillMem абсолютно безразлично относится к вопросу, что и
чем он за
производится заполнение. Границы переменных игнорируются. Третий
параметр вообще может быть переменной символьного или байтового
типа или константой со значением в пределах байта.
Как его используют ленивые программисты? Например, имеется
двумерный массив (array[1..N,1..M] of integer), и надо его обнулить. Похорошему, делается это так:
for i:=1 to N do
a[i,j]:=0;
А по-
FillMem( a, SizeOf(a), 0);
Это очень-очень плохо. Я тоже так делаю, но меня хоть совесть мучает.
Последний номер нашей программы – Move. Вообще это самое убойное,
что есть в Паскале. С ним можно сломать что угодно. Создатели ABC
попытались ограничить причиняемое им зло, но всё же не до конца.
Происхождение процедуры тянется вглубь веков, куда-то к дино
Происходит она от одноимённой ассемблерной команды. Три
Первый параметр – адрес откуда. Второй параметр – адрес куда. Третий
параметр сколько слать, в байтах. Процедура берёт указанное количество
байтов начиная с первого адреса и копирует их по второму адресу. Адрес
– имя переменной или указатель на память. Проверок никаких.
Смысл имее
полняет. Первый параметр – прос
for j:=1 to M do
плохому вот так:
т только первый. По крайней мере,
то адрес, начиная с которого
заврикам.
параметра.
я так думаю.
238

В норме Move используется только совместно с указателями. Любое
другое применение чревато и заставляет задуматься, всё ли в порядке в
программе.
А в сочетании с указателями вещь чрезвычайно полезная и даже
необходимая.
Примеров не будет. Вам ещё рано.
Никаких новых слов
А в этом разделе никаких новых слов не будет. То
расставим старые.
Когда-то, давнымдвижением. Вот так:
if sc = ArrowLeft then begin
x:=x-1;
end;
if sc = ArrowRight then begin
x:=x+1;
end;
if sc = ArrowUp then begin
y:=y-1;
end;
if sc = ArrowDown then begin
y:=y+1;
end;
Похожие конструкции встречались нам часто и есть у них одна нехорошая
особенность. Нужную клавишу мы уже встретили, нужный выбор
сделали, но уп
хорошо. Но, по крайней мере, стрелка может быть нажата только одна. А
если условия не являются взаимоисключающими? Как, например при
выборе боковой клетки для хода в крестиках-ноликах. Могут быть
свободны хоть все четыре. Мы в этом случае всегда выберем последнюю.
Нам, конечно, всё равно – в данном конк
чаще, чем иногда) желательно выбрать первую из допустимых
альтернатив.
давно, рисовали мы таракана и управляли его
орно продолжаем проверять кл
авиши дальше. Это не очень
ретном случае. Но иногда (и даже
лько по-другому
239

Как это преодолеть? Вспомнить, что есть ещё и else, и, с его помощью
отсечь излишние проверки. В нашем случае очевидно так:
if sc = ArrowLeft then begin
x:=x-1;
end
else begin
if sc = ArrowRight then begin
x:=x+1;
end
else begin
if sc = ArrowUp then begin
y:=y-1;
end
else begin
if sc = ArrowDown then begin
y:=y+1;
end;
end;
end;
end;
Очевидно то он
задуматься, а это лишнее. И всё вр
бы спрямить.
Вспомним, что операторные скобки begin-end не являются
необходимыми, если после else следует только один оператор – а в нашем
случае всё, следующее за else, можно рассматривать как один очень
длинный условный оператор. Выкидываем только что вставленные
ы и end’ы, поднимаем else’ы на строчку выше и имеем:
begin’
if sc = ArrowLeft then begin
x:=x-1;
end else
if sc = ArrowRight then begin
x:=x+1;
о очевидно, но как-то уж очень кудряво. Заставляет
емя уползает куда-то вправо. Хотелось
240
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
