Document, comentariu, eseu, bacalaureat, liceu si facultate
Top documenteAdmitereTesteUtileContact
      
    


 


Ultimele referate adaugate

Adauga referat - poti sa ne ajuti cu un referat?

Politica de confidentialitate



Ultimele referate descarcare de pe site
  CREDITUL IPOTECAR PENTRU INVESTITII IMOBILIARE (economie)
  Comertul cu amanuntul (economie)
  IDENTIFICAREA CRIMINALISTICA (drept)
  Mecanismul motor, Biela, organe mobile proiect (diverse)
  O scrisoare pierduta (romana)
  O scrisoare pierduta (romana)
  Ion DRUTA (romana)
  COMPORTAMENT PROSOCIAL-COMPORTAMENT ANTISOCIAL (psihologie)
  COMPORTAMENT PROSOCIAL-COMPORTAMENT ANTISOCIAL (psihologie)
  Starea civila (geografie)
 

Ultimele referate cautate in site
   domnisoara hus
   legume
    istoria unui galban
   metanol
   recapitulare
   profitul
   caract
   comentariu liric
   radiolocatia
   praslea cel voinic si merele da aur
 
despre:
 
Prolog
Colt dreapta
Vizite: ? Nota: ? Ce reprezinta? Intrebari si raspunsuri
 
10 Probleme rezolvabile prin backtracking q2p24pg
Mecanismul implicit de functionare a limbajului Prolog este bazat pe backtracking. De aceea problemele care se rezolva prin backtracking se implementeaza mai usor in Prolog decat in alte limbaje. Sa studiem cateva probleme rezolvabile prin backtracking.
10.1 Problema taranului
Fie urmatoarea problema: Pe malul unui rau se afla un taran cu un lup, o capra si o varza. Taranul doreste sa traverseze cu ele raul. Taranul poate face traversari inot ale raului impreuna doar cu unul din cele trei personaje de transportat, sau singur. Daca lupul ramane pe acelasi mal impreuna cu capra si taranul este pe celalalt mal, capra va fi mancata de lup. Similar se intampla cu varza si capra. Sa se scrie un program Prolog care sa ii dea taranului toate solutiile de traversare.
Vom prezenta o rezolvare pe baza de backtracking. Definim starea problemei la un moment dat ca fiind: stare(Taran, Lup, Capra, Varza) unde Taran, Lup, Capra si Varza indica pozitia (malul) fiecarui element si pot avea valorile stang sau drept. In acest fel stim exact pe ce mal se afla fiecare dintre personaje. De remarcat ca aceasta este o structura si nu un predicat. De aici rezulta ca starea intiala este (de exemplu): stare(stang, stang, stang, stang) iar starea finala este: stare(drept, drept, drept, drept).
Rezolvarea problemei prin backtracking presupune incercarea unei miscari posibile in starea curenta si apoi reluarea algoritmului. O miscare genereaza o noua stare, din care se vor incerca apoi alte miscari. Daca se ajunge intr o stare finala, s a gasit o solutie. In acest caz trebuie facuta o revenire pentru a incerca gasirea altor solutii. Trebuie specificate, evident, starea initiala si starea sau starile finale.
Daca se ajunge intr o stare ilegala, cum ar fi de exemplu, in cazul acestei probleme, stare(stang, stang, drept, drept), se executa tot o revenire in starea anterioara. Acelasi lucru trebuie sa se intample si daca starea curenta este una in care s a mai ajuns o data. Nu are rost sa trecem de mai multe ori prin aceeasi configuratie deoarece acest lucru nu face decat sa lungeasca numarul de miscari necesare si poate conduce la bucle infinite.
Deci vom avea nevoie de un predicat care, dandu se o stare, sa stie sa execute miscarile posibile din acea stare. Fie acest predicat: miscare(Stare, StareUrmatoare).
Sa vedem cum se defineste predicatul miscare in cazul acestei problemei:
1) Taranul poate lua lupul cu el pe celalat mal: miscare(stare(Taran, Taran, Capra,Varza), stare(Taran1, Taran1, Capra,Varza)) : opus((Taran, Taran1).
2) Taranul poate lua capra cu el pe celalat mal: miscare(stare(Taran, Lup, Taran,Varza), stare(Taran1, Lup, Taran1, Varza)) : opus(Taran, Taran1).
3) Taranul poate lua varza cu el pe celalat mal: miscare(stare(Taran, Lup, Capra, Taran), stare(Taran1, Lup, Capra, Taran1)) : opus(Taran, Taran1).
4) Taranul poate traversa singur raul: miscare(stare(Taran, Lup, Capra, Varza), stare(Taran1, Lup, Capra, Varza)) : opus(Taran, Taran1).
Se observa ca am folosit predicatul opus(Mal1, Mal2), care determina malul opus unui mal dat. Definitia lui este: opus(stang, drept). opus(drept, stang).
Predicatele initiala(S) si finala(S) specifica daca o stare este sau nu initiala, respectiv finala. Definitiile lor sunt: initiala(stare(stang, stang, stang, stang)). finala(stare(drept, drept, drept, drept)).
Predicatul ilegala(Stare) spune daca o stare este ilegala:
1) Daca lupul este pe acelasi mal cu capra si taranul este pe celalalt mal: ilegala(stare(Taran, Lup, Lup, _ )) :- opus(Taran, Lup).
2) Daca varza este pe acelasi mal cu capra si taranul este pe celalalt mal: ilegala(stare(Taran, _ ,Capra, Capra)) :- opus(Taran, Capra).
Se poate scrie acum predicatul de gasire a unei succesiuni de mutari care rezolva problema. Deoarece nu trebuie sa repetam stari, predicatul va primi, pe langa starea curenta, si o lista de stari deja parcurse, intorcand lista de stari (Solutie) care constituie rezolvarea: lcv(+Stare, -Solutie, -Vizitate). unde Stare este starea curenta, Solutie este solutia ce trebuie gasita, iar Vizitate este lista de stari parcurse. Acest predicat se scrie tinand cont de:
1) Daca starea curenta este o stare finala, atunci solutie este lista de stari parcurse impreuna cu starea curenta: lcv(Stare, aStare|Vizitatei, Vizitate) :- finala(Stare).
2) Altfel genereaza o noua mutare, testeaza daca este legala, testeaza daca nu a mai fost parcursa si relanseaza cautarea din noua stare: lcv(Stare, Solutie, Vizitate) : miscare(Stare, StareUrmatoare), % o noua mutare not(ilegala(StareUrmatoare)), % este ilegala? not(member(StareUrmatoare, aStare | Vizitatei)), % este deja parcursa? lcv(StareUrmatoare, Solutie, aStare | Vizitatei). % reia cautarea
Predicatul member testeaza daca un element face parte dintr o lista si a fost detaliat anterior. Predicatul rezolva rezolva problema, intorcand lista starilor parcurse din starea initiala in starea finala, deci solutia problemei. rezolva(Solutie) :- initiala(Stare), lcv(Stare, Solutie, ai ).
10.2 Problema misionarilor si canibalilor
Trei misionari si trei canibali ajung la malul estic al unui rau. Aici se afla o barca cu doua locuri cu care se poate traversa raul (raul nu se poate traversarea inot deoarece in el traiesc pesti piranha). Daca pe unul dintre maluri numarul de canibali este mai mare decat numarul de misionari, atunci misionarii de pe acel mal vor fi mancati de canibali. Problema intreaba cum pot trece toti raul fara ca misionarii sa fie mancati de canibali. Pentru a afla solutia vom parcurge pasii de la problema anterioara:
Structura starii curente este: stare(MalBarca, NMisionariVest, NCanibaliVest, NMisionariEst, NCanibaliEst). rezolva(Solutie) :- initiala(Stare), mc(Stare, Solutie, ai ).

% mc(+Stare, -Solutie, +StariVizitate) mc(Stare, aStare | Vizitatei, Vizitate) :- finala(Stare). mc(Stare,Solutie,Vizitate) : miscare(Stare, StareUrmatoare), not(ilegala(StareUrmatoare)), not(member(StareUrmatoare, aStare | Vizitatei)), mc(StareUrmatoare, Solutie, aStare | Vizitatei).




% miscare(+Stare, -StareUrmatoare) miscare(stare(est, MV,CV, ME, CE), stare(vest, MV1, CV, ME1, CE)) : oameni(N), ME >= N, ME1 is ME - N, MV1 is MV + N. miscare(stare(est, MV, CV, ME, CE), stare(vest, MV, CV1, ME, CE1)) : oameni(N), CE >= N, CE1 is CE - N, CV1 is CV + N. miscare(stare(est, MV, CV, ME, CE), stare(vest, MV1, CV1, ME1, CE1)) : ME >= 1, CE >= 1, ME1 is ME - 1, MV1 is MV + 1,
CE1 is CE - 1, CV1 is CV + 1.

miscare(stare(vest, MV, CV, ME, CE), stare(est, MV1, CV, ME1, CE)) : oameni(N), MV >= N, MV1 is MV - N, ME1 is ME + N. miscare(stare(vest, MV, CV, ME, CE), stare(est, MV, CV1, ME, CE1)) : oameni(N), CV >= N, CV1 is CV - N, CE1 is CE + N. miscare(state(vest, MV, CV, ME, CE), stare(est, MV1, CV1, ME1, CE1)) : MV >= 1, CV >= 1, MV1 is MV - 1, ME1 is ME + 1,
CV1 is CV - 1, CE1 is CE + 1.

oameni(1). oameni(2).
% ilegala(+Stare) ilegala(stare( _ , MV,CV , _ , _ )) :- MV > 0, CV > MV. ilegala(stare( _ , _ , _ , ME, CE)) :- ME > 0, CE > ME.

