Lucrarea nr. 1
Game playing
Tema lucrarii: Se va implementa un joc cu urmatoarele caracteristici:
Jocuri propuse:
|
|
|
|
|
|
|
http://imagiware.com/mancala |
|
http://www.multimania.com/nic55/dames/dames2.htm |
|
http://www.tarahill.com/instruct.html |
|
http://ringer.cs.utsa.edu/~mducote/connect4info.html |
|
http://lemes.se/renju/r1rulhis.htm |
|
http://www.ww-media.com/Othello/Othello\_rules.html |
|
http://home.vicnet.net.au/~chess/variants.htm, http://www.chessopolis.com/variants.htm |
|
http://bkgm.com, http://www.back-gammon.com |
|
|
|
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
|
3+1p |
|
3+3p |
|
1p |
|
3p |
|
1p |
|
1p |
|
-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.
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.
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.
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.
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.
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.
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.
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).
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;
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.