• No se han encontrado resultados

El juego de la Escoba

N/A
N/A
Protected

Academic year: 2021

Share "El juego de la Escoba"

Copied!
18
0
0

Texto completo

(1)

INTELIGENCIA EN REDES DE COMUNICACIONES

El juego de la

Escoba

(2)

1- Introducción y objetivos

El objetivo de esta práctica es afianzar los conocimientos presentados a lo largo

del temario de la asignatura “Inteligencia en redes de comunicaciones”, para llegar a

comprobar si somos capaces de diseñar un sistema informático dotado de inteligencia.

En este caso, he optado por diseñar y desarrollar un sistema experto que sea

capaz de jugar a la escoba. La intención 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

presentarían a un jugador humano, de forma que llegue a pensar como tal. Es decir, el

usuario es el que debe indicar al PC cuáles son las cartas que recibe, así como las que el

contrincante emplea en cada jugada. De esta forma, el PC recibe la misma información

que tendría un jugador humano.

2- Reglas básicas del juego

Para entender el funcionamiento del sistema, antes de todo tenemos que tener

claro cuáles son las reglas del juego.

Este juego requiere la utilización de la baraja española de 40 cartas. Es decir, del

1 al 10 de cada uno de los cuatro palos (oros, copas, espadas y bastos).

El funcionamiento del juego es el siguiente: Al comenzar, se colocan 4 cartas

sobre la mesa y se reparten 3 cartas a cada uno de los dos jugadores. El jugador mano

comienza a jugar. En cada turno, el jugador echa una carta de las que tiene en la mano.

El jugador puede llevarse la carta que ha echado más una o varias de las cartas que hay

sobre la mesa siempre que la suma de los valores de las cartas que se lleva sea 15. Si el

jugador se lleva todas las cartas que hay sobre la mesa, se dice que ha hecho una

escoba.

Cuando ambos jugadores se hayan quedado sin cartas, se volverán a repartir 3

cartas a cada uno de los dos, y la partida continuará. La partida terminará cuando ambos

jugadores se hayan quedado sin cartas y ya no queden cartas que repartir. Cuando la

partida termina, el último jugador que haya conseguido llevarse cartas, se lleva también

las cartas que queden sobre la mesa.

La puntuación se realiza de la siguiente manera: Cada escoba realizada cuenta

como un punto. El jugador que se haya llevado el 7 de oros gana un punto. El jugador

que se haya llevado más sietes gana un punto. El jugador que se haya llevado más oros

gana un punto. El jugador que se haya llevado más cartas gana un punto. En estos tres

últimos casos, si ambos jugadores han conseguido el mismo número de sietes, oros o

cartas, ninguno de los dos consigue el correspondiente punto. Gana la partida el jugador

que más puntos haya conseguido.

(3)

Herramienta de desarrollo

La herramienta que se va a utilizar para desarrollar el sistema experto que juega

a la escoba será Jess. En concreto, se utilizará la versión de Jess 6.0a5, que es

compatible con Java 2.

La aplicación correrá en el sistema operativo Windows 2000 en un Pentium 3 a

866 MHz. Como se verá más adelante, en este equipo el tiempo de proceso es

inapreciable.

Características de la implementación

Las funciones y reglas que se han utilizado se encuentran comentadas en el

código de la aplicación. Aparte de lo comentado es necesario destacar los siguientes

puntos:

Una carta se almacena en la aplicación en forma de multicampo con dos

símbolos. El primero de ellos es un número del 1 al 10 que representa el valor de la

carta. El segundo representa el palo, y puede tomar uno de los siguientes valores: ‘oros’,

‘copas’, ‘espadas’ o ‘bastos’.

La entrada por el teclado de una carta se realiza de la misma forma. Es decir, el

usuario debe introducir un número del 1 al 10, un espacio en blanco, y el palo (‘oros’,

‘copas’, ‘espadas’ o ‘bastos’). No he creído necesario introducir tratamiento de errores

ante lo que pueda introducir el usuario, dado que la aplicación está pensada para ser

integrada en el futuro en una interfaz gráfica que sea mucho más amigable que la simple

interfaz textual. Si el usuario introduce erróneamente estos datos, la aplicación no

funcionará correctamente.

Un conjunto de cartas se representa como un multicampo formado por la

concatenación de los multicampos que representan cada una de las cartas que lo forman.

Por lo tanto, los índices impares (1,3,5,...) indican los valores de las cartas, y los índices

pares (2,4,6,...) indican los palos.

Al usuario nunca se le pedirá que introduzca un conjunto de cartas como tal.

Siempre que sea necesario, la aplicación solicitará al usuario las cartas una por una. De

