Computer Science 1xx Assignment #N
DUE DATE: mmddyyyy
Learning Objectives:
(This is a list of the major topics that you, as
students, will learn in this
assignment:)
Grading Rubric: This document is available for you use - you should use it to to help guide your work. The rubric is a document which explains how your work will be evaluated, and is located here.
For this assignment, you will create a C# program that begins to explore how to use object oriented programming to store and look up information. For this assignment, you'll be implementing a game of 'Snakes And Ladders' (aka 'Ropes And Ladders', aka 'Chutes And Ladders) ', which (as of July 20, 2007) Wikipedia has minimal description of here. You can probably find better descriptions elsewhere on the web, and you can also try playing the pre-compiled example solution, in order to get a feel for how the game should be played. The pre-compiled version that your instructor has provided for you is available at http://your.school.name/goes/here. Please note that the starter project will compile, but won't run until you fill in your code (so compiling the starter project, running it, and having it crash is the expected result, until you add your code.
While many students will undoubtedly be familiar with this common children's game, it is highly recommended that all students read through the Example Of Play document, as it describes the particulars of this variation. Once you've read through that document (and have played the example version) , you should have a clear idea of what the final product of your work needs to look like.
For this assignment, you will need to implement the logic behind two classes: the GameCell class, and the GameBoard class. The GameBoard keeps track of all the cells on the board, using an array of references to GameCell objects. A GameCell object is used to keep track of any gold in a cell, or if there is a snake/ladder to another cell. Each GameCell does one, or the other, but not both. Many/most cells on the board will have neither gold, nor a snake/ladder, and those should be represented using a null reference (instead of an object) in the GameBoard array. Because of the linear nature of movement in the game, the GameBoard must use a one-dimensional array (a 'normal' array) to keep track of the GameCell references.
For this assignment, you will be given a 'starter project' that contains all the code to handle the user interface of the program (all the on-screen drawing, and all the user input), and also contains starter files for the two classes you need to implement. The GameBoard class is found in the GameBoard.cs file, and the GameCell class is found in the GameCell.cs file.
From here, you should finish reading this document (skimming the following tables), then open the starter project and examine the code that you'll need to finish. At that point, return here, and read through the following tables in detail, in order to understand what your code needs to do.
Make sure that you read the rest of this document, as the homework instructions continue after these tables!
Table 1: Methods That You Have To Implement on the GameCell class A couple of quick notes on the GameCell class: The GameCell class is responsible for both keeping track of two things: (1) of the amount of gold at a location (if any - this may be 'zero'), and (2) the snake (or ladder) at a location. Normally, one would separate these two different purposes out into two separate classes, one for tracking gold, and another for ladders/snakes, so that each one would have a single, coherent, clearly identified purpose. (These two classes might then be connected by inheriting from a common, abstract base class). However, In order to avoid introducing advanced topics, or extra complexity, we've decided to merge these two classes into one. At this point, each cell contains either a snake/ladder, or gold, but not both. This keeps the GameBoard object relatively simple, as it is implemented with a single array of GameCells, (as opposed to multiple arrays, or an array of structs, etc, etc) |
|||
Data Field Name |
Type |
Description: |
|
You're free to add any data fields |
that you need to, in order to accomplish |
the objectives set forth in this assignment |
|
Note: all data fields should be marked private (if you need to expose them, use Properties, or accessor/mutator methods) |
|||
Method Name |
Returns |
Description/Parameters: |
|
Constructor |
< none > |
Parameters:
The constructor initializes the GameCell object, so that it is ready to be used by the rest of the program. By default, a GameCell should have no gold points, and no cells to skip (see below).
|
|
getCellIndex |
int The index of the cell that this GameCell object represents. |
Parameters: None The number returned must be identical to the myCell parameter that was passed into the constructor. |
|
setGoldPoints |
void This method will return nothing - it merely needs to remember the value (the number of gold points) that was passed into it. |
Parameters:
When this method is called, the GameCell object will remember that this particular cell on the game board contains numGoldPoints worth of gold. Note that in this game, we currently only create squares with 1 gold point each, but by using an integer (rather than,say, a bool), we can easily change this later, if we wanted. By default, a GameCell object should have no gold points associated with it. If the provided code needs to assign points to a GameCell object, it will do so by calling this method.
|
|
getGoldPoints |
int The number of gold points that this cell contains. A GameCell that has no gold poitns should return 0 |
Parameters: None The return value says it all
|
|
setDestination |
void This method will return nothing - it merely needs to remember where the snake/ladder at this cell goes to, so that the getCellsToSkip method will work properly (see below) |
Parameters:
When this method is called, the GameCell object will remember that this particular cell on the game board contains a snake (or a ladder), and that the snake/ladder leads to the cell specified by the parameter. Both snakes AND ladders are represented in the game as a generic GameCell - the code that is provided to students handles drawing Snakes (those cells whose getCellsToSkip method returns a negative number) in one color, and drawing Ladders (those cells whose getCellsToSkip method returns a positive number) in another color. |
|
getCellsToSkip |
int The number of cells that the snake/ladder in this cell will skip. Ladders will return a positive number (meaning that the player will jump forwards when landing on this square), and Snakes will return a negative number (meaning that the player will jump backwards when landing on this square). |
Parameters: None As an example, let's say that a GameCell object is created in array slot
#1, and that setDestination(4)
is called on that object, in order to set things up so that if the player
lands on slot #1, the player will skip ahead to slot #4. In that case,
getCellsToSkip should return
3 (which is 4 - 1). This cell contains ladder because it returns a
positive number, meaning that the player will jump ahead if the player lands
on it. |
|
Make sure that you properly encapsulate this class - data fields should private, and unless methods are intended to be used by another class, they should be marked private, too. |
Table 2: Methods That You Have To Implement on the GameBoard class A couple of quick notes on the GameBoard class: Firstly, the class itself is fairly simple and small, but in order to draw everything on the screen, it will be used by the GameBoardGUI class, which handles all the graphical user-interface logic on behalf of the game board. In order for that class to be able to do it's job, it needs to be able to ask the GameBoard for information about how the game board is configured. This is why there are a bunch of methods (like getTotalCellsOnBoard, and getMaxColumn) that you need to implement, that all pretty much just return information about the GameBoard. Secondly, the GameBoard object is responsible for keeping track of where the player currently is. The GameBoardGUI class will make use of this functionality by calling the setPlayerCellByIndex and getCellContainingPlayer methods, which you will need to implement. |
|||
Data Field Name |
Type |
Description: |
|
You're free to add any data fields |
that you need to, in order to accomplish |
the objectives set forth in this assignment |
|
Note: all data fields should be marked private (if you need to expose them, use Properties, or accessor/mutator methods) |
|||
Method Name |
Returns |
Description/Parameters: |
|
Constructor |
< none > |
Parameters:
The constructor initializes the GameBoard object, so that it is ready to
be used by the rest of the program. This method should allocate any
memory that will need to be used in any other methods. The player will
start out in the very first cell on the gameboard (cell #0) |
|
getMaxColumn |
int This method will return the number of cells per column in the game board. |
Parameters: None The number returned must be identical to the maxColumn parameter that was passed into the constructor. |
|
getTotalCellsOnBoard |
void This method will return the total number of spaces on the board |
Parameters: None The number returned must be identical to the numCells parameter that was passed into the constructor. |
|
setPlayerCellByIndex |
void This method will return nothing. |
Parameters:
When this method is called, the game board object should remember that the player's location has changed, and the location that is has changed to is indicated by the newLocation parameter. If that parameter is out of bounds, then this method should put the player on the very first cell on the game board. |
|
getCellContainingPlayer |
GameCell This method will return the GameCell object that the player is currently on. If there is no GameCell object at that location, then this will return null. Note that this is a GameCell object, and not an index (unlike the setPlayerCellByIndex method) |
Parameters: None The return value says it all. |
|
getPlayerCellIndex |
int This method will return the index of the GameCell object that the player is currently on. |
Parameters: None The return value says it all. |
|
getCell |
GameCell This method will return the GameCell object that at the
index specified by the index
parameter. If there is no GameCell object at that location (or if that
slot is out-of-bounds), then this will return null. |
Parameters:
The return value and parameter say it all. |
|
getTotalGoldOnBoard |
int The total number of gold points that are on the entire board. |
Parameters: None This method should return the total number of points, from all the cells that contain gold, on the board. While it is possible to 'cache' this answer, instead you should traverse the array each time, in order to get practice writing code that traverses an array of object references. |
|
createRandomSnakeOrLadder |
int This method will return the index of the destination of the snake/ladder. If this method decides to NOT create a new snake/ladder, then this method should return the integer INVALID_DESTINATION, which is defined for you near the top of the GameBoard.cs file. |
Parameters: None When this method is called, the GameBoard will attempt to randomly pick a destination for a new snake or ladder. The destination must be any cell within the bounds of the array of GameCells. It is ok for multiple snakes/ladders to all go to the same destination cell. However, since each slot in the GameBoard array can only hold 1 GameCell, each cell can only have 1 ladder (or snake). You should be aware that this method may be called on an array slot that already has a snake or ladder. If this happens, you will need to return INVALID_DESTINATION, in order to indicate that you didn't create any new snakes/ladders. Also, you should never create a snake/ladder that leads back to the same cell (i.e., if the player is in cell #3, don't randomly create a link to cell #3) |
|
Make sure that you properly encapsulate this class - data fields should private, and unless methods are intended to be used by another class, they should be marked private, too. |
Other requirements: Modular Design
(Functional Decomposition)
There are several aspects of this assignment that can be put
into a separate function/method, and reused from different points. You
should design your program so that it exhibits good modularity (good functional
decomposition), rather than duplicating code. For example, the user might
elect to create new element from the main menu, OR when attempting to edit a
non-existent element - you should be able to find a way to create a common
method that executes the actions that those two tasks have in common.
Similarly, before writing your own code, you should look and see if there is already a method that does what you need, so that you don't duplicate code that has been provided to you.
Extra Challenge:
If you're looking for something extra to do, feel free to do
the following. Please note that
this is in no way required for this assignment, and that you won't lose points
for leaving this part out. Conversely, you won't get extra points for
doing this, either.
First, clearly state under what conditions it is possible for the game to get stuck in an infinite loop, based on the above assignment. The objective here is to be clear about the problem you're solving, so you can reason about it clearly, and so you can know that your fix for the problem actually works.
Second, outline a strategy that will prevent this infinite loop from occurring. There are at least two relatively simple ways to solve this, and at least one more complicated way to solve this - you should be able to suggest a couple of different ways to fix this, even if you're not able to write the code for all of your solutions.
Note to instructors:
You may notice that in the GameBoardUI.cs file, in the
UpdatePlayStatus method, near line 95, there's an if statement that reads:
int skip = m_GameBoard.getCellContainingPlayer().getCellsToSkip();
if (skip != 0 && false) {
int destCell = m_GameBoard.getPlayerCellIndex() + skip;
GoToCell(destCell, stat);
once = true;
PlayCue(skip);
}
This rather odd code is here to demonstrate how to something that's fun, but will cause a (logical) problem. Let's say that the player randomly creates a new snake (or ladder), and that snake/ladder's destination is a cell that contains the STARTING POINT for another snake/ladder. If you wanted to set things up so that the player will follow the snake/ladder they just created, and end up in the destination cell, and IMMEDIATELY follow the snake/ladder in that cell, you should remove the && false from the above line. We've left this odd if statement in the code to demonstrate how to do this fun thing :)
Allowing the user to do this is fun, but also creates a problem - there may be a cycle (a loop) in the chain of snakes and ladders. If the player lands on one of the spaces in cycle, they'll automatically be moved to the next cell, where they'll land on the next snake/ladder (and be moved to the next cell), etc, until they're automatically moved back to the first cell, and then the whole thing begins again. You can get this to happen with the solution file by pressing the 'make a snake/ladder' button over and over until you just happen to make a snake/ladder to a place you've already been before. It'll take a while, and some (bad) luck, but it will happen.
There are a number of different ways one can solve this problem. The simplest solution is to prevent the 'chaining' from happening - if a player lands on a square with a snake/ladder, they're taken the to the cell it connects to, and then left there, even if that cell has another snake/ladder in it, which is what we've done by putting in the && false clause. Another pretty simple solution is to not generate snake/ladders to a cell that already has one in it. I.e., in the 'randomly place snake/ladder' routine, check if the destination cell has a snake/ladder leaving it, and if so, do NOT generate a snake/ladder to that cell, thus preventing chains (and therefore cycles) from occuring at all. The easiest way to pick a new destination would be to simply re-generate a new random number (this is easy, but inefficient); a better way would be to count the squares that don't have snakes/ladders in them, randomly generate a number in that range, then traverse the board to find the empty cell that corresponds to that random number (this is less easy, but guaranteed to finish in a known finite time). The more complex solution is that when the snakes/ladders are randomly being generated, a routine to check for cycles can be used to determine if the new snake/ladder is part of a cycle. The implementation wouldn't be too bad - keep track of the current cell, then repeat the following as many times as there are cells: follow the current snake/ladder, and see if it leads to the same place as where we started (if so, there's a cycle & we're done). If not, and there is no snake/ladder at the place we ended up, then there's no cycle, and we're done. If the place we ended up has a snake/ladder, then it's now the current snake/ladder, and go back to the start of the loop.
How To Play The Game: |
Game/Program
Controls: XBox When running the game/program, you can control it using the following Gamepad controls: § 'A' Button move forwards a randomly generated distance § 'B' Button to create a snake (or a ladder) to a random location on the board |
Game/Program
Controls: PC When running the game/program, you can control it using the following keyboard controls: Here is the keyboard to controller mapping. |
You are not allowed to work in groups for this assignment. You should start, finish, and do all the work on your own. If you have questions, please contact the instructor.
Additionally, you should aggressively comment your code, paying particular attention to areas that are difficult to understand. If you found something to be tricky when you wrote it, make sure to comment it so that the next person (the instructor, who's grading you) understands what your code is doing. It is not necessary to comment every single line.
The purpose of new requirement is to both help you understand, and have you demonstrate, a thorough understanding of exactly how your program works.
Every file that you turn in should have:
·
At the top of each file that
you normally edit, you should put your name (first and last), the name of this
class (BIT 142), and the year and quarter, and the assignment number,
including the revision number, which starts at 0 (A2.0). If youre handing
this in again for a regrade, make sure to increase the minor version number by
one (from A2.0, to A2.1").
You normally edit the C# source code files (.CS files), and any Word documents
that you're handing in (if any).
You do not normally edit the .SLN or .CSPROJ files, and so you should not try to
put this identifying information in those files.
In general, you should make sure to do the following before handing in your project:
· All variables used should have meaningful names.
· The code should be formatted consistently, and in an easy to read format.
What to turn in:
· A single electronic folder (a directory). This folder should contain:
o
The source code for the
program all the .CS files in your project.
I would prefer that you include the project files stuff ending in .SLN and
.VCPROJ, so I can build your project more easily. If you can save these files
(the .SLN / . VCPROJ) into a file format that can be opened by VS.Net 2003, that
would be great.
o
You have to name the folder
with your last name, then first name, then the assignment number (both the major
version 2, and the minor (revision) number 0). Example: "Panitz, Mike,
A2.0"
· You should not include the Debug directory, or anything from it. I will dock you a couple points if you do. Also, you don't need to include your .NCB file, if it's present.
How to electronically submit your homework:
There's a link on the homework page to the document that guides you through handing in your work, using the SourceGear Vault program.