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/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