C++代写:CS104 The Left Center Right Game

在本次作业中,将代写游戏Left-Center-Right(LCR)。LCR是一种骰子游戏,玩家掷三个骰子,并根据掷骰的结果分配筹码。

LCR

Requirement

In this assignment, we are going to explore variants of the game Left-Center-Right (LCR). LCR is a dice game. Traditionally, players roll three dice and distribute chips based on the outcome of the roll. We will specify the chip distribution in the form of a table:

Die Roll Result
1 Hold
2 Hold
3 Hold
4 Pass 1 Chip to the Right
5 Place 1 Chip in the Center
6 Pass 1 Chip to the Left

The game is played until only one player still holds chips (or, in our case, until a certain number of rounds has been played if more than one player holds chips). If there is a winner (only 1 player left with chips), that player collects all the chips in the center.

Each player starts with a certain number of chips. This is an input parameter. The starting player is chosen randomly and then play continues to the left as with many card games.

A player rolls a maximum of 3 dice. If the player has 3 or fewer chips, the player rolls that many dice.

If the player has no chips, she sits out the round. In our simulation, the player is passed three dice values and must decide how many values to use.

Once the dice are rolled, the player follows the distribution table, which is also an input parameter.

For example, assume a player has 10 chips and is using the distribution table above. She rolls 3 dice with values of 2, 4, 5. She then passes one chip to the player to her right (because of the 4) and places on chip in the center (because of the 5). She does nothing for her 2 roll because that is a ‘hold’.

Step 1

Create a class called ‘Player’ which will handle the play of a single player. It is ok to make all the methods in Player public.
a. Create a constructor for Player which takes the following parameters:
Player(const int* chipDistribution)
Where chipDistribution is an array that represents the distribution rules. The 0th offset of the array represents a die roll of 1, 1st offset represents 2, etc.
The values in the chipDistribution have the following meaning:
0: hold
1: pass left
2: put in center
3: pass right
chipDistribution should be stored in a property.

b. Create a method called setLeftPlayer(Player* p) which will be used to set the player to the object’s left. You should store this value in a class property. You should also create a method Player* getLeftPlayer()which returns the value of this property.

c. Create a method called setRightPlayer(Player* p) which will be used to set the player to the object’s right You should store this value in a class property. You should also create a method Player* getRightPlayer()which returns the value of this property.

d. Create a getter and a setter for the number of chips the player holds: int getNumChips(), void setNumChips(int nc) numChips should be stored in a property.

e. Create a method called int addChips(int nc)which adds nc chips to the player’s stack. nc may be negative. Hint: you may want to do something like: this->setNumChips(this->getNumChips() + nc); addChips() should return the number of chips the player holds after the addition.

f. Create a method called: int play(int d1, int d2, int d3) which plays a round of the game for the player. d1, d2 and d3 represent dice rolls. It is up to the player to decide how many of the dice to use (based on the number of chips the player holds) and how to interpret the dice values (based on the player’s distribution table). It is also up to the player to remove chips from her own stack when distributing them to other players or to the center.

To give chips to a player, the method should call the addChips() method of the left or right player, depending on which player the distribution is going to. play() should return the number of chips placed in the center during the player’s round

Note: you may find it useful to create a method that handles a single die roll and then call it up to 3 times from play()

Step 2

Create a class called ‘Game’ which will manage game play. It is ok to make all the methods in
Game public.
a. Create a constructor for Game:
Game(int numPlayers, const int* chipDistribution, long seed)
The constructor should create a property which is a dynamically allocated array of pointers to Player objects of size numPlayers.
The constructor should populate the array by creating new Player objects and use chipDistribution as the distribution table (each Player gets passed the same chipDistribution value).
Once the array is populated, the constructor should loop through the array of Players and call setLeftPlayer() and setRightPlayer() for each array entry. If off represents the index of the player in the array, then the left player will be the player at index off-1 off+1 and the right player will be the player at index off+1 off-1.
BE CAREFUL TO HANDLE off==0 and off==numPlayers-1 propertly. If off==0, then the left right player will be at index numPlayers-1. If off==numPlayers-1, then the right left player will be at index 0. The modulo operator (%) can help with this index arithmetic.
The seed parameter should be used to create a Mersenne Twister that is a property of the class and will be used to select the first player and to roll dice.

b. Create a destructor for Game that loops through and deletes each player and then delete[]’s the player array.

c. Create a method int countPlayersWithChips() which returns the number of players that hold chips. The game is over when only one player holds chips.

d. Create a method int playRound(int startingPlayer) that loops through the players, in order with the starting point defined defined by the startingPlayer parameter, and rolls dice and then calls the Player’s play() method. You will need to create 3 random rolls of a die (values in [1,6]) using the Mersenne Twister you created in the Game’s constructor. playRound() should return the number of chips that were placed in the center during the round (that is, it should sum the values returned by Player’s play() method for the round).

For example, if there are 10 players and startingPlayer is 4, then the round will start with the Player at index 4 in the Player array. Assume you’ve named your Player array players, then you’ll call players[4]->play(d1, d2, d3) then play() of players[5], players[6], players[7], players[8], players[9], players[0], players[1], players[2], players[3].

Remember the modulo operator (%) can help you with this:

1
2
3
4
for (int i = 0; i < numPlayers; i++) {
int index = (i + startingPlayer) % numPlayers;
// do stuff with index
}

e. Create a method const int playGame(const int startingChips, int maxRounds) that plays the game until it is completed or the maximum number of rounds has been reached.

Before play begins, the starting player should be chosen randomly (using the object’s Mersenne Twister). The same starting player should be used throughout the game.

Also before play begins, treat startingChips as an array and call each Player’s setNumChips() method to set the initial number of chips each player holds (these may not be the same).

For up to maxRounds iterations, call the playRound() method and keep track of the number of chips in the center. If there is only one player with chips after the round, the game is concluded.

When the game is concluded (only one player has chips or maxRounds has been reached), then populate and return an array with the number of chips each player holds. i. If only one player holds chips, then give that player the chips in the center. ii. If the maximum number of rounds has been reached, then ignore the chips in the center.

Note: you should create the chip count array in the class constructor and delete[] it in the destructor. We are going to call playGame repeatedly and want to reuse the same chip count array.

The output of the program is a file called lcr_output.csv. The first columns in the output are a description of the game, the number of simulations run for which the analysis is being output (100,
1000, 10000, 100000) and the percentage of time the game ended with a winner (one player has all the chips). These columns are followed by the expected number of chips each player has after the simulated runs.

Add your code to the program and run it.

  • Q1: Which game (standard, standard-tilted, nocenter, nocenter-tilted, nohold, nohold-tilted) resulted in the most single-player winners? Why do you think that is?
  • Q2: How quickly do the simulated runs converge into stable expected values? Which game, if any, converges the quickest? Which game, if any, converges the slowest? If you see a difference, why do you think the convergence times differ?

In a single Word or PDF file, include the answers to the two questions, your code and lcr_output.csv.