Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
StatsCalculatorLogic.cpp
Go to the documentation of this file.
2
3#include "AIUtility.hpp"
4
5#include <algorithm>
6#include <array>
7#include <cmath>
8
9namespace monopoly::statsui
10{
11 namespace
12 {
13 inline constexpr int MovementSquareCount = 40;
14
15 [[nodiscard]] constexpr int wrapSquare(int square) noexcept
16 {
17 while (square >= MovementSquareCount) square -= MovementSquareCount;
18 while (square < 0) square += MovementSquareCount;
19 return square;
20 }
21
22 [[nodiscard]] bool validPlayer(
23 const rules::GameState& state, rules::PlayerNumber player) noexcept
24 {
25 return player < state.numberOfPlayers && player < rules::MaxPlayers;
26 }
27
28 [[nodiscard]] bool validDeed(int deed) noexcept
29 {
30 return deed >= 0 && deed < MovementSquareCount;
31 }
32 [[nodiscard]] std::int64_t rentForOwnerAt(
33 const rules::GameState& state, int square) noexcept
34 {
35 const auto owner = state.squares[static_cast<std::size_t>(square)].owner;
36 if (!validPlayer(state, owner)) return 0;
38 static_cast<rules::board::SquareType>(square),
40 }
41
42 [[nodiscard]] CalculatorResult money(double value) noexcept
43 {
45 }
46
47 [[nodiscard]] CalculatorResult percentage(double value) noexcept
48 {
50 }
51
52 [[nodiscard]] double odds(
53 const rules::GameState& state, int deed) noexcept
54 {
55 int delta = deed - static_cast<int>(
56 state.players[state.currentPlayer].currentSquare);
58 if (delta > 12 || delta < 2) delta = 1;
59 return ((6.0 - std::abs(delta - 7)) / 36.0) * 100.0;
60 }
61 [[nodiscard]] std::int64_t futureValue(
62 const rules::GameState& state, rules::PlayerNumber valuePlayer,
63 int deed, bool weighted) noexcept
64 {
65 if (!validPlayer(state, valuePlayer) || !validPlayer(state, state.currentPlayer))
66 return 0;
67 auto nextPlayer = static_cast<rules::PlayerNumber>(state.currentPlayer + 1);
68 if (nextPlayer >= state.numberOfPlayers) nextPlayer = 0;
69 int square = static_cast<int>(state.players[nextPlayer].currentSquare) + 2;
70 for (int i = 0; i < 11; ++i)
71 {
72 square = wrapSquare(square + 1);
73 if (square != deed) continue;
74 const auto rent = ai::rentIfSteppedOn(state,
75 static_cast<rules::board::SquareType>(deed),
77 if (!weighted) return rent;
78 return static_cast<std::int64_t>(
79 static_cast<double>(rent) *
81 }
82 return 0;
83 }
84
85 [[nodiscard]] std::int64_t maximumExpense(
86 const rules::GameState& state, rules::PlayerNumber player,
87 int turns) noexcept
88 {
89 if (!validPlayer(state, player) || !validPlayer(state, state.currentPlayer)) return 0;
90 auto nextPlayer = static_cast<rules::PlayerNumber>(state.currentPlayer + 1);
91 if (nextPlayer >= state.numberOfPlayers) nextPlayer = 0;
92 if (nextPlayer == player) return 0;
93 int square = static_cast<int>(state.players[player].currentSquare) + 2;
94 std::int64_t total = 0;
95 for (int turn = 0; turn < std::max(turns, 0); ++turn)
96 {
97 std::int64_t maximum = 0;
98 for (int step = 0; step < 11; ++step)
99 {
100 square = wrapSquare(square);
101 const auto owner = state.squares[static_cast<std::size_t>(square)].owner;
102 if (owner < rules::BankPlayer && owner != player)
103 maximum = std::max(maximum, rentForOwnerAt(state, square));
104 ++square;
105 }
106 total += maximum;
107 }
108 return total;
109 }
110
111 [[nodiscard]] std::int64_t maximumIncome(
112 const rules::GameState& state, rules::PlayerNumber player,
113 int turns) noexcept
114 {
115 if (!validPlayer(state, player) || !validPlayer(state, state.currentPlayer)) return 0;
116 std::array<int, rules::MaxPlayers> minimumSquare{};
117 std::array<int, rules::MaxPlayers> maximumSquare{};
118 for (std::size_t i = 0; i < state.numberOfPlayers && i < rules::MaxPlayers; ++i)
119 minimumSquare[i] = static_cast<int>(state.players[i].currentSquare);
120 const auto owned = ai::propertiesOwnedByPlayer(state, player);
121 std::int64_t total = 0;
122 for (int turn = 1; turn <= std::max(turns, 0); ++turn)
123 {
124 auto nextPlayer = static_cast<rules::PlayerNumber>(
125 static_cast<int>(state.currentPlayer) + turn);
126 // Retail resets to player zero rather than applying a full modulo.
127 if (nextPlayer >= state.numberOfPlayers) nextPlayer = 0;
128 if (nextPlayer == state.currentPlayer) continue;
129
134
135 int square = minimumSquare[nextPlayer];
136 std::int64_t maximum = 0;
137 for (int step = 0; step < 11; ++step)
138 {
139 square = wrapSquare(square);
140 if (state.squares[static_cast<std::size_t>(square)].owner == player)
141 maximum = std::max(maximum, ai::rentIfSteppedOn(state,
142 static_cast<rules::board::SquareType>(square), owned));
143 ++square;
144 }
145 total += maximum;
146 }
147 return total;
148 }
149 }
150
151 std::expected<CalculatorResult, std::string> calculate(
153 const CalculatorSelection& selection) noexcept
154 {
155 const auto requirePlayer = [&]()
156 -> std::expected<rules::PlayerNumber, std::string>
157 {
158 if (!validPlayer(state, selection.player))
159 return std::unexpected(
160 "UDStats calculator requires a valid player");
161 return selection.player;
162 };
163
164 const auto requireDeed = [&]()
165 -> std::expected<int, std::string>
166 {
167 if (!validDeed(selection.deed))
168 return std::unexpected(
169 "UDStats calculator requires a board deed");
170 return selection.deed;
171 };
172
173 switch (function)
174 {
176 {
177 const auto deed = requireDeed();
178 if (!deed)
179 return std::unexpected(deed.error());
180 if (!validPlayer(state, state.currentPlayer))
181 return std::unexpected(
182 "UDStats calculator has no current player");
183 return percentage(odds(state, *deed));
184 }
185
187 {
188 const auto player = requirePlayer();
189 if (!player)
190 return std::unexpected(player.error());
191 return money(static_cast<double>(
192 ai::totalWorth(state, *player)));
193 }
194
196 {
197 const auto deed = requireDeed();
198 if (!deed)
199 return std::unexpected(deed.error());
200 if (!validPlayer(state, state.currentPlayer))
201 return std::unexpected(
202 "UDStats calculator has no current player");
203 return money(static_cast<double>(futureValue(
204 state, state.currentPlayer, *deed, false)));
205 }
206
208 {
209 const auto deed = requireDeed();
210 if (!deed)
211 return std::unexpected(deed.error());
212 const auto player = requirePlayer();
213 if (!player)
214 return std::unexpected(player.error());
215 return money(static_cast<double>(futureValue(
216 state, *player, *deed, true)));
217 }
218
220 {
221 const auto player = requirePlayer();
222 if (!player)
223 return std::unexpected(player.error());
224 return money(static_cast<double>(maximumExpense(
225 state, *player, selection.turns)));
226 }
227
229 {
230 const auto player = requirePlayer();
231 if (!player)
232 return std::unexpected(player.error());
233 const auto owned = ai::propertiesOwnedByPlayer(state, *player);
234 const auto value = ai::averageRentReceived(
235 state, *player, 0, true, 1.0, owned);
236 return money(static_cast<double>(
237 static_cast<std::int64_t>(value)));
238 }
239
241 {
242 const auto player = requirePlayer();
243 if (!player)
244 return std::unexpected(player.error());
245 return money(static_cast<double>(maximumIncome(
246 state, *player, selection.turns)));
247 }
248
250 {
251 const auto player = requirePlayer();
252 if (!player)
253 return std::unexpected(player.error());
254 return money(static_cast<double>(
255 ai::potentialIncome(state, *player)));
256 }
257 }
258
259 return std::unexpected("UDStats calculator function is out of range");
260 }
261}
unsigned state
unsigned index
std::uint16_t value
Definition Timers.cpp:16
double averageRentReceived(const rules::GameState &state, rules::PlayerNumber player, std::int64_t startSquare, bool dontCountDevelopedMonopolies, double cashCowMultiplier, rules::board::PropertySet propertiesOwned) noexcept
std::int64_t potentialIncome(const rules::GameState &state, rules::PlayerNumber player) noexcept
double landingFrequency(SquareType square) noexcept
std::int64_t rentIfSteppedOn(const rules::GameState &state, SquareType square, rules::board::PropertySet propertiesOwned) noexcept
std::int64_t totalWorth(const rules::GameState &state, rules::PlayerNumber player) noexcept
rules::board::PropertySet propertiesOwnedByPlayer(const rules::GameState &state, rules::PlayerNumber player) noexcept
Definition AIUtility.cpp:49
constexpr PlayerNumber BankPlayer
Definition RuleTypes.hpp:15
std::uint8_t PlayerNumber
Definition RuleTypes.hpp:12
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14
std::expected< CalculatorResult, std::string > calculate(CalculatorFunction function, const rules::GameState &state, const CalculatorSelection &selection) noexcept