Sei sulla pagina 1di 11

Ingenierı́a Técnica en

Informática de Gestión
Inteligencia Artificial
Febrero 2007. 1a parte
Departamento de Informática
Universidad Carlos III de Madrid

Normas generales del examen


• El tiempo para realizar el examen es de 1,5 horas
• No se responderá a ninguna pregunta sobre el examen
• Si se sale del aula, no se podrá volver a entrar durante el examen
• No se puede presentar el examen escrito a lápiz

Problema 1. (5 puntos)
Una fábrica dispone de una cadena automatizada para el pintado de un determinado tipo de objetos. Cada
objeto debe pintarse de un único color, de entre varios disponibles, mediante la aplicación de una pistola de pintado
que tiene espacio para un único cartucho de pintura. Mientras que el tiempo de pintado es constante (y, de hecho,
puede considerarse despreciable), los cartuchos deben instalarse y desinstalarse de la pistola según un procedimiento,
también automático, que invierte una cantidad de tiempo conocida y que depende del color del cartucho anterior
y del nuevo a instalar y que se especifica, por ejemplo, con una tabla como la que se muestra a continuación:

Color anterior Color siguiente Tiempo (min.)


vacı́o azul 6
azul azul 11
azul rojo 3
azul verde 9
vacı́o rojo 11
rojo azul 12
rojo rojo 4
rojo verde 10
... ... ...

Figura 1: Tiempos de recambio entre cartuchos de diferente color

donde “vacı́o” representa el caso en el que previamente no hay instalado ningún cartucho.
La fábrica desea optimizar, a diario, el tiempo que se tarda en pintar cualquier cantidad de objetos, para cada
uno de los cuales se conoce de antemano el color deseado, empezando el turno de trabajo con una pistola de pintado
vacı́a.
Se pide:
(1 punto) a) Modelizar el dominio descrito con el uso de marcos.
(1 punto) b) Definir los operadores del dominio.
(1,5 puntos) c) Definir una función heurı́stica h(n) que sea admisible e informada, explicando claramente su
construcción.
(0,5 puntos) d) ¿Qué algoritmo de búsqueda elegirı́as para garantizar que las soluciones devueltas serán
óptimas con el uso de la función heurı́stica elegida en el apartado anterior? Razona tu respuesta.
(1 punto) e) Imagı́nese ahora que la fábrica establece como restricción adicional que un mismo cartucho no
puede pintar más de 100 objetos del mismo color, de modo que después de este número de pintados con el mismo
cartucho el reemplazo es obligatorio. ¿Afecta esta restricción a la función heurı́stica obtenida en el apartado c)? Si
es ası́, ¿qué nueva función heurı́stica propondrı́as? Razona tu respuesta
Problema 2. (5 puntos)
El juego del Rummy es un juego de mesa en el que pueden jugar entre 2 y 4 jugadores. Contiene 104 fichas
definidas por un número (del 1 al 13) y un color (rojo, amarillo, azul o negro), 2 comodines y 4 atriles. Las fichas
se corresponden a dos mazos de cartas con dos comodines. Los palos tradicionales se reemplazan por cuatro colores
diferentes, cada uno numerado del 1 al 13. Los números 1, 11, 12, 13 reemplazan al as, al jack, a la reina y al rey
respectivamente.
El objetivo principal del juego es quedarse sin fichas, el jugador que antes lo consiga gana la partida. El resto
de jugadores tendrán una penalización igual a la suma de los valores de las fichas que le queden en el atril. El valor
de cada ficha es su valor numérico (de 1 a 13) y el del comodı́n el valor de la ficha que reemplaza. Si el comodı́n
queda en el atril de un jugador cuando finaliza la partida vale 50 puntos en contra.
Para empezar el juego se mezclan las fichas sobre la mesa con la cara hacia abajo y cada jugador toma 14 fichas
aleatorias que coloca en su atril sin que el resto de jugadores las vean en ningún momento.
Los jugadores intentan acomodar sus fichas en juegos. Hay dos tipos de juegos:

Escalera: tres o más fichas de un mismo color en secuencia numérica. Por ejemplo, 3-4-5 rojo. Un 1 puede
usarse en secuencia sólo con un 2 y nunca con un 13.

