60
Departamento de Sistemas de Información Técnicas de Inteligencia Artificial empleando Mundos Virtuales (este libro se encuentra en construcción) Libro de texto creado para la asignatura de Inteligencia Artificial de las carreras de Ingeniería Civil en Informática e Ingenieria de Ejecución en Computación e Informática Escrito por: Clemente Rubio Manzano Dedicado a: Doctor Pedro Rodríguez Moreno Última Actualización: 23/09/2021

Técnicas de Inteligencia Artificial empleando Mundos

  • Upload
    others

  • View
    4

  • Download
    0

Embed Size (px)

Citation preview

Departamento de Sistemas de Información

Técnicas de Inteligencia Artificial empleando MundosVirtuales (este libro se encuentra en construcción)

Libro de texto creado para la asignatura de InteligenciaArtificial de las carreras de Ingeniería Civil en Informática e

Ingenieria de Ejecución en Computación e Informática

Escrito por:Clemente Rubio Manzano

Dedicado a:Doctor Pedro Rodríguez Moreno †

Última Actualización: 23/09/2021

Prólogo

La inteligencia es considerada, por algunos científicos, el gran logro de laevolución. Pues bien, si esto es así, entonces deberían haber existido muchasmás especies inteligentes sobre el planeta Tierra. Pero, por lo que sabemos,somos los únicos. Los dinosaurios, que estuvieron en el planeta durante másde doscientos millones de años, no llegaron a ser inteligentes.

Los humanos modernos, han estado en la Tierra aproximadamente cientode miles de años, una pequeña fracción de la edad de la Tierra, que se estimaen 4.5 billones de años. Por tanto podríamos sacar una primera conclusión,la inteligencia no es realmente necesaria para la vida. La madre naturalezase las ha ingeniado bastante bien sin criaturas inteligentes.

Pero, ¿cómo nos hicimos inteligentes?, ¿qué nos diferencia de los anima-les?, hay tres ingredientes básicos. El primer ingrediente es el pulgar oponi-ble, nos permite manipular el entorno. Así que, éste es uno de los ingredien-tes secretos, ser capaces de cambiar el mundo que nos rodea. El segundoingrediente es la visión, pero no cualquier visión, una visión de depredador.Tenemos ojos en la frontal de nuestra cabeza, no a los lados y ¿por qué?,porque los animales con ojos en la parte frontal son depredadores (leones,tigres, zorros). Los animales con ojos a los lados son presas y no son inte-ligentes, como por ejemplo un conejo. Un depredador tiene que aprender acazar y acechar, aprender a predecir los movimientos de la presa, si no, nopodrá comer. Si tú eres un conejo lo único que debes hacer es correr. El ter-cer ingrediente es el lenguaje, mediante él puedes comunicarte para que tuconocimiento adquirido durante tu vida pase de generación en generación ypor lo que sabemos los animales aunque se comunican no permiten la trans-misión de conocimiento, salvo algunas señales primitivas. Los animales notienen cultura, en el sentido más amplio de la palabra, nosotros sí y somoscapaces de transmitirla de forma eficaz.

Así se supone que el cerebro evolucionó. Tenemos pulgar oponible, tene-mos un lenguaje de entre cinco a diez mil palabras y tenemos la visión de undepredador. Por tanto, quizás la razón por la que ninguna criatura consiguióser inteligente es porque no cuenta con estos tres ingredientes.

Entonces, la siguiente pregunta está relacionada sobre la creación de se-res inteligentes. ¿Podemos hacer que un chimpancé sea inteligente comoocurrió en el planeta de los simios?. Pues lo creas o no, la respuesta podríaser que sí. Somos iguales a ellos en un 98.5 por ciento. Por tanto, en el futu-ro mediante una terapia genética podríamos crear seres inteligentes. Pero lapregunta y la controversia, por qué manipular un chimpancé para que seamás y más humano, si ya conocemos el resultado, nosotros.

Michio Kaku.

Estructura y organización de este libro

Este libro está dividido en capítulo y trata de ir desde los conceptos mássencillos a los más complejos. Se sigue una filosofía de construcción ex-plicando paso a paso cada uno de los proyectos desarrollados empleandotécnicas de inteligencia artificial sobre mundo virtuales. La organización escomo sigue:

El capítulo 1 trata de ser una introducción a la IA. Se revisan sus orige-nes, primeros autores, disciplinas más importantes. Además se explicade forma detallada la metodología de enseñanza aprendizaje que se uti-lizará y la importancia de los mundos virtuales en la actualidad.

El Capítulo 2 quiere servir como una introducción o repaso al lenguajeJava. También se presenta un problema que ayuda a introducirse en eltema de los mundos virtuales.

En el Capítulo 3 se explica la construcción paso a paso de un mun-do virtual con una entidad. También se explica los eventos de entra-da/salida y como se pueden utilizar para mover nuestra primera enti-dad.

En el Capítulo 4 se incorporan nuevas entidades (paredes, adversariosy celdas especiales) y se explica la interacción entre ellas. Aquí tambiénse explica cómo se pueden usar los hilos de ejecución para poder dotarde movimientos a las entidades.

Capítulo 1.El origen de la InteligenciaArtifical, sus caminos ycómo estudiarla

Clemente Rubio Manzano ([email protected])

Departamento de Sistemas de InformaciónUniversidad del Bío-Bío

Origen

