Monopoly -- Forum

Pages: 123
monopoly.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
58
59
60
61
62
#include <iostream>

#include "board.h"
#include "player.h"

void process(Players& pls) {
	auto& p { pls.getCurr() };                       // Player

	if (p.inPlay) {
		auto& board { pls.getBoard() };              // Board
		auto& plc { board.getPlace(p.position) };    // Places

		std::cout << sNames[p.symbol] << "\nHas " << p.amount << '\n';
		plc.getPlaceC()->show(pls);
		p.dice = board.shake();

		std::cout << "\nThrew " << p.dice << '\n';
		pls.advance(p.dice.total);

		auto& plcs { board.getPlace(p.position) };
		auto* pl { plcs.getPlaceC() };

		pl->play(pls);
		std::cout << '\n';
	}
}

int main() {
	Board board;

	if (board.size() != 40)
		return (std::cout << "Incorrect board size - " << board.size() << '\n'), 1;

	Players players(board);

	//board.display();

	const auto start { players.getCurr().symbol };

	for (unsigned round { 1 }, prev { }; players.noPlayers() > 1; round += players.getCurr().symbol == start) {
		if (round != prev) {
			char inp {'y'};

			prev = round;
			//std::cout << "Next round (d/y/n): ";
			//std::cin >> inp;

			if (inp == 'd')
				board.display();
			else if (inp != 'y')
				break;

			std::cout << "\nRound " << round << "\n\n";
		}

		process(players);
		players.next();
	}

	players.details();
	std::cout << '\n';
}


player.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
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
#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>

#include "board.h"


// Obtain number of players and generate players vector accordingly
Players::Players(Board& b) : brd(b) {
	unsigned noPlay {};
	Player pt;

	for (unsigned t { 1 }; t <= MaxPlayer; ++t) {
		pt.symbol = Symbols(t);
		pt.dice = b.shake();
		players.push_back(pt);
	}

	std::ranges::shuffle(players, rng);

	do {
		std::cout << "How many players (2 - " << MaxPlayer << "): ";
		std::cin >> noPlay;
		std::cin.clear();
		std::cin.ignore(1000, '\n');
	} while ((noPlay < 2 || noPlay > MaxPlayer) && (std::cout << "Invalid number of players\n"));

	players.resize(noPlay);

	// total symbol
	std::vector < std::pair<unsigned, Symbols>> highest;

	unsigned high {};

	for (unsigned play { }; auto& p : players) {
		brd.getPlace(Go).getPlayers().addPlayer(p.symbol);

		std::cout << "Player " << ++play << " is a " << sNames[p.symbol] << " and threw " << p.dice << '\n';

		if (p.dice.total > high) {
			highest.clear();
			highest.emplace_back(p.dice.total, p.symbol);
			high = p.dice.total;
		} else if (p.dice.total == high)
			highest.emplace_back(p.dice.total, p.symbol);
	}

	std::cout << "\nThe highest score is " << high << " for player";

	if (highest.size() > 1) {
		std::cout << "s:\n";

		for (const auto& h : highest)
			std::cout << sNames[h.second] << '\n';
	} else
		std::cout << ' ' << sNames[highest.front().second];

	std::cout << '\n';

	while (highest.size() > 1) {
		std::cout << highest.size() << " players tied for first\nTie breaker for first\n";

		for (auto& h : highest) {
			const auto t { b.shake() };

			h.first = t.total;
			std::cout << sNames[h.second] << " threw " << t << '\n';
		}

		std::sort(highest.begin(), highest.end(), [](auto p1, auto p2) {return p1.first > p2.first; });
		std::erase_if(highest, [&highest](auto p1) {return p1.first < highest.front().first; });
	}

	curr = unsigned(std::find_if(players.begin(), players.end(), [&highest](auto p) {return p.symbol == highest.front().second; }) - players.begin());
	std::cout << "\nPlay starts with " << sNames[players[curr].symbol] << "\n\n";
}

// Returns current player
Player& Players::getCurr() noexcept {
	return players[curr];
}

// Advances to next player
Player& Players::next() noexcept {
	if (++curr >= players.size())
		curr = 0;

	return getCurr();
}

// Advance position on the board
void Players::advance(unsigned no) {
	auto& p { players[curr] };
	auto& plc { brd.getPlace(p.position) };

	plc.getPlayers().removePlayer(p.symbol);

	if (p.position + no > brd.size()) {
		std::cout << "Passed ";
		brd.getPlace(Go).getPlaceC()->play(*this);
		//std::cout << ")\n";
	}

	p.position += no;
	p.position %= brd.size();
	brd.getPlace(p.position).getPlayers().addPlayer(p.symbol);
}

