/nix/store/i1aar97n7b4yf8rk94p66if25brfvvdx-gqvzl8a5pvrg3xj44q0msrndcripi8dk-source/src/statefunctions/gamestates/playerhand.cc
Line | Count | Source |
1 | | #include <array> |
2 | | #include <cstdint> |
3 | | #include <iostream> |
4 | | #include <memory> |
5 | | #include <vector> |
6 | | |
7 | | #include "controllers/playercontroller.h" |
8 | | #include "statefunctions/decisionfunction.h" |
9 | | #include "statefunctions/router.h" |
10 | | #include "statefunctions/stateutilities.h" |
11 | | #include "types/event.h" |
12 | | #include "types/gamestate.h" |
13 | | #include "types/piecetype.h" |
14 | | #include "types/statefunction.h" |
15 | | |
16 | | namespace mahjong { |
17 | | |
18 | | namespace { |
19 | 10 | std::unique_ptr<GameState> PlayerHand(std::unique_ptr<GameState> state) { |
20 | 10 | const std::vector<PossibleDecision> decisions = { |
21 | 10 | PossibleDecision{.type = Event::kTsumo, .func = CanTsumo}, |
22 | 10 | PossibleDecision{.type = Event::kConcealedKan, .func = CanConcealedKan}, |
23 | 10 | PossibleDecision{.type = Event::kConvertedKan, .func = CanConvertedKan}, |
24 | 10 | PossibleDecision{.type = Event::kRiichi, .func = CanRiichi}, |
25 | 10 | PossibleDecision{.type = Event::kDiscard, |
26 | 10 | .func = [](const GameState& state, int) { |
27 | 10 | return !state.hands.at(state.currentPlayer).riichi; |
28 | 10 | }}}; |
29 | | |
30 | 10 | bool decision_asked = false; |
31 | 50 | for (const auto& [decision, decisionIsPossible] : decisions) { |
32 | 50 | if (decisionIsPossible(*state, state->currentPlayer)) { |
33 | 10 | decision_asked = true; |
34 | 10 | state->players.at(state->currentPlayer) |
35 | 10 | .controller->ReceiveEvent(Event{ |
36 | 10 | .type = decision, // type |
37 | 10 | .player = state->currentPlayer, // player |
38 | 10 | .piece = static_cast<int16_t>( |
39 | 10 | state->pendingPiece.toUint8_t()), // piece |
40 | 10 | .decision = true, // decision |
41 | 10 | }); |
42 | 10 | } |
43 | 50 | } |
44 | | |
45 | 10 | Event decision; |
46 | 10 | if (!decision_asked) { |
47 | 0 | decision.type = Event::kDiscard; |
48 | 0 | decision.piece = static_cast<uint16_t>(state->pendingPiece.toUint8_t()); |
49 | 0 | decision.player = state->currentPlayer; |
50 | 0 | decision.decision = true; |
51 | 0 | state->nextState = StateFunctionType::kDiscard; |
52 | 10 | } else { |
53 | 10 | decision = |
54 | 10 | GetValidDecisionOrThrow(*state, state->currentPlayer, /*inHand=*/true); |
55 | 10 | } |
56 | | |
57 | | // note riichi handling is a lil borked on the player agency side |
58 | | // checkout riichi.cpp for more info |
59 | 10 | if (decision.type == Event::kDiscard) { |
60 | 10 | state->pendingPiece = Piece(decision.piece); |
61 | 10 | } |
62 | | |
63 | 10 | switch (decision.type) { |
64 | 0 | case Event::kTsumo: |
65 | 0 | state->nextState = StateFunctionType::kTsumo; |
66 | 0 | break; |
67 | 0 | case Event::kConcealedKan: |
68 | 0 | state->nextState = StateFunctionType::kConcealedKan; |
69 | 0 | break; |
70 | 0 | case Event::kConvertedKan: |
71 | 0 | state->nextState = StateFunctionType::kConvertedKan; |
72 | 0 | break; |
73 | 0 | case Event::kRiichi: |
74 | 0 | state->nextState = StateFunctionType::kRiichi; |
75 | 0 | break; |
76 | 10 | case Event::kDiscard: |
77 | 10 | state->nextState = StateFunctionType::kDiscard; |
78 | 10 | break; |
79 | 0 | default: |
80 | 0 | std::cerr << "Invalid Decision Type in playerhand: " << decision.type |
81 | 0 | << '\n'; |
82 | 0 | state->nextState = StateFunctionType::kError; |
83 | 0 | break; |
84 | 10 | } |
85 | | |
86 | 10 | return state; |
87 | 10 | } |
88 | | } // namespace |
89 | | |
90 | | REGISTER_ROUTE(PlayerHand, StateFunctionType::kPlayerHand); |
91 | | } // namespace mahjong |