Kółko i krzyżyk w Delphi

0

Witam, znalazłem przykładowy kod programu w C++ a staram się go przerobić na Delphi. Niestety w jakimś miejscu popełniłem błąd i moduł inteligencji komputera nie działa tak jak trzeba (ma korzystać z alg. mini-max, a działa tak, jakby z niego nie korzystał). Zresztą zobaczcie sami. Czy jest ktoś w stanie poprawić ten błąd? Siedzę nad tym od dłuższego czasu i już skończyły mi się pomysły.

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

{PLANSZA JEST TABLICĄ TYPU CHAR, GDZIE:
        ' ' - TO POLE PUSTE
        X - TO KRZYŻYK
        O - TO KÓŁKO
}
type Tplansza=array [1..9] of char;


var
  a:integer;
  t:Tplansza;
  gracz,wybor:char;


{
 Funkcja rysuje planszę gry w kółko i krzyżyk
 Plansza przechowywana jest w tablicy t[] w elementach o następujących indeksach:
 t[1] t[2] t[3]
 t[4] t[5] t[6]
 t[7] t[8] t[9]
---------------------------------------------------------------------------------}
procedure plansza(var t:Tplansza);
var i:integer;

begin
  for i:= 1 to 9 do begin
    write(' ',t[i],' ');
    if i mod 3 <> 0 then write('|')     { Po elementach 1,2,4,5,7,8 rysujemy |}
    else if i mod 3 <> 9 then begin
        writeln;
        writeln('---+---+---');         { Po elementach 3 i 6 poprzeczka }
    end else writeln;                   { Po elemencie 9 koniec wiersza }
  end;
end;

{
 Funkcja zwraca true, jeśli nastąpiła
 wygrana danego zawodnika
-------------------------------------}
function wygrana(var t:Tplansza; g:char; cisza:boolean):boolean;
var
  test: boolean;
  i: integer;

begin

  test:= false; { Zmienna przyjmuje true, jeśli zawodnik ma trzy figury w wierszu,
                  kolumnie lub na przekątnych }

{ Sprawdzamy wiersze }
  i:= 1;
  while  i <= 7 do begin
        test:=test or ((t[i]=g) and (t[i+1]=g) and (t[i+2]=g));
        i:=i+3;
  end;

{ Sprawdzamy kolumny }
  i:=1;
  while  i <= 3 do begin
        test := test or ((t[i]=g) and (t[i+3]=g) and (t[i+6]=g));
  i:=i+1;
  end;
  
{ Sprawdzamy przekątną 1-5-9 }
  test := test or ((t[1]=g) and (t[5]=g) and (t[9]=g));

{ Sprawdzamy przekątną 3-5-7  }
  test:= test or ((t[3]=g) and (t[5]=g) and (t[7]=g));

  if test then
    if not cisza then begin
        plansza(t); {rysuje plansze }
        writeln;
        writeln('GRACZ ',g,' WYGRYWA!!!');
        writeln;
        wygrana:=true;
        exit;
    end;
  wygrana:=false;
end;



{
 Funkcja zwraca true, jeśli na planszy nie ma już
 żadnego wolnego pola na ruch.
-------------------------------------------------}
function remis(var t:Tplansza; cisza:boolean):boolean;
var i:integer;

begin
{ Jeśli napotkamy spację, to plansza posiada wolne pola - zwracamy false }

  for i:= 1 to 9 do if t[i] = ' ' then begin
      remis:=false;
      exit;
  end;

{ Jesli pętla for zakończyła się normalnie, to na żadnym polu planszy nie było spacji.
  Mamy do czynienia z remisem - zwracamy true }

  if not cisza then begin
        plansza(t); {rysuje plansze  }
        writeln;
        writeln('REMIS !!!');
        writeln;
  end;
  remis:=true;
end;



{
 Algorytm rekurencyjny MINIMAX
 Do algorytmu wchodzimy z planszą, na której ustawione jest pole
 bieżącego gracza. Parametr gracz przekazuje literkę gracza, a
 parametr mx przekazuje jego wynik w przypadku wygranej
----------------------------------------------------------------}
function minimax(var t:Tplansza; gracz:char):integer;
var
   i, m, mmx:integer;

begin

{ Najpierw sprawdzamy, czy bieżący gracz wygrywa na planszy.
  Jeśli tak, to zwracamy jego maksymalny wynik }
  if wygrana(t,gracz,true) then begin
        if gracz='X' then minimax:=1 else minimax:=-1;
        exit;
  end;

{ Następnie sprawdzamy, czy nie ma remisu. Jeśli jest, zwracamy wynik 0 }

  if remis(t,true) then begin
        minimax:=0;
        exit;
  end;

{ Będziemy analizować możliwe posunięcia przeciwnika.
  Zmieniamy zatem bieżącego gracza na jego przeciwnika }

  if gracz='X' then gracz:='O' else gracz:='X';