unsigned Players::noPlayers() const noexcept {
	return unsigned(std::ranges::count_if(players, [](auto p) {return p.inPlay; }));
}

void Players::details() const {
	for (const auto& p : players) {
		std::cout << sNames[p.symbol] << "  ";

		if (!p.inPlay)
			std::cout << "Bankrupt\n";
		else
			std::cout << p.amount << '\n';
	}
}

places.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
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#include <algorithm>

#include "places.h"
#include "board.h"

void cPlace::show(Players& pls) {
	auto& p { pls.getCurr() };                   // Player
	auto& board { pls.getBoard() };              // Board
	auto& plc { board.getPlace(p.position) };    // Places

	std::cout << "Located on " << pNames[plc.getPlace()];
}

void cGo::play(Players& pls) {
	pls.getCurr().amount += collect;
	std::cout << "Go - collect " << collect << '\n';
}

bool cProp::allOwned(const Places& pl) const noexcept {
	return std::ranges::all_of(pl.getAssociated(), [&](auto p) {
		return pl.getBoard()->getPlace(p).getOwner() == pl.getOwner();
		});
}

void cProp::play(Players& pls) {
	show(pls);
	std::cout << '\n';

	auto& p { pls.getCurr() };                   // Player
	auto& board { pls.getBoard() };              // Board
	auto& plc { board.getPlace(p.position) };    // Places

	const auto allown { allOwned(plc) };

	// NOT DEAL WITH BUYING HOUSES/HOTEL
	if (const auto own { plc.getOwner() }; own == None) {
		// Deal with buy here
		std::cout << "Not owned - ";

		if (p.amount >= int(buy)) {
			std::cout << "Bought for " << buy << '\n';
			plc.setOwner(p.symbol);
			p.amount -= buy;
		} else
			std::cout << "Not enough money to buy!\n";
	} else if (own != p.symbol) {
		// Deal with rent here if not owned
		const auto r { hasHotel ? hotel : rent[houses] * (1 + (allown && houses == 0))};    // Rent is doubled if all owned and no properties

		std::cout << "Rent due is " << r << " - ";

		if (p.amount >= int(r)) {
			std::cout << "Rent paid\n";
			p.amount -= r;
		} else {
			std::cout << "Cannot afford to pay the rent - Bankrupt!\n";
			p.inPlay = false;
			board.release(p.symbol);
		}
	} else {
		// Deal with buying house/hotel - NOTE allow uneven build
		if (allown)
			if (!hasHotel && houses < 5) {
	     		// Buy a house if possible
				if (p.amount >= int(house)) {
					p.amount -= house;
					++houses;
					std::cout << "House bought for " << house << '\n';
				} else
					std::cout << "Not enough money to buy!\n";
			} else if (!hasHotel)
				// Buy hotel if possible
				if (p.amount >= int(hotel)) {
					p.amount -= hotel;
					houses = 0;
					hasHotel = true;
				} else
					std::cout << "Not enough money to buy!\n";
	}
}

void cProp::display(Places& pl) const noexcept {
	std::cout << pNames[pl.getPlace()];

	if (const auto own { pl.getOwner() }; own == None)
		std::cout << " Not owned";
	else {
		std::cout << " Owned by " << sNames[own];

		if (houses > 0)
			std::cout << ' ' << houses << " houses";
		else
			if (hasHotel)
				std::cout << " Hotel";
	}
}

void cComm::play(Players& pl) {
	show(pl);
	std::cout << " [not yet implemented]\n";
}

void cTax::play(Players& pls) {
	show(pls);

	auto& p { pls.getCurr() };

	if (p.amount >= tax) {
		p.amount -= tax;
		std::cout << '\n' << tax << " Tax paid\n";
	} else {
		std::cout << "\nCannot afford to pay the tax - Bankrupt!\n";
		p.inPlay = false;
		pls.getBoard().release(p.symbol);
	}
}

void cTax::display(Places& pl) const noexcept {
	std::cout << pNames[pl.getPlace()] << " " << tax;
}

unsigned cStation::manyOwned(const Places& pl) const noexcept {
	return 1 + unsigned(std::ranges::count_if(pl.getAssociated(), [&](auto p) {
		return pl.getBoard()->getPlace(p).getOwner() == pl.getOwner();
		}));
}

