Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Самоучитель игры на Паскале. АВС и немного Турбо
.pdf
begin
end;
{--------------------------------------------------------------------}
{эта процедура стирает курсор}
procedure HideCursor( x,y : integer);
{параметры – номера столбца и строки}
begin
end;
{--------------------------------------------------------------------}
{эта процедура рисует поле}
procedure DrawField;
begin
end;
{--------------------------------------------------------------------}
{эта процедура проверяет завершение игры}
{результат: крестики выиграли - 1}
{нолики выиграли - 2}
{ничья - 3}
{игра не окончена - 0}
procedure Check( a : TArray3x3;
var result : integer);
begin
end;
{--------------------------------------------------------------------}
{чего-то там дела
procedure Finale( result : integer);
begin
end;
{--------------------------------------------------------------------}
{а это ве
{на выходе – столбец и строка, куда ставить нолик}
procedure AI( a : TArray3x3;
var xkuda, ykuda : integer);
begin
end;
{--------------------------------------------------------------------}
begin
ликий и ужасный Искусственный Интеллект}
ет}
171

{тут всё самое главное}
end.
Вот такой симпатичный скелетик нашей программы.
Поле
Рисуем поле. Три на три. Не просто, а очень просто. Главное, что это у нас
процедура без параметров.
procedure DrawField;
begin
SetPenColor( clGreen);
SetPenWidth( 5);
{горизонтали}
Line( x0, y0+s, x0+3*s, y0+s);
Line( x0, y0+2*s, x0+3*s, y0+2*s);
{вертикали}
Line( x0+s, y0, x0+s, y0+3*s);
Line( x0+2*s, y0, x0+2*s,y0+3*s);
end;
Немного минималистки. Дорисуйте, чего не хватает. Добавьте по вкусу. И
немедленно пр
процедуры.
оверьте! То есть добавьте в основную программу вызов это
Крестик
и нолик
Здесь чуть сложнее – процедуры с параметром. Начнём с нолика. Что
использовать? Безвариантно – Circle. А где будет его центр? В центре
клетки, понятно.
procedure Zero( x,y : integer); {параметры – столбец, строка}
begin
SetPenColor( clRed);
SetPenWidth( 5);
Circle( x0+(x-1)*s+(s div 2), y0+(y-1)*s+(s div 2), (s div 2) -10);
end;
172

Пояснения нужны? Тогда поясняем. Пояснения для координаты X. Для Y
всё то же самое, что естественно.
x0+(x-1)*s+(s div 2). X0 – это самое начало. Его прибавлять надо всегда,
это не обсуждается. Если мы в третьей, например, клетке, нам надо
прибавить размеры двух предыдущих и половину размера от текущей
клетки, чтобы попасть ровно в центр третьей.
(s div 2) -10). Зачем нужна десятка? Чтобы бы
Например вызвав из основной тушки программы Zero
вполне прилично.
Крестик будет чуть посложнее. Сначала пишем безо всякой красоты.
procedure Cr( x,y : integer); {параметры – столбец, строка}
begin
SetPenColor( clRed);
SetPenWidth( 5);
Line( x0+(x-1)*s, y0+(y-1)*s,
x0+(x-1)*s+s, y0+(y-1)*s+s);
Line( x0+(x-1)*s+s, y0+(y-1)*s,
x0+(x-1)*s, y0+(y-1)*s+s);
end;
Теперь неплохо бы всё это проверить. Надо сразу отбросить наивную веру
в осмысленность и правильность на
ерунду с кучей ошибок. Соответственно, проверять написанное надо как
можно раньше. Написали процедуру – тут же проверили. Написать всё
сразу и потом всё сразу проверять – глупость несусветная и
необсуждаемая.
Так что проверяем. Там, где у нас будет потом самая главная программа,
пишем:
DrawField;
Zero( 1, 3);
Cr( 3,1);
Вспоминаем, что пер
– номер строки.
вый параметр у нас но
писанного нами текста. Пишем мы
ло красиво. Проверьте.
(1,1); По моему,
мер столбца, второй параметр
173

