Zeroes and Ones - портал высоких технологий - Пишем игру Крестики-нулики 3х3
Новости
Главная
Software
Hardware
Mobile
Наши новости
Техника
Электроника
Программирование
Delphi
HTML / CSS
*NIX
Дополнительно
О нас
Контакты
Последние новости

 


Опросы

Я за процессоры:



Популярное


Реклама


Партнеры
Пишем игру Крестики-нулики 3х3
Автор Albinos_X   
29.08.2009 г.
    
У многих начинающих программистов появляются желания опробовать себя в написании какой-либо игры. В том числе, некоторые преподаватели, дают задания в качестве Курсовой работы, написать какую-либо игру. Данная статья не является концептом в написании игр, в том числе и Крестиков-Нуликов, а является всего лишь небольшим примером (и может быть далеко не самым лучшим) того, как можно решить данный вопрос.
 
ШАГ 1. Определимся «чего хотим».

Определим, что мы хотим увидеть в конце нашей работы. Не будем задаваться на «навороты», поэтому определим простейший интерфейс, в который будет входить:
   1. игровое поле,  
   2. возможность начала новой игры,
   3. Возможность переключения режимов игры на 2 игрока и на 1-го игрока (второй компьютер),
   4. Возможность изменения сложности игры с компьютером
   5. Отображения текущего хода, количества сыгранных игр и победителя.
Теперь необходимо определиться, как мы будем реализовывать игровое поле. Подходов и вариантов здесь можно использовать множество, начиная от использования банальных кнопок (TButton, TSpeedButton, TBitBtn), использования TPaintBox, рисования непосредственно на канве формы и т.д., и наконец, написанием собственного компонента. Остановлюсь в реализации заданного примера на использовании TImage, для показа простейшего примера работы с графикой.

ШАГ 2. Набросаем форму.

Итак, с тем, что мы хотим увидеть, мы определились. Теперь создаём форму, бросаем на форму MainMenu1, Panel1 – будет служить контейнером для Image, Image1 – непосредственно игровое поле, RadioGroup1 – выбор количества игроков, RadioGroup2 – выбор сложности игры, Label1 – будет показывать сколько игр сыграно, Label2 – будет отображать кто ходит, Label3 – будет отображать кто победил. Форма у нас принимает, приблизительно следующий вид:
 

 
ШАГ 3. Определяемся со «структурой» программы.

У нас будет 1 форма, к ней модуль, также создадим модули Graph, который будет у нас отвечать за работу с графикой, и модуль Logik, который будет отвечать за логику компьютера, проверку выигрыша игроков и другие логические операции.

ШАГ 4. Подготовим рисунки игрового поля, крестика и нулика.

Набросаем нужного размера игровое поле, крестик и нулик по отдельности. Для этого можно использовать любой имеющийся у Вас под рукой графический редактор. Загружаем сделанные рисунки в ресурс и сохраняем его в папке с разрабатываемой нами программой под именем Res.res. Останавливаться на работе с ресурсами не будем. Не забываем также подключить этот ресурс к модулю Graph.

{$R Res.RES}

ШАГ 5. Описываем работу с графикой.

Так как мы будем всё рисовать, а рисовать мы будем заранее подготовленные рисунки, которые будем выгружать из ресурса, нам будет необходимо хранить эти снимки в переменных, при чём желательно в глобальных, т.к. каждый раз выгружать из ресурса нужную картинку не считаю целесообразным, поэтому объявляем глобальные переменные:

var
Bmp_temp : TBitmap; // для хранения текущего состояния поля
Bmp_Pole : TBitmap;
// снимок чистого поля
Bmp_X : TBitmap; // снимок креста
Bmp_O : TBitmap; // снимок нулика

Теперь пишем процедуры и функции. Так как мы используем графику из ресурса, нам необходимо её выгрузить. Пишем процедуру, которая у нас будет выгружать картинки из ресурса:

Function LoadResurce(Bmp:TBitmap; NameBitmap:String):Boolean;
begin
Result:=True;
Bmp.LoadFromResourceName(HInstance,NameBitmap);
Result:=False;
end;


Не забываем, что это всего лишь маленькая деталь, нам ещё нужно проинициализировать глобальные переменные (т.е. снимки поля и игровых фигур):

