11 {{-13.4, -13.5, -13.7, -14.0, -14.5, -14.9, -15.4, -16.1, -16.9, -17.8,
12 -19.0, -20.0, -21.0, -22.0, -23.0, -24.0, -25.0, -26.0, -27.0, -28.0, -29.0}},
13 {{10.0, 8.0, 6.0, 4.0, 2.0, 0.6, 0.0, -0.6, -1.0, -1.5,
14 -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0, -10.0, -11.0, -12.0}},
15 {{0.0, 0.05, 0.1, 0.15, 0.3, 0.9, 1.5, 0.5, 0.1, -0.5,
16 -1.0, -1.5, -2.0, -2.8, -3.6, -4.4, -5.2, -6.0, -6.8, -7.6, -8.4}},
17 {{-1.0, -0.5, -0.2, 0.1, 0.4, 1.1, 1.7, 1.3, 0.9, 0.8,
18 0.7, 0.665, 0.63, 0.595, 0.560, 0.525, 0.490, 0.455, 0.42, 0.385, 0.35}},
19 {{-3.0, -2.5, -2.0, -1.5, -1.0, -0.5, -0.1, 0.3, 0.5, 0.6,
20 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.5}},
21 {{-3.3, -2.8, -2.3, -1.8, -1.3, -0.8, -0.3, 0.1, 0.3, 0.35,
22 0.39, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.35, 0.35, 0.38, 0.45}},
23 {{-5.0, -4.5, -4.0, -3.5, -3.0, -2.5, -2.0, -1.5, -1.0, -0.5,
24 0.0, 0.15, 0.22, 0.29, 0.4, 0.45, 0.52, 0.6, 0.70, 0.78, 0.85}},
25 {{-2.5, -2.0, -1.5, -1.0, -0.5, 0.0, 0.4, 0.48, 0.50, 0.49,
26 0.49, 0.49, 0.49, 0.49, 0.49, 0.49, 0.48, 0.39, 0.39, 0.37, 0.36}}
32 return static_cast<std::size_t
>(std::min<std::int64_t>(
assets, 5000) / 250);
36 std::size_t
group, std::size_t
index,
double offset, std::uint8_t aiLevel)
noexcept
47 std::array<std::size_t, 8> order{};
51 std::stable_sort(order.begin(), order.end(), [&](std::size_t
lhs, std::size_t
rhs) {
53 ? DarzinskisImportance[lhs][chart] > DarzinskisImportance[rhs][chart]
54 : DarzinskisImportance[lhs][chart] < DarzinskisImportance[rhs][chart];
60 const rules::GameState&
state,
63 const PropertyImportanceConfig&
config,
65 std::int64_t moneyOwed)
noexcept
67 PropertyImportanceResult result{};
74 state.squares[
static_cast<std::size_t
>(square)].owner == player)
78 ?
config.propertyAllowTradeImportance
80 result.importance +=
change;
81 result.monopolyImportance +=
change;
98 result.importance +=
config.propertyTwoUnownedImportance *
factor;
102 std::array<rules::PlayerNumber, rules::MaxPlayers>
candidates{};
108 state.players[current].currentSquare ==
116 result.importance +=
config.railroadImportance[
static_cast<std::size_t
>(
railroads - 1)];
120 result.importance +=
config.utilityImportance[
static_cast<std::size_t
>(
utilities - 1)];
124 for (std::size_t
rank = 0;
rank + 1 < order.size(); ++
rank)
130 if (trade.count == 0)
138 if (
vetoes >= 0 &&
static_cast<std::size_t
>(
vetoes) <
config.monopolyVetoImportance.size())
139 result.importance +=
config.monopolyVetoImportance[
static_cast<std::size_t
>(
vetoes)];
146 std::array<rules::PlayerNumber, rules::MaxPlayers>
list,
160 MonopolyTradeGroup
best{};
162 static_cast<int>(
count + 1))
187 std::array<rules::PlayerNumber, rules::MaxPlayers>
list,
196 const rules::GameState&
state,
198 std::span<const rules::PlayerNumber>
players)
noexcept
207 if (before <
static_cast<int>(
players.size()) &&
208 after >=
static_cast<int>(
players.size()))
211 before >=
static_cast<int>(
players.size()))
213 return players.size() == 1 && before != after;
217 const rules::GameState&
state,
219 std::span<const rules::PlayerNumber>
players,
221 std::size_t
nth)
noexcept
223 MonopolyTradeGroup result{};
231 std::array<rules::PlayerNumber, rules::MaxPlayers>
subset{};
242 std::span<const rules::PlayerNumber>(
subset.data(),
count)))
251 result.count =
count;
252 std::copy_n(
subset.begin(),
count, result.players.begin());
263 std::span<const std::uint32_t>
randomKeys)
noexcept
265 std::array<std::size_t, 8> indices{};
273 std::stable_sort(indices.begin(), indices.end(),
274 [&](std::size_t
lhs, std::size_t
rhs) {
275 return randomKeys[lhs] < randomKeys[rhs];
283 std::stable_sort(indices.begin(), indices.end(),
284 [&](std::size_t
lhs, std::size_t
rhs) {
286 ? DarzinskisImportance[lhs][chart] > DarzinskisImportance[rhs][chart]
287 : DarzinskisImportance[lhs][chart] < DarzinskisImportance[rhs][chart];
291 std::array<rules::board::SquareGroup, 8> result{};
300 std::span<const rules::PlayerNumber>
candidates,
307 std::array<rules::PlayerNumber, rules::MaxPlayers>
list{};
328 for (std::size_t
index = 0;
334 if (
bit == 0 || (properties &
bit) == 0 ||
337 tax +=
static_cast<std::int64_t
>(
339 state.options.taxRate / 100;
347 for (std::size_t
group = 1;
365 if (
static_cast<std::size_t
>(
group) >=
370 for (std::size_t
index = range.begin;
index < range.endExclusive; ++
index)
390 std::int64_t moneyOwed)
noexcept
408 std::span<const std::int64_t> moneyOwed)
noexcept
411 after.numberOfPlayers != before.numberOfPlayers || player >= before.numberOfPlayers ||
415 return current < moneyOwed.size() ? moneyOwed[current] : 0;
417 std::array<std::size_t, rules::MaxPlayers>
charts{};
420 after, current,
false,
false,
debtFor(current)));
424 filtered[player].propertiesReceived = 0;
462 ?
config.givingMonopolyImportance
463 :
config.monopolyReceivedImportance;
487 player >=
state.numberOfPlayers)
504 current <
state.numberOfPlayers; ++current)
509 static_cast<double>(
state.numberOfPlayers - 1);
513 static_cast<double>(
state.options.passingGoAmount);
537 std::span<const std::int64_t> moneyOwed)
noexcept
546 if (current == player ||
547 state.players[current].currentSquare ==
552 const auto debt = current < moneyOwed.size() ? moneyOwed[current] : 0;
569 player >=
state.numberOfPlayers)
574 current <
state.numberOfPlayers; ++current)
578 if (current == player)
583 else if (current == player)
593 std::array<rules::PlayerNumber, rules::MaxPlayers>
candidates{};
596 current <
state.numberOfPlayers; ++current)
599 if (current == player ||
600 state.players[current].currentSquare ==
625 player >=
state.numberOfPlayers)
629 std::array<rules::PlayerNumber, rules::MaxPlayers>
candidates{};
634 if (current == player || current == excludedPlayer ||
635 state.players[current].currentSquare ==
648 properties).
count != 0)
663 property >= SquareType::InJail)
664 return Importance::NotImportant;
667 if (purchasingPlayer <
simulated.numberOfPlayers &&
668 purchasingProperty < SquareType::InJail && purchasingProperty != property)
669 simulated.squares[
static_cast<std::size_t
>(purchasingProperty)].owner = purchasingPlayer;
671 return Importance::CashCow;
675 const std::array<rules::PlayerNumber, 1>
one{
candidate};
682 return Importance::GivesDirectMonopoly;
687 if (current != excludedPlayer &&
directFor(current))
688 return Importance::GivesDirectMonopoly;
691 std::array<rules::PlayerNumber, rules::MaxPlayers> original{};
702 while (playerCount != 0)
706 std::span<const rules::PlayerNumber>(
players.data(), playerCount),
710 std::span<rules::PlayerNumber>(
players.data(),
players.size()), playerCount,
711 std::span<const rules::PlayerNumber>(
existing.players.data(),
existing.count));
718 std::span<const rules::PlayerNumber>(
players.data(), playerCount),
720 if (
enabled.count != 0)
return Importance::AllowsTrade;
724 return Importance::NotImportant;
728 std::span<const std::int64_t> timeLastTrade,
729 std::size_t maxTrades)
noexcept
731 if (maxTrades == 0 || maxTrades > timeLastTrade.size())
735 if (timeLastTrade[
index] == 0)
736 return static_cast<int>(
index);
744 std::uint8_t importance,
745 std::span<const std::int64_t> timeLastTrade,
746 std::size_t maxTrades,
750 player >=
state.numberOfPlayers)
753 if (
inputs.playerSendingTrade ||
state.tradeInProgress ||
754 (
inputs.buySellMortgagePlayer != player &&
764 if (
inputs.shouldGiveAwayMonopoly &&
770 ?
inputs.monopolyProbability
771 :
inputs.proposalProbability;
781 std::int64_t moneyOwed)
noexcept
784 toPlayer >=
state.numberOfPlayers ||
791 for (
const auto group : order)
802 if (owner == toPlayer ||
owned == 0)
804 next[toPlayer].propertiesReceived |=
owned;
835 result.tradePlayers[result.tradePlayerCount++] =
target;
886 result[4].importance += (
config.railroadImportance[
rails] -
892 result[5].importance += (
config.utilityImportance[
utils] -
897 for (std::size_t
head = 0;
head < result.size(); ++
head)
901 if (result[
head].importance < result[
tail].importance)
902 std::swap(result[
head], result[
tail]);
923 after.numberOfPlayers != before.numberOfPlayers ||
924 player >= before.numberOfPlayers ||
strategyPlayer >= before.numberOfPlayers)
939 std::array<rules::PlayerNumber, rules::MaxPlayers>
candidates{};
943 if (current == player ||
944 after.players[current].currentSquare ==
1029 selected = available & (0u - available);
1042 :
static_cast<int>(item) -
1045 for (
const auto group : order)
1065 current < before.numberOfPlayers; ++current)
1072 if (trade.count == 0)
1092 proposals[player].propertiesReceived |= selected;
1095 const auto given = selected & properties[current];
1111 const auto traded = proposal.propertiesReceived | proposal.propertiesGiven;
1112 auto& bit1 =
memory.bit1[proposedPlayer];
1113 auto& bit2 =
memory.bit2[proposedPlayer];
1129 const auto traded = proposal.propertiesReceived | proposal.propertiesGiven;
1131 const auto second =
traded &
memory.bit2[proposedPlayer];
1133 return first | second;
1136 return first & second;
1143 auto& bit1 =
memory.bit1[player];
1144 auto& bit2 =
memory.bit2[player];
1161 auto index =
static_cast<std::size_t
>((
attitude + 1.0) * 10.0);
1177 if (proposal.propertiesGiven != 0 || proposal.propertiesReceived != 0 ||
1178 proposal.cashReceived != 0 || proposal.cashGiven != 0)
1182 if (proposal.jailCardGiven[
index] || proposal.jailCardReceived[
index])
1193 if (
proposals[player].cashReceived != 0)
1202 std::span<const FutureImmunityRecord> immunities)
noexcept
1209 player <
state.numberOfPlayers; ++player)
1211 const auto& proposal =
proposals[player];
1216 bool given = proposal.propertiesGiven != 0 || proposal.cashGiven != 0;
1218 proposal.propertiesReceived != 0 || proposal.cashReceived != 0;
1224 for (
const auto&
immunity : immunities)
1242 std::span<const FutureImmunityRecord> immunities)
noexcept
1250 player <
state.numberOfPlayers; ++player)
1256 bool given = proposal.propertiesGiven != 0 || proposal.cashGiven != 0;
1257 bool received = proposal.propertiesReceived != 0 || proposal.cashReceived != 0;
1263 for (
const auto&
immunity : immunities)
1280 proposal.cashGiven += 1;
1290 proposal.cashReceived += 1;
1301 proposal.cashReceived += 1;
1306 proposal.cashGiven += 1;
1320 player <
state.numberOfPlayers; ++player)
1322 const auto& proposal =
proposals[player];
1323 for (std::size_t
index = 0;
1331 state.players[player].cash += proposal.cashReceived - proposal.cashGiven;
1334 if (proposal.jailCardReceived[
deck])
1365 std::span<const FutureImmunityRecord> immunities)
noexcept
1367 for (
const auto&
immunity : immunities)
1385 player >=
state.numberOfPlayers)
1388 if (
inputs.playerSendingTrade && !
state.tradeInProgress)
1407 std::int64_t
a = 0, std::int64_t
b = 0,
1408 std::int64_t
c = 0, std::int64_t
d = 0,
1409 std::int64_t
e = 0)
mutable {
1410 if (
plan.count >=
plan.actions.size())
1412 auto& message =
plan.actions[
plan.count++];
1413 message.action = type;
1414 message.fromPlayer = player;
1416 message.numberA =
a;
1417 message.numberB =
b;
1418 message.numberC =
c;
1419 message.numberD =
d;
1420 message.numberE =
e;
1437 if (
state.players[from].cashGivenInTrade[to] == 0)
1449 for (
auto& proposal :
working)
1451 proposal.cashGiven -= proposal.cashReceived;
1452 proposal.cashReceived = 0;
1453 if (proposal.cashGiven < 0)
1455 proposal.cashReceived = -proposal.cashGiven;
1456 proposal.cashGiven = 0;
1470 if (
giver.cashGiven == 0)
1475 const auto amount = std::min(
receiver.cashReceived,
giver.cashGiven);
1482 giver.cashGiven -= amount;
1504 if ((
proposals[current].propertiesReceived &
bit) != 0)
1509 static_cast<std::int64_t
>(
squareNo)))
1521 static_cast<std::int64_t
>(
squareNo)))
1530 std::array<rules::PlayerNumber, DeckCount>
cardReceived{};
1546 const auto owner =
state.cards[
deck].jailOwner;
1551 static_cast<std::int64_t
>(
deck)))
1560 ?
static_cast<std::int64_t
>(
inputs.proposedPlayer)
1576 std::int64_t amount,
1591 proposals[fromPlayer].cashGiven = amount;
1592 proposals[toPlayer].cashReceived = amount;
1598 if (amount >= 0 && amount <
static_cast<std::int64_t
>(
1603 if (propertyBit != 0)
1616 proposals[fromPlayer].propertiesGiven |= propertyBit;
1617 proposals[toPlayer].propertiesReceived |= propertyBit;
1625 if (amount >= 0 && amount <
static_cast<std::int64_t
>(
DeckCount))
1627 const auto deck =
static_cast<std::size_t
>(amount);
1632 proposal.jailCardGiven[
deck] =
false;
1633 proposal.jailCardReceived[
deck] =
false;
1650 const auto count =
static_cast<std::uint8_t
>(amount);
1652 immunities.begin(), immunities.end(),
1654 return entry.count != 0 &&
1655 entry.hitType == kind &&
1656 entry.properties == propertySet &&
1657 entry.toPlayer == toPlayer;
1666 auto free = std::find_if(
1667 immunities.begin(), immunities.end(),
1669 return entry.count == 0;
1671 if (
free != immunities.end())
1685 if (proposal.cashGiven > proposal.cashReceived)
1687 proposal.cashGiven -= proposal.cashReceived;
1688 proposal.cashReceived = 0;
1692 proposal.cashReceived -= proposal.cashGiven;
1693 proposal.cashGiven = 0;
std::array< rules::PlayerNumber, rules::MaxPlayers > players
decision::EconomicActionPlan plan
std::int64_t transferTax(const rules::GameState &state, rules::board::PropertySet properties) noexcept
void rememberTradedProperties(PropertyTradeMemory &memory, rules::PlayerNumber proposedPlayer, const TradeProposalRecord &proposal) noexcept
int findFreeTradeSpot(std::span< const std::int64_t > timeLastTrade, std::size_t maxTrades) noexcept
void forgetTradedProperties(PropertyTradeMemory &memory) noexcept
double cashMultiplier(double attitude, const CashMultiplierTable &multipliers) noexcept
PlayerPropertyAttitudeList createPlayerPropertyAttitudeList(const rules::GameState &state, rules::PlayerNumber player, const TradeProposalList &proposals, std::span< const double > playerAttitudes) noexcept
rules::PlayerNumber nextPlayerWantingCash(const TradeProposalList &proposals) noexcept
constexpr std::size_t DeckCount
std::array< TradeImportanceRecord, 6 > TradeImportanceList
ProposedTradeSendPlan buildProposedTradeSendPlan(const rules::GameState &state, rules::PlayerNumber player, const TradeProposalList &proposals, const ProposedTradeSendInputs &inputs) noexcept
bool tradeIsProper(const rules::GameState &state, const TradeProposalList &proposals, std::span< const FutureImmunityRecord > immunities) noexcept
rules::board::PropertySet propertiesAlreadyTraded(const PropertyTradeMemory &memory, rules::PlayerNumber proposedPlayer, const TradeProposalRecord &proposal, int dangerLevel) noexcept
StrategicPropertyImportance
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
int vetoMonopolies(rules::board::PropertySet properties) noexcept
MonopolyTradeGroup findSmallestMonopolyTrade(rules::PlayerNumber player, rules::board::PropertySet monopoly, std::span< const rules::PlayerNumber > candidates, const PropertySets &properties) noexcept
std::array< MonopolyTradeGenerosity, WhatToTradeEntries > WhatToTradeTable
PropertyImportanceResult findPlayerPropertyImportance(const rules::GameState &state, rules::PlayerNumber player, rules::PlayerNumber strategyPlayer, const PropertyImportanceConfig &config, std::int64_t moneyOwed) noexcept
TradeImportanceList createItemImportanceList(const rules::GameState &state, rules::PlayerNumber player, rules::PlayerNumber strategyPlayer, const PropertyImportanceConfig &config, double strategyAttitudeTowardPlayer, const CashMultiplierTable &multipliers) noexcept
bool addTypeProperty(const rules::GameState &before, const rules::GameState &after, rules::PlayerNumber player, rules::PlayerNumber strategyPlayer, rules::board::PropertySet &combinedProperties, TradeImportanceItem item, TradeProposalList &proposals, const PropertySets &properties, bool giveDescending, const WhatToTradeTable &whatToTrade, double strategyAttitudeTowardPlayer, std::int64_t playerMoneyOwed) noexcept
bool addTradeItem(TradeProposalList &proposals, FutureImmunityList &immunities, rules::TradeItemKind kind, std::int64_t amount, rules::PlayerNumber fromPlayer, rules::PlayerNumber toPlayer, rules::board::PropertySet propertySet) noexcept
std::array< double, WhatToTradeEntries > CashMultiplierTable
bool isMonopolyTrade(const rules::GameState &state, const TradeProposalList &proposals) noexcept
std::array< FutureImmunityRecord, rules::MaxCountHitSets > FutureImmunityList
void makeTradeProper(const rules::GameState &state, TradeProposalList &proposals, std::span< const FutureImmunityRecord > immunities) noexcept
bool onlyPlayerHasMonopoly(const rules::GameState &state, rules::PlayerNumber player) noexcept
void applyTradeToState(rules::GameState &state, const TradeProposalList &proposals) noexcept
bool addTradeMonopoly(const rules::GameState &state, rules::PlayerNumber toPlayer, rules::board::PropertySet &properties, PropertyClassification type, TradeProposalList &proposals, std::int64_t moneyOwed) 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
bool playerHasFutureOrImmunity(rules::PlayerNumber player, std::span< const FutureImmunityRecord > immunities) noexcept
std::array< TradeProposalRecord, rules::MaxPlayers > TradeProposalList
int possibleMonopoly(const rules::GameState &state, rules::PlayerNumber owner, rules::board::SquareType square) noexcept
MonopolyTradeDecision shouldTradeForMonopoly(const rules::GameState &state, rules::PlayerNumber player) noexcept
double calculateTradePropertyImportance(const rules::GameState &before, const rules::GameState &after, rules::PlayerNumber player, rules::PlayerNumber strategyPlayer, const TradeProposalList &proposals, bool givingMonopolyAway, const PropertyImportanceConfig &config, std::span< const std::int64_t > moneyOwed) noexcept
constexpr std::size_t WhatToTradeEntries
bool hasMonopolyTrade(const rules::GameState &state, rules::PlayerNumber player, rules::PlayerNumber excludedPlayer) noexcept
rules::PlayerNumber findNonmonopolyPlayer(const rules::GameState &state, rules::PlayerNumber player, std::span< const std::int64_t > moneyOwed) noexcept
StrategicPropertyImportance strategicPropertyImportance(const rules::GameState &state, rules::board::SquareType property, rules::PlayerNumber excludedPlayer, rules::PlayerNumber includedPlayer, rules::PlayerNumber purchasingPlayer, rules::board::SquareType purchasingProperty) noexcept
double averageRentReceived(const rules::GameState &state, rules::PlayerNumber player, std::int64_t startSquare, bool dontCountDevelopedMonopolies, double cashCowMultiplier, rules::board::PropertySet propertiesOwned) noexcept
MonopolyStage monopolyStage(const rules::GameState &state, rules::PlayerNumber player) noexcept
bool playerOwnsMonopoly(const rules::GameState &state, rules::PlayerNumber player, bool countBaltic) noexcept
std::size_t removePlayersFromList(std::span< rules::PlayerNumber > players, std::size_t playerCount, std::span< const rules::PlayerNumber > remove) noexcept
rules::board::PropertySet monopolySet(SquareType square) noexcept
bool isCashCow(SquareType square) noexcept
int numberRailroadsUtilitiesOwned(const rules::GameState &state, rules::PlayerNumber player, SquareGroup group, bool mortgageCounts, rules::board::PropertySet propertiesOwned) noexcept
int monopoliesBetweenPlayers(const rules::GameState &state, std::span< const rules::PlayerNumber > players, rules::board::SquareType extraProperty) noexcept
int monopoliesInSet(rules::board::PropertySet properties) noexcept
std::int64_t liquidAssets(const rules::GameState &state, rules::PlayerNumber player, bool countHouses, bool countMonopolies, std::int64_t moneyOwed) noexcept
bool isMonopoly(const rules::GameState &state, SquareType square) noexcept
constexpr std::array< rules::board::SquareType, 8 > ExpensiveMonopolySquares
rules::PlayerNumber firstOwnerInMonopoly(const rules::GameState &state, SquareType square) noexcept
MonopolyCollection monopoliesOwned(const rules::GameState &state, rules::PlayerNumber player, bool mortgageCounts) noexcept
bool anyMonopoly(const rules::GameState &state) noexcept
std::int64_t averageRentPaid(const rules::GameState &state, rules::PlayerNumber player, std::int64_t startSquare, bool considerDevelopedMonopolies, double cashCowMultiplier) noexcept
GroupRange groupRange(SquareGroup group) noexcept
rules::board::PropertySet propertiesOwnedByPlayer(const rules::GameState &state, rules::PlayerNumber player) noexcept
const SquareDefinition & definition(SquareType square)
constexpr std::size_t MaxPropertyGroups
std::uint32_t PropertySet
PropertySet propertyBit(SquareType square)
const PendingPhase & current(const GameState &state)
constexpr PlayerNumber BankPlayer
std::uint8_t PlayerNumber
constexpr PlayerNumber MaxPlayers
constexpr PlayerNumber NobodyPlayer
std::array< int, rules::MaxPlayers > playerIndex
ProposedTradeSendStatus status
std::int64_t purchaseCost