- El tablero en vez de ser de 3x3 casilleros es de 2x2.
- Lo que debe formarse, en vez de ser una línea horizontal, vertical o diagonal de 3 signos iguales, debe ser de 2 pero horizontal.
Claro que es menos interesante que el original, es que se me complicó.
Hay a grandes rásgos 3 tipos de IA, díganme otras si me faltan:
A- La que sabe jugar como el programador le dijo, no aprende.
B- La que sabe jugar según lo que entrenó, aprendió todo lo necesario.
C- La que está aprendiendo, aprende mientras juega.
El problema de A es que si el juego es complejo, no hay persona que sepa el modo ideal de jugar, y tampoco es fácil expresarlo.
B requiere más tiempo lograrse entre más complejo sea el juego, por lo que llaman explosión combinatoria o algo así.
C, entre más complejo sea el juego, menos útil es, porque difícilmente llegue a aprender todo lo necesario para jugar bien. El jugador podría hacer cosas distintas hasta el cansancio y en tales casos la IA sería como si recién empezara.
Supongo que pueden pulirse de distintos modos, pero no sé bien cómo.
A, en este caso simple sería así creo:
Código:
Si IA usa X:
// Intentar ganar:
Si Casillero1_1 == "" && Casillero1_2 == "X"
Poner X en 1_1
sino si Casillero1_1 == "X" && Casillero1_2 == ""
Poner X en 1_2
Si Casillero2_1 == "" && Casillero2_2 == "X"
Poner X en 2_1
sino si Casillero2_1 == "X" && Casillero2_2 == ""
Poner X en 2_2
sino
// Ya no es posible ganar (perder nunca fue):
Poner en cualquiera vacío.
sino
// Evitar perder:
Si Casillero1_1 == "" && Casillero1_2 == "X"
Poner 0 en 1_1
sino si Casillero1_1 == "X" && Casillero1_2 == ""
Poner 0 en 1_2
Si Casillero2_1 == "" && Casillero2_2 == "X"
Poner 0 en 2_1
sino si Casillero2_1 == "X" && Casillero2_2 == ""
Poner 0 en 2_2
sino
// Ya no es posible perder:
Poner en el único vacío que queda.
Sobre C no pensé mucho.
B es más difícil de lo que creí, a ver si me dan ideas.
Mi idea es algo así:
- En la situación inicial (tablero todo vacío), se prueban las acciones posibles y si se llega a una situación que no está en la lista de situaciones no resueltas, se agrega. Al principio esa lista está vacía, ahora habrá 4 porque 4 son las posibles consecuencias de las 4 posibles acciones.
- Se genera una de las situaciones de la lista. Probamos las posibles acciones, ahora el signo es 0 en vez de X. Si obtenemos situaciones que no estén en la lista, de nuevo, las anotamos.
- Repetimos eso con cada situación, el signo a poner depende de cuales sean, y llegará un punto en que poner un signo causará una victoria, por ejemplo en esta:
XV
0V
(V es vacío)
La acción nro 2 (casillero 1_2, al lado de la X) causa victoria. Cuando eso ocurre, creamos unas variables cuyos nombres implican la situación anterior a la victoria, en el ejemplo sería XV0V, y ponemos una S delante para menos lío, sabemos que S significa situación. Bueno, las variables van a ser por ejemplo:
SXV0VBestAction = 2
SXV0VDistanciaDeVictoria = 1
Significa que en esa situación conviene la acción 2 y que hace falta sólo 1 acción para llegar a la victoria.
Eliminamos esa acción de la lista de situaciones sin resolver.
- Cuando hayamos recorrido la lista de situaciones, comenzamos a recorrerla de nuevo, pero no sé bien qué debería hacerse ahora específicamente.
En programas "similares" que había hecho, lo que se hacía era que si se llega a una situación cuya DistanciaDeVictoria fuese 1, a la anterior situación se le asignaba que DistanciaDeVictoria fuese 2, y así se repite realizar acciones, asignar "puntajes" a las situaciones, eliminarlas de la lista, y en cada nueva repetición aumentar el número asignado a DistanciaDeVictoria, hasta que ya no queden situaciones a investigar.
Pero en este caso no se puede porque hay turnos, hay rivales, a ver:
Si sólo hubiera X, sería así:
Comenzamos investigando la situación
VV
VV
Probamos una acción
XV
VV
Anotamos esa situación en la lista de no resueltas.
Probamos otra acción en la situación que estamos investigando, nos queda:
VX
VV
En fin, en cierto punto vamos a investigar esta:
XV
VV
Realizamos una acción que causa victoria
XX
VV
Entonces definimos:
SXVVVBestAction=2
SXV0VDistanciaDeVictoria = 1
Y en otro punto, cuando volvamos a investigar situaciones aún no resueltas, como esta:
VV
VV
Cuando hagamos esto:
XV
VV
Llegamos a una que la tenemos evaluada como 1, entonces a la anterior (la que puse arriba, todo V) le vamos a poner 2. Está clarísimo, se requieren 2 acciones para ganar desde el inicio.
Pero hay 0, así que luego de hallar todas las victorias posibles, cuando volvamos a investigar situaciones aún no resueltas, como esta:
XV
VV
Sabemos que es turno del 0, que si pone en 2_1 o 2_2 se llegará a una situación cuya DistanciaDeVictoria es 1, pero para X, para 0 es 1 de la derrota, así que el programa no puede ponerle 2, eso se interpretaría como que a 0 le hacen falta 2 acciones para ganar, que es segurto que ganará, lo cual no es así.
¿La solución es crear otra variable? ¿que exista DistanciaDeVictoriaDeX y DistanciaDeVictoriaDe0? Hay que tener en cuenta la explosión combinatoria, no conviene crear variables en lo posibñe, además no veo bien cómo eso lo resolvería.
A ver, si no llega a una situación cuya DistanciaDeVictoriaDe0 sea 1 (cosa imposible, 0 no puede ganar), nada se anota al respecto. Y cuando la distancia de X es 1, se anota que de 0 es -1, es decir que está a 1 acción de ser derrotado. La distancia ideal es positiva y en 2nda instancia lo más cercana a 0 posible.
Bueno... cuando en una búsqueda no se anoten cosas, es decir no se llegue a situaciones resueltas, se comenzaría a anotar las que lleguen a distancias -1, luego a -2, y así, podría ser que se llegue a resolver todas las situaciones.
¿Creen que eso funcionará? ¿no, por qué?
¿Es posible usar sólo una distancia? ¿sí, cómo?
¿Cómo sería un método más rápido y eficiente? Imagino que deduciendo en base a rotar el tablero o considerar simetría, pero creo que debe haber algo como aprender la regla, aprender un código a seguir, no esto de aprender qué hacer en la situación 1, 2, 3... Cuando juegue, sería bueno que siga un código como el que puse al inicio, en vez de "llamar a la función SXVVV" (o sea, cargar los datos de esa situación a ver qué acción conviene en ella), porque cuando sean MUCHAS posibles situaciones, es un disparate hacerlo así, por la explosión ¿no? Imagina un 5 en línea en un tablero de 25x25 con 5 jugadores, por decir algo.