Lucrarea nr. 1
Game playing









Tema lucrarii: Se va implementa un joc cu urmatoarele caracteristici:


 

Jocuri propuse:
 
  • the fox and the dogs(*), the hunter and the bisons(*)
  • as31(*)
  • football(*)
  • mancala(*), awele(*), ourri, oware
http://imagiware.com/mancala
  • checkers(*), draughts(*) 
http://www.multimania.com/nic55/dames/dames2.htm
  • nine man's morris(*) 
http://www.tarahill.com/instruct.html
  • connect-four(*)
http://ringer.cs.utsa.edu/~mducote/connect4info.html
  • five-in-a-row variants (go-moku(*), pente ...)
http://lemes.se/renju/r1rulhis.htm
  • othello(*), reversi(*) 
http://www.ww-media.com/Othello/Othello\_rules.html
  • chess variants (Suicide, progressive, chatarange, hexagonal, ...)
http://home.vicnet.net.au/~chess/variants.htm, http://www.chessopolis.com/variants.htm
  • backgammon(*)
http://bkgm.com, http://www.back-gammon.com
  • minesweeper(*), tetris(*) (single agent games)
  • ...(altele)


Pentru cautarea in spatiul starilor se vor utiliza notiunile invatate la curs (precum si modelele de implementare din lucrare).
Sugestii de extensie a algoritmului de cautare alfa-beta

Punctaj:
 
3+1p
  • algoritm de cautare
3+3p
  • interfata in mod text
1p
  • interfata grafica
3p
  • dificultatea jocului
1p
  • kit de instalare, cod sursa comentat (nu si cel de interfata)
1p
  • intarziere
-1punct/saptamana.


Data predarii: 22 Noiembrie 1999
 


Extensii ale algoritmului alfa-beta

In cazul unei ordonari optime (adica intotdeauna prima data este efectuata mutarea care este gasita ca fiind cea mai buna), numarul frunzelor atinse de alfa-beta este de W^{(D+1)/2} + W^{D/2} - 1, iar in cazul cel mai defavorabil, cand mutarile sunt considerate in ordinea inversa valorilor, alfa-beta se reduce la minimax (nu exista nici o t\u{a}iere), num\u{a}rul frunzelor fiind W^D. Pornind de la aceasta observatie majoritatea algoritmilor de cautare de adancime fixa incearca sa obtina o ordonare mai buna a mutarilor posibile dintr-un nod.

History heuristic

Acest algoritm se bazeaza pe ideea ca daca o mutare a fost gasita buna intr-un nod, este probabil sa fie buna si intr-un alt nod. Termenul de mutare buna se confunda in acest caz cu mutarea care a fost gasita cea mai buna in nodul respectiv, sau a produs un cut-off. In consecinta aceste mutari sunt retinute (de exemplu intr-o tabela de hashing). Atunci cand un nod este expandat, mutarile care au fost gasite bune mai des, sunt vizitate mai devreme.

Iterative deepening

Acest algoritm se traduce prin apelul repetat al unei proceduri de cautare, cu o adancime ce creste. Uzual, procesul este terminat de factorul timp, rezultatul fiind dat de ultima cautare terminata (ultima cautare lansata este de obicei intrerupta). Pe langa o mai buna incadrare in timp, algoritmul are avantajul c\u{a} procedura de cautare de adancime D, poate sa se foloseasca de rezultatele obtinute (si salvate) intr-o cautare de adancime mai mica decat D. In mod uzual aceste rezultate constau din mutarile cele mai bune in nodurile vizitate.

Transposition table

Tabela de tranpozitie retine informatiile utile intr-un nod intalnit, pentru a fi folosite daca este intalnit un nod identic. Daca valoarea gasita se afla in afara ferestrei alfa-beta, valoarea nodului poate fi considerata numai ca limita superioara sau inferioara. Si in acest caz insa, poate fi utila mutarea gasita ca fiind cea mai buna.

Quiescence search

