Búsqueda amb adversaris
Jocs
Models d’intel·ligència artificial

Jocs
- Fins ara en els nostres problemes de cerca, l’entorn era determinista i totalment observable.
- Solament calia planificar una seqüència d’accions per arribar a l’estat objectiu.
- En els jocs hi ha adversaris.
- Cada jugador té un objectiu diferent.
- Els jugadors modifiquen l’estat de l’entorn en benefici propi.
- La cooperació pot ocórrer, però solament si és beneficiosa per a tots els jugadors.
- Els jocs són un domini molt important en la intel·ligència artificial.
- Són un domini molt comú, complexe i útil per a la investigació.
Jocs
Propietats
Tindrem en compte les següents propietats:
- Dos jugadors: Un pot ser la màquina i l’altre un humà.
- Finit: nombre finit d’estats. Si el nombre d’estats es molt gran es poden utilitzar aproximacions.
- Suma zero: el guany d’un jugador és la pèrdua de l’altre.
- Determinista: no hi ha aleatorietat.
- Informació perfecta: els jugadors coneixen l’estat del joc en tot moment. (Escacs, Go, etc.)
Dos jugadors i suma zero
Definició
- Dos jugadors: \(MAX\) i \(MIN\).
- Un conjunt de posicions \(P\) (estats)
- Una posició inicial \(p_0 \in P\)
- Un conjunt de posicions terminals \(T \subseteq P\)
- Un conjunt d’eixos dirigits \(E_Max\) i \(E_Min\) entre les posicions.
- Representaran els moviments possibles de cada jugador.
- Una funció d’utilitat \(u: T \rightarrow \mathbb{R}\) que
- el valor de cada posició terminal per a \(MAX\).

Arbre de joc
Característiques:
- Arbre de joc: capes d’estats alternant entre els jugadors.
- Arrel: Estat inicial.
- Estat del joc: posició i jugador a moure.
- Final del joc: Quan un jugador arriba a una posició terminal.
- Funció d’utilitat: Junt als terminals substitueix els objectius
- Cada node terminal \(t\) s’etiqueta segons la seva utilitat \(U(t)\).
- Per a \(MAX\) serà \(U(t)\), per a \(MIN\) \(-U(t)\).
- En la majoria de jocs que veurem, \(U(t) \in \{-1, 0, 1\}\).
Exemple

Estratègies
- \(MAX\) vol maximitzar la seva utilitat.
- \(MIN\) vol minimitzar la utilitat de \(MAX\).
- \(MAX\) no decideix sol a quin estat terminal arribarà.
- Quan \(MAX\) mou, \(MIN\) decideix a quin estat subseqüent es mourà.
- \(MAX\) ha de tindre una estratègia:
- Ha de decidir que fer per a cada possible moviment de \(MIN\).
- No hi ha prou en una seqüència d’accions, dependrà de les accions de \(MIN\).
MiniMax
Definicions
- Estraègia recursiva.
- Assumint que \(MIN\) juga sempre el seu millor moviment,
- quin moviment s’ha de fer per minimitzar la utilitat de \(MIN\)?º.
- Cada node tindrà una puntuació minimax.
- Serà la utilitat mínima que \(MAX\) pot obtenir si MIN juga òptimament.
Minimitzant el guany de MIN estem maximitzant el nostre.
Exemple
- En l’exemple de la dreta els nodes \(\triangle\) són \(MAX\) i els \(\triangledown\) \(MIN\).
- Els nodes terminals mostren la utilitat per a \(MAX\).
- La resta de nodes mostren la seva puntuació minimax
- En l’arrel la millor opció per a \(MAX\) és \(a_1\), ja que porta al node en millor puntuació minimax
- En el segon nivell la millor opció per a \(MIN\) és \(b_1\) per dur al node en menos puntuació