{
  Algorytm MINIMAX w kolejnych wywołaniach rekurencyjnych naprzemiennie analizuje
  grę gracza oraz jego przeciwnika. Dla gracza oblicza maksimum wyniku gry, a dla
  przeciwnika oblicza minimum. Wartość mmx ustawiamy w zależności od tego, czyje
  ruchy analizujemy:
  X - liczymy max, zatem mmx <- -10
  O - liczymy min, zatem mmx <-  10
}

  if gracz='O' then mmx:=10 else mmx:=-10;

{
 Przeglądamy planszę szukając wolnych pół na ruch gracza. Na wolnym polu ustawiamy
 literkę gracza i wyznaczamy wartość tego ruchu rekurencyjnym wywołaniem
 algorytmu MINIMAX. Planszę przywracamy i w zależności kto gra:
 X - wyznaczamy maximum
 O - wyznaczamy minimum
}

  for i:= 1 to 9 do
    if t[i]= ' ' then begin
       t[i]:= gracz;
       m:= minimax(t,gracz);
       t[i]:= ' ';
       if (((gracz='O') and (m<mmx)) or ((gracz='X') and (m>mmx)))
          then mmx:= m;
    end;

  minimax:=mmx;
end;


{
 Funkcja wyznacza ruch dla komputera.
------------------------------------}
function komputer(var t:Tplansza):integer;
var
  ruch, i, m, mmx:integer;

begin
  mmx:= -10;
  for i:= 1 to 9 do
    if t[i]=' ' then begin
      t[i]:= 'X';
      m:= minimax(t,'X');
      t[i]:= ' ';
      if (m > mmx) then begin
        mmx:= m;
        ruch:= i;
      end;
    end;
  komputer:=ruch;
end;


{
 Funkcja umożliwia ruch gracza
 Po ruchu następuje zamiana gracza
------------------------------------}
procedure ruch(var t:Tplansza; var gracz:char);
var r:integer;

begin
  plansza(t);
  if gracz='O' then begin
    writeln;
    write('CZLOWIEK : wybiera ruch : ');
    read(r);
  end else begin
    r:= komputer(t);
    writeln;
    writeln('KOMPUTER : wybiera ruch : ',r);
  end;
  writeln('---------------------------');
  writeln;
  if ((r >= 1) and (r <= 9) and (t[r] = ' ')) then t[r]:= gracz;
  if gracz='O' then gracz:='X' else gracz:='O';
end;




begin
  repeat
  begin
    //clrscr;
    writeln('Gra w Kolko i Krzyzyk dla gracza i komputera');
    writeln;
    writeln('============================================');
    writeln;
    for a:= 1 to 9 do t[a]:= ' ';
    gracz:= 'O';

    while(not wygrana(t,'X',false) and (not wygrana(t,'O',false)) and (not remis(t,false))) do ruch(t,gracz);

    write('Jeszcze raz ? (t = TAK) : ');
    readln;
    readln(wybor);
    end;
  until not (upcase(wybor)='T');
end.

Dla porównania, orginalny kod poniżej:

#include <iostream>

using namespace std;

// Funkcja rysuje planszę gry w kółko i krzyżyk
// Plansza przechowywana jest w tablicy t[] w elementach o następujących indeksach:
// t[1] t[2] t[3]
// t[4] t[5] t[6]
// t[7] t[8] t[9]
//---------------------------------------------------------------------------------
void plansza(char t[])
{
  for(int i = 1; i <= 9; i++)
  {
    cout << " " << t[i] << " ";
    if(i % 3)       cout << "|";               // Po elementach 1,2,4,5,7,8 rysujemy |
    else if(i != 9) cout << "\n---+---+---\n"; // Po elementach 3 i 6 poprzeczka
    else            cout << endl;              // Po elemencie 9 koniec wiersza  
  }    
}

// Funkcja zwraca true, jeśli nastąpiła
// wygrana danego zawodnika
//-------------------------------------
bool wygrana(char t[], char g, bool cisza)
{
  bool test;
  int i;
  
  test = false; // Zmienna przyjmuje true, jeśli zawodnik ma trzy figury
                // w wierszu, kolumnie lub na przekątnych

// Sprawdzamy wiersze

  for(i = 1; i <= 7; i += 3) test |= ((t[i] == g) && (t[i+1] == g) && (t[i+2] == g));

// Sprawdzamy kolumny  

  for(i = 1; i <= 3; i++)    test |= ((t[i] == g) && (t[i+3] == g) && (t[i+6] == g));

// Sprawdzamy przekątną 1-5-9

  test |= ((t[1] == g) && (t[5] == g) && (t[9] == g));

// Sprawdzamy przekątną 3-5-7

  test |= ((t[3] == g) && (t[5] == g) && (t[7] == g));

  if(test)
  {
    if(!cisza)
    {
      plansza(t);
      cout << "\nGRACZ " << g << " WYGRYWA!!!\n\n";
    }
    return true;
  }
  return false;
}

