Monopoly -- Forum

Pages: 123
jonnin, you generate 1000000 numbers...

28227 + 50616 + 80444 + 110783 + 134874 + 144278 + 135857 + 110959 + 79885 + 50848 + 27911 = 954682
28155 + 50455 + 80223 + 111385 + 135407 + 143818 + 135773 + 110919 + 80019 + 50236 + 28159 = 954549
28075 + 50412 + 80392 + 111289 + 134863 + 144368 + 134991 + 111067 + 80245 + 50392 + 28416 = 954510


What happened to the rest? ;)
For info, there are several versions of C++ Monopoly on GitHub. Eg
https://github.com/jemeador/monopoly

Looking at some of the header files etc may give you some ideas re data design and structure.
[Reminiscence]

Ah Monopoly. That takes me back. I wrote a Monopoly game for 2 - 4 players way back in the 1970's in Dartmouth Basic (HP TSB) as a school project. The only 'containers' there was were a 1 or 2 dimension array of numbers (max 26 called A to Z) and 26 string variables (A$ to Z$) - apart from numeric variables (A - Z and A0 to Z9). If you wanted to have a record of say 2 numbers and 2 strings then the only way was to use a file of records (max 512 bytes - stored as 16 bit words) which could be accessed by a record number! As this was 'played' over 110 baud teletype and after every play the board was drawn, the speed of the program wasn't that important as most of the time was spent actually printing the board onto a paper roll.

Ah the 'good old days'!
@jonnin, it's nothing like a normal distribution:
(a) it's discrete (and finite domain), not continuous (and infinite domain)
(b) the laws of "large" numbers (central-limit theorem) definitely don't apply when N=2.

Your computed fractions for 3, 7 and 11 are consistently more than 10% in error (reflecting the shapes of the two curves).
Last edited on
I understand that its not a normal. I said you could get it close enough for a casual board game, and I stand by that. If anything I said implied that it was scientific or actually correct or even really useful for anything other than a crude ballpark, I did not mean for it to come out that way.

What happened to the rest? They are 1s & 13s and maybe even a few 0s & 14s. It needs work if you wanted to use it for something. You can tinker with the curve or code around it to force it within the bounds, but as I said above, for a number of reasons rolling 1-6 twice is really the best answer. Playing with the numbers does not change *that*.

a silly & simple force fit back to the center recovers the lost rolls and steeps the curve back toward where it belongs a little more. Still off by 10% at times, but again, a toy in the ballpark...
1
2
3
4
5
6
                r =  distribution(generator);
		if(r <1.5) r = 6; else
		if(r <2) r = 7;		
		if(r > 13.5) r = 8; else
		if(r >= 13) r = 7; 
		ct[(int)r]++;	


2 28210 0.02821
3 50753 0.050753
4 80187 0.080187
5 111402 0.111402
6 149868 0.149868
7 160083 0.160083
8 149570 0.14957
9 110845 0.110845
10 80668 0.080668
11 50333 0.050333
12 28081 0.028081
Last edited on
as I said above, for a number of reasons rolling 1-6 twice is really the best answer.

Indeed. As said, in this game the result of 2d6 is not a "number", it is a tuple of a number and a boolean. The latter tells whether both die had same value.

Players act in turns. Therefore, they should be in cyclic container. For example:
WHILE no winner
  player = players.next()
  roll = ...
  player->advance(roll)
  player->action()
  if ( player->isOut() ) players.erase( player )
  else if ( roll was a double ) something()

One has to get the owner of lot when a player enters that lot. The player might know what they own, but as well the lot could know its owner. Then it is easier to check if the owner is null, me, or who.
Didn't realize a proxy war had started from distributions but I've fixed the code to be more simple and reusable using some of the suggestions given.

Dice Function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int rollDie()
{
    int rolls[2];
    int min = 1; // the min number a die can roll is 1
    int max = 6; // the max value is the die size

    for (size_t i = 0; i < 2; i++)
    {
       rolls[i] = rand() % (max - min + 1) + min;
    }

    int roll = rolls[0] + rolls[1];
    return roll;
}


If there is still a problem with this let me know

Results from the function:
How many players? 4
8
9
6
7
The Maximum Roll was 9 by Player 2
How would property ownership work? Like what structure would I use for storing when the player lands and autobuys the property.

For main (will be splitting up into multiple different classes)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#include <iostream>
#include <string>
#include <iomanip>
#include <cctype>
#include <array>
#include <queue>
#include <vector>
#include "linkedlist.cc"
#include "playerqueue.cc"

using namespace std;

struct Player
{
    int cash;
    int board_pos;
    int token;
    string player_name;
};

int rollDie()
{
    int rolls[2];
    int min = 1; // the min number a die can roll is 1
    int max = 6; // the max value is the die size

    for (size_t i = 0; i < 2; i++)
    {
        rolls[i] = rand() % (max - min + 1) + min;
    }
    cout << "Roll 1: " << rolls[0] << "\nRoll 2: " << rolls[1] << endl;
    int roll = rolls[0] + rolls[1];
    return roll;
}