El origen la Inteligencia Artificial (IA) ha suscitado mucha atención porparte de la comunidad científica desde sus comienzos ya que está directa-mente relacionada con el origen de la inteligencia humana y, al mismo tiem-po, con el origen de la vida inteligente en nuestro planeta (tema de investiga-ción de alto impacto siempre en nuestra sociedad). Tenemos que distinguirentre dos origenes diferentes pero que están estrechamente relacionados:i) el origen de la IA como concepto; ii) el origen de la IA como disciplinacientífica. El primero, se podría datar en el siglo III a.C cuando aparece porprimera en un texto en el que se relata el primer encuentro de un rey con“un artifie automático mecánico” (“una figura humana de tamaño natural” ).Mucho más tarde, en la época del escritor Assimov hace famoso el conceptode Robot y las tres leyes de la robótica (leáse por ejemplo su libro “Yo, Robot”)(Fuente concultada: https://es.wikipedia.org/wiki/Robótica).

Posteriormente, aparecieron los primeros trabajos científicos que senta-ron las bases de lo que hoy se conoce como IA (la disciplina científica). Unode los primeros artículos fue escrito por Warrern McCulloch y Walter Pittsen 1943 y fue titulado “A logical calculus of the ideas immanent in nervousactivity. Bulletin of Mathematical Biophysics, 5:115?133” [1] y propone unaunidad de cálculo, precursora de las redes neuronales, que intenta simularel funcionamiento de las neuronas del cerebro. Otro trabajo imprescindiblefue escrito por Alan Turing en 1950 y fue titulado “Computing Machineryand Intelligence” [2]. Es un ensayo que plantea la idea sobre si las máquinas

1

2

pueden llegar a pensar. Aquí aparece el famoso Test Turing. Recientemente,un grupo de científicos aseguraron haber creado un sistema que pasaba di-cho test. La noticia decía: “Un ordenador logra superar por primera vez el testde Turing (lo hace con ciertas restricciones) 29 % de los jueces pensó que erahumano Turing predijo que en 2010, el 30 % podría ser engañada”.

Inevitablemente la IA-disciplina se le ha “exigido” alcanzar los pronósti-cos de la IA conceptual, es decir, conseguir máquinas que se comporten comolos hombres. Este deseo ha quedado reflejado en las múltiples produccionescinematográficas de ciencia ficción: Terminator, Her, Ex-Machina o InterSte-llar, entre otras. Un buen ejercicio para saber cuál son los alcances de la IA esmirar la definicón de Inteligencia en el dicccionario y analizar cuáles son lascualidades de inteligencia que los actuales sistemas cumplen. La definiciónde Inteligencia es la siguiente:

1. f. Capacidad de entender o comprender.2. f. Capacidad de resolver problemas.3. f. Conocimiento, comprensión, acto de entender.4. f. Sentido en que se puede tomar una proposición,un dicho o una expresión.5. f. Habilidad, destreza y experiencia.

Ahora podemos ver si los sistemas IA cumplen alguna de estas definicio-nes:

1. Capacidad de entender o comprender. Creo que actualmente no hayningún sistema de IA que sea capaz realmente de entender o compren-der. Es cierto que se ha creado una IA que ha sido capaz de ganarun concurso de pregunta-respuesta pero se basa en procesamiento delenguaje natural junto a la representación de conocimiento y razona-miento. No dejan de ser sistemas automáticos con grandes capacidadespero creo que no se puede catalogar como comprensión.

2. Capacidad de resolver problemas. Se podría decir que el gran éxito dela IA moderna ha sido su capacidad para resolver problemas específi-cos y hacerlo incluso mejor que un un humano: jugadores artificialesde Atari, del juego Go o de ajedrez, drones inteligentes, entre otros sis-temas.

3. Conocimiento, comprensión, acto de entender. El conocimiento sepuede representar empleaando técnica de IA, por ejemplo de la Ingenie-ría del Conocimiento. No sé si todo el conocimiento, pero si aquel quese puede modelar en un computador. Hay conocimiento que creo quees dificil de representar y razonar sobre él en la actualidad. Quizás elconocimiento del que se ha podido representar mejor ha sido el lógicomatemático. Hay buenas aplicaciones gráficas pero se suelen desarro-llar de forma ad-hoc y no se pueden consideran que sean sistemas deIA. Por ejemplo, un sistema operativo tiene un gran planificador de ta-reas y nunca se le ha llegado a considerar que sea un componente IA.

4. Habilidad, destreza y experiencia. Depende del tipo de sistema que sehaya diseñado e implementado. Hay máquinas que tienen mucha habi-lidad o destreza, por ejemplo, drones que son capaces de volar con un

3

alto nivel de precisión y hacer maniobras que dificilmente un humanopodría realizar. La experiencia es una cualidad interesante porque unamáquina puede aprender en base a los ejemplos. Cuánto más cono-cimiento se tenga de un proceso mucho más fácil será construir unsistema inteligente. La clave está en su esas capacidades se puedencatalogar de habilidad, destreza o experiencia.

Pero que se ha logrado hasta ahora. Podemos encontrar muchos titularesy noticias interesantes:

"Europa quiere construir un cerebro."

"Poema creado por una IA engañó a una revista"

"La Super Computadora Watson ganó a los campeones de Jeopardy"

"Tres pacientes renuncian a sus manos por unas biónicas"

"IA aprende a jugar a los videojuegos de Atari ysupera puntajes humanos"

"IA gana al campeón del juego de mesa Go"

"Drones para la entrega de correos"

"Tesla presenta un robot humanoide,también fabrica automoviles autonomos"

"China crea un computador que estudiaráciencias de la computación"

A pesar de todos los esfuerzos podemos decir sin ningún de tipo de du-da que la IA como disciplina científica está lejos de conseguir máquinas quecuenten con una inteligencia general que se pueda adaptar a cualquier situa-ción como lo puede hacer un ser humano. Pero también podemos asegurarque en lo que se refiere a tareas específicas las máquinas inteligentes hanconseguido superar o igualar a los humanos y con mucha probabiliad lohagan en muchas otras facetas más con el paso de años. Pero la pregun-ta clave que queda fuera de esta introducción: ¿cuánta energía necesita uncomputador para llegar al nivel de un humano?.

Para finalizar esta sección revisamos una reflexión expresada en el libro[3]: .El camino hacia la Inteligencia Artificial de tipo general seguirá siendo largoy difícil; al fin y al cabo la Inteligencia Artificial tiene solo 60 años y, comodiría Carl Sagan, 60 años es un brevísimo momento en la escala cósmica delUniverso, o como muy poéticamente dijo García Marquez: Desde la apariciónde vida visible en la Tierra debieron transcurrir 380 millones de años para queuna mariposa aprendiera a volar, otros 180 millones de años para fabricaruna rosa sin otro compromiso que el de ser hermosa, y cuatro eras geológicaspara que los seres humanos fueran capaces de cantar mejor que los pájaros ymorirse de amor"

4

0.1. La IA como disciplina ciéntifica

Tras haber aclarado estos aspectos, podemos comenzar a estudiar la IAcomo disciplina científica cuyo objetivo es el diseño e implementación de sis-temas inteligentes para desarrollar tareas específicas. Como asignatura la IAes multidisciplinar, podría cubrir una carrera toda ella pues abarca áreas tandiversas como la robótica o la biomecánica, las ciencias de la computación,la algorítmica, el análisis numérico, las ciencias de la cognición, la filosofíao la antropología. Sin embargo nosotros consideramos que la IA está forma-da por tres grandes áreas: Sistemas Intelientes e Inteligencia Computacional(Software), Robótica (Hardware) y Ciencias de la Cognitición (Teoría y prác-tica asociada a la inteligencia humana que puede ser aplicada a la creaciónde sistemas inteligentes). Como ingenieros en computación, informática osoftware nos interesa estudiar el área de al Inteligencia Computacional. Siexistiera una carrera llamada Ingeniero en Inteligencia Artificial, algunos delos ramos junto a sus profesores deberían ser los siguientes:

1. Lógica y sentido común (John McCarthy - padre de la IA).

2. Ciencias de la cognición e IA (Marvin Misky).

3. Resolución de problemas y Planificación (Nils J. Nilsson).

4. Computación con percepciones (Lofti Zadeh).

5. Probabilidad y Causalidad (Judea Pearl).

6. Computación Lingüística (Noam Chomsky).

7. Ingeniería del Conocimiento (Edward Feigenbaum).

8. Reinforcement Learning (Richard Sutton).

9. Deep Learning (Geoffrey Hinton).

Nosotros como ingenieros informáticos podríamos estar interesados encualquiera de ellas, pero en un curso de Introducción a la IA y por ser laprogramación una de las competencias fundamentales de egreso, debemoscentrar nuestra atención en las técnicas de programación que permitan crearprogramas informáticos que muestren un comportamiento autónomo e inte-ligente, es decir, crearemos sistemas software inteligentes. Y esto lo realiza-remos desde una perspectiva novedosa, seremos los creadores de nuestropropio mundo, dotaremos de movimiento a nuestras entidades y las pro-gramaremos para que realicen las funciones deseadas. La técnica con la quetrabajaremos a lo largo del curso será la Resolución de Problemas y la Planifi-cación de rutas con algo de Representación de Conocimiento y Razonamientomediante la lógica de predicados y la teoría de las percepciones computacio-nes (en el sentido de la lógica difusa).

0.1. LA IA COMO DISCIPLINA CIÉNTIFICA 5

Cómo enseñar sistemas complejos

Los sistemas de IA se puden catalogar como sistemas complejos ya quepara lograr entender cada uno de sus componentes requerimos de conoci-mientos obtenidos de varias disciplinas: matemáticas, física, biología, etc.Además como hemos visto hay muchas técnicas, muchos trabajos, muchoslibros, muchos vídeos, en otras palabras, hay un exceso de información quecomplica mucho iniciarse en una disciplina.

La filosofía que vamos a seguir en este ramo consiste en olvidarnos detodas esas disciplinas (aunque evidentemente tenemos que emplear algunosconceptos básicos) y tratar de enseñar IA empleando mundos sencillos quetoda persona pueda entender. Es decir, en principio, cualquier estudiantecon conocimientos básicos de programación debería poder aprender a crearmundos virtuales y dotar a dicho mundo de entidades inteligentes que reali-cen alguna tarea.

Nuestro método se inspira en la forma de impartir clase del profesor Ta-nenbaum quien enseñaba sistemas operativos (que son uno de los softwaremás importantes y complejos que gobiernan el hardware) empleando un sis-tema operativo sencillo que creó en 1987 llamado Minix. Minix es un sistematipo Unix gratuito con propósitos educativos, que posteriormente inspiró Li-nux.

Seguiremos esta filosofía ya que como la IA trata de simular la inteligenciadel humano en el mundo real lo que haremos nosotros será crear un mundoabstracto muy sencillo donde deberemos implementar tareas que requieraninteligencia. Nos basamos también en el juego del matemático llamado juegode la vida. Se basa en celdas 2D y demostró que a partir de reglas sencillasse puede crear complejidad. Por tanto nos basamos en esta idea. El alumnoes creador de su propio mundo y las inteligencias que desarrollará actuaránen este mundo virtual construido mediante celdas 2D.

Es importante mencionar también que el mundo virtual sirve para nive-lar la competencia de programación y conseguir que todos los alumnos yalumnas comiencen el ramo con el mismo nivel de progrmación. Durante elramo el alumno/a se le apoyará en la tarea de implementar los algoritmosteóricos. Es decir: aprenderá un algoritmo (desde el punto de vista teórico),lo diseñará, lo implementará, lo incorporará a un proyecto real.

¿Qué necesitamos? Concepto de inteligencia y en-torno virtual

Cuando un humano se dispone a jugar a un juego tipo Pac-Man debe em-plear su inteligencia para poder capturar las recompensas sin ser atrapadopor los adversarios. Incluso si cambiamos el escenario, la distribución de losobstáculos y la velocidad de los oponentes, seremos capaces de resolverlo,nos adaptamos a las nuevas situaciones.

Nuestro concepto de Inteligencia va a estar relacionado con esta capaci-dad, es decir, queremos simular el comportamiento de un humano en estecontexto programando software que permita capturar las recompensas y es-

6

quivar a los oponentes de forma automática. Además lo deberá hacer aunqueel escenario y los oponentes cambien de velocidad o de inteligencia.

Para esto realizaremos la creación de un entorno virtual y de unas entida-des virtuales (agentes). Estos agentes simulan la capacidad de un humanopara recolectar las recompensas distribuidas por el mundo virtual sin sercapturados por adversarios y esquivando los obstáculos. Por tanto el con-cepto de agente inteligente será fundamental para nosotros. En IA a esto seconoce como diseño basado en Agentes.

El diseño basado en agentes hace uso de algoritmos de persecución yescape, búsqueda de rutas, planicación, decisión para poder actuar en unentorno virtual. Los agentes se relacionan directamente con el concepto deNPC (Non-player character), por tanto, en este sentido, los videojuegos y losentornos virtuales se pueden considerar sistemas multi-agente. Nuestro ob-jetivo es estudiar cómo usar estos agentes para que puedan emplearse en laconstrucción de entornos virtuales.

Cómo se impartirá el ramo

El ramo se divide en tres partes:

PARTE 1. Taller de construcción de un entorno virtual 2D mediante ellenguaje de programación Java.

PARTE 2. Taller teórico-práctico sobre las técnicas de programaciónde IA: Resolución de problemas: Búsqueda no informada e informada,Búsqueda A*); Representación de conocimiento y razonamiento: Cálculode predicados, teoría de percepciones computacionales.

PARTE 3. Taller de incorporación de las técnicas en nuestro entornovirtual.

Metodología basada en proyecto-taller. Clases tipo taller. Se seguiránunos vídeos de desarrollo, paso a paso, que explican la construcción de unentorno virtual 2D y la programación de agentes inteligentes con técnicas debúsqueda en un espacio de estados.

Se realizarán dos CERTÁMENES TEÓRICO-PRÁCTICOS (Fechas por de-terminar a mitad y final de semestre). Lo que prima es el producto final yuna vez que el producto final cumple los requisitos, entonces se puede pasara evaluar la calidad. Las pautas de evaluación dependerá de cada certamen.Se permite apuntes y código personal. La duración será de 4 secciones (4horas). Los certámenes siempre están relacionados con las clases

La nota se calcula de la siguiente forma: NF = C1 ∗ 0,3 + C2 ∗ 0,7. Loscertámenes funcionan correctamente, cumple todos los requisitos, no se caey se entrega en la fecha y hora fijada de entrega. Si el certamen no funcionacorrectamente, no cumple todos los requisitos, se cae o NO se entrega en lafecha y hora fijadas. Se podrá evaluar con NCR.

0.1. LA IA COMO DISCIPLINA CIÉNTIFICA 7

Conclusiones y observaciones finales

¿Por qué Java y Netbeans ?. Necesitamos crear un marco de trabajocomún. Por eso se usará un mismo lenguaje y un mismo entorno desarrollo.Java es un buen lenguaje de progración orientado a objetos y es el lenguajeen el que se empezó a crear el ramo. Por otro lado es el que más dominoyo personalmente por lo que os puedo apoyar en las dudas de a aprenderalgoritmos y estructuras de datos para sistemas inteligentes.

¿Qué ocurre con Python? La ciencia de datos, las redes neuronales,aprendizaje por refuerzo, deep learning, entre otras. Se han convertido enuna de las diciplinas más usadas. Python es posiblemente el lenguaje cuyacomunidad es más activa ahora mismo y tiene muchas librerías que imple-mentan mucho de los nuevos algoritmos y estructuas. Pero antes de apren-der ciertas librerías hay que ir paso a paso ya que el hecho de que todoesté implementado no ayuda al aprendizaje porque uno tiendo a buscar lasolución y emplear la librería sin que llegue a comprener cómo están im-plementadas. Para explicar y aprender sobre un determinado algoritmo otécnica el lenguaje de programación en cuál esté implementado no es muyrelevante. Otra cosa muy diferente es ser experto en el conocimiento de lasdiferentes librerías de un lenguaje pero eso deberá enseñarse en ramos másavanzados: electivos, magíster o doctorado.

Primer problema de nivelación

Se plantea el problema del explorador que debe buscar una ruta al tesorosin que se pase por donde está el cocodrilo. Se proporciona código de ayudabasado en la técnica de la vuelta atrás. Implementar la solución en Javapero realizando un correcto diseño de clases y modelado del problema: Mapa,Celdas, Explorador, Ruta, etc.

Como ayuda se proporciona código fuente obtenido de:

\verb+https://teclaycafe.com/backtracking-para-resolver-una-rata-en-un-laberinto/

Este código es una solución en Java de un problema similar empleandola técnica de vuelta atrás. El código es el siguiente:

Javaclass Maze{

private static final int SIZE = 5;

//the maze problemprivate static int[][] maze = {

{0,1,0,1,1},{0,0,0,0,0},{1,0,1,0,1},{0,0,1,0,0},{1,0,0,1,0}

8

};

//matrix to store the solutionprivate static int[][] solution = new int[SIZE][SIZE];

//function to print the solution matrixprivate static void printSolution(){

for(int i=0;i<SIZE;i++){

for(int j=0;j<SIZE;j++){

System.out.print(solution[i][j]+"t");}System.out.print("nn");

}}

//function to solve the maze//using backtrackingprivate static boolean solveMaze(int r, int c){

//if destination is reached, maze is solved//destination is the last cell(maze[SIZE-1][SIZE-1])if((r==SIZE-1) && (c==SIZE-1)){

solution[r][c] = 1;return true;

}//checking if we can visit in this cell or not//the indices of the cell must be in (0,SIZE-1)//and solution[r][c] == 0 is making sure that the cell//is not already visited//maze[r][c] == 0 is making sure that the cell is not blockedif(r>=0 && c>=0 && r<SIZE &&c<SIZE && solution[r][c] == 0 && maze[r][c] == 0){