esta forma, durante el juego, la única información que recibe el programa a través del

teclado es, o una carta en concreto, o respuestas del tipo sí/no.

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 almacenarán 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 supresión de la carta.

La inteligencia del sistema se encuentra en las funciones jugada (en el caso de

que el PC se lleve alguna carta) y mejor-opcion-cartas-mesa (donde se decide entre dos

opciones qué conjunto de cartas que quedarían en la mesa podría venirle peor al

contrario).

(4)

El comportamiento de cada una de las funciones y reglas viene comentado en el

código de la aplicación.

Comportamiento del programa

A continuación se presentará el comportamiento del sistema en todos sus

aspectos. Para ello se incluyen capturas de pantalla de cada momento significativo de la

aplicación.

Comienzo

Lo primero que hace la aplicación 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 cuáles 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 cuál es la carta que elige de entre las que tiene en la

mano para jugar. Si consigue hacer jugada (sumar 15), a continuación muestras cuáles

(5)

son las cartas que se lleva. Lo siguiente que muestra por pantalla es la razón por la que

hace esa jugada y no otra. Es decir, por qué ha elegido esta jugada en lugar de otra

jugada que también podría hacer. Por último, muestra cuáles son las cartas que quedan

sobre la mesa.

Turno del jugador

Cuando el turno corresponde al jugador, el PC se queda a la espera de que el

usuario introduzca la carta con la que va a jugar.

El usuario introduce la carta con la que juega. A continuación, se le pregunta si

con la carta que ha echado se va a llevar alguna de la mesa.

El jugador se lleva cartas

Si el jugador responde afirmativamente a la pregunta anterior, se le pedirá que

introduzca una a una las cartas que se lleva.

Cuando la suma llega a 15, significa que el usuario ya ha terminado de

introducir las cartas que se lleva. Al final, muestra las cartas que quedan sobre la mesa.

El jugador no se lleva cartas

Si el usuario indica que no se lleva ninguna carta, la carta utilizada para jugar se

añade a las que hay sobre la mesa.

Escoba del PC

Si el PC consigue hacer una escoba, se muestra el mensaje “ESCOBA!!!!” por

pantalla. Además, se aumenta la cuenta de escobas del PC en uno.

(6)

También se indica que sobre la mesa no quedan cartas.

Escoba del jugador

El jugador no debe indicar explícitamente que hace escoba. Es el sistema el que

comprueba si el jugador hace una escoba si no deja cartas sobre la mesa.

El sistema muestra el mensaje “Eso es una ESCOBA!!” y aumenta la cuenta de

escobas del jugador.

Final de la partida

Cuando concluye la partida, lo primero que se averigua es cuál 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).

A continuación se efectúa un recuento de las escobas conseguidas (llevarse el 7

de oros cuenta como una escoba) y quién ha ganado en número de cartas, oros y sietes.

Se muestra cuál de los dos jugadores ha ganado la partida (o si queda en empate) y se

pregunta si el usuario quiere comenzar una nueva partida. En caso afirmativo, el sistema

vuelve a comenzar su ejecución. Si el usuario no quiere jugar otra partida, la ejecución

termina.

Otros aspectos importantes sobre cómo utilizar el programa

Debe quedar claro que no se trata de un juego para ordenador en el que se puede

jugar a la escoba contra él, sino que es necesario disponer de una baraja. Al PC se le

debe introducir la información que recibiría un jugador (las cartas que tiene, las que el

otro echa, etc.) para que se comporte como lo haría una persona.

(7)

Por eso, para comprobar el funcionamiento, se debe utilizar una baraja y simular

una partida con ella. De esta forma nos aseguramos de que no se repiten cartas, y que la

partida termina sólo cuando se agota el mazo, ya que el sistema no da la partida por

concluida hasta que no han aparecido las 40 cartas.

Código de la aplicación

;;;;;;;;;;;;;;;;;;;;;;;; ;; JUEGO DE LA ESCOBA ;; ;;;;;;;;;;;;;;;;;;;;;;;; ;;

;; Rubén Grande Baquero ;;

;; Inteligencia en Redes de Comunicaciones ;; 5º Ing. de Telecomunicación ;; Curso 2003/2004 ;; ;;;;;;;;;;;;;;; ;; Funciones ;; ;;;;;;;;;;;;;;;

