Agente

Anuncio
Ingeniería de los
Sistemas Basados en
el Conocimiento
Simulación y Agentes
con Reglas
LAURA BEGUÉ GRANGED
GERMÁN LATORRE ANTÍN
5º Ingeniería informática
Curso 2001-2002
Centro Politécnico Superior
Universidad de Zaragoza
Índice
Índice
INTRODUCCIÓN
5
DESCRIPCIÓN DE LA APLICACIÓN ................................................................. 5
NUESTRA TAREA ........................................................................................... 5
JESS
6
INTRODUCCIÓN ............................................................................................. 6
OBTENCIÓN DE JESS ..................................................................................... 6
ARCHIVOS FUENTE ............................................................................................................ 7
INSTALACIÓN Y UTILIZACIÓN ............................................................................................ 7
DOCUMENTACIÓN .............................................................................................................. 7
PROGRAMACIÓN BÁSICA EN JESS ................................................................. 8
DESDE JAVA ...................................................................................................................... 8
PECULIARIDADES ............................................................................................................... 8
PROBLEMAS ....................................................................................................................... 9
MANEJO DE OBJETOS JAVA ........................................................................ 10
JAVABEANS ..................................................................................................................... 10
DECLARACIÓN DE CLASES EN JESS .................................................................................. 11
INSTANCIACIÓN DE LOS OBJETOS DESDE JAVA ................................................................. 11
MANEJO DE LOS OBJETOS DESDE JESS .............................................................................. 12
INVOCACIÓN A MÉTODOS DE LOS OBJETOS DESDE JESS .................................................... 12
APLICACIÓN INICIAL
14
CLASES RELEVANTES .................................................................................. 14
SIMULADOR ................................................................................................. 14
MUNDO ........................................................................................................ 14
AGENTE ....................................................................................................... 15
COCHE, PERSONA, SEMÁFORO, TESORO ................................................... 15
ACCIÓN........................................................................................................ 16
POSICIÓN ..................................................................................................... 16
CARRETERAAUTOCARGA ........................................................................... 16
RESUELVERUTASJESS ............................................................................... 17
IMPLEMENTACIÓN NO ALEATORIA
18
OBJETIVO .................................................................................................... 18
Índice
CLASES RELEVANTES .................................................................................. 18
MOTOR ........................................................................................................ 19
ATRIBUTOS ...................................................................................................................... 19
MÉTODOS ........................................................................................................................ 19
SIMULADOR ................................................................................................. 20
ATRIBUTOS ...................................................................................................................... 20
MÉTODOS ........................................................................................................................ 20
MUNDO ........................................................................................................ 21
ATRIBUTOS ...................................................................................................................... 22
MÉTODOS ........................................................................................................................ 22
AGENTE ....................................................................................................... 23
ATRIBUTOS ...................................................................................................................... 24
MÉTODOS ........................................................................................................................ 25
JAVABEANS ..................................................................................................................... 27
COCHE ......................................................................................................... 28
MÉTODOS ........................................................................................................................ 28
PERSONA ..................................................................................................... 29
MÉTODOS ........................................................................................................................ 29
SEMÁFORO .................................................................................................. 29
MÉTODOS ........................................................................................................................ 29
POSICIÓN ..................................................................................................... 30
ATRIBUTOS ...................................................................................................................... 31
MÉTODOS ........................................................................................................................ 31
CARRETERAAUTOCARGA .......................................................................... 32
ATRIBUTOS ...................................................................................................................... 33
MÉTODOS ........................................................................................................................ 34
FICHERO DE REGLAS ................................................................................... 35
COLOR DE LOS AGENTES ............................................................................. 37
APPLET ........................................................................................................ 38
APLICACIÓN ALEATORIA (1 MOTOR GLOBAL)
40
OBJETIVO .................................................................................................... 40
CLASES MODIFICADAS ................................................................................ 40
CARRETERAAUTOCARGA .......................................................................... 40
FICHERO DE REGLAS .................................................................................. 41
DESCRIPCIÓN DEL MECANISMO ........................................................................................ 41
BÚSQUEDA DE LAS POSICIONES ....................................................................................... 42
ELECCIÓN DE LA POSICIÓN............................................................................................... 42
MOVIMIENTO ................................................................................................................... 43
MÓDULO RESTRICCIONES .......................................................................................... 44
APLICACIÓN ALEATORIA (1 MOTOR/AGENTE)
46
OBJETIVO .................................................................................................... 46
Índice
CLASES MODIFICADAS ................................................................................ 46
AGENTE ....................................................................................................... 46
CARRETERAAUTOCARGA .......................................................................... 46
CREACIÓN DE AGENTES ................................................................................................... 46
INSTANCIACIÓN DE AGENTES Y POSICIONES ..................................................................... 47
CREACIÓN DE LOS MOTORES ............................................................................................ 48
MOTOR ........................................................................................................ 48
FICHERO DE REGLAS ................................................................................... 49
INDIVIDUALIZACIÓN DEL MOTOR ..................................................................................... 49
MÓDULOS ........................................................................................................................ 49
VALORACIÓN DEL TRABAJO
51
PROBLEMAS ENCONTRADOS ....................................................................... 51
COSAS INACABADAS .................................................................................... 51
POSIBLES AMPLIACIONES ........................................................................... 52
VALORACIÓN PERSONAL ............................................................................ 52
ANEXOS
54
Introducción
5
Introducción
Descripción de la aplicación
La aplicación sobre la que hemos trabajado se construyó basándose en
AgentSheets, una herramienta que construye simulaciones interactivas en Java mediante
programación tactil. AgentSheets permite definir tanto escenarios como agentes que
interactúan sobre dichos escenarios, junto con su comportamiento. Basándose en las
especificaciones y definiciones realizadas AgentSheets genera Applets de Java que
llevan a cabo la simulación.
El trabajo se ha realizado sobre una apliación construida con esta filosofía, que
proporciona una herramienta de creación de escenarios y un simulador de los escenarios
creados, donde puede observarse el comportamiento de los agentes.
Nuestra tarea
Nosotros nos hemos centrado en la parte de simulación de escenarios y agentes.
Concretamente este trabajo ha consistido en la reprogramación de las estructuras de
control que rigen el comportamiento de los agentes.
En la version previa de la aplicación de la que partíamos este control estaba
programado de forma imperativa en Java y, mediante la utilización del motor de
inferencia Jess (clon de CLIPS en Java), hemos modelado el comportamiento de los
agentes mediante reglas que interactuan directamente con los objetos de la aplicación de
partida.
De este modo el estado de cada objeto agente en Java va evolucionando de
acuerdo a las reglas programadas en Jess, lo que nos permite simplificar el control
anteriormente programado imperativamente y además posibilita la modificación del
comportamiento de los agentes con sólo modificar las reglas, sin necesidad de modificar
el código Java.
Además se ha añadido la posibilidad de especificar el color del agente, cosa que
no podía hacerse antes y de gran utilidad a la hora de identificar gráficamente a cada
agente y su comportamiento.
Se ha intentado hacer que la aplicación sea un Applet para de ese modo poder
colocarse en la web y ser ejecutada desde un navegador. Esto no debería de ser difícil,
pero se han encontrado ciertas dificultades para llevarlo acabo y dada la escasez de
tiempo no se ha podido implementar.
Jess
6
Jess
Introducción
Como hemos mencionado, la meta principal de nuestro trabajo ha sido la
programación del comportamiento de los agentes mediante reglas. Para ello utilizamos
el motor de inferencia Jess.
Jess está programado en Java. Aunque está programado basándose en CLIPS
(su sintáxis, su funcionamiento, ...) y coinciden prácticamente en todo, igualmente
existen algunas diferencias que como programadores hemos de contemplar y cuidar a la
hora de utilizar Jess o escribir sus scripts. Las semejanzas y alguna diferencia (ya que
sólo hemos utilizado lo necesario) serán mencionadas y descritas de aquí en adelante.
Jess presenta varias peculiaridades que lo hacen especialmente interesante para
determinados usos. Su principal ventaja es su preparación para trabajar y cooperar con
programas en Java. Por ejemplo, Jess permite la ejecución de scripts en Java, es decir,
puedes crear objetos y estructuras de control Java dentro de un script de Jess y
ejecutarlos sin necesidad de compilación previa.
Sin embargo la característica más interesante de cara a la implementación del
comportamiento de los agentes mediante reglas es la capacidad de interactuar
directamente con objetos de Java. Así como CLIPS maneja hechos Jess puede manejar
igualmente hechos e instancias de objetos de Java. Esto nos permite escribir las reglas
que definen el comportamiento de los agentes sin necesidad de realizar una traducción
de estructuras de datos de Java a estructuras de datos de Jess, sino haciendo referencia a
los objetos tal cual existen en Java, aunque ello conlleve una mayor complejidad en la
programación de los objetos, como se verá más adelante.
Obtención de Jess
El desarrollo de Jess se lleva a cabo en los Sandia National Laboratories en
Livermore, California. Pese a ser algo relativamente interesante, en Internet la
información de Jess es escasa, y hay que recurrir en la mayoría de las ocasiones a la web
de los laboratorios de Sandia. Dicha dirección es:
http://herzberg.ca.sandia.gov/jess/
En esta dirección puede encontrarse la documentación de Jess, una lista de
correo a la que suscribirse (muy recomendable, el mismo autor de Jess te responde a las
dudas), y por supuesto las clases o los fuentes (estos últimos bajo licencia) para
descargalos, entre otras cosas.
Jess
7
Archivos fuente
Los archivos fuente o las clases de Java pueden obtenerse en la misma dirección,
accediendo al link "Download Now!". Allí se pide el nombre, el de la empresa y la
dirección de correo (no tienen por qué darse los verdaderos, pero no hacen nada malo
con ellos). Tras ello se accede a la página donde están las diferentes versiones de Jess
desarrolladas y allí se pueden descargar o bien los fuentes o las clases de la versión
deseada.
El inconveniente es que las clases son una versión de prueba de 30 días, y para
obtener los fuentes hace falta una licencia. Puede obtenerse una licencia de estudiante,
pero hay que rellenar unos formularios, que alguien de la universidad los firme y hay
que mandarlos por fax o correo a Sandia.
Instalación y utilización
Una vez obtenido Jess (en formato .zip o .tar.gz) se descomprime y se coloca en
el ordenador, bien en el directorio en el que se esté trabajando o donde parezca un buen
sitio para tenerlo.
Si se coloca en el directorio de trabajo no habrá mayores problemas, pero si se
coloca en otro lugar habrá que asegurarse de que se incluye en la variable de entorno
CLASSPATH, bien en el autoexec.bat o a la hora de compilar/ejecutar con la opción classpath.
También se puede elegir entre tenerlo descomprimido (con lo que se obtiene una
estructura de directorios), o comprimido. Si lo está comprimido en .zip su utilización es
similar a la de un archivo .jar.
Para utilizar Jess desde cualquier aplicación hay que añadir en el archivo fuente
de Java:
import jess.*;
Con esto se puede trabajar normalmente con sus clases y su API (también
disponible en la web).
Documentación
En la web de Jess de Sandía también están disponibles los manuales de las
distintas versiones desarrolladas. Este manual no es una gran referencia, pero es la
única y aún se le puede sacar bastante jugo con algo de paciencia.
Dentro de cada manual también se puede encontrar la información de las clases
y la API de Jess generada mediante Javadoc, para que resulte familiar.
Jess
8
Programación básica en Jess
Como se ha mencionado antes, los archivos de reglas de Jess son prácticamente
idénticos a los de CLIPS, con alguna diferencia. Jess puede ejecutarse como una
aplicación igual que CLIPS, con su linea de comandos y su interfaz sencillo. A parte de
eso, Jess proporciona un API para trabajar desde Java con ella, para crear motores de
inferencia, controlar su funcionamiento, instanciar hechos, etc. y todo desde Java.
En nuestro caso vamos a utilizar Jess desde Java, desde la aplicación que existía
anteriormente.
Desde Java
En primer lugar habrá que crear un motor Jess. Esto se hace mediante la
instanciación de un objeto de la clase Rete. de la siguiente manera:
...
import jess.*;
...
Rete rete = new Rete();
...
Tras ello puede ejecutarse cualquier comando de Jess mediante el método de
Rete executeCommand. Para cargar un archivo de reglas se hace lo siguiente:
try {
rete.executeCommand("(batch \"" + rutaFichero + "\")");
rete.executeCommand("(reset)");
}
catch (JessException je) {
...
}
Finalmente para poner en marcha el motor Jess habrá que invocar al método
Sin parámetros el motor se pone en marcha hasta que no hay reglas sensibilizadas,
pero también puede pasarse como parámetro el número de pasos que se quieren
ejecutar, del mismo modo que la función (run) de CLIPS. De hecho puede invocarse
también con executeCommand:
run.
try {
rete.run(1); /* como rete.executeCommand("(run 1)"); */
rete.run(); /* como rete.executeCommand("(run)"); */
}
catch (JessException je) {
...
}
Peculiaridades
Jess
9
Hasta aquí se ha visto cómo trabajar con Jess desde Java, pero a parte de eso la
sintáxis viene a ser la misma que la de CLIPS. Existen diferencias des sintáxis en
aspectos importantes, fundamentalmente en lo que se refiere al manejo de objetos de
Java, que se verá acontinuación.
Un punto importante en Jess por su peculiaridad son los módulos. En la
programación CLIPS utilizar módulos es algo habitual e importante en el proceso de
resolución de problemas mediante reglas. Sin embargo, hasta la versión 6.0 Jess no
admitía módulos. En la versión 6.0 los admite, pero su comportamiento es algo
diferente y hay que tener bastante cuidado.
La definición del módulo se hace de la misma manera:
(defmodule MODULO)
Todas las reglas definidas a partir de esa definición pertenecerán a ese módulo.
No obstante puede indicarse explícitamente el módulo al que ha de pertenecer una regla:
(defrule MODULO::regla
...
=>
...
)
La principal diferencia es que en CLIPS los deftemplate y los hechos que
utilizan un deftemplate pertenecen al módulo en el que están definidos y en Jess son
globales. Así como en CLIPS había que indicar qué cosas exportar e importar en cada
módulo, en Jess todo es de todos.
Tanto en CLIPS como en Jess la ejecución de los módulos se maneja de la
misma manera. focus cambia el "current focus" y apila el anterior en una pila (focus
stack). Cuando la agenda del current focus se vacia se obtiene el siguiente de la pila.
(focus MODULO1)
(focus MODULO2)
Además pueden definirse reglas con auto-focus. Estas reglas en cuanto se
sensibilizan hacen que se active su módulo (obtenga el "current focus") y se disparen las
reglas sensibilizadas en dicho módulo. Cuando la agenda del módulo se vacía vuelve a
perder el focus.
(defrule MODULO::regla
(declare (auto-focus TRUE))
...
=>
...
)
Problemas
Jess
10
Tuvimos un problema con el orden de ejecución de las operaciones en la
postcondición de las reglas. Esta ejecución en teoría debería hacerse de manera
atómica, pero al parecer en Jess no, ya que nuestro problema se solucionó alterando el
orden de las operaciones realizadas.
Manejo de objetos Java
Las modificaciones realizadas en la aplicación se basan en el uso de Jess y la
facilidad de éste para manejar objetos Java. A continuación se describe el
procedimiento para llevar a cabo esta comunicación.
JavaBeans
Para que un objeto Java sea accesible desde Jess, éste objeto ha de estar
programado siguiendo la filosofía JavaBean.
Un JavaBean es un objeto que tiene, para cada atributo que quiera ser accedido
desde el exterior, un método para leerlo y otro para modificarlo. Estos métodos tendrán
como nombre "set" seguido del nombre del atributo con la primera letra mayúscula y
"get" con el mismo nombre detrás para el método de lectura y escritura respectivamente.
...
Atributo atributo
...
public Atributo getAtributo() {
return atributo;
}
...
public void setAtributo(Atributo nuevo_atributo) {
atributo = nuevo_atributo;
}
...
La tecnología JavaBean permite la comunicación entre diferentes aplicaciones
mediante la invocación a estos métodos. Jess se basa en esto para poder acceder y
modificar los objetos Java de nuestra aplicación.
Además de los métodos "set" y "get", cuando se produce una modificación en el
valor del atributo, ha de notificarse.
Para ello hay que añadir un
PropertyChangeListener, en el que se invocará al método firePropertyChange()
cada vez que se modifique un atributo. La declaración del "listener" se hace de la
siguiente manera:
public PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener pcl)
{
pcs.addPropertyChangeListener(pcl);
}
public void removePropertyChangeListener(PropertyChangeListener pcl)
{
Jess
11
pcs.removePropertyChangeListener(pcl);
}
Para invocar al método firePropertyChange() hay que indicarle el nombre del
atributo modificado, el valor antiguo de ese atributo y el valor nuevo.
...
Atributo valor_antiguo;
...
valor_antiguo = atributo;
atributo = valor_nuevo;
pcs.firePropertyChange("atributo", valor_antiguo, valor_nuevo);
...
Declaración de clases en Jess
Una vez preparados los objetos como JavaBeans, se procede a programar la
aplicación, parte en Jess, parte en Java.
En primer lugar hay que declarar las clases de Java que se van a utilizar. Para
ello se utilizará la función defclass. El resultado de esta declaración es la definición
de un template que tiene como slots cada uno de los atributos de la clase que tienen
programado un método "get" (simplemente porque Jess sólo puede leer los que tienen
dicho método).
Por ejemplo, la definición de
(defclass clase Clase) ;;;minúscula en Jess, mayúscula en Java
si la clase Clase tiene como atributos con método "get" atributo1 y
da como resultado (internamente) una definción como esta:
atributo2,
(deftemplate clase
(slot atributo1)
(slot atributo2))
Instanciación de los objetos desde Java
Una vez definidas las clases, cuando los objetos son creados en el programa
Java, han de ser instanciados (algo parecido a si fueran hechos) en Jess.
Para la instanciación de los objetos recurrimos al API de Jess para Java. Hay
que crear un objeto de la clase Funcall (llamada a función), de tipo definstance. En ese
objeto indicaremos el nombre de la clase, el tipo del objeto, y una referencia al objeto en
sí mediante creación de objetos de la clase Value. La llamada se hace de la siguiente
manera:
Rete rete;
...
Clase objeto = new Clase();
...
//motor Jess
Jess
12
f = new Funcall("definstance", rete);
f.add(new Value("clase", RU.ATOM));
f.add(new Value(objeto));
f.execute(rete.getGlobalContext());
//tipo y motor
//clase y tipo objeto
//el objeto en sí
//ejecución
Manejo de los objetos desde Jess
Tras esto, ya sólo queda utilizar los objetos en la programación de las reglas y
funciones en Jess. Su utilización es similar a la de los hechos, pero con diferencias.
A la hora de escribir las reglas, los objetos se manejan como hechos generados a
partir de deftemplates. Sin embargo, cuando aparece un objeto en la "pre" de una
regla y desea almacenarse una referencia al mismo en una variable para utilizarla en el
resto de la regla, no puede hacerse como en CLIPS o en Jess con los hechos, sino de
otra forma.
Para guardar una referencia a un hecho se haría de la siguiente manera:
(defrule regla
?referencia <- (hecho
(slot1 ?referencia1)
(slot2 ?referencia2))
=>
...
)
Para hacer lo mismo con un objeto se utilizará el slot OBJECT que tiene todo
objeto generado a partir de un defclass. Un caso equivalente al anterior se haría así:
(defrule regla
(objeto
(atributo1 ?referencia1)
(atributo2 ?referencia2)
(OBJECT ?referencia)
)
=>
...
)
Invocación a métodos de los objetos desde Jess
Tanto en las "pre" como en las "post" de las reglas suele ser necesario invocar
métodos de los objetos referenciados con el fin de observar su estado (en las "pre") o de
modificarlo (en las "post"). La invocación a métodos de objetos desde Jess se hace de
la siguiente manera.
Si tenemos que invocar a un método "get" o "set" lo podemos hacer directamente
con la siguiente sintáxis:
(get ?referencia atributo)
(set ?referencia atributo valor)
Jess
13
Siendo ?referencia una referencia (valga la redundancia) a un objeto de una
clase, y atributo un atributo de dicha clase que siguiendo la filosofía JavaBean tiene
los métodos "get" y "set" implementados.
En general, cualquier método se puede invocar mediante la sentencia call de la
siguiente manera:
(call ?referencia método parámetro1 ... parámetroN)
Los métodos "get" y "set" también pueden ser invocados de esta última forma:
(call ?referencia getAtributo)
(call ?referencia setAtributo valor)
Aplicación inicial
14
Aplicación inicial
Inicialmente la aplicación estaba programada enteramente en Java. Constaba de
un interfaz gráfico, una clase simulador que se encarga de gestionar los agentes y el
escenario y las clases de los agentes con su comportamiento definido.
Clases relevantes