initiala(stare(est, 0, 0, 3, 3)). finala(stare(vest, 3, 3, 0, 0)).
10.3 Problema galetilor cu apa
Exista doua galeti cu capacitatile de 8 si respectiv 5 litri, fara alte marcaje. Se cere sa se masoare exact 4 litri dintr un vas mare care contine cel putin 20 de litri. Operatiile admise sunt: umplerea unei galeti din vasul mare, golirea unei galeti in vasul mare si transferul continutului unei galeti in alta galeata, pana cand galeata din care se toarna s a golit complet, sau galeata in care se toarna s a umplut pana la refuz.
Pentru rezolvarea problemei vom urmari aceeasi schema de la cele doua problemele anterioare. rezolva(Solutie) :- initiala(Stare), galeti(Stare, ai, Solutie). galeti(Stare, Solutie, Vizitate) :- miscare(Stare, StareUrmatoare), % se genereaza starea urmatoare not(member(StareUrmatoare, aStare | Vizitatei)), % starea a mai fost generata? galeti(StareUrmatoare, Solutie, aStare | Vizitatei).
% daca nu, mergem mai departe
O stare este o structura cu cinci campuri: stare(D1, D2, G1, G2, M), care au urmatoare semnificatie: D1 = cati litri de apa sunt in galeata 1, D2 = cati litri de apa sunt in galeata 2, G1 = capacitatea galetii 1, G2 = capacitatea galetii 2, M = mesajul explicativ asociat tranzitiei in aceasta stare (M spune ce actiune a fost executata cand s-a trecut in aceasta stare).
% miscare(+Stare,-StareUrmatoare)
% Regulile comentate nu sunt necesare, ele generand solutii sau executii mai lungi. miscare(stare(D1, D2, G1, G2, _ ), stare(0, D2, G1, G2, 'Golesc G1 in vas.')). miscare(stare(D1, D2, G1, G2, _ ), stare(D1, 0, G1, G2, 'Golesc G2 in vas.')). miscare(stare(D1, D2, G1, G2, _ ), stare(G1, D2, G1, G2, 'Umplu G1 din vas.')). miscare(stare(D1, D2, G1, G2, _ ), stare(D1, G2, G1, G2, 'Umplu G2 din vas.')). miscare(stare(D1, D2, G1, G2, _ ), stare(0, D22, G1, G2, 'Golesc G1 in G2.')) : D1 > 0, D22 is D1 + D2, D22 =< G2.
% miscare(stare(D1, D2, G1, G2, _ ), stare(D11, 0, G1, G2, 'Golesc G2 in G1.')) : % D2 > 0, D11 is D1 + D2, D11 =< G1. miscare(stare(D1, D2, G1, G2, _ ), stare(D11, G2, G1, G2, M)) : T is G2 - D2, D11 is D1 - T, D11 > 0, string_ term(S, T), concat(a'Torn', S, ' litri din G1 in G2.'i, M).
% miscare(stare(D1, D2, G1, G2, _ ), stare(G1, D22, G1, G2, M)) : % T is G1 - D1, D22 is D2 - (G1 - D1), D22 > 0,
% string_ term(S, T), concat(a'Torn', S, ' litri din G2 in G1.'i, M).

Se observa ca problema a fost rezolvata pentru cazul general, putand varia atat capacitatile galetilor, cat si numarul de litri de apa care trebuie masurat.
% Starea initiala initiala(stare(0, 0, 8, 5, 'Galetile sunt goale.')).
% Starea finala finala(stare(4, _ , _ , _ , _ )). finala(stare( _ , 4, _ , _ , _ )).

Functia member a fost rescrisa deoarece mesajul asociat unei stari nu trebuie sa influenteze procesul de rezolvare a problemei. O stare este data de fapt doar de primii doi parametrii ai structurii stare, urmatorii doi fiind doua valori constante, iar ultimul, mesajul, a fost introdus doar pentru a descrie rezolvarea problemei. Deci putem ajunge in aceeasi stare cu doua mesaje diferite. member(stare(X, Y, _ , _ , _ ), astare(X, Y, _ , _ , _ ) | _ i ). member(X, a _ | Resti ) :- member(X, Rest).

Pentru afisarea frumoasa a solutiilor se adauga predicatele: run :- rezolva(Solutie), scrie_solutie(Solutie).

scrie_solutie(ai) :- nl. scrie_solutie(aS | Resti) :- scrie_solutie(Rest), scrie_stare(S).

scrie_stare(stare(X, Y, _ , _ )) :- write(X), write($ $), write(Y), nl.
Functionarea programului este urmatoarea:
?- run.
0 0 Galetile sunt goale.
8 0 Umplu G1 din vas.
3 5 Torn 5 litri din G1 in G2.
3 0 Golesc G2 in vas.
0 3 Golesc G1 in G2.
8 3 Umplu G1 din vas.
6 5 Torn 2 litri din G1 in G2.
6 0 Golesc G2 in vas.
1 5 Torn 5 litri din G1 in G2.
1 0 Golesc G2 in vas.
0 1 Golesc G1 in G2.
8 1 Umplu G1 din vas.
4 5 Torn 4 litri din G1 in G2. yes
10.4 Exercitii propuse
EP1. Fie un graf neorientat definit de legaturile dintre nodurile sale, exprimate ca fapte Prolog: leg(a, b). leg(b, c). leg(a, c). leg(a, d).
1) Sa se defineasca predicatul drum(N1, N2), care verifica daca exista un drum in graf intre nodurile N1 si N2.
2) Sa se critice urmatoarea implementare a predicatului drum: drum(N, N). drum(N1, N2) :- leg(N1, N), drum(N, N2).
3) Sa se modifice definitia predicatului drum la drum(N1, N2, ListaN) astfel incat sa se obtina in ListaN lista nodurilor parcurse intre N1 si N2, in cazul in care exista un drum in graf intre nodurile N1 si N2, si lista vida in caz contrar.
EP2. Studiind texte vechi, in incercarea de a reconstitui arborele genealogic al unei familii boieresti, un istoric si-a notat pe fise separate, relatiile de rudenie dintre diferitele persoane ale unei singure familii, sub forma a trei tipuri de relatii: x este sotul (sotia) lui y x este fiul (fiica) lui y x este fratele (sora) lui y unde x si y sunt prenumele unor membri din aceeasi familie.
In ipoteza ca nu exista doua persoane cu acelasi prenume, sa se scrie un program care citeste dintr-un fisier relatii de rudenie intre perechi de persoane si care stabileste urmatoarele:
- daca informatiile privind relatiile de rudenie sunt compatibile sau contradictorii, stiind ca in familie nu s-au facut casatorii intre rude. In caz ca informatiile sunt contradictorii se da mesajul 'Informatii incompatibile' si nu se mai fac alte operatii privind familia in cauza;
- daca informatiile nu sunt complete pentru alcatuirea arborelui genealogic, se afiseaza numai mesajul 'Informatii incomplete'. Daca datele permit alcatuirea arborelui genealogic, se afiseaza mai intai relatiile de rudenie deduse in afara celor date initial si se afiseaza arborele genealogic sub o forma sugestiva. (Concurs de Programare UPB 1995, Etapa locala).
11 Strategii de cautare in spatiul starilor
Unul dintre cele mai utilizate modele de rezolvare a problemelor este prin reprezentarea cautarii sub forma unui graf orientat in care nodurile sunt stari succesive in rezolvare iar arcele corespund tranzitiilor sau operatorilor legali ce pot fi aplicati pentru trecerea dintr-o stare in alta aFlo93,LS93i.
11.1 Cautarea solutiilor in spatiul starilor
Pentru a construi o descrierea unei probleme rezolvate prin reprezentare in spatiul starilor trebuie parcurse urmatoarele etape:
1) Se defineste spatiul starilor care contine toate toate configuratiile posibile ale obiectelor relevante (si poate si unele imposibile). Spatiul se poate defini explicit prin indicarea tuturor starilor (acesta fiind un caz particular si rar intalnit in practica) sau implicit prin indicarea transformarilor care genereaza o noua stare dintr-o stare data.
2) Se specifica una sau mai multe stari din spatiu care descriu situatii posibile de la care poate porni procesul de rezolvare a problemei (starea initiala).
3) Se specifica una sau mai multe stari care ar fi acceptabile ca solutii ale problemei. Aceste stari se numesc stari scop (sau stari finale sau scopuri).
4) Se specifica un set de reguli care descriu actiunile (operatorii) disponibile si care definesc tranzitiile sau transformarile intre stari. In aceasta etapa trebuie analizate urmatoarele probleme:
• Ce presupuneri nedeclarate explicit sunt prezente in descrierea informala a problemei?
• Cat de generale trebuie sa fie regulile?
• Cat de mult din calculul necesar rezolvarii problemei ar trebui facut inainte si reprezentat in reguli?
Problema poate fi rezolvata apoi utilizand o strategie de control adecvata, pentru a parcurge o parte din spatiul starilor (eventual tot) pana cand este gasita o cale de la o stare initiala la o stare finala, in cazul in care problema admite solutie. Cautarea este un mecanism general care poate fi utilizat atunci cand o metoda directa (determinista) de rezolvare a problemei nu este cunoscuta. In acelasi timp, ea ofera cadrul in care pot fi incapsulate metode mai directe de rezolvare a anumitor parti ale problemei (subproblemelor).
11.2 Cautare prin backtracking
Rezolvarea unei probleme folosind strategia de backtracking a fost expusa in capitolul 10. In continuare se prezinta schema generala a unei astfel de cautari care poate fi aplicata pentru orice descriere a unei subprobleme in termeni de stari initiale, stari finale si operatori sau tranzitii intre stari. Pentru ilustrare, se defineste explicit un spatiu de cautare finit, ipotetic, prin definirea predicatului succ(stare, stare_urmatoare). O definire a predicatului succ specifica unei probleme particulare cuplata cu schema generala de rezolvare prin backtracking ce este prezentata in continuare rezolva problema.
% Graful care descrie spatiul de cautare complet. succ(a,b). % a stare initiala succ(b,c). succ(c,d). succ(d,g). succ(a,e). succ(e,f). succ(f,g). final(g). % Stare finala

