Игра Ним

Описание программы см, п.2.7.3.

Uses CRT; {Подключение библиотеки дополнительных процедур и функций для управления экраном}

const

MAXROW = 14; {Максимальное количество рядов} 

MAXCOL = 20; {Максимальное количество фишек в ряду}

type

ColType = array [1.. MAXROW] of Integer;

var

exit : Boolean; {Признак окончания работы}

change : Boolean; {Признак изменения условий игры}

nrow : Integer; { Количество рядов}

ncol : ColType; {Максимальное количество фишек по рядам}

col : ColType; {Текущее количество фишек по рядам}

{-----------------}

Procedure ShowField;

{Отображает на экране текущее состояние игрового поля}

const

FISH = #220; {Символ-указатель фишки} 

Х0 =4; {Левая колонка номеров рядов} 

X1 = 72; {Правая колонка количества фишек} 

X =20; {Левый край игрового поля}

var

i,j : Integer;

begin {ShowField}

for i := 1 to nrow do 

begin

GotoXY(X0,i+4) ;

write(i); {Номер ряда} 

GotoXY(Xl,i+4) ;

write (col [i] :2) ; {Количество фишек в ряду} 

for j := 1 to ncol [i] do {Вывод ряда фишек:} 

begin

GotoXY(X+2*j,i+4) ;

if j<=col[i] then write (FISH) else write ( ' . ' ) 

end

end 

end; {ShowField}

{---------------}

Procedure Prepare;

{ Подготовка данных и формирование экрана }

const

Header0='ИГРА НИМ';

Header1=' Вы можете взять любое число фишек из любого ряда.';

Header2='Выигрывает тот, кто возьмет последнюю фишку.';

Header3='Номер ряда';

Header4='Кол-во фишек';

var

i : Integer; 

begin {Prepare}

ClrScr;{Очищаем экран }

{Выводим заголовок:}

GotoXY( (80 -Length (Header0))div 2,1);

write (Header0) ;

GotoXY( (80-Length(Headerl))div 2,2);

write (Header1) ;

GotoXY( (80-Length(Header2))div 2,3);

writeln(Header2) ;

write (Header3) ;

GotoXY (80- Length ( Header 4 ) , 4 ) ;

write (Header4) ;

{Подготовить начальную раскладку: }

for i := 1 to nrow do col [i] := ncol [i] 

end; {Prepare}

{-----------------}

Procedure GetPlayerMove;

{Получить, проконтролировать и отобразить ход игрока }

const

ТЕХТ1 = 'Введите Ваш ход в формате РЯД КОЛИЧ ' +

'(например, 2 3 - взять из 2 ряда 3 фишки)';

ТЕХТ2='или введите 0 0 для выхода из игры; -1 0 для настройки

игры'; ТЕХТЗ=' Ваш ход: '; 

Y=20; {номер строки для вывода сообщений}

var

correctly : Boolean;{признак правильности сделанного хода}

xl,x2 : Integer;{вводимый ход} 

{-------------------}

Procedure GetChange;

