Board Game 

A.First Edition
This is actually first edition of the board game assignment.
1﹝ Basic idea: 
It is actually a simply game we all play at our childhood, say "Monopoly".
2﹝ Program design: 
The specification is more complicated than the code itself. (A joke)
3﹝ Major functionㄩ
	
	
4﹝ Further improvementㄩ
		
Program specification:
Snakes and Ladders
Write a C++ program to simulate a game of snakes and ladders with 4 players. The game is made up of a board of 100
squares numbered 1 to 100, 2 dice, and 4 tokens (one for each player). Most squares of the board are standard, but some
contain the low end of a ladder and some contain the tail of a snake. If a token falls on a square that contains the low end
of a ladder, then it will climb up the ladder to a higher-numbered square. If a token falls on a square that contains a snake*s
tail, then it will slide down the snake to a lower-numbered square on the board. If a token falls on a square that contains
nothing special, then it will stay there.
The goal of the game is to bring its token to the last square (square 100) according to these rules:
1. Each player plays in turn. The player throws 2 dice (or one, see next sentence) and advances on the board, a number of
squares determined by the sum of the dice. If the token is a square 94 or more, in that case, the player only throws 1 die
instead of 2.
2. Two tokens cannot be on the same square simultaneously. So if a player should move his token on a square that is
already occupied by the token of another player, the token does not move and the player loses his turn.
3. If a token falls on a square that contains the low-end of a ladder, the token climbs the ladder and reaches the square
where the top-end of the ladder is located (except, if this square is already occupied by another token, in that case, rule
2 above applies).
4. If a token falls on a square where there is a snake*s tail, then the token slides down the snake and reaches the square
where the head of the snake is located (except, if this square is already occupied by another token, in that case, rule 2
above applies).
5. The first player to bring his token on square 100 wins the game. Note that the token must reach square 100 with an
exact move. For example, if the token is on square 97, if the die (notice that only 1 die is used) rolls the value 3, the
player wins; if the die rolls a value smaller than 3, the player moves normally; if the die rolls a value greater than 3, the
token stays at square 97.
The program can be divided into 2 main parts: the initial configuration of the board, and the actual game simulation.

Configuration of the board:
To represent the board, you can use an array of 100 integers, where each element will have one of the following values:
- the value zero to represent a standard ssquare (with no low-end of ladders and no snake*s tail);
- a positive value to represent the low-ennd of a ladder. The value of the element will indicate the number of squares to
climb up. For example, if the element 6 of the array contains the value 14, it means that at square 7 of the board, there
is a low-end of a ladder that brings the tokens to square 21 (remember that square 1 in the board is element 0 in the
array);
- a negative value to represent a snake*ss tail. The value of the element will indicate the number of squares to slide down.
For example, if the element 35 of the array contains the value -22, it means that at square 36 of the board, there is a
snake*s tail that brings the tokens to square 14 on the board (again, remember that square 1 in the board is element 0 in
the array).
The board will contain 9 ladders and 9 snakes. The minimum length of a ladder is 16 and the maximum length is 30. The actual
length of each ladder will be picked at random between 16 and 30. The minimum length of a snake is 12 and the maximum
length is 48. The actual length of each snake will be picked at random between 12 and 48.
The location of the low-end of ladders and the snake tails will be chosen at random on the board following these rules:
- a square can only contain one low-end off ladder or one snake*s tail. In other words, if a square is chosen to contain one
of these 2 elements, than it cannot be chosen again to contain another ladder or tail;
- a square that is the high-end of a laddeer or the hard of a snake cannot also represent a low-end of a ladder or a snake
tail. This rule is to prevent multiple sequences or cycles (for example, if we have placed a low-end of a ladder in square
12 with a length of 20, then we cannot place another low-end of ladder or snake*s tail in square 32, otherwise this
would allow a token to climb several ladders or slide down several snakes in one turn);
- no ladder can reach square 100;
- no snake tail can be placed at square 1000 (for evident reasons).
On the Web page, you will find a fragment de code (configuration.cpp) that will help you complete this part of the program
easily. Once the board has been configured, you must display it on the screen as follows:
Configuration of the board:
Square no. 1 = 0 Square no. 2 = 0 Square no. 3 = 28 Square no. 4 = 0
Square no. 5 = 28 Square no. 6 = 0 Square no. 7 = 0 Square no. 8 = 0
Square no. 9 = 0 Square no. 10 = 0 Square no. 11 = 28 Square no. 12 = 0
Square no. 13 = 27 Square no. 14 = 0 Square no. 15 = 0 Square no. 16 = 0
Square no. 17 = 0 Square no. 18 = 0 Square no. 19 =-18 Square no. 20 = 0
Square no. 21 = 0 Square no. 22 = 0 Square no. 23 = 0 Square no. 24 = 0
Square no. 25 = 0 Square no. 26 =-12 Square no. 27 = 30 Square no. 28 = 0
Square no. 29 = 0 Square no. 30 = 0 Square no. 31 = 0 Square no. 32 = 0
etc.
Page 3 of 4
Simulation of the game
Once the board has been configured, we can now simulate the game with the 4 players. The tokens of the players will be
represented by an array of 4 integers. Before the start of the game, all the elements of this array are initialized to zero to indicate
that the tokens have not started the game yet. Then the players throw the 2 dice (or the die, see rule 1 on page 1) in turn and
move on the board. Your program should show the moves of each player as shown in the output below:
Simulation of the game:
Turn token #1 token #2 token #3 token #4
---- ------- ------- ------- -------
1. 6 - 6 3 每 32L 4 - 4 4 - 0D
2. 3 - 9 3 - 35 7 每 39L 12 - 12
3. 9 - 18 12 - 47 9 - 48 12 - 24
4. 6 每 18D 10 - 57 5 每 72L 3 每 24D
5. 2 - 20 8 - 65 4 - 76 4 - 28
6. 2 - 22 12 - 49S 9 - 85 2 - 30
7. 12 每 55L 2 每 78L 4 - 89 2 - 32
8. 10 - 65 10 - 88 2 - 91 8 - 40
9. 9 - 74 2 - 90 4 - 95 10 - 50
10. 8 - 43S 12 每 90O 1* - 96 6 - 8S
11. 9 - 52 10 每 100W