// Funkcja zwraca true, jeśli na planszy nie ma już
// żadnego wolnego pola na ruch.
//-------------------------------------------------
bool remis(char t[], bool cisza)
{
// Jeśli napotkamy spację, to plansza posiada wolne pola - zwracamy false  

  for(int i = 1; i <= 9; i++) if(t[i] == ' ') return false;

// Jesli pętla for zakończyła się normalnie, to na żadnym polu planszy nie było
// spacji. Mamy do czynienia z remisem - zwracamy true

  if(!cisza)
  {
    plansza(t); cout << "\nREMIS !!!\n\n";
  }
  return true;     
}

// Algorytm rekurencyjny MINIMAX
// Do algorytmu wchodzimy z planszą, na której ustawione jest pole
// bieżącego gracza. Parametr gracz przekazuje literkę gracza, a
// parametr mx przekazuje jego wynik w przypadku wygranej
//----------------------------------------------------------------
int minimax(char t[], char gracz)
{
  int m, mmx;
  
// Najpierw sprawdzamy, czy bieżący gracz wygrywa na planszy. Jeśli tak, to
// zwracamy jego maksymalny wynik

  if(wygrana(t,gracz,true)) return (gracz == 'X') ? 1 : -1;

// Następnie sprawdzamy, czy nie ma remisu. Jeśli jest, zwracamy wynik 0

  if(remis(t,true)) return 0;

// Będziemy analizować możliwe posunięcia przeciwnika. Zmieniamy zatem
// bieżącego gracza na jego przeciwnika

  gracz = (gracz == 'X') ? 'O' : 'X';

// Algorytm MINIMAX w kolejnych wywołaniach rekurencyjnych naprzemiennie analizuje
// grę gracza oraz jego przeciwnika. Dla gracza oblicza maksimum wyniku gry, a dla
// przeciwnika oblicza minimum. Wartość mmx ustawiamy w zależności od tego, czyje
// ruchy analizujemy:
// X - liczymy max, zatem mmx <- -10
// O - liczymy min, zatem mmx <-  10

  mmx = (gracz == 'O') ? 10 : -10;

// Przeglądamy planszę szukając wolnych pół na ruch gracza. Na wolnym polu ustawiamy
// literkę gracza i wyznaczamy wartość tego ruchu rekurencyjnym wywołaniem
// algorytmu MINIMAX. Planszę przywracamy i w zależności kto gra:
// X - wyznaczamy maximum
// O - wyznaczamy minimum

  for(int i = 1; i <= 9; i++)
    if(t[i] == ' ')
    {
       t[i] = gracz;
       m = minimax(t,gracz);
       t[i] = ' ';
       if(((gracz == 'O') && (m < mmx)) || ((gracz == 'X') && (m > mmx))) mmx = m;
    }
  return mmx;
}

// Funkcja wyznacza ruch dla komputera.
//------------------------------------
int komputer(char t[])
{
  int ruch, i, m, mmx;
  
  mmx = -10;
  for(i = 1; i <= 9; i++)
    if(t[i] == ' ')
    {
      t[i] = 'X';
      m = minimax(t,'X');
      t[i] = ' ';
      if(m > mmx)
      {
        mmx = m; ruch = i;     
      }        
    }    
  return ruch;
}

// Funkcja umożliwia ruch gracza
// Po ruchu następuje zamiana gracza
//------------------------------------
void ruch(char t[], char &gracz)
{
  int r;
   
  plansza(t);
  if(gracz == 'O')
  {
    cout << "\nCZLOWIEK : wybiera ruch : ";
    cin >> r;
  }
  else
  {
    r = komputer(t);
    cout << "\nKOMPUTER : wybiera ruch : " << r << endl;
  }
  cout << "---------------------------\n\n";
  if((r >= 1) && (r <= 9) && (t[r] == ' ')) t[r] = gracz;
  gracz = (gracz == 'O') ? 'X' : 'O';
}

int main(int argc, char * argv[])
{
  char t[10],gracz,wybor;
  
  do {
    cout << "Gra w Kolko i Krzyzyk dla gracza i komputera\n"
            "============================================\n\n";
    for(int i = 1; i <= 9; i++) t[i] = ' ';
    gracz = 'O';
    
    while(!wygrana(t,'X',false) && !wygrana(t,'O',false) && !remis(t,false)) ruch(t,gracz);
    
    cout << "Jeszcze raz ? (T = TAK) : ";
    cin >> wybor; 
    cout << "\n\n\n";

  } while((wybor == 'T') || (wybor == 't'));

  return 0;
}

Będę wdzięczny za wszelką pomoc :)

0

Ja w przerabianiu kodu nie bardzo jestem w stanie pomóc, bo nie znam praktycznie w ogółe C i podobnych, a
jedynie jako tako Delphi. Skorzystał bym jednak na Twoim miejscu z tego kodu, powstałego jeszcze pod 3cią
wersją Delphi, a ktory kiedyś znalazłem w sieci. Link to: http://www.speedyshare.com/files/23044233/kik.rar

0

Niestety podany przez Ciebie przykład nie korzysta z algorytmu mini-max (czyli nie gra inteligentnie), a zależy mi właśnie na przykładzie z inteligentnym AI korzystającym z tego algorytmu...

1 użytkowników online, w tym zalogowanych: 0, gości: 1