; Esta funcion se ejecuta al comenzar la partida. (deffunction inicializacion()

(printout t "Deseas ser mano? (si/no) ")

(if (eq si (read)) then (assert (turno-de jugador)) else (assert (turno-de PC))

)

; Al comenzar la partida, hay que colocar cuatro cartas sobre la mesa (printout t "Introduzca las cuatro cartas que hay sobre la mesa" crlf) (printout t "Primera carta: ")

(bind $?carta1 (explode$ (readline))) (printout t "Segunda carta: ") (bind $?carta2 (explode$ (readline))) (printout t "Tercera carta: ") (bind $?carta3 (explode$ (readline))) (printout t "Cuarta carta: ")

(bind $?carta4 (explode$ (readline)))

(assert (cartas-en-mesa $?carta1 $?carta2 $?carta3 $?carta4)) (assert (eliminar-de-mazo $?carta1))

(assert (eliminar-de-mazo $?carta2)) (assert (eliminar-de-mazo $?carta3)) (assert (eliminar-de-mazo $?carta4)) (run-until-halt)

)

; Esta funcion determinara la estrategia del PC

; ?cartas-str es una cadena de caracteres que contiene las cartas que hay en la mano del PC

; $?cartas-mesa contiene las cartas que hay sobre la mesa (deffunction jugada(?cartas-str $?cartas-mesa)

; Recuperamos en un multicampo las cartas que puede echar el PC (bind $?cartas (explode$ ?cartas-str))

; En principio, todavía no hemos hecho jugada (bind ?hay-posibilidad FALSE)

(if (= 0 (length$ $?cartas-mesa)) then

;No hay cartas en la mesa. Hay que echar una

(assert (usar-carta (menos-mala (implode$ $?cartas) $?cartas-mesa))) (assert (echar-sin-llevarse))

(return) )

; Ahora se mira las posibilidades carta por carta (while (<> 0 (length$ $?cartas))

(bind ?valor-actual (nth$ 1 $?cartas)) (bind $?cartas (rest$ $?cartas)) (bind ?palo-actual (nth$ 1 $?cartas)) (bind $?cartas (rest$ $?cartas))

(8)

(if (= 15 (+ ?valor-actual (sumar-puntos $?cartas-mesa))) then ;; HAY ESCOBA

; Si tenemos la carta de oros echamos esa. Si no, la primera.

(if (str-index (implode$ (create$ ?valor-actual oros)) (implode$ $?cartas)) then (assert (usar-carta ?valor-actual oros))

else

(assert (usar-carta ?valor-actual ?palo-actual)) )

; El PC se lleva todas las cartas que hay sobre la mesa (assert (echar-llevandose $?cartas-mesa))

; Añadimos uno a la cuenta de escobas (bind ?*escobas-PC* (+ 1 ?*escobas-PC*)) (printout t "ESCOBA!!!!" crlf)

(bind ?*razon* "hago escoba") (return)

)

(if (< 15 (+ ?valor-actual (sumar-puntos $?cartas-mesa))) then ;; Puede que haya alguna posibilidad de llevarse algo

; El numero de combinaciones de cartas que nos podemos llevar es 2^N - 1, siendo N el numero de cartas

; que hay sobre la mesa. Cada carta ocupa dos campos (valor y palo) del multicampo (bind ?n-posibles-combinaciones (- (** 2 (/ (length$ $?cartas-mesa) 2)) 1))

; Buscaremos combinacion por combinacion. (bind ?indice 1)

(while (< ?indice ?n-posibles-combinaciones) ;La ultima combinacion ya se ha tenido en cuenta (escoba)

(bind $?cartas-a-llevarse (combinacion ?indice $?cartas-mesa)) (if (= 15 (+ ?valor-actual (sumar-puntos $?cartas-a-llevarse))) then

; Nos podemos llevar cartas (if ?hay-posibilidad then

; Hay que comparar las dos opciones:

; Opcion 1: La jugada que se habia decidido hasta ahora ; Opcion 2: La nueva jugada posible

(bind $?opcion1 (create$ ?carta-decidida $?jugada-decidida))

(bind $?opcion2 (create$ ?valor-actual ?palo-actual $?cartas-a-llevarse)) (bind ?*razon* "me llevo el 7 oros")

(if (and (str-index "7 oros" (implode$ $?opcion2)) (not (str-index "7 oros" (implode$ $?opcion1)))) then

; Si con la nueva jugada nos llevamos el 7 de oros, que no nos llevabamos con la anterior, sera la nueva la elegida

(bind $?carta-decidida (create$ ?valor-actual ?palo-actual)) (bind $?jugada-decidida $?cartas-a-llevarse)

else ;en caso contrario...

(if (not (and (str-index "7 oros" (implode$ $?opcion1)) (not (str-index "7 oros" (implode$ $?opcion2))))) then

; Si la jugada nueva no implica perder el 7 de oros... (bind ?*razon* "me llevo mas sietes")

(if (and (not ?*num-sietes-decidido*) (> sietes $?opcion2) (sumar-sietes $?opcion1))) then

; El criterio en este caso es el numero de sietes, siempre que ya no este decidido

(bind $?carta-decidida (create$ ?valor-actual ?palo-actual)) (bind $?jugada-decidida $?cartas-a-llevarse)

else

(if (or ?*num-sietes-decidido* (= (sumar-sietes $?opcion2) (sumar-sietes $?opcion1))) then

(bind ?*razon* "me llevo mas oros")

(if (and (not ?*num-oros-decidido*) (> (sumar-oros $?opcion2) (sumar-oros $?opcion1))) then

; Ahora el criterio es el numero de oros, siempre que ya no este decidido (bind $?carta-decidida (create$ ?valor-actual ?palo-actual))

(bind $?jugada-decidida $?cartas-a-llevarse) else

(if (or ?*num-oros-decidido* (= (sumar-oros $?opcion2) (sumar-oros $?opcion1))) then

(bind ?*razon* "me llevo mas cartas")

(if (and (not ?*num-cartas-decidido*) (> (length$ $?opcion2) (length$ $?opcion1))) then

; Ahora el criterio es el numero de cartas que nos llevamos (bind $?carta-decidida (create$ ?valor-actual ?palo-actual)) (bind $?jugada-decidida $?cartas-a-llevarse)

(9)

(if (or ?*num-cartas-decidido* (= (length$ $?opcion2) (length$ $?opcion1))) then

; Entonces hay que procurar dejar las peores cartas para el contrario (bind $?cartas-sobrantes1 (quitar-cartas (implode$ $?jugada-decidida) $?cartas-mesa))

(bind $?cartas-sobrantes2 (quitar-cartas (implode$ $?cartas-a-llevarse) $?cartas-mesa))

(if (= 2 (mejor-opcion-cartas-mesa (implode$ $?cartas-sobrantes1) $?cartas-sobrantes2)) then

(bind $?carta-decidida (create$ ?valor-actual ?palo-actual)) (bind $?jugada-decidida $?cartas-a-llevarse)

) ) ) ) ) ) ) ) ) else

; Si es la primera jugada posible que encontramos... (bind ?hay-posibilidad TRUE)

(bind $?carta-decidida (create$ ?valor-actual ?palo-actual)) (bind $?jugada-decidida $?cartas-a-llevarse)

(bind ?*razon* "no puedo hacer otra cosa") )

)

; Terminada la busqueda de esta posibilidad, continuamos por la siguiente (bind ?indice (+ 1 ?indice))

) ) )

