CPSC 124 Introduction to Programming Spring 2007

Programming Assignment 3: Snake Pit Game - Part II

Due date: Thursday May 3rd at 1:30pm

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/s07/cs124/labs/pa3 to your home directory. This directory should contain three files: SnakePit.java, Snake.java, and GameBoard.java. You will complete GameBoard.java (which contains some skeleton code). SnakePit.java and Snake.java contain my solution to programming assignment 2. You should not have to modify these files. Feel free to swap in your copies of SnakePit.java or Snake.java (as long as they work!). The directory also contains a directory called working/ which has class files for running a working copy of SnakePit. This will allow you to see how the program should work.

For an overview of the game and a description of the structure of the program see the writeup for programming assignment 2. Below are some details that are specific to this assignment.

GameBoard Class

The GameBoard class extends JPanel and overrides the paintComponent() method to draw the board onto the panel. Around the outside of the board, the paintComponent() method should draw a yellow border, which will represent the walls of the game (there should some space between the walls and the edges of the panel). The yellow border should be 10 pixels thick. Within the yellow border is the actual playing field. It is split into 50x50 block squares where each block contains 10x10 pixels. The GameBoard class should store the color of each block in a 2-dimensional array grid[][]. This array is set in two places: initBoard() when the game is started and setColor() (discussed below). There are three possible colors for any particular block: black (unoccupied), blue, (occupied by the snake) or red (occupied by an apple). The Snake class (discussed below) handles coloring the Snake cells blue. GameBoard class colors all the other blocks. It uses the snake object (passed in via initBoard()) to determine, which blocks belong to the snake. All other blocks it colors black (if empty) or red (if contains an apple). Important: painting is done only in paintComponent(), all other methods manipulate grid[][] to change the colors (e.g., initBoard, setColor).

GameBoard uses another 2-dimensional array, apples[][], to keep track of which blocks in the playing field hold apples. This array is a boolean array with an entry per block (the dimensions and sizes are the same as Grid[][]). If an entry is true, then that corresponding block holds an apple, otherwise, it does not hold an apple. This array should be randomly set using the number of apples inputted to initBoard(). At the start of the game, the apples[][] array is used to set the color in Grid[][] for the blocks that contain apples. As the game is running, this array is used to determine when a snake eats an apple (i.e., its head moves to a block containing an apple). The GameBoard class also keeps track of how many apples have been eaten. Each time an apple is eaten, a counter (called applesEaten) is incremented. This is used to calculate the score at the end of the game (score = 10*applesEaten). In addition, the GameBoard class listens for both button clicks and key pushes (i.e., implements ActionListener, KeyListener). You will need to implement the actionPerformed() and keyReleased() methods to handle these two types of events. The methods that you will have to modify or implement are discussed below.

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. This new panel will use FlowLayout as its layout. In the panel, there should be four components: a label "Snake speed:"; a selector that contains 1, 2, 3, 4, or 5; a label "Number of apples:" (a selector is a JComboBox - see the book for details); a text field for writing the number of apples; and a button for starting the game. See the book for descriptions of these components. You will need to add an action listener for your button. You can use the gameBoard object (again using 'this') as the listener. Your constructor should add the gameBoard object ('this') as a listener for the start button (the keyboard listener is already implemented for you). Finally, your window should be appropriately sized and when closed should cause the program to exit.

initBoard()

initBoard() initializes the blocks that are not occupied by the snake to black and randomly sets some of the blocks to red, to indicate positions occupied by an apple. initBoard() does this by setting the entries in Grid[][] (which has an entry per block in the playing field) to the appropriate color. The snake object, which is an input to initBoard(), is used to avoid coloring a block that is occupied by the snake either black or red. The number of apples is also an input to initBoard(). initBoard() can assume that this is a reasonable value: greater than 0 and not larger than the number of blocks on the board. initBoard() randomly selects blocks on the board to contain apples based on this input. For each block that will contain an apple, initBoard() sets the corresponding entry in apples[][] to true. All other entries are set to false.

setColor()

setColor() allows the color of a block to be set by other classes. In particular, the snake class should use setColor() to the set the blocks occupied by a snake cell to blue.

paintComponent()

paintComponent() does the actual painting of the board using the grid[][] array (note: all other methods just manipulate grid[][]). paintComponent() should call the paintComponent() in the parent class (JPanel class), before painting. It should first paint everything black, then paint the walls yellow, and finally use grid[][] to paint each block the appropriate color. paintComponent() must also check if the gameEnded flag has been set to true (done when the SnakePit class calls gameOver()), and if so, to 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" ("40" varies per game based on how many apples were eaten - the number should be 10 * number of apples eaten).

isAppleAtPosition()

isAppleAtPosition() takes as input a row and column, and should return a boolean indicating whether that position contains an apple. It should use the array apples[][] to determine if an apple exists at a particular block.

eatApple()

eatApple() takes as input a row and a column. It should only be called if there is an apple at the specified position. If there is not an apple at the inputted position (row, column) it should throw an exception. Otherwise, it should increment its count of the number of apples eaten, it should decrement its count of the number of apples remaining, and it should set the corresponding entry in apples[][] to false.

applesLeft()

applesLeft() returns a boolean indicating whether their are apples remaining (used by the SnakePit class to determine when the game is over).

getSpeedText()

getSpeedText() returns the current string in the speed text field. This string is needed by the SnakePit class to determine the delay to use in the game.

actionPerformed()

actionPerformed() is called whenever the start button is clicked. This should simply call beginGame() (a static method) in the SnakePit class.

keyReleased()

keyReleased() is called whenever a key is released. You should check which key was released. If the key was an arrow, then your method should call changeDirection() (a static method) in the SnakePit class. Note that changeDirection() requires an argument that represents the direction that the snake should be moved (LEFT, RIGHT, UP, or DOWN). You should see chapter 6 for details on how to implement this method. Note: you should leave keyPressed() and keyTyped() blank You will only need keyReleased() to determine when an arrow key was hit.

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 write the GameBoard class, you should do it incrementally. 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. 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 pa3 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 GameBoard class should be a multiline comment describing the program and indicating the author. At the start of all methods should be a description of 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 GameBoard class. Your write-up should also describe how any help you received from tutors, teaching assistants, or the instructor.

Handin

Verify that your pa3 folder contains all the files you modified, then copy your entire folder pa3 to the handin directory ~mcorliss/handin/124/username (where username is replaced with your username).