first commit

This commit is contained in:
Jose Caban
2025-06-07 01:59:34 -04:00
commit 388ac241f0
3558 changed files with 9116289 additions and 0 deletions

116
CS1322/p6/MyWay/CCoord.java Normal file
View File

@@ -0,0 +1,116 @@
/**
* <PRE>
* Coord.java
*
* Revisions: 1.0 Nov. 12, 2002
* Created the Coord class
* 1.1 Nov. 12, 2002
* Compiled, Commented, Finished
*
* </PRE>
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Manuel Caban</A>
* @version Version 1.1, Nov. 12, 2002
*/
/*
*Everything from the Model and Control are extenstions of Coord for the
*simple reason that everything in the 2D world will be a coordinate on a Grid
*handled by a separate class
*/
public class CCoord {
/**
*Points
*/
private int x;
private int y;
private int h;
///////////////
//Constructor//
///////////////
/**
*Default Constructor
*sets all values to 0
*/
public CCoord(){
x = 0;
y = 0;
h = 0;
}
/**
*Takes in params for all values
*@param x, the x coordinate
*@param y, the y coordinate
*@param h, the height value
*/
public CCoord(int x, int y, int h){
this.x = x;
this.y = y;
this.h = h;
}
///////////////////////
//Accessors/Modifiers//
///////////////////////
/**
*@return value of x
*/
public int getX(){
return x;
}
/**
*@param x, the new x coordinate
*/
public void setX(int x){
this.x = x;
}
/**
*@return value of y
*/
public int getY(){
return y;
}
/**
*@param y, the new y value
*/
public void setY(int y){
this.y = y;
}
/**
*@return the height
*/
public int getH(){
return h;
}
/**
*@param h, the new height
*/
public void setH(int h){
this.h = h;
}
/*********************************************/
/**
* Debugging main for class Coord.
* This method will rigorously test my code.
*
* <br><br>
* @param args a String array of command line arguments.
*/
public static void main(String[] args) {
}// end of main(String[] args)
}// end of class Coord

View File

@@ -0,0 +1,77 @@
/**
* <PRE>
* CEngine.java
*
* Revisions: 1.0 Nov. 17, 2002
* Created the CEngine class
*
* </PRE>
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Manuel Caban</A>
* @version Version 1.0, Nov. 17, 2002
*/
public class CEngine {
/**
*Scan the input file and assign game map to the grid
*/
public void scanMap(String inFile){
try{
BufferedReader br = new BufferedReader(new FileReader(inFile));
}
catch(FileNotFoundException fnfe){
System.err.println("File not found - CNode.scanMap()" + fnfe);
}
//skip first 5 lines
try{
for(int i=0;i<5;br.readLine());
}
catch(Exception e){
System.err.println(e+" when attempting to skip 5 lines of file"+
" - CNode.scanMap()");
}
for(int x=0; x<15; x++){
String strTemp = br.readLine();
for(int y=0; y<15; y++){
switch(strTemp.charAt(z)){
case s:
grid[x][y] = new CTile(x,y,1,false);
grid[x][y].setSpecial(true);
grid[x][y].setNibble(false);
break;
case f:
grid[x][y] = new CTile(x,y,1,false);
grid[x][y].setParam(false);
break;
case n:
grid[x][y] = new CTile(x,y,1,false);
grid[x][y].setNibble(true);
grid[x][y].setSpecial(false);
break;
//create a wall by default
default:
case w:
grid[x][y] = new CTile(x,y,2,false);
break;
}//end switch
}//end for(y)
}//end for(x)
}
/**
* Debugging main for class CEngine.
* This method will rigorously test my code.
*
* <br><br>
* @param args a String array of command line arguments.
*/
public static void main(String[] args) {
}// end of main(String[] args)
}// end of class CEngine

View File

@@ -0,0 +1,69 @@
/**
* <PRE>
* CGrid.java
*
* Revisions: 1.0 Nov. 17, 2002
* Created the CGrid class
*
* </PRE>
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Manuel Caban</A>
* @version Version 1.0, Nov. 17, 2002
*/
import java.io.*;
public class CGrid {
/**
*2D array to store the game data
*/
private CCoord[][] grid;
////////////////
//Constructors//
////////////////
public CGrid(){
//create default size
this(15,15);
}
public CGrid(int iLength, int iHeight){
grid = new CCord[iLength][iHeight];
}
public CGrid(String inFile){
this(15,15);
this.scanMap();
}
public CGrid(String inFile, boolean varGrid){
if(varGrid){
scanMap(inFile);
}
else{
this(15,15);
scanMap(inFile);
}
}
///////////
//Methods//
///////////
/**
* Debugging main for class CGrid.
* This method will rigorously test my code.
*
* <br><br>
* @param args a String array of command line arguments.
*/
public static void main(String[] args) {
}// end of main(String[] args)
}// end of class CGrid

View File

