Vous êtes sur la page 1sur 23

PROYECTO PACMAN

INVESTIGACIN DE OPERACIONES II

ING. VILLEGAS CUBAS JUAN


2017
UNIVERSIDAD NACIONAL
PEDRO RUIZ GALLO
FACULTAD DE INGENIERA CIVIL, SISTEMAS Y
ARQUITECTURA

INVESTIGACIN DE OPERACIONES

ALUMNOS:
-CUMPA QUESQUEN EDGARD

-GUEVARA PANTA, JUAN FRANCISCO

-SALAZAR VILLANUEVA KEVIN

-VELSQUEZ CAJUSOL ALEX

LAMBAYEQUE, NOVIEMBRE 2017


NDICE
Historia del Juego ................................................................................................................................ 1
Modo de Juego .................................................................................................................................... 1
Fantasmas ........................................................................................................................................... 2
A. Fantasma rojo (Blinky) ................................................................................................... 2
B. Fantasma rosado (Pinky)............................................................................................... 2
C. Fantasma celeste (Inky) ............................................................................................ 2
D. Fantasma anaranjado (Clyde) ................................................................................. 3
Premios................................................................................................................................................ 3
Modos de movimiento ........................................................................................................................ 5
Describiendo el algoritmo de Pathfinding A* .................................................................................... 5
ILUSTRACIONES

ILUSTRACIN 1 PACMAN EST ENFRENTE, LA RUTA CALCULADA TERMINA ATRS DE PINKY. ........................ 2
ILUSTRACIN 2 INKY DECIDIENDO HACIA CUL CELDA DEBE CALCULAR LA MEJOR RUTA. .............................. 3
ILUSTRACIN 3 IMAGEN DEL JUEGO ORIGINAL ................................................................................................. 4
PACMAN

Historia del Juego


Pacman es un videojuego de arcade ampliamente conocido creado por Toru Iwatani en
1980, siendo uno de los ms populares de toda la historia de los videojuegos. Gracias a esta
fama, cualquier persona levemente familiarizada con el mundo de los videojuegos tiene una
nocin de su funcionamiento. El juego tiene un gran nmero de versiones y variantes, pero
la mayor a conserva el mismo planteamiento clsico. En el Pacman tpico (ver figura 1.1), el
jugador controla al personaje Pacman a travs de un laberinto, en el cual debe comer las
pldoras que en este se encuentra y evitar a otros cuatro personajes que aparecen, los
fantasmas. Adems, Pacman puede utilizar cuatro pldoras especiales para comer a los
fantasmas durante un periodo de tiempo.

Modo de Juego
El protagonista del videojuego Pacman es un crculo amarillo al que le falta un sector por lo
que parece tener boca. Aparece en laberintos donde debe comer puntos pequeos
(llamados Pacdots en ingls), puntos mayores y otros premios con forma de frutas y otros
objetos. El objetivo del personaje es comer todos los puntos de la pantalla, momento en
el que se pasa al siguiente nivel o pantalla. Sin embargo, cuatro fantasmas o monstruos,
Shadow (Blinky), Speedy (Pinky), Bashful (Inky) y Pokey (Clyde), recorren el laberinto para
intentar comerse a Pac-Man. Estos fantasmas son, respectivamente, de colores rojo, rosa,
cyan y naranja. Los fantasmas no son iguales, as mientras Blinky es muy rpido, y tiene la
habilidad de encontrar a Pacman en el escenario, Inky es muy lento y muchas veces evitar
el encuentro con Pacman.
Hay cuatro puntos ms grandes de lo normal situados cerca de las esquinas del laberinto
nombrados en ingls Power Pellets, y que proporcionan a Pac-Man la habilidad temporal
de comerse a los monstruos (todos ellos se vuelven azules mientras Pac-Man tiene esa
habilidad). Despus de haber sido tragados, los fantasmas se regeneran en "casa" (una caja
situada en el centro del laberinto). El tiempo en que los monstruos permanecen vulnerables
vara segn la pantalla, pero tiende a decrecer a medida que progresa el juego, y al cabo
de muchas pantallas los puntos especiales no tienen ningn efecto sobre los fantasmas.
Adems de comer los puntos, Pac-Man puede obtener puntuacin adicional si se come
alguno de los objetos que aparecen dos veces por pantalla justo debajo de la caja en el [1]
centro del laberinto de donde salen los monstruos. El objeto cambia cada pantalla o dos, y
su valor en puntos aumenta, de forma que dos cerezas (el premio de la primera pantalla)
valen 100 puntos, mientras que el ltimo objeto, la llave, vale 5.000.

