6.2 Result Analysis of SC MPC Simulations
6.2.1 Constant Load System Performance
En este subapartado vamos a comentar cómo se implementa un cliente que mueva de forma aleatoria a sus unidades y tenga la opción de gráficos en 3D.
Nuestro cliente va a estar formado por tres ficheros:
waypoints_main.c
ClientEventHandler.h
ClientEventHandler.c
El fichero waypoints_main.c contiene la función void add_options(), la función int main(int
argc, char **argv) y la clase Looper.
La función void add_options(), simplemente sirve para incluir las opciones que nuestro cliente va a considerar válidas, en nuestro caso –usegfx para el modo gráfico en 3D.
La función main realiza todas las comprobaciones e inicializaciones necesarias para poder ejecutar el cliente. Entre ellas, comprueba si el modo gráfico está o no activo para realizar las correspondientes gestiones.
Por último, la clase Looper contiene una estructura ClientAIState (definida en el fichero
ClientEventHandler.h), un constructor y el método void loop().
El constructor inicializa el atributo state de tipo ClienteAIState con la estructura
ClientAIState que recibe por parámetros. Dicha estructura se explica en detalle más adelante.
El método loop se va a ejecutar hasta que el juego termine o el cliente finalice (es llamado desde el main). Dicho método busca mensajes entrantes procedentes del servidor, si los hay llama a los handlers y si no se duerme un milisegundo. Además, realiza gestiones del modo gráfico.
El fichero ClientEventHandler.h contiene la definición de la estructura ClientAIState y la definición de la clase ClientEventHandler.
Sergio Núñez Covarrubias Anexo C. Tutorial de iniciación a ORTS
BlackRose: Un modelo de razonamiento
con incertidumbre en juegos de estrategia 176
La estructura ClientAIState contiene la información necesaria para conocer el estado actual del juego. Los elementos que forman dicha estructura son los siguientes:
gsm es un puntero a GameStateModule, gracias al cual podemos conocer información relativa al estado del juego.
gfxm es un puntero a GfxModule, gracias al cual se actualiza la imagen gráfica del juego. Si el modo gráfico 3D no está activado, gfxm vale 0.
just_drew es una variable de tipo bool que indica si hay que dibujar en el instante actual.
quit es una variable de tipo bool que indica si hay que salir o no del juego.
error es una variable de tipo bool que indica si se ha producido o no un error.
el constructor inicializa los punteros gsm y gfxm con los parámetros que recibe, además de inicializar just_drew, quit y error a false.
La clase ClientEventHandler contiene un constructor, una referencia a una estructura
ClientAIState, un objeto random y los prototipos de los métodos bool handle_event(const Event &e) y void compute_actions().
El constructor inicializa la referencia a la estructura con el parámetro que recibe.
El fichero ClientEventHandler.c contiene la implementación de los métodos bool
handle_event(const Event &e) y void compute_actions().
El primero recibe eventos y los trata. Diferencia entre tres tipos diferentes:
STOP_MSG indica que hay que salir del juego.
READ_ERROR_MSG indica que se ha producido un error y por ello hay que salir del juego.
VIEW_MSG indica que ha recibido un mensaje vista, lo que quiere decir que se ha actualizado el estado del juego. Por ello, se llama al método void compute_actions(),
Sergio Núñez Covarrubias Anexo C. Tutorial de iniciación a ORTS
BlackRose: Un modelo de razonamiento
con incertidumbre en juegos de estrategia 177
se mandan las acciones al servidor y se realizan unas gestiones de los gráficos, a la vez que se imprimen por pantalla unas estadísticas del estado actual.
El método void compute_actions() (aproximadamente línea 86) es el que determina las acciones de cada una de las unidades del cliente. Por ello, es el único método que va a ser diferente al segundo cliente que vamos a implementar. En este método se concentra toda la inteligencia artificial del cliente.
Nuestro método para el primer cliente es muy sencillo, ya que únicamente mueve a todos sus objetos a un punto elegido de forma aleatoria.
Podríamos decir que el método se divide en dos partes. En la primera, obtenemos la información necesaria sobre el estado actual del juego. Dicha información es la siguiente:
Nuestro id de jugador. Se obtiene mediante la siguiente sentencia:
const sint4 cid = game.get_client_player();
cid es la variable que va a contener el id del jugador después de ejecutar el método get_client_player mediante el objeto game. Dicho objeto es de tipo Game y hay que actualizarlo en cada iteración31 mediante la siguiente sentencia:
const Game &game = state.gsm->get_game();
game es una referencia al propio juego, mediante la cual podemos obtener información relativa al estado del juego (mapa, objetos, etc.). Por otro lado, state es el atributo público de tipo estructura ClientAIState de la clase ClientEventHandler.
Todos nuestros objetos. Se obtienen mediante la siguiente sentencia:
const Game::ObjCont &objs = game.get_objs(cid);
objs es una referencia a un vector de punteros, en el que cada uno apunta a un objeto concreto. cid es el id del jugador del que se quieren obtener todos sus objetos. Al igual que la referencia al juego (game), los objetos hay que obtenerlos
31
Cada vez que se ejecuta el método compute_actions, es decir, cada vez que cambia el estado del juego después de que se
hayan procesado las últimas instrucciones enviadas al servidor.Sergio Núñez Covarrubias Anexo C. Tutorial de iniciación a ORTS
BlackRose: Un modelo de razonamiento
con incertidumbre en juegos de estrategia 178
en cada iteración porque pueden cambiar de una iteración a otra (debido a que se hayan creado nuevos objetos, haya cambiado la información del algún objeto concreto, etc.).
El ancho del mapa, medido en puntos. Se obtiene mediante la siguiente sentencia:
const sint4 points_x = map.get_width() * game.get_tile_points();
points_x contiene el resultado de multiplicar el número de tiles del mapa (a lo
ancho) por el número de puntos/tile. Mediante la referencia al juego, podemos obtener cuantos puntos forman una tile32. Por otro lado, map es una referencia al
mapa actual del juego que se obtiene mediante la siguiente sentencia:
const Map<GameTile> &map = game.get_map();
Por ultimo, el método get_width devuelve el número tiles que forman el ancho del mapa.
El largo del mapa, medido en puntos. Se obtiene mediante la siguiente sentencia:
const sint4 points_y= map.get_height() * game.get_tile_points();
points_y contiene el resultado de multiplicar el número de tiles del mapa (a lo largo)
por el número de puntos/tile. El método get_height devuelve el número tiles que forman el largo del mapa.
Como podemos observar, toda esta información se obtiene gracias a la referencia del juego (game) y como ella se tiene que obtener/actualizar en cada iteración, toda la información con la que está relacionada también se tiene que actualizar en cada una de las iteraciones.
La segunda parte, consta de un bucle for para cada uno de nuestros objetos. Dentro del bucle, comprobamos que el objeto actual exista, esté en juego, dentro del mapa y no esté muerto. Si es así, preguntamos si nuestro objeto está quieto o en movimiento, ya que si está en movimiento hay que dejarle que llegue a su destino. Si está quieto, calculamos unas nuevas coordenadas de
32
Se supone que las tiles son cuadradas y que el método get_tile_points devuelve el número de puntos (a lo largo o a lo
ancho) que forman una tile.Sergio Núñez Covarrubias Anexo C. Tutorial de iniciación a ORTS
BlackRose: Un modelo de razonamiento
con incertidumbre en juegos de estrategia 179
forma aleatoria y le asignamos la acción move. Todo esto se puede ver en el código adjunto, que está detalladamente comentado.