//if safe to visit then visit the cellsolution[r][c] = 1;//going downif(solveMaze(r+1, c))

return true;//going rightif(solveMaze(r, c+1))

return true;//going upif(solveMaze(r-1, c))

return true;//going left

0.1. LA IA COMO DISCIPLINA CIÉNTIFICA 9

if(solveMaze(r, c-1))return true;

//backtrackingsolution[r][c] = 0;return false;

}return false;

}

public static void main(String[] args){

if (solveMaze(0,0))printSolution();

elseSystem.out.println("No solutionn");

}}

10

Bibliografía

[1] Warren S McCulloch and Walter Pitts. A logical calculus of the ideasimmanent in nervous activity. The bulletin of mathematical biophysics,5(4):115–133, 1943.

[2] Alan M Turing. Computing machinery and intelligence. In Parsing theturing test, pages 23–65. Springer, 2009.

[3] Ramón López de Mántaras Badia and Pedro Meseguer González. Inteli-gencia artificial. 2017.

11

Capítulo 2Mi primer agente inteligente

Clemente Rubio Manzano ([email protected])

Departamento de Sistemas de InformaciónUniversidad del Bío-Bío

Qué aprenderemos en este capítulo

Este capítulo aprenderemos a modelar y programar nuestro primer agenteinteligente. Será un agente que puede alcanzar un tesoro sin la ayuda denadie, se moverá siguiendo una ruta que previamente calculadremos conun algoritmo diseñado para ello. Explicaremos el modelado del problema deExplorador y su implementación empleando un algoritmo de vuelta atrás(backtracking). Implementaremos dos versiones del mismo, una recursiva(la clásica) y una iterativa que obtedremos a partir de la lógica del algoritmorecursivo. El objetivo es familiarizarnos con el lenguaje de programación Javay con el diseño orientado a objetos. El contenido de este capítulo lo apoyamoscon dos documentos más en formato pdf (conjunto de slides) que explicancon más detalle el código mostrado en este capítulo. Además se adjunta elcódigo fuente programado en un proyecto Netbeans para que el alumno lopueda analizar y estudiar con mayor detalle.

Modelado del problema del Explorador basado enla filosofía del mundo virtual

Nuestra primera inteligencia es una muy sencilla y consistirá en propor-cionarle al explorador los pasos necesarios para alcanzar un tesoso. El ex-plorador debe seguir una ruta para llegar al tesoro y podrá encontrarse en elcamino con obstáculos y un cocodrilo (que inicialmente supodremos que nose mueve). Como queremos construir una inteligencia que haga lo mismo queun humano lo que haremos primero será diseñar e implementar una aplica-ción que permita a una persona (usuario) moverse por un mapa y alcanzarel tesoro. La solución que queremos obtener es algo así:

1

2

[E][ ][ ][ ] [ ][ ][ ][ ] [ ][ ][ ][ ][ ][ ][ ][ ]--> [E][ ][ ][ ]--> [ ][ ][ ][ ][ ][ ][ ][ ] [ ][ ][ ][ ] [E][ ][ ][ ][ ][ ][ ][T] [ ][ ][ ][T] [ ][ ][ ][T]

[ ][ ][ ][ ] [ ][ ][ ][ ] [ ][ ][ ][ ][ ][ ][ ][ ]--> [ ][ ][ ][ ]--> [ ][ ][ ][ ][ ][ ][ ][ ] [ ][ ][ ][ ] [ ][ ][ ][ ][E][ ][ ][T] [ ][E][ ][T] [ ][ ][E][T]

[ ][ ][ ][ ][ ][ ][ ][ ]--> Se llegó al tesoro[ ][ ][ ][ ][ ][ ][ ][E]

El explorador lo denotaremos por “E” y el tesoro por el carácter “T”. Vamosa realizar un modelado del problema teniendo en mente el desarrollo de unMundo Virtual gráfico. Por tanto tendremos una clase MundoVirtual conelementos que se mostrarán en el mundo: mapa, explorador y tesoro. Laclase Mapa estará formada por el ancho y el largo del Mapa, Explorador yTesoro tendrán la posición y el nombre. A continuación, realizaremos unprototipado rápido de clases y construiremos los diferentes métodos en baseél.

// Clase Mapa

public class Mapa {

public int largo, ancho;

public Mapa(int largo,int ancho) {

this.largo=largo;this.ancho=ancho;

}

}

//Clase Explorador

public class Explorador {

public int x,y;public char nombre;

public Explorador(int x,int y) {

this.x=x;this.y=y;nombre=’E’;

3

}

@Overridepublic String toString() { return ""+nombre; }

}

//Clase Tesoro

public class Tesoro {

public int x,y;public char nombre;

public Tesoro(int x,int y) {

this.x=x;this.y=y;nombre=’T’;

}

@Overridepublic String toString() { return ""+nombre; }

}

Ahora creamos una clase MundoVirtual que contendrá a cada uno deestos elementos. Supondremos un mapa de 4× 4, explorador que parte en laposición (0, 0) y tesoro situado en la posición (3, 3).

public class MundoVirtual {

public Mapa mapa;public Explorador explorador;public Tesoro tesoro;

public MundoVirtual() {

mapa=new Mapa(4,4);explorador=new Explorador(0,0);tesoro=new Tesoro(3,3);

}

@Overridepublic String toString() {

String cadena="";for(int i=0; i < mapa.largo; i++) {

for(int j=0; j < mapa.ancho; j++) {

4

if ( i==explorador.x && j==explorador.y) {

cadena+="["+explorador.toString()+"]";

}else {

if ( i==tesoro.x && j==tesoro.y) {

cadena+="["+tesoro.toString()+"]";

}else {

cadena+="[ ]";

}

}

}cadena+="\n";

}return cadena;

}

}

Una vez hecho esto, podemos crear nuestra clase Main.java y escribirel método main que nos permitirá comprobar si podemos crear MundoVir-tual correctamente mostrando la información que nos proporciona el objetocuando se instancia:

public class Main {

public static void main ( String [] args ) {

MundoVirtual mundo=new MundoVirtual();System.out.println(mundo.toString());

}}

//Al ejecutar

[E][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][T]

Una vez que hemos comprobado que tenemos la funcionalidad necesa-ria para soportar mundo virtuales, vamos a implementar los movimienos delexplorador. Supondremos que podemos realizar 4 movimientos: arriba, aba-

5

jo, izquierda y derecha (las diagonales no las consideramos pero se podríaconsiderar sin problema). El método quedaría como sigue:

public void mover(int mov,Mapa m) {

switch(mov) {

case ARRIBA:

if ( x > 0 ) x-=1;else System.out.println(" no se puede mover arriba");

break;

case ABAJO:

if ( x < m.largo-1 ) x+=1;else System.out.println(" no se puede mover abajo");

break;

case DERECHA:

if ( y < m.ancho-1 ) y+=1;else System.out.println(" no se puede mover derecha");

break;

case IZQUIERDA:

if ( y > 0 ) y-=1;else System.out.println(" no se puede mover izquierda");

break;

}}

Ahora estamos en condiciones de implementar un bucle que permita alexplorador moverse por el escenario hasta llegar al tesoro. Lo que haremosserá preguntar por el siguiente movimiento por teclado empleando la funcio-nalidad que proporciona el objeto Scanner. Una vez obtenido el movimientollamaremos el método mover y finalmente consultaremos si hemos alcanzadoel tesoso mediante un método finPartida() que comprueba si la posicióndel explorador es igual a la del tesoro, en caso afirmativo, salimos del bucley mostrador el mensaje: “ Explorador llegó al tesoro”.

public static void main ( String [] args ) {

MundoVirtual mundo=new MundoVirtual();Scanner entrada=new Scanner(System.in);

6

do {

System.out.println(mundo.toString());System.out.println("...mover explorador...");int mov=entrada.nextInt();

mundo.explorador.mover(mov, mundo.mapa);

}while(!mundo.finPartida());

System.out.println("...el explorador encontró el tesoro...");

}

Algoritmo para buscar una ruta en un mapa

Vamos a emplear un algoritmo de vuelta atrás para implementar un bus-cador de rutas. La filosofía de la vuelta atrás consiste en explorar todas lasopciones para ir calculando una solución. En nuestro caso lo que se reali-za es una exploración del mapa hasta encontrar el tesoro. Conforme vamoshaciendo la exploración vamos marcando que puede ser solución o que nopuede serlo. El método recursivo para un algoritmo de vuelta aplicado a labúsqueda de rutas es el siguiente:

public boolean buscarRuta(int x, int y) {// si estoy dentro de los limitesif ( x>=0 && y>=0 && x < largo && y < ancho ) {

// si encuentro el tesoro devuelvo trueif( mapa[x][y]==’T’ ) {

solucion[x][y] = ’E’;return true;

}// si aun no es solucion y si aun no ha sido exploradoif( solucion[x][y] == ’#’ && mapa[x][y] == ’ ’) {

//marco como posible rutasolucion[x][y] = ’E’;

if(buscarRuta(x, y+1)) return true;if(buscarRuta(x-1, y)) return true;if(buscarRuta(x+1, y)) return true;if(buscarRuta(x, y-1)) return true;

//vuelta atrássolucion[x][y] = ’#’;return false;

}return false;

}

7

return false;}

Los algoritmos de vuelta atrás son de naturaleza recursiva por lo que senecesita tener un cierto nivel de programación y de experiencia en la inter-pretación, depuración y trazabilidad de programas. La buena noticia es quetodo algoritmo recursivo se puede transformar en iterativo. Por tanto, comoqueremos que este manual sea lo más sencillo posible, y fácil de seguir, loque haremos será crear una versión iterativa del mismo. Además esta versiónnos ayudará a enteder el algoritmo recursivo de vuelta vueltás y también esuna buena introdución a los algoritmos de búsqueda que veremos posterior-mente.

public void buscarRuta_Iterativa(int x, int y) {//empleamos una pilaStack<Posicion> pila=new Stack();//ponemos en la pila la primera posición (0,0)pila.push(new Posicion(x,y,"inicial"));//mientras la pila no sea vacía y no hayamos encontrado//soluciónwhile(!pila.empty() && !exito ) {

Posicion p=pila.pop(); //tomamos cima de la pila

if ( p.x>=0 && p.y>=0 && p.x < largo && p.y < ancho ) {// cuando encontramos salimosif( mapa[p.x][p.y]==’T’ ) {

solucion[p.x][p.y] = ’E’;pasos.add(p.oper);exito=true;break;

}// si aun no es solucion y si aun no ha sido exploradoif( solucion[p.x][p.y] == ’#’ && mapa[p.x][p.y] == ’ ’) {

solucion[p.x][p.y] = ’E’;pasos.add(p.oper);pila.push(new Posicion(p.x, p.y-1,"izquierda"));pila.push(new Posicion(p.x-1, p.y,"arriba"));pila.push(new Posicion(p.x+1, p.y,"abajo"));pila.push(new Posicion(p.x, p.y+1,"derecha"));

}

}//condicional de los limites

}//fin del bucle

}//fin del metodo iterativo

8

El resto de clase está formada por 5 atributos: el mapa que representa lainformación obtenida del mundo virtual, el ancho y el largo que se obtenienedel mismo mapa creado o recibido como argumento, una matriz llamada so-lución donde iremos marcando que es camino y que no es camino, una listade pasos donde iremos insertando los pasos que vamos dando en cada mo-mento y finalmente un variable booleana llamada exito que me dirá cuandohemos alcanzado el tesoro.

