Помощь - Поиск - Пользователи - Календарь
Полная версия: Игра "Определитель"
Форум «Всё о Паскале» > Pascal, Object Pascal > Написание игр
setare
Здравствуйте! Мне очень сильно нужно помощь! Нам задали написать тгру определитель, предварительно даже не рассмотрев хотя бы одну программу по написанию игр на Паскале. Я тоже не могу найти никакую литературу по теории игр и реализации их на Паскале. Суть игры состоит в следующем: Есть матрица 3 на 3. 2 Игрока ходят поочередно и ставят цифры в любое место матрицы, при том цифры не должны повторяться. Когда вся матрица заполнена, мы считаем ее детерминант. Если Д больше 0, то выиграет первый, если меньше, то второй, если равно, то ничья. Моя проблема в том, что откуда приступить в задаче. С чего вообще начать? Понятно какими методами нужно пользовться: перебором, отсечением альфа-бета, рекурсией. Но откуда приступить к написанию кода? Как сделать так, чтобы пользователь вписал цифру в таблицу и компьютер отвечал?

Пожалуйста, обьясните мне и не закрывайте тему! Спасибо большое!!!!
klem4
Игрой это назвать конечно можно с натяжкой smile.gif Но теме не менее, графики тут никакой нету на соклько я понял smile.gif))) Собственно куда ее тут пихать smile.gif Пишется за 5 минут smile.gif в чем конкретно проблема ? Надо ппочередно игрокам ввести матрицу, а потом посчитаь детерминант и все !
volvo
Цитата(klem4 @ 18.09.2005 11:12)
Пишется за 5 минут smile.gif

klem4, время пошло !!! Через 15 минут ты выкладываешь готовую игру, ОК?

Цитата(klem4 @ 18.09.2005 11:12)
Надо ппочередно игрокам ввести матрицу, а потом посчитаь детерминант и все !
Внимательно читаем задание !!!
Цитата(Задание)
Как сделать так, чтобы пользователь вписал цифру в таблицу и компьютер отвечал?
setare
Извините, но матрица же одна. Как поочередно каждому вести матрицу?Всю матрицу?Каждый должен ввести одно число после хода другого.
klem4
Ну это я образно выразился, а вообще сейчас попробую smile.gif
klem4
Вот на скорую руку ...
Потестил, но могут быть ошибки.
uses crt;

const n=3;

type

   TField = array[1..n,1..n] of byte;

var

   field : TField;

procedure WriteField(x : TField);
var
   i,j : integer;
begin
   for i := 1 to n do begin
      writeln;
      for j := 1 to n do
       write(x[i,j]:2, '  ');
   end;
end;


procedure Fill(var x : TField);
var
   i,j : integer;

   count,t : byte;

   bool : boolean;

   s : set of byte;

begin

   count := 0;

   bool := TRUE;

   s := [];

   repeat

      case bool of
         TRUE : begin

            writeln;
            write('Player1 I : '); readln(i);
            write('Player1 J : '); readln(j);
            write('Player1 x[i,j] : '); readln(t);

            if (x[i,j]=0)and(not (t in s)) then begin
               x[i,j] := t;
               s := s + [t];
               inc(count);
               bool := not(bool);
            end
             else clrscr;
         end;

         FALSE : begin

             writeln;
             write('Player2 I : '); readln(i);
             write('Player2 J : '); readln(j);
             write('Player2 x[i,j] : '); readln(t);

             if (x[i,j]=0)and(not(t in s)) then begin
                 x[i,j] := t;
                 inc(count);
                 bool := not(bool);
             end
              else clrscr;
         end;
      end;

      clrscr;

      WriteField(x);

   until count =9 ;

end;

function Det(x : TField) : integer;
var
   i,j : integer;

   d : integer;

begin

   d := (x[2,2]*x[3,3]-x[3,2]*x[2,3])

                   -
        (x[2,1]*x[3,3]-x[3,1]*x[2,3])

                   +
        (x[2,1]*x[3,2]-x[3,1]*x[2,2]);

   case d>0 of
      TRUE  :  Det := 1;
      False :  Det := -1;
   else
    Det := 0;
   end;

end;


Begin

   clrscr;

   FillChar(Field, sizeof(Field), 0);

   WriteField(Field); writeln;

   Fill(Field);

   writeln;

   write('Winner : ');

   Case Det(Field) of
      1 : write('Player1');
     -1 : write('Player2');
      0 : write('Nobody');
   end;

   readln;