Fantasmas
Cada uno posee una personalidad, la cual (ya hablando de forma tcnica) es la celda meta
que buscar cada fantasma. Esta caracterstica, junto a los modos de movimiento, logra una
aparente persecucin colaborativa, aunque en realidad no haya comunicacin entre los
fantasmas.
A. Fantasma rojo (Blinky)
Utiliza la forma ms bsica de persecucin, el de ruta ms corta y su meta objetivo es en s
la posicin actual de Pacman, utilizando el algoritmo de pathfinding de manera tradicional.

B. Fantasma rosado (Pinky)


La ruta que calcula est basada en el siguiente paso que dar Pacman, es decir, que para
encontrar el mejor camino toma como destino la celda enfrente de la posicin de Pacman.
Por ello Pinky tiende a desviarse del camino obvio cuando Pacman avanza en la misma lnea
y en direccin opuesta a l (Ver Imagen 2).

Ilustracin 1 Pacman est enfrente, la ruta calculada termina atrs de Pinky.

C. Fantasma celeste (Inky)


No comienza a acosar a Pacman mientras ste no haya consumido 30 puntos. Su
peculiaridad radica en la casilla que toma como meta. Dicha celda se calcula en tres pasos:
Identificar la segunda celda en frente de Pacman.
[2]
Calcular la distancia X y la distancia Y desde l hasta dicha celda. - La celda objetivo est en
la posicin (2*X, 2*Y) tomando a Inky como (0,0). Esta nueva celda ser la que Inky utilizar
como meta al momento de calcular su ruta de movimiento (Ver Imagen 3)
- .

Ilustracin 2 Inky decidiendo hacia cul celda debe calcular la mejor ruta.

D. Fantasma anaranjado (Clyde)


Vara su modo de persecucin dependiendo de la distancia a la que se encuentre de Pacman; si la
distancia es ms de ocho celdas, se mantiene en modo Chase, con el algoritmo A* al igual que Blinky. Si
la distancia de Pacman es de menos de ocho celdas, Clyde entra en modo Scatter, aunque no est en su
esquina correspondiente. El clculo de la distancia se realiza cada vez que se desplaza una celda.

Premios
A lo largo del juego, Pac-Man puede encontrar diversos premios:

Nivel 1: Cereza 50 puntos.

Nivel 2: Fresa 300 puntos.

Niveles 3 y 4: Naranja 500 puntos.

Niveles 5 y 6: Manzana 700 puntos.

Niveles 7 y 8: Uvas 1000 puntos.

Niveles 9 y 10: Galaxian 2000 puntos.


[3]
Niveles 11 y 12: Campana 3000 puntos.

Niveles 13 al 255: Llave 5000 puntos.


En cada nivel aparecen dos veces los premios.

Si Pacman pierde una vida cuando aparece un premio, este desaparece a la vida siguiente.

Puedes ver el juego en accin en este vdeo en You Tube:


http://www.youtube.com/watch?v=XlPq7dgQrQ0

Ilustracin 3 Imagen del juego original

[4]
Modos de movimiento

En Pacman existen 3 modos de movimiento, agrega el artculo de dicha pgina, es decir


formas en que cada fantasma ejecuta su algoritmo de avance. Estos modos son comunes
para los 4 de la siguiente manera:

Modo Chase (persecucin): Normal, toma la posicin de Pacman como parmetro para su
persecucin.

Modo Frightened (asustado): Se activa cuando Pacman se come un power up y consiste


en huir de Pacman a una velocidad reducida.

Modo Scatter (dispersin): Se activa para cada fantasma en una esquina distinta del
escenario, se abandona el algoritmo normal de movimiento y recorre un camino ya
predefinido

Describiendo el algoritmo de Pathfinding A*

Segn nos comenta Danilo Cuevas, en su informe final de proyecto de graduacin para optar al ttulo de
ingeniero civil en informtica: Anlisis de Algoritmos Pathfinding. Este algoritmo es una mejora del
algoritmo de Dijkstra y trabaja considerando el escenario como una rejilla con muchas celdas: tenemos
el punto de inicio dentro de una y nuestro punto objetivo en otra. El algoritmo consiste en calcular un
costo general para cada celda que rodee la casilla que se est analizando, se elige la celda de menor
costo general y luego esta misma se convertir en la celda de anlisis, teniendo como casilla padre
aqulla que acabamos de abandonar. Danilo nos aclara que en este algoritmo los valores de los costos
de movimiento pueden variar dependiendo de la situacin que se est simulando; pero es evidente que
un videojuego base Pacman no es necesario que se haga esta distincin, ya que es igualmente costoso
trasladarse de una celda a otra en todo el escenario.

