Professional Documents
Culture Documents
El juego de la
Escoba
En este caso, he optado por disear y desarrollar un sistema experto que sea
capaz de jugar a la escoba. La intencin no es implementar el juego de la escoba para
jugar en el PC (como otros juegos como el Solitario o el Buscaminas), sino realizar un
sistema experto que determine las jugadas a seguir ante las situaciones que se le
presentaran a un jugador humano, de forma que llegue a pensar como tal. Es decir, el
usuario es el que debe indicar al PC cules son las cartas que recibe, as como las que el
contrincante emplea en cada jugada. De esta forma, el PC recibe la misma informacin
que tendra un jugador humano.
Caractersticas de la implementacin
Las funciones y reglas que se han utilizado se encuentran comentadas en el
cdigo de la aplicacin. Aparte de lo comentado es necesario destacar los siguientes
puntos:
Las cartas que quedan en el mazo, as como las cartas que hay sobre la mesa y
las cartas que tiene el PC en la mano, se almacenarn en forma de hechos. De esta
forma, su estado podr disparar una determinada regla. Para eliminar una carta de uno
de estos tres conjuntos se incluir un hecho en el que se indica la carta que hay que
eliminar. Este hecho disparar una regla cuyo resultado es la supresin de la carta.
Comienzo
Lo primero que hace la aplicacin es preguntar al usuario si desea ser mano, o
por el contrario, que lo sea el PC.
Inicio de la partida
Al comenzar la partida, se deben colocar 4 cartas sobre la mesa. Estas cuatro
cartas las debe introducir el usuario por teclado.
Repartir
Para repartir, el usuario debe indicar al PC cules son las 3 cartas que coge. No
se deben indicar las cartas que coge el jugador, ya que el PC no tiene por qu
conocerlas.
Turno del PC
Cuando el turno corresponde al PC, ste elige una de entre las cartas que tiene
para echar.
Lo primero que responde es cul es la carta que elige de entre las que tiene en la
mano para jugar. Si consigue hacer jugada (sumar 15), a continuacin muestras cules
son las cartas que se lleva. Lo siguiente que muestra por pantalla es la razn por la que
hace esa jugada y no otra. Es decir, por qu ha elegido esta jugada en lugar de otra
jugada que tambin podra hacer. Por ltimo, muestra cules son las cartas que quedan
sobre la mesa.
Escoba del PC
Si el PC consigue hacer una escoba, se muestra el mensaje ESCOBA!!!! por
pantalla. Adems, se aumenta la cuenta de escobas del PC en uno.
Tambin se indica que sobre la mesa no quedan cartas.
Final de la partida
Cuando concluye la partida, lo primero que se averigua es cul de los dos
jugadores se lleva las cartas que quedan sobrantes sobre la mesa, que ser el ltimo en
hacer jugada (el ltimo que se ha llevado alguna carta).
Cdigo de la aplicacin
;;;;;;;;;;;;;;;;;;;;;;;;
;; JUEGO DE LA ESCOBA ;;
;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Rubn Grande Baquero
;;
;; Inteligencia en Redes de Comunicaciones
;; 5 Ing. de Telecomunicacin
;; Curso 2003/2004
;;
;;;;;;;;;;;;;;;
;; Funciones ;;
;;;;;;;;;;;;;;;
; Esta funcion elige una de las dos opciones, que son las cartas
; que quedaran en la mesa despues de la jugada
; ?opcion1-str es una cadena de caracteres que contiene las cartas que quedarian
; en la mesa de escoger la opcion 1
; $?cartas-mesa contiene las cartas que quedarian en la mesa de escoger la opcion 2
(deffunction mejor-opcion-cartas-mesa(?opcion1-str $?opcion2)
(bind $?opcion1 (explode$ ?opcion1-str))
;; Caracteristicas:
; Puntos sobre la mesa:
(bind ?puntos1 (sumar-puntos $?opcion1))
(bind ?puntos2 (sumar-puntos $?opcion2))
; Numero de oros;
(bind ?oros1 (sumar-oros $?opcion1))
(bind ?oros2 (sumar-oros $?opcion2))
; Numero de sietes:
(bind ?sietes1 (sumar-sietes $?opcion1))
(bind ?sietes2 (sumar-sietes $?opcion2))
(if (and (not str-index "7 oros" (implode$ $?opcion1)) (str-index "7 oros" (implode$
$?opcion2))) then
(bind ?*razon* "no quiero echar el 7 de oros")
(return 1)
)
(if (and (not str-index "7 oros" (implode$ $?opcion2)) (str-index "7 oros" (implode$
$?opcion1))) then
(bind ?*razon* "no quiero echar el 7 de oros")
(return 2)
)
(if (and (or (< ?puntos1 5) (> ?puntos1 15)) (>= ?puntos2 5) (<= ?puntos2 15)) then
(bind ?*razon* "el contrincante no puede hacer escoba")
(return 1)
)
(if (and (or (< ?puntos2 5) (> ?puntos2 15)) (>= ?puntos1 5) (<= ?puntos1 15)) then
(bind ?*razon* "el contrincante no puede hacer escoba")
(return 2)
)
; Hay que constatar que si ya hay una figura, el hecho de echar otra figura
; del mismo valor no aumenta las posibilidades de jugada por parte del contrario.
; En caso contrario, las posibilidades siempre pueden aumentar.
; Llegados a este punto, no hay criterio para elegir una opcion u otra
; En posteriores versiones se podrian aumentar los criterios aumentando
; de esta forma la inteligencia del programa
(bind ?*razon* "me es indiferente")
; Opcion por defecto
(return 1)
)
; Esta funcion auxiliar devuelve los puntos que suman un conjunto de cartas
(deffunction sumar-puntos($?cartas)
(bind ?total 0)
(while (<> 0 (length$ $?cartas))
(bind ?total (+ ?total (nth$ 1 $?cartas)))
(bind $?cartas (rest$ $?cartas))
(bind $?cartas (rest$ $?cartas))
)
?total
)
; Esta funcion auxiliar devuelve la cantidad de oros que hay en un conjunto de cartas
(deffunction sumar-oros($?cartas)
(bind ?total 0)
(bind ?indice (member$ oros $?cartas))
(while ?indice
(bind ?total (+ 1 ?total))
(bind $?cartas (delete$ $?cartas ?indice ?indice))
(bind ?indice (member$ oros $?cartas))
)
?total
)
; Esta funcion auxiliar devuelve la cantidad de sietes que hay en un conjunto de cartas
(deffunction sumar-sietes($?cartas)
(bind ?total 0)
(bind ?indice (member$ 7 $?cartas))
(while ?indice
(bind ?total (+ 1 ?total))
(bind $?cartas (delete$ $?cartas ?indice ?indice))
(bind ?indice (member$ 7 $?cartas))
)
?total
)
; Vamos recorriendo las cartas que tenemos, buscando la que hay que quitar
(while (<> 0 (length$ $?cartas))
(bind ?valor (nth$ 1 $?cartas))
(bind $?cartas (rest$ $?cartas))
(bind ?palo (nth$ 1 $?cartas))
(bind $?cartas (rest$ $?cartas))
(if (or (<> ?valor-a-quitar ?valor) (neq ?palo-a-quitar ?palo)) then
; Si no hay que eliminar esta carta, la aadimos a $?cartas-aux
(bind $?cartas-aux (create$ $?cartas-aux ?valor ?palo))
)
)
; Las cartas recogidas en $?cartas-aux son las que nos quedan
(bind $?cartas $?cartas-aux)
)
(return $?cartas)
)
; Esta funcion aade a la cuenta de cartas, oros y sietes del PC los obtenidos de una
jugada.
(deffunction contar-puntos-PC($?cartas)
; Se aade le numero de cartas conseguidas
(bind ?*monton-PC* (+ ?*monton-PC* (/ (length$ $?cartas) 2)))
; Se aade el numero de oros
(bind ?*oros-PC* (+ ?*oros-PC* (sumar-oros $?cartas)))
; Se aade el numero de sietes
(bind ?*sietes-PC* (+ ?*sietes-PC* (sumar-sietes $?cartas)))
; Si hemos conseguido el 7 de oros, esto cuenta como una escoba
(if (str-index "7 oros" (implode$ $?cartas)) then
(bind ?*escobas-PC* (+ 1 ?*escobas-PC*))
)
; Tambien comprobamos si algun objetivo del juego ya se ha conseguido
(if (> ?*monton-PC* 20) then
(bind ?*num-cartas-decidido* TRUE)
)
(if (> ?*oros-PC* 5) then
(bind ?*num-oros-decidido* TRUE)
)
(if (> ?*sietes-PC* 2) then
(bind ?*num-sietes-decidido* TRUE)
)
)
;;;;;;;;;;;;
;; Reglas ;;
;;;;;;;;;;;;
; Esta regla se activa cada vez que aparece una carta nueva, para eliminarla del mazo
(defrule eliminar-de-mazo (declare (salience 10))
?regla-eliminar <- (eliminar-de-mazo ?valor ?palo)
?regla-mazo <- (cartas-en-mazo $?previas ?valor ?palo $?posteriores)
=>
(retract ?regla-eliminar)
(retract ?regla-mazo)
(assert (cartas-en-mazo $?previas $?posteriores))
)
; Esta regla se activa cada vez que se coge una carta de la mesa, para eliminarla
(defrule eliminar-de-mesa (declare (salience 10))
?regla-eliminar <- (eliminar-de-mesa ?valor ?palo)
?regla-mesa <- (cartas-en-mesa $?previas ?valor ?palo $?posteriores)
=>
(retract ?regla-eliminar)
(retract ?regla-mesa)
(assert (cartas-en-mesa $?previas $?posteriores))
)
; Esta regla se activa cuando el PC va a echar una carta sin llevarse ninguna
(defrule echar-sin-llevarse
?regla-echar <- (echar-sin-llevarse)
?regla-turno <- (turno-de PC)
?regla-mesa <- (cartas-en-mesa $?cartas-mesa)
?regla-carta <- (usar-carta $?carta) ; Indica la carta que va a echar
=>
; Se eliminan los hechos obsoletos
(retract ?regla-echar)
(retract ?regla-turno)
(retract ?regla-mesa)
(retract ?regla-carta)
; La carta ya no la tiene el PC en la mano
(assert (eliminar-de-mano $?carta))
; El turno siguiente sera del jugador
(assert (turno-de jugador))
; Se aade la carta a la mesa
(assert (cartas-en-mesa $?cartas-mesa $?carta))
; Se indica todo por pantalla
(printout t "Echo el " (implode$ $?carta) " porque " ?*razon* crlf)
(printout t "Cartas sobre la mesa:" crlf)
(printout t (create$ $?cartas-mesa $?carta) crlf)
)
; Esta regla se activa cada vez que tenemos que repartir 3 cartas a cada uno
(defrule repartir
(cartas-en-mazo ? $?) ;si todavia quedan cartas
?hecho-PC <- (cartas-de-PC)
(test (= ?*n-cartas-jugador* 0)) ;y los jugadores se han quedado sin cartas
=>
(retract ?hecho-PC)
;;;;;;;;;;;;
;; Hechos ;;
;;;;;;;;;;;;
(deffacts hechos-iniciales
; Mazo inicial: Las 40 cartas de la baraja
(cartas-en-mazo
1 oros
2 oros
3 oros
4 oros
5 oros
6 oros
7 oros
8 oros
9 oros
10 oros
1 copas
2 copas
3 copas
4 copas
5 copas
6 copas
7 copas
8 copas
9 copas
10 copas
1 espadas
2 espadas
3 espadas
4 espadas
5 espadas
6 espadas
7 espadas
8 espadas
9 espadas
10 espadas
1 bastos
2 bastos
3 bastos
4 bastos
5 bastos
6 bastos
7 bastos
8 bastos
9 bastos
10 bastos)
;;;;;;;;;;;;;;;;;;;;;;;;
;; Variables globales ;;
;;;;;;;;;;;;;;;;;;;;;;;;
(defglobal
?*n-cartas-jugador* = 0 ;Numero de cartas que el jugador tiene en la mano
?*escobas-PC* = 0 ;Numero de escobas que ha conseguido el PC
?*escobas-jugador* = 0 ;Numero de escobas que ha conseguido el jugador
?*monton-PC* = 0 ;Numero de cartas que ha conseguido el PC
?*monton-jugador* = 0 ;Numero de cartas que ha conseguido el jugador
?*oros-PC* = 0 ;Numero de oros que ha conseguido el PC
?*oros-jugador* = 0 ;Numero de oros que ha conseguido el jugador
?*sietes-PC* = 0 ;Numero de sietes que ha conseguido el PC
?*sietes-jugador* = 0 ;Numero de sietes que ha conseguido el jugador
?*num-cartas-decidido* = FALSE ;Si ya se sabe quien gana en numero de cartas
?*num-oros-decidido* = FALSE ;Si ya se sabe quien gana en numero de oros
?*num-sietes-decidido* = FALSE ;Si ya se sabe quien gana en numero de sietes
?*razon* = "" ;Indica la razon por la que el PC echa una carta y no otra
)
(reset)
(inicializacion)