vector<string> fillboard()
{
    vector<string> board{
        "GO",
        "Mediterranean Avenue ($60)",
        "Community Chest",
        "Baltic Avenue ($60)",
        "Income Tax (Pay $200)",
        "Reading Railroad ($200)",
        "Oriental Avenue ($100)",
        "Chance",
        "Vermont Avenue ($100)",
        "Connecticut Avenue ($120)",
        "Jail",
        "St. Charles Place ($140)",
        "Electric Company ($150) ",
        "States Avenue ($140)",
        "Virginia Avenue ($160)",
        "Pennsylvania Railroad ($200)",
        "St. James Place ($180)",
        "Community Chest",
        "Tennessee Avenue ($180)",
        "New York Avenue ($200)",
        "Free Parking",
        "Kentucky Avenue ($220)",
        "Chance",
        "Indiana Avenue ($220)",
        "Illinois Avenue ($240)",
        "B&O Railroad ($200)",
        "Atlantic Avenue ($260)",
        "Ventnor Avenue ($260)",
        "Water Works ($150)",
        "Marvin Gardens ($280)",
        "Go To Jail",
        "Pacific Avenue ($300)",
        "North Carolina Avenue ($300)",
        "Community Chest",
        "Pennsylvania Avenue ($320)",
        "Short Line ($200)",
        "Chance",
        "Park Place ($350)",
        "Luxury Tax (Pay $75)",
        "Boardwalk ($400)"};

    return board;
}

vector<int> property()
{
    vector<int> propertyvalue{
        0, 60, 0, 60, 200, 200, 100, 0, 100, 120, 0, 140, 150,
        140, 160, 200, 180, 0, 180, 200, 0, 220, 0, 220, 240,
        200, 260, 260, 150, 280, 0, 300, 300, 0, 320, 200, 0,
        350, 75, 400};
    return propertyvalue;
}



bool isOwned(int property) {
    if (property ==)
    {
        /* code */
    }
    
}

int main(int argc, char const *argv[])
{

    //********************************************
    // *Used for Player count
    // *Takes an input based on playercount 2-4
    //********************************************
    vector<Player> playerlist;
    int maxRoll;
    int players;
    string name;
    do
    {
        cout << "How many players? ";
        cin >> players;
    } while (players > 4 || players < 2);
    //  Populate the playerlist
    for (int i = 0; i < players; i++)
    {
        Player player;
        //  Populate player info
        playerlist.push_back(player);
    }
    for (auto player : playerlist) // inital gamestart.
    {
        cout << "Enter your name: ";
        cin >> name;
        player.cash = 1500;
        player.board_pos = 0;
        player.player_name = name;
        cout << "Player Cash: $" << player.cash << endl;
        cout << "Position on board: " << player.board_pos << endl;
        cout << "Player Name: " << player.player_name << endl;
    }

    int rolloff[players];
    srand(time(0));
    for (int i = 0; i < players; i++)
    {
        rolloff[i] = rollDie();
        cout << rolloff[i] << endl;
    }

    int n = sizeof(rolloff) / sizeof(rolloff[0]);
    // Intialize the value of max and index
    int max = INT_MIN;
    int index = -1;
    // Iterate the array
    for (int i = 0; i < n; i++)
    {
        if (rolloff[i] > max)
        {
            // If current value is greater than max
            // value then replace it with max value
            max = rolloff[i];
            index = i;
        }
    }
    cout << "The Maximum Roll was ";
    cout << max << " by Player " << index + 1 << endl;

    vector<string> board = fillboard();
    vector<int> propertyvalue = property();
    cout << "\nThe Gameboard:\n"
         << endl;
    for (int i = 0; i < board.size(); i++)
    {
        if(propertyvalue[i] != 0) {
        cout << board[i] << " $" << propertyvalue[i] << endl;
        }
        else{
            cout << board[i] << endl;
        }
    }

    //********************************************
    // * Start of the game
    // * Iteration through a for loop inbeded in a while loop
    //********************************************
    bool gamestate = true;
    do
    {

        for (auto player : playerlist)
        {
            player.board_pos = player.board_pos + rollDie();
            cout << "Player " << player.player_name << " Landed on "
                 << board[player.board_pos] << endl;
            if(isOwned(propertyvalue[player.board_pos]) == false)
            player.cash = player.cash - propertyvalue[player.board_pos];
            cout << 
        }

    } while (gamestate == true);
    return 0;
}


I know the last part is incomplete it's temporary
Last edited on
property should also be an object.
consider (this may be overkill since you simplified some of the rules but hey, its an example)
1
2
3
4
5
6
7
8
9
10
11
12
struct property
{
  int price;
  string name;
  int rent[6]; //none, 1,2,3,4 house, hotel
  int housecount{};
  Player * owner{}; //nullptr at first, or make a bogus bank player?
  bool mortgaged{};//false default @ construct   
  property * related[3]; //relate the other properties of same type or color?
  //optional cool junk like how many times landed on or total rent paid into it
}
vector<property> board; ...