Pierna: tres o cuatro fichas del mismo valor numérico, pero de diferentes colores. Por ejemplo, 4 rojo, 4
amarillo y 4 azul.

Un comodı́n puede usarse en lugar de cualquier ficha en una combinación.


El jugador que es mano, si está en condiciones de exponer juegos, lo hará teniendo en cuenta que la primera
vez no debe ser menor de 30 puntos (la suma del valor de las fichas). A partir de allı́, en su turno, puede seguir
exponiendo sin puntaje mı́nimo. Si no puede jugar debe tomar una ficha del mazo y colocarla en su atril. A
continuación, sigue de la misma manera el jugador de la izquierda y ası́ sucesivamente todos los jugadores. Una
vez hecha la primera exposición de su juego, el jugador está habilitado en su turno para agregar una o más fichas
a una escalera o completar una pierna que haya presentado otro jugador, además puede cambiar una ficha de un
juego por otra que no le haga perder su condición de pierna o escalera, y esta ficha usarla para abrir un nuevo
juego. Por ejemplo, si en el atril hay un 10 amarillo, un 7 azul y un 7 negro, y sobre la mesa una escalera amarilla
compuesta por 7-8-9. El 10 amarillo se agrega a la escalera y se retira el 7 para formar una pierna de 7 azul, 7
negro y 7 amarillo.
Si hay un comodı́n formando parte de un juego puede ser canjeado por la ficha que corresponda y utilizar el
comodı́n para un nuevo juego o agregarlo a otro, no pudiendo ser retirado para ser utilizado con posterioridad, ni
tampoco puede ser canjeado en una pierna de 3 fichas.
Se pide:

(2 puntos) a) Formalizar en lenguaje de marcos las clases necesarias para representar el problema y poder
simular partidas del juego utilizando un sistema de producción.

(2 puntos) b) Escribir el subconjunto de reglas del sistema de producción, con su prioridad y en sintaxis
CLIPS, para implementar las acciones del juego que se detallan a continuación. Se puede suponer que en
vez de marcos se han definido plantillas:

- Repartir las 14 fichas iniciales a cada jugador.


- Comprobar si uno de los jugadores ha ganado la partida.
- Calcular la puntuación de las fichas que restan en el atril a cada jugador cuando uno termina la partida.
No hace falta tener en cuenta los comodines.
- Crear una escalera suponiendo que es la primera jugada (suma más de 30 puntos). No hace falta tener
en cuenta los comodines.
- Añadir una ficha en una escalera que ya exista. No hace falta tener en cuenta los comodines.
- Cambiar de turno comprobando previamente si el jugador actual tiene que robar ficha.

¿Qué estrategia de control es la más adecuada?


(1 punto) c) Si en vez de un sistema de producción se quiere utilizar métodos y demonios, definir el marco
necesario para llevar el control del juego (reparto de fichas, permitir que cada jugador juegue en su turno,
detección del fin de partida y suma de puntos de las fichas restantes al finalizar la partida) y definir el
pseudocódigo del método necesario para simular una partida. No es necesario definir los posibles métodos
de ese mismo marco o de otros marcos a los que dicho método pueda invocar. Simplemente indicar el nombre
del método y del marco al que pertenece y comentar su funcionalidad.
Soluciones del examen de Inteligencia Artificial.
Febrero 2007

Solución al problema 1
a) Los elementos fundamentales para la representación de cada estado, tal y como se pedı́a en el primer apartado,
son únicamente: la pistola, indistinguible del cartucho mismo puesto que siempre se asumirá que está cargada
con alguno; cada objeto a pintar, naturalmente, sin los que no habrı́a problema y, por último, la tabla de
coste para cambiar un cartucho por otro.
Ası́, por lo tanto, la representación con marcos que se sugiere es:

Pistola
es-un:
Atributo Posibles valores/Valor
color sı́mbolo/’Vacı́o’
número entero/0
capacidad capacidad/0