% rez(+Stare, -Sol) rez(Stare, Sol) :- bkt(Stare, ai, Sol). bkt(Stare, Drum, aStare | Drumi) :- final(Stare). bkt(Stare, Drum, Sol) :- succ(Stare, N1), not (member(N1, Drum)), bkt(N1, aStare | Drumi, Sol).

?- rez(a,Sol).
Sol=ag,d,c,b,ai
Cautarea prin backtracking poate fi cuplata cu impunerea unei adancimi maxime de cautare, necesara in special in cazul spatiilor de cautare infinite in care solutia poate sa nu se afle pe ramura curenta pe care a inceput cautarea. Stabilirea unei adancimi maxime de cautare se poate face astfel:
% rez1(Stare, Sol) impune o adancimea maxima Max = 10 rez1(Stare, Sol) :- bkt1(Stare, Sol,10). bkt1(Stare, aStarei, _ ) :- final(Stare). bkt1(Stare, aStare | Soli, Max) :-
Max > 0, succ(Stare, N1),
Max1 is Max-1, bkt1(N1, Sol, Max1).
In acest caz, s-a eliminat testul de stari anterior parcurse deoarece, existand o adancime maxima de cautare se elimina buclele dar cu penalizarea eventuala a reparcurgerii unor stari. Exercitiul propus 1 al acestui capitol va cere o combinare a acestor doua solutii.
11.3 Cautare pe nivel si in adancime
Pentru implementarea acestor doua strategii de cautare de baza se folosesc doua liste: lista OPEN a nodurilor explorate in cautare (sau FRONTIERA portiunii cunoscute a spatiului de cautarea la un moment dat cu partea necunoscuta inca a acestui spatiu) si lista CLOSED a nodurilor expandate (sau TERITORIUL portiunii cunoscute din spatiul de cautare). Detalii suplimentare asupra acestor strategii de cautare se pot gasi in aFlo93i. Pentru a putea obtine calea de la starea initiala la starea finala, odata ce s-a gasit starea finala, ambele liste vor contine perechi aStare, Predecesori, unde Predecesor este starea predecesoare a starii Stare. Pentru starea initiala se va introduce in OPEN perechea aStareInitiala, nili, cu nil o constanta arbitrar fixata.
Implementarea foloseste doua tipuri de date, stiva si coada, pentru exploatarea listei OPEN, respectiv stiva in cazul cautarii in adancime si coada in cazul cautarii pe nivel. Definirea operatiilor tipice asociate acestor structuri de date abstracte in Prolog este pusa in evidenta de implementarea ce urmeaza.
% Cautare pe nivel si in adancime
% Se folosesc tipurile de date abstracte Stack and Queue.

% TDA Stack
% emptys(+S) - testeaza daca stiva este vida
% emptys(-S) - initializeaza stiva emptyst(ai).
% stack(-Top, -SNou, +S) - are efect de pop
% stack(+Top, +S, -SNoua) - are efect de push
% stack(-Top, _, +S) - are efect de top stack(Top, Stack, aTop | Stacki).

% TDA Queue
% empty(+Q) - testeaza daca coada este vida
% empty(-Q) - initializeaza coada emptyq(ai).
% enqueue(+El, +Q, -QNoua) introduce un element in coada enqueue(El, ai, aEli). enqueue(El, aX | Resti, aX | R1i) :- enqueue(El, Rest, R1).
% dequeue(-El, +Q, -QNoua) elimina un element din coada dequeue(El, aEl | Ri, R).

% Spatiul de cautare succ(a, b). succ(b, c). succ(c, d). succ(d, g). succ(a, e). succ(e, f). succ(f, g). final(g).

% Rezolvare cu parcurgere pe nivel
% rezbreadth(+StareInitiala) rezbreadth(Si) :- emptyq(Open), emptyq(Closed), enqueue(aSi, nili, Open, Open1), breadth(Open1, Closed). breadth(Open, _) :- emptyq(Open), !, write('Nu exista solutie'), nl. breadth(Open, Closed) :- dequeue(aStare | Predeci, Open, _), final(Stare),
write('S-a gasit o solutie'), nl, scriecale(aStare | Predeci, Closed). breadth(Open, Closed) : dequeue(aStare| Predeci, Open, _), final(Stare),
write('S-a gasit o solutie'),nl, showpath(aStare| Predeci, Closed). breadth(Open, Closed) : dequeue(aStare, Predi, Open, RestOpen), enqueue(aStare, Predi, Closed, Closed1), listsucc(Stare, RestOpen, Closed1, LSucc), append(RestOpen, Lsucc, Open1), breadth(Open1, Closed1). listsucc(Stare, RestOpen, Closed, Lsucc) : bagof(aS, Starei, (succ(Stare, S), not member(aS,_i, RestOpen), not member(aS, _i, Closed) ), LSucc). listsucc(Stare, RestOpen, Closed, ai).

% Rezolvare cu parcurgere pe in adancime
% rezdepth(+StareInitiala) rezdepth(Si) :- emptyst(Open), emptyst(Closed), stack(aSi, nili, Open, Open1), depth(Open1, Closed). depth(Open, _) :- emptyst(Open), write('Nu exista solutie'), nl. depth(Open, Closed) :- stack(aStare | Predeci, RestOpen, Open), final(Stare),

write('S-a gasit o solutie'), nl, scriecale(aStare | Predeci, Closed). depth(Open, Closed) :- stack(aStare, Predi, RestOpen, Open), stack(aStare, Predi, Closed, Closed1), listsucc(Stare, RestOpen, Closed1, LSucc), append(LSucc, RestOpen, Open1), depth(Open1, Closed1).
% Afiseaza calea de la Si la Sf
% scriecale(+Cale, +Closed) scriecale(aS, nili, _) :- scrie(S), nl. scriecale(aS, Predeci, Closed) :- member(aPredec, Pi, Closed), scriecale(aPredec, Pi, Closed), scrie(S), nl. scrie(S) :- write(S). member(El, aEl | _i). member(El ,a_ | Resti) :- member(El, Rest). append(ai, L, L). append(aX | L1i, L2, aX | L3i) :- append(L1, L2, L3).

?- rezbreadth(a). a e f g
?- rezdepth(a). a b c d g
Strategia de cautare pe nivel este o strategie completa care, in plus, gaseste calea de la starea initiala la starea finala cu numarul minim de tranzitii de stari. Strategia de cautare in adancime nu este completa pentru orice spatii de cautare dar consuma mai putina memorie decat cea in adancime.
Pentru a pune in evidenta diferenta intre strategia de cautare in adancime si cea de backtracking, se va rescrie schema generala de backtracking din sectiunea precedenta pe acelasi sablon cu cel de la strategiile precedente. rezbkt(Si) :-emptyst(Open), stack(Si, Open, NewOpen), bkt(NewOpen). bkt(Open) :- stack(S, _, Open), final(S), write('S-a gasit o solutie'), afis(Open). bkt(Open) :- stack(S, _, Open), succ(S, S1), not member(S1, Open), stack(S1, Open, NewOpen), bkt(NewOpen). afis(ai) :- nl. afis(aS | Resti) :- afis(Rest), scrie(S).
Se observa ca, deoarece strategia de backtracking pastreaza numai starile de pe calea curenta de cautare si face testul pentru stari anterior parcurse numai pe aceasta cale, este suficient mentinerea numai a listei OPEN. In plus, aceasta lista nu mai trebuie sa fie o lista de perechi aStare, Predecesori ci numai o lista de stari parcurse, la detectia starii finale continutul lui OPEN fiind chiar calea spre solutie. Implementarea de mai sus este echivalenta cu cea din sectiunea precedenta. Predicatul afis permite afisarea starilor parcurse in ordinea de la starea initiala la cea finala.
11.4 Algoritmul A*
A* este un algoritm de cautare in spatiul starilor a solutiei de cost minim (solutia optima) ce utilizeaza o functie euristica de estimare a distantei starii curent parcurse fata de starea finala. Pe parcursul cautarii, starile sunt considerate in ordinea crescatoare a valorii functiei f(S)=g(S)+h(S), unde g(S) este functia de cost a portiunii parcurse pana in starea S iar h(S) este functia euristica de estimare a distantei din S la starea finala. Functia euristica trebuie sa fie admisibila (mai mica sau cel mult egala cu distanta reala) pentru a garanta optimalitatea solutiei. O prezentare sistematica si detalii suplimentare asupra algoritmului A* pot fi gasite in aFlo93i. Desi are o complexitate timp tot exponentiala, ca orice procedura de cautare, algoritmul este mai eficient, in general, decat stategiile neinformate datorita componentei euristice care ghideaza cautarea.
Trebuie observat ca algoritmul A* poate fi aplicat si in cazul in care intr-o problema nu exista costuri, considerand implicit toate costurile tranzitiilor de stari egale cu 1. In acest caz, rezultatele algoritmului sunt similare cu cele ale parcurgerii pe nivel, in sensul gasirii drumului de la starea initiala la starea finala cu un numar minim de tranzitii de stari, dar cautarea este, in general, mai rapida. Programul Prolog ce urmeaza presupune, implicit, costurile tranzitiilor de stari egale cu 1.
Implementarea algoritmului A* se va face pe baza schemelor anterioare de cautare, cu urmatoarele modificari. In loc de a exploata lista OPEN ca o stiva sau ca o coada, acesta lista va fi tratata ca o lista de prioritati in functie de f(S). Fiecarei tranzitii de stari i se va asocia un cost, in functie de problema, si fiecarei stari i se va asocia o valoare pentru functia euristica h(S). Lista OPEN va fi o lista de liste de patru elemente de forma aStare, Predecesor, G, H, Fi. La fel ca in cazurile precedente, programul va fi exemplificat pe un spatiu de cautare ipotetic pentru ca in capitolul urmatoar schema generala de cautare sa fie aplicata pe probleme reale.
% Spatiul de cautare succ(a, b). succ(b, c). succ(c, d). succ(d, g). succ(a, e). succ(e, f). succ(f, g). final(g).

