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

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

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