// возвращает True, если что-то прошло не удачно
Function CreateBmp:Boolean;
begin
Result:=True;
// создаём витмапы
Bmp_temp := TBitmap.Create;
Bmp_Pole := TBitmap.Create;
Bmp_X := TBitmap.Create;
Bmp_O := TBitmap.Create;
// загружаем ресурсы
if LoadResurce(Bmp_Pole, 'BITMAP_3') then
   Exit;
if LoadResurce(Bmp_O, 'BITMAP_4') then
   Exit;
if LoadResurce(Bmp_X, 'BITMAP_5') then
   Exit;
Bmp_temp.Height:=300;
Bmp_temp.Width:=300;
Bmp_temp.Canvas.StretchDraw(Bmp_temp.Canvas.ClipRect, Bmp_Pole);
Result:=False;
end;


Теперь нам также нужно предусмотреть освобождение памяти, после окончания работы с программой:

procedure DestroyBmp;
begin
Bmp_temp.Free;
Bmp_Pole.Free;
Bmp_X.Free;
Bmp_O.Free;
end;


Предусмотрим процедуру очистки игрового поля:

Procedure ClearPole;
begin
Bmp_temp.Canvas.StretchDraw(Bmp_temp.Canvas.ClipRect,Bmp_Pole);
end;


Так как пользователь будет щёлкать мышью по полю, нам будет необходимо вычислить, куда он попал, а точнее в какую клетку:

// вычисление клетки в которую попал пользователь
Function CalcClick(Height, Width, X, Y : Integer): TPoint;
begin
Result.X:=X div (Width div 3) + 1;
Result.Y:=Y div (Height div 3) + 1;
end;


Написанная функция возвращает координаты клетки, в которую попал пользователь. Координаты у нас будут измеряться от 1. Не забываем также и о том, что нам нужно ещё и нарисовать сделанный пользователем ход:

// рисование хода
procedure StepBmp(Step:TPoint; O_X:Boolean);
var
X, Y : Integer;
tmp:TBitmap;
begin
if O_X then
tmp:=Bmp_X
else
tmp:=Bmp_O;
X:=(Step.X-1)*100+50-(tmp.Width div 2);
Y:=(Step.Y-1)*100+50-(tmp.Height div 2);
Bmp_temp.Canvas.Draw(X,Y,tmp);
end;


Здесь переменная O_X, как Вы уже поняли, определяет, чем сделан ход, крестом или нуликом. Ну и напоследок, сделаем процедуру, которая будет обновлять содержимое Image, т.е. то, что будет видеть пользователь:

procedure RefreshPole(Can:TCanvas);
begin
Can.StretchDraw(Can.ClipRect,Bmp_temp);
end;


Не забываем, что нам нужно написанные процедуры и функции описать в разделе interface модуля.

ШАГ 6. Описываем логику

Для хранения ходов мы будем использовать массив по размерности поля, в частности 3х3. Также нам необходимо для удобства массив, в котором мы будем хранить количество сделанных ходов по линиям, т.к. выигрышных линий для каждой клетки может быть только 4 (горизонталь, вертикаль и 2 диагонали), то это будет массив размерностью в 4 байта. Также нам нужно будет хранить, чей сейчас ход и на случай игры с компьютером, необходимо знать, чем ходит компьютер. Опишем типы и переменные:

Const
Dimens = 3; // размерность поля

Type
TMas_Pole = array[1..Dimens,1..Dimens] of Integer;
{ cspGoriz : Byte; cspVert : Byte; cspDiag1 : Byte; cspDiag2 : Byte; }
TColStepPole = array[1..4] of Byte;
…..
// здесь мы будем описывать написанные функции и процедуры
….
var
// 0 - clear, 1 - O, 2 - X
Mas_Pole: TMas_Pole;
// определяет чем ходит комп
// False - O, True - X

Komp_Logik : Boolean;
O_X : Boolean; // чей ход false - 0, true - X


Напишем первые процедуры и функции, а в частности процедуру очистки массива, записи хода пользователя:

Procedure ClearMasPole;
var
i, j : Byte;
begin
for i:=1 to Dimens do
for j:=1 to Dimens do
Mas_Pole[i,j]:=0;
end;

// запись в массив и проверка, можно и туда ходить
Function StepUser(Step:TPoint; O_X:Boolean):Boolean;
begin
Result:=false;
if Mas_Pole[Step.X,Step.Y]=0 then
begin
Mas_Pole[Step.X,Step.Y]:=Integer(O_X)+1;
Result:=True;
end;
end;


Напишем функцию, которая будет проверять окончание игры и выигрыш (комментарии в коде):