public class BuscadorRuta {

public char[][] mapa;public int largo, ancho;public char[][] solucion;public ArrayList<String> pasos;public boolean exito;

public BuscadorRuta() {

mapa =new char[][]{{’ ’,’#’,’ ’,’ ’},{’ ’,’#’,’ ’,’ ’},{’ ’,’#’,’ ’,’ ’},{’ ’,’ ’,’ ’,’T’}};

largo=mapa.length;ancho=mapa[0].length;solucion = new char[largo][ancho];//inicialmente no hay caminofor(int i=0; i < largo; i++)

for(int j=0; j < ancho; j++)solucion[i][j]=’#’;

pasos=new ArrayList<>();exito=false;

}.../* algoritmos explicados previamente */...

@Overridepublic String toString() {

String cadena="";for(int i=0;i<largo;i++){

for(int j=0;j<ancho;j++){cadena+=solucion[i][j];

}cadena+="\n";

}return cadena;

}}

9

Para comunicar el paquete busqueda_ruta y explorador crearemos uncosntructor nuevo en Busqueda Ruta que recibirá un mapa. Además enMundo Virtual crearemos un método que me permite generar un mapa char[][]para poder pasárselo como argumento al objeto búsqueda ruta

// en Mundo Virtual.java

public char[][] darMundoVirtual() {

char[][] mundo_virtual=new char[mapa.largo][mapa.ancho];

for(int i=0; i < mapa.largo; i++) {

for(int j=0; j < mapa.ancho; j++) {

if ( i==explorador.x && j==explorador.y) {

mundo_virtual[i][j]=’ ’;

}else {

if ( i==tesoro.x && j==tesoro.y) {mundo_virtual[i][j]=’T’;

}else {mundo_virtual[i][j]=’ ’;

}}

}

}return mundo_virtual;

}

//En Buscador_Ruta.java

public BuscadorRuta(char[][]mapa) {

largo=mapa.length;ancho=mapa[0].length;this.mapa = mapa;solucion = new char[largo][ancho];//inicialmente no hay caminofor(int i=0; i < largo; i++)

for(int j=0; j < ancho; j++)solucion[i][j]=’#’;

pasos=new ArrayList<>();exito=false;

}

Ahora podemos implementar la funcionalidad que permita calcular lospasos que el explorador tiene que dar y ejecutar un bucle con pausa que si-

10

mule los movimientos como si los hicera un humano. Para ello puede cargary ejecutar el buscar de rutas y despues empleando la lista de pasos le po-demos dar los pasos al explorador uno a uno. La clase Main quedaría comosigue:

public class Main {

public static void main ( String [] args ) {

MundoVirtual mundo=new MundoVirtual();System.out.println(mundo.toString());

BuscadorRuta br=new BuscadorRuta(mundo.darMundoVirtual());br.buscarRuta_Iterativa(0, 0);

System.out.println(br.pasos);for(int i=1; i < br.pasos.size(); i++ ){

System.out.println(mundo.toString());

String mov=br.pasos.get(i);System.out.println(" El explorador se moverá: "+mov);mundo.explorador.mover(mov, mundo.mapa);pausa();

}

}

public static void pausa() {try { Thread.sleep(5000); } catch (InterruptedException ex) { }

}

}

Capítulo 3.Desarrollo de un mundovirtual sencillo: escenariovacío y eventos de E/S

Aclaración Inicial

El gran volumen de información que existe hoy en día sobre la InteligenciaArtificial y la rapidez de sus avances pueden convertir el proceso de iniciarseen esta disciplina en una experiencia traumática y compleja. El objetivo deeste material docente es presentar a los mundos virtuales sencillos como unaherramienta muy útil a partir de la cual aislarse del mundo real y que per-mite enseñar los algoritmos más importantes de inteligencia artificial, tantodel paradigma simbólico como del conexionista.

Los mundos virtuales sencillos tienen como principal objetivo que el alumnose olvide de la complejidad del mundo real y ponga toda su atención en estospequeños escenarios artificiales. La idea subyacente está basada en el juegode la vida de Conway, que demostró que a partir de reglas simples se puedecrear complejidad y "vida.artificial.

El siguiente capítulo forma parte del material docente de la asignatura deInteligencia Artificial para las carreras de Ingeniería Civil en Informática y deEjecución en Computación e Informática de la Universidad del Bío-Bío. Suobjetivo es explicar, paso a paso, el desarrollo de un mundo virtual en dosdimensiones mediante el lenguaje de programación Java (sin emplear nin-guna librería especializada para el desarrollo de videojuegos, ni motorgráfico, ni de inteligencia artificial).

Posteriormente las técnicas de búsqueda de inteligencia artificial vistas enlas clases teóricas se incorporaran con el objetivo de dotar a los personajes deun comportamiento autónomo e inteligente. Entendiendo inteligencia cómola capacidad de adaptación de una entidad en un mundo cerrado en funciónde cierta información previa y cambiante. El código fuente que se presentaes de programación libre y personal, no sigue los estándares de programa-ción orientada objetos, todos los atributos son públicos para conseguir unprototipado más directo.

El objetivo es que éste sea lo más sencillo posible de entender para un

1

2

programador no experto. Un programador experto podrá mejorarlo con fa-cilidad y adaptarlo a los patrones de la POO y a los estándares requeridoscuando se trabaja en proyectos de varios programadores. No se sigue ningúnpatrón de diseño de desarrollo software, lo que no significa que no se utiliceprogramación de calidad.

0.1. Organización de esta primera sesión

El objetivo de esta primera sesión es aprender a construir un escenarioformado por celdas en dos dimensiones y conocer las técnicas de manejo deeventos desde los dispositivos de entrada y salida más habituales, ratón yteclado, en nuestro caso. La sección 0.2 se encarga de explicar cómo se pue-de implementar un escenario en dos dimensiones compuesto por celdas conposiciones x e y (el resultado debería ser similar al mostrada en la Figura 2).

0.2. Construcción de un escenario virtual basadoen celdas.

Cuando observamos un escenario virtual en dos dimensiones (ver Figu-ra 1) podemos identificar muchos elementos de forma visual:

Obstáculos.

Personajes secundarios.

Premios.

Vidas.

Tiempo.

Puntos acumulados.

Un escenario virtual se compone de diferentes capas en las cuales apa-recen estos elementos visuales. Cada uno éstos se situará en una posicióndeterminada (X,Y ). Dicha posición puede ser fija durante toda la ejecucióno bien podría variasr, es decir, se podrán mover de una celda a otra a lolargo del escenario. Por tanto, podemos distinguir dos tipos de entidades:sin movimiento (barreras, premios, caminos) y con movimiento (personaje,adversarios, coches, etc). Una forma de diseñar este escenario consiste endividirlo en celdas. Una celda (ver Figura 2) es un elemento atómico, repre-sentado habitualmente por un cuadrado o un rectángulo, que tendrá unadeterminada posición y unas dimensiones concretas. El objetivo principal denuestra inteligencia artificial será que algunas de esas celdas, las que repre-sentan un personaje o un adversario, se muevan de forma automática conalgún tipo de objetivo.

Si nos pidieran dibujar el escenario anterior, la primera opción sería usarlápices de colores y papel, como lo hacíamos en el colegio cuando eramos pe-queños. En el lenguaje de programación Java esto equivale al objeto Canvas

0.2. CONSTRUCCIÓN DE UN ESCENARIO VIRTUAL BASADO EN CELDAS.3

Figura 1: Ejemplo de un Escenario o Entorno 2D de un vídeo juego comercial

(papel o lienzo) y al objeto Graphics (lápiz y lapices de colores). Por ejemplo,si quisiéramos pintar un circulo, tomaríamos papel, lápiz y pintaríamos uncirculo en cualquier posición del papel. Para hacer esto en Java, debemoscrear un objeto Canvas (zona de dibujo), y usar el objeto Graphics (nuestrolápiz) haciendo uso de los métodos de los que esta clase dispone para dibujarcualquier figura geométrica en 2D (para más detalle ver la API de Java). Laúnica diferencia entre el método manual y el computacional es que la panta-lla está dividida en píxeles, por tanto tenemos que indicar al sistema dóndequeremos dibujar el círculo y cuáles serán sus dimensiones.

El objetivo de esta sesión es mostrar, de forma práctica, los recursos dis-ponibles en Java para construir escenarios en dos dimensiones. Inicialmentepartiremos de un plano vacío constituido por un conjunto de celdas. Nues-tro propósito es conseguir una ventana dividida en celdas o cuadrantes. Encada cuadrante se podrá, posteriormente, colocar cualquier elemento: unpersonaje, un obstáculo, una moneda, una trampa, etc, representado poruna imagen representativa de tal elemento (el tamaño de las celdas y de lasimágenes será el mismo con el objetivo de facilitar la programación de losalgoritmos de búsqueda).

El primer paso a realizar consiste en ser capaces de pintar Celdas. Se-rán nuestras unidades atómicas, indivisibles. Para pintar una celda hare-mos uso de la clase Celda que implementará la funcionalidad para dibujarrectángulos de unas determinadas dimensiones almacenadas en la interfazConstantes.

public interface Constantes {//tamaño de la celdapublic final int PIXELS=32;//el ancho de la celdapublic final int NUMERO_CELDAS_ANCHO=12;//largo de la celdapublic final int NUMERO_CELDAS_LARGO=9;

4

//desplazamientopublic final int ANCHO_BORDE_VENTANA=30;public final int LARGO_BORDE_VENTANA=50;public final int DESPLAZAMIENTO=10;

public final int ANCHURA_ESCENARIO=(PIXELS*NUMERO_CELDAS_ANCHO)+ANCHO_BORDE_VENTANA;

public final int LARGO_ESCENARIO=(PIXELS*NUMERO_CELDAS_LARGO)+LARGO_BORDE_VENTANA;

}

La interfaz Constantes la utilizamos como si fuera un archivo de configu-ración. Es decir, se utiliza para definir parámetros o estructuras que utiliza-remos en más de una clase y que necesitamos que sean conocidas (visibles)para cada una de ellas al inicio de la ejecución. Por ahora: i) anchura y al-tura de las celdas en pixeles (PIXELS); ii) número de celdas a lo largo y a loancho (NUMERO_CELDAS_ANCHO,NUMERO_CELDAS_LARGO); iii) anchura y alturadel mundo virtual, esto, tamaño de una celda por el número de celdas a loancho y a lo alto, respectivamente.

Es conveniente, como primera decisión diseño, decidir que el tamaño delas celdas sea igual al tamaño de las imágenes que usaremos para crearnuestro mundo virtual (esto lo veremos en capítulos posteriores). La claseCelda permite implementar la funcionalidad para mostrar una celda de lasdimensiones mencionadas de forma gráfica. Por tanto es una clase que here-da de JComponent (contiene funcionalidad para pintar figuras geométricas)e implementa la interface Constantes (donde tenemos las dimensiones). Sucódigo es el siguiente:

import java.awt.Graphics;import javax.swing.JComponent;

public class Celda extends JComponent implements Constantes {//atributospublic int xPixel;public int yPixel;

//constructor, inicializa los atributospublic Celda(int x,int y) {

this.xPixel=x;this.yPixel=y;

}

//metodo que permite dinujar rectangulo en la posion xPixel,yPixel

@Overridepublic void paintComponent(Graphics g) {

g.drawRect(xPixel,yPixel,PIXELS,PIXELS);}

}

0.2. CONSTRUCCIÓN DE UN ESCENARIO VIRTUAL BASADO EN CELDAS.5