Nótese, en primer lugar, que el Vacı́o es considerado como cualquier otro color. No hay de hecho ningún
motivo para no hacerlo ası́ y la misma consideración se extenderá a la definición de operadores en el siguiente
apartado. Además, la pistola mantiene el número de pintados que aún quedan disponibles en su cartucho
que, por defecto, serán 0 puesto que ése es el número de pintados que se hacen en Vacı́o. Por último, se
entiende que cada pistola tiene una capacidad máxima de pintados.
Para la definición de cada objeto:

Objeto
es-un:
Atributo Posibles valores/Valor
color sı́mbolo
número entero

se ha tenido en cuenta una simplificación notable que reduce significativamente el espacio de estados: en vez
de representar cada objeto separadamente, éstos se agrupan por su color, puesto que en la solución óptima no
tiene sentido dejar cartuchos a medias con más objetos de pintar del mismo color. Por lo tanto, cada estado
mantiene sobre sus objetos, el número de ellos que deben pintarse de cada color.
El último marco es trivial, y debe servir únicamente para registrar el coste de cambiar de un cartucho de un
color a otro, de modo que la siguiente definición es auto-explicativa:

Coste
es-un:
Atributo Posibles valores/Valor
color1 sı́mbolo
color2 sı́mbolo
t entero

donde t registra el tiempo que se tardará en reemplazar un cartucho de color1 por otro de color2 .

b) A pesar de que en el enunciado se habla de las acciones de “reemplazar ” el cartucho y de “pintar ” objetos, lo
cierto es que ¡cuando se reemplaza un cartucho es precisamente para pintar objetos de ese color! de modo
que resulta obvio que hay un único operador, reemplazar-y-pintar, en el que la única condición notable es
que haya objetos para pintar del color del cartucho que se va a instalar:
reemplazar-y-pintar:

SI ∃p ∈ Pistola ∧
∃o ∈ Objeto, o.numero > 0 ∧
∃c ∈ Coste, c.color1 = p.color
c.color2 = o.color
ENTONCES p.color = o.color
cantidad de objetos efectivamente pintados
z }| {
p.numero = p.capacidad − (o.numero − máx(0, o.numero − p.capacidad))
o.numero = máx(0, o.numero − p.capacidad)
k = c.t
y esta condición se verifica simplemente observando que la instancia elegida de objeto tiene un valor de
número estrictamente positivo. Como quiera, además, que los operadores se distinguen por su coste (esto
es, el tiempo que se tarda en pasar de un color a otro), se ha añadido un elemento de condición adicional
únicamente para acceder al marco instancia de coste que almacena el tiempo para pasar del color actual de
la pistola (c.color1 = p.color) al del objeto, c.color2 = o.color. La misma instancia, c, se accede después
en el consecuente del operador para indicar en k el coste de aplicación del operador en cada caso.
El resto del consecuente es más difı́cil de explicar que de entender. En realidad, el pequeño movimiento de
restas que hay es para tener en cuenta simultáneamente los dos únicos casos que pueden darse: que se vaya a
pintar una cantidad de objetos (o.numero) menor que la de la capacidad del cartucho instalado en la pistola
(p.capacidad) o, por el contrario, que se vaya a agotar el cartucho completamente pintando objetos de este
color. Por lo tanto, después de actualizar el color de la pistola en la primera clausula del consecuente del
operador (p.color = o.color), se calcula la cantidad de objetos que aún podrán pintarse con el cartucho
recién instalado simplemente substrayendo la cantidad de objetos efectivamente pintados, resaltada en la
formulación del operador. A continuación, sólo resta actualizar el número de objetos del color actual que
quedan por pintarse.
c) Por supuesto, se seguirá la técnica de relajación de restricciones para obtener funciones heurı́sticas admisibles
e informadas que sirvan para resolver este problema óptimamente. En este caso, las restricciones que podrı́an
violarse son más que las que se observan en el operador del apartado anterior. A continuación se discuten
algunas posibilidades:

Obviamente, relajar el problema tanto que se considere que todos los objetos son del mismo color y, al
mismo tiempo, que la pistola tiene capacidad infinita, resultará en una función heurı́stica admisible:

h1 = mı́n {t(p.color, o.color)}


∀o∈objeto