// определение победы и победителя
// 0 - игра не закончена
// 1 - победили 0
// 2 - победили Х
// 3 - ничья

function WinLogik:Byte;
var i,j:Byte;
h,w,x1,x2:Integer;
b:Boolean;
begin
x1:=0;
x2:=0;
b:=false;
Result:=0;
for i:=1 to Dimens do
begin
h:=0;
w:=0;
for j:=1 to Dimens do
begin
// просматриваем одновременно строки и столбцы
h:=(Mas_Pole[i,j] and 2)*10+(Mas_Pole[i,j] and 1)+h;
w:=(Mas_Pole[j,i] and 2)*10+(Mas_Pole[j,i] and 1)+w;
// проверяем наличие свободных клеток
if Mas_Pole[j,i]=0 then
b:=True;
end;
// просматриваем диагонали
x1:=(Mas_Pole[i,i] and 2)*10+(Mas_Pole[i,i] and 1)+ x1;
x2:=(Mas_Pole[i,(Dimens+1)-i] and 2)*10+(Mas_Pole[i,(Dimens+1)-i] and 1)+ x2;
// проверяем просмотренные столбцы и строки
if (h=3) or (w=3) then
begin
Result:=1;
exit;
end;
if (h=60) or (w=60) then
begin
Result:=2;
exit;
end;
end;
// проверяем диагонали
if (x1=3) or (x2=3) then
begin
Result:=1;
exit;
end;
if (x1=60) or (x2=60) then
begin
Result:=2;
exit;
end;
// если до сюда мы дошли, то
// проверяем есть ли свободные клетки
// если нет то возвращаем ничью = 4

if not b then
Result:=3;
end;

Как Вы уже заметили, мы проверяем только выигрышные линии, к тому же, способ проверки не совсем стандартный, но позволяющий нам избежать использование дополнительных переменных для О и Х отдельно или прогонять алгоритм повторно для каждого игрока, что ускоряет работу самого алгоритма. Сам используемый способ, может чем-то напомнить метод проверки контрольных сумм.
Теперь, попробуем реализовать функцию выбора, чем будет играть компьютер и простейшую игру компьютера. И в том и другом случае будем использовать генератор случайных чисел:


// выбирает чем ходит комп
// False - O, True - X

function KompLogik : Boolean;
begin
Randomize;
Result:=Boolean(Random(1000) mod 2);
end;

// подсчёт свободных клеток
function CalcFreeStep:Byte;
var
i, j : Byte;
begin
Result:=0;
for i:=1 to Dimens do
for j:=1 to Dimens do
Result:=Integer(Mas_Pole[i,j]=0)+Result;
end;

// играет комп, простая игра
// клетки выбираются случайно

Function EasyStepKomp:TPoint;
var
fr,tmp : Byte;
i,j : Byte;
begin
tmp:=0;
Randomize;
fr:=Random(CalcFreeStep);
if fr=0 then fr:=1;
for i:=1 to Dimens do
for j:=1 to Dimens do
begin
if (Mas_Pole[i,j]=0) then
begin
inc(tmp);
if tmp=fr then
begin
Result.X:=i;
Result.Y:=j;
Break;
end;
end;
end;
end;

В функции EasyStepKomp, во избежание зацикливания функции, когда случайные числа попадают только на занятые клетки, мы считаем количество свободных ходов, выбираем по полученному значению случайное число и последовательно выбираем эту позицию, начиная с крайней верхней клетки.

Игру средней сложности компьютером сделаем на основе оценочной таблицы, оценка будет производиться поэтапно, т.е. сначала оцениваем, можем ли мы сделав этот ход выиграть. Потом оценим, может ли противник следующим ходом выиграть, тем самым предотвратить его выигрыш. Далее делаем анализ на возможность выигрыша через ход, т.е. если в минимум одной из полос, есть наш знак и нет знака противника. Такой же анализ сделаем и для противника, потом оценим количество выигрышных полос, которые мы можем занять, поставив на клетку свой знак. Далее можно ещё оценить и количество и длину доступных диагоналей. Итак, вот что у нас получится:

// средняя игра компа
// на основе оценочной таблицы
// таблицу не делаем, оцениваем на лету

function MediumStepComp(Mas_Pole: TMas_Pole; O_X, Komp_Logik:Boolean):TPoint;
Type
TMaxOcen=record
mmoX:Byte;
mmoY:Byte;
end;
TMasMaxOcen=array of TMaxOcen;
var
Mas_Ocen : Integer;
i,j :Byte;
tmp_max : Integer;
MasMaxOcen:TMasMaxOcen;
begin
tmp_max:=1;
SetLength(MasMaxOcen,0);
// если комп ходит вторым, и если не занята центральная ячейка
// необходимо её занять