; En este punto ya se han estudiado todas las posibilidades (if ?hay-posibilidad then

; Si podemos hacer jugada, ya tenemos decidida la carta a echar y la jugada (assert (usar-carta $?carta-decidida))

(assert (echar-llevandose $?jugada-decidida)) else

; Si no podemos hacer jugada, simplemente procuramos dejar en la mesa las peores cartas para el contrario

(assert (usar-carta (menos-mala ?cartas-str $?cartas-mesa))) (assert (echar-sin-llevarse))

) )

; Esta funcion elige una de entre las cartas que se tiene ; para echarla cuando el PC no se puede llevar ninguna

; ?cartas-str es una cadena de caracteres que contiene las cartas que hay en la mano del PC

; $?cartas-mesa contiene las cartas que hay sobre la mesa (deffunction menos-mala(?cartas-str $?cartas-mesa)

(bind $?cartas (explode$ ?cartas-str)) (bind ?valor-decidida (nth$ 1 $?cartas)) (bind $?cartas (rest$ $?cartas))

(bind ?palo-decidida (nth$ 1 $?cartas)) (bind $?cartas (rest$ $?cartas))

(bind $?carta-decidida (create$ ?valor-decidida ?palo-decidida)) ; Por ahora la unica razon para echar esta carta es que no hay otra (bind ?*razon* "no puedo echar otra")

(while (<> 0 (length$ $?cartas))

(bind ?valor-aestudiar (nth$ 1 $?cartas)) (bind $?cartas (rest$ $?cartas))

(bind ?palo-aestudiar (nth$ 1 $?cartas)) (bind $?cartas (rest$ $?cartas))

(bind $?carta-aestudiar (create$ ?valor-aestudiar ?palo-aestudiar)) ; Comparamos las dos opciones

(bind $?opcion1 (create$ $?carta-decidida $?cartas-mesa)) (bind $?opcion2 (create$ $?carta-aestudiar $?cartas-mesa))

(bind ?eleccion (mejor-opcion-cartas-mesa (implode$ $?opcion1) $?opcion2)) ; Si es mejor la segunda opcion, sera la decidida por ahora

(if (= 2 ?eleccion) then (bind $?carta-decidida $?carta-aestudiar)) )

(10)

(return $?carta-decidida) )

