in C++ write a program that simulates one turn of the Monopoly game example. For this program, only the following squares exist: RegularSquare, GoSquare, IncomeTaxSquare, JailSquare, and GoToJailSquare.
Using the UML sketches in the text as your blueprint, the design utilizes the following patterns: polymorphism, indirection, pure fabrication, and protected variations.
Here are the UML sketches from the text from which to base your implementation:
A player has a location, but thanks to the polymorphism pattern, that location could have different effects based on the landedOn responsibility.
Figure 25.3 is not the final design. A new class by Pure Fabrication is introduced at the end. The final design for your implementation is reflected in figure 25.9 which replaces figure 25.3. However, there is still important information here for the design, so it’s included here.
Note in figure 25.3 a player passes itself to the square through the landedOn message.
When landing on a RegularSquare, nothing happens. In this case, landedOn is a NO-OP method.
IncomeTaxSquare can send a message to Player because it receives a reference to the Player through the landedOn message.
IncomeTaxSquare has to ask the player for it’s net worth so it can figure out the amount of tax and then send a message to Player telling it how much to reduce it’s cash attribute.
Here, the GoToJailSquare is initialized with a reference to JailSquare so it can be passed as an argument to Player.
By Pure Fabrication, we can lower coupling and increase cohesion by introducing a “made up” class named Cup to roll the dice for us. Figure 25.9 should replace figure 25.3 in your final implementation.
Make sure your identifiers match up with the UML. For example, class Player needs to have a variable named loc and class Board must have a method named getTotal with two parameters. Have the player start the turn with $1500 and display the cash total at the end of the turn. Have the Player display the dice roll total to the screen. Have each square display to the user they landed on the square.
Yes, we’re violating the Model-View separation principle for the assignment so we don’t have to include a UI layer and we’re not following the layered architecture pattern.
Grading will be based on how accurately your solution implements the above design, identifiers and all.
Hints:
Create a array of Shape references/pointers in class Board. These references can be used for the loc attribute in class Player.
Since there are only 5 squares in our simple program, the array should be 5 elements long.
Everything you need to create design classes is provided by the UML sketches. Some design class attributes are implicit.
There is no requirement for a layered architecture in this assignment.
Incorporate the logic for taking the turn within the Player class constructor. As a result, your main function will only contain a statement instantiating the Player object.
Don’t panic, this looks like a lot, but it’s really not. Each class usually has one method. Divide and conquer. Just take it one class at a time.
Here are three sample runs from my solution:
Here’s what my Player class’s constructor looks like:
And here’s my main function:
Expert Answer
#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:nn"; 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"); } }