if (Komp_Logik<>O_X) and (Mas_Pole[2,2]=0) then
begin
Result.X:=2;
Result.Y:=2;
Exit;
end;
// производим оценку ячеек
for i:=1 to Dimens do
for j:=1 to Dimens do
begin
Mas_Ocen:=MasOcenCell(Mas_Pole, i, j,O_X);
// производим сразу оценку максимумов
if tmp_max<Mas_Ocen then
begin
// обнуляем массив до начального
SetLength(MasMaxOcen,1);
// записываем координаты максимума
MasMaxOcen[0].mmoX:=i;
MasMaxOcen[0].mmoY:=j;
// изменяем на новый максимум
tmp_max:=Mas_Ocen;
end
else
begin
// если максимум равен
if tmp_max=Mas_Ocen then
begin
// добавляем значение в список координат максимумов
SetLength(MasMaxOcen,Length(MasMaxOcen)+1);
MasMaxOcen[Length(MasMaxOcen)-1].mmoX:=i;
MasMaxOcen[Length(MasMaxOcen)-1].mmoY:=j;
end;
end;
end;
// выбираем случайное значение из масива координат максимумов
Randomize;
Mas_Ocen:=Random(Length(MasMaxOcen)-1);
Result.X:=MasMaxOcen[Mas_Ocen].mmoX;
Result.Y:=MasMaxOcen[Mas_Ocen].mmoY;
SetLength(MasMaxOcen,0);
end;

// подсчёт занятых на диагонали/полосе определённым игроком клеток
Function CalcStep(Mas_Pole: TMas_Pole; X,Y:Byte; O_X:Boolean;
var EnemyColStepPole:TColStepPole):TColStepPole;
var i :Byte;
User :Integer;
Enemy:Integer;
Temp1_int,Tepm2_int:Integer;
pit:TPoint;
begin
for i:=1 to 4 do
begin
Result[i]:=0;
EnemyColStepPole[i]:=0;
end;
User:=Integer(O_X)+1;
Enemy:=Integer(not O_X)+1;
// смотрим горизонтали и вертикали
for i:=1 to Dimens do
begin
if Mas_Pole[X,i]=User then
inc(Result[1]);
if Mas_Pole[i,Y]=User then
inc(Result[2]);
if Mas_Pole[X,i]=Enemy then
inc(EnemyColStepPole[1]);
if Mas_Pole[i,Y]=Enemy then
inc(EnemyColStepPole[2]);
end;
pit:=LengthDiagonalPoleCell(X,Y);
// смотрим диагонали
Temp1_int:=((X-Y)*Integer((X-Y)>0)+1);
Tepm2_int:=(Abs(X-Y)*Integer((X-Y)<0)+1);
for i:=0 to Pit.X-1 do
begin
if Mas_Pole[Temp1_int+i,Tepm2_int+i]=User then
inc(Result[3]);
if Mas_Pole[Temp1_int+i,Tepm2_int+i]=User then
inc(Result[4]);
end;
Temp1_int:=Dimens-(Dimens+1-X-Y)*Integer((Dimens+1-X-Y)>0);
Tepm2_int:=Abs(Dimens+1-X-Y)*Integer((Dimens+1-X-Y)<0)+1;
for i:=0 to Pit.Y-1 do
begin
if Mas_Pole[Temp1_int+i,Tepm2_int+i]=User then
inc(Result[3]);
if Mas_Pole[Temp1_int+i,Tepm2_int+i]=Enemy then
inc(EnemyColStepPole[3]);
end;
For i:=0 to Pit.Y-1 do
begin
if Mas_Pole[Temp1_int-i,Tepm2_int+i]=User then
inc(Result[4]);
if Mas_Pole[Temp1_int-i,Tepm2_int+i]=Enemy then
inc(EnemyColStepPole[4]);
end;
end;

// считаем оценку для конкретного случая
Function AttributionOcen(ColStepPole:TColStepPole; ColStep, ColStepEnemy,
Ocen:Integer; EnemyColStepPole:TColStepPole):Integer;
var i:Byte;
begin
Result:=0;
for i:=1 to 4 do
if (ColStepPole[i]=ColStep) and (EnemyColStepPole[i]=ColStepEnemy) then
Result:=Result+Ocen;
end;