Algorisme
- Entrada: Un arbre de joc \(A\), un node \(n\), un jugador \(j\).
- Sortida: La puntuació minimax del node \(n\).
- Algorisme: Algorisme recursiu.
- Si \(n\) és un node terminal, retornar la seva utilitat.
- Si \(j\) és \(MAX\): Retornar el màxim de les puntuacions dels fills.
- Si \(j\) és \(MIN\): Retornar el mínim de les puntuacions dels fills.
Implementació
def cerca_minimax(joc, estat):
jugador = estat.a_moure
return valor_maxim(joc, jugador, estat)
def valor_maxim(joc, jugador, estat):
if joc.es_terminal(estat):
return joc.utilitat(estat, jugador), None
v, moviment = float('-inf'), None
for a in joc.accions(estat):
v2, _ = valor_minim(joc, jugador, joc.resultat(estat, a))
if v2 > v:
v, moviment = v2, a
return v, moviment
def valor_minim(joc, jugador, estat):
if joc.es_terminal(estat):
return joc.utilitat(estat, jugador), None
v, moviment = float('inf'), None
for a in joc.accions(estat):
v2, _ = valor_maxim(joc, jugador, joc.resultat(estat, a))
if v2 < v:
v, moviment = v2, a
return v, moviment
Problemes
- Complexitat: \(O(b^m)\)
- sent \(b\) el nombre de branques per node i \(m\) la profunditat de l’arbre.
- La complexitat pot ser massiva.
- En el joc d’escacs, \(b \approx 35\) i \(m \approx 100\). \(Nodes \approx 10^{54}\)
- En el joc del Go, \(b \approx 250\) i \(m \approx 150\). \(Nodes \approx 10^{360}\)
- Això fa que sigui impossible explorar tot l’arbre de joc en jocs complexos.
- Veurem técniques que poden ajudar-nos.
Poda alfa-beta
Introducció
- Poda alfa-beta: técnica per reduir el nombre de nodes a explorar en l’arbre de joc.
- Poda: eliminar nodes de l’arbre de joc sense explorar-los.
- Alfa: valor mínim que \(MAX\) està assegurat de poder obtenir.
- Beta: valor màxim que \(MIN\) està assegurat de poder obtenir.
- Nodes a podar: Nodes que, indepentment del seu valor, no modificarán el nivell superior.
Exemple
- La primera fulla baix \(B\) té valor \(3\). Per tant \(B\) (node \(MIN\)) té un valor màxim de \(3\).
- La segona fulla baix \(B\) té valor \(12\).
- \(MIN\) evitaria aquest moviment, per lo que \(B\) encara té un valor màxim de \(3\).
-
La tercera fulla baix \(B\) té valor \(8\). El valor de final de \(B\) és \(3\).
- Podem deduir llavors que el valor mínim d’\(A\) és \(3\), al tindre un node terminal amb valor \(3\).
- La primera fulla baix \(C\) té valor \(2\). Per tant \(C\), que es un node \(MIN\), té un valor màxim de \(2\).
- Sabem que \(B\) té un valor de \(3\), per lo que \(MAX\) mai escollirà \(C\).
- Així sabem que no cal explorar els altres nodes fills de \(C\),
- Aquesta és la poda alfa-beta.
- Sabem que \(B\) té un valor de \(3\), per lo que \(MAX\) mai escollirà \(C\).
- Al acabar l’exploració sabem els valors de cada node necessari.
Regles
- La poda alfa-beta no afecta al resultat de l’algorisme.
- Es pot aplicar a qualsevol profunditat de l’arbre.
- Moltes vegades es poden, fins i tot, podar arbres sencers.
- Principi general, per un node \(n\):
- Si hi ha una opció millor al mateix nivell (\(m'\)) o superior (\(m\)), \(n\) no es visitarà.

Implementació
def busqueda_alfa_beta(joc, estat):
jugador = estat.a_moure
return valor_maxim_ab(joc, jugador, estat, float('-inf'), float('inf'))
def valor_maxim_ab(joc, jugador, estat, alfa, beta):
if joc.es_terminal(estat):
return joc.utilitat(estat, jugador), None
v, moviment = float('-inf'), None
for a in joc.accions(estat):
v2, _ = valor_minim_ab(joc, jugador, joc.resultat(estat, a), alfa, beta)
if v2 > v:
v, moviment = v2, a
if v >= beta:
return v, moviment
alfa = max(alfa, v)
return v, moviment
def valor_minim_ab(joc, jugador, estat, alfa, beta):
if joc.es_terminal(estat):
return joc.utilitat(estat, jugador), None
v, moviment = float('inf'), None
for a in joc.accions(estat):
v2, _ = valor_maxim_ab(
joc, jugador, joc.resultat(estat, a), alfa, beta
)
if v2 < v:
v, moviment = v2, a
if v <= alfa:
return v, moviment
beta = min(beta, v)
return v, moviment
Millores
- Ordenació de nodes: Ordenar els nodes fills corréctament permet podar més.
- Una bona ordenació pot permetre passar d’examinar de \(O(b^{3d/4})\) a \(O(b^{d/2})\)
- En un joc d’escacs, els moviments que mengen peces són més probables de ser bons.
- Una bona ordenació pot permetre passar d’examinar de \(O(b^{3d/4})\) a \(O(b^{d/2})\)
- Per no explorar estates repetits, es pot utilitzar una taula de transposició.
- Semblant al conjunt de visitats, però amb els valors de cada node.
- Aplicar heurístiques per tallar l’avaluació:
- Aplicar una funció d’avaluació a les posicions no terminals per fer-les terminals
Funcions d’avaluació
Introducció
- En jocs complexos, no es pot explorar tot l’arbre de joc.
- En lloc d’això, es pot utilitzar una funció d’avaluació per estimar la utilitat d’un estat.
- La funció d’avaluació no ha de ser perfecta.
- Ha de ser rápida de calcular.
- Ha de ser consistent amb la utilitat real.
Exemple: Tic-Tac-Toe
- En el joc del tres en ratlla, podem utilitzar la següent funció d’avaluació:
- \[u(s) = \sum_{i=1}^3 \sum_{j=1}^3 \begin{cases} 1 & \text{si } s_{i,j} = \text{MAX} \\ -1 & \text{si } s_{i,j} = \text{MIN} \\ 0 & \text{altrament} \end{cases}\]
- Explicació: Sumem 1 per cada fitxa de \(MAX\) i restem 1 per cada fitxa de \(MIN\).
Funcions d’avaluació
Implementació
def avalua_tres_en_ratlla(joc, estat):
jugador = estat.a_moure
utilitat = 0
for i in range(3):
for j in range(3):
if estat.tauler[i][j] == jugador:
utilitat += 1
elif estat.tauler[i][j] == joc.jugador_contrari(jugador):
utilitat -= 1
return utilitat