Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Самоучитель игры на Паскале. АВС и немного Турбо

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
факториала. Но нас это не спасёт – компьютер всё равно считает слишком быстро. Другой вариант – посчитать один и тот же факториал не один раз, а десять. Или сто. Лично у меня ненулевые значения появились при миллионе повторений, а программа приобрела вот такой вид:
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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]