% Valorile functiei euristice folosite in algoritmul A* euristic(a, g, 3). euristic(b, g, 3). euristic(c, g, 2). euristic(d, g, 1). euristic(g, g, 0). euristic(e, g, 2). euristic(f, g, 1). euristic(_, _, 0).

% Coada de prioritati (coada este sortata crescator in functie de cheia F1). inspq(El, ai, aEli). inspq(El, aX | Resti, aEl, X | Resti) :- precedes(El, X), !. inspq(El, aX | Resti, aX | R1i) :- inspq(El, Rest, R1). precedes(a_, _, _, _, F1i, a_, _, _, _, F2i) :- F1<F2.

rezastar(Si, Scop) :- emptyq(Open), emptyq(Closed), euristic(Si, Scop, H), inspq(aSi, nil, 0, H, Hi, Open, Open1), astar(Open1, Closed, Scop).

astar(Open, _, _) :- emptyq(Open), !, write('Nu exista solutie'), nl. astar(Open, Closed, Scop) :- dequeue(aS, Pred, _, _, _i, Open, _),
S=Scop,
write('S-a gasit o solutie'), nl, scriecale1(aS, Pred, _, _, _i, Closed). astar(Open, Closed, Scop) :- dequeue(aS, Pred, G, H, Fi, Open, RestOpen), inspq(aS, Pred, G, H, Fi, Closed, Closed1),
(bagof(aUrmator, H1i, (succ(S, Urmator), euristic(Urmator, Scop, H1)), LSucc),!,
G1 is G+1, actual_toti(S, G1, LSucc, RestOpen, Closed1, OpenR, ClosedR);
OpenR=RestOpen, ClosedR=Closed1), astar(OpenR, ClosedR, Scop).

actual_toti(_, _, ai, Open, Closed, Open, Closed) :- !. actual_toti(Stare, G, aaS, Hi | Resti, Open, Closed, OpenR, ClosedR) :- actual(Stare, G, aS, Hi, Open, Closed, Open1, Closed1), actual_toti(Stare, G, Rest, Open1, Closed1, OpenR, ClosedR). actual(Stare, G, aS, Hi, Open, Closed, OpenR, Closed) :- member(aS, Pred, G1, _, _i, Open), !,
( G1=<G, OpenR=Open, !;
F is G+H, elim(aS, Pred, G1, _, _i, Open, Open1), inspq(aS, Stare, G, H, Fi, Open1, OpenR)).

actual(Stare, G, aS, Hi, Open, Closed, OpenR, ClosedR) :- member(aS, Pred, G1, _, _i, Closed), !,
( G1=<G, ClosedR=Closed, OpenR=Open, !;
F is G+H, elim(aS, Pred, G1, _, _i, Closed, ClosedR), inspq(aS, Stare, G, H, Fi, Open, OpenR)).

actual(Stare, G, aS, Hi, Open, Closed, OpenR, Closed) :-
F is G+H, inspq(aS, Stare, G, H, Fi, Open, OpenR).

scriecale1(aS, nil, _, _, _i, _) :- scrie(S), nl. scriecale1(aS, Pred, _, _, _i, Closed) :- member(aPred, P, _, _, _i, Closed), scriecale1(aPred, P, _, _, _i, Closed), scrie(S), nl. scrie(S) :- write(S). elim(_, ai, ai). elim(X, aX | Resti, Rest) :- !. elim(X, aY | Resti, aY | Rest1i) :- elim(X, Rest, Rest1).
?- rezastar(a). a e f g

11.5 Exercitii propuse
EP1. Sa se rescrie schema de cautare cu backtracking combinand varianta care memoreaza lista starilor parcurse cu cea care impune o adancime maxima de cautare.
EP2. Adancimea maxima de cautare trebuie impusa, in anumite cazuri (spatii de cautare infinite) si pentru cautarea in adancime. Sa se modifice schema cautarii in adancime prin includerea unei adancimi maxime de cautare.
EP3. Strategiile de cautare backtracking, in adancime si pe nivel prezentate determina prima solutie, in cazul in care problema admite solutie. Sa se modifice programele corespunzatoare acestor strategii astfel incat sa determine si sa afiseze toate solutiile problemei, daca problema admite mai multe solutii.
EP4. Sa se modifice algoritmul A* din sectiunea 11.4 prin adaugarea unui cost explicit, diferit de 1, tranzitiilor de stari.
12 Utilizarea cautarii in rezolvarea problemelor
In acest capitol se aplica schemele de strategii de cautare dezvoltate in capitolul precedent pentru rezolvarea unor probleme "clasice" in inteligenta artificiala. In acest scop, pentru fiecare problema, se indica reprezentarea starilor cu identificarea starii initiale si a starii finale, se definesc tranzitiile legale de stari si, acolo unde este cazul, costuri si functii euristice de ghidare a cautarii.
12.1 Problema misionarilor si canibalilor
Reluam problema misionarilor si canibalilor din sectiunea 10.2 si aratam cum se poate rezolva acesta problema pe baza schemelor de cautare prezentate in capitolul 11. Spre deosebire de rezolvarea din sectiunea 10.2, se modifica reprezentarea unei stari a problemei din stare(MalBarca, NMisionariVest, NCanibaliVest, NMisionariEst, NCanibaliEst)
in stare(MalBarca, NMisMalBarca, NCanMalbarca, NMisMalOpus, NCanMalOpus) unde MalBarca poate fi, la fel ca inainte, est sau vest. Implementarea care urmeaza pune in evidenta avantajele acestei noi reprezentari din punct de vedere al simplificarii prelucrarilor necesare.
Se considera starea initiala in care cei trei misionari si canibali sunt pe malul de est, ei dorind sa ajunga pe malul de vest fara ca misionarii sa fie mancati de canibali.
% Stare initiala initial(st(est, 3, 3, 0, 0)).
% Starea finala final(st(vest, 3, 3, 0, 0)).

% Malurile opuse opus(est, vest). opus(vest, est).

% sigur(+NrMisionari, +NrCanibali) - stare sigura sigur(0, _ ). sigur(X, Y) :- X > 0, X >= Y.

% Tranzitii intre stari, succ(+StareCurenta, - StareUrmatoare)
% muta doi misionari succ(st(X, MX, CX, MY, CY), st(Y, MY1, CY, MX1, CX)) :- opus(X, Y), modifica(2, MX, MY, MX1, MY1), sigur(MX1, CX), sigur(MY1, CY).
% muta doi canibali succ(st(X, MX, CX, MY, CY), st(Y, MY, CY1, MX, CX1)) : opus(X, Y), modifica(2, CX, CY, CX1, CY1), sigur(MX, CX1), sigur(MY, CY1).
% muta un misionar si un canibal succ(st(X, MX, CX, MY, CY), st(Y, MY1, CY1, MX1, CX1)) :- opus(X, Y), modifica(1, MX, MY, MX1, MY1), modifica(1, CX, CY, CX1, CY1), sigur(MX1, CX1), sigur(MY1, CY1).
% muta un misionar succ(st(X, MX, CX, MY, CY), st(Y, MY1, CY, MX1, CX)) :- opus(X, Y), modifica(1, MX, MY, MX1, MY1), sigur(MX1, CX), sigur(MY1, CY).
% muta un canibal succ(st(X, MX, CX, MY, CY), st(Y, MY, CY1, MX, CX1)) :- opus(X, Y), modifica(1, CX, CY, CX1, CY1), sigur(MX, CX1), sigur(MY, CY1).

% modifica(+Cati, +NrInit1, +NrInit2, -NrRez1, -NrRez2) modifica(N, NX, NY, NX1, NY1) :- NX >= N, NX1 is NX - N, NY1 is NY + N.

