Ігровий Паскаль

 | 17.11

 Насамперед

Для початку давайте передбачимо непередбачуване :-), тобто детально вивчимо всі можливі ігрові ситуації та надзвичайні події, які можуть виникнути в процесі гри — цим треба зайнятись перед початком проектування.

Перше — в нашому варіанті грають двоє людей. Потім, звичайно ж, можна додати в якості суперника комп’ютерного опонента, але це потім. Гра консольна. Проект представлятиме собою класичні хрестики-нулики з розміром ігрового поля 3х3. Виграє той, хто перший закреслить одну з вертикальних, горизонтальних або діагональних ліній. Зверніть увагу на те, що цілком можлива і нічия. Це все, що треба знати перед початком.

Геймплей

Як виглядатиме початковий екран? Можна «розмалювати» його зірочками, доларами, собачками та іншими символами. Я не буду цього зараз робити. Давайте просто дамо зрозуміти користувачу, що за гру він запустив і вкажемо версію програмного продукту. Думаю, цього вистачить. Для більшого реалізму додамо можливість гравцям ввести їхні імена і вибрати, хто чим буде грати. Дефолтний варіант: один грає хрестиками, інший — нуликами. Якщо ж це не задовільнятиме, то буде змога обрати індивідуальний символ. Тут же виникає проблема — а якщо вони виберуть однакові символи для гри? Гра тоді перестане бути грою. Через це необхідно застерегти і заборонити їм вибирати однакові символи. З двома іменами ситуація не буде критичною для програми, та все ж краще також не дати гравцям назвати себе одним і тим же іменем.

Ввели вони імена, обрали значки для гри. Після цього буде показано ігрове поле. Внизу бажано вказати, хто саме здійснює хід в даний момент. А от яким чином ходити? На роль поля 3х3 краще за все підійде «калькуляторна» частина клавіатури. Наприклад, після передачі програмі числа 1 закреслиться ліва нижня клітинка, 9 — права верхня, 5 — центральна і т.д. Приблизний варіант інтерфейсу зображено на рис. 1.

Програмування

Почнемо писати код. Можна сховати все під капотом, винісши процедури в окремий модуль. Глобальні змінні, котрі нам знадобляться для реалізації проекту:

 Table:array [1..3,1..3] of char; {ігрове поле у вигляді двовимірного масиву}

 Name1, Name2:string; {імена гравців}

 Symbol1, Symbol2:char; {символи, якими ці гравці будуть ходити. Змінну Symbol1 ми ще використаємо в інших цілях, про це згодом.}

 i,j:integer; {керуючі змінні циклів}

 Victory, Player1:boolean; {Victory — для перевірки, чи часом хтось не переміг, Player1 дасть програмі знати, який гравець здійснює хід}

 Moves:byte = 0; {лічильник ходів}

 Основний текст програми я зробив достатньо лаконічним і зрозумілим:

begin

BeginGame; {початок гри}

while not Victory do {поки не досягнуто перемоги, виконуємо наступне…}

begin

 Move; {хід гравця}

 CheckVictory; {дивимось, чи хтось не переміг після ходу}

 ShowTable; {показуємо поле}

 inc(Moves); {збільшуємо на 1 лічильник ходів}

 if Victory then EndGame; {якщо перемога, то гра завершується}

 if Moves = 9 then Draw {а от якщо після дев’яти ходів виграшу не досягнуто, тоді нічия. 10 ходів на полі 3х3 зробити важкувато 🙂 }

end {кінець циклу}

end.

Тепер розберемо детально процедури Draw, EndGame, Move, ShowTable, CheckVictory, BeginGame. Почнемо з найпростіших — Draw і EndGame, котрі відрізняються тільки тим, що виводять різний текст. Тому приводжу приклад тільки однієї.

procedure EndGame;

begin

writeln(‘***Game Over***’); {виводимо оголошення про закінчення гри (відповідно для іншого випадку повідомляємо про нічию)}

halt {завершуємо виконання програми і передаємо керування операційній системі}

end;

ShowTable це не що інше, як вивід двовимірного масиву на монітор.

procedure ShowTable;

begin

Clrscr; {оскільки поле буде демонструватись більш ніж один раз, непогано би було очистити екран перед наступним показом. І не забудьте підключити модуль Crt!}

for i := 1 to 3 do

 begin

 for j := 1 to 3 do

 write (Table[i,j]:3);

 writeln {Стандартний шаблон. Таким чином виводяться всі двовимірні масиви. Формат виводу — три клітинки — я поставив з метою розтягнути поле, щоб воно набуло форми квадрата}

 end

end;

Перевіряти перемогу можна двома способами (принаймні ці два видаються найбільш логічними). Перший — математичний підхід — створення ще одного масиву цілочисельних цифр. Заповнюватиметься він паралельно з масивом символів (наше поле). Хрестику відповідатиме одна цифра, нулику — інша. Будуть певні початкові значення елементів поля. А тоді перевірити суму по рядках, стовпцях і діагоналях масиву і, проаналізувавши результат, перевірити істинність перемоги. Такий метод має переваги в швидкості, порівняно з тим, про який піде мова кількома рядками нижче, і займає менший об’єм. Також він є універсальним, тобто підходить для поля 3х3, 10х10 чи навіть 100х100. Єдиний вагомий мінус — потрібно виділяти пам’ять для ще одного масиву.

В моєму варіанті втілено інший варіант. Я просто-напросто перелічив всі можливі виграшні комбінації, і програма щоразу “проганяє” масив, шукаючи переможну комбінацію. Якщо така знайдена, логічна змінна Victory отримує значення true. В основному тексті програма побачить, що настала перемога і перейде до виконання EndGame.