; 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)) ; El criterio seguira el siguiente orden:

(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) )

(if (> 5 ?puntos1 ?puntos2) then

(bind ?*razon* "es mas probable que despues podamos hacer escoba") (return 1)

)

(if (> 5 ?puntos2 ?puntos1) then

(bind ?*razon* "es mas probable que despues podamos hacer escoba") (return 2)

)

(if (and (not ?*num-sietes-decidido*) (< ?sietes1 ?sietes2)) then (bind ?*razon* "dejo menos sietes en la mesa")

(return 1) )

(if (and (not ?*num-sietes-decidido*) (< ?sietes2 ?sietes1)) then (bind ?*razon* "dejo menos sietes en la mesa")

(return 2) )

(if (and (not ?*num-oros-decidido*) (< ?oros1 ?oros2)) then (bind ?*razon* "dejo menos oros en la mesa")

(return 1) )

(if (and (not ?*num-oros-decidido*) (< ?oros2 ?oros1)) then (bind ?*razon* "dejo menos oros en la mesa")

(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.

(11)

(bind ?*razon* "ya hay un 10 en la mesa") (return 1)

)

(if (> (contar-veces 10 $?opcion2) (contar-veces 10 $?opcion1) 0) then (bind ?*razon* "ya hay un 10 en la mesa")

(return 2) )

(if (> (contar-veces 9 $?opcion1) (contar-veces 9 $?opcion2) 0) then (bind ?*razon* "ya hay un 9 en la mesa")

(return 1) )

(if (> (contar-veces 9 $?opcion2) (contar-veces 9 $?opcion1) 0) then (bind ?*razon* "ya hay un 9 en la mesa")

(return 2) )

(if (> (contar-veces 8 $?opcion1) (contar-veces 8 $?opcion2) 0) then (bind ?*razon* "ya hay un 8 en la mesa")

(return 1) )

(if (> (contar-veces 8 $?opcion2) (contar-veces 8 $?opcion1) 0) then (bind ?*razon* "ya hay un 8 en la mesa")

(return 2) )

; 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 )

; Esta funcion auxiliar devuelve la cantidad de veces que aparece un valor ; determinado en un conjunto de cartas

(deffunction contar-veces(?valor $?cartas) (bind ?total 0)

(12)

(while ?indice

(bind ?total (+ 1 ?total))

(bind $?cartas (delete$ $?cartas ?indice ?indice)) (bind ?indice (member$ ?valor $?cartas))

) ?total )

; Esta funcion auxiliar devuelve el conjunto de cartas resultante de eliminar ; unas cartas determinadas de un conjunto dado

; ?cartas-a-quitar-str es una cadena de caracteres que contiene el conjunto de cartas que hay que eliminar