Antes de profundizar en este algoritmo, necesitamos entender unos conceptos:

G(N): Es el costo de movimiento al trasladarse de una celda a otra. Este se asigna cuando se est
implementando el algoritmo.
[5]
H(N): Es el costo de desplazamiento desde la celda analizada hasta la celda objetivo. Este valor se calcula
utilizando una heurstica especificada al implementar el algoritmo A*.
F(N): Aqu lo llamamos costo general y es la suma H(N) y G(N).

Nodo: instancia de una clase que expresa caractersticas de una celda: identificador, nodo
predecesor, fue analizada o no, G(N), H(N), F(N). En general para este algoritmo hablaremos de
nodos en lugar de celdas.

Lista abierta: una lista enlazada donde vamos agregando los nodos que debemos de analizar.

Lista cerrada: una lista enlazada donde estn agregados y se seguirn agregando nodos que ya no
debemos analizar. En este sentido el mejor camino se busca de esta manera:

Este resumen de algoritmo, es una adaptacin de la amplia explicacin que Danilo Cuevas Guzmn 4 nos
hace.

Aplicado a Pacman, el primer nodo de la ruta es la posicin del fantasma, el segundo nodo es la celda a
donde moverse.
[6]
PAQUETE TEST (MAIN)

1 package Test;
2 import javax.swing.JFrame;
3 import level.Board;
4 public class Pacman extends JFrame{
5 /**
Constructor. Inicializa los atributos correspondientes a JFrame
y aniade un objeto Board (hijo de JPanel).
6 */
7 public Pacman()
8 { add(new Board());
9 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
10 setSize(570, 676);
11 setLocationRelativeTo(null);
12 setTitle("Pacman");
13 setVisible(true);
14 }
15 public static void main(String [] args)
16 {
17 new Pacman();

18 }
19 }

PAQUETE ACTORS

CLASE GHOST

import graphics.AnimationManager;
import java.awt.Image;
import java.awt.Rectangle;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Date; [7]
import java.util.HashMap;
import java.util.Random;
import javax.swing.ImageIcon;
import actors.Movable;

/**
* Clase Ghost que maneja las instancias de los fantasmas. Hereda
* de la clase Movable que gestiona la accin en general del movimiento.
* En esta clase se gestiona la inteligencia artificial de los fantasmas
* en base a los parametros HvsV (Horizontal vs Vertical) y accuracy_threshold (lmite
* de eficacia).
*
* El ancho y el alto (width y height) son virtuales. No corresponden con el
* ancho y alto de la imagen del fantasma. De esta manera podemos permitir cierto
* solapamiento para dar la sensacin de que efectivamente han tocado al jugador ya que
* han invadido el espacio del mismo.
*
* Gracias al sistema de configuracin va JSON y al algoritmo de bsqueda mediante
* los parmetros anteriormente citados, no es necesario crear una clase
* especfica por cada fantasma.
*
*
* @see Movable
*
*/