End.
volvo
klem4, а компьютер как играть будет? AI его ты сделал?
klem4
Цитата
2 Игрока ходят поочередно


я подумал что 2 человека, ну можно и компа сделать

ща сделаю, нашел ошибку :


Код
case d>0 of
     TRUE  :  Det := 1;
     False :  Det := -1;
  else
   Det := 0;
  end;


заменить на

Код
if d>0 then
   Det := 1
  else
   if d<0 then
    Det := -1
  else
   Det := 0;
volvo
Можно, конечно. smile.gif Только учти, что задача компьютера - выиграть у игрока, а не просто так весело провести время ;)

Кстати, а почему ты после хода второго игрока не делаешь
s := s + [t];
? unsure.gif И зачем вообще делать
Case bool of
  True: ...
  False: ...
End;
, если производятся одни и те же действия? Только чтобы написать "Player 1" или "Player 2"???
klem4
ага, это я зря sad.gif(

да уж, если он еще и думать должен, то это не на пять минут точно будет smile.gif)))
volvo
Ага, и сразу
if d>0 then
   Det := 1
  else
   if d<0 then
    Det := -1
  else
   Det := 0;
заменить на
If d = 0 Then Det := 0
Else Det := (d div Abs(d));
:yes:
klem4
Цитата(volvo @ 18.09.2005 13:18)
Ага, и сразу
if d>0 then
   Det := 1
  else
   if d<0 then
    Det := -1
  else
   Det := 0;
заменить на
If d = 0 Then Det := 0
Else Det := (d div Abs(d));
:yes:


вот это здорово придумал smile.gif
setare
Извините, а здесь рекурсия используется? И еще альфа-бета отсечение?
klem4
Вопрос : Играть с компютером надо ? Если да, то надо писать AI sad.gif
setare
Да!
setare
Извините, но вы не могли бы подсказать как написать ai? Как раз самое сложное именно в этом. И именно это я и не понимаю!!!!
Дож
klem4, а правильно ли твоя прога считает детерминант? Я не знаю что это, но в функции Det не используется первый столбец матрицы... <_<
klem4
Все конечно может быть ... в торопях писал, но вообще не очень понимаю о чем ты, а считал определитель по первой строке ...
Дож
Цитата(klem4)
вообще не очень понимаю о чем ты, а считал определитель по первой строке ...


Код

d := (x[2,2]*x[3,3]-x[3,2]*x[2,3])
                  -
       (x[2,1]*x[3,3]-x[3,1]*x[2,3])
                  +
       (x[2,1]*x[3,2]-x[3,1]*x[2,2]);


Здесь вроде бы один и тот же результат будет при любом состоянии чисел
x[1,2],x[1,1],x[1,3]... А детерминант скорее всего должен считатся с помощью всех эл-в матрицы.
volvo
setare, а обязательно надо использовать "альфа - бета отсечение"? Просто в задаче такого рода это не имеет особого смысла, ведь при размере матрицы (3 х 3) имеется всего 9! вариантов взаимного расположения чисел, то есть 362880... Причем после того, как первый ход сделан, количество оставшихся вариантов сокращается уже до 8! = 40320, и т.д. => быстродействие может быть приемлемым без отсечения вариантов...

Я бы попробовал сделать так: процедура AI компьютера получает на вход частично заполненную матрицу, генерирует (рекурсивно) для нее все возможные варианты продолжения, и выбирает из них тот (само число и его расположение), при котором окончательный детерминант будет максимальным/минимальным (в зависимости от очередности хода компьютера). Если представлять матрицу как строку из 9-ти символов (развернуть матрицу построчно), то памяти в "куче" должно хватить.

P.S. klem4, на самом деле подозрительно. У меня например D считается вот так:
function get_determ(mx: tmatrix): integer;
begin
  result :=
    mx[1, 1]*mx[2, 2]*mx[3, 3] +
    mx[1, 2]*mx[2, 3]*mx[3, 1] +
    mx[1, 3]*mx[2, 1]*mx[3, 2] -

    mx[1, 3]*mx[2, 2]*mx[3, 1] -
    mx[1, 1]*mx[2, 3]*mx[3, 2] -
    mx[1, 2]*mx[2, 1]*mx[3, 3];