; $?cartas es el conjunto de cartas del que hay que eliminar las indicadas (deffunction quitar-cartas(?cartas-a-quitar-str $?cartas)

(bind $?cartas-a-quitar (explode$ ?cartas-a-quitar-str)) ; Vamos a quitar las cartas una a una

(while (<> 0 (length$ $?cartas-a-quitar))

; En este multicampo se iran añadiendo las que no hay que quitar (bind $?cartas-aux (create$))

(bind ?valor-a-quitar (nth$ 1 $?cartas-a-quitar)) (bind $?cartas-a-quitar (rest$ $?cartas-a-quitar)) (bind ?palo-a-quitar (nth$ 1 $?cartas-a-quitar)) (bind $?cartas-a-quitar (rest$ $?cartas-a-quitar))

; 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 añadimos 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 auxiliar devuelve una combinacion determinada de cartas a partir de un conjunto dado

; Para ello, el numero de combinacion realiza una especie de seleccion binaria de las cartas.

; Por ejemplo, si tenemos 4 cartas y el indice es 9 (= 1001b) se devolveran las cartas primera y ultima.

; ?indice es el numero que determina la combinacion escogida

; $?cartas es el conjunto de cartas de las que hay que seleccionar las indicadas (deffunction combinacion(?indice $?cartas)

; En este multicampo se iran añadiendo las cartas seleccionadas (bind $?escogidas (create$))

(while (<> 0 (length$ $?cartas))

(bind ?valor-a-anadir (nth$ 1 $?cartas)) (bind $?cartas (rest$ $?cartas))

(bind ?palo-a-anadir (nth$ 1 $?cartas)) (bind $?cartas (rest$ $?cartas)) (if (= 1 (mod ?indice 2)) then

(bind $?escogidas (create$ $?escogidas ?valor-a-anadir ?palo-a-anadir)) )

(bind ?indice (div ?indice 2)) ; Division entera (desplazamiento en binario) )

(return $?escogidas) )

; Esta funcion añade a la cuenta de cartas, oros y sietes del PC los obtenidos de una jugada.

(deffunction contar-puntos-PC($?cartas) ; Se añade le numero de cartas conseguidas

(bind ?*monton-PC* (+ ?*monton-PC* (/ (length$ $?cartas) 2))) ; Se añade el numero de oros

(bind ?*oros-PC* (+ ?*oros-PC* (sumar-oros $?cartas))) ; Se añade 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

(13)

(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) )

)

; Esta funcion es analoga a la anterior, ahora en el caso del jugador humano (deffunction contar-puntos-jugador($?cartas)

(bind ?*monton-jugador* (+ ?*monton-jugador* (/ (length$ $?cartas) 2))) (bind ?*oros-jugador* (+ ?*oros-jugador* (sumar-oros $?cartas)))

(bind ?*sietes-jugador* (+ ?*sietes-jugador* (sumar-sietes $?cartas))) (if (str-index "7 oros" (implode$ $?cartas)) then

(bind ?*escobas-jugador* (+ 1 ?*escobas-jugador*)) )

(if (> ?*monton-jugador* 20) then (bind ?*num-cartas-decidido* TRUE) )

(if (> ?*oros-jugador* 5) then (bind ?*num-oros-decidido* TRUE) )

(if (> ?*sietes-jugador* 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 cada vez que el PC echa una carta (defrule eliminar-de-mano (declare (salience 10)) ?regla-eliminar <- (eliminar-de-mano ?valor ?palo)

?regla-mano <- (cartas-de-PC $?previas ?valor ?palo $?posteriores) =>

(retract ?regla-eliminar) (retract ?regla-mano)

(assert (cartas-de-PC $?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)

(14)

(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 añade 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 cuando el PC va a hacer jugada (defrule echar-llevandose

?regla-echar <- (echar-llevandose $?jugada) ; Indica las cartas que se lleva ?regla-turno <- (turno-de PC)

?regla-mesa <- (cartas-en-mesa $?cartas-mesa)

?regla-carta <- (usar-carta $?carta) ; Indica la carta que va a echar ?regla-ganador <- (ultimo-ganador ?)

=>

; Se eliminan los hechos obsoletos (retract ?regla-echar)

(retract ?regla-turno) (retract ?regla-mesa) (retract ?regla-carta) (retract ?regla-ganador)

; 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 indica todo por pantalla

(printout t "Echo el " (implode$ $?carta) " ." crlf) (printout t "Me llevo: " $?jugada crlf)

(printout t "Porque " ?*razon* crlf) (printout t "Cartas sobre la mesa:" crlf)

(bind $?cartas-que-quedan (quitar-cartas (implode$ $?jugada) $?cartas-mesa)) (printout t $?cartas-que-quedan crlf)

; Se actualiza la puntuacion obtenida por el PC (contar-puntos-PC $?carta $?jugada)

; Se indica que el ultimo que se ha llevado cartas es el PC, necesario para saber ; quien se lleva las cartas sobrantes al final de la partida

(assert (ultimo-ganador PC))

; Se eliminan de la mesa las cartas que se ha llevado el PC (assert (cartas-en-mesa $?cartas-que-quedan))

)

; Esta regla se activa cada vez que el turno corresponde al PC. ; El comportamiento se define en la funcion 'jugada'.

(defrule turnoPC (turno-de PC)

(cartas-de-PC ? $?) ;Si le quedan cartas (cartas-de-PC $?cartas)

(cartas-en-mesa $?cartas-mesa) =>

(jugada (implode$ $?cartas) $?cartas-mesa) )

; Esta regla se activa cada vez que el turno corresponde al jugador. (defrule turnojugador (declare (salience -10))

?regla-turno <- (turno-de jugador)

?regla-cartas <- (cartas-en-mesa $?cartas-mesa) ?regla-ganador <- (ultimo-ganador ?)

=>

; Las cartas en la mesa no seran las mismas al terminar la jugada (retract ?regla-cartas)

; Se pide al jugador que indique la carta que echa (printout t "Introduzca la carta que echa: ") (bind $?carta (explode$ (readline)))

; La carta introducida ya no estara en el mazo (assert (eliminar-de-mazo $?carta))

(bind ?*n-cartas-jugador* (- ?*n-cartas-jugador* 1))

; En esta variable booleana se indicara si el texto introducido por el usuario es erroneo

(bind ?respuesta-erronea TRUE) (while ?respuesta-erronea

(15)

; Se pregunta al jugador si con la carta que echa se lleva alguna (printout t "Se lleva alguna carta? (si/no) ")

(bind ?respuesta (read)) (if (eq ?respuesta no) then

; Si no se lleva ninguna carta, simplemente se añade a la mesa (bind ?respuesta-erronea FALSE)

(assert (cartas-en-mesa $?cartas-mesa $?carta)) (printout t "Cartas sobre la mesa:" crlf)

(printout t (create$ $?cartas-mesa $?carta) crlf) )

(if (eq ?respuesta si) then

; Si consigue hacer jugada, se le pregunta que cartas son las que se lleva (bind ?respuesta-erronea FALSE)

(printout t "Introduzca las cartas que se lleva con el " (implode$ $?carta) " :" crlf)

; En este multicampo se iran guardando las cartas que se lleva el jugador (bind $?jugada (create$))

; Aqui se van sumando los puntos de la jugada para comprobar si son 15 (bind ?total (nth$ 1 $?carta))

; Contador del numero de carta (bind ?i 1)

; Esta variable booleana toma el valor TRUE si aun no tenemos 15 puntos (bind ?otra-mas TRUE)

(while ?otra-mas

; Se va preguntando al jugador las cartas que se lleva una por una (printout t ?i "a carta: ")

(bind $?carta-cogida (explode$ (readline))) (bind ?total (+ ?total (nth$ 1 $?carta-cogida))) (if (= 15 ?total) then

; Si ya tiene 15 puntos, no se tiene que llevar ninguna mas (bind ?otra-mas FALSE)

)

(if (< 15 ?total) then

; Si supera los 15 puntos, significa que la jugada no es valida, porque tiene que ser 15 puntos exactos

(printout t "Jugada no valida." crlf)

; Se le vuelve a preguntar al jugador por la jugada correcta

(printout t "Introduzca las cartas que se lleva con el " (implode$ $?carta) " :" crlf)

(bind ?i 0)

(bind ?total (nth$ 1 $?carta)) (bind $?jugada (create$)) )

; Contador++ (bind ?i (+ 1 ?i))

; Se añade la carta indicada a la jugada

(bind $?jugada (create$ $?jugada $?carta-cogida)) )

(if (eq (quitar-cartas (implode$ $?jugada) $?cartas-mesa) (create$)) then

; Si no uedan cartas en la mesa, significa que el jugador ha conseguido una escoba

(printout t "Eso es una ESCOBA!!" crlf)

(bind ?*escobas-jugador* (+ 1 ?*escobas-jugador*)) )

; Se actualiza la puntuacion del jugador (contar-puntos-jugador $?carta $?jugada)

; Se indica que el ultimo en hacer jugada es el jugador humano (retract ?regla-ganador)

(assert (ultimo-ganador jugador))

; Se actualizan las cartas que hay sobre la mesa

(assert (cartas-en-mesa (quitar-cartas (implode$ $?jugada) $?cartas-mesa))) (printout t "Cartas sobre la mesa:" crlf)

(printout t (quitar-cartas (implode$ $?jugada) $?cartas-mesa) crlf) )

)

; El siguiente turno corresponde al PC (retract ?regla-turno)

(assert (turno-de PC)) )

; 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 =>

(16)

(retract ?hecho-PC)

; Pedimos al usuario que introduzca las 3 cartas que coge el PC (printout t "Introduzca las cartas que coge el PC" crlf) (printout t "Primera carta: ")

(bind $?carta1 (explode$ (readline))) (printout t "Segunda carta: ") (bind $?carta2 (explode$ (readline))) (printout t "Tercera carta: ") (bind $?carta3 (explode$ (readline)))

(assert (cartas-de-PC $?carta1 $?carta2 $?carta3)) ; Las cartas indicadas ya no estaran en el mazo (assert (eliminar-de-mazo $?carta1))

(assert (eliminar-de-mazo $?carta2)) (assert (eliminar-de-mazo $?carta3))

; El jugador ahora tendra 3 cartas en la mano (bind ?*n-cartas-jugador* 3)

)

; Esta regla solo se activa cuando ha terminado la partida (defrule final (declare (salience -10))

; Si ya no quedan cartas ni en el mazo ni en ninguna mano, la partida ha terminado (cartas-en-mazo) (cartas-de-PC) (test (= 0 ?*n-cartas-jugador*)) (ultimo-ganador ?ganador) (cartas-en-mesa $?restantes) => (printout t crlf)

; El ultimo en hacer jugada se lleva las cartas restantes en la mesa (if (eq ?ganador PC) then

(printout t "El PC se lleva las cartas que quedan" crlf) (contar-puntos-PC $?restantes)

)

(if (eq ?ganador jugador) then

(printout t "Te llevas las cartas que quedan" crlf) (contar-puntos-jugador $?restantes)

)

; Se hace el recuento final

(bind ?puntuacion-PC ?*escobas-PC*)

(printout t "El PC ha conseguido " ?*escobas-PC* " escobas." crlf) (if (> ?*monton-PC* 20) then

(printout t "El PC ha ganado en numero de cartas, ya que tiene " (integer ?*monton-PC*) "." crlf)

(bind ?puntuacion-PC (+ 1 ?puntuacion-PC)) )

(if (> ?*oros-PC* 5) then

(printout t "El PC ha ganado en numero de oros, ya que tiene " ?*oros-PC* "." crlf) (bind ?puntuacion-PC (+ 1 ?puntuacion-PC))

)

(if (> ?*sietes-PC* 2) then

(printout t "El PC ha ganado en numero de sietes, ya que tiene " ?*sietes-PC* "." crlf)

(bind ?puntuacion-PC (+ 1 ?puntuacion-PC)) )

(printout t "En total, el PC ha conseguido " ?puntuacion-PC " puntos." crlf) (bind ?puntuacion-jugador ?*escobas-jugador*)

(printout t "Has conseguido " ?*escobas-jugador* " escobas." crlf) (if (> ?*monton-jugador* 20) then

(printout t "Has ganado en numero de cartas, ya que tienes " (integer ?*monton-jugador*) "." crlf)

(bind ?puntuacion-jugador (+ 1 ?puntuacion-jugador)) )

(if (> ?*oros-jugador* 5) then

(printout t "Has ganado en numero de oros, ya que tienes " ?*oros-jugador* "." crlf) (bind ?puntuacion-jugador (+ 1 ?puntuacion-jugador))

)

(if (> ?*sietes-jugador* 2) then

(printout t "Has ganado en numero de sietes, ya que tienes " ?*sietes-jugador* "." crlf)

(bind ?puntuacion-jugador (+ 1 ?puntuacion-jugador)) )

(17)

; Se indica quien ha ganado, o en su caso, si la partida ha terminado en empate (if (> ?puntuacion-PC ?puntuacion-jugador) then

(printout t "EL PC HA GANADO LA PARTIDA" crlf) )

(if (< ?puntuacion-PC ?puntuacion-jugador) then (printout t "HAS GANADO LA PARTIDA" crlf) )

(if (= ?puntuacion-PC ?puntuacion-jugador) then (printout t "LA PARTIDA HA ACABADO EN EMPATE" crlf) )

; Se pregunta si el usuario quiere jugar otra partida (printout t "Deseas jugar otra vez? (si/no)" crlf) (bind ?seguir (read))

(if (eq si ?seguir) then

; Si el usuario quiere jugar otra vez, comenzamos una nueva partida (reset)

(inicializacion) else

; En caso contrario, salimos de la aplicacion (exit) ) ) ;;;;;;;;;;;; ;; 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)

; Tanto el PC como el jugador carecen de cartas al principio (cartas-de-PC)

; "?*n-cartas-jugador*=0" en defglobal

; En este hecho se guarda el ultimo que se ha llevado cartas, ; para saber quien se lleva las cartas que sobran al final.

(18)

(ultimo-ganador PC) ) ;;;;;;;;;;;;;;;;;;;;;;;; ;; 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)

Referencias

Documento similar

You may wish to take a note of your Organisation ID, which, in addition to the organisation name, can be used to search for an organisation you will need to affiliate with when you

Where possible, the EU IG and more specifically the data fields and associated business rules present in Chapter 2 –Data elements for the electronic submission of information

The 'On-boarding of users to Substance, Product, Organisation and Referentials (SPOR) data services' document must be considered the reference guidance, as this document includes the

In medicinal products containing more than one manufactured item (e.g., contraceptive having different strengths and fixed dose combination as part of the same medicinal

Products Management Services (PMS) - Implementation of International Organization for Standardization (ISO) standards for the identification of medicinal products (IDMP) in

Products Management Services (PMS) - Implementation of International Organization for Standardization (ISO) standards for the identification of medicinal products (IDMP) in

This section provides guidance with examples on encoding medicinal product packaging information, together with the relationship between Pack Size, Package Item (container)

Package Item (Container) Type : Vial (100000073563) Quantity Operator: equal to (100000000049) Package Item (Container) Quantity : 1 Material : Glass type I (200000003204)