{ Ввести новую настройку игры (количество рядов и количество фишек в каждом ряду} 

const

t1= 'НАСТРОЙКА ИГРЫ';

t2= '(ввод количества рядов и количества фишек в каждом ряду)'; 

var

correctly : Boolean; 

i : Integer; 

begin {GetChange} 

clrscr;

GotoXY((80-Length (t1)) div 2,1); 

write(t1);

GotoXY((80-Length(t2)) div 2,2); 

write(t2); 

repeat

GotoXY(1,3);

write('Введите количество рядов (максимум ',MAXROW,'): ');

GotoXY(WhereX-6,WhereY);

readln(nrow);

correctly := (nrow<=MAXROW) and (nrow>1);

if not correctly then

write (#7) 

until correctly; 

for i : = 1 to nrow do 

repeat

GotoXY(1,i+3) ;

write ('ряд',i,',количество фишек(максимум',MAXCOL,'):           ');

GotoXY (Wherex- 6, WhereY) ;

readlntncol [i] ) ;

correctly := (ncol [i] <=MAXCOL) and (ncol [i] >0) ;

if not correctly then

write (#7) 

until correctly 

end; {GetChange} 

{-------------------}

begin {GetPlayerMove}

ShowField; {Показать начальное состояние поля }

{ Сообщить игроку правила ввода хода: }

GotoXY ( (80 -Length (TEXT1) ) div 2,Y);

write (TEXT1) ;

GotOXY( (80-Length(TEXT2) ) div 2, Y+1);

write (TEXT2) ;

repeat

{ Пригласить игрока ввести ход: } 

GotoXY (1, Y+2) ;

Write (ТЕХТЗ ); {вывести приглашение и стереть предыдущий ход} 

GotoXY (WhereX-1 6, Y+2) ; {курсор влево на 16 позиций} 

ReadLn (x1 , х2 ) ; {ввести очередной ход} 

exit := x1=0; {контроль команды выхода} 

change := x1=-1; {контроль команды изменения} 

if not (exit or change) then 

begin

correctly := (x1>0) and (x1<=nrow) and 

(x2<=col [x1] ) and (x2>0) ; 

if correctly then

begin {ход правильный: }

col [x1] := col[x1]-x2; {изменить раскладку фишек} 

ShowField {показать поле} 

end 

else

write (#7) {ход неправильный: дать звуковой сигнал } 

end 

else

correctly := true {случай EXIT или CHANGE} 

until correctly; 

if change then

GetChange

end; {GetPlayerMove} 

{--------------------------------}

Procedure SetOwnerMove;

{ Найти и отобразить очередной ход программы }

{------------------}

Function CheckField : Integer;

{ Проверка состояния игры. Возвращает 0, если нет ни одной фишки (победа игрока) , 1 - есть один ряд (победа машины) и количество непустых рядов в остальных случаях } 

var

i,j : Integer; 

begin {CheckField}

j := 0;

for i := 1 to nrow do if col[i]>0 then inc(j);

CheckField := j

end; {CheckField}

{--------------------}

Procedure CheckPlay;

{ Контроль окончания игры }

var

i : Integer; 

begin {CheckPlay}

GotoXY(1,25) ;

write ( 'Введите 1, если хотите сыграть еще раз, 0 - выход:');

readln(i);

if i=l then change := true else exit := true 

end; {CheckPlay}

{--------------------}

Procedure PlayerVictory;

{ Поздравить игрока с победой и усложнить игру }

const

t1 = 'ПОЗДРАВЛЯЮ С ОТЛИЧНОЙ ПОБЕДОЙ!'; var i : Integer; begin

GotoXY( (80-Length(t1) ) div 2,24);

writeln(t1,#7) ;

for i : = 1 to nrow do

if ncol [i] <MAXROW then inc (ncol [i] ) ; 

CheckPlay

end; {PlayerVictory} 

{---------------------}

Procedure OwnVictory; 

{ Победа машины } 

const

t1 = 'ВЫ ПРОИГРАЛИ: СЛЕДУЮЩИМ ХОДОМ Я БЕРУ ВЕСЬ РЯД';

var

i : Integer; 

begin {OwnVictory} 

i := 1;

while col[i]=0 do inc(i); 

GotoXY( (80-Length(t1) ) div 2,24); 

write(t1,i,#7);

delay (2000); {задержка на 2 секунды} 

col [i] := 0; 

ShowField; 

CheckPlay 

end; {OwnVictory}

{--------------------}

Procedure ChooseMove;

{ Выбор очередного хода }

const

BIT = 6; {количество двоичных разрядов} 

type

BitType = array [1..BIT] of Integer; 

var

ncbit : array [1..MAXROW] of BitType;

i,j,k : Integer;

nbit : BitType; 

{------------------}

Procedure BitForm(n : Integer; var b : BitType);

{ Формирует двоичное представление b целого числа n }

var

i : Integer; 

begin {BitForm}

for i := BIT downto 1 do 

begin

if odd(n) then b[i] := 1 else b[i] := 0;

n := n shr 1 

end 

end; {BitForm}

{------------------}

begin {ChooseMove}

{Найти двоичное представление количества фишек во всех рядах:} 

for i := 1 to nrow do BitForm(col [i] ,ncbit [i] ) ; 

{Найти сумму разрядов по модулю 2:} 

for i := 1 to BIT do 

begin

nbitti] := 0;

for j := 1 to nrow do nbitti] := nbitti] xor ncbit [j / i] 

end;

{Найти i = старший ненулевой разряд суммы} 

i := 1;

while nbitti] =0 do inc(i); 

if i>BIT then

{Опасный вариант} 

begin j := 1;

while col[j]=0 do inc(j); {найти ненулевой ряд} 

k := 1 {взять из него 1 фишку} 

end 

else

{Безопасный вариант} 

begin j := 1;

while ncbit [j,i]=0 do inc(j); {найти нужный ряд} 

for i := i to BIT do 

if nbit[i] =1 then

ncbit [j,i] := ord (ncbit [j , i] =0) ; {инверсия разрядов} 

k := 0;

for i := 1 to BIT do 

begin

if ncbit [j,i]=1 then inc(k); 

if i<BIT then k := k shl 1 

end;

k := col [j] - k 

end;

GotoXY(1,23);

write('Мой ход: '); 

GotoXY(WhereX-8,WhereY); 

delay (.1000) ; 

write (j, ' ' ,k) ; 

col[j] := col[j] -k 

end; {ChooseMove}

{-------------------}

begin {SetOwnerMove} 

case CheckField of {проверить количество непустых рядов} 

0 : PlayerVictory; {все ряды пусты - Победа игрока} 

1 : OwnVictory; {один непустой ряд - победа машины} 

else

ChooseMove; {выбрать очередной ход} 

end;{case}

end; {SetOwnerMove}

{--------------}

begin {Главная программа} 

nrow : = 3 ; { Подготовить игру } 

ncol [1] := 3; { на поле из трех } 

ncol [2] := 4; { рядов фишек } 

ncol [3] := 5; 

repeat{ Цикл изменения условий игры }

Prepare; { Подготовить экран } 

repeat { Игровой цикл }

GetPlayerMove; { Получить ход пользователя } 

if not (exit or change) then

SetOwnerMove { Определить собственный ход } 

until exit or change 

until exit 

end.