The first number of the output should count which turn of the game we are playing. The second number of the output should
indicate the total of the dice. If the player used only 1 die (see rule 1 on page 1) then the value of the die must be followed by an
asterisk (ex: token #3 in turn 10 above). The third number of the output (after the dash) should indicate the square where the
token will land; this number can be followed by one of the following letters:
- the letter L to indicate that the token  has reached a square where there is the low-end of a ladder and the token has jumped
to the square indicated. For example, token #2 at turn 1 needs to move 3 positions (sum of the dice) and has reached square
number 3 which contains a ladder of length 29, the token thus finally arrives at square 32 (hence 32L);
- the letter S to indicate that the token  has reached a square where there is a snake*s tail and the token has slid to the square
indicated. For example, token #2 at turn 6 needs to move 12 positions and has reached square 77 which contains a snake*s
tail of length 28, the token thus finally arrives at square 49 (hence 49S);
- the letter D to indicate that the token  has lost its turn (cannot move) because the square that it should normally reach is
already occupied by another token. For example, token #4 at turn 1 cannot move because token #3 already occupies square
4;
- the letter O to indicate that the token  has lost its turn because the sum of her dice (or the value of the single die) would
bring it over the board (after square 100). For example, token #2 at turn 10 cannot move because it is standing on square 90
and the sum of its dice is 12, which would bring it to square 102...which does not exist;
- finally, the letter W to indicate that tthe token has just reached square 100 and therefore has won the game. For example,
token #2 at turn 11.
Note that in the above example, token #4 does not move in turn 4 (24D). This is because, at the beginning of its turn, the token
was on square 24 and normally, it should advance 3 positions, and land on square 27. However, on square 27, there is a ladder
that should bring it up 30 positions. The token should therefore move to square 57, but currently, token #2 is already on square
57; so token #4 cannot go there and must stay at its original place (hence 24D).
Hints:
- Do not be afraid by the length of this ddescription, the program is shorter than it looks.
- Here is a method to determine if a tokenn should move or not:
1. determine which square the token should move to;
2. compare this position with the position of the other 3 tokens;
3. if step 2 shows that there is no collision, then move your token; otherwise, the token loses its turn.
///////////////////////////////////////////////////////////////////////////////////
//Title: Game of snake & ladders
//Course: comp248
//Assignment 4
//Section: X
//Name: Qingzhe Huang
//ID: 5037735
//Purpose: Use of array and function, searching in array, generating random number
///////////////////////////////////////////////////////////////////////////////////
//This is game.h file
#include <iostream>
#include <iomanip>
#include <ctime>
using namespace std;
//status equivalence to the L, S, D, O, W
enum State
{ N, L, S, D, O, W};  //N means nothing, it is my default situation means no situation
const SquareNum = 100;  //number of squares
const TokenNum =4;  //number of players
const SnakeNum =9;  //number of snake tails
const LadderNum =9; //number of ladders
const LadderShift =16;  //min number of squares to advance for ladders
const LadderScale = 15;  //max -min of number of squares to advance for ladders
const SnakeShift = 12; //min number of squares to retreat for snake
const SnakeScale = 37; //range of snake = max -min of snake
//array of game
int board[SquareNum] = {0};
//array of player, record the index of array, not the position
int token[TokenNum] = {0};
//the variable recording numbers of turns
int turnCounter = 1;
//check status of the new position, and write the new position back to array of game
//if there is any change 
State checkState(int player, int step);
//a general function to generate random number for range of scale, and shift by shift
int randNum(int scale, int shift);
//roll 1 dice
int roll1Dice();
//roll 2 dice
int roll2Dice();
//play game
void playGame();
//initialize the array of game
void initialize();
//display the array of game
void displayBoard();
//this is the cpp file game.cpp
#include "game.h"
int main()
{
	char choice[3];
	//in order to try different seed to get different outcome of same code
	cout<<"Do you want a fixed seed 248?";
	cin>>choice;
	if (strcmp("yes", choice)==0)
	{
		srand(248);
	}
	else
	{
		srand(time(0));
	}
	initialize();
	displayBoard();
	playGame();
	return 0;
}
//check specific position to see if there is other token on it
bool checkPlayer(int newPos)
{
	for (int i=0; i<TokenNum; i++)
	{
		if (token[i]==newPos)
		{
			return true;
		}
	}
	return false;
}
//check all situation of new position
State checkState(int player, int step)
{
	int newPos;
	newPos = token[player] + step; //newPos is the index of array
	
	//at the biginning of game the position should minus 1
	if (token[player] == 0)
	{
		newPos --;
	}
	
	//if find there is another token in your new position, don't move
	if (checkPlayer(newPos))
	{
		return D;
	}
	
	//if new position is exceeding board, it is "O"
	if (newPos > 99)
	{
		return O;
	}
	//the winning situation
	if (newPos == 99)
	{
		token[player] = 99;
		return W;
	}
	//if it is snake
	if (board[newPos]<0)
	{
		//and if the tail of snake is not occupied by other token, then move to it.
		if (!checkPlayer(board[newPos]+newPos))
		{
			token[player] = board[newPos] + newPos;
			return S;
		}
		else
		{
			return D;
		}
	}
	//if it is ladder,
	if (board[newPos]>0)
	{
		//and there is no other token occupying new position
		if (!checkPlayer(board[newPos]+newPos))
		{
			token[player] = board[newPos] + newPos;
			return L;
		}
		else
		{
			return D;
		}
	}
	//if no status is found, it is ok to go, write the new pos to array of player
	token[player] = newPos;
	return N;
}
//output total of dice, status, and new position
void outPut(int player, int step)
{
	State state;
	
	//output turn number at beginning of each turn
	if (player%TokenNum==0)
	{
		cout<<setiosflags(ios::right)<<setw(3)<<turnCounter<<".";
	}
	//output total of dice
	cout<<setiosflags(ios::right);
	if (token[player]>=93)
	{
		cout<<setw(8)<<setiosflags(ios::right)<<step<<"*";
	}
	else
	{
		cout<<setw(9)<<setiosflags(ios::right)<<step;
	}
	//output moves, if next pos is free to go
	cout<<" - ";
	
	state = checkState(player, step);
	//at the beginning the position is 0, but elsewhere it is "index +1 = position"
	if (token[player]!=0)
	{
		cout<<setw(3)<<resetiosflags(ios::right)<<setiosflags(ios::left)
			<<token[player] + 1;
	}
	else
	{
		cout<<setw(3)<<resetiosflags(ios::right)<<setiosflags(ios::left)
			<<0;
	}
	
	switch(state)
	{
	case L:
		cout<<"L";
		break;
	case S:
		cout<<"S";
		break;
	case D:
		cout<<"D";
		break;
	case O:
		cout<<"O";
		break;
	case W:
		cout<<"W";
		break;
	case N:
		cout<<" ";
		break;
	}
	//if it is last player of this turn, new line
	if (player == TokenNum -1)
	{
		cout<<endl;
	}
}
bool checkWinner(int player)
{
	int step =0;
	if (token[player]>= 93)   //index is 93, pos is 94
	{
		step = roll1Dice();
	}
	else
	{
		step = roll2Dice();
	}
	outPut(player, step);
	return token[player]==99;
}
void playGame()
{
	int player =0;
	cout<<"\nSimulation of the game:\n\n";
	cout<<"Turn";
	for (int i =0; i<TokenNum; i++)
	{
		cout<<setiosflags(ios::right)<<setw(15)<<"token #"<<i+1;
	}
	cout<<endl;
	cout<<"----";
	for (i=0; i<TokenNum; i++)
	{
		cout<<setw(16)<<setiosflags(ios::right)<<"--------";
	}
	cout<<endl;
	
	while (!checkWinner(player))
	{		
		player++;
		//when reached 4, new turn
		if (player == TokenNum)
		{
			turnCounter++;
			player =0;
		}
	}
	cout<<endl;	
}

//this is a general function to randomize number based on a range(or scale)
//and then shift by "shift" numbers
int randNum(int scale, int shift)
{
	return rand()%scale + shift;
}
int roll1Dice()
{
	return randNum(6, 1);
}
int roll2Dice()
{
	return roll1Dice() + roll1Dice();
}
//this function will be called by function initialize to check when generating
//Ladders and snakes to prevent they occupy the same square
bool checkOccupy(int occupy[], int size, int pos, int effect)
{
	//the rule is eliminate situations that snake or ladders is at winning position=100
	//or exceed bounds(negative or bigger than 99)
	if (pos+effect<0||pos+effect>=99||pos==99)
	{
		return false;
	}
	
	//prevent same position of two snakes or ladders and their tails or heads
	for (int i=0; i< size; i++)
	{
		if (pos==occupy[i]||(pos + effect)==occupy[i])
		{
			return false;
		}
	}
	return true;
}
void initialize()
{
	//I simply cannot find a better way to avoid repeat position of ladders and 
	//snakes. So I use a temporary array to record the position of each S's&L's
	//and their "high end" or "head place".
	int occupy[2*LadderNum+2*SnakeNum] = {0};
	//
	int pos=0, //position of snake or ladder in board
		effect =0;//either advance or retreat number of ladder or snake
	
	//generate snakes
	for (int snake = 0; snake < SnakeNum; snake++)
	{
		pos = randNum(100, 0);
		effect = - randNum(SnakeScale, SnakeShift);//snake is minus effect
		while (!checkOccupy(occupy, snake*2, pos, effect))
		{
			pos = randNum(100, 0);
			effect = - randNum(SnakeScale, SnakeShift);			
		}
		board[pos] = effect;   //put snake on board
		//record snake tail and head place in array "occupy"
		occupy[2*snake] = pos;
		occupy[2*snake+1] = pos + effect;
	}
	//generate ladders
	for (int ladder =0; ladder< LadderNum; ladder++)
	{
		pos = randNum(100, 0);
		effect = randNum(LadderScale, LadderShift);
		//check if generated position and its effect until they are ok,
		while (!checkOccupy(occupy, SnakeNum*2 + ladder* 2, pos, effect))
		{
			pos = randNum(100, 0);
			effect = randNum(LadderScale, LadderShift);
		}
		board[pos] = effect;   //put ladder on board
		//record ladder and end place in array "occupy"
		occupy[SnakeNum*2 + ladder* 2] = pos;
		occupy[SnakeNum*2 + ladder* 2+1] = pos + effect;
	}
}
void displayBoard()
{
	for (int i=0; i< SquareNum; i++)
	{
		
		cout<<"Square no."<<setw(3)<<setiosflags(ios::right)<<i+1<<" = "
			<<setiosflags(ios::right)<<setw(3)<<board[i];
		//display in 4 column, change line when i+1 mod 4
		//use i+1 simply to avoid 0%4 for the first case
		cout<<((i+1)%4!=0?"\t":"\n");
	}
}

                                                        back.gif (341 bytes)       up.gif (335 bytes)         next.gif (337 bytes)

Hosted by www.Geocities.ws

1