public class Ghost extends Movable{

/**
* Propiedades de la clase Ghost:
* has_collided - Utilizada para determinar si ha colisionado con otro
* fantasma, agilizar las busquedas de colisiones, y evitar
* problemas con dobles colisiones.
* start_dead_time - Hora en milisegundos a la que empez el estado DEAD
* state - Estado actual del fantasma.
* width - Ancho virtual del fantasma
* height - Altura virtual del fantasma
* HvsV - Preferencia a la hora de elegir buscar en horizontal o
* vertical. (A menor valor, es ms probable que elija horizontal
* y viceversa)
* accuracy_threshold - lmite o umbral de eficacia. Cuanto menor sea el valor menos
* se preferir buscar al jugador. De esta manera se evitan
* atascos del fantasma recalculando la prxima direccin. [8]
* id - Identificador del fantasma (red, blue, orange y pink)
* imageset - Set de imagenes del fantasma
* name - Nombre del fantasma (definido en el fichero JSON)
*/

private boolean has_collided;


private ArrayList<String> collides_with;
private long start_dead_time;
private int state, width, height, HvsV, accuracy_threshold;
private String id, imageset, name;

/**
* Sistema de animacion
*/
private AnimationManager anim_manager;

/**
* Variables estticas para definir los estados del fantasma.
* CHASING - Persiguiendo
* SCARY - Asustado
* DEAD - Muerto
*/

public static final int CHASING = 1;


public static final int SCARY = 2;
public static final int DEAD = 3;

/**
* Constructor.Inicializa los valores iniciales del fantasma. El ancho y alto
* se asignan automaticamente por el parametro size. Se reduce en un 90% el tamanio
* para dar la sensacion de solapamiento sobre el jugador u otros fantasmas.
*
*
* @param x Posicion X inicial del fantasma
* @param y Posicion Y inicial del fantasma
* @param size Tamanio virtual del fantasma. Se asigna a ancho y alto automaticamente
* transformandolo a un 90% del valor dado.
* @param id Identificador del fantasma
* @param imageset Set de imagenes
* @param HvsV Preferencia de horizontal vs vertical
* @param accuracy_threshold limite de precisin del fantasma.
*/
[9]
public Ghost(int x, int y, int size, String id, String imageset, int HvsV, int accuracy_threshold)
{
super(x, y);
state = CHASING;
this.id = id;
this.imageset = imageset;
this.HvsV = HvsV;
this.accuracy_threshold = accuracy_threshold;
this.width = (int)(((long) size) * 0.95);
this.height = (int)(((long) size) * 0.95);
this.has_collided = false;
this.collides_with = new ArrayList<String>();
loadImageSet();
}

/**
* Devuelve el identificador del fantasma.
* @return Un String con el id del fantasma
*/

public String getID() {


return id;
}

/**
* Establece si el fantasma ha chocado o no.
* @param collided boolean que define si ha colisionado o ha dejado de colisionar
*/

public void setHasCollided(boolean collided) {


has_collided = collided;
}

/**
* Metodo para saber si esta colisionando con un fantasma en concreto
* @param id id del fantasma
* @return true si esta colisionando con el fantasma informado
*/
public boolean isCollidingWithGhost(String id){
return (collides_with.indexOf(id) != -1);
}
[10]

/**
* Metodo para quitar de la lista el fantasma con el que esta colisionando
* @param id identificador del fantasma a quitar la colision.
*/
public void removeGhostCollision(String id) {
int ghost_index = collides_with.indexOf(id);
if (ghost_index >= 0) {
collides_with.remove(ghost_index);
}
}

/**
* Aniadir a la lista de fantasmas colisionados
* @param id anaiade el id del fantasma a la lista de fantasmas colisionados
*/
public void addGhostCollision(String id) {
int ghost_index = collides_with.indexOf(id);
if (ghost_index < 0) {
collides_with.add(id);
}
}

/**
* Devuelve el numero de fantasmas con los que esta colisionando
* @return numero de fantasmas con el que esta colisionando
*/

public int withManyGhostCollides(){


return collides_with.size();
}

/**
* Devuelve el estado del fantasma. Comparar con los atributos
* estticos de la clase.
*
* @return devuelve un entero con el estado del fantasma
* @see Movable
*
*/

public int getState() {


return state; [11]
}
/**
* Establece el estado del personaje. En caso de ser el estado DEAD,
* inicializa la hora a la que murio, para que empiece a hacerse la comprobacion
* de si debe volver a la vida.
*
* @param new_state nuevo estado, los valores posibles son los establecidos
* como variables estaticas de la clase.
*/

public void setState(int new_state) {


state = new_state;
if (new_state == DEAD)
{
start_dead_time = new Date().getTime();
}
}

/**
* Comprueba el tiempo que lleva en estado DEAD.
* En caso de llevar 5 segundos o ms, inicializa el valor
* de la hora a 0, y cambia el estado a CHASING.
*/

public void checkDeadTime() {


long new_time = new Date().getTime();
if (new_time >= start_dead_time + 5000) {
state = CHASING;
start_dead_time = 0;
}
}

/**
* Getter para saber si el fantasma ya ha colisionado con otro fantasma
* previamente.
* @return Devuelve un valor booleano indicando si ha colisionado
* o no.
*/

public boolean hasCollided() { [12]


return has_collided;
}
/**
* Mtodo con la lgica principal de bsqueda del fantasma.
* Se utilizan nmeros aleatorios y los valores de los atributos HvsV
* (horizontal vs vertical) y accuracy_threshold.
* Cuanto menor sea la configuracin de HvsV, es posible que el fantasma
* prefiera buscar en horizontal. Y cuanto menor sea el nmero de
* accuracy_threshold ser menos probable que el fantasma decida buscar al jugador.
*
* @param player_x posicion X actual del jugador
* @param player_y posicion Y actual del jugador
*/

public void calculateNextDirection(int player_x, int player_y) {

int rn;
Random r = new Random();

// Se ha establecido un umbral de error para no preferir siempre horizontal o


vertical
// para evitar estancamientos de los fantasmas.

// Cuanto mas bajo sea el numero aleatorio mas se preferira horizontal en base al
atributo HvsV
rn = r.nextInt(100);
boolean search_vertical = (HvsV < rn);

if (search_vertical) {
if (player_y > GetY()) {
setNextDirection(DOWN);
} else {
setNextDirection(UP);
}
} else {
if (player_x > GetX()) {
setNextDirection(RIGHT);
} else {
setNextDirection(LEFT);
}
} [13]

// Para evitar estancamientos, ahora decidiremos si vamos a ir en busca del jugador


rn = r.nextInt(100);
if (accuracy_threshold < rn) {
// No buscamos a pacman.
int nextdir;
do{
nextdir = r.nextInt(4);
}while(nextdir == getActualDirection());
setNextDirection(nextdir);
}

/**
* Carga las imgenes del fantasma
*
*/

private void loadImageSet() {


anim_manager = new AnimationManager();
try {

String[] anim = new String[2];


anim[0] = getImageSetFolder() + "up-1.png";
anim[1] = getImageSetFolder() + "up-2.png";

anim_manager.addAnimation("up", 200, 0, anim);

anim[0] = getImageSetFolder() + "down-1.png";


anim[1] = getImageSetFolder() + "down-2.png";

anim_manager.addAnimation("down", 200, 0, anim);

anim[0] = getImageSetFolder() + "left-1.png";


anim[1] = getImageSetFolder() + "left-2.png";

anim_manager.addAnimation("left", 200, 0, anim);

anim[0] = getImageSetFolder() + "right-1.png"; [14]


anim[1] = getImageSetFolder() + "right-2.png";
anim_manager.addAnimation("right", 200, 0, anim);

anim[0] = getImageSetFolder("scary") + "scary-1.png";


anim[1] = getImageSetFolder("scary") + "scary-2.png";

anim_manager.addAnimation("scary", 200, 0, anim);

anim = new String[1];


anim[0] = getImageSetFolder("dead") + "dead.png";

anim_manager.addAnimation("dead", 1000, 0, anim);

} catch (FileNotFoundException e) {

e.printStackTrace();
}

/**
* Funcion polimorfica: devuelve la ruta del set de imagenes del fantasma.
*
* @return Cadena de texto con la ruta de las imagenes.
*/

private String getImageSetFolder() {


return "../resources/images/actors/ghosts/" + imageset + "/";
}

/**
* Devuelve la cadena de texto con la ruta de la imagen. Esta version
* fuerza a otro set de imagenes.
*
* @param other_imageset
* @return Cadena de texto con la ruta de la imagenes.
*/

private String getImageSetFolder(String other_imageset) {


return "../resources/images/actors/ghosts/" + other_imageset + "/";
}
[15]
/**
* Devuelve la imagen en base al estado actual del fantasma.
* @return Devuelve la imagen.
* @see Image
*/

public Image getImage() {


//System.out.println();
switch(state){
default:
case CHASING:
switch(getActualDirection()) {
case Movable.UP:
return anim_manager.getImage("up");
case Movable.DOWN:
return anim_manager.getImage("down");
case Movable.LEFT:
return anim_manager.getImage("left");
case Movable.RIGHT:
default:
return anim_manager.getImage("right");
}

case SCARY:
return anim_manager.getImage("scary");
case DEAD:
return anim_manager.getImage("dead");
}
}

/**
* Devuelve un objeto Bounds con la posicion actual y tamanio virtual
* del fantasma.
*
* @return objeto Bounds con la posicion y tamao del objeto.
* @see Rectangle
*/

public Rectangle getBounds() {


Rectangle b = new Rectangle(GetX(), GetY(), width, height);
return b;
}
[16]

/**
* Establece el nombre del fantasma
* @param name
*/
public void setName(String name) {
this.name = name;
}

/**
* Devuelve el el nombre del fantasma.
* @return devuelve el nombre del fantasma.
*/
public String getName() {
return name;
}
}

[17]
[18]