% Afiseaza solutia
% Predicatul scrie din capitolul precedent se redefineste astfel: scrie(st(B, MX, CX, MY, CY)) :- nl, scrielista(aB, ' ', MX, ' ', CX, ' ', MY, ' ', CYi). scrielista(ai). scrielista(aX | Resti) :- write(X), scrielista(Rest).
Rezolvarea problemei este acum imediata folosind schemele de cautare din capitolul precedent. Selectia predicatului rezbkt conduce la o rezolvare echivalenta cu cea prezentata in sectiunea 10.2.
% Se alege strategia dorita (nivel, adancime sau backtracking) solutie :- nl, initial(Si), rezbreadth(Si).
% rezdepth(st(est, 3, 3, 0, 0)).
% rezbkt(Si).
Pentru a aplica o strategie A* se fixeaza implicit costuri de 1 pentru fiecare tranzitie intre doua stari si trebuie definita o functie euristica admisibila. Se noteaza cu Ne numarul persoanelor de pe un anumit mal (misionari plus canibali) in starea S si se definesc urmatoarele trei functii euristice : h1(S) = nE(S), numarul de persoane de pe malul de est in starea S h2(S) = nE(S) ? 2 nE(S) + 1, daca barca este la vest si nE(S) ? 0 h3(S) = nE(S) - 1, daca barca este la est si nE(S) ? 0
0 daca nE(S) = 0
Functia h1 nu este admisibila, functiile h2 si h3 sunt admisibile si monotone, cu h3 functie euristica mai informata decat h2. Pentru a rezolva problema cu algoritmul A* se defineste, de exemplu, functia h3 in Prolog, se pastreza specificatiile anterioare ale problemei si se foloseste strategia de cautare informata definita in sectiunea 11.4. Solutia obtinuta este minima (optima) in termeni de numar de tranzitii de stare.
% Functia euristica h3 euristic(st(est, MX, CX, _ , _ ), Sfin, H) : - final(Sfin), Ne is MX + CX, (Ne \= 0, !, H is Ne - 1; H = 0). euristic(st(vest, _ , _ , MY, CY), Sfin, H) : - final(Sfin), Ne is MY + CY, (Ne \= 0, !, H is Ne + 1; H=0).
% Rezolvare solutie :- nl, initial(Si), final(Sfin), rezastar(Si, Sfin).
12.2 Problema mozaicului cu opt cifre
Problema mozaicului cu opt cifre consta in gasirea mutarilor de executat pentru a ajunge dintr o configuratie initiala a mozaicului intr-o configuratie finala. O configuratie finala poate fi cea prezentata in figura 11.
1 2 3
8 4
7 6 5
Figura 11. O configuratie finala a mozaicului cu opt cifre
Aceasta problema, desi aparent simpla, are un spatiu de cautare foarte mare in cazul general si este un bun exemplu ce pune in evidenta utilitatea folosirii functiilor euristice de ghidare a cautarii. In sectiunile ce urmeaza vom arata cum se rezolva problema cu diverse strategii si vom pune in evidenta incapacitatea strategiilor de cautare neinformate de a gasi solutia pentru multe din configuratiile initiale propuse. Va sugeram sa testati, pentru diverse pozitii initiale, care metode reusesc si care nu. Proprietatile importante care determina esuarea unor metode si reusita altora sunt numarul de stari generate, numarul de apeluri recursive si numarul de mutari care rezolva problema.
Problema nu contine costuri dar, daca ne intereseaza rezolvarea mozaicului printr-un numar minim de miscari (tranzitii de stare) se poate considera costul tuturor tranzitiilor egal cu 1. Pentru rezolvarea problemei printr-un algoritm A* se pot defini urmatoarele functii euristice alternative aBra88i:
1) h1(S) = numarul de cifre care nu sunt la locul lor in starea S fata de starea finala;
2) h2(S) = suma distantelor, pe orizontala si verticala, la care se afla cifrele in starea S fata de pozitia lor finala (distanta Manhattan);
3) h3(S) = h2(S) + 3 ? D(S), unde D(S) este suma din d(c) pentru fiecare cifra c, mai putin ultima, in starea S. Functia d(c) se calculeaza astfel:
• Daca c este pe pozitia in care se va afla in final spatiul liber, atunci d(c) = 1;
• Daca c este pe pozitia p, iar c + 1 se afla pe pozitia p + 1, atunci d(c) = 0;
• Daca c este pe pozitia p, iar c + 1 nu se afla pe pozitia p + 1, atunci d(c) = 2.

Functiile h1 si h2 sunt admisibile, h2 fiind mai informata decat h1 deoarece incearca sa surprinda si dificultatea structurala a configuratiei initiale. Functia h3 nu este admisibila deoarece in unele situatii este mai mare decat h* (distanta reala). Ea este utila deoarece este mai informata decat h2 si ajunge mult mai repede la o solutie buna, chiar daca solutia nu este optima (solutia este destul de aproape de solutia optima). Asa cum se va vedea mai departe, h3 este singura functie care poate rezolva problema pentru anumite configuratii initiale.
In continuare se va prezenta rezolvarea acestei probleme prin trei metode neinformate, backtracking, parcurgere in adancime, parcurgere pe nivel, apoi rezolvarea pe baza algoritmului A*, utilizand functiile h1, h2 si h3.
Se va vedea ca o cautare in adancime functioneaza pana la maxim 2 3 mutari necesare pentru rezolvarea problemei, cautarea pe nivel poate ajunge pana la 5 6 mutari si A* cu h1 pana la 8 9 mutari, cu h2 pana la 18 20 de mutari, iar cu h3 chiar pana la 25 de mutari.
12.3 Rezolvarea problemei mozaicului prin cautari neinformate
O prima posibilitate de reprezentare a starilor problemei ar fi printr-o lista de perechi, o pereche fiind de forma apozitie, cifrai, unde pozitie este pozitia pe tabla (de exemplu p1,...p9) iar cifra este cifra existenta intr-o anumita stare pe acea pozitie. Astfel, starea finala s-ar specifica in Prolog prin: final(aap1,1i,ap2,2i,ap3,3i,ap4,8i,ap5,bli,ap6,4i,ap7,7i,ap8,6i,ap9,5ii)
O astfel de reprezentare este mai intuitiva si mai convenabila pentru o afisare frumoasa a solutiei dar este ineficienta pentru prelucrare. Din acest motiv vom folosi o reprezentare diferita, respectiv o lista ordonata de tipul:
apozitie_blanc, pozitie_cifra1, ..., pozitie_cifra9i de exemplu, pentru starea finala, avem definitia: final(a5, 1, 2, 3, 6, 9, 8, 7, 4i).
Pentru a putea testa problema pe mai multe stari initiale, predicatul initial va avea un paramentru suplimentar, respectiv numarul unei stari initiale. In continuare se definesc starile initiale alternative, starea finala, tranzitiile de stari si un predicat de afisare "frumoasa" a mozaicului.
% Definirea problemei mozaicului cu 8 cifre
% Stare initiala 1 - merge cu orice strategie initial(s1, a1, 4, 2, 3, 6, 9, 8, 7, 5i).
% Stare initiala 2 - merge cu orice strategie initial(s2, a3, 4, 1, 2, 6, 9, 8, 7, 5i).
% Stare initiala 3 - merge cu parcurgere pe nivel si in adancime, dar nu cu bkt initial(s3, a5, 1, 6, 2, 3, 9, 8, 7, 4i).
% Stare initiala 4 - merge cu parcurgere pe nivel, nu merge cu adancime si bkt initial(s4, a8, 4, 1, 3, 6, 9, 5, 7, 2i).
% Stare initiala 4.1 - merge cu nivel si adancime, dar nu cu bkt initial(s5, a5, 1, 6, 2, 3, 9, 8, 7, 4i).
% Stare initiala 6 - nu merge cu nici o strategie neinformata initial(s6, a9, 4, 2, 6, 8, 5, 1, 3, 7i).
% Stare initiala 7 - nu merge cu nici o strategie neinformata initial(s7, a2, 4, 5, 6, 3, 9, 8, 7, 1i).
% Stare finala final(a5, 1, 2, 3, 6, 9, 8, 7, 4i).

% muta(+Tabla, -TablaNoua, +Sens) - muta cifra care se poate muta in sensul Sens muta(Tabla, TablaN, Sens) :-
Tabla = aPoz | Resti, detlist(Sens, L), not member(Poz, L), calc(Poz, Poz1, Sens), repl(Poz1, Poz, Rest, Rest1),
TablaN = aPoz1 | Rest1i, !.

% succ(+Tabla, -TablaNoua) - determina urmatoarea stare a tablei succ(S, S1) :- (muta(S, S1, stg); muta(S, S1, dr); muta(S, S1, sus); muta(S, S1, jos)).

% detlist determina pozitiile din care nu se poate muta in Sens detlist(stg, a1, 4, 7i) :- !. detlist(dr, a3, 6, 9i) :- !. detlist(sus, a1, 2, 3i) :- !. detlist(jos, a7, 8, 9i).

% calc(+Poz, -Poz1, +Sens) - calculeaza noua pozitie dupa mutare in Sens calc(Poz, Poz1, stg) :- !, Poz1 is Poz-1. calc(Poz, Poz1, dr) :- !, Poz1 is Poz+1. calc(Poz, Poz1, sus) :- !, Poz1 is Poz-3. calc(Poz, Poz1, jos) :- !, Poz1 is Poz+3.

% repl - actualizeaza pozitia pe tabla repl(Poz1, Poz, aPoz1 | Resti, aPoz | Resti) :- !. repl(X, X1, aY | Resti, aY | Rest1i) :- repl(X, X1, Rest, Rest1).

% Afisare poza(I) :- pozitie(1, I, V1), pozitie(2, I, V2), pozitie(3, I, V3), pozitie(4, I, V4), pozitie(5, I, V5), pozitie(6, I, V6), pozitie(7, I, V7), pozitie(8, I, V8), pozitie(9, I, V9), nl, tab(10), write(V1), write(' '), write(V2), write(' '), write(V3), nl, tab(10), write(V4), write(' '), write(V5), write(' '), write(V6), nl, tab(10), write(V7), write(' '), write(V8), write(' '), write(V9), nl. pozitie(X, I, V) :- poz(X, I, 0, N), (N = 0, !, V = ' '; V = N). poz(X, aX | _ i, N, N) :- !. poz(X, aY | Resti, N, NR) :- N1 is N+1, poz(X, Rest, N1, NR).