end;

т.е. используются все элементы...
klem4
да я забыл домножать на элементы первой строки sad.gif( забывать начинаю мат.часть smile.gif

Цитата
        a1 a2 a3
A=    b1 b2 b3
        c1 c2 c3 

detA= a1*(b2*c3-c2*b3)-a2*(b1*c3-c1*b3)+a3*(b1*c2-c1*b2)
setare
Извините, пожалуйста, volvo, но не могли бы вы еще раз но чуть по легче сказать то, что вы только что написали. Я не очень поняла как компьютер получает на вход частично заполненную матрицу?
Дож
Имелось ввиду нечто типа этого:
Type PAIResult=^TItemField
        TAIResult=record
          I,J : 1..n;
          Item: byte;
         result: byte;
       end;
...
Function AI(X:TField):PAIResult;
Var temp: PAIResult;
      i,j:integer;
      item:byte;
      g     :boolean;
      y     :TField;
begin
 y:=X;
 new(temp);
 If busy(X) then begin {Если массив заполнен, то}
  temp.result:=Det(X); 
  AI^:=temp^;            {Возвращаем D}
 end;
 For i:=1 to n do begin {Проссматриваем все возможные ходы:}
    For j:=1 to n do begin
      If X[i][i]=0 then
          For item:=0 to 255 do begin
            y[i][j]:=item;
            temp:=AI(y);
            If temp^.result=1 then AI^:=temp^;
           end;
    end;
 end;
 {Если выигрошного хода не нашлось, то}
     AI^.result:=-1; {Эта строчка была доб. позже...}
 AI^:=temp^;
end;
{
  Здесь компьютер играет либо на золото либо на проигрыш...
  Не учитывается ниченая возможность...
}
...
setare
Ухты, ну и код. Здесь надо долго разбираться. А можно ведь и без указателей, правда? И busy(x) это функция? Кстати, отсечение обязательно. При том нужно дойти до конца и потом отсекать. С последнего уровня и идти наверх.
Дож
Цитата
Кстати, отсечение обязательно. При том нужно дойти до конца и потом отсекать. С последнего уровня и идти наверх.
Ну если я правильно понимаю что ты имеешь ввиду под отсечением, то здесь оно вроде как присутствует- если встречается хороший ход, то остальные ходы не просматриваются...
Цитата
А можно ведь и без указателей, правда?
Да наверно можно и без указателей, например, функция AI будет возвращать результат через глобальную переменную...
Цитата
И busy(x) это функция?
Да это функция, возвращающая true, если в массиве нет ни одного нуля иначе False. Ну, только не спрашивай как ее реализовать, а то тебя в FAQ пошлют...

код кое-как исправлен:
Type PAIResult=^TItemField
        TAIResult=record
          I,J : 1..n;
          Item: byte;
         result: byte;
       end;
...
Function AI(X:TField):PAIResult;
Var temp: PAIResult;
      i,j:integer;
      item:byte;
      g     :boolean;
      y     :TField;
begin
 y:=X;
 new(temp);
 If busy(X) then begin {Если массив заполнен, то}
  temp.result:=Det(X); 
  AI^:=temp^;            {Возвращаем D}
 end;
 For i:=1 to n do begin {Проссматриваем все возможные ходы:}
    For j:=1 to n do begin
      If X[i][i]=0 then
          For item:=0 to 255 do begin
            y[i][j]:=item;
            temp:=AI(y);
            If temp^.result=1 then AI^:=temp^;
           end;
    end;
 end;
 {Если выигрошного хода не нашлось, то}
     AI^.result:=-1;
 AI^:=temp^;
end;
{
  Здесь компьютер играет либо на золото либо на проигрыш...
  Не учитывается ниченая возможность...
}
...
setare
Ладно не буду. Спасибо. А почему вы написали
Код


For item:=0 to 255 do begin

И почему X[i][i]?
volvo
:no: Дож, это - не мой вариант. Мой вариант - что-то в таком духе:

Type
  TResult = Record
    Digit: byte; Pos: Byte;
  End;
Var
  max_D: LongInt;
  max_Matrix: String;

{
  CurrMatrix - "развернутое" в строку представление матрицы
  ToUse - оставшиеся для использования цифры
}
Procedure AI(CurrMatrix, ToUse: String);
Var
  D: LongInt;
  s_u, s_m: String;
Begin
  If ToUse = '' Then Begin { цифр больше не осталось -> матрица заполнена }
    D := get_determ(CurrMatrix); { считаем детерминант }
    If D > max_D Then Begin { если найденный D больше/меньше всех предыдущих }
      { запоминаеи макс/мин детерминант и саму матрицу, при которой это произошло }
      max_D := D; max_matrix := CurrMatrix;
    End;
    Exit; { и заканчиваем рекурсию } 
  End;

  { А вот если матрица заполнена не окончательно (ToUse не пустая строка)... }
  {
    ... то генерируем возможные сочетания элементов,
    и передаем это опять в AI:
  }
  For j := 1 To Length(ToUse) Do
    For i := 1 To Length(CurrMatrix) Do
      If CurrMatrix[i] = '0' Then Begin
        s_m := CurrMatrix; s_m[i] := ToUse[j];
        s_u := ToUse; Delete(s_u, j, 1);
        AI(s_m, s_u); { Рекурсия !!! }
      End;
End;

Procedure ComputerMove(CurrMatrix, ToUse: String; Var R: TResult);
Begin
  AI(CurrMatrix, ToUse);
  {
    Когда мы здесь - уже известно, какая матрица из возможных
    дает выигрышную комбинацию, и имея CurrMatrix и max_Matrix,
    можно выбрать одну из цифр, НЕ присутствующих в CurrMatrix...

    Результат (цифра и ее позиция в строке) возвращается через R,
    а потом при отрисовке R.Pos легко переведется в строку и столбец матрицы...
  }
End;
setare
Извините за глупый вопрос, но что значит при отрисовке R.pos легко переведется в строку матрицы? И кстати, там , где вы написали выход их рекурсии, до этого рекурсия не использовалась, правильно? Она используется вами уже после, когда touse не равно " "?
volvo
Цитата(setare @ 18.09.2005 19:05)
что значит при отрисовке R.pos легко переведется в строку матрицы?


После того, как компьютер сделал свой ход, матрицу значений надо перерисовать, чтобы второй игрок видел картинку, правда? Но я в процедуре ComputerMove возвращаю НЕ 2 координаты (строку/столбец), а только одну, учитывая то, что при известном размере матрицы из 7-ой, например, позиции "растянутой" матрицы получить 3-ю строку и 1-ый столбец не составит труда...

Цитата(setare @ 18.09.2005 19:05)
И кстати, там , где вы написали выход их рекурсии, до этого рекурсия не использовалась, правильно? Она используется вами уже после, когда touse не равно " "?

Ну да, это же основное правило составления рекурсивных подпрограмм: сначала проверяется условие выхода, и только если оно НЕ выполняется, рекурсивный вызов продолжается. Иначе очень просто получить бесконечную рекурсию, и как результат - переполнение стека и RunTime Error...
setare
Спасибо за обьяснение! Если возникнут вопросы, я опять вас побеспокою! :D
setare
volvo Здравствуйте, извините, значит в вашей последней программе в последней процедуре уже ничего не надо добовлять?После вызова ai? Или нужно что-то делать с R.pos? Я уже несколько раз перечитывала ваше обьяснение, но никак не могла понять что надо еще что-то добавлять или нет?
volvo
Надо, конечно smile.gif Я просто привел сам способ реализации AI; после вызова
AI(CurrMatrix, ToUse);
будет известна оптимальная матрица, но нужно ведь еще выбрать очередной ход (их же может быть несколько...) Например, у нас на данный момент заполнено 3 элемента:
Цитата(Текущая матрица)
0 0 0
1 2 3
0 0 0
а после вызова AI будет известна оптимальная матрица:
Цитата(max_Matrix)
4 5 6
1 2 3
7 8 9

(это просто пример, так что не надо обращать внимание на конкретные значения... ) Так вот, процедура ComputerMove должна выбрать, какое из чисел, присутствующих во второй матрице, и НЕ присутствующих в первой будет очередным ходом компьютера, то есть, для моего примера, у компьютера есть возможность сделать 6 разных ходов, которые могут привести его к выигрышу: 4 в позицию (1, 1); 5 в позицию (1, 2) и т.д.

Какой именно вариант выбрать и нужно решить сразу после вызова AI...
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.