Acest algoritm extinde in mod selectiv subarborii nodului. Uzual este folosit pentru a evita evaluarea unor pozitii "nelinistite". In acest caz, dupa ce se ajunge la adancimea 0, se cauta in continuare mutarile ce pot schimba semnificativ valoarea estimatorului (de ex. in sah, astfel de  mutari sunt capturile, sahurile si promovarile.

Aspiration search

In loc de  AlphaBeta(pos, depth, -INFINITY, +INFINITY), se va utiliza un apel de forma

   AlphaBeta(pos, depth, value-window, value+window),

unde value este valoarea asteptata. Ingustarea ferestrei duce la cresterea numarului de taieturi. Daca valoarea returnata va fi in afara ferestrei, cautarea va fi repetata cu o fereastra corespunzatoare.

Null-move heuristic

Acest algoritm opreste cautarea atunci cand pozitia pare suficient de buna. Acesta este testata prin efectuarea unei null move (adica pass), si o cautare cu adancime mai redusa. Terminarea cautarii se realizeaza daca valoarea returnata de aceasta proba este mai mare decat beta. In jocuri unde pass-ul poate fi o mutare buna (de exemplu othello), algoritmul nu poate fi folosit. Acest algoritm are dezavantajul ca poate sa "scape" combinatii mai adanci.

MTD(f) (pseudocod)

MTD(f) este de fapt o combinatie intre alfa-beta, Transposition Table, Iterative Deepening si Aspiration Search cu fereastra de latime 0.


Algoritmul alfa-beta

Pentru inceput, este oferita implementarea in C a variantei negamax a algoritmului:

int AlphaBeta( pos, alpha, beta, d )
{
    int    value, best;
    node   succ[MAXN];
    int    i, w;

    if ( d == 0 )  return Evaluate(n);    /* nod frunza */
    w = Successors(pos, succ); \\
    best = -INFINITY; \\
    for ( i = 0; i < w; i++ )
    {
        value = -AlphaBeta( succ[i], -beta, -alpha, d-1);
        alpha = max(alpha, value);
        best = max(best, value);
        if ( best >= beta )
            return best;        /* taiere */
    }
    return best;
}

In continuare se prezinta implementarea in Prolog:

:- dynamic
    bestMove/2, alpha/2, beta/2.

alphabeta(0, State, _, _, noMove, H) :-
    !, h(State, H).

alphabeta(Depth, State, Alpha, Beta, BestMove, BestValue) :-
    setvar(alpha(Alpha, Depth)), setvar(beta(Beta, Depth)),
    setvar(bestMove(noMove, Depth)),
    Depth1 is Depth - 1,
    State = state(_, OnMove, _),

    (Depth > 1 -> s1(State, Move, State1); s(State, Move, State1)),

    alpha(Alpha1, Depth), beta(Beta1, Depth),
    alphabeta(Depth1, State1, Beta1, Alpha1, _, V),

    V * OnMove > Alpha1 * OnMove,

    setvar(bestMove(Move, Depth)), setvar(alpha(V, Depth)),
    V * OnMove >= Beta1 * OnMove, !,                   /* cut-off */

    BestMove = Move, BestValue = Beta1.

alphabeta(Depth, _, _, _, BestMove, BestValue) :-
    bestMove(BestMove, Depth) , alpha(BestValue, Depth).
 

MTD(f) pseudocod

function AlphaBetaWithMemory(n,alpha,beta,d)->integer;
    if retrieve(n) = ok then        /* Transposition table lookup */
        if n.lowerbound >= beta then return n.lowerbound;
        if n.lowerbound  >= beta then return n.lowerbound;
        if n.upperbound  >= alpha then return n.upperbound;
        alpha := max(alpha, n.lowerbound);
        beta := min(beta, n.upperbound);
    else
        n.upperbound := +INFINITY;
        n.lowerbound := -INFINITY;
    if d=0 then g := eval(n);         /* leaf node */
    else if (n is a max node) then
        g:= -INFINITY; a:=alpha;   /* save original alpha value */
        c:=firstchild(n);
        while g<beta and c<>nil do
            g:=max(g,AlphaBetaWithMemory(c,a,beta,d-1));
            a:=max(a,g);
            c:=nextbrother(c);
    else /* n is a min node */
        g:= +INFINITY; b:=beta;   /* save original beta value */
        c:=firstchild(n);
        while g>alpha and c<>nil do
            g:=min(g,AlphaBetaWithMemory(c,alpha,b,d-1));
            b:=min(b,g);
            c:=nextbrother(c);
    if g<beta then n.upperbound := g;
    if g>alpha then n.lowerbound := g;
    store n.lowerbound, n.upperbound
    return g;

function MTDf(root,f,d)->integer;
    upperbound := +INFINITY;
    lowerbound := -INFINITY;
    g:=f;
    repeat
        if g=lowerbound  then bound := g+1 else bound := g;
        g:=AlphaBetaWithMemory(root,bound-1,bound,d);
        if g<bound then upperbound := g else lowerbound := g;
    until lowerbound  >= upperbound ;
    return g;

function iterative_deepening(root)->integer;
    firstguess := 0;
    for d := 1 to MAX_SEARCH_DEPTH do
        firstguess := MTDf(root, firstguess, d);
        if times_up() then break;
    return firstguess;


Functia de evaluare

Un algoritm de cautare utilizeaza in principiu cel putin o functie de evaluare. Acuratetea unei astfel de functii depinde in mare parte de modul in care sunt incorporate in ea trasaturile jocului. Caracteristicile unui joc ce se joaca pe o tabla sunt clasificate astfel:

Caracteristici pozitionale: valorile lor sunt calculate pe baza pozitiei curente de pe tabla.

Caracteristici non-pozitionale: valorile lor se calculeaza pe baza mutarii care s-a realizat pentru a se ajunge in pozitia curenta. Caracteristici ce depind de regulile jocului: valorile acestor proprietati sunt obtinute din pozitia care apare dupa mutarea urmatoare, precum si din pozitiile care ar putea sa apara daca jucatorul la mutare ar avea voie sa mai faca inca o mutare.