| CPSC 124 |
Introduction to Programming |
Spring 2008 |
Programming Assignment 3: Snake Pit Game - Part II
Due date: Thursday, May 8th at 5pm
Introduction
The programming projects are an opportunity for you to work on a larger
program, where you create more of it on your own from scratch. To begin,
copy the files from /classes/s08/cs124/projects/project3 to your home directory. This
directory should contain five files: SnakePit.java, Snake.class (my working
Snake class file), SnakeCell.java,
Apples.java, and GameBoard.java. SnakePit.java and SnakeCell.java are simple
classes, which are already completed. You completed Snake.java in project 2,
and for this project, I've given you my Snake.class file. In this project you
will complete Apples.java and GameBoard.java, which already contain some skeleton code.
There is a directory within the project3 directory called working. As in
project 2, this contains a working program that you can run. This will allow
you to see how the program should work. Your program should eventually work
like this one.
For an overview of the game and a description of the structure of the program
see the writeup for project 2. Below are
some details that are specific to this assignment.
The interface for all of the classes is available here.
Apples Class (must implement for project 3)
The Apples class models the apples on the board in the same way that the Snake
class models the snake on the board. It provides methods for determining what
positions contain apples and for eating apples. The variables and methods of the
Apple class are discussed below. Note: you may find that you need to add other
variables and methods in addition to the ones described below.
- Apples variables. All the variables you define should be private instance
variables unless specified otherwise.
- boolean[][] apples. The Apples class will need a 2-dimensional,
boolean array, which has an entry for each position on the board, to keep track of which
positions contains apples. The positions are chosen randomly when an Apples object is
first created (in the constructor, which is discussed below). If an entry in the array
is true, then the corresponding position holds an apple, otherwise, it does not hold
an apple. Some of the methods within the Apples class will use this array. For example,
isAppleAtPosition (discussed below) will use the array to determine if an apple is at a
particular position (i.e., that entry in the array is set to true). eatApple (discussed
below) will remove an apple from a position by setting the corresponding entry to false.
- numApples. The Apples class will also need to keep track of the number of
apples remaining. This will be helpful when implementing the getter method getNumApples
(discussed below), which returns the number of remaining apples. The GameBoard class
needs this information to determine if the game has ended.
- eatenApples. The Apples class will also need to keep track of the number of
apples eaten. This will be helpful when implementing the getter method
getApplesEaten.
This is needed by the GameBoard class to determine the player's score.
- Apples methods. Note: the signatures of each public method is provided within the
Apples class. You will only need to write the body of these methods (and perhaps add other
private methods as needed).
- Constructor. The constructor takes the number of apples as input and uses
it to assign the numApples instance variable. If this number is less than 0 or greater
than the total number of board positions minus one row (the row the snake resides on),
then the numApples variable is automatically set to 10 (to prevent having too many apples
or a negative number of apples). The apples array is then
initialized and several positions in the array are randomly chosen to hold apples.
If a position is chosen to hold an apple it is set to true, otherwise, it is set to
false. A position can hold at most one apple. In addition, an apple can not be
placed on the middle row (15) since the snake initially starts there. Finally,
the applesEaten variable should be initially set to 0 since no apples have been
eaten when the Apples object is first created.
- getNumApples. This is a getter method, which returns the number of
remaining apples. It is needed by the GameBoard class to determine if the game
is over.
- getApplesEaten. This is a getter method, which returns the number of
eaten apples. It is needed by the GameBoard class to determine the player's score.
- isAppleAtPosition. This method takes as input a row and a column (both
ints), and returns a boolean indicating whether the corresponding position contains
an apple. If it returns true, the position contains an apple, otherwise the
position does not contain an apple. This method is used by the Snake class
to determine if there is an apple at a particular position.
- eatApple. This method takes as input a row and a column and eats the
apple at that position. To eat the apple, the method simply sets the corresponding
entry in the apples array to false. This method should first check if there
is an apple at this position (i.e., if the entry is true) and throw an exception
if there is no apple at that position.
GameBoard Class (must implement for project 3)
The GameBoard class handles the program graphics as well as the game engine (the
logic for playing the game). To display the graphics, it extends JPanel and overrides
the paintComponent() method to draw the board onto the panel. It creates a window
(JFrame object) and adds itself (the 'this' object) as the center panel. It also
listens for three types of events: button clicks (for starting the game),
key pushes (for moving the snake), and timers (for periodically moving the snake
in the current direction). The variables and methods of the
GameBoard class are discussed below. Note: you may find that you need to add other
variables and methods in addition to the ones described below.
- GameBoard variables. All the variables you define should be private instance
variables unless specified otherwise.
- Snake snake. The GameBoard class will need a Snake object for modeling
the snake. This should be created every time a new game is started (in initBoard,
discussed below). GameBoard can use the Snake object to determine where the snake is
on the board (to draw it the appropriate color) and if the snake is dead (and thus,
the game is over).
- Apples apples. The GameBoard class will also need an Apples object for
modeling the apples on the board. This should be created every time a new game is
started (in initBoard, discussed below). GameBoard can use the Apples object to determine
where the apples are on the board (to draw them) and if all of the apples have
been eaten (and thus, the game is over).
- boolean gameOver. The GameBoard class will need a flag indicating whether the
game is over. This will be needed by the method paintComponent (discussed below)
to determine when to draw the "end of game" text on the board (displaying player's
score, game outcome, etc.).
- int speed. The GameBoard class will also need to keep track of the
snake's speed, which it will get from the speed selector when the game is
first started (in initBoard, discussed below). GameBoard will use this to
determine the length of the timer (discussed below), which when triggered moves
the snake in the current direction.
- int growth. The GameBoard class will also need to keep track of the
snake's growth amount, which it will get from the growth amount selector when the
game is first started (in initBoard, discussed below). GameBoard will pass this
to the Snake's constructor, when it creates the snake. It will also use this
to calculate the player's score when the game is over.
- Timer timer. The GameBoard class will also need a timer variable for
moving the snake periodically. This will be constructed and started in initBoard
(discussed below). It will be stopped in the gameOver method (discussed below).
- GameBoard methods. Note: the signatures of each public method is provided within the
GameBoard class. You will only need to write the body of these methods (and perhaps add other
private methods as needed).
- Constructor. The constructor, which takes no parameters, must build the GUI for
the program. The main window should have a content pane that uses BorderLayout as its layout.
In the center, the constructor should add the gameBoard object (using 'this' to
reference it), which extends JPanel. In the south, the constructor should add a
new panel (p2). This new panel (p2) will use GridLayout as its layout with 2 rows and 1 column.
It should contain two components: another panel (p3) and the start button. The panel (p3) should use
FlowLayout as its layout. In the panel,
there should be six components: a label "Snake speed:"; a selector that
contains 1-10; a label "Snake growth:"; a selector that contains 2, 4, 6, 8, 10;
a label "Number of apples:" (a selector is a JComboBox -
see the book for details); and a text field for writing
the number of apples. See the book for
descriptions of these components. You will need to add an action listener for
your start button. You can use the gameBoard object (again using 'this') as the
listener. Finally, your window should be appropriately sized and when closed should cause
the program to exit.
- initBoard. initBoard initializes the speed and growth using the speed and growth
selectors (see
constructor above). It should then construct the timer based on the speed. Use the GameBoard
object as the listener for the timer (i.e., using 'this'). It should also start the timer.
It should also get the number of apples from the apples textfield (see the constructor above)
and use this to construct the apples object. It should also construct the snake object.
Finally, it should set the gameOver flag to false, disable the start button, and repaint
the screen.
- paintComponent. paintComponent does the actual painting of the board.
It should first paint everything black, then paint the walls yellow, and finally use the
Snake and Apples objects to draw the snake and apples on the board. If the gameOver flag
is set to true, then paintComponent should
paint the final text on the screen. If the game is over, it should
print three lines: (i) "Game Over", (ii), either "You win!" or "You lose", and (iii) "Final
Score: 40". The score is computed as 10*speed*growth*numApplesEaten.
The board is made up of 30 rows and 30 columns of blocks. Each block is
15x15 pixels. If a block is empty it should be painted black, if it
contains a snake it should be painted green. If the block contains an
apple it should be black with a red circle within it. The board is
surrounded by a blue boundary that is 15 pixels thick. Surrounding
the blue boundary is a black strip that is also 15 pixels thick. Run
the program in working/ to view how this should look.
- actionPerformed. actionPerformed is called whenever the start button is clicked
or the timer goes off. You will have to figure out how to distinguish between the two
events. If the button was clicked, then actionPerformed should simply call initBoard.
If the timer went off then actionPerformed should determine if the game is over. If it
is, it should call gameOver (discussed below). Otherwise it should move the snake in the
current direction using the snake object.
- keyReleased, keyPressed, and keyTyped.
These are called whenever a key is pushed or released. You should add code in
keyReleased for moving the snake if an arrow is pushed. You should pass the move method
the correct new row and column (use the Snake object to find the correct
row and column). All other
keys should be ignored. keyPressed and keyTyped should simply call keyReleased.
- gameOver.
This method should set the gameOver boolean to true and re-enable the start button
since the game is over. It should also stop the timer.
Testing
Use my .class files to run the program and see how it's supposed to work.
To run the program, cd into the directory working/ and then use
"java SnakePit". Your program should eventually work like mine.
Incremental Programming
When you do project 3, you should write the code it incrementally. Start by writing the
Apples class. When you finish the Apples class you should test it out before
moving on to the GameBoard class. You can copy the file GameBoard.class from the
working/ directory to your project 3 directory and then run the program to see
if it works correctly. It should run like the program in the working/ directory.
The GameBoard class is a large enough class that you should also break it up
into steps. You should start by writing the constructor. First, get the window to
display with all of the appropriate components such as panels, buttons, text fields,
etc. (your window should look exactly like the program in the working/ directory).
Note: you will not see the board at this point since paintComponent() isn't implemented.
Then implement initBoard() and paintComponent(), and get the board initially drawn
correctly whenever initBoard() is called (i.e., draw yellow walls as well as black,
red, blue blocks). Finally, implement the other methods to get the full
program working.
Extra Credit
There are lot of opportunities to earn extra credit on this programming
assignment. Here is a list of some. This list is by no means exhaustive.
- You might try making significant (not just changing the colors)
improvements to the look and feel of the game. For example, you might print the
score briefly to the screen each time the player eats an apple. You could
also add a score text field (not editable of course) to the window. You
could also add a pause button. You might keep track of the high scores and
display this somewhere in the panel (you would need to add a name textfield
to the bottom panel for this).
- You might add other snakes into the game. These snakes would be played
by the computer and would never die. If the user snakes runs into these
snakes then they die.
- You might make the snake grow in width as well as length when it eats an
apple.
- You might try incorporating levels into the SnakePit game. At the start,
the player is at level 1, and each time they get all the apples without
dying the proceed to the next level. In each level the speed should get
faster and the number of apples should get larger. After some number of levels
the player should win the game. The user should be able to start at whatever
level they wish, although they should no longer be able to enter the number
of apples or speed.
If you decide to try for extra credit you should make two versions
of SnakePit. The base version that meets the requirements above and the
extra credit version. Submit these in two separate directories within your
project3 handin folder.
Collaboration and All That
Remember the collaboration policy for individual programming assignments:
This assignment is to be completed by you. You are not allowed to talk
to other students about any aspect of this assignment, including general plans
or specific debugging problems. You may ask a tutor for debugging help
only and must indicate in writing who helped you and explain how they
helped. You may talk to me as much as you want about any aspect of the
assignment - general strategy, programming details, debugging help, etc.
Documentation
Your programs should be heavily commented. At the start of the Apples class and
GameBoard class
should be a multiline comment describing the class and indicating the author.
At the start of all methods should be a description of what the method does, what it's
inputs are (i.e., parameters), and what it returns. You should also comment all
complicated statements within the methods. You should also properly indent all
of your code and use indicative variable names.
In addition, you should write up how your program works in a file called
SnakePit.txt. This should be at least a half of page describing in detail
how your program works. You can focus your write-up on the Apples and GameBoard
classes. Your
write-up should also describe how any help you received from tutors, teaching
assistants, or the instructor.
Handin
Verify that your project3 folder contains all the files you modified, then copy
your entire folder project3 to the handin directory
~mcorliss/handin/cs124/username (where username is
replaced with your username).