Coverage Report

Created: 2025-09-03 03:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/nix/store/i1aar97n7b4yf8rk94p66if25brfvvdx-gqvzl8a5pvrg3xj44q0msrndcripi8dk-source/src/statefunctions/gamestates/discard.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
#include "types/walls.h"
16
17
namespace mahjong {
18
19
namespace {
20
11
std::unique_ptr<GameState> Discard(std::unique_ptr<GameState> state) {
21
11
  AlertPlayers(*state, Event{
22
11
                           .type = Event::kDiscard,         // type
23
11
                           .player = state->currentPlayer,  // player
24
11
                           .piece = static_cast<int16_t>(
25
11
                               state->pendingPiece.toUint8_t()),  // piece
26
11
                           .decision = false,                     // decision
27
11
                       });
28
11
  DiscardPiece(*state, state->currentPlayer, state->pendingPiece);
29
30
11
  const std::vector<PossibleDecision> decisions = {
31
11
      {.type = Event::kChi, .func = CanChi},
32
11
      {.type = Event::kPon, .func = CanPon},
33
11
      {.type = Event::kKan, .func = CanKan},
34
11
      {.type = Event::kRon, .func = CanRon},
35
11
  };
36
37
11
  std::array<bool, 4> need_decision = {false, false, false, false};
38
55
  for (int player = 0; player < 4; player++) {
39
44
    if (player == state->currentPlayer) {
40
11
      continue;
41
11
    }
42
132
    for (const auto& [decision, decisionIsPossible] : decisions) {
43
132
      if (decisionIsPossible(*state, player)) {
44
2
        need_decision.at(player) = true;
45
2
        state->players.at(player).controller->ReceiveEvent(Event{
46
2
            .type = decision,                // type
47
2
            .player = state->currentPlayer,  // player
48
2
            .piece =
49
2
                static_cast<int16_t>(state->pendingPiece.toUint8_t()),  // piece
50
2
            .decision = true,  // decision
51
2
        });
52
2
      }
53
132
    }
54
33
  }
55
56
11
  Event decision = kDeclineEvent;
57
55
  for (int i = 0; i < 4; i++) {
58
44
    if (need_decision.at(i)) {
59
2
      Event temp_decision =
60
2
          GetValidDecisionOrThrow(*state, i, /*inHand=*/false);
61
2
      if (temp_decision.type < decision.type) {  // lower is higher priority
62
1
        temp_decision.player = i;
63
1
        temp_decision.piece =
64
1
            static_cast<int16_t>(state->pendingPiece.toUint8_t());
65
1
        decision = temp_decision;
66
1
      }
67
2
      if (temp_decision.type == Event::kRon) {
68
0
        state->hasRonned.at(i) = true;
69
0
      }
70
2
    }
71
44
  }
72
73
11
  if (decision.type == Event::kDecline &&
74
11
      state->walls.GetRemainingPieces() == 0) {
75
0
    state->nextState = StateFunctionType::kExhaust;
76
0
    return state;
77
0
  }
78
79
11
  if (decision.type != Event::kDecline) {
80
1
    state->lastCaller = decision.player;
81
1
  }
82
83
11
  switch (decision.type) {
84
10
    case Event::kDecline:
85
10
      state->nextState = StateFunctionType::kDraw;
86
10
      break;
87
0
    case Event::kRon:
88
0
      state->nextState = StateFunctionType::kRon;
89
0
      break;
90
0
    case Event::kChi:
91
0
      state->nextState = StateFunctionType::kChi;
92
0
      break;
93
1
    case Event::kPon:
94
1
      state->nextState = StateFunctionType::kPon;
95
1
      break;
96
0
    case Event::kKan:
97
0
      state->nextState = StateFunctionType::kKan;
98
0
      break;
99
0
    default:
100
0
      std::cerr << "Invalid Decision Type in Discard: " << decision.type
101
0
                << '\n';
102
0
      state->nextState = StateFunctionType::kError;
103
0
      break;
104
11
  }
105
106
11
  return state;
107
11
}
108
}  // namespace
109
110
REGISTER_ROUTE(Discard, StateFunctionType::kDiscard);
111
}  // namespace mahjong