es decir, el tiempo de cambiar del color actual (que no tiene por qué ser el vacı́o, la función heurı́stica h(· )
se aplica en cualquier nodo), al color que se asume que tendrán ahora todos los objetos. Esta función, sin
embargo, es tan admisible como inútil: no es informada en absoluto porque siempre devolverá el mismo
valor.
Aunque podrı́a intentar mejorarse un poco la heurı́stica anterior no relajando la condición de que la
pistola tenga capacidad infinita:
P 
o.numero
h2 = mı́n {t(p.color, o.color)} ×
∀o∈objeto p.capacidad
lo cierto es que la función heurı́stica, si bien está más informada que la anterior, es igualmente inútil,
puesto que el factor por el que se ha afectado a h2 es el mismo independientemente de la cantidad de
colores pendientes de aplicarse.
Con el propósito de tener en cuenta simultáneamente todos los colores que aún están pendientes de
ser aplicados, podrı́a relajarse la condición de que para tener un cartucho de un determinado color2
con un determinado coste t, es necesario tener el color1 que se especifique para él. Por lo tanto, en
este nuevo problema relajado es posible ahora transitar a cualquier color con un coste determinado
independientemente del color original y, por lo tanto, el coste óptimo puede calcularse como:
 
X o.numero
h3 = mı́n{t(c, o.color)} ×
∀c p.capacidad
∀o∈objeto
donde se han tenido en cuenta los cambios de cartucho que tengan que hacerse para pintar todos los
objetos de cada color.
Pero aún hay otras relajaciones que pueden proponerse. Por ejemplo, ¿y si se supone ahora que la pistola
tiene capacidad para almacenar una cantidad arbitraria k 0 de cartuchos?
Dado un nodo cualquiera n del espacio de estados retratado con los marcos clase del primer apartado,
considérese que la pistola tiene ahora mismo un color cualquiera instalado, p.color y que aún hay objetos
de hasta k colores diferentes pendientes de ser pintados. Obviamente, k 0 ≤ k pero pueden distinguirse
varios casos, todos ellos, que darán diferentes y mejor informadas funciones heurı́sticas:
k 0 = k → En este caso, el coste para reemplazar el color actual, p.color por todos los k restantes
(puesto que se asume que k 0 = k) es:

h4 = máx{t(p.color, c)}
∀c

donde c son cada uno de los k colores restantes


k 0 = (k − 1) → Ahora será preciso operar en dos fases: en la primera se sustituirá el cartucho actual por
(k − 1) de modo que en la segunda fase se cambiará alguno de esos (k − 1) colores por el último. El
cálculo de la solución óptima, por lo tanto, consistirá en el cálculo de qué color debe dejarse para el
último de modo que el coste de los primeros (k − 1) recambios (denotados genéricamente como i en
la expresión que sigue) se calculará como en h4 y el último (algún j distinto de todos los i) como
en h1 ó h2 , esto es, de un color a otro determinados:

h5 = mı́n {máx{t(p.color, i)} + mı́n{t(i, j)}}


i6=j

y de todas las combinaciones posibles, se toma el mı́nimo para garantizar que se está calculando la
solución óptima del problema relajado.
k 0 = (k − 2) → Como antes, pero ahora dejaremos dos colores j1 y j2 para el final, de modo que el coste
óptimo es, simplemente:

h6 = mı́n {máx{t(p.color, i)} + mı́n{t(i, j1 )} + mı́n{t(i, j2 )}}


i6=j1 ,j2

k 0 = (k − r) → Generalizando la misma idea, imagı́nese ahora que la pistola tiene capacidad para tener
simultáneamente hasta (k − r) colores, de modo que cada uno de los r restantes se irán colocando
a continuación:
( r
)
X
h7 = mı́n máx{t(p.color, i)} + mı́n{t(i, js )}
i6=j1 ,j2 ,...,jr
s=1