Predicatul poza(I) permite afisarea sub forma de mozaic a unei stari. El poate fi folosit in afisarea solutiei definind predicatul scrie(S) astfel: scrie(S) :- poza(S).
La cautarea in adancime se fac foarte multe mutari pana la starea finala. Din aceasta cauza nu se poate folosi pentru orice configuratie initiala afisarea cu predicatul poza (este generata eroarea Not enough stack) si este necesara pastrarea variantei initiale de definire a lui scrie in care afisarea starilor se va face sub forma de lista, cu semnificatia definita in sectiunea precedenta.
Solutia problemei, pentru o stare initiala, NrS, fixata dintre cele definite (s1, s2,...), se obtine astfel: solutie(NrS) :- initial(NrS, Si), poza(Si), rezbreadth(Si).
% rezdepth(Si).
% rezbkt(Si).
12.4 Rezolvarea problemei mozaicului cu A*
Se pastreaza reprezentarea starilor problemei descrisa in sectiunea precedenta, se utilizeaza schema de rezolvare cu algoritmul A* prezentata in capitolul 11 si se definesc trei variante de predicate pentru calculul functiei euristice, conform celor trei functii euristice discutate in sectiunea 12.2. Functia euristica h1 se calculeaza astfel:
% euristic(+S, -H) - corespunzatoare functiei h1 euristic(S, H) :- final(Sf), calc(S, Sf, 0, H). calc(ai, _ , HR, HR) :- !. calc(S, Sf, H, HR) :- S = aPoz | Resti, Sf = aPoz1 | Rest1i,
(Poz = Poz1, N = 0; Poz\ = Poz1, N = 1),
H1 is H+N, calc(Rest, Rest1, H1, HR).
Functia euristica h2, distanta Manhattan, se calculeaza astfel:
% euristic(+S, +H) - corespunzatoare functiei h2 euristic(S, H) :- final(Sf), S = a _ | Resti, Sf = a _ | Rest1i, calc(Rest, Rest1, 0, H).
% calc(+R, +R1, +ValInit, -H) calc(ai, _ , H, H) :- !. calc(S, Sf, H, HR) :- S = aPoz | Resti, Sf = aPoz1 | Rest1i, lincol(Poz, L, C), lincol(Poz1, L1, C1),
(L> = L1, !, D1 is L-L1; D1 is L1-L),
(C> = C1, !, D2 is C-C1; D2 is C1-C),
Manh is D1+D2, H1 is H+Manh, calc(Rest, Rest1, H1, HR).

Pentru calculul functiei h3 se aduga la h2 valoarea 3 * D(S), cu D calculata de predicatul dep(S,D).
% Daca se adauga la h2 si 3*D se obtine h3 e-adimisibila
% euristic(+S, +H) - corespunzatoare functiei h3 euristic(S,H): final(Sf), S = a _ | Resti, Sf = a _ | Rest1i, calc(Rest, Rest1, 0, H1), dep(Rest,D), H is H1+ (3 * D).

calc(ai, _ , H, H) :- !. calc(S, Sf, H, HR) :- S = aPoz | Resti, Sf = aPoz1 | Rest1i, lincol(Poz, L, C), lincol(Poz1, L1, C1),
(L> = L1, !, D1 is L-L1; D1 is L1-L),
(C> = C1, !, D2 is C-C1; D2 is C1-C),
Manh is D1+D2, H1 is H+Manh, calc(Rest, Rest1, H1, HR).

dep(S, D) :- dep(S, 0, D). dep(aPi, D, D). dep(aP, P1 | Resti, D, DR) :-
(P = 5, !, D1 = 1; urm(P, U), (P1 = U, !, D1 = 0;D1 = 2)),
D2 is D+D1, dep(aP1 | Resti, D2, DR).

% functii ajutatoare div(X, Y, R) :- div(X, Y, 0, R). div(X, Y, N, N) :- (X = 0; X < Y), !. div(X, Y, N, NR) :- X > = Y, X1 is X-Y, N1 is N+1, div(X1, Y, N1, NR).
% P mod 3 = 0 atunci Coloana = 3, Linie = P div 3
% P mod 3 \= 0 atunci Coloana = P mod 3, Linie = P div 3+1 lincol(P, L, C) :- T is P mod 3,
(T = 0, !, C = 3, div(P, 3, L);
C = T, div(P, 3, L1), L is L1+1).

urm(1, 2). urm(2, 3). urm(3, 6). urm(4, 1). urm(6, 9). urm(9, 8). urm(8, 7). urm(7, 4).
%% Stare initiala 1 initial(s1,a1,4,2,3,6,9,8,7,5i).
%% Stare initiala 2 initial(s2,a3,4,1,2,6,9,8,7,5i).
%% Stare initiala 3 initial(s3,a5,1,6,2,3,9,8,7,4i).
%% Stare initiala 4 - merge cu h1, h2, h3 initial(s4,a8,4,1,3,6,9,5,7,2i).
%% Stare initiala 5 - merge cu h2, h3, nu merge cu h1 initial(s5,a5,2,1,9,4,8,3,7,6i).
%% Stare initiala 6 - merge numai cu h3 initial(s6,a9,4,2,6,8,5,1,3,7i).
%% Stare initiala 7 - merge numai cu h3 initial(s7,a2,4,5,6,3,9,8,7,1i).
Rezolvarea problemei se obtine astfel (atentie ! se alege numai una dintre cele trei definitii posibile pentru predicatul euristic, corespunzatoare lui h1 sau h2 sau h3): solutie(NrS): initial(NrS,Si), poza(Si), rezastar(Si).
Pentru exemplificarea performantelor strategiilor de cautare (neinformate si informate) discutate in acest capitol se prezinta rezultatele obtinute pentru sapte stari initiale in problema mozaicului cu opt cifre (D. s. inseamna depasirea stivei), in figura 12.
Si Poza Strategii de cautare neinformate Cautare informata cu A*
Nivel Adancime BKT A* - h1 A* - h2 A* - h3

Si 1 2 3 2 mutari 30 mutari 30 mutari 2 mutari 2 mutari 2 mutari
1 8 4 5 apeluri 28 apeluri 28 apeluri 2 apeluri 2 apeluri 2 apeluri
7 6 5

Si 2 2 3 4 mutari 4 mutari 4 mutari 4 mutari 4 mutari 4 mutari
1 8 4 15 apeluri 4 apeluri 4 apeluri 5 apeluri 4 apeluri 4 apeluri
7 5 5

Si 3 1 3 4 4 mutari 30 mutari Nu merge. 4 mutari 4 mutari 4 mutari
8 2 26 apeluri D. s. 5 apeluri 4 apeluri 4 apeluri
7 6 5

Si 4 2 8 3 5 mutari D.s. D. s. 5 mutari 5 mutari 5 mutari
1 6 4 57 apeluri 163 apeluri 338 apeluri 6 apeluri 5 apeluri 5 apeluri
7 5

Si 5 2 1 6 D. s. D. s. D. s. D. s. 18 mutari 18 mutari
4 8
7 5 3

Si 6 6 2 7 D. s. D. s. D. s. D. s. D. s. 23 mutari
1 5 3 74 apeluri 163 apeluri 124 apeluri
8 4

Si 7 8 4 D. s. D. s. D. s. D. s. D. s. 22 mutari
1 2 3 74 apeluri 163 apeluri 84 apeluri
7 6 5

Figura 12. Performantele strategiilor de cautare in problema mozaicului
12.5 Exercitii propuse
EP1. Sa se implementeze problema misionarilor si canibalilor folosind un algoritm A* bazat pe functia euristica h2.
EP2. Sa se extinda problema misionarilor si a canibalilor pentru N misionari, N canibali si capacitatea barcii de M persoane. Sa se rezolve aceasta problema prin strategii de cautare neinformate si informate si sa se faca o analiza comparativa a performantelor metodelor, de tipul celei din figura 12.
EP3. Sa se foloseasca schemele generale de cautare pentru rezolvarea problemei maimutei si a bananei prezentata in capitolul 4.
EP4. Sa se foloseasca cautari informate si neinformate pentru rezolvarea problemei taranului (10.1); se va defini o functie euristica adecvata. Se va propune, de asemenea, o reprezentare a starilor problemei alternativa celei din sectiunea 10.1.
EP5. Sa se modifice implementarile rezolvarii problemei mozaicului astfel incat sa se afiseze statisticile prezentate in figura 12.
EP6. Sa se aplice algoritmul A* cu costuri explicite (dezvoltat in cadrul exercitiului propus 4 din capitolul 11) pentru rezolvarea problemei comis-voiajorului.
13 Strategii de cautare aplicate la jocuri
In aceasta sectiune se discuta strategii de cautare specifice unui joc ce implica doi adversari. Strategia de cautare necesara in acest caz este mai complicata datorita existentei adversarului ostil si imprevizibil in mutari. Exista doua tipuri de jocuri: jocuri in care spatiul de cautare poate fi investigat exhaustiv si jocuri in care spatiul de cautare nu poate fi investigat complet deoarece este prea mare.
13.1 Algoritmul Minimax pentru spatii de cautare investigate exhaustiv
In procesul de cautare trebuie luate in considerare miscarile posibile ale adversarului. Se presupune ca oponentul are acelasi cunostinte despre spatiul de cautare ca si jucatorul si ca foloseste aceste cunostinte pentru a castiga. Algoritmul minimax implementeaza o strategie de cautare bazata pe aceasta presupunere. In prezentarea algoritmului, ne vom situa pe pozitia unuia din cei doi participanti, ales arbitrar si numit jucator, celalat participant fiind numit adversar sau oponent.
Jucatorul este referit ca MAX iar adversarul sau ca MIN. MAX incearca sa castige maximizandu si avantajul, iar MIN incearca sa castige minimizand avantajul lui MAX. Se presupune ca MIN va muta intotdeauna in configuratia care este cea mai defavorabila pentru MAX in acel moment.
Pe parcursul jocului se genereaza spatiul de cautare ce contine ca noduri configuratiile posibile ale jocului iar ca tranzitii intre acestea mutarile posibil de executat. Pentru implementarea cautarii se eticheteaza fiecare nivel din spatiul de cautare cu MAX sau cu MIN, dupa cum miscarea este facuta de jucator sau de adversar. Se genereaza tot spatiul de cautare si fiecare nod terminal este evaluat la scorul configuratiei terminale corespunzatoare; de exemplu 1 daca este o configuratie castigatoare pentru MAX si 0 daca este necastigatoare (este castigatoare pentru MIN). Se pot asocia si valori (pozitive) de castig pentru jucator in fiecare nod terminal, corespunzatoare punctajului obtinut de acesta la sfarsitul jocului. Apoi se propaga aceste valori in sus in graf, la nodurile parinti, dupa urmatoarele reguli:
1. daca nodul parinte este MAX atunci i se atribuie valoarea maxima a succesorilor sai;
2. daca nodul parinte este MIN atunci i se atribuie valoarea minima a succesorilor sai.
Valoarea asociata astfel fiecarei stari descrie cea mai buna valoare pe care jucatorul poate spera sa o atinga (presupunand ca oponentul joaca conform algoritmului minimax). Valorile obtinute sunt folosite pentru a alege intre diverse mutari posibile. Un exemplu de spatiu de cautare Minimax este prezentat in figura 13.