La función g.drawRect(X,Y,Ancho,Largo); dibuja un rectángulo en laposición x e y, de ancho Ancho y de largo Largo. La clase Escenario seencarga de crear la estructura del futuro mundo virtual. En nuestro caso, elmundo virtual es una matriz (cuadrícula) de Celdas que van a representarlas posiciones en las cuales las entidades el mundo virtual van a poder estar.Por tanto se hará uso de la clase Celda creado una matriz de N por M . De lamisma forma que ocurría con la clase Celda, la clase Escenario contará conun método paintComponent encargado de dibujar graficamente en el canvas(Lienzo) un escenario vacío basado en Celdas.

import java.awt.Graphics;import javax.swing.JComponent;

public class Escenario extends JComponent implements Constantes {//matriz de celdaspublic Celda[][] celdas;//constructor del escenariopublic Escenario() {

celdas=new Celda[NUMERO_CELDAS_ANCHO][NUMERO_CELDAS_LARGO];//inicializar el array de celdasfor(int i=0; i < NUMERO_CELDAS_ANCHO; i++)

for ( int j=0 ; j < NUMERO_CELDAS_LARGO ; j++)celdas[i][j]=new Celda(i*PIXELS+DESPLAZAMIENTO,

j*PIXELS+DESPLAZAMIENTO);}

//se encarga de pintar una matriz de celdas@Overridepublic void paintComponent(Graphics g) {

for(int i=0; i < NUMERO_CELDAS_ANCHO ; i++)for ( int j=0 ; j < NUMERO_CELDAS_LARGO; j++)celdas[i][j].paintComponent(g);//pintar celda

}}

La clase MundoVirtual hereda de la clase Canvas que cuenta con lafuncionalidad necesaria para poder visualizar figuras geométricas simplesy complejas (también imágenes). Una vez elegido el color de fondo, indica-remos su tamaño. Cuando un objeto Canvas se instancia y se añade a uncontenedor (ver página siguiente) se llamará al método paint encargado depintar los objetos indicados en él.

import java.awt.Canvas;import java.awt.Color;import java.awt.Graphics;

public class MundoVirtual extends Canvas implements Constantes{

public Escenario escenario;

6

public MundoVirtual(){escenario=new Escenario();this.setBackground(Color.orange);this.setSize(ANCHURA_ESCENARIO,LARGO_ESCENARIO);

}//metodo paint para pintar el escnario@Overridepublic void paint(Graphics g) {

//pintar escenarioescenario.paintComponent(g);

}

}

Por último, tendríamos las dos últimas clases: la clase VentanaPrincipalpara visualizar el mundo virtual sobre un JFrame y la clase Main que con-tiene el método main(). La clase VentanaPrincipal hereda de JFrame, ad-quiriendo de esta forma todas las funcionales para dibujar una ventana (im-plementadas en JFrame y sus clases padre). Posteriormente, ajustaremos eltamaño de la ventana mediante la función this.setSize(500,400);.

import java.awt.Color;import javax.swing.JFrame;

public class VentanaPrincipal extends JFrame {

public MundoVirtual mv;

public VentanaPrincipal() {this.getContentPane().setLayout(null);

mv=new MundoVirtual();mv.setBounds(10,10,400, 300);this.getContentPane().add(mv);

this.setSize(500,400);this.setTitle("MUNDO VIRTUAL 2D SENCILLO BASADO EN CELDAS");this.getContentPane().setBackground(Color.orange);

}

}

import javax.swing.JFrame;

public class Main {

public static void main (String[]args) {

VentanaPrincipal vp=new VentanaPrincipal();vp.setVisible(true);vp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

0.3. MI PRIMERA ENTIDAD Y LOS EVENTOS DE TECLADO 7

Figura 2: Plano vacío del escenario donde iremos colocando las entidades denuestro mundo virtual

}

}

Con este pequeño conjunto de clases tendríamos implementado el esce-nario 2D basado en celdas que pretendíamos al inicio.

0.3. Mi primera entidad y los eventos de teclado

Una vez disponemos de un escenario vacío podríamos colocar una celdaespecial para que se pueda mover por el mismo. Esta celda se convertiráen nuestra primera entidad y le asociaremos eventos de teclado para po-der moverla. Lo primero que vamos hacer es incorporar es una nueva claseque vamos a llamar Entidad la cual irá sufriendo algunos cambios en fu-turos capítulos. Por ahora, queremos que sea una clase que implemente lafuncionalidad de tener una Celda que podemos mover aprovechando la nue-va funcionalidad de la clase Celda. Lo importante es una entidad tiene dosatributos xMov y yMov que van contener los valores de la celda actual (ini-cialmente xMov=0, yMov=0). En función de si modifico una varible y otramodificaré la posición de la entidad. Los movimientos se resumen como si-guen (controlando siempre no salirme de los límites del escenario):

Si modifico la yMov restando una unidad entonces significa que iré haciaarriba; si le sumo una unidad implica ir hacia abajo.

Si modifico la xMov restando una unidad entonces significa que iré haciala izquierda; si le sumo una unidad implica ir hacia la derecha.

Esta lógica se implementa en los respectivos métodos: moverArriba, mo-verAbajo, moverDerecha y moverIzquierda.

8

import java.awt.Color;import java.awt.Graphics;import javax.swing.JComponent;

/**** @author klemenzza

*/public class Entidad extends JComponent implements Constantes{

public int xMov;public int yMov;

public Entidad(int x,int y) {xMov=x;yMov=y;

}

public void moverArriba(){ if ( yMov > 0 ) yMov-=1; }

public void moverAbajo(){ if (yMov < NUMERO_CELDAS_LARGO-1 )yMov+=1; }

public void moverIzquierda(){ if (xMov > 0 ) xMov-=1; }

public void moverDerecha(){ if (xMov < NUMERO_CELDAS_ANCHO-1 )xMov+=1; }

//algoritmo de pintado de una entidad@Overridepublic void paintComponent(Graphics g) {

g.drawRect(xMov*PIXELS+DESPLAZAMIENTO,yMov*PIXELS+DESPLAZAMIENTO,PIXELS,PIXELS);

g.setColor(Color.green);//cambiamos color

g.fillRect(xMov*PIXELS+DESPLAZAMIENTO,yMov*PIXELS+DESPLAZAMIENTO,PIXELS,PIXELS);

g.setColor(Color.BLACK);//volvemos a cambiar

g.setFont(FUENTE);

g.drawString("E",(xMov*PIXELS+DESPLAZAMIENTO)+5,(yMov*PIXELS+DESPLAZAMIENTO)+28);

}}

Por último debo implementar el pintado de la Entidad aplicando un algo-ritmo sencillo de impresión gráfica que detalla en la función paintComponent,los pasos son los siguientes:

0.4. EVENTOS DE TECLADO 9

Figura 3: Plano vacío del escenario con la nueva entidad “E” con fondo verdey fuente creada expresamente para ello

1. Dibujo un rectángulo con borde negro transparente (drawRect).

2. Cambio el color a verde (setColor).

3. Dibujo un rectángulo relleno con el color actual (verde).

4. Cambio de nuevo el color (setColor).

5. Cambio la fuente.

6. Dibujo la letra “E”.

El resultado es como aparece en la Figura 3Una vez que tenemos la funcionalidad de pintado de la entidad debemos

asociar cada método de movimiento con una tecla. Para ello debemos utilizarlos eventos de teclado.

0.4. Eventos de teclado

El objetivo de esta sección es mostrar al alumno cómo se pueden incor-porar eventos de teclado. La idea es que el escenario virtual pueda comenzara ser ser interactivo, es decir, reaccione a los eventos de entrada del usuario(habitualmente de ratón o teclado).

La interacción de las aplicaciones Java 2D se consigue a través de lacaptura de eventos. La terminología concreta utilizar en Java son los “es-cuchadores” de eventos (listeners). Los eventos de entrada/salida van a sereventos que se desea recuperar y que active o dispare una una determina-da acción en un instante concreto, por ejemplo, mover alguna entidad haciaalgún lado (arriba, abajo, izquierda o derecha)

Vamos a extender la clase MundoVirtual para dar soporte a los eventosmediante escuchadores de eventos. Con más precisión un escuchador se-rá un objeto que tendrá algún método que podrá lanzarse cuando el usuario

10

pulse una tecla. La gestión de los eventos lo haremos en la clase MundoVirtual.java.Por tanto nos situamos en ella y en el constructor colocamos el siguiente có-digo:

addKeyListener(new KeyAdapter() {

@Override

public void keyPressed(KeyEvent evt) {

identificar_Tecla_Pulsada(evt);repaint();

}

});

Este código nos proporciona la funcionalidad para añadir un nuevo escu-chador de eventos de teclado haciendo uso del método addKeyListener quepermite crear un objeto del tipo KeyAdapter. Este objeto posee un métodokeyPressed que proporciona la funcionalidad para identificar la tecla que sepulsó. Se puede sobre escribir e incorporar nuestra propia funcionalidad. Loque haremos será llamar a un método identificar_Tecla_Pulsada(evt);donde incorporaremos las acciones en función de la tecla pulsada. Despuésde haber pulsado una tecla, posiblemente, el escenario habrá sufrido algunamodificación, por ello la llamada al método repaint() que volverá a dibujarel escenario con los cambios que se hayan podido producir tras los eventos.

El segundo paso consiste en elegir la celda qué queremos mover. Por sim-plicidad aquí se ha elegido la primera celda, es decir, celda cuya coordenadaX es igual a cero, y cuya coordenada Y es igual a cero. Creamos por tan-to una Entidad llamada public Entidad entidad que inicializaremos enel constructor con las coordenadas (0,0) y pasándole el escenario donde semoverá (luego veremos que no será necesario pasarle el escenario ya que laclase MundoVirtual tiene una referencia del escenario disponible).

Figura 4: Celda seleccionada para moverse por el escenario: arriba, abajo,izquierda o derecha

0.4. EVENTOS DE TECLADO 11

Aquí implementamos el método identificar_Tecla_Pulsada(evt); querecibe como argumento un objeto del tipo KeyEvent. Dicho objeto posee unmétodo denominado getKeyCode(). Para saber cuáles son los códigos quecorresponden a las flechas de las teclas arriba, abajo, izquierda, derecha; po-demos acceder a la clase KeyEvent desde NetBeans situando el ratón sobreel nombre, pulsando control y haciendo click sobre él. Esta clase posee unasconstantes junto a sus códigos:

/*** Constant for the non-numpad <b>left</b> arrow key.

* @see #VK_KP_LEFT

*/public static final int VK_LEFT = 0x25;

/*** Constant for the non-numpad <b>up</b> arrow key.

* @see #VK_KP_UP

*/public static final int VK_UP = 0x26;

/*** Constant for the non-numpad <b>right</b> arrow key.

* @see #VK_KP_RIGHT

*/public static final int VK_RIGHT = 0x27;

/*** Constant for the non-numpad <b>down</b> arrow key.

* @see #VK_KP_DOWN

*/public static final int VK_DOWN = 0x28;

Por tanto, el método identificar_Tecla_Pulsada(evt); queda comosigue:

public void identificar_Tecla_Pulsada( KeyEvent evento ) {

switch( evento.getKeyCode() ) {

case KeyEvent.VK_UP:System.out.println("Mover arriba");entidad.moverArriba();

break;

case KeyEvent.VK_DOWN:System.out.println("Mover abajo");entidad.moverAbajo();

break;

case KeyEvent.VK_LEFT:System.out.println("Mover izquierda");entidad.moverIzquierda();

12

break;

case KeyEvent.VK_RIGHT:System.out.println("Mover derecha");entidad.moverDerecha();

break;

}}

A continuación mostramos cómo sería la implementación del método moverArriba()de la clase Entidad. Como tarea se deja la implementación de los otros tresmétodos restantes al lector. El resultado debería ser similar a la Figura 4donde la celda que aparece en la posición cero, cero; se puede mover por elescenario pulsando las flechas: arriba, abajo, izquierda, derecha.