// оценка ячейки
// X,Y - координаты оцениваемой ячейки

Function MasOcenCell(Mas_Pole: TMas_Pole; X,Y:Byte; O_X:Boolean):Integer;
var tmp, tmp_next:TColStepPole;
LenDiag:TPoint;
begin
Result:=0;
if Mas_Pole[X,Y]<>0 then Exit;
Result:=1;
{1. Оценка Клетки с точки зрения, если мы можем выиграть поставив в неё}
// если ставим в неё то выигрываем

tmp:=CalcStep(Mas_Pole, X,Y,O_X,tmp_next);
Result:=AttributionOcen(tmp,2, 0,1000000, tmp_next)+Result;
{//////////////////////////////////////////////////////////////////////}
{2. Оценка предотвращение выигрыша противника}
tmp:=CalcStep(Mas_Pole, X,Y,not O_X, tmp_next);
Result:=AttributionOcen(tmp,2,0,100000, tmp_next)+Result;
{3. Оценка ячейки с точки зрения, возможности выиграть, т.е. в полосе нет
противника и один свой знак уже стоит, тут же чем больше пересекающихся
полос, в которых присутствует такие варианты, оценка будет расти в число
раз пересекающихся полос, что обеспечивает возможность установки вилок}

tmp:=CalcStep(Mas_Pole, X,Y, O_X, tmp_next);
Result:=AttributionOcen(tmp,1,0,10000, tmp_next)+Result;
{4. Делаем тоже самое для противника, с целью предотвращение установки вилок
противником, и перекрытия его ходов}

tmp:=CalcStep(Mas_Pole, X,Y, not O_X, tmp_next);
Result:=AttributionOcen(tmp,1,0,1000, tmp_next)+Result;
{5. оцениваем число выигрышных полос, которые ещё не заняты}
LenDiag:=LengthDiagonalPoleCell(X,Y);
tmp:=CalcStep(Mas_Pole, X,Y, not O_X, tmp_next);
if (LenDiag.X=3) or (LenDiag.Y=3) then
// если есть диагонали = 3 ячейкам
Result:=AttributionOcen(tmp,0,0,100*(LenDiag.X+LenDiag.Y), tmp_next)+Result
else
// если нет диагоналей = 3 ячейкам
Result:=AttributionOcen(tmp,0,0,10, tmp_next)+Result;
{****************************************************************}
// оцениваем доступные диагонали

Result:=(LenDiag.X-1)+(LenDiag.Y-1)+Result;
end;

// определение длины диагонали
function LengthDiagonalPoleCell(X,Y:Byte):TPoint;
begin
// первая диагональ
Result.X:=Dimens-ABS(X-Y);
// вторая диагональ
Result.Y:=Dimens-ABS((Dimens-X+1)-Y);
end;


Если Вы внимательно смотрели код, то обратили внимание, что мы на случай наличия нескольких максимумов, записываем их в массив, а потом случайно выбираем из этого массива координаты ячейки, на которую пойдём. Кроме того, Вы должны были заметить, что для оценки диагоналей у меня используется функция, автоматизирующая вычисление начальной ячейки диагонали, в зависимости от того на какую диагональ попала текущая позиция.

Алгоритм естественно не является лучшим в решении данного вопроса, а является как я писал в самом начале статьи, всего лишь примером возможного способа реализации.
 
 ШАГ 6. Главный модуль

Здесь уже ничего сложного нет. Описываем самые простейшие события и описываем необходимые глобальные переменные. В своём коде я решил уйти от объявления глобальных переменных, и буду использовать стандартные средства различных компонентов:

// Image1.Tag:=X; - координаты мыши над Image
// Panel1.Tag:=Y; - координаты мыши над Image
// Form1.Tag - счётчик игр

// кнопка «Выход» в меню

procedure TForm1.N4Click(Sender: TObject);
begin
Close;
end;

// создание формы
procedure TForm1.FormCreate(Sender: TObject);
begin
if CreateBmp then
begin
MessageDlg('Не удалось инициировать игровое поле',mtError,[mbCancel],0);
Halt;
end;
Image1.Picture.Bitmap:=Bmp_Temp;
RadioGroup1Click(Sender);
end;

// уничтожение формы
procedure TForm1.FormDestroy(Sender: TObject);
begin
DestroyBmp;
end;


Опишем клики мышкой по игровому полю:

// т.к. поверку на выигрыш мы будем применять часто
// вынесем её в отдельную функцию

Function WinShowMessage:Boolean;
var win:byte;
s: string;
begin
Result:=False;
win:=WinLogik;
case win of
1 : s:='Победил "О"!!!';
2 : s:='Победил "X"!!!';
3 : s:='Ничья';
end;
if (win>0) and (win<4) then
begin
Result:=True;
Form1.Label3.Caption:=s;
ShowMessage('Игра закончена.'+s);
end;
end;

procedure TForm1.Image1Click(Sender: TObject);
var
Step:TPoint;
begin
// проверяем окончание игры
if WinShowMessage then
Exit;
// вычисляем куда попали
Step:=CalcClick(Image1.Height, Image1.Width, Image1.Tag, Panel1.Tag);
if StepUser(Step, O_X) then
begin
StepBmp(Step, O_X);
// обновляем рисунок
RefreshPole(Image1.Canvas);
Application.ProcessMessages;
// проверяем окончание игры
if WinShowMessage then
Exit;
end
else exit;
// от количества игроков
if RadioGroup1.ItemIndex=0 then // 1 игрок
begin
// меняем игрока
O_X:=not O_X;
if O_X then
Label2.Caption:='Ходит "X" (Компьютер)'
else
Label2.Caption:='Ходит "O" (Компьютер)';
case RadioGroup2.ItemIndex of
0 : Step:=EasyStepKomp; // простая
1 : Step:=MediumStepComp(Mas_Pole, O_X, not Boolean(Form1.Tag mod 2)); // Средняя
// 2 : ComplexStepComp; // сложная
end;
StepUser(Step, O_X);
StepBmp(Step, O_X);
// обновляем рисунок
RefreshPole(Image1.Canvas);
// проверяем окончание игры
WinShowMessage;
end; {if RadioGroup1.ItemIndex=0 then}
// меняем игрока
O_X:=not O_X;
if O_X then
   Label2.Caption:='Ходит "X"'
   else
   Label2.Caption:='Ходит "O"';
end;

// запоминаем координаты мыши на полем
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
Image1.Tag:=X;
Panel1.Tag:=Y;
end;


Теперь нам нужно описать событие начала новой игры и переходы между вариантами игры, т.е. один игрок или два игрока у нас играют:

// новая игра
procedure TForm1.N2Click(Sender: TObject);
var Step:TPoint;
begin
ClearMasPole;
ClearPole;
RefreshPole(Image1.Canvas);
Form1.Tag:=Form1.Tag+1;
// Х и О ходят первыми по очереди
O_X:=not Boolean(Form1.Tag mod 2);
Label1.Caption:='Игра '+inttostr(Form1.Tag);
if O_X then
Label2.Caption:='Ходит "X"'
else
Label2.Caption:='Ходит "O"';
Label3.Caption:='Победитель не определён';
// т.к. началась новая игра необходимо проверить
// кто ходит первый (если 1 игрок)

if RadioGroup1.ItemIndex=0 then
begin
if (Komp_Logik=O_X) then
begin
Label2.Caption:=Label2.Caption+'(Компьютер)';
case RadioGroup2.ItemIndex of
0 : Step:=EasyStepKomp; // простая
1 : Step:=MediumStepComp(Mas_Pole, O_X, not Boolean(Form1.Tag mod 2)); // Средняя
// 2 : Step:=ComplexStepComp; // средняя
end;
StepUser(Step, O_X);
StepBmp(Step, O_X);
// обновляем рисунок
RefreshPole(Image1.Canvas);
O_X:=not O_X;
end;
end; {if Komp_Logik=O_X then}
end;

// вариант игры (1, 2 игрока)
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
Komp_Logik:=KompLogik;
RadioGroup2.Enabled:=not Boolean(RadioGroup1.ItemIndex);
N2Click(Sender);
end;


Как видите, ничего сложного здесь нет. Если у Вас есть замечания, вопросы или предложения прошу писать на почту или ICQ которые указаны в контактах.

СУВ, Albinos_X (С) 2009 г.

ВНИМАНИЕ: Коммерческая публикация данной статьи запрещена без согласования с автором.



Комментарии

Функция доступна только зарегистрированным пользователям.
Войдите под своей учетной записью или зарегистрируйтесь.

Powered by AkoComment 2.0!

 
2001-2007 Jey_k & Albinos_X
Мой ip проверка
ALLDAY.RU - портал обо всем интересном в дизайне