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 square (with no low-end of ladders and no snake*s tail); - a positive value to represent the low-end 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*s 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 of 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 ladder 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 100 (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 the 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 description, the program is shorter than it looks. - Here is a method to determine if a token 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"); } }
﹛