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