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/controllers/thricebot.cc
Line
Count
Source
1
#include "controllers/thricebot.h"
2
3
#include <cstddef>
4
#include <vector>
5
6
#include "controllers/controllermanager.h"
7
#include "types/event.h"
8
#include "types/pieces.h"
9
#include "types/piecetype.h"
10
#include "types/winds.h"
11
12
namespace mahjong {
13
namespace {
14
const float kHonorWeight = 1.5;
15
const float kNormalWeight = 1;
16
17
}  // namespace
18
19
REGISTER_PLAYER_CONTROLLER(ThriceBot);
20
21
0
void ThriceBot::RoundStart(std::vector<Piece> _hand, Wind s, Wind p) {
22
0
  for (const Piece m : _hand) {
23
0
    HandTile h;
24
0
    h.piece = m;
25
0
    if (m.isHonor()) {
26
0
      h.weight = kHonorWeight;
27
0
    } else {
28
0
      h.weight = kNormalWeight;
29
0
    }
30
0
    hand_.push_back(h);
31
0
  }
32
0
  assignweights();
33
0
  lastEvent_.type = Event::kDiscard;
34
0
  swind_ = s;
35
0
  pwind_ = p;
36
0
}
37
38
0
void ThriceBot::assignweights() {
39
0
  for (size_t i = 0; i < hand_.size(); i++) {
40
0
    for (size_t j = i; j < hand_.size(); j++) {
41
0
      if (j != i) {
42
0
        if (static_cast<int>(hand_.at(i).weight) == 2 &&
43
0
            (discarded_.at(hand_.at(i).piece.toUint8_t()) == 2 ||
44
0
             discarded_.at(hand_.at(i).piece.toUint8_t()) == 2)) {
45
0
          hand_.at(i).weight -= kNormalWeight;
46
0
        } else if (hand_.at(i).piece.getSuit() == hand_[j].piece.getSuit() &&
47
0
                   hand_.at(i).piece.getPieceNum() ==
48
0
                       hand_[j].piece.getPieceNum()) {
49
0
          hand_.at(i).weight += kNormalWeight;
50
0
          hand_[j].weight += kNormalWeight;
51
0
        }
52
0
      }
53
0
    }
54
0
  }
55
0
}
56
57
0
ThriceBot::HandTile ThriceBot::assignTileWeight(HandTile h1) {
58
0
  HandTile h;
59
0
  h.piece = h1.piece;
60
0
  h.weight = h1.weight;
61
0
  for (auto& i : hand_) {
62
0
    if (i.piece.getSuit() == h.piece.getSuit() &&
63
0
        i.piece.getPieceNum() == h.piece.getPieceNum()) {
64
0
      i.weight += kNormalWeight;
65
0
      h.weight += kNormalWeight;
66
0
    }
67
0
  }
68
0
  return h;
69
0
}
70
71
0
void ThriceBot::checkDiscard() {
72
0
  for (size_t i = 0; i < hand_.size(); i++) {
73
0
    for (size_t j = i; j < hand_.size(); j++) {
74
0
      if (j != i) {
75
0
        if (static_cast<int>(hand_.at(i).weight) == 2 &&
76
0
            (discarded_.at(hand_.at(i).piece.toUint8_t()) == 2 ||
77
0
             discarded_.at(hand_.at(i).piece.toUint8_t()) == 3)) {
78
0
          hand_.at(i).weight -= kNormalWeight;
79
0
        }
80
0
      }
81
0
    }
82
0
  }
83
0
}
84
85
0
void ThriceBot::ReceiveEvent(Event e) {
86
0
  if (e.decision) {
87
0
    if (e.type <= lastEvent_.type) {
88
0
      lastEvent_ = e;
89
0
    }
90
91
0
    if (e.type == Event::kDiscard && e.player == pid_) {
92
0
      HandTile h;
93
0
      h.piece = Piece(e.piece);
94
0
      if (h.piece.isHonor()) {
95
0
        h.weight = kHonorWeight;
96
0
      } else {
97
0
        h.weight = kNormalWeight;
98
0
      }
99
0
      h = assignTileWeight(h);
100
0
      hand_.push_back(h);
101
0
      checkDiscard();
102
0
    }
103
104
0
  } else if (e.type == Event::kDiscard) {
105
0
    discarded_.at(Piece(e.piece).toUint8_t())++;
106
0
  }
107
0
}
108
109
0
Event ThriceBot::RetrieveDecision() {
110
0
  if (lastEvent_.type == Event::kDiscard) {
111
0
    lastEvent_.piece = popDiscard().toUint8_t();
112
0
  } else if (lastEvent_.type == Event::kPon) {
113
0
    auto p = Piece(lastEvent_.piece);
114
0
    if (!checkTile(p)) {
115
0
      lastEvent_.type = Event::kDecline;
116
0
    }
117
0
  } else if (lastEvent_.type == Event::kChi) {
118
0
    auto p = Piece(lastEvent_.piece);
119
0
    if (!checkTile(p)) {
120
0
      lastEvent_.type = Event::kDecline;
121
0
    }
122
0
  } else if (lastEvent_.type == Event::kRiichi) {
123
0
    auto p = Piece(lastEvent_.piece);
124
0
    if (discarded_.at(p.toUint8_t()) == 3) {
125
0
      lastEvent_.type = Event::kDecline;
126
0
    }
127
0
  }
128
0
  Event e = lastEvent_;
129
0
  lastEvent_.type = Event::kDiscard;  // lowest """priority""" event type
130
0
  return e;
131
0
}
132
133
0
bool ThriceBot::checkTile(Piece p) {
134
0
  int j = 0;
135
0
  for (const auto& i : hand_) {
136
0
    if (i.piece.getSuit() == p.getSuit() &&
137
0
        i.piece.getPieceNum() == p.getPieceNum()) {
138
0
      j++;
139
0
    }
140
0
  }
141
0
  return j == 2 || j == 3;
142
0
}
143
144
0
Piece ThriceBot::popDiscard() {
145
0
  if (hand_.empty()) {
146
0
    return Piece(kError);
147
0
  }
148
0
  int index_of_lowest = 0;
149
0
  for (size_t i = 0; i < hand_.size(); i++) {
150
0
    if (hand_.at(i).weight < hand_[index_of_lowest].weight) {
151
0
      index_of_lowest = i;
152
0
    }
153
0
  }
154
0
  const Piece p = hand_[index_of_lowest].piece;
155
0
  hand_.erase(hand_.begin() + index_of_lowest);
156
0
  discarded_.at(p.toUint8_t())++;
157
0
  return p;
158
0
}
159
}  // namespace mahjong