now, there is a concept called parallel arrays that can be handy, where you split up an object into many arrays that are all tied together via their index.
in that case, just if you want to see it:
1
2
3
4
5
6
7
enum properties_e{baltic, ...fill in all of them..., boardwalk, numprops};
int Pprice[numprops];
string Pnames[numprops];
int Prents[numprops][6];
...etc
and then
cout << Pnames[boardwalk] << "rent is " <<Prents[boardwalk][housecount];

or whatever. I don't recommend splitting it up like this for you, but there are places where it has a performance lift in high speed code projects, or other places where this can make sense to do. Its a very annoying way to do things.

your rolldice is great. I still recommend learning <random> when you get a chance, its so much more flexible and only a tiny bit more effort. Not really a war, I was being fast and loose with some math, and while its close enough to screw around with, its not valid and they were right to call that out. The right approximation can be amazing in the right places, or complete trash as well. It was the latter, here.
Last edited on
You have several problem in your code. Especial line 126. The 'auto' trap. Try the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int main(int argc, char const *argv[])
{

    //********************************************
    // *Used for Player count
    // *Takes an input based on playercount 2-4
    //********************************************
    vector<Player> playerlist;
    int maxRoll;
    int players;
    string name;
    do
    {
        cout << "How many players? ";
        cin >> players;
    } while (players > 4 || players < 2);
    //  Populate the playerlist
    for (int i = 0; i < players; i++)
    {
        Player player;
        //  Populate player info
        playerlist.push_back(player);
    }
    for (auto player : playerlist) // inital gamestart.
    {
        cout << "Enter your name: ";
        cin >> name;
        player.cash = 1500;
        player.board_pos = 0;
        player.player_name = name;
    }
    for (auto player : playerlist) // inital gamestart.
    {
        cout << "Player Cash: $" << player.cash << endl;
        cout << "Position on board: " << player.board_pos << endl;
        cout << "Player Name: " << player.player_name << endl;
    }

   return 0;
}
How many players? 2
Enter your name: a
Enter your name: b
Player Cash: $0
Position on board: 0
Player Name: 
Player Cash: $0
Position on board: 0
Player Name: 

So the player in playerlist has no content. Why? Because player on line 126 is a copy and dismissed after the loop ends. Consider:

for (Player& player : playerlist) // Note: & // inital gamestart.

You actually don't need two loops (line 120/126). You can do all this within the loop on line 120
@jonnin - minor point but the related array should have 4 elements not 3 as the 4 stations need to be linked as the rent due for landing on one increases if more than 1 owned.
re game start. The players first roll the dice and the highest scorer starts. The rules (at least with my very old version) say nothing about duplicate highest scorers but we always then had the highest scorers roll again - and again et al until one person has a highest score who then starts first.

Also the names used are those of the tokens (which used to be ship, hat, dog, iron, car, boot).

In a first playable version, I'd avoid mortgages and property auctions. These add somewhat complexity. So if you land an an unowned property you either buy it or not. if not, then it's not auctioned. These additions can be added later if required.

re game start. The players first roll the dice and the highest scorer starts. The rules (at least with my very old version) say nothing about duplicate highest scorers but we always then had the highest scorers roll again - and again et al until one person has a highest score who then starts first.

Statistically, that just boils down to "randomly select start player, with each person having an equal chance of being start player".

With physical dice, the "who rolls highest" method is a simple way of implementing that, but in a digital version, there's no need to model that process. Simply randomly selecting one player is statistically identical, and much more straightforward to implement.
I said 3 because self+3 -> 4 total. But that reminds me that you might want bools for 'is train' or 'is utility' on a property to trigger all the special logic for those. You can do it with 4 slots and put self into them, if you prefer it.
Simply randomly selecting one player is statistically identical, and much more straightforward to implement.

Yes, put them into "bag" (e.g. vector) and shuffle. Then you have players in (random) order. https://cplusplus.com/reference/algorithm/shuffle/
Refined Main:

No clue how to implement the class structures (Being Player, Board, Community Chest and Properties) into the game in a smooth manner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#include <iostream>
#include <string>
#include <iomanip>
#include <cctype>
#include <array>
#include <queue>
#include <vector>
#include "linkedlist.cc"

using namespace std;

class Player
{
public:
    int cash;
    int board_pos;
    int token; // player turn
    string player_name;
    vector<Properties> propertyOwned; // played owned properties
};