@@ -0,0 +1,57 @@
/**
* <PRE>
* CIntelligence.java
*
* Revisions: 1.0 Nov. 13, 2002
* Created the CIntelligence class
*
* </PRE>
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Manuel Caban</A>
* @version Version 1.0, Nov. 13, 2002
*/
public class CIntelligence implements Constants{
public CGrid createNodeGrid(CGrid cGrid, int iFloorLevel){
CGrid cToSend = new CGrid(cGrid.getX(),cGrid.getY());
for(int x=0; x<cGrid.length; x++){
for(int y=0; y<cGrid[].length;y++){
if(cGrid[x][y].getH() == iFloorLevel){
cToSend[x][y] = new Coord(x,y,iFloorLevel);
}
}
}
for(int x=0; x<cGrid.length;x++){
for(int y=0; y<cGrid[].length;y++){
if(cToSend[x][y] != null){
createNode(cToSend,new Coord(x,y,iFloorLevel));
}
}
}
}
private void createNode(CGrid cToAdd, Coord cAt){
int x = cAt.getX();
int y = cAt.getY();
int h = cAt.getH();
/**
* Debugging main for class CIntelligence.
* This method will rigorously test my code.
*
* <br><br>
* @param args a String array of command line arguments.
*/
public static void main(String[] args) {
}// end of main(String[] args)
}// end of class CIntelligence

View File

@@ -0,0 +1,59 @@
/**
* <PRE>
* Monster.java
*
* Revisions: 1.0 Nov. 13, 2002
* Created the Monster class
*
* </PRE>
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Manuel Caban</A>
* @version Version 1.0, Nov. 13, 2002
*/
public class CMonster extends CCoord implements Constants{
/**
*Defines the Intelligence of the monster
*whether player controlled or CPU controlled
*/
private CIntelligence oIntel;
/**
*Define image for monster
*Icons should be named as follows:
*Iconname000.jpg, with the 000 incrementing up with each animation number
*When used with the constructor, just give the iconname without 000 and the
*constructor will search for the correct icon (so for a single icon lacking
*any animation, it would be iconname000.jpg and be sent to the constructor
*as iconname. Similarly, an icon with animations would have files
*icon000.x, icon001.x, and icon002.x and have the name "icon" send to the
*constructor.
*/
ImageIcon[] imgSprite;
////////////////
//Constructors//
////////////////
public CMonster(int x, int y, int h, String iconFile, CIntelligence oIntel){
super(x,y,h);
}
///////////
//Methods//
///////////
/**
* Debugging main for class Monster.
* This method will rigorously test my code.
*
* <br><br>
* @param args a String array of command line arguments.
*/
public static void main(String[] args) {
}// end of main(String[] args)
}// end of class Monster

106
CS1322/p6/MyWay/CNode.java Normal file
View File

@@ -0,0 +1,106 @@
/**
* <PRE>
* CNode.java
*
* Revisions: 1.0 Nov. 17, 2002
* Created the CNode class
* 1.1 Nov. 17, 2002
* Added Primary code, Commented, Finished
*
* </PRE>
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Manuel Caban</A>
* @version Version 1.1, Nov. 17, 2002
*/
/**
*The Engine class (CEngine) can take in a board and turn it into a game map.
*The Engine class then creates another grid which will below the game map
* that holds the nodes for the Intelligence class (CIntelligence).
*The Node classes hold the data for where a ghost can move and are used to
* send ghosts after the player. If pacman is between Node A and Node B and
* a ghost finds itself in Node A, it will turn and head in the direction of
* Node B. These Intelligence specifics are handled in the
* CIntelligence->CIntelGhost class
*/
public class CNode extends CCoord{
/**
*Define where the ghost can move when at this node
*/
private boolean MV_LEFT;
private boolean MV_RIGHT;
private boolean MV_UP;
private boolean MV_DN;
private boolean INC_HEIGHT;
///////////////
//Constructor//
///////////////
/**
*@param up, can move up?
*@param down, can move down?
*@param left, can move left?
*@param right, can move right?
*@param incH, can increase height by using node?
*@param x, x-coord
*@param y, y-coord
*@param h, h-coord
*/
public CNode(boolean up, boolean down, boolean left, boolean right,
boolean incH, int x, int y, int h){
super(x,y,h);
MV_UP = up;
MV_DN = down;
MV_LEFT = left;
MV_RIGHT = right;
INC_HEIGHT = incH;
}
///////////
//Methods//
///////////
/**
*@return true if can go up from node
*/
public boolean canMV_UP(){
return MV_UP;
}
/**
*@return true if can go down from node
*/
public boolean canMV_DN(){
return MV_DN;
}
/**
*@return true if can move left from node
*/
public boolean canMV_LEFT(){
return MV_LEFT;
}
/**
*@return true if can move right
*/
public boolean canMV_RIGHT(){
return MV_RIGHT;
}
/**
*@return true if can increase height at this node
*/
public boolean canINC_HEIGHT(){
return INC_HEIGHT;
}
/**
*@return Coordinates of the Node
*/
public CCoord getNodeCoord(){
return (new CCoord(this.getX(), this.getY(), this.getH()));
}
}// end of class CNode

View File

@@ -0,0 +1,80 @@
/**
* <PRE>
* Tile.java
*
* Revisions: 1.0 Nov. 12, 2002
* Created the Tile class
*
* </PRE>
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Manuel Caban</A>
* @version Version 1.0, Nov. 12, 2002
*/
public class CTile extends CCoord{
/**
*Defines whether the object is coming from the ground up to a given level
*or whether it is a floating object (i.e. a bridge)
*/
private boolean bIsFloating;
/**
*Define whether block has a special nibble
*/
private boolean bHasSpecial;
/**
*Define whether block has a nibble
*/
private boolean bHasNibble;
////////////////
//Constructors//
////////////////
/**
*@param x, the x-coord
*@param y, the y-coord
*@param h, the height
*/
public CTile(int x, int y, int h){
this(x,y,h,false);
}
/**
*Same as other constructor but adds
*@param bIsFloating, defines whether the object is floating or not
*/
public CTile(int x, int y, int h, boolean bFloating){
super(x,y,h);
bIsFloating = bFloating;
}
///////////////////////
//Accessors/Modifiers//
///////////////////////
/**
*@return true if object can be passed over
*/
public boolean isPassable(Coord op){
if(op.getH() == this.h){
return true;
}
return false;
}
/**
* Debugging main for class Tile.
* This method will rigorously test my code.
*
* <br><br>
* @param args a String array of command line arguments.
*/
public static void main(String[] args) {
}// end of main(String[] args)
}// end of class Tile

View File

@@ -0,0 +1,27 @@
/**
* <PRE>
* Constants.java
*
* Revisions: 1.0 Nov. 13, 2002
* Created the Constants class
*
* </PRE>
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Manuel Caban</A>
* @version Version 1.0, Nov. 13, 2002
*/
public interface Constants {
/*Directions*/
/**
*Define 4 movement constants: MV_UP, MV_DN, MV_LFT, MV_RGHT
*/
public static final int MV_UP = 0;
public static final int MV_DN = 1;
public static final int MV_LFT = 2;
public static final int MV_RGHT = 3;
}// end of class Constants

View File

@@ -0,0 +1,20 @@
0 7
1 1
13 1
13 13
1 13
w w w w w w w w w w w w w w w
w s f n n n n w n n n n f s w
w f w w w w n w n w w w w f w
w n w n n n n w n n n n w n w
w n w n w w w w w w w n w n w
w n w n n n n n n n n n w n w
w n w n n n w w w n n n w n w
f n n n n n w f w n n n n n w
w n w n n n w w w n n n w n w
w n w n n n n n n n n n w n w
w n w n w w w w w w w n w n w
w n w n n n n w n n n n w n w
w f w w w w n w n w w w w f w
w s f n n n n w n n n n f s w
w w w w w w w w w w w w w w w

View File

@@ -0,0 +1,20 @@
0 7
1 1
13 1
13 13
1 13
w w w w w w w w w w w w w w w
w s f n n n n n n n n n f s w
w f w w w w w n w w w w w f w
w n w f f f w n w f f f w n w
w n w w w w w n w w w w w n w
w n w n n n n n n n n n w n w
w n w n w n w w w n w n w n w
f n n n n n w f w n n n n n f
w n w n w n w w w n w n w n w
w n w n n n n n n n n n w n w
w n w w w w w n w w w w w n w
w n w f f f w n w f f f w n w
w f w w w w w n w w w w w f w
w s f n n n n n n n n n f s w
w w w w w w w w w w w w w w w

800
CS1322/p6/MyWay/p6.nfo.txt Normal file
View File

@@ -0,0 +1,800 @@
CS1322: Programming Assignment #6 - Fall 2002.
Assigned: November 11, 2002
Phase I Instant Feedback Due Date: November 21, 2002 11:59:59
Phase II Instant Feedback Due Date: November 25, 2002 11:59:59
DUE Date: November 25, 2002 11:59:59 PM
Program Title: Pacman
Files Provided
====================================================================
o P6.nfo.txt - this file
o board_1.txt - an input file that specifies board #1
o board_2.txt - an input file that specifies board #2
o GhostAI.class - a class that makes decisions on Ghost movement
o P6Constants.java - Interface needed for P6
(You must use these constants when appropriate)
o various image files
Learning Objectives
====================================================================
o Advanced DataStructures
o Advanced GUIs
Other Resources
====================================================================
Website containing screenshots and nfo file with pictures at
http://128.61.63.158/cs1322/P6
PROGRAM OVERVIEW
====================================================================
Welcome to the 6th and final programming assignemnt! A word of
caution: This project will be the most difficult one in this course.
You will be required to utilize all that you have learned in order to
complete this assignment. START EARLY!
This assignment will be very similar to the arcade classic - Pacman.
For those of you who aren't familiar with the game, Pacman is a little
yellow smiley face controlled by you, the user. The goal is to have
Pacman eat all the nibbles (dots on the board). If Pacman completes
this objective, you win the game. However, there are 4 ghosts
pursuing Pacman. If one of the ghosts catches him, the game is over.
Pacman has one defense however: the special nibbles placed on the
board. If Pacman eats a special nibble, the ghosts can be eaten by
Pacman for a time. During this period, the ghosts run away from
Pacman. If a ghost is eaten, it returns to its start location on the
board and resumes it's role of pursing and attempting to catch Pacman
even if the others are still running from Pacman. After a time, all
the ghosts return to the original state of pursuing Pacman.
HEY! guess what! START EARLY!
A Word About Imlementation and Grading
====================================================================
In the past five assignments, the TAs and nfo files have largely
specified exactly which methods, parameters, classes and fields you
would need to complete the assignment. This assignment will be
different. While we will supply a recommended design, the exact means
of implementation in terms of which methods or fields you use is left
ambiguous. With the exception of the Instant Feedback phases, you
will be largely responsible for figuring out which methods you wish to
create to solve a given problem. This assignment will be graded more
in terms of functionality i.e. "does this feature function correctly?"
rather than "is this method present with these exact parameters".
With this in mind, you can feel free to create whatever methods you
wish to solve the problem. In addition, those of you looking for a
challenge or wanting to fight "the man" can feel free to deviate as
much or as little as you want from the design below. (Again, with the
exception of the Instant Feedback phases. You must do what we tell
you there.) Keep in mind, however, that as you deviate from the
recommended design, it becomes difficult to give partial credit if
things don't work correctly. So if you do choose to go your own way,
do so at your own risk. If you are not yet comfortable leaving the
nest, the design below is provided.
<cough>Start Early!
Overall Design
====================================================================
In the MVC (Model, View, Controller) paradigm of GUI programming, we
separate parts of the program into three pieces:
The Model - This class or group of classes stores the state of the
data in the program. It is often a data structure such as a BST or
Heap. The model typically is independent of any GUI widgets. This
allows our program to be more portable. If we wanted to use our
data in some interface other than a GUI, say a text console
interface, there would be little to no recoding of the model.
The View - This gruop of classes is responsible for displaying the
GUI. Often they extend or hold GUI widget instances. We don't
want the view to hold the state of any data. This is the role of
the model.
The Controller - This class or group of classes is responsible for
manipulating the model according to the needs of the program.
Often is the listener of events from GUI widgets (as EventHandler
was in P5)
The UIDelegate paradigm of GUI programming is very similar to MVC.
The only difference is that in UIDelegate we combine the roles of View
and Controller into one class or group of classes.
Now an overview of the classes of Pacman:
Model - GamePiece, WallPiece, FloorPiece, MoveablePiece, Ghost, Pacman
View - GameBoard, PacmanFrame
Controller - PacmanFileReader, PacmanEngine, PacmanFrame
Notice PacmanFrame is both a View and a Controller. This means it
will be a UIDelegate class.
PHASE I - The Pieces - Instant Feedback
====================================================================
The classes in this phase will be model classes that represent the
tiles and pieces on the board. They will be independent of any GUI
widgets. They merely will hold information about the piece such as
where it is on the board.
Create a class called GamePiece. This class will be an abstract super
class to all pieces of the game board. GamePiece should include the
following:
o a protected int called "x". This variable will represent which
column the piece is in. i.e., it's location along the x-axis.
o a protected int called "y". This variable will represent which
row the piece is in. i.e., it's location along the y-axis.
Together these two variables represent the exact location of the
board this particular piece is in. The location 0,0 will be the
upper left hand corner of the game board.
o public accessors for both of the above variables.
o a public method called setPostition that takes in two
parameters: x followed by y and sets the location variables to
these parameters.
o a constructor that takes in two parameters: x followed by y and
sets the location variables to these parameters.
Create a class called FloorPiece. This class will extend GamePiece.
It will also add the functionality of nibbles (The dots that Pacman
eats) Each FloorPiece can have a regular nibble, a special nibble
(one that allows Pacman to chase the ghosts), or neither (but not
both). FloorPiece should have the following:
o a private boolean variable called "hasNibble". This variable
will represent whether or not this FloorPiece has a nibble that
Pacman eats (the regular ones, not the ones that lets Pacman
chase Ghosts)
o a private boolean variable called "hasSpecial". This variable
will represent whether or not this FloorPiece has a special
nibble that allows Pacman to chase Ghosts.
o accessors and modifiers for both of the above
o a constructor that takes in four parameters: the x and y
location and two booleans representing whether or not this FloorPiece
should have a nibble or a special. This constructor should chain
to the super constructor and set hasNibble and hasSpecial
according to the parameters.
Create a class called WallPiece. This class will extend GamePiece.
It will also add the functionality of a wall on the game board.
Neither ghosts nor Pacman can travel on or through walls. They can
only travel on FloorPieces. Your WallPiece class should have the
following:
o a constructor that takes in two parameters: the x and y
locations of this piece. This constructor should chain to the
super constructor.
Create a class called MoveablePiece. This class will be abstract and
extend GamePiece. This class will be the super class of both Ghost
and Pacman. MoveablePiece should have the following:
o a constructor that takes in two ints: the x and y location of
this piece. This constructor should chain to the
super constructor.
o a public method named "move" that returns void and takes in an
int representing a direction. This parameter will be either
NORTH, EAST, SOUTH, or WEST from P6Constants. This constants
will represent up, right, down and left respectively. In this
method you should "move" the piece. This means modify the
piece's x or y location accordingly. Don't forget that (0,0) is
the top left corner of the board.
PHASE II - Ghosts and Pacman - Instant Feedback
====================================================================
These two classes will be model classes for Pacman and an individual ghost.
Create a class called Pacman which extends MoveablePiece. This class
will model the Pacman piece which the user will control. Pacman
should include the following:
o a private int variable named "direction". This variable will
contain one of the four constants from P6Constants: NORTH, EAST,
SOUTH, WEST. This variable will represent the direction Pacman
is currently heading on the board. This variable will be
initialized to EAST.
o a constructor that takes in two ints: the x and y location of
this piece. This constructor should chain to the
super constructor.
Now the Ghosts...
Before we get into the details of implementing Ghost, let's talk
about how the Ghost will chase (or run from) Pacman.
Provided to you is a class called GhostAI. This class has a single
static method:
public static int[] getGhostMove(int ghostX, int ghostY,
int pacmanX, int pacmanY)
Here's what the parameters do
o ghostX: the x location of a ghost
o ghostY: the y location of a ghost
o pacmanX: the x location of Pacman
o pacmanY: the y location of Pacman
The return value will always be an int array of size four. The array
contains each of the four directions from P6Constants in the order of
priority. So:
o index 0: the ghost's first choice of directions to move
o index 1: the ghost's second choice of directions to move
o index 2: the ghost's third choice of directions to move
o index 3: the ghost's fourth (last) choice of directions to
move
So why bother with all this priority stuff? Why not just return the
direction the ghost wants to move. Well what if we have a case like
the following:
Ghost
Wall Wall Wall Wall Wall
Pacman
The ghost's first choice of movement will be SOUTH toward Pacman. But
that's not an option because of the wall. We don't want our Ghosts to
just sit if they can't go somewhere. So we would try our second
choice, which in this case would be either EAST or WEST. The last
choice, in index 3, would be NORTH away from Pacman. Consider the
following case:
Wall Wall
Wall Wall
Wall Ghost Wall
Wall Wall Wall
Pacman
In this case, none of the first three choices will contain viable
moves. So the ghost's only choice is to go up. If this happens,
there will be a gap between the Ghost and the bottom wall. On the
next turn the Ghost can indeed use its first choice and move SOUTH
(Given that Pacman isn't going anywhere for some reason) If Pacman
were to stay put, the Ghost would end up oscillating back and forth
against the bottom wall. What happens if the Ghost is running from
Pacman after Pacman ate a special nibble. The priorities are just
reversed. Logically this makes sense. If a ghost's first priority
was to move SOUTH towards Pacman while chasing Pacman, SOUTH will be
the last priority when Pacman is chasing the ghost. Unfortunately,
the Ghost artificial intelligence algorithm we provide is somewhat
primitive. Consider the following case:
Wall Wall Wall Wall Wall Wall
Wall
Pacman Wall Ghost
Wall Wall Wall
If Pacman were to never move, the Ghost would simply run up against
the West wall over and over. If the Ghost were smart, it would go
around the South wall to get to Pacman but it doesn't. You must use
the Ghost movement algorithm we provide for instant feedback.
However, for the final turn in, you can develop your own, better
movement algorithm if you wish. (Just make sure it works) This would
be worth some juicy extra credit.
Create a class called Ghost that extends MoveablePiece. This class
will represent a Ghost. There will be four instances in your program,
one for each of the four ghosts. Ghost should have the following:
o a private int variable called "ghostNum". This will be the
number of the ghost.
o a private boolean variable called "runAwayFlag". This will be
true if this Ghost is running from Pacman and false if chasing
Pacman.
o a private int variable called "runAwayCount". This will
contain the number of returns remaining for this ghost to run
from Pacman. (Remember that a ghost will run from Pacman for a
fixed number of moves if Pacman eats a special nibble)
o a private int variable called "startX". This will contain the
starting x coordinate for this ghost.
o a private int variable called "startY". This will contain the
starting y coordinate for this ghost.
The startX and startY variables together represent the start
coordinates for the Ghost. The ghost will begin the game at this
location. The ghost will also return to this location if eaten
by pacman when running from him.
o accessors for ghostNum, runAwayFlag, startX and startY
o a public void method named "runAway" which takes in no parameters.
This sets runAwayFlag to true and initializes runAwayCount to
RUN_AWAY_DURATION from P6Constants
o a public void method called "resetGhost" which takes in no
parameters. This method will be invoked if this ghost is eaten
by Pacman. It should turn runAwayFlag to false and return the
ghost to it's start coordinates.
o a public method named "getMove" that returns an int[] and takes
in two parameters: pacman's x and y location. This method will
calculate the array of size four containing this ghost's choices
of movement as described above. Feel free to use the GhostAI
class. Keep in mind however that the getGhostMove method
provided assumes the ghost is chasing pacman. You will need to
modify the array to have the ghost run away from Pacman if this
is the case. If the Ghost is indeed running from Pacman, be sure
to deduct a turn from runAwayCount and check to see if
runAwayCount is zero, in which case you should have the ghost no
longer run from Pacman. This method will not actually set the
location of the ghost based on the GhostAI method. It simply
returns the array of choices. We do this because this ghost has
no clue whether any of its choices will take it into a wall. A
class later in the project will process the choices and decide
which moves are legal for this ghost. The ghost will then be
told which way to move.
o a public constructor that takes in three parameters: an x and y
start location and a ghostNum representing this ghost's number.
You should set the instance variable accordingly.
PHASE III - PacmanFileReader
====================================================================
From this point, you can feel free to ignore the design and do your
own thing if you wish provided you supply all the functionality of the
design we recommend. Remember the warning about grading though.
Even if you choose to use your own design, your program must still
present equivalent capabilities. And if your program doesn't work and
you used a different design, there is less chance of getting partial
credit.
Also realize that from this point there will be less explicit
information on what methods you need or how to implement them. There
are a variety of implementations that fit the suggested design. You
can choose one that suits you. You do not need to ask if you can
create a particular method or field. If it seems like you need method
x to perform some task, by all means, do it.
Onward...
Create a class called PacmanFileReader. This class will be
responsible for reading in a board file like one of the two we
provided you and translating it into an array of GamePiece instances.
If you open up one of the board files, you'll notice several rows of
seemingly random characters. Let's discuss the tokens found in
board_1.txt:
w <==> P6Constants.WALL <==> an instance of WallPiece
f <==> P6Constants.FLOOR <==> an instance of FloorPiece w/out either
kind of nibbles
n <==> P6Constants.NIBBLE <==> an instance of FloorPiece with a
regular nibble
s <==> P6Constants.SPECIAL <==> an instance of FloorPiece with a
special nibble
The first five rows specify the starting locations of Pacman and the
ghosts.
0 7 <-- Pacman's x,y start coordinates
1 1 <-- Ghost #1's startX and startY instance variable values.
13 1 <-- Ghost #2's startX and startY instance variable values.
13 13 <-- Ghost #3's startX and startY instance variable values.
1 13 <-- Ghost #4's startX and startY instance variable values.
The next rows will contain the four tokens discussed above. There
will always be 15x15 tokens. Look at the picture on the project
website under "using board_1.txt". Can you see the relationship
between the text file and the appearance of the board?
In this class, the goal is to translate these tokens into an array of
GamePieces. We recommend your PacmanFileReader have the following:
o a private GamePiece[][] variable named "pieceArray". This
array will model the board seen in the GUI.
o a BufferedReader to read the board file
o instance variables for pacman and four ghosts
o a constructor that takes in the name of a file to read.
o a helper method or series of helper methods called by the
consturctor that does the following:
- instantiates a BufferedReader to read the board file
passed into the constructor
- Reads each of the first five lines of the board file, uses
a StringTokenizer to get the x and y start coordinates and
instantiates Pacman or each of the four Ghosts (depending on
which line you're on)
- for the rest of the fifteen lines: Keep two counters, one
for the x index of the board and one for the y index.
Read in a line and use a StringTokenizer to get each
token. Instantiate each cell of pieceArray to the
appropriate GamePiece subclass passing in the x and y
index as the GamePiece coordinates. After each token
increment the x counter. After each line increment y and
reset x.
PHASE IV - The GUI
====================================================================
In this phase we will code the View portion of our MVC design. There
are two classes: PacmanFrame and GameBoard which will extend JFrame
and JPanel, respectively. The GameBoard will be responsible for
drawing or painting the board according to the GamePiece array you
created in PacmanFileReader. Now it's time for our segway into
painting.
Let's talk a moment about paintComponent:
Every GUI component has a protected method called paintComponent that
is reponsible for making the GUI component look the way it does. The
unoverrided form draws the default appearance for the widget. For a
JPanel this is just huge blank gray area. For JButtons there is some
slight shadowing on the edges of the button. However, you can
override this method in a subclass of a GUI widget. In this overriden
method you can make the widget appear almost anyway you want. You can
draw lines, pictures, shapes, change the widget's color, etc.
The paintComponent() function receives a Graphics object that is
used to draw on the component which owns it (In this case a GameBoard).
It is important to know that you will NEVER call paintComponent directly;
if you would like to update your component, call repaint(). The swing
windowing environment will send the proper Graphics context to
paintComponent(). Familiarize yourself with all of the methods in the
Graphics component from the API so you can draw using your Graphics
object. The Graphics parameter can sort of be thought of as your
brush and the widget where paintComponent is overriden as your canvas.
An example of an overridden paintComponent function:
protected void paintComponent( Graphics g ) {
// I call the super paintComponent to draw the component the regular
//way first
super.paintComponent( g );
//set the Graphics object's current color to blue. This is sort of
like dipping your brush into blue paint
g.setColor(java.awt.Color.blue);
//draw a circle (actually an oval with equal width and height)
//100 pixels in diameter. The result will be an unfilled
//blue circle
g.drawOval( 0, 0, 100, 100 );
}// end of paintComponent( Graphics )
Let's talk a moment about images. Images in java are represented by a
variety of classes. The easiest way to use images is to use the
ImageIcon class. The constructor of this class takes in a String that
is the file name of a jpg, gif, etc. In order to draw images on a GUI
widget, we need to draw an instance of Image not ImageIcon. ImageIcon
provides a method called .getImage which returns an Image instance
representation of the image at the file name you passed in. The
Graphics instance also requires you have an ImageObserver as a
parameter to the drawImage method. ImageIcons come with another
method called .getImageObserver() which will fill this requirement.
Create a class called GameBoard that extends JPanel. This class should have the
following:
o a private GamePiece[][] variable named "pieceArray". This
array will model the board seen in the GUI.
o a constructor that takes in a file name of a board file. This
file name will be passed off to a PacmanFileReader.
o a method called from the constructor that initializes the
provided images of the four ghosts, the image of a ghost in run
away mode, and of pacman.
o a protected method called paintComponent that takes in a
Graphics instance and returns void. This method is fairly
complicated since it has to draw the entire Pacman board
including the Pacman image and the ghost images.
We will be drawing a series of rectangles on the widget. Each
rectangle will represent one value of pieceArray. WallPiece
instances will be drawn blue. FloorPiece instances black.
FloorPiece instances with regular nibbles will have small yellow
rectangles or circles in the center of the black rectangle and
instances with special nibbles will have larger orange
rectangles or circles in the center of the black rectangle.
Pacman and the ghosts will be drawn last over the top of the
board. So how exactly are we supposed to draw one blue or black
or orange rectangle at an exact location on the board. For this
we'll have to do a little arithmetic.
The number of rectangles drawn per row will be the same number of
elements in one dimension of piecesArray and the number of rows
drawn will be the same number in the other dimension of
piecesArray.
The width of one board rectangle will be the width of the whole
board divided by the number of elements in the X dimension of
pieceArray. The height of one board rectangle will be the
height of the whole board divided by the number of elements in
the Y dimension of pieceArray.
The method .fillRect(int, int, int, int) inside Graphics takes in
four parameters: xStart, yStart, width, height. the "pen" starts
at pixel xStart and yStart as the top left corner in the
rectangle and draws a rectangle width wide and height high. So
how do we get to the correct x and y start locations. Since each
board rectangle represents an array of GamePieces, we can draw
the correct GamePiece rectangle in the correct spot by setting
our x start pixel to be the x index of the array multiplied by
the width of one rectangle. Respectivley we can set our y start
pixel to be the y index of the array multiplied by the height of
one rectangle. See the figure on the P6 website under "Drawing a
Rectangle on GameBoard". Now we'll discuss what to do in
paintComponent:
- call super.paintComponent to draw the default widget
first.
- get the width and height of one rectangle
- iterate through pieceArray. If the piece we're at is a
WallPiece instance, draw a blue rectangle. If it's
a FloorPiece with no nibble, draw a black rectangle. If
it has a nibble, draw the nibble accordingly. You may
have to experiment with a centering the nibble in the
middle of the black rectangle. Here's a hint though:
you'll be dividing by two a lot.
- after drawing the GamePieces, draw the pacman image at his
current location.
- draw each of the ghosts at their current location. Don't
forget to draw the ghosts with the correct image. If they
are being chased you need to draw them appropriately.
NOTE: The x and y instance variables in Pacman and Ghost are
the index the GamePiece they reside on. In other words
thore values will always be between 0 and 14. The x and y
values when dealing with the Graphics object are pixels.
Keep this in mind.
Create a class called PacmanFrame which extends JFrame. This class
will contain a GameBoard and a menu. PacmanFrame should have the
following:
o an instance of GameBoard
o a JMenu with two JMenuItems:
- one that activates a new game: This should restart the
game no matter what the state of the current game.
- one that exits the program. This should stop the JVM just
as if we clicked on the X in the top right corner of the
frame.
o a default constructor which sets up the frame with a
BorderLayout, instantiates a new GameBoard and adds it to the
center. There is a constant in P6Constants called BOARD_FILE
containing the name of the file to tokenize into a GamePiece
array.
o We recommend you set the size of the frame to be about 520x520
o helper methods that set up the menus.
o We recommend you have this class be a listener for its own
JMenuItems. You will need to then have this class implement
ActionListener.
o The program should exit gracefully if either the quit menu is
chosen or the X in the top right is clicked.
PHASE V - PacmanEngine
====================================================================
The PacmanEngine class will be our controller in the MVC paradigm for
this project. It will likely be your single biggest class. For lack
of a better description, PacmanEngine will be the arbiter for the
game. It will process user control inputs, decide which movements by
pacman and the ghosts are legal, regulate the rate at which pacman and
the ghosts, move and determine whether a win or loss condition exists.
First let's talk about Swing Timers. The class javax.swing.Timer is a
class that can be set to fire ActionEvents at a specific
interval. (yes the same Events fired by JButtons and JMenuItems) We
will be using two Timers in PacmanEngine. One will regulate Ghost
movement. Every time the Ghost Timer fires, we want all the ghosts to
move one square. Where they move will be determined by the four
element array returned from Ghost's getMove method. A second timer
will regulate Pacman's movement. This timer will fire at a slightly
faster rate because we want Pacman to be able to outrun the ghosts.
Every time the Pacman Timer fires, we want Pacman to move one square
in the direction specified in Pacman's direction variable. You will
need to look at the API for specific methods to use.
Next let's talk about Key Listening. We've worked with ActionEvents
and ChangeEvents. Now you will use a third event/listener group that
listens for events from the keyboard. The game will be controlled by
six different keyboard keys. The four arrow keys will control
Pacman's direction. The 's' key will start the game (start Pacman and
the Ghosts moving) at the beginning of a game. The 'p' key will pause
and unpause the game. It's important to understand how Pacman will be
controlled. Pacman does not move only when a key is pressed. He
progresses in the direction specified by his direction variable at all
times. The arrow key pressed merely changes this direction variable.
There are exceptions to this. If pacman's direction takes him into a
wall, Pacman will halt and wait until an arrow key is pressed to give
him a new direction.
Now let's talk about JOptionPanes. The JOptionPane class provides
simple pre-packaged diaglog boxes. All you need is a JFrame instance
and the message you want to show the user. Here's how we will use one
for this project:
JOptionPane.showMessageDialog(<instance of JFrame>,
<String instance containing message
you want to show to the user>,
<String instance containing message
to be displayed in Dialog Box title bar>,
<int constant representing icon to be displayed in
dialog box along with message>);
This method call will pop up a small dialog box on top of your PacmanFrame.
Your instance of JFrame will be your PacmanFrame instance. Your message
and title should be something meaningful and relevant. You can use any icon
you want. Check out the API for a listing of icons and how to use them.
Included in your dialog box will be an "OK" JButton which will close the box
if pressed. This button is added and handled automatically by the method.
No effort on your part is needed to add or handle the event from the button.
Create a class called PacmanEngine that implements both ActionListener
and KeyListener. PacmanEngine should have the following:
o a javax.swing.Timer called GhostTimer. This variable will fire
an ActionEvent every P6Constants.GHOST_INTERVAL milliseonds
causing all the Ghosts to move one square
o a javax.swing.Timer called PacmanTimer. This variable will fire
an ActionEvent every P6Constants.PACMAN_INTERVAL milliseonds
causing Pacman to move one square.
o a GameBoard instance called board. This will be the GameBoard
instance used to draw the state of the board
o a PacmanFrame instance.
o a Ghost[] variable called ghosts. This array will hold the
four Ghosts in play.
o a Pacman variable called pacman. This will be the Pacman
instance in play.
o a constructor which takes in a PacmanFrame and a GameBoard and
sets the appropriate variables to these parameters. You will
also need to initialize both Timers. (but don't start them yet.)
Also set the Ghost array and the Pacman variable to those
variables initialized by PacmanFileReader.
o a method public void actionPerformed which takes in an
ActionEvent. This method will be called by both Timers at the
appropriate interval.
If this method was called by the PacmanTimer, it is Pacman's turn
to move. You do not need to worry about any keys pressed in this
method. Remember Pacman always moves in the direction specified
by his direction variable every time the Timer fires. Before
moving Pacman to the next square though, you need to first
determine that the move is legal (i.e., Pacman won't be moving on
top of a WallPiece or off the board) You have the GameBoard,
pacman's current x and y locations and his current direction.
From this information you can determine whether or not any given
move is legal. It is recommended that the functionality for
determining whether a given move is legal be abstracted to a
helper method since you will be using the same functionality for
the ghosts. If a given move is found to be legal, call Pacman's
move method (extended from MoveablePiece) passing in the
direction. Do not have PacmanEngine directly set the x and y
location variables.
If this method was called by the GhostTimer, you will need to
iterate through all four ghosts. At each ghost, call the getMove
method to retrieve each ghost's array of move choices. You
should then iterate through this array and move the ghost using the
first legal move you encounter. (Remember the zeroth index holds
the highest choice of movement for a particular ghost)
No matter which Timer called the method, you will need to
determine if the most recent move by either Pacman or the Ghosts
resulted in a state that should end the game. If all of the
FloorPieces are missing nibbles, the player has won the game.
You should display a notificatoin that the player has won using a
JOptionPane message dialog. (That's why we have an instance of
PacmanFrame which extends JFrame) If one of the Ghosts and
Pacman occupies the same square and that Ghost is not running
from Pacman, the player loses. Again display an appropriate
message dialog to the user. If a Ghost and Pacman occupies the
same square but that ghost is in fact running from Pacman, you
should reset that ghost. You also need to process any time
Pacman eats a nibble. If it's a regular simply remove that
nibble from the board by notifying that particular floor piece to
turn it's nibble boolean to false. If it's a special nibble, you
not only need to have the FloorPiece set it's special boolean to
false but you also need to iterate through all four ghosts and
have them go into run away mode. The ghosts should run from
Pacman and begin counting down their runAwayCount variables.
After processing all moves, if no end game condition exists, you
should call repaint() on the GameBoard instance. This will make
the GameBoard display the new locations of the ghosts.
o a method public void keyPressed which takes in a KeyEvent.
This method will be automatically called when any keyboard key is
pressed. There are only six we care about though. If the 's'
key is pressed, you should start both timers if they are not
already running. If the 'p' key is pressed, you should stop the
timers if they are running and start them if stopped. If one of
the four arrow keys is pressed, you should set Pacman's direction
variable to reflect the key pressed. You should only set the
variable, however, if the Timers are running. But how do we
figure out which key was pressed? The KeyEvent class has a
method called getKeyCode() which returns an int that represents a
key. Look through the KeyEvent API to figure out which constant
represents the appropriate keys.
PHASE VI - Testing and Turnin
====================================================================
Create a Driver class called "P6". This class should have a main
method which instantiates PacmanFrame.
Swing GUI components can sometimes throw exceptions without the
program crashing. i.e. you may see a stack trace on the
console but amazingly the program still appears to be
functioning. We consider this no different than if the program
crashed. You should not have any stack traces from exceptions
of any kind. You should catch and handle all exceptions.
This project, again, will require you to look through the API for many
solutions to a given task. Please make an honest effort to figure out
things for yourself before you seek outside help. This is the point
of the exercise.
Make sure you turn in the following files:
o GamePiece.java
o WallPiece.java
o FloorPiece.java
o MoveablePiece.java
o Ghost.java
o Pacman.java
o PacmanFileReader.java
o GameBoard.java
o PacmanFrame.java
o PacmanEngine.java
o P6.java
These may vary if you deviated from the suggested design.
PHASE VII - Extra Credit (Optional)
====================================================================
There will be a modest amount of extra credit given to those students
who go above and beyond the call of duty in the project. Make sure
you do not attempt any extra credit until you can certify that the
core requirements of the project have been satisfied.
Here are some ideas:
o In board #2, allow Pacman and/or the ghosts to go through the
hole on either the left or right side of the board and appear on
the opposite side. (i.e. wrapping around the board)
o Make pacman "chew" as he moves along the board. A second
pacman image with his mouth closed is provided
o Make pacman and the ghosts move smoothly from square to square
rather than abruptly appear in the next square.
o Create a smarter ghost movement algorithm that allows ghosts to
go around walls to get to pacman rather than just run up against
the wall.
o Develop a scoring system and display it somehow in PacmanFrame
o Put fruit on the board like in Ms. Pacman
o Use sound!
o Two player!
====================================================================