Your work for this exercise sheet will be automatically collected in (if you put it in the right place) at 5 p.m. on Friday 6th December.
Put your work into a subdirectory of your cs2000
directory
called sheetC
.
In this exercise, you will write a program for playing the Hindu board game of Moksha-Patamu. In this game, players move around a board according to the throw of a dice. Players are occasionally rewarded (for virtue, faith, reliability, generosity, knowledge and asceticism) by being allowed to directly ascend to later squares on the board. Other times, they are punished (for disobedience, vanity, vulgarity, theft, lying, drunkenness, debt, rage, greed, pride, murder and lust) by being required to directly descend to earlier squares on the board. The first to reach the final square wins.
In case you haven't guessed, Moksha-Patamu is the original form of Snakes and Ladders. These days the ladders and snakes are no longer labeled with rights and wrongs. Some might say that this reflects the moral vacuum at the heart of modern society.
Write a class definition Dice
.
For generality, we will allow our dice to be n-sided
dice, where n is supplied as a parameter to the
constructor.
The interface is as follows:
There are two constructors. The first constructor is told how many sides the dice is to have. The second constructs a standard 6-sided dice.
public Dice(int theNumOfSides) public Dice()
public int roll()
The getter uses Java's random number facilities (covered in lectures) to roll this dice. The return value is an integer between 1 and the number of sides inclusive.
The next class definition we need will represent squares on the board. This has quite a lot of methods, but first here are some hints about its instance variables.
int
: the number of the next
square. Don't do it that way. Instead, use an instance variable of
type Square
. (If this variable contains null
,
then this square has no next square, and it must be the final square
on the board.)int
s.
And again if this variable is null
, then there is no
snake head in this square.)ListOfPlayers
. It provides
a zero-argument constructor to create an empty list of players,
and it provides two methods, add
and remove
,
for inserting and removing players from the list, respectively.
Obtain your copy of this class definition
here.
Now here's the interface for your Square
class definition:
public Square()
In your constructor, assign a unique number to the square being created. The first square that ever gets created should be square 1; the next should be square 2; and so on.
Think about all the other instance variables. Are Java's default initialisations appropriate?
public int getSquareNumber()
- returns this square's unique number.
public boolean hasNext()
- tells you whether this square has a next square or not (hence we can use this to tell whether this is the final square on the board).
public Square getNext()
- gets the next square (but null if there is no next square).
public boolean containsSnakeHead()
- returns true if this square contains a snake head.
public boolean containsLadderFoot()
- returns true if this square contains a ladder foot.
public Square getSnakeDestination()
- returns the square at the very end of the tail of the snake (but null if this square contains no snake head).
public Square getLadderDestination()
- returns the square at the very top of the ladder (but null if this square contains no ladder).
public ListOfPlayers getPlayers()
- returns the list of players who are currently resident in this square.
These all make simple changes to the instance variables that you defined earlier.
public void linkTo(Square theNextSquare)
- links this square to the next square.
public void addSnake(Square theTail)
- adds a snake to this square. The snake's head is in this square. The location of its tail is given as a parameter.
public void addLadder(Square theTop)
- adds a ladder to this square. The foot of the ladder is in this square. The location of the top of the ladder is given as a parameter.
public void addPlayer(Player thePlayer)
- adds a player to this square. This updates the contents of the list of players.
public void removePlayer(Player thePlayer)
- removes a player from this square. Again this updates the contents of the list of players.
Now it's time to write Player
. There are three instance
variables: the player's name (use a String
for this;
don't bother with PersonName
on this occasion);
the player's dice; and the player's current square.
public Player(String theName, Dice theDice)
public String getName()
- returns the player's name.
public int roll()
- rolls the player's dice and returns the result.
public Square getSquare()
- returns the square that this player is currently on.
The setters are quite tricky, so think hard about them.
public void setSquare(Square theSquare)
This puts the player onto whatever
square is supplied as a parameter. Obviously, this method will
update one of
Player
's instance variables.
But it has to do more than that! This setter must also send messages to two squares: to remove the player from one of the squares and to add him/her to another square.
public void moveBy(int theDisplacement)
We use this method to move the player after s/he has rolled a certain number on his/her dice. The amount by which s/he should move is the parameter of this method.
Think about how
you used to do this when you played the game for real.
You lifted up your counter, and you counted off an appropriate number
of next squares. E.g. if theDisplacement
is three,
you count off three next squares.
If during this, you were moved to or beyond the final square, you could stop counting and you could place your counter triumphantly on the final square (you have won).
Once you have stopped counting, you looked at the square your counter ended up on. Did it contain a snake head? If so, your counter slithered to the square at the tail of the snake. Or did it contain the foot of a ladder? If so, your counter climbed to the top of the ladder.
But things didn't stop there! On arriving at the tail of a snake or top of a ladder, you then looked to see whether you had arrived at the head of another snake or foot of another ladder. This process would repeat until your counter was resting in a square that contained no more snake heads or ladder feet.
Your moveBy
method should be a faithful implementation
of what you used to do when playing the game for real.
Don't forget to update the instance variables in Player
and in the two Squares
that are affected by this move.
Note that if the board contains a cyclic structure of snakes and ladders, then some moves will be infinite. If this happens when you run the program, you will have to kill off the program (using CTRL-C). If this seems unsatisfactory to you, then see the Challenge Exercise.
There's just one class definition remaining. It's called
SnakesAndLadders
. I've made a start on it for you,
which you can obtain here.
I've already defined its instance variables, written its constructor,
some getters and a main
method. The parts I've written
create the Snakes & Ladders board, i.e. the parts I've
written create an
appropriate number of Square
objects, and link them
together in sequence, using your Square
class
definition. My code also generates a random number of snakes
and ladders and places them into the squares.
So there is only one thing left for you to do, and that is to
write the play()
method.
Assume that the players (in the array called players
)
take turns in the same order as they appear in the array.
Place each player onto the starting square of the board. Then each takes a turn to throw their dice and move to a new square. If anyone reaches the final square, the game is immediately over.
Include some print statements in your play
method
and add some extra print statements to your moveBy
method in Player
so that when you run the game, the
user gets some idea of what is going on.
Here's some example output for three players:
All 3 players are at the start. Ann rolls 1 After moving 1 place, s/he is now at square 2 But this contains a ladder! S/he clambers to square 16 Bob rolls 4 After moving 4 places, s/he is now at square 5 But this contains a ladder! S/he clambers to square 26 Col rolls 5 After moving 5 places, s/he is now at square 6 Ann rolls 6 After moving 6 places, s/he is now at square 22 Bob rolls 5
etc.
You've finished. This part is just for fun.
I've written some other class definitions to provide a GUI for your Snakes and Ladders game. My class definitions use your class definitions. So if yours are wrong, my stuff may not compile or may not run correctly.
You need to obtain copies of the following files: MokshaPatamu.java, SLController.java, SLGUI.java and SLSquareGUI.java.
Compile using
javac MokshaPatamu.java
and run using
java MokshaPatamu
The GUI doesn't use lovely pictures of snakes and ladders. Sorry - I've no time to make it look better.
This Challenge Exercise does not involve programming. It only involves thinking!
As mentioned above, my code for randomly generating the snakes and ladders sometimes generates cyclic structures, e.g. a snake head in square i might have its tail in square j; but j might be the foot of a ladder that takes you back up to i!
The challenge is to write down a precise set of rules for the legal placement of snakes and ladders on a m by n board.
If you think this is easy, then you haven't thought about it at all!
Discuss your ideas with me and/or put them into a text file in
sheetC/challenge
.
If you're mad keen, you could try to implement your rules in Java and add them to your program.