Figura 13. Un posibil spatiu de cautare Minimax
Fie urmatorul joc (NIM): Mai multe bete se pun pe masa intr o gramada initiala. Fiecare participant trebuie sa imparta cate o gramada in doua gramezi cu un numar diferit de bete. De exemplu, daca initial sunt 6 bete, acestea pot fi impartite 5 1 sau 4 2, dar nu 3 3. Primul jucator care nu mai poate face nici o miscare pierde. Spatiul de cautare pentru configuratia initiala de 7 bete este prezentat in figura 14.


Figura 14. Spatiul de cautare Minimax pentru Nim cu 7 bete
Se observa ca daca incepe MIN, jucatorul MAX poate castiga intotdeauna jocul, deoarece orice miscare initiala a lui MIN duce intr o stare din care MAX poate castiga daca joaca bine.
13.2 Algoritmul Minimax cu adancime de cautare finita
Daca nu se poate investiga exhaustiv spatiul de cautare, se aplica algoritmul minimax. pana la o adancime finita, arbitrar prestabilita. Configuratiile aflate la aceasta adancime sunt considerate stari terminale. Deoarece ele nu sunt (de cele mai multe ori) stari terminale, deci de sfarsit de joc, punctajul asociat acestora se estimeaza pe baza unei functii euristice care indica cat de promitatoare este o astfel de stare pentru castigul jucatorului.
De pe fiecare nivel curent se cauta pe urmatoarele n nivele ale spatiului. Valoarea care se atribuie unui nod in urma investigarii a n nivele sub el si folosind functia euristica nu este o indicatie daca se castiga sau se pierde, ci o estimare euristica a celei mai bune stari la care se poate ajunge in n miscari de la nodul dat. Descrierea algoritmului in aceasta abordare este:
Descriere Minimax( S ) pentru fiecare succesor Sj al lui S (obtinut printr-o mutare opj) executa val( Sj ) ? Minimax( Sj ) aplica opj pentru care val( Sj ) este maxima sfarsit
Minimax( S ) A intoarce o estimare a starii S S daca nivel( S ) = n atunci intoarce eval( S ) altfel daca MAX muta in S atunci pentru fiecare succesor Sj al lui S executa val( Sj ) ? Minimax( Sj )
intoarce max( val( Sj ), ?j ) altfel A MIN muta in S S pentru fiecare succesor Sj al lui S executa val( Sj ) ? Minimax( Sj )
intoarce min( val( Sj ), ?j ) sfarsit
Pentru a da un exemplu de functie de estimare, sa consideram jocul de Tic Tac Toe (X si O). Alegem o functie de estimare euristica eval( S ) ce incearca sa masoare conflictul existent in joc in starea S. Valorea eval( S ) este numarul total posibil de linii castigatoare ale lui MAX in starea S din care se scade numarul total posibil de linii castigatoare ale lui MIN in starea S. Prin linie castigatoare se intelege o linie orizontala, verticala sau diagonala care a fost, este doar partial sau poate fi completata numai cu X sau numai cu O. Daca S este o stare din care MAX poate face o miscare cu care castiga, atunci eval( S ) = ? (o valoare foarte mare), iar daca S este o stare din care MIN poate castiga cu o singura mutare, atunci eval( S ) = -? (o valoare foarte mica). Un exemplu este prezentat in figura 15.
X X are 6 linii castigatoare posibile
O are 5 linii castigatoare posibile
O eval( S ) = 6 - 5 = 1
Figura 15. Functia euristica de evaluare a unei stari in jocul Tic Tac Toe
Dezavantajul abordarii cu adancime de cautare finita egala cu n este acela ca poate sa apara efectul de orizont, adica la nivelul n se neglijeaza cai care la nivelul (n + 1) ar putea fi mai bune. Efectul de orizont poate fi partial inlaturat daca se investigheaza inainte anumite stari care par a fi foarte promitatoare. Astfel, anumite stari cheie (de exemplu captura unei piese) se investigheaza la nivele suplimentare. De obicei se pune o limita de timp si se investigheaza iterativ nivele din ce in ce mai mari pana cand se atinge limita de timp impusa.
Prezentam in continuare implementarea in Prolog a acestui algoritm si aplicarea lui la jocul de Tic Tac Toe (X si O):
% Implementarea algoritmului Minimax
% Partea independenta de jocul particular

% Predicatul minimax: minimax(+Poz, -CelMaiBun, -Val)
% Poz este pozitia considerata, Val este valoarea sa minimax.
% Cea mai buna mutare este intoarsa in CelMaiBun

minimax(Poz, CelMaiBun, Val) :-
% PozList = lista mutarilor posibile din pozitia Poz mutari(Poz, aPrim | PozListi), !, minimax(Prim, _ , VPrim), bun(Poz, PozList, Prim, VPrim, CelMaiBun, Val). minimax(Poz, _ , Val) :- valstatic(Poz, Val). % Poz nu are succesori

bun(Curenta, ai, Poz, Val, Poz, Val) :- !. bun(Curenta, aPoz | PozListi, PozCurenta, ValCurenta, PozBuna, ValBuna) :- minimax(Poz, _ , Val), bun_dela(Curenta, Poz, Val, PozCurenta, ValCurenta, PozNoua, ValNoua), bun(Curenta, PozList, PozNoua, ValNoua, PozBuna, ValBuna).

bun_dela(Curenta, Poz0, Val0, Poz1, Val1, Poz0, Val0) :- muta_min(Curenta), Val0 < Val1, !. bun_dela(Curenta, Poz0, Val0, Poz1, Val1, Poz0, Val0) :- muta_max(Curenta), Val0 > Val1, !. bun_dela(Curenta, Poz0, Val0, Poz1, Val1, Poz1, Val1).

% Jocul X si 0
% O pozitie se memoreaza astfel: pos(x, ax, b, 0, b....i, 3) unde
% x = jucatorul care muta curent
% a...i este ceea ce se gaseste in tabla, pe linii
% 3 reprezinta cate mutari se mai fac in adancime, maxim, in minimax

% Generatorul de mutari posibile mutari(Poz, PozList) :- bagof(P, mutare(Poz, P), PozList). mutare(pos(P, Tabla, N), pos(O, Tabla1, N1)) :-
N > 0, not(final(Tabla, _ )), opus(P, O), N1 is N - 1, subst(Tabla, P, Tabla1).

opus(x, 0). opus(0, x). subst(ab | Resti, P, aP | Resti). subst(aX | Resti, P, aX | Rest1i) :- subst(Rest, P, Rest1).

% Evaluarea statica valstatic(pos( _ , B, N), Val) :- final(B, P), !, castigator(P, N, Val). valstatic(pos( _ , Tabla, _ ), Val) :- lin1(Tabla, L1), lin2(Tabla, L2), lin3(Tabla, L3), col1(Tabla, C1), col2(Tabla, C2), col3(Tabla, C3), dia1(Tabla, D1), dia2(Tabla, D2),
Val is L1 + L2 + L3 + C1 + C2 + C3 + D1 + D2.

final(aP, P, P | _ i, P) :- P \= b, !. final(a _ , _ , _ , P, P, P | _ i, P) :- P \= b, !. final(a _ , _ , _ , _ , _ , _ , P, P, Pi, P) :- P \= b, !. final(a P, _ , _ , P, _ , _ , P | _ i, P) :- P \= b, !. final(a _ , P, _ , _ , P, _ , _ , P | _ i, P) :- P \= b, !. final(a _ , _ , P, _ , _ , P, _ , _ , Pi, P) :- P \= b, !. final(aP, _ , _ , _ , P, _ , _ , _ , Pi, P) :- P \= b, !. final(a _ , _ , P, _ , P, _ , P | _ i, P) :- P \= b, !. final(Tabla, b) :- not(member(b, Tabla)).

castigator(x, N, Val) :- !, Val is -10 * (N + 1). castigator(0, N, Val) :- !, Val is 10 * (N + 1). castigator( _ , _ , 0).

lin1(ab, b, b | _ i, 1) :- !. lin1( _ , 0). lin2(a_ , _ , _ , b, b, b | _ i, 1) :- !. lin2( _ , 0). lin3(a _ , _ , _ , _ , _ , _ , b, b, bi, 1) :- !. lin3( _ , 0).

col1(a b, _ , _ , b, _ , _ , b | _ i, 1) :- !. col1( _ , 0). col2(a _ , b, _ , _ , b, _ , _ , b | _ i, 1) :- !. col2( _ , 0). col3(a _ , _ , b, _ , _ , b, _ , _ , bi, 1) :- !. col3( _ , 0).

dia1(a b, _ , _ , _ , b, _ , _ , _ , bi, 1) :- !. dia1( _ , 0). dia2(a _ , _ , b, _ , b, _ , b | _ i, 1) :- !. dia2( _ , 0).

%Tipul nodului (min sau max) muta_min(pos(x, _ , _ )). muta_max(pos(0, _ , _ )).

% Jocul efectiv run :- primul(P), adancime(N), initial(Tabla), scrie_tabla(Tabla), joc(pos(P, Tabla, N)).

