/nix/store/i1aar97n7b4yf8rk94p66if25brfvvdx-gqvzl8a5pvrg3xj44q0msrndcripi8dk-source/src/statefunctions/stateutilities.cc
Line | Count | Source |
1 | | #include "statefunctions/stateutilities.h" |
2 | | |
3 | | #include <algorithm> |
4 | | #include <array> |
5 | | #include <cstdint> |
6 | | #include <iostream> |
7 | | #include <memory> |
8 | | #include <vector> |
9 | | |
10 | | #include "controllers/playercontroller.h" |
11 | | #include "statefunctions/decisionfunction.h" |
12 | | #include "types/event.h" |
13 | | #include "types/gamestate.h" |
14 | | #include "types/piecetype.h" |
15 | | #include "types/player.h" |
16 | | #include "types/winds.h" |
17 | | |
18 | | namespace mahjong { |
19 | | |
20 | 37 | Wind GetSeat(int round, int player) { |
21 | 37 | return static_cast<Wind>((player + 3 * (round % 4)) % 4); |
22 | 37 | } |
23 | | |
24 | | // Push Event to Player Queue |
25 | 24 | void AlertPlayers(const GameState& state, Event e) { |
26 | 24 | e.decision = false; |
27 | 96 | for (const auto& player : state.players) { |
28 | 96 | player.controller->ReceiveEvent(e); |
29 | 96 | } |
30 | 24 | } |
31 | | |
32 | | // Count number of piece p that are in given players hands |
33 | 247 | uint8_t CountPieces(const GameState& state, int player, Piece p) { |
34 | 247 | return std::count(state.hands.at(player).live.begin(), |
35 | 247 | state.hands.at(player).live.end(), p); |
36 | 247 | } |
37 | | |
38 | | // Remove an instance of piece p from given players hand |
39 | 18 | uint8_t RemovePieces(GameState& state, int player, Piece p, uint8_t count) { |
40 | 18 | count = std::min(CountPieces(state, player, p), count); |
41 | 18 | uint8_t removed = 0; |
42 | 18 | state.hands.at(player).live.erase( |
43 | 18 | std::remove_if(state.hands.at(player).live.begin(), |
44 | 18 | state.hands.at(player).live.end(), |
45 | 190 | [&](Piece _p) { |
46 | 190 | if (count > removed && p == _p) { |
47 | 18 | removed++; |
48 | 18 | return true; |
49 | 18 | } |
50 | 172 | return false; |
51 | 190 | }), |
52 | 18 | state.hands.at(player).live.end()); |
53 | 18 | return removed; |
54 | 18 | } |
55 | | |
56 | | // Discard an instance of piece p from given players hand |
57 | 13 | void DiscardPiece(GameState& state, int player, Piece p) { |
58 | 13 | RemovePieces(state, player, p, /*count=*/1); |
59 | 13 | state.hands.at(player).discards.push_back(p); |
60 | 13 | } |
61 | | |
62 | 1 | Piece AskForDiscard(const GameState& state) { |
63 | 1 | state.players.at(state.currentPlayer) |
64 | 1 | .controller->ReceiveEvent(Event{ |
65 | 1 | .type = Event::kDiscard, // type |
66 | 1 | .player = state.currentPlayer, // player |
67 | 1 | .piece = 0, // piece |
68 | 1 | .decision = true, // decision |
69 | 1 | }); |
70 | | |
71 | 1 | return Piece( |
72 | 1 | GetValidDecisionOrThrow(state, state.currentPlayer, /*inHand=*/true) |
73 | 1 | .piece); |
74 | 1 | } |
75 | | |
76 | 13 | Event GetValidDecisionOrThrow(const GameState& state, int player, bool inHand) { |
77 | 13 | Event decision; |
78 | 13 | bool valid = false; |
79 | 13 | int i = 0; |
80 | 126 | while (!valid) { |
81 | 114 | if (i > 100) { |
82 | 1 | Event replacement_decision = decision; |
83 | 1 | replacement_decision.type = inHand ? Event::kDiscard : Event::kDecline; |
84 | 1 | if (inHand) { |
85 | 1 | replacement_decision.piece = static_cast<int16_t>( |
86 | 1 | state.hands.at(player).live.back().toUint8_t()); |
87 | 1 | } |
88 | 1 | if (ValidateDecision(state, player, replacement_decision, inHand)) { |
89 | 1 | return replacement_decision; |
90 | 1 | } |
91 | 0 | std::cerr |
92 | 0 | << "WARNING: Player Controller sent invalid event too many times." |
93 | 0 | << '\n'; |
94 | 0 | std::cerr << "Decision.type: " << decision.type << " Decision.piece " |
95 | 0 | << decision.piece << " player: " << player |
96 | 0 | << " inHand: " << (inHand ? "true" : "false") << '\n'; |
97 | 0 | std::cerr << "ERROR: was not able to recover from invalid event." << '\n'; |
98 | 0 | throw 0xBAD22222; |
99 | 1 | } |
100 | 113 | i++; |
101 | 113 | decision = state.players.at(player).controller->RetrieveDecision(); |
102 | 113 | valid = ValidateDecision(state, player, decision, inHand); |
103 | 113 | } |
104 | 12 | return decision; |
105 | 13 | } |
106 | | |
107 | | bool ValidateDecision(const GameState& state, int player, Event decision, |
108 | 114 | bool inHand) { |
109 | 114 | if (decision.type > Event::kDiscard) { |
110 | 0 | return false; |
111 | 0 | } |
112 | 114 | if (decision.type > Event::kDecline && !inHand) { |
113 | 0 | return false; |
114 | 0 | } |
115 | 114 | if (decision.type < Event::kTsumo && inHand) { |
116 | 0 | return false; |
117 | 0 | } |
118 | 114 | switch (decision.type) { |
119 | 0 | case Event::kRon: |
120 | 0 | return CanRon(state, player); |
121 | 0 | case Event::kKan: |
122 | 0 | return CanKan(state, player); |
123 | 1 | case Event::kPon: |
124 | 1 | return CanPon(state, player); |
125 | 0 | case Event::kChi: |
126 | 0 | return CanChi(state, player); |
127 | 0 | case Event::kTsumo: |
128 | 0 | return CanTsumo(state, player); |
129 | 0 | case Event::kConcealedKan: |
130 | 0 | return CanConcealedKan(state, player); |
131 | 0 | case Event::kConvertedKan: |
132 | 0 | return CanConvertedKan(state, player); |
133 | 0 | case Event::kRiichi: |
134 | 0 | return CanRiichi(state, player); |
135 | 112 | case Event::kDiscard: |
136 | 112 | return CountPieces(state, player, Piece(decision.piece)) > 0; |
137 | 1 | case Event::kDecline: |
138 | 1 | return true; |
139 | 0 | default: |
140 | 0 | return false; |
141 | 114 | } |
142 | 114 | } |
143 | | |
144 | | } // namespace mahjong |