А почему мы вызвали с параметрами (1,3), а не (1,1)? Наверное, для того,
чтобы отловить случай перепутанных местами координат. Ошибка
совершенно обычная и несимметричные параметры очень хорошо её
ловят.
Всё работает? А я пугал, что работать без проверки не будет? Ну так я
ведь уже всё проверил…
Работать-то оно работает, но как-то не
разм
етку поля. Надо бы крестик слегка пообкусать по краям. По всем
аккуратно. Крестик наезжает на
четырём. Ход мысли должен быть примерно таким – левый верхний край
должен быть короче, то есть находиться правее и ниже. Значит, к обоим
координатам надо что-то прибавить. Прибавить, конечно, что-то
одинаковое. А если оди
наковое – значит нужна константа. А поскол
ьку
константа эта применяться будет только внутри процедуры – то внутри
процедуры её и объявим. Получаем:
procedure Cr( x,y : integer); {параметры – столбец, строка}
const
d = 5;
begin
SetPenColor( clRed);
SetPenWidth( 5);
Line( x0+(x-1)*s+d, y0+(y-1)*s+d,
x0+(x-1)*s+s-d, y0+(y-1)*s+s-d);
Line( x0+(x-1)*s+s-d, y0+(y-1)*s+d,
x0+(x-1)*s+d, y0+(y-1)*s+s-d);
end;
Не то, что бы очень, но туда-сюда. Проверить и подобрать D по вкусу!
тута
Курсор и чтобы бегал
Сначала курсор, потом чтобы бегал. Курсор будет внизу клетки и, уже
ясно, чуть короче. Вот так:
procedure DrawCursor( x,y : integer); {параметры – столбец, строка}
const
d = 5;
begin
174

SetPenColor( clBlue);
SetPenWidth( 5);
Line( x0+(x-1)*s+d, y0+(y-1)*s+s-d,
x0+(x-1)*s+s-d, y0+(y-1)*s+s-d);
end;
{--------------------------------------------------------------------}
procedure HideCursor( x,y : integer); {параметры – столбец, строка}
const
d = 5;
begin
SetPenColor( clWhite);
SetPenWidth( 5);
Line( x0+(x-1)*s+d, y0+(y-1)*s+s-d,
x0+(x-1)*s+s-d, y0+(y-1)*s+s-d);
end;
А как проверить? А вот так (дописываем в хвост).
DrawField;
Zero( 1, 3);
Cr( 3,1);
DrawCursor(3,2);
Readln;
HideCursor(3,2);
Деликатный вопрос – а зачем отдельные процедуры для рисования и
стирания? А вот захотелось мне так. Размер небольшой, можно и
продублировать (почти), зато наглядно. Не нравится – напишите одн
универсальную процедуру с указанием цве
В статике работает. А теперь в динамике, чтобы бегало. Этот цикл у нас
будет в основной программе. Выглядеть сначала он будет так же, как и
цикл раньше для бегающего таракана. Прочитали клавишу – стёрли
таракана, где стоит, поменяли позицию, нарисовали таракана. Не забыть
только инициализировать по
нарисовать курсор.
зицию таракана/курсора перед циклом и
та рисования.
у
175

А когда цикл закончится? Когда закончится игра (для этого у нас
припасена переменная gamover). Или когда игроку надоест и он нажмёт
Esc.
Ещё раз – ЭТО у нас будет в теле основной программы.
DrawField;
x:=1;
y:=1;
DrawCursor(x,y);
gamover:=false;
repeat
if KeyPressed then begin
ch:=ReadKey;
if Ord(ch) = 0 then begin
ch:=ReadKey;
if Ord(ch) = ArrowLeft then begin
HideCursor(x,y);
x:=x + 1;
DrawCursor(x,y);
end;
if Ord(ch) = ArrowRight then begin
HideCursor(x,y);
x:=x - 1;
DrawCursor(x,y);
end;
if Ord(ch) = ArrowDown then begin
HideCursor(x,y);
y:=y + 1;
DrawCursor(x,y);
end;
if Ord(ch) = ArrowUp then begin
HideCursor(x,y);
y:=y - 1;
DrawCursor(x,y);
end;
end;
176

if Ord(ch) = SpaceBar then begin { пробел }
Cr( x,y); { делаем ход}
end;
until (Ord(ch) = 1) or gamover; { 1 – это Esc }
Finale(result);
О главном. Не перепутайте – проверка нажатия клавиши пробел
находится ВНЕ обработки клавиш возвращающих при нажатии ноль. Это
не моя прихоть. Пробел – это честная, порядочная клавиша, она сразу
возвращает 32. Вам не нравится? И мне не нравится. Но так вот он
о на
самом дел устроено. В TP было проще. А в Delphi будет проще.
Замечание в сторону
. В финальную процедуру мы передаём результат как
параметр. Хотя могли бы сэкономить и воспользоваться соответствующей
переменной, ведь её из процедуры видно. Самой процедуре всё равно,
текст её от этого не изменился бы ни на копейку. Но – глобальные
переменные есть зло! И мы будем их уничтожать, там, где встретим.
Теперь по существу. Курсор рез
во бегает. Это хорошо. Но бегает где
попало. Это плохо. Никаких ограничителей на выход за пределы поля у
нас нет. Надо добавить.
Ещё это называется “защита от дураков”. А почему мелким шрифтом? А чтобы дураки не прочитали.
Для стрелки влево это будет выглядеть так:
if (Ord(ch) = ArrowLeft) and (x>=2) then begin
HideCursor(x,y);
x:=x - 1;
DrawCursor(x,y);
end;
Для трёх оставшихся случаев первая строка условного оператора
модифицируется соответственно:
if ({направо}) and (x<=2) then begin
if ({вверх }) and (y>=2) then begin
if ({вниз }) and (y<=2) then begin
177