primul(P) :-
write($Incepe un nou joc...$), nl, write($Eu sunt cu 0, tu esti cu x$), nl,
write($Cine incepe (x/0)? $), read(P).

adancime(N) :- write($Numarul de semi-mutari adancime? $), read(N).

initial(ab, b, b, b, b, b, b, b, bi).

joc(pos(P, Tabla, _ )) :- final(Tabla, P1), !, scrie_castig(P1). joc(pos(x, Tabla, N)) :- !, det_mutare(Tabla, Succ), scrie_tabla(Succ), joc(pos(0, Succ, N)). joc(pos(0, Tabla, N)) :- minimax(pos(0, Tabla, N), pos(x, Succ, _ ), Val), scrie_mutare(Succ, Val), joc(pos(x, Succ, N)).

scrie_castig(b) :- !, write($Remiza.$), nl. scrie_castig(x) :- !, write($Ai castigat.$), nl. scrie_castig(_ ) :- write($Ai pierdut.$), nl.

det_mutare(Tabla, Succ) :- repeat, det_coord(L, C), N is L * 3 + C, verifica(N, Tabla, Succ), !.

scrie_mutare(Tabla, Val) :-
write($Mutarea mea este: $), nl, scrie_tabla(Tabla), write($Punctaj: $), write(Val), nl, anunta(Val).

anunta(10) :- !, write($Acum sunt sigur ca voi castiga.$), nl. anunta(_ ).

det_coord(L, C) :-
write($Linia si coloana? $), read(L1), read(C1), L is L1 - 1, C is C1 - 1.

verifica(0, ab | Resti, ax | Resti) :- !. verifica(N, aX | Tablai, aX | Succi) :- N1 is N - 1, verifica(N1, Tabla, Succ).

scrie_tabla(Tabla) :- repl(Tabla, ' ', aE1, E2, E3, E4, E5, E6, E7, E8, E9i),
write($ 1 2 3$), nl,
write($1 $), write(E1), write($ $), write(E2), write($ $), write(E3), nl,
write($2 $), write(E4), write($ $), write(E5), write($ $), write(E6), nl,
write($3 $), write(E7), write($ $), write(E8), write($ $), write(E9), nl.

% Predicate de uz general repeat. repeat :- repeat.

member(X, aX | _ i). member(X, a_ | Resti) :- member(X, Rest).

repl(ai, _ , ai) :- !. repl(ab | Resti, X, aX | Rest1i) :- !, repl(Rest, X, Rest1). repl(aE | Resti, X, aE | Rest1i) :- repl(Rest, X, Rest1).
13.3 Algoritmul taierii alfa beta
Este posibil sa se obtina decizia corecta a algoritmului Minimax fara a mai inspecta toate nodurile din spatiului de cautare pana la un anumit nivel. Procesul de eliminare a unei ramuri din arborele de cautare se numeste taiere (pruning).
Pentru a ilustra ideea acestui algoritm, se considera spatiul de cautare din figura 13, redesenat in figura 16.

Figura 16. Taierea alfa-beta a spatiului de cautare
Se observa ca, odata gasit succesorul H cu valoarea 2 al nodului C, nu mai este necesara inspectarea celorlati succesori ai nodului C, deoarece:
? Nodul C este pe un nivel de MIN si va primi o valoare mai mica sau egala cu 2;
? Pentru nodul A, care este pe un nivel de MAX, am gasit anterior succesorul B cu valoarea 3, deci A va avea o valoare mai mare sau egala cu 3, ceea ce inseamna ca nodul C devine un succesor neinteresant A (valoarea lui C este mai mica decat 3).
Fie ? cea mai buna valoare (cea mai mare) gasita pentru MAX si ? cea mai buna valoare (cea mai mica) gasita pentru MIN. Algoritmul alfa beta actualizeaza ? si ? pe parcursul parcurgerii arborelui si elimina investigarile subarborilor pentru care ? sau ? sunt mai proaste. Terminarea cautarii (taierea unei ramuri) se face dupa doua reguli:
1. Cautarea se opreste sub orice nod MIN cu o valoare ? mai mica sau egala cu valoarea ? a oricaruia dintre nodurile MAX predecesoare nodului MIN in cauza.
2. Cautarea se opreste sub orice nod MAX cu o valoare ? mai mare sau egala cu valoarea ? a oricaruia dintre nodurile MIN predecesoare nodului MAX in cauza.
Deci algoritmul exprima o relatie intre nodurile de pe nivelul n si nodurile de pe nivelul (n + 2). Daca jucatorul MAX are posibilitatea sa ajunga la un nod m care este mai bun decat nodul curent de pe nivelul n, atunci intr un joc real, nodul de pe nivelul n nu poate fi niciodata considerat, asa cum se vede din figura 17. Se observa ca
A are ? = 3 (A este pe un nivel MIN, deci val( A ) nu poate fi mai mare ca 3)
B este ? taiat deoarece 5 > 3
C are ? = 3 (C este pe un nivel MAX, deci val( C ) nu va fi mai mica decat 3)
D este ? taiat deoarece O < 3
E este ? taiat deoarece 2 < 3, deci val( C ) este 3.

Figura 17. Eliminarea investigarii unor noduri in algoritmul alfa-beta
Algoritmul alfa beta consta in doua proceduri: MIN si MAX. Algoritmul incepe fie prin apelarea procedurii MAX, daca jucatorul muta primul, fie prin apelarea procedurii MIN, daca adversarul muta primul, initial ? fiind foarte mic (0) si ? foarte mare (?).
MAX(S, ?, ?) A Intoarce valoarea maxima a unei stari. S daca nivel( S ) = n atunci intoarce eval( S ) altfel pentru fiecare succesor Sj al lui S executa
? ? max(?, MIN(Sj, ?, ?)) daca ? ? ? atunci intoarce ?
intoarce ? sfarsit
MIN(S, ?, ?) A Intoarce valoarea minima a unei stari. S daca nivel( S ) = n atunci intoarce eval( S ) altfel pentru fiecare succesor Sj al lui S executa
? ? min(?, MAX(Sj, ?, ?)) daca ? ? ? atunci intoarce ?
intoarce ? sfarsit
Algoritmul alfa beta nu elimina limitarile de la Minimax, cum ar fi efectul de orizont. Avantajul sau este insa acela ca reduce numarul de noduri inspectate, permitand o crestere a nivelului de adancime in investigarea nodurilor urmatoare, n. Algoritmul devine eficient daca se face o ordonare a succesorilor in inspectare: cele mai bune miscari ale lui MAX, respectiv ale lui MIN, din punctul de vedere al lui MAX intai. De exemplu, pentru jocul de sah, unde factorul de ramificare este aproximativ egal cu 35, utilizarea algoritmului alfa-beta si o ordonare adecvata a starilor inspectate poate reduce factorul de ramificare la 6, ceea ce permite dublarea adancimii de cautare, permitand trecerea de la un nivel de novice la un nivel de expert.
Prezentam in continuare implementarea algoritmului alfa beta in Prolog si aplicarea lui in jocul de Tic Tac Toe (X si O):
% Implementarea algoritmului alfa beta
% Partea independenta de jocul particular jucat

alfabeta(Poz, Alfa, Beta, SucBun, Val) :- mutari(Poz, aP1 | LPozi), !, alfabeta(P1, Alfa, Beta, _ , V1), nou(Alfa, Beta, P1, V1, NAlfa, NBeta), bunsucc(LPoz, NAlfa, NBeta, P1, V1, SucBun, Val). alfabeta(Poz, Alfa, Beta, SucBun, Val) :- valstatic(Poz, Val).

bunsucc(ai, _ , _ , P1, V1, P1, V1) :- !. bunsucc(aPoz | LPozi, Alfa, Beta, Poz1, Val1, PozBuna, ValBuna) :- alfabeta(Poz, Alfa, Beta, _ , Val), bun_dela(Poz, Val, Poz1, Val1, Poz2, Val2), destuldebun(LPoz, Alfa, Beta, Poz2, Val2, PozBuna, ValBuna).

destuldebun(_ , Alfa, Beta, Poz, Val, Poz, Val) :- muta_min(Poz), Val > Beta, !; muta_max(Poz), Val < Alfa, !. destuldebun(LPoz, Alfa, Beta, P, Val, PozBuna, ValBuna) :- nou(Alfa, Beta, P, Val, NAlfa, NBeta), bunsucc(LPoz, NAlfa, NBeta, P, Val, PozBuna, ValBuna).

nou(Alfa, Beta, Poz, Val, Val, Beta) :- muta_min(Poz), Val > Alfa, !. nou(Alfa, Beta, Poz, Val, Alfa, Val) :- muta_max(Poz), Val < Beta, !. nou(Alfa, Beta, _ , _ , Alfa, Beta).

bun_dela(Poz, Val, Poz1, Val1, Poz, Val) :- muta_min(Poz), Val > Val1, !; muta_max(Poz), Val < Val1, !. bun_dela(_ , _ , Poz1, Val1, Poz1, Val1).

Pentru a aplica algoritmul alfa-beta la jocul Tic-Tac-Toe, singura modificare fata de implementarea prezentata in


Colt dreapta
Creeaza cont
Comentarii:

Nu ai gasit ce cautai? Crezi ca ceva ne lipseste? Lasa-ti comentariul si incercam sa te ajutam.
Esti satisfacut de calitarea acestui document, eseu, cometariu? Apreciem aprecierile voastre.

Nume (obligatoriu):

Email (obligatoriu, nu va fi publicat):

Site URL (optional):


Comentariile tale: (NO HTML)


Noteaza documentul:
In prezent fisierul este notat cu: ? (media unui numar de ? de note primite).

2345678910

 
Copyright© 2005 - 2024 | Trimite document | Harta site | Adauga in favorite
Colt dreapta