procedure CheckVictory;

begin

if

 (Table[1,1] and Table[1,2] and Table[1,3]=Symbol1)

or (Table[2,1] and Table[2,2] and Table [2,3]=Symbol1)

{далі йдуть наступні варіанти, всього їх сім — три вертикалі, три горизонталі і дві діагоналі. Горизонталь, закреслена зліва направо, нічим не відрізняється від такої ж, закресленої справа наліво 🙂 }

then Victory := true;

{Опісля розташований аналогічний блок коду, тільки перевіряємо вже нулики (чи що там буде), тому змінено Symbol1 на Symbol2}

end;

Як користувач ходитиме, я вже пояснив. На рис. 2 показано, як воно виглядає в процесі. Зараз наводжу код підпрограми:

procedure Move;

var MoveDone:boolean = false; coord:byte; {дві змінні, котрі потрібні тільки всередині процедури. MoveDone — процедура не завершиться, доки хід не буде зроблено. coord зберігає в собі цифру від 1 до 9 — координати поля}

begin

В цій процедурі, як і в попередній, є два блоки коду, що відрізняються лише тим, що в другому замість Name1 — Name2, а замість Symbol1 — Symbol2.

if Player1 then {якщо це перший гравець}

repeat {повторювати поки…}

write(Name1,’, номер клітинки: ‘);

readln(coord); {дізнаємось, куди треба ходити}

case coord of

1:if Table[3,1] = ‘.’ then begin Table[3,1] := Symbol1; MoveDone := true end; {такий собі міні-конвертер, який спершу перевірить, чи ніхто до цього не походив на клітинку, а потім відповідно до введеної гравцем цифри замінить певний елемент масиву на інший символ. І врешті-решт підніме наш прапорець, котрий відповідає за завершення циклу repeat … until}

{далі перелік чисел продовжується до 9}

9:if Table[1,3] = ‘.’ then begin Table[1,3] := Symbol1; MoveDone := true end

end; {кінець case-конвертера}

until MoveDone {умова виходу з циклу — вдалий хід гравця}

Повторюсь, далі йде схожий блок і, нарешті, кінець процедури:

MoveDone := false; {опускаємо прапорець в початкове положення}

Player1 := not Player1; {змінюємо гравця на протилежного}

end;

Завершальний етап — опис того, як гра починається :-). В цій процедурі є багато питань, що програма ставитиме гравцям, тут же можна реалізувати псевдографічний інтерфейс, намалювати красиве лого гри, створити меню та багато іншого.

Цю підпрограму пишу від і до, без пропусків частин коду, бо тут все таке важливе, що не можна щось пропустити :-).

procedure BeginGame;

begin

writeln(‘Хрестики-нулики’); {це в мене таке графічне оформлення 🙂 }

writeln(‘версія 0.0.1 альфа’); {продовжуємо оформлення}

writeln;

repeat

 repeat

 write(‘Ім’я першого гравця: ‘);

 readln(Name1); {дізнаємось ім’я}

 if length(Name1) > 20 then writeln(‘Ім’я не має перевищувати 20 символів!’);

 until length(Name1) <= 20; {я поставив умову на те, що ім’я не може перевищувати 20 символів}

 repeat

 write(‘Ім’я другого гравця: ‘);

 readln(Name2); {ті самі дані питаємо в другого гравця}

 if length(Name2) > 20 then writeln(‘Ім’я не має перевищувати 20 символів!’);

 until length(Name2) <= 20;

 if Name1 = Name2 then writeln(‘Введіть різні імена!’)

until Name1 <> Name2; {бажано, щоб їх звали по-різному. До речі, при вводі двох однакових імен можна перейменувати їх в Коля№1 і Коля№2, але це вже зробите самі}

repeat

 write(‘Стандартні значки? (y/n)’);

 readln(Symbol1); {поки що змінна Symbol1 без діла, тому доручаємо їй збереження відповіді на запитання}

 if (Symbol1 <> ‘y’) and (Symbol1 <> ‘n’) then writeln(‘Некоректна відповідь!’); {захист від хакерів 🙂 }

until (Symbol1 = ‘y’) or (Symbol1 = ‘n’);

if Symbol1 = ‘y’

then begin

 Symbol1 := ‘x’;

 Symbol2 := ‘o’ {якщо гравці погодились на стандартний варіант, тоді в якості символів будуть букви “х” та “о”}

 end

else begin {коли ж дефолтний варіант не влаштовує, вони можуть вибрати собі щось оригінальніше}

 repeat

 write(‘Значок першого гравця: ‘);

 readln(Symbol1);

 write(‘Значок другого гравця: ‘);

 readln(Symbol2);

 if Symbol1 = Symbol2 then writeln(‘Введіть різні значки! Не можна використовувати крапку!’);

 until (Symbol1 <> Symbol2) and (Symbol1 <> ‘.’) and (Symbol2 <> ‘.’);

 end;

 

for i :=1 to 3 do

 for j := 1 to 3 do

 Table[i,j] := ‘.’; {тут у нас лежить важливий вкладений цикл з параметром. Він робить наше поле пустим. Якщо в якості порожня клітинка це крапка, то гравцеві не можна вводити крапку ні в якому разі}

ShowTable {демонструємо щойно створене ігрове поле}

end;

 

Р.S. Незважаючи на «невеличкість» прикладу, на його основі можна навчитись багато чому, зокрема — плануванню програми, лаконічності написання коду. — Прим. ред.

Robo User
Web-droid editor

Додати коментар

Ваша email адреса не буде опублікована.