Deberı́a resultar claro, sin embargo, que cada una de estas expresiones (h4 , h5 , . . . ) tienen una complejidad
de cálculo cada vez mayor de modo que con frecuencia no será posible aplicar h6 para valores de r muy
altos. Debe recordarse en este punto, que las funciones heurı́sticas, para ser útiles, deben computarse en
un tiempo razonable.
Y aún hay una última relajación que no se ha considerado, ¿y si ahora hubiera una cantidad arbitraria
de pistolas de pintado? En este caso, cada una pintarı́a unos cuantos objetos de diferentes colores. El
coste óptimo, en vez de calcularse como una expresión matemática resultarı́a de la aplicación de un
algoritmo muy sencillo: comenzando con el color de la pistola, p.color del nodo actual, n, se toma el
cambio más barato, t(p.color, c1 ), hasta algún color c1 y ası́, nuevamente desde él hasta el siguiente c2
y ası́ sucesivamente hasta que, o bien se han recorrido los k colores restantes por aplicarse, o algún ci
era igual a algún color anterior, cj , j < i. En el primer caso, el valor de la función heurı́stica, h7 , serı́a
la suma de todos los cambios calculados de esta manera; en el segundo caso, se vuelve a empezar desde
cualquier otro color (emulando ası́ la disponibilidad de cualquier otra pistola) y se procede de nuevo
hasta que, o bien se han aplicado ya todos los colores, o bien se vuelve a visitar un color anterior, en cuyo
caso se inicia nuevamente desde cualquier otro color, imaginando ası́ la disponibilidad de una tercera
pistola y ası́ sucesivamente.
En el caso general, la función heurı́stica sugerida en este apartado se calcula como la suma, para todas
las pistolas empleadas, de los costes de los cambios calculados con este algoritmo.
d) Puesto que se requiere, explı́citamente, el cálculo de soluciones óptimas con el uso de funciones heurı́sticas, la
selección se restringe, únicamente, a cualquiera de los algoritmos admisibles de búsqueda heurı́stica vistos en
clase, cada uno con sus ventajas e inconvenientes:

A∗ : es un algoritmo completo (encuentra al menos una solución si ésta existe) y admisible (devuelve la
solución óptima) si está guiado por una función heurı́stica admisible —como las funciones h mostradas
en el apartado anterior. Ahora bien, el algoritmo de búsqueda A∗ emplea una lista denominada abierta
donde almacena todos los nodos pendientes de expansión ordenados por su valor f (n) = g(n) + h(n).
Después de cada expansión, esta lista crece y, de hecho, se puede demostrar que el crecimiento de esta
lista ocurre exponencialmente por lo que el algoritmo A∗ puede consumir la memoria del ordenador en
unos pocos minutos para problemas con una dificultad media o incluso pequeña–media.
IDA∗ : en los casos en los que el algoritmo A∗ consume la memoria del ordenador sin llegar a devolver una
solución, el algoritmo IDA∗ es una buena elección, puesto que en vez de mantener una lista ABIERTA,
procede en profundidad (y, por lo tanto, con un consumo de memoria lineal) con umbrales iguales al
mı́nimo exceso cometido en la iteración anterior. Evidentemente, el algoritmo IDA∗ re-expande varias
veces los mismos nodos (especialmente la raı́z y los que se encuentran en los primeros niveles del árbol
de búsqueda), pero garantiza que encontrará una solución óptima, si alguna existe. En otras palabras,
este algoritmo también es completo y admisible.