void cStation::play(Players& pls) {
	show(pls);
	std::cout << '\n';

	auto& p { pls.getCurr() };                   // Player
	auto& board { pls.getBoard() };              // Board
	auto& plc { board.getPlace(p.position) };    // Places

	if (const auto own { plc.getOwner() }; own == None) {
		// Deal with buy here
		std::cout << "Not owned - ";

		if (p.amount >= int(buy)) {
			std::cout << "Bought for " << buy << '\n';
			plc.setOwner(p.symbol);
			p.amount -= buy;
		} else
			std::cout << "Not enough money to buy!\n";
	} else if (own != p.symbol) {
		// Deal with rent here if not owned
		const auto r { rent[manyOwned(plc) - 1] };

		std::cout << "Rent due is " << r << " - ";

		if (p.amount >= int(r)) {
			std::cout << "Rent paid\n";
			p.amount -= r;
		} else {
			std::cout << "Cannot afford to pay the rent - Bankrupt!\n";
			p.inPlay = false;
			board.release(p.symbol);
		}
	}
}

void cStation::display(Places& pl) const noexcept {
	std::cout << pNames[pl.getPlace()];

	if (const auto own { pl.getOwner() }; own == None)
		std::cout << " Not owned";
	else
		std::cout << " Owned by " << sNames[own];
}

void cChance::play(Players& pl) {
	show(pl);
	std::cout << " [not yet implemented]\n";
}

void cJail::play(Players& pl) {
	show(pl);
	std::cout << " [not yet implemented]\n";
}

unsigned cUtility::manyOwned(const Places& pl) const noexcept {
	return 1 + unsigned(std::ranges::count_if(pl.getAssociated(), [&](auto p) {
		return pl.getBoard()->getPlace(p).getOwner() == pl.getOwner();
		}));
}

void cUtility::play(Players& pls) {
	show(pls);
	std::cout << '\n';

	auto& p { pls.getCurr() };                   // Player
	auto& board { pls.getBoard() };              // Board
	auto& plc { board.getPlace(p.position) };    // Places

	if (const auto own { plc.getOwner() }; own == None) {
		// Deal with buy here
		std::cout << "Not owned - ";

		if (p.amount >= int(buy)) {
			std::cout << "Bought for " << buy << '\n';
			plc.setOwner(p.symbol);
			p.amount -= buy;
		} else
			std::cout << "Not enough money to buy!\n";
	} else if (own != p.symbol) {
		// Deal with rent here if not owned
		const auto r { rent[manyOwned(plc) - 1] };

		std::cout << "Rent due is " << r << " - ";

		if (p.amount >= int(r)) {
			std::cout << "Rent paid\n";
			p.amount -= r;
		} else {
			std::cout << "Cannot afford to pay the rent - Bankrupt!\n";
			p.inPlay = false;
			board.release(p.symbol);
		}
	}
}

void cUtility::display(Places& pl) const noexcept{
	std::cout << pNames[pl.getPlace()];

	if (const auto own { pl.getOwner() }; own == None)
		std::cout << " Not owned";
	else
		std::cout << " Owned by " << sNames[own];
}

void cParking::play(Players& pl) {
	show(pl);
	std::cout << " Lucky - nothing to do!\n";
}

void cGoJail::play(Players& pl) {
	show(pl);
	std::cout << " [not yet implemented]\n";
}

[thread is alive!]
New post for the project:
I only need one more class/classes for my program to be fully operational. Right now I need chance and community chest and SCUFFEDOPOLY will be done.

Decks.cc:
1
2
3
4
5
6
7
8
9
10
11
class CommunityChest
{
public:
    deque<string> communityChestDeck;
};

class Chance
{
public:
    deque<string> chanceDeck;
};


I'm thinking this is how I would do it though I'm not sure
it needs enough bits to do things, not just a string.
if you ignore the evil one that charges you based off your houses, most of the rest are a flat out +- monetary change, apart from the get out of jail and then move to space ones. so an int for +- money change and a code for a special action, maybe.
Both lines 4 and 10 should be private, not public.
Both your classes need a DrawCard() and ReturnCard() function.
Only DrawCard() and ReturnCard() should have access to your deque<>s.

As jonin suggested you need more information than just a string.
Something like this:
1
2
3
4
5
struct CommChestCard
{   string text;
    int action;  //  Or possibly enum
    int cost;    // + or - if appropriate
};


Line 4 becomes:
 
    deque<CommChestCard> communityChestDeck;