Simulador
Mundo
Agente
Coche
Persona
Semáforo
Tesoro
Acción
Posición
CarreteraAutocarga
ResuelveRutasJESS
Simulador
Hereda de la clase Gamelet que contiene un thread que se encarga de generar
"ticks". Un "tick" es la unidad de tiempo de la simulación.
Gestiona el ritmo de ejecución de la simulación. Para ello implementa el método
"tick()" que invoca al método del mismo nombre de la clase ActorManager que, en
última instancia, invoca al método "tick()" de cada posición.
Realiza otra serie de tareas que no están relacionadas con el desarrollo de la
nueva aplicación y que, de hecho, serán eliminadas.
Mundo
Proporciona un método size (int, int) que, dado el número de filas y columnas
del tablero, genera los objetos posición del escenario.
Tiene un método que dadas unas coordenadas (x,y) devuelve el objeto posición
correspondiente. También implementa una serie de métodos que, dada una posición,
devuelven las posiciones vecinas.
Aplicación inicial
15
Agente
Es una clase abstracta.
Contiene los atributos que caracterizan al agente (nombre, estado, orientación,
posición en la que se encuentra, frecuencia con la que se mueve, tiempo de espera desde
el último movimento...) y los métodos de acceso a dichos atributos. También la
redefinición del método draw(Graphics) para el redibujado del agente.
Atributos y métodos relacionados con la definición del comportamiento usando
Java. Éstos serán eliminados en la nueva versión de la aplicación ya que el
comportamiento de los agentes se define y se gestiona mediante Jess.
Método "tick()". Este método es invocado desde la clase posición para cada
agente contenido en ella. "tick()" se invoca cada cierto número de milisegundos y un
agente realiza un movimiento cada cierto número de "ticks" especificado mediante su
atributo "frecuencia."
El algoritmo es similar al siguiente:
if (--turnoActual != 0) return;
// Espera a que le toque el turno
else turnoActual = frecuencia;
//--Para cada posicion vecina a la que nos encontramos ahora...
//--Para cada agente que se encuentra en una posicion vecina...
Accion accion = getAccion (agenteVecino.getTipo(),
agenteVecino.getEstado());
reorientarse();
if ((accion.isDireccional() && estaEnfrente(posicionVecina))
|| !accion.isDireccional()))
accionesARealizar.addElement(accion);
if (accionesARealizar.size() != 0)
this.apilaObjetivo((Accion)accionesARealizar.elementAt(0));
ejecutaSiguienteAccion();
actualizarPosicion();
Coche, Persona, Semáforo, Tesoro
Métodos relacionados con la realización de acciones y comportamientos propios
de cada tipo de agente:
acciónANDAR(),acciónIR_A, acciónPARAR, ejecutarSiguienteAcción.
Estos métodos desaparecerán al utilizar Jess y definir el comportamiento de los agentes
mediante reglas.
Los atributos de la clase agente toman valores específicos de cada tipo de
agente, cada tipo de agente puede adoptar unos estados determinados y puede visitar
distintos tipos de casillas (pej. para un coche las casillas permitidas son la carretera y los
pasos de cebra mientras que un peatón puede ir sólo por las aceras y los pasos de cebra).
Aplicación inicial
16
La clase Tesoro respondía a una funcionalidad de la aplicación que en la nueva
versión no tiene sentido y se elimina. En la versión antigua existía la posibilidad de
colocar un agente Tesoro (sin ningún tipo de comportamiento) hacia el cual se movían
los agentes. Para ello los agentes con capacidad de movimiento (Coche y Persona)
cuando se les indicaba que debían buscar el tesoro trazaban una ruta entre su posición y
la del objetivo y, una vez que la tenían se limitaban a moverse por ese camino. Era aquí
donde se hacía uso de Jess, en la obtención de la ruta. Sin embargo este no es un
verdadero comportamiento de agentes ya que, en teoría un agente debe "decidir" su
movimiento en función de su entorno y no moverse siguiendo un itinerario
precalculado.
Acción
Esta clase determina un tipo de comportamiento para un agente. Posee prioridad
y puede ser parametrizada. Tiene además métodos que comprueban si se puede aplicar
y métodos de acceso a sus atributos.
La clase acción se elimina en la nueva versión ya que, como se ha comentado
antes, se sustituye el control del comportamiento de los agentes realizado desde Java por
un comportamiento definido mediante reglas utilizando Jess.
Posición
Clase que representa cada una de las casillas en las que está dividido el tablero y
en las que se puede encontrar un agente.
Tiene unas coordenadas (x,y) y una lista de los agentes que se encuentran
situados en ella en cada momento. También tiene un vector con las posiciones
adyacentes o vecinas así como diferentes métodos de acceso a estos atributos.
Método "tick()" que es invocado desde ActorManager y que invoca al método
"tick()" de todos los actores que se encuentren en la posición en ese momento.
Métodos de redibujado que se encargan de dibujar no sólo la superficie de la
posición (carretera, hierba, árbol, etc) sino todos los agentes que se encuentran en ella.
CarreteraAutocarga
Hereda de la clase Simulador y es la clase que se encarga de lanzar la
simulación. Carga el fichero de definición de un escenario y luego crea a partir de él un
objeto mundo y los objetos agentes con lo que la simulación queda ya en marcha .
Aplicación inicial
17
Posee diversos métodos y atributos relacionados con la representación gráfica
que no influyen en la realización de la nueva versión ya que se mantiene intacta la
gestión del interfaz gráfico.
ResuelveRutasJESS
En esta clase es en la única en la que se utiliza Jess en la versión inicial de la
aplicación. Posee como atributos un motor de inferencia de Jess (rete) y la ruta de un
archivo con las reglas en Jess.
Existe un método que inicializa el motor y otro que calcula la ruta dado un
origen y un destino. Esto era así porque en la versión anterior de la aplicación los
agentes se movían o bien buscando un tesoro o bien porque el usuario les indicaba una
posición destino. En la nueva versión el comportamiento que se queire obtener de los
agentes es distinto por lo que esta clase resulta eliminada.
Implementación no aleatoria
18
Implementación no aleatoria
Objetivo
El objetivo de esta versión de la aplicación es modelar el comportamiento de los
agentes en Jess mediante el uso de reglas y eliminar el código Java que hacía esta
función en la versión anterior.
El comportamiento de los agentes cambia. Ahora no existe la posibilidad de
fijar una posición destino para el agente, ya que de lo que se trata es de modelar un
comportamiento reactivo al entorno (escenario y otros agentes) y a los cambios que se
produzcan en él.
Con la desaparición del comportamiento descrito desparece también la clase
Tesoro, cuya única función era ser el destino de un movimiento de una persona. Al no
haber destinos, no hay tesoros.
Así mismo desaparecerá la clase Acción, ya que el comportamiento será
enteramente descrito mediante reglas, sin necesidad del uso de esta clase.
Clases relevantes