e) En el primer apartado, ya se habı́a tenido en cuenta el hecho, natural, de que cada cartucho instalado en la
pistola de pintado tiene una capacidad limitada, p.capacidad. Este valor sólo aparece en las heurı́sticas h2 y
h3 en las que ahora basta sustituir p.capacidad por 100. Nótese que, en el resto de las funciones heurı́sticas
sugeridas, no se ha considerado este parámetro.
Solución al problema 2
a) Se piden los marcos necesario para resolver el problema utilizando un sistema de producción; es decir, se tiene
una base de hechos donde están almacenadas todas las instancias y para representar las posibles relaciones
entre clases bastará utilizar identificadores. Se tendrá una clase JUGADOR para representar los posibles
jugadores, con un atributo identificador que será un no del 1 al 4. Habrá tantas instancias de esta clase
como jugadores en la partida, cada uno con un identificador diferente. Luego, habrá otra clase FICHA para
representar las fichas del juego que tendrá un atributo posicion para indicar dónde está esa ficha, si la posee
alguno de los jugadores (no del 1 al 4) o está en el mazo (no 0). Habrá 106 instancias de esta clase, una por
cada ficha diferente, además será necesario utilizar un identificador al haber fichas duplicadas. Por último, se
necesita otra clase PARTIDA para llevar el control del juego con un atributo turno que indique el jugador
que le toque colocar fichas, es decir, será no del 1 al 4. Habrá una única instancia de esta clase en cada
partida. Además, se necesita otra clase JUEGO que tendrá dos subclases, PIERNA y ESCALERA, para
representar los diferentes juegos que los jugadores realicen. Al haber fichas repetidas es necesario un atributo
identificador para diferenciar posibles juegos iguales. Inicialmente no hay instancias de estas clases y se irán
creando a medida que los jugadores las vayan realizando en su turno de juego. El resto de atributos necesarios
para cada clase se especifican a continuación:

(defclass JUGADOR (is-a INITIAL-OBJECT)


(slot id ;;igual que slot posicion de FICHA y slot turno de PARTIDA
(type INTEGER)
(range 1 4)
(create-accessor read-write))
(slot num_ini ;;solo para repartir, no de fichas del jugador
(type INTEGER)
(create-accessor read-write)
(default 0))
(slot puntuacion ;;para calcular suma de puntos al finalizar una partida
(type INTEGER)
(create-accessor read-write)
(default 0))
(slot empezo ;;para saber si ha puesto el primer juego
(type SYMBOL)
(allowed-values si no)
(default no))
)

(defclass FICHA (is-a INITIAL-OBJECT)


(slot id ;;necesario porque hay fichas repetidas
(type SYMBOL)
(create-accessor read-write))
(slot valor
(type INTEGER) ;;50 para el comodin
(create-accessor read-write))
(slot color
(type SYMBOL)
(allowed-values rojo amarillo azul negro comodin)
(create-accessor read-write))
(slot posicion ;;igual que slot id de JUGADOR
(type INTEGER)
(range 0 4) ;;0 en el mazo, 1-4 id jugadores
(create-accessor read-write)
(default 0))
)

(defclass PARTIDA (is-a INITIAL-OBJECT)


(slot num_jug;;no de jugadores en la partida
(type INTEGER)
(range 1 4)
(create-accessor read-write))
(slot turno ;;igual que slot id de JUGADOR
(type INTEGER)
(range 1 4)
(create-accessor read-write)
(default 1))
(slot estado ;;control de las distintas fases de una partida
(type SYMBOL)
(create-accessor read-write)
(allowed-values repartir jugar fin)
(default repartir))
(slot jugo ;;para saber si ha puesto ficha el jugador en turno
(type SYMBOL)
(allowed-values si no)
(default no))
(slot num_id ;;para ir a~
nadiendo diferentes id a los juegos
(type INTEGER)
(create-accessor read-write)
(default 1))
)

(defclass JUEGO (is-a INITIAL-OBJECT)


(slot id ;;igual que slot num_id de PARTIDA, necesario por haber fichas repetidas
(type INTEGER)
(create-accessor read-write))
(slot valor;;valor de la ficha menor que compone el JUEGO
(type INTEGER)
(range 1 13)
(create-accessor read-write))
)

(defclass ESCALERA (is-a JUEGO)


(slot valorf;;valor de la ficha mayor que compone la ESCALERA
(type INTEGER)
(range 1 13)
(create-accessor read-write))
(slot color ;;igual slot color de FICHA,
(type SYMBOL)
(allowed-values rojo amarillo azul negro)
(create-accessor read-write))
(slot comodin;;valor ocupado por el comodı́n si lo hubiese
(type INTEGER)
(range 0 13)
(create-accessor read-write)
(default 0) ;;no hay comodı́n
)

(defclass PIERNA (is-a JUEGO)


(multislot colores ;;igual slot color de FICHA, colores ya utilizados en la PIERNA
(type SYMBOL)
(allowed-values rojo amarillo azul negro comodin)
(create-accessor read-write))
)

b) Las reglas pedidas son:

;;;;;REGLAS PARA REPARTIR FICHAS****


;;; La estrategia tiene que ser random para que
;;; cada jugador robe fichas al azar

(defrule reparte
(object (is-a PARTIDA) (estado repartir))
?fic <- (object (is-a FICHA) (posicion 0))
?jug <- (object (is-a JUGADOR) (id ?i) (num_ini ?n&:(< ?n 14)))
=>
(modify-instance ?jug (num_ini (+ 1 ?n)))
(modify-instance ?fic (posicion ?i))
)

;; Regla para verificar que todos los jugadores tienen las 14 fichas iniciales
;; y se puede empezar a jugar. Cambia el estado de la partida a jugar
(defrule fin-reparte
?par <- (object (is-a PARTIDA) (estado repartir))
(not (object (is-a JUGADOR) (num_ini ?n&~14)))
=>
(modify-instance ?par (estado jugar))
)

;;;;;******REGLAS PARA COMPROBAR SI HAY UN GANADOR*****


;;Tienen mas prioridad que las de jugar y cambiar de turno

;;No se utiliza el slot num_ini de JUGADOR porque implica estar actualizándolo


;;siempre que el jugador a~ nade o roba una ficha. Cambia el estado de la partida a fin
(defrule ganar
(declare (salience 300))
?par <- ( object (is-a PARTIDA) (estado jugar))
(object (is-a JUGADOR) (id ?id))
(not ( object (is-a FICHA) (posicion ?id)))
=>
(printout t crlf "Ha ganado jugador " ?id crlf)
(modify-instance ?par (estado fin))
)

;;******REGLAS PARA PUNTUAR CUANDO GANA UN JUGADOR

;;Cada vez que se suma el valor de una ficha se quita la instancia


(defrule puntuar
(object (is-a PARTIDA) (estado fin))
?fic <- ( object (is-a FICHA) (posicion ?j) (valor ?v))
?jug <- ( object (is-a JUGADOR) (id ?j) (puntuacion ?p))
=>
(modify-instance ?jug (puntuacion (+ ?p ?v)))
(unmake-instance ?fic)
)

;;;******REGLAS PARA JUGAR


;;Tienen menos prioridad que las de ganar y más que las de cambiar turno

;;Comprueba que el estado de la partida sea jugar y que el jugador


;;en turno tenga tres fichas consecutivas del mismo color y cuya
;;suma total sea mayor de 30. Genera una nueva instancia de ESCALERA con esos valores
;;y el identificador indicado por atributo num_id de PARTIDA, que se debe incrementar
;;para el siguiente juego. Actualiza el slot empezo de JUGADOR. Las intancias de las
;;fichas utilizadas se borran de la base de hechos.
(defrule juega-salir-escalera
(declare (salience 200))
?par <- ( object (is-a PARTIDA) (estado jugar) (turno ?t) (num_id ?j))
?jug <- ( object (is-a JUGADOR) (id ?t))
?fi1 <- ( object (is-a FICHA) (posicion ?t) (valor ?v1) (color ?c))
?fi2 <- ( object (is-a FICHA) (posicion ?t) (valor ?v2) (color ?c))
?fi3 <- ( object (is-a FICHA) (posicion ?t) (valor ?v3) (color ?c))
(test (= ?v2 (+ 1 ?v1)))
(test (= ?v3 (+ 1 ?v2)))
(test (>= (+ ?v1 ?v2 ?v3) 30))
=>
(modify-instance ?par (jugo si) (num_id (+ 1 ?j)))
(modify-instance ?jug (empezo si))
(make-instance of ESCALERA (id ?j) (valor ?v1) (valorf ?v3) (color ?c))
(unmake-instance ?fi1 ?fi2 ?fi3)
)

(defrule juega-poner-escalera-arriba
(declare (salience 200))
?par <- ( object (is-a PARTIDA) (estado jugar) (turno ?t))
( object (is-a JUGADOR) (id ?t) (empezo si))
?fi1 <- ( object (is-a FICHA) (posicion ?t) (valor ?v1) (color ?c))
?esc <- ( object (is-a ESCALERA) (valor ?vi) (valorf ?vf) (color ?c))
(test (= ?v1 (+ 1 ?vf)))
=>
(modify-instance ?par (jugo si))
(unmake-instance ?fi1)
(modify-instance ?esc (valorf ?v1))
)