I've noticed you're using the .cc extension for header files.
You should not do this. Most C++ compilers will recognize .cc files are source files and try and compile them. This will cause problems.
Header files should be .h or .hpp.
I've now updated my Monopoly code from above - fixed some logic errors, simplified some operations, added full Community Chest/Chance support, added Jail (but not up to throwing 3 doubles to get out (uses Get Out Of Jail Free card if held or pays the fine), added support for throwing doubles (including going to Jail on 3rd double). Note that interactive output is now to std::cerr so that std::cout can be re-directed to a file to capture the game.

The following limitations still apply:
- No mortgages
- No selling back property (houses/hotel) to bank
- No inter-player buying/selling

No claims are made. There may still be logic or other issues present. This code is freeware and may be used by anyone freely without any restrictions.

There are now 10 files for the program
board.h, cards.h, dice.h, monopoly.h, places.h, player.h
cards.cpp, monopoly.cpp (has main), places.cpp, player.cpp

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
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
#pragma once

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

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

// 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
	cCards community { CCBegin, CCEnd };   // Community cards
	cCards chance { CBegin, CEnd };        // Chance cards

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);
	}

	// Get community
	cCards& getComm() {
		return community;
	}

	// Get chance
	cCards getChance() {
		return chance;
	}
};

// Return info re player, board, places
inline auto getPBP(Players& pls) {
	auto& p { pls.getCurr() };                   // Player
	auto& board { pls.getBoard() };              // Board
	auto& plc { board.getPlace(p.position) };    // Places

	return std::tuple<decltype(p), decltype(board), decltype(plc)> {p, board, plc};
}

// Return info re player, places
inline auto getPP(Board& brd, Players& pls) {
	auto& p { pls.getCurr() };                 // Player
	auto& plc { brd.getPlace(p.position) };    // Places

	return std::tuple<decltype(p), decltype(plc)> {p, plc};
}


cards.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#pragma once

#include <random>
#include <deque>
#include <iostream>

#include "places.h"
#include "player.h"

class cCards {
	std::deque<Cards> cards;

public:
	cCards(Cards, Cards);
	Cards turnover(Players&);
	void returnCard(Cards);
};

// Process card
void cProcess(Cards c, Players& pls);


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;
	}
};


Last edited on
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
74
#pragma once

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

#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
	bool hasCCGetOut {};      // Community Charge get out of jail free
	bool hasCGetOut {};       // Chance get out of jail free
};

// 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:
	Players(Board& b);                     // Obtain number of players and generate players vector accordingly
	Player& getCurr() noexcept;            // Returns current player
	Player& next() noexcept;               // Advances to next player
	void advance(unsigned no) noexcept;    // Advance position on the board for the current player
	void adjust(int amnt) noexcept;        // Adjust total for every player - for Community Charge birthday
	unsigned noPlayers() const noexcept;   // Number of active players
	void details() const noexcept;         // Show player details
	void bankcrupt() noexcept;             // Bankrupt player
	void moveTo(Place) noexcept;           // Move to specific location

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

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

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

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

	// For range-for
	auto begin() const {
		return players.begin();
	}

	// For range-for
	auto end() const {
		return players.end();
	}
};


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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#pragma once

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

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

inline constexpr unsigned MaxPlayer { 6 };