class Board
{
public:
    int location;
    LinkList<Properties> b; // board properties
    vector<string> fillboard() //Names Only
    {
        vector<string> board{
            "GO",
            "Mediterranean Avenue ",
            "Community Chest",
            "Baltic Avenue ",
            "Income Tax ",
            "Reading Railroad ",
            "Oriental Avenue ",
            "Chance",
            "Vermont Avenue ",
            "Connecticut Avenue ",
            "Jail",
            "St. Charles Place ",
            "Electric Company ",
            "States Avenue ",
            "Virginia Avenue ",
            "Pennsylvania Railroad ",
            "St. James Place ",
            "Community Chest",
            "Tennessee Avenue ",
            "New York Avenue ",
            "Free Parking",
            "Kentucky Avenue ",
            "Chance",
            "Indiana Avenue ",
            "Illinois Avenue ",
            "B&O Railroad ",
            "Atlantic Avenue ",
            "Ventnor Avenue ",
            "Water Works ",
            "Marvin Gardens ",
            "Go To Jail",
            "Pacific Avenue ",
            "North Carolina Avenue ",
            "Community Chest",
            "Pennsylvania Avenue ",
            "Short Line ",
            "Chance",
            "Park Place ",
            "Luxury Tax ",
            "Boardwalk "};
    }
    return board;
};

class CommunityChest //use 2 stacks for cards (CHANCE/ComChest)
{
public:
};

class Properties
{
public:
    vector<int> property() // ADD to properties class
    {
        vector<int> propertyvalue{
            0, 60, 0, 60, 200, 200, 100, 0, 100, 120, 0, 140, 150,
            140, 160, 200, 180, 0, 180, 200, 0, 220, 0, 220, 240,
            200, 260, 260, 150, 280, 0, 300, 300, 0, 320, 200, 0,
            350, 75, 400};
        return propertyvalue;
    }
};

int rollDie()
{
    int rolls[2];
    int min = 1; // the min number a die can roll is 1
    int max = 6; // the max value is the die size

    for (size_t i = 0; i < 2; i++)
    {
        rolls[i] = rand() % (max - min + 1) + min;
    }
    cout << "Roll 1: " << rolls[0] << "\nRoll 2: " << rolls[1] << endl;
    int roll = rolls[0] + rolls[1];
    return roll;
}

bool isOwned(int property)
{

    //*TODO: Add return*//
    // OR SCRAP FUNCTION//
}

int main(int argc, char const *argv[])
{

    //********************************************
    // *Used for Player count
    // *Takes an input based on playercount 2-4
    //********************************************
    vector<Player> playerlist;
    int players;
    string name;

    do
    {
        cout << "How many players? ";
        cin >> players;
    } while (players > 4 || players < 2);

    //  Populate the playerlist
    for (int i = 0; i < players; i++)
    {
        Player player;
        //  Populate player info
        playerlist.push_back(player);
    }
    for (auto player : playerlist) // inital gamestart.
    {
        cout << "Enter your name: ";
        cin >> name;
        player.cash = 1500;
        player.board_pos = 0;
        player.player_name = name;
    }
    for (auto player : playerlist) // inital gamestart.
    {
        cout << "Player Cash: $" << player.cash << endl;
        cout << "Position on board: " << player.board_pos << endl;
        cout << "Player Name: " << player.player_name << endl;
    }
    int rolloff[players];
    srand(time(0));
    for (int i = 0; i < players; i++)
    {
        rolloff[i] = rollDie();
        cout << rolloff[i] << endl;
    }

    int n = sizeof(rolloff) / sizeof(rolloff[0]);
    // Intialize the value of max and index
    int max = INT_MIN;
    int index = -1;
    // Iterate the array
    for (int i = 0; i < n; i++)
    {
        if (rolloff[i] > max)
        {
            // If current value is greater than max
            // value then replace it with max value
            max = rolloff[i];
            index = i;
        }
    }
    cout << "The Maximum Roll was ";
    cout << max << " by Player " << index + 1 << endl;

    vector<string> board = fillboard(); //edit this**
    vector<int> propertyvalue = property(); //edit this**
    cout << "\nThe Gameboard:\n"
         << endl;
    for (int i = 0; i < board.size(); i++)
    {
        if (propertyvalue[i] != 0)
        {
            cout << board[i] << " $" << propertyvalue[i] << endl;
        }
        else
        {
            cout << board[i] << endl;
        }
    }

    //********************************************
    // * Start of the game
    // * Iteration through a for loop inbeded in a while loop
    //********************************************
    //bool gamestate = true;
    // do
    // {

    //     for (auto player : playerlist)
    //     {
    //         player.board_pos = player.board_pos + rollDie();
    //         cout << "Player " << player.player_name << " Landed on "
    //              << board[player.board_pos] << endl;
    //         if (isOwned(propertyvalue[player.board_pos]) == false)
    //             player.cash = player.cash - propertyvalue[player.board_pos];
    //         player.addProperty = board[player.board_pos];
    //         cout << "Current Cash: $" << player.cash << endl;
    //     }

    // } while (gamestate == true);
    return 0;
}


A lot of errors for obvious reasons
Last edited on
Oh yeah and here's the concurrent outline for my program just to get a basis:
I would like to focus the majority of my attention on the Properties class because that's the one causing the most issues