(defrule juega-poner-escalera-abajo
(declare (salience 200))
?par <- ( object (is-a PARTIDA) (estado jugar) (turno ?t))
(object (is-a JUGADOR) (id ?t) (empezo si))
?fi1 <- ( object (is-a FICHA) (posicion ?t) (valor ?v1) (color ?c))
?esc <- ( object (is-a ESCALERA) (valor ?vi) (valorf ?vf) (color ?c))
(test (= ?v1 (- ?vi 1)))
=>
(modify-instance ?par (jugo si))
(unmake-instance ?fi1)
(modify-instance ?esc (valor ?v1))
)

;;;******REGLAS PARA CAMBIAR DE TURNO


;; Tienen que tener menos prioridad que las de jugar
;;; La estrategia de control tiene que ser random para que
;;; cada jugador robe fichas al azar

(defrule cambia-turno ;;sin robar


(declare (salience 100))
?par <- ( object (is-a PARTIDA) (estado jugar) (turno ?t) (jugo si) (num_jug ?n))
=>
(modify-instance ?par (turno (+ 1 (mod ?t ?n))) (jugo no))
)

(defrule cambia-turno-roba ;;tiene que robar


(declare (salience 100))
?par <- ( object (is-a PARTIDA) (estado jugar) (turno ?t) (jugo no) (num_jug ?n))
?fic <- ( object (is-a FICHA) (posicion 0))
=>
(modify-instance ?par (turno (+ 1 (mod ?t ?n))))
(modify-instance ?fic (posicion ?t))
)

c) En caso de utilizar métodos y demonios se necesita un marco desde el que acceder al resto de instancias que
intervienen en la partida para poder invocar los métodos correspondientes. Un posible marco para llevar el
control del juego será:
PARTIDA
es-un:
Atributo Posibles valores/Valor Descripción
num-jug entero {2..4} no de jugadores en la partida
jugadores #lista de instancias de JUGADOR
fichas #lista de instancias de FICHA fichas en el mazo
juegos #lista de instancias de JUEGOS inicialmente NULL

El pseudocódigo del método de PARTIDA para simular partidas es:

simula-partida ()
marco: PARTIDA

;;Reparto inicial de fichas entre los jugadores


for(i=0; i<self.num-jug; i++)
self.fichas = send(self.jugadores[i], reparte, self.fichas)

;;Bucle principal de juego hasta que haya un ganador


jugar = TRUE, i = 0
WHILE jugar DO
self.juegos = send(self.jugadores[i], juega, self.juegos)
IF (send(self.jugadores[i],gana))
THEN jugar = FALSE
ELSE i=mod(i+1, self.num-jug) ;;cambia turno
IF (NOT(send(self.jugadores[i], puso-ficha)))
THEN self.fichas = send(self.jugadores[i], roba, self.fichas)
ELSE send(self.jugadores[i], cambia-jugo, FALSE)

;;Suma final de puntos cuando hay un ganador


for(i=0; i<self.num-jug; i++)
send(self.jugadores[i], suma-puntos)

Los métodos invocados por simula-partida son todos del marco JUGADOR. Se describen en la siguiente
tabla:

Método Entrada Salida Descripción


reparte #lista de FICHAS #lista de FICHAS reparte 14 fichas aleatorias al jugador y
devuelve las fichas restantes
juega #lista de JUEGOS #lista de JUEGOS crea/modifica juegos con sus fichas
gana TRUE/FALSE comprueba si el jugador se ha quedado sin fichas
puso-ficha TRUE/FALSE comprueba si el jugador colocó al menos una ficha
roba #lista de FICHAS #lista de FICHAS roba una ficha y devuelve las fichas restantes
cambia-jugo TRUE/FALSE cambia el valor del atributo jugo
suma-puntos suma los puntos de las fichas que posee

Potrebbero piacerti anche