enum Place :unsigned {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 Symbols :unsigned {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");

enum Cards :unsigned {CCHospital, CCBegin = CCHospital, CCInherit, CCBankError, CCInsurance, CCTaxRefund, CCAdvanceGo, CCDoctorFee,
	CCGetOut, CCAnnunity, CCSale, CCShares, CCBeauty, CCBirthday, CCOldKentRoad, CCCChance, CCGoJail, CCEnd = CCGoJail,
CBankDiv, CBegin = CBankDiv, CCrossword, CDrunk, cAdvanceGo, CSpeeding, CLoan, CGoJail, CGetOut, CMayfair, CPallMall,
CTrafalgar, CGoBack3, CMarylebone, CSchool, CEnd = CSchool};

inline constexpr const char* cNames[] { "Pay hospital 100", "You inherit 100", "Bank error in your favour - Collect 200",
"Pay your insurance premium 50", "Income tax refund collect 20", "Advance to Go", "Doctor's fee - pay 50", "get out of jail free",
"Annuity Matures - collect 100", "From sale of stock you get 50", "Receive interest on 7% preference shares - 25",
"You have won second prize in a beauty contest - collect 10", "It is your birthday - Collect 10 from each player",
"Go back to Old Kent Road", "Pay a 10 fine or take a chance", "Go to Jail, Move Directly to Jail, Do not pass Go, do not collect 200",
"Bank pays you dividend of 50", "You have won a crossword competition - collect 100", "Drunk in charge - fine 20",
"Advance to Go", "Speeding fine 15", "Your building loan matures - receive 150",
"Go to Jail, Move Directly to Jail, Do not pass Go, do not collect 200", "Get out of jail free", "Advance to Mayfair",
"Advance to Pall Mall. If you pass Go, collect 200", "Advance to Trafalgar Square. If you pass Go collect 200",
"Go back 3 spaces", "Take a trip to Marylebone Station and if you pass Go collect 200", "Pay school fees of 150" };

static_assert(std::size(cNames) == CEnd + 1, "Missing card name");

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
181
182
#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 {
	static const unsigned fine { 50 };     // Fine to pay for going to jail

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

cards.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
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
#include <iostream>

#include "cards.h"
#include "places.h"
#include "board.h"

cCards::cCards(Cards st, Cards en) {
	for (auto c { st }; c <= en; c = Cards(c + 1))
		cards.push_front(c);

	std::ranges::shuffle(cards, rng);
}

// Get top card and return to bottom if not get out of jail
Cards cCards::turnover(Players& pls) {
	const auto c { cards.front() };
	auto& p { pls.getCurr() };          // Player

	cards.pop_front();

	if (c == CCGetOut)
		p.hasCCGetOut = true;
	else if (c == CGetOut)
		p.hasCGetOut = true;
	else
		returnCard(c);

	return c;
}

// Return a card to bottom of the pack
void cCards::returnCard(Cards c) {
	cards.push_back(c);
}

// Process card
void cProcess(Cards c, Players& pls) {
	auto [p, board, plc] {getPBP(pls)};

	const auto pay { [&](int amt) {
			if (p.amount >= amt)
				p.amount -= amt;
			else
				pls.bankcrupt();
	} };

	const auto advanceTo { [&](Place pl) {
		if (p.position > pl) {
				std::cout << "Passed ";
				board.getPlace(Go).getPlaceC()->play(pls);
			}

			pls.moveTo(pl);
	} };

	const auto choose { [&] {
		Dice dd;

		// 11 totals possible. 2 - 6 false, 8 - 12 true, 7 shake again
		do dd = board.shake();
		while (dd.total == 7);

		return dd.total >= 8;
	 } };

	std::cout << '\n' << cNames[c] << '\n';

	switch (c) {
		case CCHospital:
			pay(100);
			break;

		case CCBankError:
			p.amount += 200;
			break;

		case CCInsurance:
		case CCDoctorFee:
			pay(50);
			break;

		case CCTaxRefund:
			p.amount += 20;
			break;

		case CCAdvanceGo:
		case cAdvanceGo:
			pls.moveTo(Go);
			break;

		case CCGoJail:
		case CGoJail:
			pls.moveTo(GoToJail);
			break;

		case CBankDiv:
		case CCSale:
			p.amount += 50;
			break;

		case CCrossword:
		case CCAnnunity:
		case CCInherit:
			p.amount += 100;
			break;

		case CCBirthday:
			pls.adjust(10);
			break;

		case CCBeauty:
			p.amount += 10;
			break;

		case CCShares:
			p.amount += 25;
			break;

		case CCOldKentRoad:
			pls.moveTo(OldKentRoad);
			break;

		case CCCChance:
			if (p.amount <= 10 || choose()) {
				std::cout << " Taking a chance ";
				cProcess(pls.getBoard().getChance().turnover(pls), pls);
			} else {
				std::cout << " fine paid ";
				p.amount -= 10;
			}

			break;

		case CDrunk:
			pay(20);
			break;

		case CSpeeding:
			pay(15);
			break;

		case CLoan:
			p.amount += 150;
			break;

		case CSchool:
			pay(150);
			break;

		case CGoBack3:
			// For chance, can never go back beyond Go
			pls.moveTo(Place(p.position - 3));
			break;

		case CMarylebone:
			advanceTo(MaryleboneStation);
			break;

		case CTrafalgar:
			advanceTo(TrafalgarSquare);
			break;

		case CPallMall:
			advanceTo(PallMall);
			break;

		case CMayfair:
			pls.moveTo(Mayfair);
			break;

		default:
			std::cout << "Not yet implemented\n";
			break;
	}
}


monopoly.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
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>

#include "board.h"
#include "player.h"
#include "cards.h"

void process(Players& pls) {
	auto [p, board, plc] {getPBP(pls)};

	unsigned doubles {};

	if (p.inPlay) {
		bool again {};

		std::cout << sNames[p.symbol] << '\n';
		plc.getPlaceC()->show(pls);

		do {
			std::cout << "\nHas " << p.amount;
			p.dice = board.shake();
			doubles += p.dice.isdouble;
			again = p.inPlay && p.dice.isdouble;    // Note that inPlay can change within the loop!
			std::cout << "\nThrew " << p.dice << '\n';

			if (doubles < 3)
				pls.advance(p.dice.total);
			else {
				std::cout << "3 doubles thrown ";
				again = false;
				pls.moveTo(Jail);
			}
		} while (again && (std::cout << "Double thrown - play again"));
	}
}

int main() {
	Board board;

	if (board.size() != 40)
		return (std::cout << "Incorrect board size - " << board.size() << '\n'), 1;

	Players players(board);

	//board.display();

	const auto start { players.getCurr().symbol };

	for (unsigned round { 1 }, prev { }; players.noPlayers() > 1; round += players.getCurr().symbol == start) {
		if (round != prev) {
			char inp {'y'};

			prev = round;
			//std::cout << "Next round (d/y/n): ";
			//std::cin >> inp;

			if (inp == 'd')
				board.display();
			else if (inp != 'y')
				break;

			std::cout << "\nRound " << round << "\n\n";
		}

		process(players);
		players.next();
	}

	players.details();
	std::cout << '\n';
}

places.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
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#include <algorithm>

#include "places.h"
#include "board.h"

void cPlace::show(Players& pls) {
	auto [p, board, plc] {getPBP(pls)};

	std::cout << "Located on " << pNames[plc.getPlace()];
}

void cGo::play(Players& pls) {
	pls.getCurr().amount += collect;
	std::cout << "Go - collect " << collect << '\n';
}

bool cProp::allOwned(const Places& pl) const noexcept {
	return std::ranges::all_of(pl.getAssociated(), [&](auto p) {
		return pl.getBoard()->getPlace(p).getOwner() == pl.getOwner();
		});
}

void cProp::play(Players& pls) {
	show(pls);
	std::cout << '\n';

	auto [p, board, plc] {getPBP(pls)};
	const auto allown { allOwned(plc) };

	if (const auto own { plc.getOwner() }; own == None) {
		// Deal with buy here
		std::cout << "Not owned - ";

		if (p.amount >= int(buy)) {
			std::cout << "Bought for " << buy << '\n';
			plc.setOwner(p.symbol);
			p.amount -= buy;
		} else
			std::cout << "Not enough money to buy!\n";
	} else if (own != p.symbol) {
		// Deal with rent here if not owned
		const auto r { hasHotel ? hotel : rent[houses] * (1 + (allown && houses == 0))};    // Rent is doubled if all owned and no properties

		std::cout << "Rent due is " << r << " - ";

		if (p.amount >= int(r)) {
			std::cout << "Rent paid\n";
			p.amount -= r;
		} else
			pls.bankcrupt();
	} else {
		// Deal with buying house/hotel - NOTE allow uneven build
		if (allown)
			if (!hasHotel && houses < 5) {
	     		// Buy a house if possible
				if (p.amount >= int(house)) {
					p.amount -= house;
					++houses;
					std::cout << "House bought for " << house << '\n';
				} else
					std::cout << "Not enough money to buy!\n";
			} else if (!hasHotel)
				// Buy hotel if possible
				if (p.amount >= int(hotel)) {
					p.amount -= hotel;
					houses = 0;
					hasHotel = true;
				} else
					std::cout << "Not enough money to buy!\n";
	}
}

void cProp::display(Places& pl) const noexcept {
	std::cout << pNames[pl.getPlace()];

	if (const auto own { pl.getOwner() }; own == None)
		std::cout << " Not owned";
	else {
		std::cout << " Owned by " << sNames[own];

		if (houses > 0)
			std::cout << ' ' << houses << " houses";
		else
			if (hasHotel)
				std::cout << " Hotel";
	}
}

void cComm::play(Players& pls) {
	show(pls);
	cProcess(pls.getBoard().getComm().turnover(pls), pls);
}

void cTax::play(Players& pls) {
	show(pls);

	auto& p { pls.getCurr() };

	if (p.amount >= int(tax)) {
		p.amount -= tax;
		std::cout << '\n' << tax << " Tax paid\n";
	} else
		pls.bankcrupt();
}

void cTax::display(Places& pl) const noexcept {
	std::cout << pNames[pl.getPlace()] << " " << tax;
}

unsigned cStation::manyOwned(const Places& pl) const noexcept {
	return 1 + unsigned(std::ranges::count_if(pl.getAssociated(), [&](auto p) {
		return pl.getBoard()->getPlace(p).getOwner() == pl.getOwner();
		}));
}

void cStation::play(Players& pls) {
	show(pls);
	std::cout << '\n';

	auto [p, board, plc] {getPBP(pls)};

	if (const auto own { plc.getOwner() }; own == None) {
		// Deal with buy here
		std::cout << "Not owned - ";

		if (p.amount >= int(buy)) {
			std::cout << "Bought for " << buy << '\n';
			plc.setOwner(p.symbol);
			p.amount -= buy;
		} else
			std::cout << "Not enough money to buy!\n";
	} else if (own != p.symbol) {
		// Deal with rent here if not owned
		const auto r { rent[manyOwned(plc) - 1] };

		std::cout << "Rent due is " << r << " - ";

		if (p.amount >= int(r)) {
			std::cout << "Rent paid\n";
			p.amount -= r;
		} else
			pls.bankcrupt();
	}
}

void cStation::display(Places& pl) const noexcept {
	std::cout << pNames[pl.getPlace()];

	if (const auto own { pl.getOwner() }; own == None)
		std::cout << " Not owned";
	else
		std::cout << " Owned by " << sNames[own];
}

void cChance::play(Players& pls) {
	show(pls);
	cProcess(pls.getBoard().getChance().turnover(pls), pls);
}

void cJail::play(Players& pl) {
	show(pl);
	std::cout << " - Just visiting!\n";
}

unsigned cUtility::manyOwned(const Places& pl) const noexcept {
	return 1 + unsigned(std::ranges::count_if(pl.getAssociated(), [&](auto p) {
		return pl.getBoard()->getPlace(p).getOwner() == pl.getOwner();
		}));
}

void cUtility::play(Players& pls) {
	show(pls);
	std::cout << '\n';

	auto [p, board, plc] {getPBP(pls)};

	if (const auto own { plc.getOwner() }; own == None) {
		// Deal with buy here
		std::cout << "Not owned - ";

		if (p.amount >= int(buy)) {
			std::cout << "Bought for " << buy << '\n';
			plc.setOwner(p.symbol);
			p.amount -= buy;
		} else
			std::cout << "Not enough money to buy!\n";
	} else if (own != p.symbol) {
		// Deal with rent here if not owned
		const auto r { rent[manyOwned(plc) - 1] * p.dice.total};

		std::cout << "Rent due is " << r << " - ";

		if (p.amount >= int(r)) {
			std::cout << "Rent paid\n";
			p.amount -= r;
		} else
			pls.bankcrupt();
	}
}

void cUtility::display(Places& pl) const noexcept{
	std::cout << pNames[pl.getPlace()];

	if (const auto own { pl.getOwner() }; own == None)
		std::cout << " Not owned";
	else
		std::cout << " Owned by " << sNames[own];
}

void cParking::play(Players& pl) {
	show(pl);
	std::cout << " Lucky - nothing to do!\n";
}

void cGoJail::play(Players& pls) {
	show(pls);
	std::cout << "\nGo to Jail, Go directly to Jail, Do not collect 200\n";

	auto [p, board, plc] {getPBP(pls)};

	if (p.hasCCGetOut) {
		std::cout << "\nCommunity Chest get Out Of Jail Free Card used\n";
		p.hasCCGetOut = false;
		board.getComm().returnCard(CCGetOut);
	} else if (p.hasCGetOut) {
		std::cout << "\nChance get Out Of Jail Free Card used\n";
		p.hasCGetOut = false;
		board.getChance().returnCard(CGetOut);
	} else
		if (p.amount >= fine) {
			std::cout << "Jail fine of " << fine << " paid\n";
			p.amount -= fine;
		} else
			pls.bankcrupt();

	if (p.inPlay)
		pls.moveTo(Jail);
}

player.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
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
#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>

#include "board.h"

// Obtain number of players and generate players vector accordingly
Players::Players(Board& b) : brd(b) {
	unsigned noPlay {};
	Player pt;

	for (unsigned t { 1 }; t <= MaxPlayer; ++t) {
		pt.symbol = Symbols(t);
		pt.dice = b.shake();
		players.push_back(pt);
	}

	std::ranges::shuffle(players, rng);

	do {
		std::cerr << "How many players (2 - " << MaxPlayer << "): ";
		std::cin >> noPlay;
		std::cin.clear();
		std::cin.ignore(1000, '\n');
	} while ((noPlay < 2 || noPlay > MaxPlayer) && (std::cerr << "Invalid number of players\n"));

	players.resize(noPlay);

	// total symbol
	std::vector < std::pair<unsigned, Symbols>> highest;

	unsigned high {};

	for (unsigned play { }; auto& p : players) {
		brd.getPlace(Go).getPlayers().addPlayer(p.symbol);

		std::cout << "Player " << ++play << " is a " << sNames[p.symbol] << " and threw " << p.dice << '\n';

		if (p.dice.total > high) {
			highest.clear();
			highest.emplace_back(p.dice.total, p.symbol);
			high = p.dice.total;
		} else if (p.dice.total == high)
			highest.emplace_back(p.dice.total, p.symbol);
	}

	std::cout << "\nThe highest score is " << high << " for player";

	if (highest.size() > 1) {
		std::cout << "s:\n";

		for (const auto& h : highest)
			std::cout << sNames[h.second] << '\n';
	} else
		std::cout << ' ' << sNames[highest.front().second];

	std::cout << '\n';

	while (highest.size() > 1) {
		std::cout << highest.size() << " players tied for first\nTie breaker for first\n";

		for (auto& h : highest) {
			const auto t { b.shake() };

			h.first = t.total;
			std::cout << sNames[h.second] << " threw " << t << '\n';
		}

		std::sort(highest.begin(), highest.end(), [](auto p1, auto p2) {return p1.first > p2.first; });
		std::erase_if(highest, [&highest](auto p1) {return p1.first < highest.front().first; });
	}

	curr = unsigned(std::find_if(players.begin(), players.end(), [&highest](auto p) {return p.symbol == highest.front().second; }) - players.begin());
	std::cout << "\nPlay starts with " << sNames[players[curr].symbol] << "\n\n";
}

// Returns current player
Player& Players::getCurr() noexcept {
	return players[curr];
}

// Advances to next player
Player& Players::next() noexcept {
	if (++curr >= players.size())
		curr = 0;

	return getCurr();
}

// Advance position on the board
void Players::advance(unsigned no) noexcept {
	auto& p {getCurr() };      // Player

	auto newplc { p.position + no };

	if (newplc > brd.size()) {
		std::cout << "Passed ";
		brd.getPlace(Go).getPlaceC()->play(*this);
	}

	newplc %= brd.size();
	moveTo(Place(newplc));
	std::cout << '\n';
}

// Number of current players
unsigned Players::noPlayers() const noexcept {
	return unsigned(std::ranges::count_if(players, [](auto p) {return p.inPlay; }));
}

// Show details for all players that started
void Players::details() const noexcept {
	for (const auto& p : players) {
		std::cout << sNames[p.symbol] << "  ";

		if (!p.inPlay)
			std::cout << "Bankrupt\n";
		else
			std::cout << p.amount << '\n';
	}
}

// Deal with player bankruptcy
void Players::bankcrupt() noexcept {
	auto [p, plc] {getPP(brd, *this)};

	std::cout << "Cannot afford to pay - Bankrupt!\n";
	p.inPlay = false;
	plc.getPlayers().removePlayer(p.symbol);
	brd.release(p.symbol);
}

// Move player from current to specified place
void Players::moveTo(Place newplc) noexcept {
	auto [p, plc] {getPP(brd, *this)};

	plc.getPlayers().removePlayer(p.symbol);
	p.position = newplc;

	auto& plc1 { brd.getPlace(p.position) };

	plc1.getPlayers().addPlayer(p.symbol);
	plc1.getPlaceC()->play(*this);
}

// Reduce every in play players total by specified amount - except current
// For Community Charge Birthday
void Players::adjust(int decamnt) noexcept {
	const auto c { curr };

	for (unsigned pl {}; pl < players.size(); ++pl) {
		auto& p { players[pl] };

		if (p.inPlay && (pl != c))
			if (p.amount >= decamnt) {
				p.amount -= decamnt;
				players[c].amount += decamnt;
			} else {
				curr = pl;
				bankcrupt();
			}
	}

	curr = c;
}

Topic archived. No new replies allowed.
Pages: 123