public void moverArriba(){ if ( yMov > 0 ) yMov-=1; }

Un detalle importante es como quedará el paint del MundoVirtual quellamará a las funciones de impresión gráfica de entidad y de escenario.

//metodo llamada la primera vez que se pinta@Overridepublic void paint(Graphics g) {

entidad.paintComponent(g);escenario.paintComponent(g);

}

Otro detalle que hemos añadido es una nueva fuente a la clase Constantes.java:

--- resto quedaría igual

//nueva constantepublic final Font FUENTE=new Font("Times New Roman",Font.BOLD,28);

Donde se crea una nueva fuente (Font) llamada FUENTE que será del tipo’Times New Roman’, la pondremos con formato BOLD y tamaño 28.

0.5. Extra: Eventos de ratón

Para explicar los eventos de ratón dejaremos la clase Constantes intac-ta. En la clase Celda debemos incorporar un nuevo atributo que denomi-naremos celdaSeleccionada , inicializada a false. Esto quiere decir queinicialmente ninguna celda está seleccionada. Para ello, extendemos el mé-todo paintComponent con el objetivo de distinguir entre estos dos nuevosestados: i) celda seleccionada; ii) celda no seleccionada.

@Overridepublic void paintComponent(Graphics g) {

0.5. EXTRA: EVENTOS DE RATÓN 13

g.drawRect(x,y,PIXEL_CELDA,PIXEL_CELDA);

if ( celdaSeleccionada ) {

g.fillRect(x,y,PIXEL_CELDA,PIXEL_CELDA);

}}

Una vez que hemos extendido este método debemos incorporar un nuevométodo llamado comprobarSiCeldaSeleccionada(int clickX,int clickY)con el objetivo de identificar la celda que se ha seleccionado. Para ello debe-mos implementar la funcionalidad para crear un área igual a las coordenadasobtenidas en el evento del ratón. A estas coordenadas le sumamos la anchu-ra y la altura de cada celda. Esto lo hacemos empleando la clase Rectangleque nos permite saber si un punto está incluido en un rectángulo. Si el pun-to está incluido en el rectángulo entonces sabremos que la celda actual seha seleccionado y debemos modificar el atributo celdaSeleccionada con elvalor true. Por tanto declaramos un nuevo atributo y un nuevo método.

//si el click esta sobre la celdapublic void comprobarSiCeldaSeleccionada(int clickX,int clickY) {

if ( rectanguloCelda.contains(new Point(clickX,clickY)) ) {celdaSeleccionada = !celdaSeleccionada;

}

}

La clase Celda por tanto quedaría como se muestra a continuación:

import java.awt.Graphics;import java.awt.Point;import java.awt.Rectangle;import javax.swing.JComponent;

public class Celda extends JComponent implements Constantes {

public int x;public int y;public boolean celdaSeleccionada;public Rectangle rectanguloCelda;//constructorpublic Celda(int x,int y) {

this.x=x;this.y=y;this.celdaSeleccionada=false;rectanguloCelda=new Rectangle(x,y,PIXEL_CELDA,PIXEL_CELDA);

}

@Override

14

public void paintComponent(Graphics g) {g.drawRect(x,y,PIXEL_CELDA,PIXEL_CELDA);if ( celdaSeleccionada ) {g.fillRect(x,y,PIXEL_CELDA,PIXEL_CELDA);

}}

//si el click esta sobre la celdapublic void comprobarSiCeldaSeleccionada(int clickX,int clickY) {

if ( rectanguloCelda.contains(new Point(clickX,clickY)) ) {celdaSeleccionada = !celdaSeleccionada;

}

}}

La clase Escenario quedaría con el mismo código fuente.

0.5.1. Eventos de ratón

Los evento de ratón se enseñan para mostrar al alumno como se debenimplementar. Sin embargo, los eventos de ratón no se utilizarán mucho a noser que se quieran utilizar para para colocar determinados objetos, es decir,que el usuario pueda diseñar sus propios escenarios.

El primer paso consiste en añadir un escuchador a la clase Lienzo me-diante el método addMouseListener cuyo argumento es un objeto MouseAdapterque posee un método que implementa el método mouseClicked encargadode implementar la funcionalidad de capturar un evento de ratón. Cuandosobrescribimos ese método lo que hacemos es implementar la nueva funcio-nalidad al hacer click, en este caso deberemos identificar la celda que se hapulsado comprobando que la coordenada X e Y del ratón pertenece alguna delas celdas previamente creadas. Esto lo realizaremos con un método privadodenominado activarCelda(MouseEvent evt).

La clase MundoVirtual quedaría de la siguiente forma.

import java.awt.Canvas;import java.awt.Color;import java.awt.Graphics;import java.awt.event.KeyEvent;import java.awt.event.MouseAdapter;import java.awt.event.MouseEvent;

public class MundoVirtual extends Canvas implements Constantes{

//para pintar el lienzopublic Escenario escenario;public Entidad entidad;

public MundoVirtual(){

0.5. EXTRA: EVENTOS DE RATÓN 15

escenario=new Escenario();entidad=new Entidad(0,0,escenario);

//color de fondothis.setBackground(Color.orange);this.setFocusable(true);

//añadimos el escuchadoraddMouseListener(new MouseAdapter() {

@Overridepublic void mouseClicked(MouseEvent evt) {

activarCelda(evt);repaint();

}});

//escuchador eventos de tecladoaddKeyListener(new java.awt.event.KeyAdapter() {

@Overridepublic void keyPressed(KeyEvent evt) {

moverCelda(evt);repaint();

}});

}//metodo llamada la primera vez que se pinta@Overridepublic void paint(Graphics g) {

escenario.paintComponent(g);}

private void activarCelda(MouseEvent evt) {for(int i=0; i < NUMERO_CELDAS_ANCHO ; i++)

for ( int j=0 ; j < NUMERO_CELDAS_LARGO; j++)escenario.celdas[i][j].comprobarSiCeldaSeleccionada(evt.getX(),

evt.getY());}

public void moverCelda( KeyEvent evento ) {

switch( evento.getKeyCode() ) {

case KeyEvent.VK_UP:

System.out.println("Mover arriba");entidad.mover_Arriba();

break;

case KeyEvent.VK_DOWN:

System.out.println("Mover abajo");entidad.mover_Abajo();

16

break;

case KeyEvent.VK_LEFT:

System.out.println("Mover izquierda");entidad.mover_Izquierda();

break;

case KeyEvent.VK_RIGHT:

System.out.println("Mover derecha");entidad.mover_Derecha();

break;

}}

}

Las clases VentanaPrincipal y Main quedarían igual.

0.6. Para prácticar

Una tarea interesante sería poder desplazar una entidad de un sitio aotro del escenario pulsando una celda, manteniendo pulsado y llevándola adonde queramos del mismo.

Capítulo 4.Interacción y Movimientos

Qué vamos a aprender en este capítulo

En este capítulo vamos a explicar cómo se puede tener diferentes tipos deentidades en el escenario y cómo podemos crear cierta interacción entre ellaspara dotarlas de inteligencia, aunque ésta sea muy sencilla y solo permitirámovimientos autónomos y aleatorios.

Añadiendo entidades sin movimiento y con inter-acción al escenario

Una forma de situar nuevas entidades en el escenario es cambiar el pin-tado que se hacia habitualmente con las celdas. Hasta ahora el pintado deceldas consitía en dibujar un réctangulo sin fondo. Así podiamos tener unamatriz de celdas que llamabamos escenario. Por tanto si queremos hacer queuna, dos o tres celdas se pinten de forma diferente en el escenario podemoshacerlo. La clave está en tener alguna variable que nos diga de que tipo serácada una de ellas. Como el pintado del escenario depende de la clase celdapodemos extender su paintComponent para que en función del tipo que que-ramos visualizar se opte por esa opción. Por tanto el primer paso consiste enexteder dicha clase.

import java.awt.Color;import java.awt.Graphics;import javax.swing.JComponent;

/**** @author klemenzza

*/public class Celda extends JComponent implements Constantes {

public int xPixel;public int yPixel;public char tipo;

1

2

//constructorpublic Celda(int x,int y,char t) {

this.xPixel=x;this.yPixel=y;this.tipo=t;

}

@Overridepublic void paintComponent(Graphics g) {

switch(tipo) {case CAMINO:

g.drawRect(xPixel,yPixel,PIXELS,PIXELS);break;case OBSTACULO:

g.fillRect(xPixel,yPixel,PIXELS,PIXELS);break;case AMOR:

g.setColor(Color.pink);g.fillRect(xPixel,yPixel,PIXELS,PIXELS);g.setColor(Color.black);

break;}

}

}

Como se puede observar ahora contamos con un nuevo atributo del ti-po char que tiene información del tipo que se debe pintar al instanciar unobjeto de la clase Celda. Tendremos tres tipos: CAMINO, OBSTACULO, AMOR.El tipo CAMINO dibuja una celda de la forma habitual (un rectángulo simplesin fondo). El tipo OBSTACULO dibujará un rectángulo con fondo negro y sufuncionalidad será impedir el paso de la entidad o de los adversarios (verFigura 1). Por último el tipo AMOR pintará un rectángulo rosa y su funcionali-dad será la de modificar el estado de la entidad de alguna forma (lo veremosmás adelante).

Figura 1: Nuevas entidades sin movimiento en el escenario con alguna inter-acción respecto de la entidad

3

Como hemos añadido nuevos elementos en el escenario necesitamos re-programar el comportamiento del agente extendiendo los movimientos de laentidad teniendo en cuenta que ahora cuando se mueva a alguna celda cer-cana podría toparse con alguno de estos elementos (una pared, una celda deamor o un adversario). Por tanto antes de mover preguntaremos qué tipo decelda es a la que nos queremos mover y en función del tipo que sea imple-mentaremos un tipo de interacción u otra. Veamos primero como queda laclase Entidad.

import java.awt.Color;import java.awt.Graphics;import javax.swing.JComponent;

/**** @author klemenzza

*/public class Entidad extends JComponent implements Constantes{

public int xMov;public int yMov;public String estado;

public Entidad(int x,int y) {xMov=x;yMov=y;estado="VIVO";

}

public void moverArriba() { yMov-=1; }

public void moverAbajo() { yMov+=1; }

public void moverIzquierda() { xMov-=1; }

public void moverDerecha() { xMov+=1; }

//algoritmo de pintado de una entidad@Overridepublic void paintComponent(Graphics g) {

g.drawRect(xMov*PIXELS+DESPLAZAMIENTO,yMov*PIXELS+DESPLAZAMIENTO,PIXELS,PIXELS);

g.setColor(Color.green);//cambiamos colorg.fillRect(xMov*PIXELS+DESPLAZAMIENTO,

yMov*PIXELS+DESPLAZAMIENTO,PIXELS,PIXELS);g.setColor(Color.BLACK);//volvemos a cambiarg.setFont(FUENTE);g.drawString("E",(xMov*PIXELS+DESPLAZAMIENTO)+5,(yMov*PIXELS+DESPLAZAMIENTO)+28);g.setColor(Color.white);//volvemos a cambiarg.fillRect((xMov*PIXELS+DESPLAZAMIENTO),

(yMov*PIXELS+DESPLAZAMIENTO)-15,PIXELS,PIXELS/2);

4

g.setColor(Color.BLACK);//volvemos a cambiarg.setFont(FUENTE2);g.drawString(estado,

(xMov*PIXELS+DESPLAZAMIENTO)+1,(yMov*PIXELS+DESPLAZAMIENTO)-5);

}}

