Desarrollo de un juego de naipes en JESS

Transcription

Desarrollo de un juego de naipes en JESS
Desarrollo de un juego de naipes en JESS
Paloma Fuentes Gutierrez
5º ETSIT
c/ Palmera, 12
28918 Leganes
+34 620866421
[email protected]
ABSTRACTO
Tabla 1. Sistemas clásicos vs Sistemas expertos
Desarrollo de un sistema experto usando JESS, CLIPS
reprogramado en JAVA.
Términos Generales
Diseño de un sistema experto usando JESS. Se implementará el
juego “Seises”, en una versión simplificada, con dos palos nada
más y cuatro jugadores.
Palabras Clave
“Seises”, JESS, CLIPS, sistemas expertos, rule-based-system.
Sistema clásico
Sistema experto
Conocimiento y
procesamiento combinados en
un programa
Base de conocimientos
separada del mecanismo de
procesamiento
No contiene errores
Puede contener errores
No da explicaciones, los datos
se usan o escriben
Una parte del sistema experto
lo forma el módulo de
explicación
Cambios tediosos. El sistema
solo opera completo
Las cambios en reglas son
fáciles. El sistema puede
funcionar con pocas reglas
Se ejecuta paso a paso y
necesita información completa
para operar
La ejecución usa heurísticas y
lógica, y puede operar con
información incompleta
Representa y usa datos
Representa y usa conocimiento
1. INTRODUCCIÓN A LOS SISTEMAS
EXPERTOS
“Los sistemas expertos son programas que reproducen el proceso
intelectual de un experto humano en un campo particular,
pudiendo mejorar su productividad, ahorrar tiempo y dinero,
conservar sus valiosos conocimientos y difundirlos más
fácilmente.” [2]
Los sistemas expertos, dentro del campo de la inteligencia
artificial, son sistemas capaces de realizar algo muy parecido a
“razonar y pensar”. Permiten crear máquinas que razonan en un
espacio restringido de conocimientos, siguiendo los pasos que
seguiría un experto humano. Actuan como un especialista humano
en un dominio particular o área de conocimiento. El experto
humano transmite su conocimiento al sistema, y el usuario lo
utiliza para resolver problemas con el eficacia del especialista. El
usuario también puede aprender observando, es decir, puede
considerarse al sistema experto como un medio de ejecución y
transmisión del conocimiento.
La característica fundamental de un sistema experto es que separa
los conocimientos almacenados (base de conocimiento) del
programa que los controla (motor de inferencia). Los datos
propios de un determinado problema se almacenan en una base de
datos aparte (base de hechos).
Es importante diferenciar entre estos sistemas y los clásicos. En la
Tabla 1, podemos observar las principales diferencias entre un
sistema clásico y un sistema experto.
.
1.1 Motor de Inferencias en un Sistema
Experto
El motor de inferencias es un programa que controla el proceso de
razonamiento que seguirá el sistema experto. Utilizando los datos
que se le suministran, recorre la base de conocimientos para
alcanzar una solución. La estrategia de control puede ser de
encadenamiento progresivo o de encadenamiento regresivo. En el
primer caso se comienza con los hechos disponibles en la base de
datos, y se buscan reglas que satisfagan esos datos, es decir,
reglas que verifiquen la parte SI. Normalmente, el sistema sigue
los siguientes pasos:
1. Evaluar las condiciones de todas las reglas respecto a la base de
datos, identificando el conjunto de reglas que se pueden aplicar
(aquellas que satisfacen su parte condición)
2. Si no se puede aplicar ninguna regla, se termina sin éxito; en
caso contrario se elige cualquiera de las reglas aplicables y se
ejecuta su parte acción (esto último genera nuevos hechos que se
añaden a la base de datos)
3. Si se llega al objetivo, se ha resuelto el problema; en caso
contrario, se vuelve al paso 1
A este enfoque se le llama también guiado por datos, porque es el
estado de la base de datos el que identifica las reglas que se
pueden aplicar. Cuando se utiliza este método, el usuario
comenzará introduciendo datos del problema en la base de datos
del sistema.
Al encadenamiento regresivo se le suele llamar guiado por
objetivos, ya que, el sistema comenzará por el objetivo (parte
acción de las reglas) y operará retrocediendo para ver cómo se
deduce ese objetivo partiendo de los datos. Esto se produce
directamente o a través de conclusiones intermedias o
subobjetivos. Lo que se intenta es probar una hipótesis a partir de
los hechos contenidos en la base de datos y de los obtenidos en el
proceso de inferencia.
1.2 Sistemas Expertos Basados en Reglas
Surgen durando los 70 y los 80 en el campo de la investigación de
la Inteligencia Artificial. En ellos, los datos del problema son
almacenados como hechos, y razonan usando reglas del formato
“IF…THEN…ELSE”.
Estos sistemas se usan cuando el dominio del problema es
estrecho, es decir, es un dominio en el que se comprende bien la
teoría. Además, el conocimiento debe poder representarse
mediante hechos y reglas. La salida a estos sistemas es una
recomendación y en general no tienen habilidad de aprender,
aunque no es imposible.
2. JESS (JAVA EXPERT SYSTEM SHELL)
WORKIN
G
MEMORY
RULE
INFERENC
E
ENGINE
PATTERN
EXECUTION
MATCHE
R
ENGINE
BASE
AGEND
A
Figura 1. Diagrama de la arquitectura JESS
3. EL JUEGO: “SEISES”
3.1 El Juego Original
Para ilustrar el funcionamiento de los sistemas expertos, hemos
desarrollado el juego “Seises” usando JESS.
El juego consiste en descartarse de todas las cartas que el jugador
tiene en su poder. Se juega con los cuarenta naipes de la baraja
española (ver Fig 5), que se reparten entre los jugadores.
Comienza colocando el jugador que tenga en su poder el 6 de
oros, y a partir de ahí se seguirá jugando en el sentido de las
agujas del reloj.
El juego consiste en formar las 4 escaleras (una por palo),
partiendo de los seises. El jugador que tenga el turno podrá
continuar una escalera ya existente, o crear una nueva si tiene
alguno de los seises en su poder. Si el jugador no puede colocar
ninguna carta, deberá decir que “pasa” y ceder el turno al
siguiente jugador sin haber colocado carta.
El jugador que antes consiga colocar sus cartas es el ganador.
JESS es desarrollado en Sandia National Laboratories a finales de
los 90. Fue creado por el Dr. Ernest J. Friedmans Hill, y está
inspirado en CLIPS, que es un lenguaje basado en reglas de
Inteligencia Artificial. Existe un API JAVA completamente
desarrollado para crear sistemas expertos basados en reglas.
2.1 Funcionamiento de JESS
JESS une hechos de la base de hechos con reglas de la base de
reglas. Estas reglas contienen llamadas a funciones que manipulan
la base de hechos y/u otro código Java. JESS usa el algoritmo
Rete (ree-tee) para unir patrones. Una red Rete es una colección
de nodos interconectados, es decir, una memoria funcional.
Figura 2. Ejemplo juego “Seises” con baraja española
3.2 Juego Simplificado
En nuestro caso el juego se ha simplificado y solo jugaremos con
diez cartas rojas y diez cartas negras, que irán del 1 al 10 en
ambos casos. En total tendremos 20 cartas, que se repartirán entre
los jugadores. Para el ejemplo creado, tendremos 4 jugadores, con
5 cartas cada uno inicialmente.
4. IMPLEMENTACION DEL JUEGO EN
JESS
Para implementar nuestro juego, se han creado inicialmente dos
plantillas. La plantilla llamada “extremos” controlará las últimas
cartas que se han colocado en ambas escaleras, a fin de saber qué
cartas pueden ser colocadas en el turno actual. La otra plantilla,
“turno”, controlará qué jugador tiene el turno.
(deftemplate extremos
(slot ext1)
(slot ext2)
(slot ext3)
(slot ext4)
)
(deftemplate turno
Figura 3. Cartas rojas con las que jugaremos (1-10)
(slot turno_de)
)
Así mismo, se han definido una serie de variables globales, que se
usarán a lo largo del programa. Estas variables controlarán el
número de cartas que tiene cada jugador. Cada vez que un jugador
ponga una carta en alguna de las escaleras, se decrementará este
valor. Cuando llegue a 0, el jugador habrá ganado la partida.
El número de cartas está inicializado a 5, porque tenemos 20
cartas y 4 jugadores. Si fueran más o menos jugadores,
deberíamos variar este valor inicial.
(defglobal
?*nrocartasJ1* = 5
?*nrocartasJ2* = 5
Figura 4. Cartas negras con las que jugaremos (11-20)
?*nrocartasJ3* = 5
?*nrocartasJ4* = 5
Las cartas estarán identificadas por número del 1 al 20, siendo del
1 al 10 las rojas y del 11 al 20 las negras, como vemos en la
figura.
Comienza poniendo carta el jugador que tenga el 6 rojo. Tras
colocar una carta, el turno pasa al siguiente jugador. El siguiente
jugador podrá colocar si tiene alguna carta consecutiva, es decir,
si tiene el 5 o el 7 rojo, o si tiene el 6 negro. A partir de ahí, los
jugadores podrán colocar tanto en la escalera roja como en la
negra, pero siempre respetando el orden consecutivo de éstas.
Ganará el jugador que antes se descarte de sus cartas.
)
Además de las plantillas y las variables globales, se añaden al
principio una serie de hechos, que inicializarán la partida. Se
reparten las cartas a los jugadores (debería hacerse de manera
aleatoria, pero en nuestro caso, por simplicidad, se asignan las
cartas a los jugadores.) Estos datos iniciales tambien se pueden
poner en un fichero aparte (reparto.dat) que se cargaría al
comienzo del programa con “load”.
(deffacts CARTASJ1
(cartaJ1 15)
(cartaJ1 13)
(cartaJ1 17)
(cartaJ1 16)
(cartaJ1 14)
)
(deffacts CARTASJ2
Figura 5. Reparto inicial de las cartas en nuestro ejemplo
(cartaJ2 11)
(cartaJ2 3)
(cartaJ2 8)
A partir de aquí, se implementan las reglas que van a regir el
juego.
(cartaJ2 19)
(cartaJ2 1)
4.1 Reglas Utilizadas
)
(deffacts CARTASJ3
(cartaJ3 12)
(cartaJ3 2)
(cartaJ3 6)
(cartaJ3 9)
(cartaJ3 5)
)
Las cuatro primeras reglas (“empieza_J1”, “empieza_J2”,
“empieza_J3” y “empieza_J4”), se activan cuando el jugador
correspondiente tiene la carta 6 (que corresponde con el 6 rojo)
(defrule empieza_J1
?s <- (cartaJ1 6)
?v <- (extremos (ext1 ?e1) (ext2 ?e2) (ext3 ?e3) (ext4 ?e4))
?t <- (turno (turno_de ?g))
=>
(deffacts CARTASJ4
(cartaJ4 10)
(cartaJ4 4)
(cartaJ4 18)
(cartaJ4 20)
(cartaJ4 7)
)
(printout t "El jugador uno coloca un 6 rojo en la mesa" crlf)
(assert (turno (turno_de 2)))
(retract ?t)
(bind ?*nrocartasJ1* (- ?*nrocartasJ1* 1))
(assert (extremos (ext1 5) (ext2 7) (ext3 0) (ext4 0) ))
(retract ?v)
(retract ?s)
)
Durante esta regla se van a modificar los hechos “extremos”,
“cartaJ1” y “turno”, así que los identificamos con las variables
?v,?s y ?t para, una vez modificados, poder eliminar los hechos
“antiguos”.
Al asignar de esta manera un hecho a una variable (?var <hecho), lo que estamos haciendo es pasar el identificador del
hecho, para que (retract ?var) pueda eliminarlo de la lista de
hechos.
then
Tambien hemos modificado la variable que contiene el número de
cartas del jugador, para indicar que el jugador ya ha colocado una
de sus cartas.
(assert (turno (turno_de 3)))
A partir de aquí, se escriben 4 reglas más, que se activarán según
el jugador que tenga el turno, mediante la comprobación del
hecho “turno”.
(printout t "El jugador 2 coloca la primera carta negra" crlf)
(retract ?f)
(retract ?t)
(assert (extremos (ext1 ?e1) (ext2 ?e2) (ext3 15) (ext4 17)))
(printout t "Coloca la carta " ?card crlf crlf)
(retract ?v)
(bind ?*nrocartasJ2* (- ?*nrocartasJ2* 1))
(defrule Juega_J2
(return)
)
?t <- (turno (turno_de 2))
?v <- (extremos (ext1 ?e1) (ext2 ?e2) (ext3 ?e3) (ext4 ?e4))
?f <- (cartaJ2 ?card)
=>
…
)
Dentro de cada una de estas reglas es donde comprobamos si
podemos poner carta o no, y si hemos puesto todas nuestras
cartas.
Lo primero que comprueba la regla es si tenemos el 6 negro, para
colocar esta carta y abrir las posibilidades de colocación.
A continuación, va comprobando las últimas cartas colocadas a
ambos extremos de las escaleras, para intentar colocar una de sus
cartas. En nuestro caso, comienza intentando colocar las rojas
(primero las más bajas) y luego las negras (empezando también
por las bajas). Si encuentra alguna que colocar, elimina esa carta
(retract ?f), pasa el turno al siguiente jugador y modifica los
extremos de las escaleras. Se incluye un (return) para que no siga
comprobando el resto de posiblidades y vaya al siguiente jugador.
(…)
(…)
(if (= ?card ?e1)
(if (and (<= ?*nrocartasJ2* 1) (or (= ?card ?e1)(= ?card ?e2)(=
?card ?e3)(= ?card ?e4) ) )
then
then
(printout t "Ha ganado el jugador numero 2" crlf)
(assert (turno (turno_de 0)))
(retract ?t)
(halt)
(return)
(…)
Comprobamos primero si ya hemos puesto 4 cartas y si la última
que nos queda podemos colocarla. Si es así. ¡El jugador ha
ganado! Si no, comprobaremos si tenemos alguna carta que
colocar en las escaleras.
(printout t "El jugador 2 coloca una carta roja por debajo" crlf)
(retract ?f)
(assert (turno (turno_de 3)))
(retract ?t)
(assert (extremos (ext1 (- ?e1 1)) (ext2 ?e2) (ext3 ?e3) (ext4 ?e4)))
(printout t "Coloca la carta " ?card crlf crlf)
(retract ?v)
(bind ?*nrocartasJ2* (- ?*nrocartasJ2* 1))
(return)
)
(if (= ?card ?e2)
then
(printout t "El jugador 2 coloca una carta roja por encima" crlf)
(…)
else
(retract ?f)
(assert (turno (turno_de 3)))
(retract ?t)
(if (= ?card 16)
(assert (extremos (ext1 ?e1) (ext2 (+ ?e2 1)) (ext3 ?e3) (ext4
?e4)))
then
(printout t "Coloca la carta " ?card crlf crlf)
(assert (turno (turno_de 3)))
(retract ?v)
(retract ?t)
(bind ?*nrocartasJ2* (- ?*nrocartasJ2* 1))
)
(return)
)
)
)
(printout t "El jugador 2 no puede poner. Pasa turno." crlf)
(if (= ?card ?e3)
then
(printout t "El jugador 2 coloca una carta negra por debajo" crlf)
(retract ?f)
(assert (turno (turno_de 3)))
(retract ?t)
(assert (extremos (ext1 ?e1) (ext2 ?e2) (ext3 (- ?e3 1)) (ext4 ?e4)))
(printout t "Coloca la carta " ?card crlf crlf)
(retract ?v)
5. PROBLEMAS ENCONTRADOS
Durante la implementación del sistema nos encontramos con
varios problemas. En un principio se intentó evitar el uso de los IF
para localizar las cartas que se podían poner, pero el código
hubiese sido notablemente más grande, y para facilitar la
comprensión del sistema se decidió incluir solo las 4 reglas
activadas por los turnos, y usar las esructuras IF para comprobar
las cartas.
(bind ?*nrocartasJ2* (- ?*nrocartasJ2* 1))
(return)
6. TEST DEL SISTEMA
)
(if (= ?card ?e4)
then
(printout t "El jugador 2 coloca una carta negra por encima" crlf)
(retract ?f)
Para probar nuestro sistema, añadiremos las cartas que tiene cada
jugador como hechos. A continuación cargaremos nuestro fichero
“sesies.clp” mediante (batch sesies.clp), y para activar los hechos,
haremos un (reset). Solo son quedar ejecutar un (run) y ver qué
ocurre. Hemos probado el programa con los siguientes hechos
iniciales
(assert (turno (turno_de 3)))
(retract ?t)
(deffacts ini_turno
(assert (extremos (ext1 ?e1) (ext2 ?e2) (ext3 ?e3) (ext4 (+ ?e4
1))))
(turno (turno_de 0))
(printout t "Coloca la carta " ?card crlf crlf)
)
(retract ?v)
(bind ?*nrocartasJ2* (- ?*nrocartasJ2* 1))
(return)
(deffacts CARTASJ1
)
(cartaJ1 15)
(…)
(cartaJ1 13)
(cartaJ1 17)
Por ultimo, se comprueba si ninguna de las cartas que tiene el
jugador puede ser colocada. En este caso, el jugador pasa su
turno. No se elimina ninguna de sus cartas ni se modifican los
extremos de las escaleras, tan solo se varía el turno.
(…)
(if (and (<> ?card ?e1) (<> ?card ?e2) (<> ?card ?e3) (<>
?card ?e4))
(cartaJ1 16)
(cartaJ1 14)
)
(deffacts CARTASJ2
(cartaJ2 11)
(cartaJ2 3)
(cartaJ2 8)
(cartaJ2 19)
(cartaJ2 1)
)
(deffacts CARTASJ3
(cartaJ3 12)
(cartaJ3 2)
(cartaJ3 6)
(cartaJ3 9)
(cartaJ3 5)
)
(deffacts CARTASJ4
(cartaJ4 10)
(cartaJ4 4)
(cartaJ4 18)
(cartaJ4 20)
(cartaJ4 7)
)
Figura 6. Resultados obtenidos con los hechos iniciales
especificados
7. MEJORAS POSIBLES DEL SISTEMA
(deffacts inicio
(extremos (ext1 0) (ext2 0) (ext3 0) (ext4 0))
)
Como vemos, es suficiente con estas 2 reglas por jugador para que
el juego pueda desarrollarse. En nuestro estudio no se ha dotado
de especial inteligencia al sistema, simplemente colocará las
cartas en base a las reglas expuestas (deben ser consecutivas, y el
6 rojo va primero), pero el sistema podría hacerse mucho más
“inteligente” con solo incluir unas cuantas reglas más, del tipo “SI
tengo dos cartas consecutivas en mi poder Y puedo colocar una de
ellas, ENTONCES colocaré siempre una de esas cartas”. Esta
regla conseguiría evitar que otro jugador pueda poner una de sus
cartas hasta que nosotros coloquemos una nuestra, y además
permite al jugador actual colocar seguro en su próximo turno.
Nuestro sistema puede ser notablemente mejorado, tanto la
interfaz (en vez de números podríamos identificar las cartas con
su nombre, “7 de picas”, por ejemplo), como su funcionalidad,
añadiendo más reglas que permitiesen a los jugadores desarrollar
algún tipo de estrategia. Sin embargo, se ha considerado que el
sistema desarrollado es suficiente para ilustrar en qué consiste un
sistema experto y cómo debe usarse.
8. ACKNOWLEDGMENTS
9. REFERENCES
Gracias a Julio Villena, profesor de la asignatura Inteligencia en
Redes de Comunicaciones de la Universidad Carlos III de Madrid,
por su ayuda inestimable.
[1] Wikipedia.org
[2] Juan José Samper Márquez . “Introducción a los Sistemas
Expertos”. Redcientífica.com
[3] “Intro to JESS”. Jason Morris. Morris Technical Solutions