Motor
Simulador
Mundo
Agente
Coche
Persona
Semáforo
Posición
CarreteraAutocarga
A continuación se muestra el diagrama de clases (sin métodos ni atributos, ya se
mostrarán más adelante):
Implementación no aleatoria
19
Posicion
Mundo
-mundo
+posicion
+mundo
Simulador
-owner
CarreteraAutoCarga
Agente
+$motorRete
Motor
Coche
Persona
Semaforo
Motor
La clase Motor es una nueva clase que surge con la necesidad de crear un motor
Jess que se ejecute independientemente del hilo de ejecución de la aplicación principal.
De este modo la aplicación sigue su curso mientras que el motor Jess se ejecuta en un
thread independiente, disparando y ejecutando reglas conforme sea necesario.
Atributos
Rete rete;
Métodos
public Motor (Rete p_rete);
Es el constructor. Construye el thread a partir del objeto de clase Rete que es el
motor de inferencia de Jess y se pasa por parámetro.
public void run ();
Es la función de ejecución del thread. Lo único que hace en este caso es invocar
a rete.run(), para poner en marcha el motor. Una vez puesto en marcha no acaba nunca.
Implementación no aleatoria
20
Simulador
Como se ha mencionado, en esta aplicación se elimina el comportamiento de
asignar destinos a agentes. Esto se hacía seleccionado un agente con el ratón e
indicándole dónde tenía que ir. Ahora eso ya no ocurre, con lo que los atributos y
métodos relacionados son eliminados.
Borramos el atributo que indicaba quién está seleccionado y el método
getAgenteSeleccionado.
También se eliminan los métodos clickRatón(),
seleccionarAgente() y deSeleccionarAgente().
Atributos
public Mundo mundo;
private String modoEjecucion;
private Vector listaDeModos;
private Vector agentes;
private boolean keypressed;
Métodos
public abstract int mundoX ();
public abstract int mundoY ();
Métodos a implementar que devuelven la anchura y la altura del tablero.
public abstract String mundoDescripcionEstandar ();
Implementación no aleatoria
21
Método a implementar que proporciona una descripción del mundo.
public void init () { ... }
Método que inicializa el simulador y el mundo. No se han hecho grandes
cambios aquí, simplemente algo de limpieza de código y comentarios.
public void tick () { ... }
Método "tick", que inicia la secuencia de invocaciones a "tick's" hasta llegar a
los agentes. No se ha modificado, sólo limpiado.
public void run () { ... }
Indica cuándo invocar al método propio "tick()" en función de si el modo de
ejecución es contínuo o dirigido por teclado.
public
public
public
public
void elegirModo (String modo) { ... }
String modoActual () { ... }
void addAgente (Agente agente) { ... }
void removeAgente (Agente agente) { ... }
Métodos que manejan los agentes del simulador y los modos de ejecución.
public boolean handleRequestedEvent (Event theEvent) { ... }
Método que gestiona los eventos de teclado. Tan sólo se ha limpiado el código.
public abstract Image getImagenSeleccion();
public abstract Point getTamTablero();
Otros métodos abstractos.
Mundo
En la clase Mundo se crean los objetos Posición para el escenario y se
almacenan las orientaciones y métodos de gestión de posiciones, posiciones vecinas,
etc.
No se ha tocado nada con respecto a la aplicación inicial. Puede que haya
alguna cosa innecesaria pero se ha dejado por si se le encontrara alguna utilidad en el
futuro.
Implementación no aleatoria
22
Atributos
public
public
public
public
static
static
static
static
final
final
final
final
int
int
int
int
ARRIBA
ABAJO
IZQUIERDA
DERECHA
=
=
=
=
3;
1;
2;
0;
Constantes que indican la orientación de un agente.
private Simulador owner;
private Container ownerTab;
El simulador y el tablero.
private int ancho = 0, alto = 0;
Ancho y alto del tablero.
private Vector posiciones;
Objetos de Posicion contenidos en el tablero.
Métodos
Mundo (Simulador simul) { ... }
Constructor. Asigna los valores de owner y de ownerTab.
Implementación no aleatoria
23
public Vector orientaciones () { ... }
Devuelve un vector con los nombres (strings) de las orientaciones posibles.
public void size (int x, int y) { ... }
A partir de las dimensiones del tablero construye los objetos Posición.
public Posicion posicion (int x, int y) { ... }
Devuelve la posición correspondiente a las coordenadas x e y.
private void borrarMundo () { ... }
Elimina los objetos Posición del ActorManager.
public Vector posicionesVecinasDe (int x, int y) { ... }
public Vector posicionesVecinasDe (int x, int y,
int orientacionInicial) {...}
public Vector posicionesVecinasDe (Posicion pos) { ... }
public Vector posicionesVecinasDe (Posicion pos,
int orientacionInicial) {...}
Devuelven un Vector con las posiciones vecinas de la Posición dada.
public Posicion posicionDe(int xAbs, int yAbs,
int anchoTotal, int altoTotal) {...}
Devuelve la posición correspondiente a las coordenadas y dimensiones
introducidas.
public int[][] croquis(Vector posicionesPermitidas) { ... }
A partir del escenario y de un vector de posiciones permitidas genera un
esquema con 0's y 1's del tablero.
Agente
La clase Agente sirve como soporte para la implementación de agentes concretos
(Persona, Coche, ...). Proporciona atributos con información del estado del agente,
métodos para obtener dicha información y el método "tick()", que antes era el que
controlaba el comportamiento del agente y que ahora se ha reducido significativamente,
ya que el control pasa al motor Jess.
Inicialmente la clase Agente contenía bastantes atributos y métodos con relación
a la recepción de mensajes (órdenes recibidas por el agente) y al control de la ejecución
del movimiento de cada agente (acciones permitidas, objetivos, ...). Toda esta
información es eliminada en esta nueva versión, ya que los agentes no reciben órdenes,
no hay objetivos ni planificación y el comportamiento se describe mediante reglas.
Implementación no aleatoria
24
Atributos
public
public
public
public
static
static
static
static
final
final
final
final
int
int
int
int
ARRIBA
ABAJO
IZQUIERDA
DERECHA
=
=
=
=
Mundo.ARRIBA;
Mundo.ABAJO;
Mundo.IZQUIERDA;
Mundo.DERECHA;
Implementación no aleatoria
25
Direcciones del Mundo.
public int orientacionAgente;
Hacia dónde esta mirando el agente.
public Posicion posicion = null;
Atributo nuevo. Es la posición sobre la que se encuentra el agente en un
momento dado.
public int frecuencia;
public int espera;
Cada cuantos "tick's" mueve el agente y número de "tick's" que le quedan para
mover respectivamente.
public String tipo;
public String nombre;
Tipo del agente y nombre del agente en particular. El atributo nombre no lo
utilizamos pero puede resultar de utilidad en el futuro.
public Vector posicionesPermitidas;
Posiciones por las que el agente puede moverse.
public String estado;
Estado del agente (Ej. "andando", "parado", ...).
Métodos
public abstract int framesTotal ();
public abstract int framesHorizontal ();
Frames totales y por fila del dibujo .gif que representa al agente.
public void setCurrentframe (int frame) { ... }
public int getCurrentframe () { ... }
Métodos "set" y "get" de currentFrame, atributo que indica el frame de la imagen
gif que representa al actor que debe dibujarse.
public void setPosicionesPermitidas
(Vector posicionesPermitidas) { ... }
public Vector getPosicionesPermitidas() { ... }
public boolean esPermitida (Posicion pos) { ... }
Implementación no aleatoria
26
Métodos de manejo del vector de posiciones permitidas.
El método
esPermitida() será invocado con frecuencia desde Jess para determinar el movimiento
del agente.
abstract String getTipo();
Obtiene el tipo del agente.
public int getEspera () { ... }
public void setEspera (int nuevaEspera) { ... }
public int getFrecuencia () { ... }
Métodos "set" y "get" de espera y frecuencia para ser invocados desde Jess.
Frecuencia tiene tan solo un método "get" porque es un atributo que no se modificará
desde Jess, con lo cual no necesita de método "set".
public String getNombre() { ... }
public void setNombre(String nombre) { ... }
Manejo del nombre.
public String getEstado() { ... }
public void setEstado(String nuevoEstado) { ... }
Manejo del estado. Métodos "set" y "get" invocados frecuentemente desde Jess.
public int getOrientacion() { ... }
public void setOrientacion(int orientacion) { ... }
Métodos "set" y "get" de orientación, de gran utilidad y uso en Jess.
public
public
public
public
boolean
boolean
boolean
boolean
estaEnfrente(Posicion pos) { ... }
estaAlLado (Posicion pos) { ... }
esAdyacente (Posicion pos) { ... }
estaDetras (Posicion pos) { ... }
Métodos de relación del agente con respecto a posiciones. Serán igualmente
invocados con frecuencia desde Jess para determinar qué regla disparar en función de la
situación del agente.
public int calculaOrientacion(Posicion pIni,
Posicion pFin) { ... }
Dadas dos posiciones (posición inicial del agente y posición destino
generalmente) calcula la orientación desde la primera a la segunda. Este método se
utiliza en Jess para calcular la orientación en la que quedará el agente antes de que
mueva.
Agente (Simulador simul, String color) { ... }
Constructor. Es prácticamente tal cual estaba, quitando lo innecesario.
public void setPosicion(Posicion nP) { ... }
public Posicion getPosicion () { ... }
Implementación no aleatoria
27
Métodos "set" y "get" de la posición del agente. Esencial para Jess.
public void destruir () { ... }
Elimina el agente. No se utiliza explícitamente, pero se deja por si tuviera
utilidad.
public void draw (Graphics g) { ... }
Método de redibujado del agente.
public void tick () { ... }
Método esencial en el flujo de ejecución. En la aplicación anterior aquí se
centraba todo el control, objetivos, decisiones, etc. del movimiento del agente. Ahora
todo ese control pasa al motor Jess y a las reglas programadas.
En esta aplicación sólo se encarga de marcar el ritmo de movimiento de los
agentes, pero las decisiones y el movimiento corren a cargo de Jess. El ritmo lo marca
reduciendo espera en 1 cada vez que "tick()" es invocado hasta llegar a 0. Cuando
"tick" llegue a 0 Jess detectará que el agente tiene que mover e iniciará su "turno" de
movimiento. El código es el siguiente:
if (this.getEspera() != 0)
this.setEspera (this.getEspera() - 1);
La simplificación del método es impresionante.
JavaBeans
Como se menciona en el apartado de Jess, los atributos de una clase que quieran
ser utilizados desde Jess han de estar programados siguiendo la filosofía de JavaBeans.
Esto hace que cada uno de ellos tenga un método "set" y otro "get".
Además, los métodos "set" que sean invocados desde Jess u otro método que
modifique un atributo que es leído desde Jess, tras hacer la modificación, ha de
notificarlo mediante una invocación a firePropertyChange().
Para ello en primer lugar hay que realizar las siguiente instancia, tal y como está
en la clase Agente:
public PropertyChangeSupport pcs =
new PropertyChangeSupport(this);
public void addPropertyChangeListener
(PropertyChangeListener pcl)
{
pcs.addPropertyChangeListener(pcl);
}
public void removePropertyChangeListener
(PropertyChangeListener pcl)
{
Implementación no aleatoria
28
pcs.removePropertyChangeListener(pcl);
}
Luego, tras cada modificación de los atributos correspondientes se hará la
invocación de la siguiente manera:
pcs.firePropertyChange("nombre_atributo",
valor_viejo,
valor_nuevo);
como se indicó en el apartado de programación con Jess.
Coche
La clase coche extiende Agente, implementando los métodos abstractos de
Agente para un agente de tipo coche y creando un constructor que inicializa los
atributos con los valores adecuados.
En la versión anterior se instanciaban las acciones de cada una de las subclases
de Agente, se establecían objetivos y se utilizaba Jess. Además se implementaban
métodos para cada una de las acciones que tenía que hacer el agente. Ahora en cambio
no hay que hacer nada de esto ni en Coche, ni en Persona, ni en Semáforo.
Métodos
public String getTipo() { ... }
Devuelve "COCHE";
Coche (Simulador s, String nombre, String color) { ... }
Constructor de Coche. Instancia el nombre, la frecuencia y espera del agente,
las posiciones posibles, el estado inicial y la orientación.
También admite el color como parámetro. Esto es algo añadido a la aplicación
inicial. En el constructor de la clase Agente es donde se abre el fichero de la imagen del
agente. Allí, de acuerdo con el color se abrirá un fichero u otro para el agente. Resulta
extremadamente útil para distinguir visualmente los agentes y observar que su
comportamiento es el correcto.
public int framesHorizontal () { ... }
public int framesTotal () { ... }
Implementación no aleatoria
29
Devuelven 4 y 4 (las 4 orientaciones posibles del coche).
Persona
Como Coche pero para Persona.
Métodos
public String getTipo() { ... }
Devuelve "PERSONA";
Persona (Simulador s, String nombre, String color) { ... }
Constructor de Persona. Instancia el nombre, la frecuencia y espera del agente,
las posiciones posibles, el estado inicial y la orientación.
En este caso, aunque exista color como parámetro, no hace caso de él. Sin
embargo el procedimiento para añadir personas de colores sería idéntico al de los
coches.
public int framesHorizontal () { ... }
public int framesTotal () { ... }
Devuelven 4 y 4 (las 4 orientaciones posibles de la persona).
Semáforo
Como las anteriores pero para Semáforo.
Métodos
public String getTipo() { ... }
Implementación no aleatoria
30
Devuelve "SEMÁFORO";
Coche (Simulador s, String nombre, String color) { ... }
Constructor de Semáforo. Instancia el nombre, la frecuencia y espera del agente,
las posiciones posibles, el estado inicial y la orientación.
En este caso, aunque exista color como parámetro, no hace caso de él. Sin
embargo el procedimiento para añadir semáforos de colores, aunque puede no tener
sentido, sería idéntico al de los coches y las personas.
public int framesHorizontal () { ... }
public int framesTotal () { ... }
Devuelven 3 y 3 (los 3 estados posibles del semáforo).
public void redibujar () { ... }
Establece el "currentFrame" del semáforo correspondiente a su estado.
Posición
La clase Posición prácticamente no varía con respecto a la versión inicial. Sólo
se hace limpieza de código y eliminación de los atributos y métodos innecesarios.
Implementación no aleatoria
Atributos
public Vector agentes;
Vector que contiene los agentes que están en la posición.
private int posX, posY, ancho, alto;
Situación de la posición.
private String descripcion;
Tipo de posición.
private Mundo mundo;
Tablero en el que se encuentra la posición.
Métodos
Posicion (Simulador simul, Mundo mundo,
int nuevaPosX, int nuevaPosY,
int anchoEspacio, int altoEspacio) { ... }
31
Implementación no aleatoria
32
Constructor de la clase. Inicializa el valor de los atributos.
public int posX() { ... }
public int posY() { ... }
Obtienen los valores de posX y posY respectivamente.
public void tick() { ... }
Invoca al método "tick()" de cada uno de los agentes que hay en esa posición.
public void draw(Graphics g) { ... }
Dibuja todos los agentes que están en la posición. Invoca a dibujar().
public void dibujar (Image frame, int xFrame,
int yFrame, int anchoFrame,
int altoFrame, Graphics g) { ... }
Dibuja una imagen.
public String descripcion() { ... }
Devuelve el tipo de posición.
public void nuevaDescripcion (String d, Image i) { ... }
public void nuevaDescripcion (String d) { ... }
Crea una nueva descripción con su imagen. Si se la pasan como parámetro tal
cual, y si no la obtiene con el nombre.
public Vector tieneAgente (String nombre) { ... }
public Vector getAgentes() { ... }
Devuelven un Vector con el agente con el nombre pasado como parámetro y un
Vector con todos los agentes de la posición respectivamente.
public
public
public
public
Vector
String
String
String
orientaciones() { ... }
siguienteOrientacion (String orientacion) { ... }
anteriorOrientacion (String orientacion) { ... }
cambiarOrientacion (String orientacion,
String modo) { ... }
Manejo de las orientaciones.
public int[][] croquis(Vector posicionesPermitidas)
Invoca a Mundo.croquis().
CarreteraAutoCarga
{ ... }
Implementación no aleatoria
33
Esta es la clase ejecutable. Hace todas las labores para preparar el comienzo de
la ejecución de la simulación.
Atributos
public static Rete rete;
public static Motor motorRete;
Objeto Rete y Motor que lo contiene.
String nombre;
String nombreFicheroEscenario;
Nombres del escenario y del fichero donde está almacenado el escenario.
Vector nombresPosicion;
Vector imagenesPosicion;
Vectores de nombres que puede tener una posición e imágenes que puede tener
una posición respectivamente.
int[][] tablero;
Matriz de enteros que indica el tipo de suelo que hay en cada casilla del tablero.
Implementación no aleatoria
int
int
int
int
int
int
34
casillasAncho;
casillasAlto;
anchoTotal;
altoTotal;
numAgentes;
numTiposAgente;
Datos varios del escenario.
Vector lanzaAgentes;
Vector utilizado para la creación de los agentes a partir del fichero que contiene
la información del escenario.
Vector nombresTiposAgente;
Vector con los tipos de agente que hay en el escenario.
Métodos
boolean cargaEscenario() { ... }
Pide al usuario el nombre del fichero del escenario mediante una ventana de
navegación de ficheros y directorios y con ese fichero genera el nuevo escenario. Se ha
modificado con respecto a la versión inicial, ya que en esta hay que obtener el color del
agente del fichero. Se hace en la siguiente línea de código:
lanzaAgentes.addElement(new String(st.nextToken()));
//se lee el color!
public Point getTamTablero(){ ... }
Devuelve un objeto Point con las dimensiones del tablero.
public int mundoX ()
public int mundoY ()
Obtienen las dimensiones del tablero (en Posiciones).
public String mundoDescripcionEstandar ()
Devuelve una descripción del mundo. No parece demasiado útil, pero se deja
por si acaso.
public void init ()
Método que inicializa el escenario y el motor Rete. Para el escenario invoca al
método cargaEscenario(), pone el fondo del escenario, y crea cada una de las posiciones
y agentes en el escenario.
Para el motor Jess carga los paquetes, carga el fichero de reglas, y define las
instancias de cada una de las posiciones y agentes en Jess, además de crear el Motor que
Implementación no aleatoria
35
contiene el objeto Rete. A continuación se muestran los fragmentos de código más
significativos.
rete.executeCommand("(batch \"" + this.currentPath() +
"archivoReglas.jess\")");
rete.executeCommand("(reset)");
...
for (int y = 0; y < casillasAlto; y++) {
for (int x = 0; x < casillasAncho; x++) {
...
f = new Funcall("definstance", rete);
f.add(new Value("posicion", RU.ATOM));
f.add(new Value(mundo.posicion (x,y)));
f.execute(rete.getGlobalContext());
}
}
...
for (int i=0; i<numAgentes ;i++ ) {
...
//obtención del color
String colorAgente = (String)lanzaAgentes.elementAt(4*i+3);
agente = creaAgentePorNombre(this, tipoDelAgente, colorAgente,
mundo.posicion(pX, pY));
...
}
this.motorRete = new Motor (this.rete);
...
public static void main(String[] args) { ... }
Crea un objeto CarreteraAutoCarga e invoca a sus métodos init() y start().
Agente creaAgentePorNombre(Simulador padre,
String nombre,
String color,
Posicion posicion) { ... }
Crea un agente del nombre, tipo, color y posición indicados, instanciándolo
además en el motor Jess. El código para instanciar en Jess es el siguiente (Como
ejemplo el caso de Persona:
Persona per = new Persona(padre, nombre);
...
f = new Funcall("definstance", rete);
f.add(new Value("persona", RU.ATOM));
f.add(new Value(per));
f.execute(rete.getGlobalContext());
Fichero de reglas
En esta versión de la aplicación existe un único fichero de reglas. Es una
primera aproximación en la que se ha implementado más o menos lo evidente, es decir,
el fichero está compuesto por reglas del tipo "si el agente está en esta situación,
entonces mueve (o gira o para, según proceda)". Parece algo evidente, pero la siguiente
Implementación no aleatoria
36
versión hace que para que un agente actúe tenga que activarse y dispararse una
secuencia más compleja de reglas.
Es una versión "no aleatoria" debido a que el motor Jess no es aleatorio. La
programación de las reglas es independiente, no indica que se tengan que disparar en
orden secuencial, pero Jess utiliza siempre el mismo algoritmo para ordenar y disparar
las reglas, con lo que los agentes acaban haciendo movimientos cíclicos.
Por ejemplo, si existiesen dos reglas, una "girar a derecha" y otra "girar a
izquierda" y en un cruce de caminos un agente pudiera optar por ambas, el algoritmo de
planificación de Jess siempre las ordenaría de la misma manera y dispararía la primera,
siempre la misma. En este caso podría optar por coger siempre "girar a derecha", con lo
que el agente, avanzaría por el escenario en el sentido de las agujas del reloj SIEMPRE.
Para la activación de las reglas programadas se cuenta con el valor del atributo
"espera" de los agentes. Este atributo es decrementado por la función "tick()", invocada
periódicamente por la aplicación principal, hasta llegar a 0. Lo que las reglas hacen es
poner como precondición (además de las condiciones del estado del agente y el
escenario) que la espera sea 0.
A continuación un ejemplo de regla sencilla:
(defrule semaforo-a-rojo
(semaforo (espera ?t&:(eq ?t 0))
(estado ?es&:(eq ?es "AMBAR"))
(OBJECT ?s))
=>
(set ?s espera (get ?s frecuencia))
(set ?s estado "ROJO")
(call ?s redibujar)
)
Esta regla pone un semáforo a rojo cuando le toca. Como se ha dicho, lo
primero que hace es mirar que la espera sea 0. Además mira que el estado anterior sea
ambar, ya que no se puede pasar de verde a rojo ni de rojo a rojo, y guarda en la variable
"?s" la referencia al objeto (como se ha descrito en el apartado de manejo de objetos en
Jess).
Lo que hace esta regla es hacer un "reset" de la espera, cambiar el estado a rojo e
invocar al método "redibujar", que cambia el frame de la imagen del semáforo.
Como se ha mencionado anteriormente, aunque parezca absurdo, es importante
el orden de las operaciones realizadas en la "post" de una regla. Es por ello que lo
primero que se hace es restaurar la espera a su valor inicial. En caso de no hacerlo así,
lo que ocurre es que se activan más reglas, ya que en el intervalo de tiempo entre que la
regla se dispara y la espera es modificada se disparan más reglas para el mismo objeto
porque su espera no es 0.
Por similitud a esto, y más por precaución que por otra cosa, lo primero que se
hace en la precondición de la regla es verificar que la espera es 0. Así, si la espera es lo
primero que se modifica en la postcondición y lo primero que se verifica en la
Implementación no aleatoria
37
precondición, se garantiza que no ocurran "cosas raras" como la mencionada en el
anterior párrafo.
Una regla más complicada sería:
(defrule coche-default-retrocede
(coche (espera ?t&:(eq ?t 0))
(posicion ?pos)
(OBJECT ?c))
(posicion (OBJECT ?pos-enfrente&:(and
(call ?c estaEnfrente ?pos-enfrente)
(not (call ?c esPermitida ?pos-enfrente)))))
(posicion (OBJECT ?pos-libre&:(and
(call ?c estaDetras ?pos-libre)
(call ?c esPermitida ?pos-libre))))
(not (posicion (OBJECT ?pos-lateral&:(and
(call ?c estaAlLado ?pos-lateral)
(call ?c esPermitida ?pos-lateral)))))
=>
(set ?c espera (get ?c frecuencia))
(set ?c posicion ?pos)
(set ?c estado "ANDANDO")
(set ?c orientacion
(call ?c calculaOrientacion ?pos ?pos-libre))
(set ?c currentframe (call ?c getOrientacion))
)
En la precondición busca un coche que tenga espera 0, cuya posición de enfrente
no sea permitida, que tenga una posición detrás permitida (lógico si ha llegado desde
ella) y que no tenga posiciones a izquierda o a derecha permitidas para él.
Evidentemente la postcondición lo que hace es restaurar la espera inicial y
reorientar el coche hacia esa posición de detrás, que es la única hacia la que puede
moverse. Además mantiene la misma posición (ya que no anda, gira), el estado pasa a
ser "ANDANDO" (ya que el agente está en movimiento), y se cambia el frame de la
imagen del agente en función de la nueva orientación.
Al final del documento se anexan todos los ficheros de reglas con resaltado de
sintáxis para mayor facilidad de lectura.
Color de los agentes
El color de los agentes viene especificado en el fichero de texto que define el
escenario de simulación. En dicho fichero viene definido, para cada agente su tipo y su
situación. Lo que hacemos es añadir a la línea en la que viene esa información, un
campo más con el color del agente.
Fragmento del fichero antes (cada fila tiene [x, y, tipo]):
NUMERO_AGENTES 3
3 2 0
3 7 1
7 9 0
Implementación no aleatoria
38
Fragmento del fichero ahora (cada fila tiene [x, y, tipo, color]):
NUMERO_AGENTES 3
3 2 0 amarillo
3 7 1 verde
7 9 0 rojo
A la hora de generar el escenario y crear los agentes (como se menciona en
CarreteraAutoCarga) se tiene en cuenta este dato para parsear el fichero de definición
del escenario e invocar a los métodos de creación. Además, como también se menciona
en las respectivas clases, los constructores de Persona, Coche y Semáforo manejan de
manera adecuada el parámetro "color".
Esta modificación en principio se hizo sólo para observar mejor el
comportamiento de los agentes, ya que con agentes del mismo color había dificultad
para saber si se cruzaban o se daban la vuelta, pero quedó de lo más vistoso. En
cualquier caso sólo se ha implementado para la clase Coche y la aplicación de
construcción de escenarios no está preparada para generar los ficheros de escenario con
el campo "color", por lo que habría que pulir la aplicación en ese aspecto para que su
funcionamiento fuera completo y correcto.
En caso de encontrar esta modificación inútil, molesta o costosa de acabar no
habría ningún inconveniente en deshacerla o rehacerla de nuevo. Aun así, como se ha
dicho antes, es de gran ayuda para la identificación de los agentes del mismo tipo
durante la simulación.
Applet
Se ha intentado hacer un Applet con la aplicación.
En teoría la clase "Gamelet" de la que descienden "Simulador" y
"CarreteraAutoCarga" estaba preparada para ser un Applet. Lo que se intentó fue hacer
que en vez de que Gamelet descienda de JPanel, descienda de JApplet.
Con esto surgen problemas de seguridad, ya que la aplicación maneja ficheros y
accede a propiedades del sistema que a un Applet le están prohibidas. Esto se soluciona
en parte haciendo que en vez de Gamelet al sistema para encontrar el directorio actual,
el atributo que lo almacena dentro de Gamelet sea la cadena "./" (ver en el fuente de
Gamelet.java la variable o atributo "currentPath").
Si se hacen estas dos modificaciones el applet funciona con el visor
"appletviewer". El problema viene cuando se cuelga de un servidor, ya que entonces los
archivos han de accederse vía protocolo HTTP, con lo que las rutas de los ficheros son
URL's, y las clases de manejo de ficheros utilizadas en la aplicación no admiten URL's
como parámetro.
Implementación no aleatoria
39
No obstante es posible utilizar URL's, ya que utiliza la clase BufferedReader
para realizar la lectura y si se construye previamente un objeto de la clase URL, puede
crearse un BufferedReader a partir de la URL. El código podría ser algo así:
URL urlFichero = new URL(nombreFicheroEscenario);
BufferedReader ficheroEntrada =
new BufferedReader(new InputStreamReader(urlFichero.openStream()));
Lo que ocurre es que la identificación de todos los casos en los que se hace una
lectura del fichero o de una imagen puede no ser trivial y llevar más tiempo del en
principio necesario.
Además existe el problema de que lo que hace la aplicación es crear un JFrame,
cosa que desde un Applet ejecutado en un navegador puede dar problemas, y se intentó
cambiar pero no funcionaba correctamente.
Aplicación aleatoria (1 motor global)
40
Aplicación aleatoria (1 motor global)
Objetivo
Dado que la planificación de Jess es determinista y que lo que se desea obtener
son agentes que "deambulen" por el escenario sin rumbo fijo, hay que inventar algún
mecanismo para que su movimiento sea realmente aleatorio. Aunque la forma de
escribir las reglas en la aplicación predecesora sea la más intuitiva a priori, habrá que
reescribirlas para que, aun de manera menos sencilla y lógica, se implemente un
movimiento aleatorio.
Los cambios realizados en la aplicación afectan fundamentalmente a la escritura
de las reglas. También hay modificaciones en el código Java, pero siempre relacionadas
con el manejo del API de Jess, para definición de hechos o instancias.
Clases modificadas

CarreteraAutocarga
CarreteraAutoCarga
Al ser la clase principal en la que se instancia el motor Jess y se definen los
hechos e instancias, es lógico que en ella se hagan las principales modificaciones con
respecto a la versión anterior. De hecho, es la única clase modificada.
El mecanismo implementado para hacer que el movimiento de los agentes sea
aleatorio se basa en la aserción de hechos "posiciones", que contienen las posiciones
accesibles por un agente en un momento dado, de las cuales se elegirá una al azar. Cada
agente tendrá un hecho posiciones asociado, por lo que hay que hacerlo en tiempo de
creación de los agentes, y esa tarea se hace en esta clase.
Concretamente se modifica el método "creaAgentePorNombre()", con el que se
crea cada agente. Una vez creado el agente e instanciado en el motor Jess, se
instanciará un hecho "posiciones" para ese agente. Hay que decir que esto podría
haberse intentado hacer en Jess, escribiendo una regla inicial que para todo objeto de la
clase "agente" se instanciara un hecho "posiciones" de la misma manera, pero se optó
por la solución de resultados más inmediatos, que es la modificación del código Java.
La modificación concreta es la siguiente:
...
//-CÓDIGO YA EXISTENTE-
Aplicación aleatoria (1 motor global)
if (nombre.equals("coche"))
41
{
Coche coc = new Coche(padre, nombre, color);
coc.setPosicion(posicion);
f = new Funcall("definstance", rete);
f.add(new Value("coche", RU.ATOM));
f.add(new Value(coc));
f.execute(rete.getGlobalContext());
//-CÓDIGO NUEVOFact hecho = new Fact("posiciones",rete);
hecho.setSlotValue("agente", new Value(coc));
rete.assert(hecho);
//-FIN CÓDIGO NUEVO//-CÓDIGO YA EXISTENTEreturn (coc);
}
if (nombre.equals("persona")){
Persona per = new Persona(padre, nombre);
per.setPosicion(posicion);
f = new Funcall("definstance", rete);
f.add(new Value("persona", RU.ATOM));
f.add(new Value(per));
f.execute(rete.getGlobalContext());
//-CÓDIGO NUEVOFact hecho = new Fact("posiciones",rete);
hecho.setSlotValue("agente", new Value(per));
rete.assert(hecho);
//-FIN CÓDIGO NUEVO//-CÓDIGO YA EXISTENTEreturn (per);
}
...
Fichero de Reglas
Las principales modificaciones de esta versión recaen sobre el fichero de reglas.
Se cambia casi completamente la estructura y el funcionamiento de este archivo.
Descripción del mecanismo
Lo más significativo es la elección aleatoria de la posición hacia la cual mover.
Para esto, como se ha indicado antes, lo que se hace es buscar todas las posiciones
adyacentes accesibles y tras ello elegir una de ellas al azar e iniciar el movimiento hacia
ella.
Aplicación aleatoria (1 motor global)
42
Para esto se define un hecho "posiciones" por cada agente que existe. En este
hecho cada posición encontrada a la que el agente pueda mover es añadida. Se evitará
que el agente retroceda siempre que no sea necesario, para conseguir un movimiento
medianamente lógico en el que el agente mueva de manera aleatoria pero siempre
avanzando.
Búsqueda de las posiciones
Las posiciones a las que un agente puede mover se encontrarán mediante reglas
y serán añadidas al hecho "posiciones" correspondiente. Veamos las reglas.
(defrule persona-default-gira
(persona (espera ?t&:(eq ?t 0))
(posicion ?pos)
(OBJECT ?per))
(posicion (OBJECT ?pos-libre&:(and
(call ?per esAdyacente ?pos-libre)
(not (call ?per estaDetras ?pos-libre))
(call ?per esPermitida ?pos-libre))))
(not (persona (posicion ?pos-persona&:(eq ?pos-persona
?pos-libre))))
?pos-posibles <- (posiciones
(agente ?a&:(call ?a equals ?per))
(posibles $?p&:(not (member$ ?pos-libre $?p))))
(idle)
=>
(printout t "regla persona-default-gira ejecutada" crlf)
(modify ?pos-posibles (posibles $?p ?pos-libre))
)
Esta regla modela la búsqueda de posiciones en las que un agente persona pueda
mover. Lo que hace es buscar las posiciones adyacentes (excepto la de detrás) en las
que pueda mover y en las que además no haya ninguna persona (ya que no puede haber
dos personas en el mismo sitio).
Comprueba que la posición no ha sido añadida con anterioridad (para no repetir
posiciones) y, si todo lo anterior se cumple, añade al hecho "posibles" perteneciente al
agente en cuestión y referenciado por "?pos-posibles" la nueva posición encontrada.
Hay otra regla que se encarga de añadir la posición de detrás cuando no se
cumple esta regla para ninguna posición, es decir, cuando al agente no le queda más
remedio que retroceder.
Elección de la posición
Una vez encontradas todas las posiciones hay que elegir una hacia la que
dirigirse. El movimiento se hace en dos pasos. El agente en primer lugar gira hacia la
posición elegida y después, si aún sigue libre, se desplaza dentro de ella. Para esto hay
dos reglas fundamentales, una para girar y otra para mover.
Aplicación aleatoria (1 motor global)
43
Regla para girar.
(defrule persona-gira
(persona (espera ?t&:(eq ?t 0))
(estado ?e&:(not (eq ?e "GIRANDO")))
(posicion ?pos)
(OBJECT ?per))
?pos-posibles <- (posiciones
(agente ?a&:(call ?a equals ?per))
(posibles $?p&:(not (eq 0 (length$ $?p)))))
=>
(set ?per espera (get ?per frecuencia))
(set ?per posicion ?pos)
(set ?per estado "GIRANDO")
(set ?per orientacion
(call ?per calculaOrientacion ?pos (eligePos (length$ $?p) $?p)))
(set ?per currentframe (get ?per orientacion))
(retract ?pos-posibles)
(assert (posiciones (agente ?a)))
(printout t "regla persona-gira ejecutada" crlf)
)
Esta regla hace que un agente persona gire. Obtiene un agente que no esté
girando con su hecho "posiciones" asociado y efectúa la elección y el giro. Lo que hace
es cambiar la orientación del agente dirigiéndolo hacia la posición elegida al azar,
cambiar el estado a "GIRANDO" (para garantizar que no haga más de un giro), eliminar
el hecho "posiciones" del que se ha elegido y crear uno nuevo vacío.
Para la elección de la posición aleatoriamente utiliza la siguiente función:
(deffunction eligePos (?maximo $?lista )
(bind ?ran (new Random))
(bind ?nrandom (+ (call ?ran nextInt ?maximo) 1))
(return (nth$ ?nrandom $?lista))
)
Movimiento
Regla para mover:
(defrule persona-mueve
(persona (espera ?t&:(eq ?t 0))
(estado ?e&:(eq ?e "GIRANDO"))
(posicion ?pos)
(OBJECT ?per))
?pos-posibles <- (posiciones
(agente ?a&:(call ?a equals ?per))
(posibles $?p&:(not (eq 0 (length$ $?p)))))
(posicion (OBJECT ?pos-enfrente&:(and
(call ?per estaEnfrente ?pos-enfrente)
(member$ ?pos-enfrente $?p))))
(not (persona (posicion ?pos-persona&:(eq ?pos-persona
?pos-enfrente))))
=>
(set ?per espera (get ?per frecuencia))
(set ?per posicion ?pos-enfrente)
Aplicación aleatoria (1 motor global)
44
(set ?per estado "ANDANDO")
(set ?per currentframe (get ?per orientacion))
(retract ?pos-posibles)
(assert (posiciones (agente ?a)))
(printout t "regla persona-mueve ejecutada" crlf)
)
Esta regla describe el comportamiento de la persona cuando gira. Lo que hace
es buscar un agente persona que está en estado "GIRANDO", su hecho "posiciones"
asociado (se han recalculado las posiciones para ver si todavía puede mover donde
quería), y busca la posición que está enfrente, mirando si hay alguna persona que haya
ocupado esa posición.
Si todo va bien mueve a esa posición, cambia el estado a "ANDANDO" para que
el agente vuelva a girar en el próximo turno y borra y crea de nuevo su hecho
"posiciones".
Existen otras dos reglas (reglas "no-puede") que lo que hacen es que cuando el
agente bien girando o andando no pueda seguir normalmente con su movimiento,
vuelvan a recalcular todo de nuevo para iniciar una nueva elección.
Módulo RESTRICCIONES
Para el correcto funcionamiento de esta versión es necesaria la utilización de, al
menos, un módulo. Este módulo es el módulo RESTRICCIONES, que contendrá reglas
para las situaciones en las que un agente esté obligado a parar por interacción con otro
agente (semáforo, peatón-coche, ...), para proseguir su movimiento normalmente tras
detenerse.
Para utilizar módulos hay que usar la versión 6.0 de Jess, que es la única que los
admite. Su uso se explica en el apartado de Jess.
Programaremos las reglas con "auto-focus" para que en cuanto se produzca una
situación descrita en este módulo se active inmediatamente con prioridad sobre las otras
e impida avanzar al agente hasta que la regla se desactive.
Ejemplo:
(defmodule RESTRICCIONES)
...
(defrule RESTRICCIONES::persona-coche
(declare (auto-focus TRUE))
(persona (espera ?t&:(eq ?t 0))
(posicion ?pos)
(OBJECT ?per))
(coche (posicion ?pos-coche&:(call ?per estaEnfrente ?pos-coche)))
(posicion (OBJECT ?pos-enfrente&:(and
(call ?per estaEnfrente ?pos-enfrente)
(call ?per esPermitida ?pos-enfrente))))
=>
(set ?per espera (get ?per frecuencia))
Aplicación aleatoria (1 motor global)
(set ?per
(set ?per
(set ?per
(printout
45
posicion ?pos)
estado "GIRANDO")
currentframe (call ?per getOrientacion))
t "regla persona-coche ejecutada" crlf)
)
Esta regla modela el comportamiento de cuando una persona se encuentra con
un coche en la posición a la que quiere ir. Lo que hace la persona es esperar a que ese
coche pase para continuar con su movimiento. En un principio se pasaba a estado
"PARADO", pero esto ocasionaba que cuando el coche pasaba (o lo que fuera) el agente
reiniciaba su movimiento y podía cambiar de orientación en vez de seguir con lo que
estaba haciendo. Esto carecía de sentido, por lo que finalmente pone "GIRANDO" para
que cuando el agente reanude su movimiento avance a la posición hacia la que esta
orientado.
Aplicación aleatoria (1 motor/agente)
46
Aplicación aleatoria (1 motor/agente)
Objetivo
Conseguido el funcionamiento aleatorio la meta de esta nueva versión de la
aplicación es conseguir que cada agente tenga su propio motor Jess y hacer que todos
funcionen concurrentemente colaborando e interactuando entre sí.
Esta versión es muy inestable. Lo que se ha hecho es programar de manera
similar a la versión anterior, pero haciendo un archivo de reglas para cada tipo de agente
e instanciando un thread Motor para cada objeto agente, sea del tipo que sea.
Sin embargo, partiendo de este planteamiento que parece evidente, no se ha
conseguido que la versión funcione de manera coherente, ya que el comportamiento de
los agentes es imprevisible y desobedecen sus reglas a menudo debido a problemas que
no se han conseguido identificar.
Clases modificadas



Agente
CarreteraAutoCarga
Motor
Agente
A la clase Agente se le añade un atributo de clase Rete que será el motor de
inferencia propio del agente. El código añadido es el siguiente:
public Rete rete = new Rete();
CarreteraAutoCarga
Al haber ahora un motor por cada objeto, la creación e instanciación de los
objetos y hechos en cada uno de los motores cambia.
Creación de agentes
Aplicación aleatoria (1 motor/agente)
47
En primer lugar la creación de los agentes cambia. Ahora tenemos un archivo de
reglas para cada tipo de agente.
Veamos un trozo de código del método
"creaAgentePorNombre()":
...
//creamos el semáforo.
Semaforo sem = new Semaforo(padre, nombre);
//le damos su posición.
sem.setPosicion(posicion);
//cargamos el fichero de jess con las reglas del semáforo.
sem.rete.executeCommand("(batch \"" + this.currentPath() + "reglas_"
+ sem.getTipo().toLowerCase()
+ ".jess\")");
//asignamos a la variable ?*this* en JESS el puntero al objeto.
Defglobal global = new Defglobal("*this*", new Value(sem));
sem.rete.addDefglobal(global);
return (sem);
...
Ahora es diferente. Creamos el objeto semáforo, asignamos su posición, pero
ahora cargamos el archivo de reglas propio para los semáforos y definimos una variable
global en Jess llamada "*this*". Esta variable global contiene una referencia al objeto.
El motor de cada objeto tendrá una instancia de esta variable, que será utilizada para
identificar al objeto "dueño" del motor.
El comportamiento atípico de esta versión puede estar relacionado con el uso de
esta variable, ya que a veces da la impresión de que un agente responde a órdenes de un
motor que no es el suyo. En todo caso la gestión y compartición de memoria en Jess
seguramente no será tan trivial como se ha pensado a la hora de programar esta versión
multi-thread.
Instanciación de agentes y posiciones
Ahora cada agente tiene su propio motor, pero aun así tendrá que tener
constancia de que existen todas las posiciones del escenario y todos los agentes del
escenario. Esto implica la instanciación de agentes y posiciones para todos los motores
existentes (uno por agente) excepto los de los semáforos, que no necesitan nada de esto.
Aquí puede haber otra fuente de fallos, ya que, como se ha dicho antes, la
compartición de memoria no debe de ser trivial en estos casos y se ha tratado como tal.
Al tener todos los motores constancia de todos los objetos, como tienen que hacer
llamadas todos entre sí, algún problema tiene que haber, y puede que esto sea la
principal fuente de problemas de esta versión.
Fragmento del código de instanciación:
...
//Instanciación de todos los agentes
for (int j = 0; j < this.agentes.size(); j++)
Aplicación aleatoria (1 motor/agente)
48
{
try {
Agente agente_j = (Agente) agentes.elementAt(j);
Funcall fun = new Funcall("definstance", agente_i.rete);
fun.add(new Value(agente_j.getTipo().toLowerCase(), RU.ATOM));
fun.add(new Value(agente_j));
fun.execute(agente_i.rete.getGlobalContext());
}
catch (JessException jeex) { ... }
}
//Instanciación de todas las posiciones
try {
for (int y = 0; y < casillasAlto; y++) {
for (int x = 0; x < casillasAncho; x++)
{
Funcall fun = new Funcall("definstance", agente_i.rete);
fun.add(new Value("posicion", RU.ATOM));
fun.add(new Value(mundo.posicion (x,y)));
fun.execute(agente_i.rete.getGlobalContext());
}
}
}
catch (JessException jeji){ ... }
...
Creación de los motores
Una vez instanciado todo y creado todo, con cada objeto Rete de cada agente se
crea un nuevo objeto Motor. Aquí está el código:
...
//Dentro del bucle de 1 a agentes.size()
...
motores.addElement (new Motor (agente_i.rete));
...
Motor
El comportamiento de la aplicación no es el esperado, y entre otras cosas hace
que los motores de inferencia acaben su ejecución (lo cual no tiene mucho sentido dada
la existencia del "idle-fact"). Para apañar esto se añade un bucle infinito en la ejecución
del thread de Motor:
rete.reset();
while (true) {
Aplicación aleatoria (1 motor/agente)
49
rete.run();
}
Fichero de reglas
Como se ha mencionado anteriormente, ahora hay un fichero de reglas por cada
tipo de agente. Además cada motor consta de varios módulos para asegurar que el
disparo de las reglas de movimiento se hace en el orden adecuado.
Individualización del motor
Cada motor ha de saber a quién pertenece (a qué agente). Para esto se utiliza
una variable global "*this*", como se ha mencionado anteriormente.
Esta variable apunta a una instancia del agente que es el "propietario" del motor
Jess. A la hora de ejecutar las reglas, cuando tenga que estar seguro de que el objeto es
el agente propietario, verificará si es el contenido en la variable "*this*".
Ejemplo:
(defrule GIRA::coche-default-gira
(coche (espera ?t&:(eq ?t 0))
(posicion ?pos)
(OBJECT ?c&:(eq ?c ?*this*)))
(posicion (OBJECT ?pos-libre&:(and
(call ?c esAdyacente ?pos-libre)
(not (call ?c estaDetras ?pos-libre))
(call ?c esPermitida ?pos-libre))))
(not (coche (posicion ?pos-coche&:(eq ?pos-coche ?pos-libre))))
?pos-posibles <- (posiciones (posibles $?p&:(not
(member$ ?pos-libre $?p))))
=>
(modify ?pos-posibles (posibles $?p ?pos-libre))
(printout t "regla coche-default-gira ejecutada "
(get ?c color) crlf)
)
Esta regla es equivalente a la coche-default-gira de la versión anterior. En esta
comprueba que el objeto del coche referenciado por "?c" es igual al referenciado por
"*this*" lo primero de todo (o casi).
Módulos
En esta versión los archivos de reglas constan de varios módulos.
Aprovechando que la versión 6.0 de Jess los admite, y que ahora el motor de cada
agente sigue su curso de ejecución sin tener en cuenta a los demás motores, se ha hecho
un planteamiento algo más estructurado que el anterior.
Aplicación aleatoria (1 motor/agente)
50
De este modo quedan 4 módulos: GIRA, RETROCEDE, MUEVE y
RESTRICCIONES (además de MAIN). Los 3 primeros módulos se apilan para que se
ejecuten en ese orden (de hecho MAIN no hace nada en el movimiento más que apilar
los módulos). Así se garantiza que el agente primero intenta girar, si no ha podido girar
retrocederá y cuando haya hecho una de las dos cosas anteriores moverá donde
corresponda. Mientras tanto, el módulo RESTRICCIONES, programado con autofocus, puede activarse en cualquier momento, como en la versión anterior.
Veamos un ejemplo de cómo se apilan los módulos:
(defrule MAIN::inicia-movimiento
(coche (espera ?t&:(eq ?t 0))
(OBJECT ?c&:(eq ?c ?*this*)))
=>
(focus MUEVE)
(focus RETROCEDE)
(focus GIRA)
(printout t "regla inicia-movimiento " (get ?c color) crlf)
)
Esta regla apila los módulos para un agente coche. Lo que hace es que cuando
encuentre a su propietario, apile los motores. La precondición es algo absurda, pero es
para garantizar dos cosas: que siempre se ejecute y que se ejecute sólo una vez. Sin
embargo puede que no fuera necesaria, ya que cuando se apilan los módulos, hasta que
no se han ejecutado los 3 módulos apilados no puede volver a ejecutarse la regla de
apilado.
El resto de reglas que definen el movimiento y las restricciones vienen a ser
como las de la versión anterior, pero distribuídas en módulos. Al final del documento
pueden verse los archivos de reglas.
Aplicación aleatoria (1 motor/agente)
51
Valoración del trabajo
Problemas encontrados
El principal problema de este trabajo, como en muchas otros, es el tener como
punto de partida un trabajo anterior y ya no tener que ampliarlo, sino modificar su
programación. Esto implica tener que entender la forma de programar del autor de la
práctica de partida, así como su estilo de escritura de código (espacios en blanco, saltos
de línea, nombres de variables y clases, etc.).
No obstante con estas cosas siempre se aprende, y en nuestra vida de
informáticos tendremos que enfrentarnos a muchas situaciones peores que esta, en la
que se parta de una aplicación que puede tener una codificación y metodología de
programación horrible y además no estar documentada y tengamos que hacer algo
estructurado y elegante.
En nuestro caso hemos de decir que al ser una aplicación "de tercera mano", el
código estaba ya bastante mareado y "parcheado", y hay cosas, como por ejemplo hacer
que la aplicación sea un Applet, que si desde un principio se hubieran planteado así no
habrían supuesto ningún problema, pero en la situación actual resultan extremadamente
costosas.
Otro problema bastante famoso en los trabajos de los compañeros es la
utilización de Jess. Se ha visto que Jess es mucho menos rápido que CLIPS, que CLIPS
es mucho más eficiente. Nosotros además añadimos que Jess es "raro". Es raro porque
tiene un comportamiento a veces atípico en cuándo y en qué orden se activan o disparan
las reglas, en la ejecución "no atómica" de la postcondición de las reglas, ...
La gran ventaja de Jess para este trabajo es el manejo de objetos de Java como si
fueran hechos. También es cierto que la mayor parte de los problemas que hemos
encontrado en Jess parecen tener relación con el hecho de manejar dichos objetos Java.
En cualquier caso, la utilidad de Jess para estas tareas es magnífica y quizás una cada
vez mejor comprensión de los mecanismos de reglas, hechos y objetos de Jess subsane
gran parte de los problemas encontrados.
Cosas inacabadas
Como se ha mencionado ya, hay cosas inacabadas.
La aplicación en este momento puede ejecutarse como Applet y como aplicación
normal. Lo que ocurre es que como Applet sólo puede ejecutarse localmente (con
appletviewer) ya que el acceso que se hace a los ficheros desde Java lo hace al
Aplicación aleatoria (1 motor/agente)
52
directorio actual, sin tener en cuenta la URL en la que pueda encontrarse el Applet en
un servidor web. Esto puede modificarse (todo es posible en Java, por supuesto) y no es
demasiado complicado, pero hay tantos accesos a ficheros en la aplicación que
identificar cada caso resultaba demasiado costoso y no ha habido tiempo de ello. En el
apartado de la "Aplicación no aleatoria", del "Applet" habla más de ello.
Además queda a mitad de hacer lo de los colores de los agentes. Actualmente
está hecho sólo para la clase coche, y el constructor de escenarios no lo tiene en cuenta.
Igualmente en el apartado de "Aplicación no aleatoria", de "Color de los agentes" habla
más del tema.
Por supuesto queda pendiente "arreglar" la versión con threads. La versión hace
que los agentes se muevan, pero hay agentes que se quedan bloqueados pasado un cierto
tiempo, y además no respetan su comportamiento tanto como debieran. Habría que
mirar estas cosas y buscar posibles causas. Como se ha dicho antes, conforme se vaya
conociendo más acerca del funcionamiento de Jess, y conforme Jess se vaya mejorando
y consolidando, teniendo un comportamiento más estable, se sacará un mejor partido a
lo que Jess porporciona y tal vez se arreglen estos problemas, incluso replanteando la
programación inicial.
Posibles ampliaciones
En la presentación hecha en clase se mencionaban mejoras: ampliación del
campo de visión de los agentes, mejora de iconos de agentes, nuevos agentes y
comportamientos y comportamientos complejos.
Todo esto, además de acabar las "cosas inacabadas" puede hacerse.
Hay que decir aquí que la aplicación en cada iteración y versión es más
enrevesada y complicada, ya que el código se va parcheando y ensuciando. Lo que
haría falta es tal vez replantear el problema de nuevo y hacerlo bien estructurado,
teniendo en cuenta todo lo que se ha ido teniendo en cuenta poco a poco para enfocarlo
a ello desde el principio, pero tal vez fuera un trabajo demasiado costoso.
Valoración personal
Como se ha mencionado antes se ha encontrado el problema de la reutilización
de código, que impide hacer las cosas todo lo bien que se quisiera. No obstante es
cierto que también es una experiencia y se aprenden cosas en las que uno no habría
pensado si no las hubiera visto, tanto en el contenido del programa como en la forma.
La utilización de Jess en el trabajo ha resultado de lo más interesante. Si bien
Jess plantea una serie de problemas anteriormente mencionados, aporta comodidades a
la hora de programar con reglas como el manejo del motor Jess desde Java con el API
que proporciona y, sobretodo, el manejo de objetos Java. Esto seguramente podrá
hacerse con CLIPS y CORBA o algún mecanismo para ello, pero Jess está programado
Aplicación aleatoria (1 motor/agente)
53
para utilizar objetos Java y comunicarse cómodamente. Esta es una capacidad que de
explotarse bien tendría una gran utilidad y de hecho la ha tenido para este trabajo.
Es por esto que la utilización de Jess resulta interesante y ventajosa, y hace del
aprendizaje de Jess sea algo en cierto modo fascinante, lo que hace que el trabajo en sí
sea fascinante. El único inconveniente de Jess es su falta de estabilidad y su extraño
comportamiento a veces, como ya se ha mencionado, que compensa la fascinación por
el trabajo (convirtiéndola a veces en desesperación). Sin embargo no por ello deja de
ser un aprendizaje estimulante e interesante.
En general la realización de este trabajo, quitando los problemas mencionados,
nos ha gustado bastante y nos ha mantenido interesados y en cierto modo apasionados
con los pequeños descubrimientos en Jess que estábamos haciendo, y con lo que se
simplificaba la aplicación de partida. Consideramos que ha sido una experiencia
positiva y recomendable para otros años.
Anexos
Anexos





Reglas no aleatorias
Reglas aleatorias para un solo motor
Reglas aleatorias persona
Reglas aleatorias coche
Reglas aleatorias semáforo
54
Descargar