- allow to choose the amount of players (players can be all bots)
- have a simple display of what each player has landed on and a print out of all - transactions that happened during the game with output to a file
- have the players start with $1500 at the beginning of the game
- make it that whenever a player lands on a property the player auto buys that property
- make it that whenever a player lands on an already bought property they pay rent to that player
- make it that whenever a player passes go their money increments by $200
- make it that if a player lands on go to jail they get sent directly to jail( don't need to roll doubles to get out)
- make use of the community chest and chance cards. Only need to use 5 cards for each and have them recycle

No GUI stuff needed this is just going to be console/text based
I'm just trying to do this in the simplest form possible
Last edited on
You might consider reading property information from a file.
This allows you to change the game without reprogramming it.

property.txt
format is: <name> , <action> , <cost>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
        GO,0,0
        Mediterranean Avenue,1,60
        Community Chest,2,0
        Baltic Avenue,1,60
        Income Tax,3,200
        Reading Railroad,4,200
        Oriental Avenue,1,100
        Chance,5,0
        Vermont Avenue,1,100
        Connecticut Avenue,1,120
        Jail,6,0
        St. Charles Place,1,140
        Electric Company,7,150
        States Avenue,1,140 
        Virginia Avenue,1,160
        Pennsylvania Railroad,4,200
        St. James Place,1,180
        Community Chest,2,0
        Tennessee Avenue,1,180 
        New York Avenue,1,200
        Free Parking,8,0
        Kentucky Avenue,1,220
        Chance,5,0
        Indiana Avenue,1,220
        Illinois Avenue,1,240
        B&O Railroad,4,200
        Atlantic Avenue,1,260 
        Ventnor Avenue,1,260 
        Water Works,7,150
        Marvin Gardens,1,280
        Go To Jail,9,0
        Pacific Avenue,1,300
        North Carolina Avenue,1,300 
        Community Chest,2,0
        Pennsylvania Avenue,1,320
        Short Line,4,200
        Chance,5,0
        Park Place,1,350
        Luxury Tax,3,75
        Boardwalk,1,400


property.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#pragma once
#include <string>

//  The following values are hard coded in the property.txt file
enum class Action
{
    GO = 0,
    PROPERTY = 1,
    COMMUNITY_CHEST = 2,
    TAX = 3,
    RAILROAD = 4,
    CHANCE = 5,
    JAIL = 6,
    UTILITY = 7,
    FREE_PARKING = 8,
    GOTO_JAIL = 9
};

struct Property
{
    std::string     m_name;
    Action          m_action;
    int             m_cost;
    //  Other attributes 
};


PropFile.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#pragma once
#include <istream>
#include <fstream>
#include <string>
#include <vector>
#include "property.h"

class PropFile 
{
    std::ifstream   m_ifs;

public:
    PropFile(const std::string& fname);

    bool ReadData(std::vector<Property>& props);
    void Dump(const std::vector<Property>& props);
};


PropFile.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <istream>
#include <fstream>
#include <iostream>
#include <sstream>
#include <iomanip>
#include "PropFile.h"
using namespace std;

/*
*   Class to read property information from a file   
*   Format is:
*       <property name> , <action> , <cost>
*/

PropFile::PropFile(const std::string& fname)
{
	m_ifs.open(fname);
	if (!m_ifs.is_open())
	{
		cout << "Unable to open property data file" << endl;
		exit(1);
	}
}

bool PropFile::ReadData (vector<Property> & props)
{
   string          line;
    int             act;
       
    while (getline(m_ifs, line))
    {
        stringstream    ss(line);
        Property        prop;
        string          temp;

        if (!getline(ss, prop.m_name, ','))
            return false;   //  error reading property name
        if (!getline(ss, temp, ','))
            return false;
        act = atoi(temp.c_str());
        prop.m_action = (enum Action)act;
        if (!getline(ss, temp, ','))
            return false;
        prop.m_cost = atoi(temp.c_str());
        props.push_back(prop);
    }
    return true;
}

void PropFile::Dump(const std::vector<Property>& props)
{
    for (auto prop : props)
        cout << setw(30) << prop.m_name << "  "
        << (int)prop.m_action << "  "
        << prop.m_cost << endl;

}


A test program:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <vector>
#include <sstream>
#include "propfile.h"
using namespace std;

int main(int argc, char const* argv[])
{   
    vector<Property>    properties;
    PropFile            propfile("property.txt");
    
    if (!propfile.ReadData(properties))
    {
        cout << "Problem parsing line" << endl;
        return 2;
    }
    propfile.Dump(properties);
    return 0;
}

Last edited on
I 'knocked up' this program over the weekend whilst watching sport. I confess there was very little thought given to the design! It automatically plays with 2 - 6 players. It has some current restrictions. No chance/community chest. No in Jail. No mortgaging. No auctions.

It might give some ideas...

If I get some of the rest coded before the time limit on this thread expires, I'll post the updated code.

There are 8 files board.h, dice.h, monopoly.h, places.h, player.h, places.cpp, player.cpp, monopoly.cpp (main)

Note that I make no claims re this. Now that it can be used to 'play', it's probably due for a refactor. However, anyone who wants to use/change feel free.

board.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#pragma once

#include "places.h"
#include "dice.h"

#include <vector>
#include <initializer_list>
#include <iostream>

// Represents the Monopoly board
class Board {
	std::vector<Places> places {    // Specifies the details of each place on the board
		{Go, new cGo},
		{OldKentRoad, new cProp(2, 10, 30, 90, 160, 250, 60, 50, 50, 30), {WhitechapelRoad}},
		{CommunityChest1, new cComm},
		{WhitechapelRoad,new cProp(4, 20, 60, 180, 320, 450, 60, 50, 50, 30), {OldKentRoad}},
		{IncomeTax, new cTax(200)},
		{KingscrossStation, new cStation(25, 50, 100, 200, 200, 100), {MaryleboneStation, FenchurchStreetStation, LiverpoolStreetStation}},
		{TheAngelIslington, new cProp(6, 30, 90, 270, 400, 550, 100, 50, 50, 50), {EustonRoad, PentovilleRoad}},
		{Chance1, new cChance},
		{EustonRoad, new cProp(6, 30, 90, 270, 400, 550, 100, 50, 50, 50), {TheAngelIslington, PentovilleRoad}},
		{PentovilleRoad, new cProp(8, 40, 100, 300, 450, 600, 100, 50, 50, 60), {TheAngelIslington, EustonRoad}},
		{Jail, new cJail},
		{PallMall, new cProp(10, 50, 150, 450, 625, 750, 140, 100, 100, 70)},
		{ElecticCompany, new cUtility(150, 75)},
		{Whitehall, new cProp(10, 50, 150, 450, 625, 750, 140, 100, 100, 70)},
		{NorthumberlandAvenue, new cProp(12, 60, 180, 500, 700, 900, 160, 100, 100, 80)},
		{MaryleboneStation, new cStation(25, 50, 100, 200, 200, 100)},
		{BowStreet, new cProp(14, 70, 200, 550, 750, 950, 180, 100, 100, 90)},
		{CommunityChest2, new cComm},
		{MarlboroughStreet, new cProp(14, 70, 200, 550, 750, 950, 180, 100, 100, 90)},
		{VineStreet, new cProp(16, 80, 220, 600, 800, 1000, 200, 100, 100, 100)},
		{Freeparking, new cParking},
		{Strand, new cProp(18, 90, 250, 700, 875, 1050, 220, 150, 150, 110)},
		{Chance2, new cChance},
		{FleetStreet, new cProp(18, 90, 250, 700, 875, 1050, 220, 150, 150, 110)},
		{TrafalgarSquare, new cProp(20, 100, 300, 750, 925, 1100, 240, 150, 150, 120)},
		{FenchurchStreetStation, new cStation(25, 50, 100, 200, 200, 100)},
		{LeicesterSquare, new cProp(22, 110, 330, 800, 975, 1150, 260, 150, 150, 130)},
		{CoventryStreet, new cProp(22, 110, 330, 800, 975, 1150, 260, 150, 150, 130)},
		{WaterWorks, new cUtility(150, 75)},
		{Picadilly, new cProp(22, 120, 360, 850, 1025, 1200, 280, 150, 150, 140)},
		{GoToJail, new cGoJail},
		{RegentSreet, new cProp(26, 130, 390, 900, 1100, 1275, 300, 200, 200, 150)},
		{OxfordStreet, new cProp(26, 130, 390, 900, 1100, 1275, 300, 200, 200, 150)},
		{CommunityChest3, new cComm},
		{BondStreet, new cProp(28, 150, 450, 1000, 1200, 1400, 320, 200, 200, 160)},
		{LiverpoolStreetStation, new cStation(25, 50, 100, 200, 200, 100)},
		{Chance3, new cChance},
		{ParkLane, new cProp(35, 175, 500, 1100, 1300, 1500, 350, 200, 200, 175)},
		{SuperTax, new cTax(100)},
		{Mayfair, new cProp(50, 200, 600, 1400, 1700, 2000, 400, 200, 200, 200)}
	};

	cDice dic;    // Dice to use

public:
	Board() {
		// Set pointer for each place to Board class
		for (auto& p : places)
			p.setBoard(this);
	}

	~Board() {
		for (auto& p : places)
			delete p.getPlaceC();
	}

	// Can't copy or assign Board class
	Board(const Board&) = delete;
	Board& operator=(const Board&) = delete;

	// Number of board elements
	unsigned size() const noexcept {
		return unsigned(places.size());
	}

	// Display the board
	void display() noexcept {
		for (auto& b : places) {
			for (const auto& ps : b.getPlayers())
				std::cout << sNames[ps] << "  ";

			b.getPlaceC()->display(b);
			std::cout << '\n';
		}

		std::cout << '\n';
	}

	// Get specified place
	Places& getPlace(unsigned pos) noexcept {
		return places[pos];
	}

	// Shake the dice
	Dice shake() noexcept {
		return dic.shake();
	}

	// Release owned places
	void release(Symbols owner) {
		for (auto& b : places)
			if (b.getOwner() == owner)
				b.setOwner(None);
	}
};


dice.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#pragma once

#include <random>
#include <ostream>

struct Dice {
	unsigned first {};    // First dice thrown
	unsigned second {};   // Second dice thrown
	unsigned total {};    // Total of the two die
	bool isdouble {};     // Set if both dice the same value
};

// Display the dice
inline std::ostream& operator<<(std::ostream& os, const Dice& di) {
	return os << di.total << " (" << di.first << ", " << di.second << ')';
}

// Class for the dice
class cDice {
	std::uniform_int_distribution<unsigned> die;
	Dice dice {};

public:
	cDice() noexcept : die(1, 6) {}

	// Shake the dice
	Dice shake() noexcept {
		dice.first = die(rng);
		dice.second = die(rng);
		dice.total = dice.first + dice.second;
		dice.isdouble = dice.first == dice.second;
		return dice;
	}

	// Get last die throw
	Dice last() const noexcept {
		return dice;
	}
};


monopoly.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#pragma once

#include <vector>
#include <iterator>
#include <utility>
#include <random>

inline std::mt19937 rng(std::random_device {}());

inline constexpr unsigned MaxPlayer { 6 };

enum Place {Go, OldKentRoad, CommunityChest1, WhitechapelRoad, IncomeTax, KingscrossStation, TheAngelIslington, Chance1,
	EustonRoad, PentovilleRoad, Jail, PallMall, ElecticCompany, Whitehall, NorthumberlandAvenue, MaryleboneStation,
	BowStreet, CommunityChest2, MarlboroughStreet, VineStreet, Freeparking, Strand, Chance2, FleetStreet, TrafalgarSquare,
	FenchurchStreetStation, LeicesterSquare, CoventryStreet, WaterWorks, Picadilly, GoToJail, RegentSreet, OxfordStreet,
	CommunityChest3, BondStreet, LiverpoolStreetStation, Chance3, ParkLane, SuperTax, Mayfair};


enum PlaceType {GoT, PropT, CommChestT, IncometaxT, StationT, ChanceT, InJail, VisitJail, UtilityT, ParkingT, GoJailT};

enum Symbols {None, Iron = 1, Hat, Dog, Boot, Ship, Car};

inline constexpr const char* pNames[] { "Go", "Old Kent Road", "Community Chest", "White Chapel Road", "Income Tax", "Kings Cross Station",
"The Angel Islington", "Chance", "Euston Road", "Pentoville Road", "Jail", "Pall Mall", "Electric Company", "Whitehall",
"Northumberland Avenue", "Marylebone Station", "Bow Street", "Community Chest", "Marlborough Street", "Vine Street", "Free Parking",
"Strand", "Chance", "Fleet Street", "Trafalgar Square", "Fenchurch Street Station", "Leicester Square", "Coventry Street",
"Water Works", "Picadilly", "Go To Jail", "Regent Street", "Oxford Street", "Community Chest", "Bond Street", "Liverpool Street Station",
"chance", "Park Lane", "Super Tax", "Mayfair" };

inline constexpr const char* sNames[] { "None", "Iron", "Hat", "Dog", "Boot", "Ship", "Car" };

static_assert(Mayfair == 39, "Missing Place");
static_assert(Car == 6, "Missing player");
static_assert(std::size(pNames) == Mayfair + 1, "Missing place name");
static_assert(std::size(sNames) == Car + 1, "Missing symbol name");
static_assert(Car <= MaxPlayer, "Missing symbol name");

Last edited on
places.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#pragma once

#include <vector>
#include <iostream>
#include <algorithm>

#include "monopoly.h"
#include "player.h"

class Board;
class cPlace;

class Places {
	cPlace* const place;                    // Pointer to required class
	cPlayers players;                       // Players currently on this place
	const std::vector<Place> associated;    // Associated places of the same type
	Symbols owner { None };                 // Owner
	Board* board {};                        // Pointer to main board
	const Place pl {};                      // Place name

public:
	Places(Place plc, cPlace* p) noexcept : pl(plc), place(p) {}

	Places(Place plc, cPlace* p, std::initializer_list<Place> ass) : pl(plc), place(p), associated(ass.begin(), ass.end()) {}

	// Get place derived class
	cPlace* getPlaceC() noexcept {
		return place;
	}

	// Set pointer to board
	void setBoard(Board* b) noexcept {
		board = b;
	}

	// Return pointer to board
	Board* getBoard() const noexcept {
		return board;
	}

	// Get associated properties
	const std::vector<Place>& getAssociated() const noexcept {
		return associated;
	}

	// Get players for this place
	cPlayers& getPlayers() noexcept {
		return players;
	}

	// Get place name
	Place getPlace() noexcept {
		return pl;
	}

	// Get property owner
	Symbols getOwner() const noexcept {
		return owner;
	}

	void setOwner(Symbols o) noexcept {
		owner = o;
	}
};

// Base for polymorphic classes for each place type
class cPlace {
public:
	virtual ~cPlace() = default;

	void show(Players& pls);
	virtual void play(Players&) = 0;
	virtual void display(Places& pl) const noexcept {
		std::cout << pNames[pl.getPlace()];
	}
};

// GO class
class cGo : public cPlace {
	static const unsigned collect { 200 };   // Amount to collect when pass/land on Go

public:
	void play(Players&) override;
};

// Property class
class cProp : public cPlace{
	const unsigned rent[5] {};        // Rents for number houses (0 - 4)
	const unsigned rentHot {};        // Rent for hotel
	const unsigned buy {};            // Price to buy
	const unsigned house {};          // Cost for a house
	const unsigned hotel {};          // Cost for an hotel
	const unsigned mortage {};        // Mortgage value (not yet used)

	unsigned houses {};               // Number of houses erected
	bool hasHotel {};                 // Has an hotel been built?

	bool allOwned(const Places& pl) const noexcept;  // Are all associated properties owned?

public:
	cProp(unsigned r, unsigned r1, unsigned r2, unsigned r3, unsigned r4, unsigned rhot, unsigned b, unsigned h, unsigned hot, unsigned mor) noexcept :
		rent{r, r1, r2, r3, r4}, rentHot(rhot), buy(b), house(h), hotel(hot), mortage(mor) {}

	void play(Players&) override;
	void display(Places& pl) const noexcept override;
};

// Community Chest class
class cComm : public cPlace {
public:
	void play(Players&) override;
};

// Income Tax class
class cTax : public cPlace {
	const unsigned tax {};    // Amount of tax to pay

public:
	cTax(unsigned t) : tax(t) {}

	void play(Players&) override;
	void display(Places& pl) const noexcept override;
};

// Station class
class cStation : public cPlace {
	const unsigned rent[4] {};             // Rents for 1 - 4 stations owned
	const unsigned buy {};                 // Price to buy
	const unsigned mortage {};             // Mortgage value (not yet used)

	unsigned manyOwned(const Places& pl) const noexcept;  // How many stations are owned

public:
	cStation(unsigned r, unsigned r2, unsigned r3, unsigned r4, unsigned b, unsigned mor) noexcept :
		rent { r, r2, r3, r4 }, buy(b), mortage(mor) {}

	void play(Players&);
	void display(Places& pl) const noexcept override;
};

// Chance class
class cChance : public cPlace {
public:
	void play(Players&) override;
};

// Jail class
class cJail : public cPlace {
// Needs to deal with in jail and just visiting!!!

public:
	void play(Players&) override;
};

// Utility class
class cUtility : public cPlace {
	const static inline unsigned rent[2] { 4, 10 };       // Rents due if 1 or both utilities used
	unsigned buy {};
	unsigned mor {};

	unsigned manyOwned(const Places& pl) const noexcept;  // How many utilities owned

public:
	cUtility(unsigned b, unsigned m) noexcept : buy(b), mor(m) {}

	void play(Players&) override;
	void display(Places& pl) const noexcept override;
};

// Parking class
class cParking : public cPlace {
public:
	void play(Players&) override;
};

// Go to Jail class
class cGoJail : public cPlace {
public:
	void play(Players&) override;
};


player.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#pragma once

#include <vector>
#include <set>
#include <algorithm>

#include "monopoly.h"
#include "dice.h"

class Board;

// Represents a player
class Player {
	static const unsigned init { 1500 };

public:
	int amount {init};        // Amount of money player has
	Symbols symbol;           // Symbol used by this player
	unsigned position {};     // Board position
	Dice dice {};             // Last dice thrown
	bool inPlay { true };     // false if bankrupt
};

// Represents all players playing the game
class Players {
	std::vector<Player> players;    // All valid players
	unsigned curr {};               // Current player index into players
	Board& brd;                     // Board to use

public:
	// Obtain number of players and generate players vector accordingly
	Players(Board& b);

	// Returns current player
	Player& getCurr() noexcept ;

	// Advances to next player
	Player& next() noexcept;

	// Advance position on the board for the current player
	void advance(unsigned no);

	// Returns board
	Board& getBoard() {
		return brd;
	}

	unsigned noPlayers() const noexcept;

	void details() const;
};

// Players currently on any same board place
class cPlayers {
	std::set<Symbols> players;

public:
	void addPlayer(Symbols p) {
		players.insert(p);
	}

	void removePlayer(Symbols p) {
		players.erase(p);
	}

	auto begin() {
		return players.begin();
	}

	auto end() {
		return players.end();
	}
};

Pages: 123