Аккуратненько внесите соответствующие изменения. Проверьте.
Делаем ход
Теперь придётся немного подумать. Сделать ход в выбранное место
несложно. Стрелками перемещаемся в нужную клетку, нажимаем пробел
и рисуем крестик.
if Ord(ch) = SpaceBar then begin
Cr( x,y);
end;
Только одна проблема. Даже не проблема – обстоятельство, являющееся
нормой жизни программиста. Есть объекты на экране – курсор, крестики,
нолики. И есть отображение этих объ
на экране. И есть переменные X и Y, помнящие, где курсор сейчас
находится. Если немного поразмыслить, придём к выводу, что эти две
переменные и есть, в сущности, курсор с точки зрения программы.
Если есть крестик на экране, должен быть и крестик в программе. Мы об
этом уже позаботились, объявив массив A, размером три на три. Если в
соответствующей ячейке массива ноль – зн
нет. Если там единица – значит там крестик, если двойка – то нолик.
А дальше как всегда. Не забыть проинициализировать массив нулями
перед началом главного цикла:
DrawField;
x:=1;
y:=1;
DrawCursor(x,y);
Gamover:=false;
for i:=1 to 3 do
for j:=1 to 3 do
a[i,j]:=0;
А теперь сосредоточьтесь. Первый индекс мас
за номер строки, второй индекс – за номер столбца. Переменная X не
менее традиционно отвечает за горизонталь, то есть за номер столбца.Y
ектов в программе. Так, есть курсор
ачит в этой клетке ничего пока
сива традиционно отвечает
178

это, соответственно, номер строки. И ещё одно – а если клетка уже
занята? Живёт там уже кто-то – крестик или нолик? Значит, сначала надо
проверить, пустая ли клетка.
Итого:
if Ord(ch) = SpaceBar then begin
if a[y,x]=0 then begin
Cr( x,y);
a[y,x]:=1;
end;
end;
А не выиграл ли кто?
Вообще-то, было бы очень неплохо, если бы, прежде че
извилистым течением моей мысли, Вы (и вы
запрограммировать соответствующую часть программы самостоятельно.
Не получилось самостоятельно – идём дальше совместно.
Вот заготовка процедуры проверки на завершение игры. Смотрим на неё и
думаем. Причём, думаем обо всём сразу.
{эта процедура проверяет завершение игры}
{результат: крестики выиграли - 1}
{нолики выиграли - 2}
{ничья - 3}
{игра не ок
procedure Check( a : TArray3x3;
var result : integer);
begin
end;
Думаем об объявлени
вспоминаем, что это и зачем.
Думаем, а откуда будет вызываться эта процедура. Думаем об этом даже
раньше, чем мы эту процедуру написали, и это правильно. Мне кажется,
что выз
на её результат.
ончена - 0}
и процедуры и о типе параметра A. Или просто
ывать её надо сразу после хода крестиком. И как-
) попробовали бы сначала
м следовать за
то отреагировать
179

Как, кстати, отреагировать? Если её результат ноль, то никак. Игра
продолжается. Впрочем, “никак” не совсем подходящее слово. Реакцией
будет ход ноликов. А вот во всех трёх остальных случаях реакция, в
сущности одинаковая – завершить игру, выйти из основного цикла и чтото такое специальное сделать. Потом сделать, после цикла. А что означает
выйти из цикла? Gamover:=true. А как мы узнаем после цикла, че
закончилась? А мы дадим себе честно
переменной result до выхода из цикла. Не испортим, разумеется, в том
только случае, когда gamover истинно.
if sc = SpaceBar then begin
if a[y,x]=0 then begin
Cr( x,y);
a[y,x]:=1;
Check( a, result);
if result = 0 then begin
{жизнь продолжается}
end;
if (result=1) or (result=2) or (result=3) then begin
gamover:=true;
end;
end;
end;
Вроде бы выглядит не
на текущий момент – что же мы такое напишем в нашу процедуру
проверки?
А как могут выиграть крестики? В смысле, как крестики при этом могут
быть расстановлены? Три крестика в первом ряду. Или три во втором.
Или три в третьем. В пер
диагонали из левого верхнего угла в правый нижний угол. Или из левого
нижнего в правый верхний. Каж
вариантов.
Приступаем
if ((a[1,1]=1) and (a[1,2]=1) and (a[1,3])=1)) or {первая строка}
((a[2,1]=1) and (a[2,2]=1) and (a[2,3])=1)) or {вторая строка}
плохо, правдоподобно. Начинаем думать о главном
вом, втором или третьем столбике. Наконец, по
е слово, что не испортим значение
ется, ничего не пропущено. Итого восемь
м игра
180
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
