16 using Clock = std::chrono::steady_clock;
23 struct EconomicRuntimeState
66 return static_cast<int>(
economicRuntime.hasPendingControlAction[player]) +
78 ((
tradeIngress.pendingTradeAcceptPlayers & (1u << player)) != 0) +
116 const auto bit = 1u << player;
129 if (purchasingPlayer == player)
188 message.numberA >= 0 &&
200 message.numberC > 0 ? message.numberC : 0;
207 message.numberC > 0 ? message.numberC : 0;
227 message.numberD >= 3)
240 profile::ConfigContext context{};
244 context.localAIPlayer[player] =
248 context.purchasingPlayer = purchasingPlayer;
249 context.purchasingProperty = purchasingProperty;
254 const rules::GameState&
state,
255 const actions::Message& message)
noexcept
259 if (message.numberA >= 0 &&
270 message.numberA < 0 ||
282 const auto& playerState =
state.players[player];
287 playerState.aiPlayerLevel);
300 const actions::Message& message)
noexcept
304 message.numberB >= 0 &&
314 message.numberA ==
static_cast<std::int64_t
>(purchasingProperty))
322 const rules::GameState&
state,
323 const actions::Message& message)
noexcept
330 const auto completed =
341 if (message.numberA <= 0)
343 const auto requested =
static_cast<std::uint32_t
>(message.numberA);
370 if (player >=
state.numberOfPlayers ||
377 std::span<const std::uint8_t>(message.binaryDataA),
388 if (message.numberB <= 0)
391 static_cast<std::uint32_t
>(message.numberB);
393 if (message.numberC < 1)
403 const auto bit = 1u << player;
421 message.numberA >= 0 &&
426 if (player <
state.numberOfPlayers &&
453 message.numberA ==
static_cast<std::int64_t
>(
465 const rules::GameState&
state,
466 const actions::Message& message)
noexcept
472 context.localAIPlayer[player] = context.localAIPlayer[player] &&
507 context.localAIPlayer[player] = context.localAIPlayer[player] &&
519 const profile::ConfigContext& context)
noexcept
521 trade::TradeAcceptanceConfig
config{};
525 config.numberTimesAllowPropertyTrade =
526 strategy.numberTimesAllowPropertyTrade;
534 return static_cast<double>(std::rand()) /
540 std::array<std::uint32_t, 8>
keys{};
547 const rules::GameState&)
noexcept
582 const decision::EconomicActionPlan&
plan)
noexcept
600 const rules::GameState&
state,
602 const decision::EconomicActionPlan&
plan)
noexcept
632 struct EconomicDecision
634 decision::EconomicActionPlan
plan{};
639 const rules::GameState&
state,
641 const profile::ConfigContext& context)
noexcept
643 EconomicDecision result{};
650 strategy.housingPurchaseStrategy, context.moneyOwed[player]);
660 strategy.housingPurchaseStrategy, context.moneyOwed[player]);
661 if (result.plan.acted())
667 context.moneyOwed[player]))
671 context.moneyOwed[player]);
691 const rules::GameState&
state,
694 if (player >=
state.numberOfPlayers ||
709 if (
next.defer || !
next.plan.acted())
717 const rules::GameState&
state,
718 const actions::Message& message)
noexcept
724 const auto completed =
static_cast<actions::Type>(message.numberA);
728 if (message.numberB == 0)
742 const auto owner = buySellMortgagePlayer(
state);
744 owner <
state.numberOfPlayers &&
772 if (message.numberA < 0 || message.numberA >=
state.numberOfPlayers)
777 if (buySellMortgagePlayer(
state) != player ||
802 const rules::GameState&
state,
803 const actions::Message& message)
noexcept
809 const auto completed =
static_cast<actions::Type>(message.numberA);
826 message.numberA < 0 || message.numberA >=
state.numberOfPlayers ||
827 message.numberB == 0)
840 context.moneyOwed[player]))
844 context.moneyOwed[player]);
861 const rules::GameState&
state,
862 const actions::Message& message)
noexcept
872 message.numberA < 0 || message.numberA >=
state.numberOfPlayers ||
873 message.numberB < 0 ||
886 strategy.minCashOnHand, context.moneyOwed);
894 const rules::GameState&
state,
895 const actions::Message& message)
noexcept
914 if (message.numberB < 0 || message.numberC < 0 ||
919 message.numberA <
state.numberOfPlayers
924 (
static_cast<unsigned long long>(std::rand()) *
state.numberOfPlayers) /
925 (
static_cast<unsigned long long>(
RAND_MAX) + 1ull));
933 state.players[player].currentSquare ==
static_cast<std::uint8_t
>(
938 decision::AuctionBidConfig
config{};
941 config.housingPurchaseStrategy =
strategy.housingPurchaseStrategy;
945 (std::rand() % 2) != 0);
959 const rules::GameState&
state,
960 const actions::Message& message)
noexcept
966 const auto completed =
static_cast<actions::Type>(message.numberA);
975 if (message.numberA < 0 || message.numberA >=
state.numberOfPlayers ||
976 message.numberC <= 0)
984 const auto building = message.numberB > 0
991 context.moneyOwed[player]);
994 static_cast<std::int64_t
>(*square)))
999 message.numberD >= 3 || message.numberE <= 0)
1002 const auto allowed =
static_cast<std::uint32_t
>(message.numberE);
1004 message.numberA <
state.numberOfPlayers
1007 const auto building = message.numberC > 0
1021 strategy.housingPurchaseStrategy, context.moneyOwed[player]) ==
1026 strategy.minCashOnHand, 0,
false, context.moneyOwed[player]);
1038 const rules::GameState&
state,
1039 const actions::Message& message)
noexcept
1049 message.numberA < 0 || message.numberA >=
state.numberOfPlayers)
1069 const rules::GameState&
state,
1070 const actions::Message& message)
noexcept
1080 message.numberA < 0 || message.numberA >=
state.numberOfPlayers)
1090 message.numberB != 0,
1091 message.numberC != 0,
1092 message.numberD != 0);
1098 static_cast<std::int64_t
>(*choice)))
1111 state.players[player].currentSquare ==
1123 return state.options.aiTakesTimeToThink &&
1129 const rules::GameState&
state,
1130 const actions::Message& message)
noexcept
1134 !
state.options.aiTakesTimeToThink))
1147 if (
next.defer || !
next.plan.acted())
1156 std::uint8_t importance,
const profile::Profile&
strategy,
1157 const profile::ConfigContext& context)
noexcept
1159 decision::SemiImportantTradeInputs
inputs{};
1161 state, player,
false,
false, context.moneyOwed[player]);
1163 state.players[player].aiPlayerLevel != 3)
1174 inputs.deriveOfferedGroups =
true;
1175 inputs.wantedPropertyRoll =
static_cast<std::uint32_t
>(std::rand());
1176 inputs.offeredPropertyRoll =
static_cast<std::uint32_t
>(std::rand());
1177 inputs.importance = importance;
1178 inputs.minimumNonmonopolyTradeAttitude =
1179 strategy.minimumNonmonopolyTradeAttitude;
1183 struct SemiImportantPartnerOrder
1185 std::array<rules::PlayerNumber, rules::MaxPlayers>
players{};
1194 SemiImportantPartnerOrder result{};
1195 std::array<rules::PlayerNumber, rules::MaxPlayers>
candidates{};
1213 std::size_t start{};
1223 result.players[result.count++] =
1232 const profile::ConfigContext& context,
1234 const decision::MonopolyProposalConfig&
config,
1243 attempt.excludedPlayer = excludedPlayer;
1262 const profile::ConfigContext& context)
noexcept
1264 if (player >=
state.numberOfPlayers || !context.localAIPlayer[player] ||
1271 strategy.maxHousesPerSquareForGiveAway, context.moneyOwed);
1273 trade::TradeCadenceInputs
cadence{};
1276 cadence.buySellMortgagePlayer = buySellMortgagePlayer(
state);
1278 cadence.giveAwayProbability =
strategy.proposeMonopolyGiveAwayProbability;
1290 cadence.shouldGiveAwayMonopoly =
false;
1306 decision::ProactiveTradeInputs
inputs{};
1309 state, player,
false,
false, context.moneyOwed[player]);
1310 if (
state.players[player].aiPlayerLevel == 3)
1342 const decision::EconomicActionPlan&
plan)
noexcept
1358 static_cast<std::int64_t
>(
plan.
square), 0, 0, 1))
1367 const rules::GameState&
state,
1370 std::uint8_t importance,
1371 const profile::ConfigContext& context)
noexcept
1373 if (player >=
state.numberOfPlayers ||
1375 state.tradeInProgress ||
1383 trade::TradeCadenceInputs
cadence{};
1384 cadence.buySellMortgagePlayer = buySellMortgagePlayer(
state);
1386 state, player, importance,
1394 owner <
state.numberOfPlayers; ++owner)
1397 decision::ProactiveTradeInputs
inputs{};
1398 inputs.importance = importance;
1401 state, player,
false,
false, context.moneyOwed[player]);
1402 if (
state.players[player].aiPlayerLevel == 3)
1433 const rules::GameState&
state,
1434 const actions::Message& message)
noexcept
1449 const auto completed =
static_cast<actions::Type>(message.numberA);
1484 if (player >=
state.numberOfPlayers ||
1487 state.tradeInProgress ||
1492 debtMoneyOwed[player] = message.numberC > 0 ? message.numberC : 0;
1495 if (message.numberB < 0 ||
1504 context.localAIPlayer[current] = context.localAIPlayer[current] &&
1508 state, player,
false);
1521 state, player,
true);
1540 const rules::GameState&
state,
const actions::Message& message)
noexcept
1544 !
state.options.aiTakesTimeToThink))
1548 context.localAIPlayer[current] = context.localAIPlayer[current] &&
1566 if (pending == PendingTurnPrompt::None)
1568 const auto action = pending == PendingTurnPrompt::RollDice
1580 if (player >=
state.numberOfPlayers ||
1597 context.localAIPlayer[current] = context.localAIPlayer[current] &&
1607 if (!
next.defer &&
next.plan.acted())
1618 const rules::GameState&
state,
const actions::Message& message)
noexcept
1623 const auto completed =
static_cast<actions::Type>(message.numberA);
1635 if (message.numberA < 0 || message.numberA >=
state.numberOfPlayers)
1642 ? PendingTurnPrompt::RollDice : PendingTurnPrompt::EndTurn;
1664 tradeIngress.pendingTradeAcceptPlayers |= 1u << player;
1669 const rules::GameState&
state,
1670 const actions::Message& message)
noexcept
1673 message.numberA < 0 ||
tradeIngress.tradeJustRejectedCountered)
1676 const auto requested =
static_cast<std::uint32_t
>(message.numberA);
1679 player <
state.numberOfPlayers; ++player)
1700 switch (decision.status)
1735 state, player,
true,
1736 tradeIngress.counterSessions[player].timesCounteredTrade == 0
1747 const actions::Message& message)
noexcept
1751 const auto bit = 1u << player;
1773 const rules::GameState&
state,
1774 const actions::Message& message)
noexcept
1797 if (!result.acted())
1878 const trade::TradeIngressState&
1884 const profile::RuntimeState&
std::array< decision::EconomicActionPlan, rules::MaxPlayers > pendingControlAction
bool decompositionControl
std::array< bool, rules::MaxPlayers > controlReleaseInFlight
std::array< rules::PlayerNumber, rules::MaxPlayers > players
std::array< bool, rules::MaxPlayers > controlRestartInFlight
std::array< bool, rules::MaxPlayers > actionInFlight
std::array< bool, rules::MaxPlayers > controlRequestInFlight
rules::PlayerNumber controlOwner
std::array< bool, rules::MaxPlayers > hasPendingControlAction
decision::EconomicActionPlan plan
std::filesystem::path directory
@ NotifyTradeAcceptanceDecision
@ NotifyBuyOrAuctionDecision
@ NotifyFlatOrFractionTaxDecision
@ NotifyAINeedParametersForSave
@ NotifyProposedConfiguration
@ NotifyPlayerBuySellMort
EconomicActionPlan planBuyHouseAction(const rules::GameState &state, rules::PlayerNumber player, CashStrategy strategy, std::int64_t minCashOnHand, std::uint8_t housingPurchaseStrategy, std::int64_t moneyOwed) noexcept
EconomicActionPlan planDebtLiquidationStep(const rules::GameState &state, rules::PlayerNumber player, bool sellHouses) noexcept
bool shouldGiveAwayMonopoly(const rules::GameState &state, rules::PlayerNumber player, CashStrategy strategy, std::int64_t minCashOnHand, int maxHousesPerSquareForGiveAway, std::span< const std::int64_t > moneyOwed) noexcept
bool buildSemiImportantTrade(const rules::GameState &state, rules::PlayerNumber player, std::span< const double > playerAttitudes, const ai::trade::PropertySets &properties, const SemiImportantTradeInputs &inputs, ai::trade::TradeProposalList &proposals, const MonopolyProposalConfig &config) noexcept
HousePurchaseDecision shouldBuyHouse(const rules::GameState &state, rules::PlayerNumber player, CashStrategy strategy, std::int64_t minCashOnHand, std::uint8_t housingPurchaseStrategy, std::int64_t moneyOwed) noexcept
bool shouldBuyProperty(const rules::GameState &state, rules::PlayerNumber player, rules::board::SquareType property, CashStrategy strategy, std::int64_t minCashOnHand, std::span< const std::int64_t > moneyOwed) noexcept
std::optional< rules::board::SquareType > chooseHousingAuctionSquare(const rules::GameState &state, rules::PlayerNumber player, HousingAuctionBuilding building, CashStrategy strategy, std::int64_t minCashOnHand, rules::board::PropertySet legalSquares, bool reservedBuilding, std::int64_t moneyOwed) noexcept
EconomicActionPlan planUnmortgagePropertyAction(const rules::GameState &state, rules::PlayerNumber player, CashStrategy strategy, std::int64_t minCashOnHand, std::int64_t moneyOwed) noexcept
std::int64_t bidForAuctionItem(const rules::GameState &state, rules::PlayerNumber player, rules::board::SquareType item, std::int64_t currentBid, rules::PlayerNumber currentBidder, const AuctionBidConfig &config, bool useTenDollarIncrement) noexcept
std::optional< bool > chooseFractionTax(const rules::GameState &state, rules::PlayerNumber player) noexcept
EconomicActionPlan planHouseSaleAction(const rules::GameState &state, rules::board::SquareType referenceSquare) noexcept
ProactiveTradeResult buildProactiveTrade(const rules::GameState &state, rules::PlayerNumber player, std::span< const double > playerAttitudes, const ai::trade::PropertySets &properties, const ProactiveTradeInputs &inputs, const MonopolyProposalConfig &config) noexcept
bool shouldUnmortgageProperty(const rules::GameState &state, rules::PlayerNumber player, CashStrategy strategy, std::int64_t minCashOnHand, std::int64_t moneyOwed) noexcept
std::optional< JailExitChoice > chooseJailExitChoice(const rules::GameState &state, rules::PlayerNumber player, bool canRollDoubles, bool canPayFee, bool canUseCard) noexcept
std::expected< void, Error > initializeRuntime(RuntimeState &state, const std::filesystem::path &directory)
decision::CounterProposalBalanceConfig makeCounterBalanceConfig(const ProfileSet &profiles, rules::PlayerNumber strategyPlayer, const ConfigContext &context) noexcept
void clearPlayerRuntime(RuntimeState &state, rules::PlayerNumber player) noexcept
std::expected< void, Error > loadPlayerRuntime(RuntimeState &state, rules::PlayerNumber player, std::uint8_t token, std::uint8_t level)
decision::MonopolyProposalConfig makeMonopolyProposalConfig(const ProfileSet &profiles, rules::PlayerNumber strategyPlayer, const ConfigContext &context) noexcept
decision::TradeEvaluationConfig makeTradeEvaluationConfig(const ProfileSet &profiles, rules::PlayerNumber strategyPlayer, const ConfigContext &context) noexcept
decision::CounterProposalPreflightConfig makeCounterPreflightConfig(const ProfileSet &profiles, rules::PlayerNumber strategyPlayer, const ConfigContext &context) noexcept
bool encode(const State &state, std::vector< std::uint8_t > &result) noexcept
bool decode(std::span< const std::uint8_t > data, State &result) noexcept
constexpr std::uint8_t TradeForCash
bool tradeIsProper(const rules::GameState &state, const TradeProposalList &proposals, std::span< const FutureImmunityRecord > immunities) noexcept
void resetTradeIngress(TradeIngressState &state) noexcept
bool playerInvolvedInTrade(const TradeProposalRecord &proposal) noexcept
constexpr std::uint8_t TradeSomewhatImportant
std::array< rules::board::SquareGroup, 8 > orderMonopolyImportance(std::int64_t liquidAssets, MonopolySortOrder order, std::span< const std::uint32_t > randomKeys) noexcept
std::array< double, rules::MaxPlayers > tradeResponseAttitudeChanges(const rules::GameState &gameState, rules::PlayerNumber proposer, rules::PlayerNumber respondingPlayer, bool accepted, const TradeIngressState &state, const profile::ProfileSet &profiles, const profile::ConfigContext &context) noexcept
constexpr std::uint8_t TradeDesperate
bool sendProactiveTrade(const rules::GameState &gameState, rules::PlayerNumber player, const TradeProposalList &proposal, const profile::Profile &strategy, TradeIngressState &state) noexcept
bool shouldTrade(const rules::GameState &state, rules::PlayerNumber player, std::uint8_t importance, std::span< const std::int64_t > timeLastTrade, std::size_t maxTrades, const TradeCadenceInputs &inputs) noexcept
std::array< rules::board::PropertySet, rules::MaxPlayers > PropertySets
void advanceTradeTurn(std::uint8_t numberOfPlayers, profile::ProfileSet &profiles, const std::array< bool, rules::MaxPlayers > &localAIPlayers, TradeIngressState &state) noexcept
CounterTradeRunResult counterProposeCurrentTrade(const rules::GameState &gameState, rules::PlayerNumber player, bool tradeAccept, double counterRoll, int pendingActions, bool auctionOn, const decision::CounterProposalPreflightConfig &preflightConfig, const decision::CounterProposalBalanceConfig &balanceConfig, TradeIngressState &state) noexcept
std::array< TradeProposalRecord, rules::MaxPlayers > TradeProposalList
TradeAcceptanceResult evaluateCurrentTradeAcceptance(const rules::GameState &gameState, rules::PlayerNumber player, const TradeAcceptanceConfig &config, const TradeIngressState &state) noexcept
bool processTradeRuleMessage(const rules::GameState &gameState, const actions::Message &message, TradeIngressState &state) noexcept
const profile::RuntimeState & profileRuntimeStateReadOnly() noexcept
std::expected< void, profile::Error > initializeMessageIngressProfiles(const std::filesystem::path &directory)
std::int64_t liquidAssets(const rules::GameState &state, rules::PlayerNumber player, bool countHouses, bool countMonopolies, std::int64_t moneyOwed) noexcept
const trade::TradeIngressState & tradeIngressStateReadOnly() noexcept
void processMessage(const rules::GameState &state, const actions::Message &message) noexcept
void resetMessageIngress() noexcept
rules::board::PropertySet propertiesOwnedByPlayer(const rules::GameState &state, rules::PlayerNumber player) noexcept
constexpr std::int64_t ErrorTradeAccepted
constexpr std::int64_t ErrorTradeChanging
constexpr std::int64_t ErrorTradeRejected
bool sendAction(const actions::Message &message)
std::uint32_t PropertySet
bool acceptedConfigurationMessage(const GameOptions &options, PlayerNumber fromPlayer, bool interim, actions::Message &result)
const PendingPhase & current(const GameState &state)
void restart(GameState &state)
constexpr PlayerNumber BankPlayer
std::uint8_t PlayerNumber
constexpr PlayerNumber MaxPlayers
constexpr PlayerNumber NobodyPlayer
bool requestRemoveLocalPlayer(const GameState &uiState, PlayerNumber player)
bool slotIsLocalAIPlayer(PlayerNumber slot)
bool isLocalRecipient(PlayerNumber recipient)
bool acted() const noexcept
rules::board::SquareType square