Junto a la E aparece el estado de la entidad: VIVO, si la entidad estáviva; AMOR, si la entidad paso por una celda del tipo AMOR; PARED, si laentidad chocó con obstáculo negro; MUERTO, si la entidad fue capturadapor le adversario. Para completar la implemetación añadiremos tres nuevasconstantes y un método especial que nos permite mostrar mensajes gráficosmediante una ventana de diálogo.

import java.awt.Font;

public interface Constantes {

// el resto permanece igual

public final Font FUENTE2=new Font("Times NewRoman",Font.BOLD,10);

//para la interaccionpublic final char OBSTACULO=’O’;public final char CAMINO=’C’;public final char AMOR=’A’;

default void lanzar_mensaje(String mensaje) {JOptionPane.showMessageDialog(null,mensaje,"Mundo Virtual",JOptionPane.PLAIN_MESSAGE);

}}

Antes de pasar al MundoVirual para implementar la interacción veamoscomo queda el constructor de Escenario y la diferencia con la definiciónde las Celdas. Ahora cada Celda lleva el tipo CAMINO. Todas las celdas soncamino inicialmente, será en MundoVritual donde irémos definiendo cómoserá nuestro mundo.

public Escenario() {

celdas=new Celda[NUMERO_CELDAS_ANCHO][NUMERO_CELDAS_LARGO];//inicializar el array de celdas

for(int i=0; i < NUMERO_CELDAS_ANCHO; i++) {

5

for ( int j=0 ; j < NUMERO_CELDAS_LARGO ; j++) {

celdas[i][j]=new Celda(i*PIXELS+DESPLAZAMIENTO,j*PIXELS+DESPLAZAMIENTO,CAMINO);

}}

}

Ahora extendemos la funcionalidad de movimientos en MundoVirtual. Laclase MundoVirtual quedaría de la siguiente forma. Primero mostramos losatributos y la definición del MundoVirtual

import java.awt.Canvas;import java.awt.Color;import java.awt.Graphics;import java.awt.event.KeyAdapter;import java.awt.event.KeyEvent;import javax.swing.JOptionPane;

/**** @author klemenzza

*/public class MundoVirtual extends Canvas implements Constantes{

//para pintar el lienzopublic Escenario escenario;public Entidad ent;public Adversario adv1;

public MundoVirtual(){

escenario=new Escenario();ent=new Entidad(1,1);//se indican donde estarán los obstaculosescenario.celdas[2][2].tipo=OBSTACULO;escenario.celdas[2][3].tipo=OBSTACULO;//y la salidaescenario.celdas[10][8].tipo=AMOR;//adversariosadv1=new Adversario("#1",5,5);

//color de fondothis.setBackground(Color.orange);this.setFocusable(true); //queremos que escuches en este

elemento

//escuchador eventos de tecladoaddKeyListener(new KeyAdapter() {

@Overridepublic void keyPressed(KeyEvent evt) {

mover(evt);

6

repaint();}

});}

//metodo llamada la primera vez que se pinta@Overridepublic void paint(Graphics g) {

escenario.paintComponent(g);ent.paintComponent(g);adv1.paintComponent(g);

}

Una vez tenemos la funcionalidad para pintar nuestro mundo virtual mos-tramos como quedaría la lógica de los movimientos. El comportamiento denuestro mundo se implementa en varios métodos. El método mover es el en-cargado de implementar los cambios de estado en función de lo que hayaocurrido, en nuestro caso: solo podremos mover la entidad mientras esté vi-va, si es así identicamos el movimiento y comprobamos si la entidad pasopor la celda amor, si es así cambiamos su estado, si no es así chequeamossi tocamos al adversario. Si fuera ese caso, cambiamos el estado a MUER-TO e informaremos al usuario y además ya no nos podremos mover. Noteque también se implementar la funcionalidad de cambiar el estado a PAREDcuando nos topamos con una pared.

public void mover( KeyEvent evento ) {if ( !ent.estado.equals("MUERTO")) {

identificar_Tecla_Pulsada(evento);

if ( amor()) ent.estado="AMOR";else {

if ( !adversario() ) {if ( !ent.estado.equals("PARED"))

ent.estado="VIVO";}

else {

lanzar_mensaje("El adversario te atrapó");ent.estado="MUERTO";

}}

}}

7

public boolean amor() {return escenario.darCelda(ent.xMov,

ent.yMov).tipo==AMOR;}

public boolean adversario() {return ent.xMov==adv1.xMov && ent.yMov==adv1.yMov;

}

Una explicado el método principal que implementa la funcionalidad en lainteracción de ciertas entidades vamos a completar la explicación con el mé-todo identificar tecla pulsada que implementa los movimientos. Nos vamos acentrar un movimiento el resto se implementan de forma análoga

public void identificar_Tecla_Pulsada( KeyEvent evento ) {ent.estado="VIVO";switch( evento.getKeyCode() ) {

case KeyEvent.VK_UP:System.out.println("Mover arriba");

if ( ent.yMov > 0 ) {if (

escenario.celdas[ent.xMov][ent.yMov-1].tipo!=OBSTACULO) {

ent.moverArriba();

}else ent.estado="PARED";}

break;

//...resto de movimientos

}}

En este caso si xMov es mayor que cero y la celda a la que queremos ir esdiferente de pared entonces nos moveremos. En caso contrario, la entidad sequedará en el mismo sitio. La clave está en la instrucción:

if ( escenario.celdas[ent.xMov][ent.yMov-1].tipo!=OBSTACULO )

que es donde se comprueba si la celda a la que me quiero mover (en estecaso arriba) es distinto del tipo OBSTACULO.

Por último, se puede observar que existe un atributo nuevo denomina-do Adversario adv1. Hemos decidido crear una clase Adversario que serádiferente a los otros elementos ya que tendrá método de movimientos (al fi-nal un algoritmo guiará al adversario). El código del adversario se muestra acontinuación:

8

import java.awt.Color;import java.awt.Graphics;import javax.swing.JComponent;

/**** @author klemenzza

*/public class Adversario extends JComponent implements Constantes{

public int xMov;public int yMov;public String nombre;

public Adversario(String n,int x,int y) {xMov=x;yMov=y;nombre=n;

}

public void moverArriba() { yMov-=1; }

public void moverAbajo() { yMov+=1; }

public void moverIzquierda() { xMov-=1; }

public void moverDerecha() { xMov+=1; }

//algoritmo de pintado de una entidad@Overridepublic void paintComponent(Graphics g) {

g.drawRect(xMov*PIXELS+DESPLAZAMIENTO,yMov*PIXELS+DESPLAZAMIENTO,PIXELS,PIXELS);g.setColor(Color.RED);//cambiamos colorg.fillRect(xMov*PIXELS+DESPLAZAMIENTO,yMov*PIXELS+DESPLAZAMIENTO,PIXELS,PIXELS);g.setColor(Color.BLACK);//volvemos a cambiarg.setFont(FUENTE);g.drawString("A",(xMov*PIXELS+DESPLAZAMIENTO)+5,(yMov*PIXELS+DESPLAZAMIENTO)+28);g.setColor(Color.white);//volvemos a cambiarg.fillRect((xMov*PIXELS+DESPLAZAMIENTO),

(yMov*PIXELS+DESPLAZAMIENTO)-15,PIXELS,PIXELS/2);g.setColor(Color.BLACK);//volvemos a cambiarg.setFont(FUENTE2);g.drawString(nombre,

(xMov*PIXELS+DESPLAZAMIENTO)+1,(yMov*PIXELS+DESPLAZAMIENTO)-5);

}

}

9

Finalmente el escenario con todos los elementos: entidad, obstáculos (ne-gros y rosas) y adversario quedaría cómo se muestra en la Figura 2. Noteademás que el tamaño de las celdas es mayor en esa visualización, basta conir a Constantes y poner la constante PIXELS a 48.

Figura 2: Nuevas entidades sin movimiento en el escenario con alguna inter-acción respecto de la entidad

Movimientos: animación e inteligencia básica

El objetivo de esta sección es explicar cómo se pueden animar las entida-des, esto es, proporcionales movimiento de forma automática. Para nosotrosanimar significa indicarle a la entidad qué pasos debe realizar, por tanto, seránuestra primera inteligencia, una inteligencia muy básica. Esta funcionali-dad puede implementarse en Java con Hilos de Ejecución. Informalmente,y en este contexto, un hilo de ejecución será una tarea (bloque de código) quese ejecuta cada cierto tiempo de forma concurrente o paralela junto a otroshilos de ejecución (el propio método main() se ejecuta como un hilo).

En Java existen varias formas de implementar un hilo de ejecución (he-renado de la clase Thread o implementando la clase Runnable). También sepuede utilizar la funcional de los TimerTask. Un TimerTask puede lanzarsecada X segundos por un lanzador de tareas. En cada lanzamiento se ejecutael código situado en el método run(). Una primera implemtación de la claseInteligencia podría ser la siguiente:

import java.util.TimerTask;

public class Inteligencia extends TimerTask implements Constantes{

public MundoVirtual mv;

public Inteligencia() {

}

@Override

10

public void run() {int movAleatorio=numeroAleatorio(0,3);System.out.println(" Movimiento generado:"+movAleatorio);

switch(movAleatorio) {case 0:

System.out.println(" Entidad : mover arriba ");if ( mv.ent.yMov > 0 ) {

if (mv.escenario.celdas[mv.ent.xMov][mv.ent.yMov-1].tipo!=OBSTACULO) {

mv.ent.moverArriba();

}}break;

case 1:System.out.println(" Entidad : mover abajo ");if ( mv.ent.yMov < NUMERO_CELDAS_LARGO-1 ) {if (

mv.escenario.celdas[mv.ent.xMov][mv.ent.yMov+1].tipo!=OBSTACULO){

mv.ent.moverAbajo();

}}break;

case 2:System.out.println(" Entidad : mover derecha ");if ( mv.ent.xMov!=0) {

if (mv.escenario.celdas[mv.ent.xMov-1][mv.ent.yMov].tipo!=OBSTACULO){

mv.ent.moverIzquierda();}

}break;

case 3:System.out.println(" Entidad : mover izquierda ");if ( mv.ent.xMov < NUMERO_CELDAS_ANCHO-1 ) {

if (mv.escenario.celdas[mv.ent.xMov+1][mv.ent.yMov].tipo!=OBSTACULO)

mv.ent.moverDerecha();}break;

}

if ( fin()) {System.out.println(" [ Entidad : LLegaste al final ");this.cancel();

11

}else {System.out.println(" [ Entidad : Actualizando ");actualizarMundoVirtual();

}

}

public boolean fin() {return

mv.escenario.darCelda(mv.ent.xMov,mv.ent.yMov).tipo==AMOR;}

public void actualizarMundoVirtual() {mv.repaint();

}

}

Si observamos la clase tiene un atributo MundoVirtual que será la infor-mación que la inteligencia artificial tiene del mundo en el que se encuentra.

El método run() es donde se implementa la inteligencia de la Entidad.En este caso es muy sencilla: se genera un número aleatorio entre 0 y 3. Sies 0 la entidad se mueve arriba, si es 1 la entidad se mueve hacia abajo,si es 2 se mueve a la derecha y si es 3 se mueve a la izquerida. En todoslo casos imprimimos un mensaje que indica que la entidad se ha movidoy en qué dirección. Note que la inteligencia sustituye a la parte del códigoque teniamos implemntadas para los eventos de teclado. Ya que ahora no esel humano quien dirigirá a la entidad, será la inteligencia artificial de cadaentidad quien lo haga. Por tanto la clase Entidad tiene un atributo de tipoInteligencia que no nos olvidemos hereda de TimerTask y por tanto es unhilo de ejecución. La clase Entidad queda de la siguiente forma:

import java.awt.Color;import java.awt.Graphics;import javax.swing.JComponent;

public class Entidad extends JComponent implements Constantes{

public int xMov;public int yMov;public String estado;public Inteligencia inteligencia;

public Entidad(int x,int y) {

xMov=x;yMov=y;estado="VIVO";inteligencia=new Inteligencia();

12

}

//el resto de la clase no sufre cambios

}

Por último una vez definido la entidad y su inteligencia (hilo de ejecución)necesitamos un objeto que lance o ejecute dichos objetos. Para ello haremosuso de la funcionalidad Timer que permite lanzar o ejecutar objetos Timer-Task. La clase VentanaPrincipal sufre un pequeño cambio:

import java.util.Timer;import javax.swing.JFrame;

public class VentanaPrincipal extends JFrame implements Constantes{

//nuestra clase se compone de un lienzo de dibujo (herada decanvas)

public MundoVirtual mundo_virtual;public Timer lanzadorEntidadesAutonomas;

//constructorpublic VentanaPrincipal() {

mundo_virtual=new MundoVirtual();this.getContentPane().add(mundo_virtual);//el tamaño de la venta es la del escenario y el incremento de

los bordesthis.setSize(ANCHURA_ESCENARIO,LARGO_ESCENARIO);

mundo_virtual.ent.inteligencia.mv=mundo_virtual;lanzadorEntidadesAutonomas=new Timer();lanzadorEntidadesAutonomas.

scheduleAtFixedRate(mundo_virtual.ent.inteligencia,0,1000);}

}

Las partes de código destacables son: la asignación del mundo virtual ala inteligencia (mundo_virtual.ent.inteligencia.mv=mundo_virtual;);la creación de un nuevo timer llamado lanzador de entidades autónomas;la llamada al método scheduleAtFixedRate que permite lanzar un objetoTimerTask cada cierto tiempo, en nuestro caso, cada segundo. De esta for-ma al ejecutar nuestra aplicación la entidad se irá movimiento de celda encelda de forma aleatoria. A partir de aquí comenzaremos a ver algoritmosde inteligencia artificial para resolver problemas, nuestra primera parada se-rá los basados en búsqueda en un espacio de estados de los que su mayorexponente es el algoritmo A estrella o también conocido por A*.

Anexo I.-Incorporarimágenes al mundo virtual

Clemente Rubio Manzano ([email protected])

Departamento de Sistemas de InformaciónUniversidad del Bío-Bío

Qué aprenderemos en este capítulo

Este capítulo aprenderemos a incluir imágenes en el mundo virtual. Vere-mos las opciones disponibles: seleccionar e incoporar una imagen de fondoen el mundo virtual, seleccionar e incorporar una imagen para la entidad,usar sprites y asociarlos a los respectivos movimientos.

Imagen de fondo del mundo virtual

En Java existen paquetes que permiten el manejo de imágenes. Las im-portantes son las siguientes:

java.io.File, para poder abrir un archivo guardado en memoria se-cundaria.

java.io.IOException, para manejar posibles errores en la aperturade tales archivos.

javax.imageio.ImageIO, para poder cargar archivos que sean imáge-nes, posee un método read() que devuelve una referencia a un objetodel tipo BufferedImage.

java.awt.image.BufferedImage, es un obejto que permite el manejode imagenes.

Es habitual emplear una imagen de fondo para dar más realismo al mun-do virtual. Por ejemplo, supongamos que queremos emplear una imagen conpasto para que sea el fondo de nuestro mundo virtual. Podríamos elegir unaimagen que la simule (ver Figura 1).

Para incorporar este fondo debemos modificar la clase MundoVirtual.javahaciendo uso de las funciones de carga y dibujo de imágenes. El código de

1

2

Figura 1: Fondo del mundo virtual

la clase MundoVirtual quedaría cómo sigue (solo se muestra las partes quecambian):

import java.awt.Graphics;import java.awt.Image;import java.awt.event.KeyAdapter;import java.awt.event.KeyEvent;import java.awt.image.BufferedImage;import java.io.File;import java.io.IOException;import java.util.logging.Level;import java.util.logging.Logger;import javax.imageio.ImageIO;

/**** @author klemenzza

*/public class MundoVirtual extends Canvas implements Constantes{

public Escenario escenario;public Image fondo; //aqui se declara el objeto que maneja el

fondo

public MundoVirtual(){//creamos un método privado que se encarga//de cargar la imagencargar_Imagen_Fondo();

escenario=new Escenario();

//color de fondo//this.setBackground(Color.orange);

3

this.setFocusable(true); //queremos que escuches en esteelemento

//escuchador eventos de tecladoaddKeyListener(new KeyAdapter() {

@Overridepublic void keyPressed(KeyEvent evt) {

identificar_Tecla_Pulsada(evt);repaint();

}});

}

public void identificar_Tecla_Pulsada( KeyEvent evento ) {

//no cambia

}

//metodo llamada la primera vez que se pinta@Overridepublic void paint(Graphics g) {

g.drawImage(fondo, 0, 0, null);escenario.paintComponent(g);

}

private void cargar_Imagen_Fondo() {try {

fondo = ImageIO.read(new File("imagenes/fondo.jpg"));

} catch (IOException ex) {

}}

}

Note que la imagen debería tener las dimensiones adecuadas y se deberíaajustar al tamaño del escenario para tener un control de todas las posicionesy bloques. Es importante destacar que la imagen fondo.jpg se encuentra enuna carpeta “imagenes” que crearemos a la altura del “src”.

4

Figura 2: Fondo del mundo virtual incorporado en la ventana y mostradojunto al escenario 2D

Imágenes sencillas para el resto de elementos: en-tidad, adversario y obstáculo

El objetivo de esta sección es mostrar cómo se puede diseñar los actoresque van intervenir en el mundo virtual. El primer paso consiste en elegir lasimágenes que se utilizaran para representar gráficamente a las entidades.Por simplicidad supondremos dos elementos: entidad y obstáculos. Comoocurre con el fondo para poder usar las imágenes en el proyecto NetBeanscopiaremos en la imagenes/ aquellas imágenes que hayamos seleccionadopara representar nuestros elementos. Recordar que el tamaño de las imá-genes debe ser similar al tamaño de las celdas indicadas en la interfaceConstantes para tener un mayor control del pintado y sobre todo para laimplementación de los algoritmos de navegación.

Al finalizar esta parte de la sección deberías ser capaz de poder crear unescenario 2D como el de la Figura 3. Notar que una vez hayas hecho estopodrás añadir los elementos que quieras y diseñar los mundos virtual más omenos complejos, más o menos simples. Esto es una decisión de diseño enla que no entramos por el momento.

Para crear una entidad con una imagen asociada vamos a extender laclase Entidad.java con un atributo que nos proporcionará dicha funcio-nalidad. Vamos a declarar public BufferedImage jugador; y en el cons-tructor trataremos de instanciarlo empleando la lectura de imagen que nosproporcionan los paquetes de Java:

jugador = ImageIO.read(new File("imagenes/NESninja64.png"));

Esta instrucción nos permite leer el archivo NESninja64.png situado en lacarpeta imagnes del proyecto, se crea un objeto File y éste es pasado comoargumento al método read de ImageIO que se encargará de crear un objetvode tipo BufferedImage. Finalmente, como ya tengo el objeto gráfico cargado

5

Figura 3: Fondo del mundo virtual incorporado en la ventana y mostradojunto al escenario 2D, las entidades y los obstáculos

puedo mostrarlo mediante la instrucción:

\verb+g.drawImage(jugador,xMov*PIXELS+DESPLAZAMIENTO,yMov*PIXELS+DESPLAZAMIENTO, null);

El código de la clase quedaría como sigue:

import java.awt.Graphics;import java.awt.image.BufferedImage;import java.io.File;import java.io.IOException;import java.util.logging.Level;import java.util.logging.Logger;import javax.imageio.ImageIO;import javax.swing.JComponent;

/**** @author klemenzza

*/public class Entidad extends JComponent implements Constantes{

public int xMov;public int yMov;public BufferedImage jugador;

public Entidad(int x,int y) {try {

xMov=x;yMov=y;jugador = ImageIO.read(new File("imagenes/NESninja64.png"));

6

Figura 4: Ejemplos Sprites

} catch (IOException ex) {Logger.getLogger(Entidad.class.getName()).log(Level.SEVERE,

null, ex);}

}

// MÉTODOS DE MOVER QUEDAN IGUAL

//algoritmo de pintado de una entidad

@Overridepublic void paintComponent(Graphics g) {

g.drawImage(jugador,xMov*PIXELS+DESPLAZAMIENTO,yMov*PIXELS+DESPLAZAMIENTO,null);

}}

0.1. Sprites

Los sprites (“duendes”) se emplean en videojuegos para crear los gráficosjugadores y adversarios. Se utilizan para producir una simulación de movi-miento, como un personaje corriendo, alguna expresión de la cara o algúnmovimiento corporal (ver Figura 4).

Para emplearlos se procede de forma simular a la carga de imágenes. Sinembargo ahora debemos recorrer la imagen e ir cargando de forma indivi-dual cada imagén por separado. Normalmente el proceso consiste en leersubimágenes e ir almacenándolas en un array (ver Figura 5)

A continuación, para implementar el proceso de carga de las subimágenesdebemos crear cuatro atributos en la clase MundoVirtual.java. Los dosprimeros se encargan de gestionar las rocas, los dos últimos los árboles.

public BufferedImage obstaculos[],imagenObstaculos;public BufferedImage arboles[],imagenArboles;

Ahora en el constructor lo que hacemos es leer el archivo princial de cadauno de ellos y obtener subimágenes a partir de ellos. Para ello en el con-

0.1. SPRITES 7

Figura 5: Proceso de lectura de un sprites y su almacenamiento de sussubimágenes en un array

tructor hacemos una llamada a dos métodos privados que se encargan deimplementar esta funcionalidad:

cargar_Imagenes_Obstaculos();cargar_Imagenes_Arboles();

La lógica de estos métodos es siempre la misma. Primero cargo la imagenprincipal empleando ImageIO y llamando al método read. Segundo, una veztengo la imagen cargada crearé un array de elementos tan grande como loselementos de mi sprite (2 en el caso de las rocas, 4 en el caso de los árbo-les). Entonces puedo utilizar el método subImage para obtener “trozos” de laimagen principal. Los sprites se diseñan para que cada uno de estos trozoscorresponda con un elemento gráfico. La implementación de estos métodoses como sigue:

/* IMPLEMENTACIÓN DEL MÉTODO QUE CARGA ÁRBOLES */

private void cargar_Imagenes_Arboles() {

try {

imagenArboles = ImageIO.read(new File("imagenes/treetop.png"));//creo una array de 4 imágenesarboles = new BufferedImage[4];//lo recorro separando las imagenesarboles[0] =imagenArboles.getSubimage(0,0,98,98);arboles[1] =imagenArboles.getSubimage(64,0,98,98);arboles[2] =imagenArboles.getSubimage(0,98,98,98);arboles[3] =imagenArboles.getSubimage(0,98,98,98);

} catch (IOException ex) {

}

}

8

/* IMPLEMENTACIÓN DEL MÉTODO QUE CARGA OBSTACULOS */

private void cargar_Imagenes_Obstaculos() {

try {

imagenObstaculos = ImageIO.read(new File("imagenes/rock.png"));//creo una array de 2obstaculos = new BufferedImage[2];//lo recorro separando las imagenesobstaculos[0] =imagenObstaculos.getSubimage(0,0,64,64);obstaculos[1] =imagenObstaculos.getSubimage(64,0,64,64);

} catch (IOException ex) {

}}