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/analysis/handnode.cc
Line
Count
Source
1
#include "analysis/handnode.h"
2
3
#include <algorithm>
4
#include <cstddef>
5
#include <format>
6
#include <iostream>
7
#include <string>
8
#include <vector>
9
10
#include "types/piecetype.h"
11
#include "types/sets.h"
12
#include "types/typeprinter.h"
13
14
namespace mahjong {
15
16
namespace {
17
0
std::string NodeTypeToColorStr(SetType nodetype) {
18
0
  switch (nodetype) {
19
0
    case SetType::kChi:
20
0
      return "purple";
21
0
    case SetType::kPon:
22
0
      return "yellow";
23
0
    case SetType::kPair:
24
0
      return "green";
25
0
    case SetType::kSingle:
26
0
      return "blue";
27
0
    default:
28
0
      return "red";
29
0
  }
30
0
}
31
32
0
std::string NodeTypeToShapeStr(SetType nodetype) {
33
0
  switch (nodetype) {
34
0
    case SetType::kChi:
35
0
      return "house";
36
0
    case SetType::kPon:
37
0
      return "septagon";
38
0
    case SetType::kPair:
39
0
      return "oval";
40
0
    case SetType::kSingle:
41
0
      return "box";
42
0
    default:
43
0
      return "Mdiamond";
44
0
  }
45
0
}
46
}  // namespace
47
48
0
std::string Node::typeToStr() const {
49
0
  switch (type_) {
50
0
    case kChi:
51
0
      return "Chi";
52
0
    case kPon:
53
0
      return "Pon";
54
0
    case kPair:
55
0
      return "Pair";
56
0
    case kSingle:
57
0
      return "Single";
58
0
    default:
59
0
      return "Error";
60
0
  }
61
0
}
62
63
0
Node::ConstIterator Node::begin() const {
64
0
  return ConstIterator(this, /*end=*/false);
65
0
}
66
67
0
Node::ConstIterator Node::end() const {
68
0
  return ConstIterator(this, /*end=*/true);
69
0
}
70
71
0
Node::Iterator Node::begin() {
72
0
  return Iterator(this, /*end=*/false);
73
0
}
74
75
0
Node::Iterator Node::end() {
76
0
  return Iterator(this, /*end=*/true);
77
0
}
78
79
0
bool Node::operator!=(const Node& n) const {
80
0
  return id_ == n.id_ && type_ == n.type_ && start_ == n.start_ &&
81
0
         parent_ == n.parent_ && leaves_ == n.leaves_;
82
0
}
83
84
0
Node::ConstIterator& Node::ConstIterator::operator++() {
85
0
  if (!root_->leaves_.empty()) {
86
0
    root_ = root_->leaves_.front().get();
87
0
    return *this;
88
0
  }
89
0
  if (root_->parent_ == nullptr) {
90
0
    end_ = true;
91
0
    return *this;
92
0
  }
93
0
  const Node* traveler = root_;
94
0
  size_t leaf_pos_next = traveler->leafPosInParent() + 1;
95
0
  while ((traveler->parent_ != nullptr) &&
96
0
         traveler->parent_->leaves_.size() <= leaf_pos_next) {
97
0
    traveler = traveler->parent_;
98
0
    leaf_pos_next = traveler->leafPosInParent() + 1;
99
0
  }
100
0
  if (traveler->parent_ == nullptr) {
101
0
    end_ = true;
102
0
    return *this;
103
0
  }
104
105
0
  if (traveler->parent_->leaves_.size() > leaf_pos_next &&
106
0
      traveler->parent_->leaves_[leaf_pos_next].get() != root_) {
107
0
    root_ = traveler->parent_->leaves_[leaf_pos_next].get();
108
0
    return *this;
109
0
  }
110
0
  std::cerr << "FORWARD TRAVERSAL FAILED: Set to end." << '\n';
111
0
  std::cerr << "ROOT: " << root_ << '\n';
112
0
  if (traveler->parent_ != nullptr) {
113
0
    std::cerr << "PARENT: " << traveler->parent_ << '\n';
114
0
  }
115
0
  std::cerr << "TRAVELER: " << *traveler << '\n';
116
0
  end_ = true;
117
0
  return *this;
118
0
}
119
120
0
Node::Iterator& Node::Iterator::operator++() {
121
0
  itr_.operator++();
122
0
  return *this;
123
0
}
124
125
0
Node& Node::Iterator::operator*() const {
126
0
  return const_cast<Node&>(*itr_.root_);
127
0
}
128
129
0
const Node& Node::ConstIterator::operator*() const {
130
0
  return *root_;
131
0
}
132
133
0
bool Node::Iterator::operator!=(const Iterator& other) const {
134
0
  return itr_ != other.itr_;
135
0
}
136
137
0
bool Node::ConstIterator::operator!=(const Node::ConstIterator& other) const {
138
0
  return end_ != other.end_;
139
0
}
140
141
0
std::ostream& Node::DumpAsTGF(std::ostream& os) const {
142
0
  std::vector<std::string> nodes;
143
0
  std::vector<std::string> connections;
144
0
  for (const auto& node : *this) {
145
0
    const bool is_root = node.parent_ == nullptr;
146
0
    const std::string piece = is_root ? "Root" : node.start().toStr();
147
0
    const std::string type = is_root ? "RootNode" : node.typeToStr();
148
0
    nodes.push_back(
149
0
        std::format("{} Piece: {} Type: {}", node.id_, piece, type));
150
0
    for (const auto& leaf : node.leaves_) {
151
0
      connections.push_back(std::format("{} {}", node.id_, leaf->id_));
152
0
    }
153
0
  }
154
0
  for (const auto& node : nodes) {
155
0
    os << node << '\n';
156
0
  }
157
0
  os << "#" << '\n';
158
0
  for (const auto& connection : connections) {
159
0
    os << connection << '\n';
160
0
  }
161
0
  return os;
162
0
}
163
164
0
std::ostream& Node::DumpAsDot(std::ostream& os) const {
165
0
  std::vector<std::string> nodes;
166
0
  std::vector<std::string> connections;
167
0
  for (const auto& node : *this) {
168
0
    const bool is_root = node.parent_ == nullptr;
169
0
    const std::string piece = is_root ? "Root" : node.start().toStr();
170
0
    const std::string type = is_root ? "RootNode" : node.typeToStr();
171
0
    const std::string shape =
172
0
        is_root ? "underline" : NodeTypeToShapeStr(node.type_);
173
0
    const std::string color =
174
0
        is_root ? "black" : NodeTypeToColorStr(node.type_);
175
0
    nodes.push_back(std::format("{} [label=\"{}: {}\",shape={},color={}];",
176
0
                                node.id_, piece, type, shape, color));
177
0
    for (const auto& leaf : node.leaves_) {
178
0
      connections.push_back(std::format("{} -> {};", node.id_, leaf->id_));
179
0
    }
180
0
  }
181
0
  os << "digraph {" << '\n';
182
0
  for (const auto& node : nodes) {
183
0
    os << "  " << node << '\n';
184
0
  }
185
0
  os << '\n';
186
0
  for (const auto& connection : connections) {
187
0
    os << "  " << connection << '\n';
188
0
  }
189
0
  os << "}" << '\n';
190
0
  return os;
191
0
}
192
193
424
std::vector<std::vector<const Node*>> Node::AsBranchVectors(const Node* root) {
194
424
  if (!root) {
195
0
    std::cerr << "ERROR: AsBranchVectors called with null root\n";
196
0
    return {};
197
0
  }
198
199
424
  std::vector<std::vector<const Node*>> branches;
200
424
  std::vector<const Node*> nodeloc;
201
424
  nodeloc.push_back(root);
202
203
5.25k
  while (!nodeloc.empty()) {
204
4.83k
    if (nodeloc.empty()) {
205
0
      std::cerr << "ERROR: AsBranchVectors nodeloc became empty unexpectedly\n";
206
0
      break;
207
0
    }
208
209
4.83k
    const Node* current = nodeloc.back();
210
4.83k
    if (!current) {
211
0
      std::cerr << "ERROR: AsBranchVectors null node in nodeloc\n";
212
0
      nodeloc.pop_back();
213
0
      continue;
214
0
    }
215
216
4.83k
    if (!current->leaves_.empty()) {
217
3.96k
      nodeloc.push_back(current->leaves_[0].get());
218
3.96k
    } else {
219
      // Skip the root node when adding to branches list.
220
869
      branches.emplace_back(nodeloc.begin() + 1, nodeloc.end());
221
869
      size_t next = current->leafPosInParent() + 1;
222
223
4.83k
      while ((current->parent_ != nullptr) &&
224
4.83k
             current->parent_->leaves_.size() <= next) {
225
3.96k
        nodeloc.pop_back();
226
3.96k
        if (nodeloc.empty()) {
227
0
          break;
228
0
        }
229
3.96k
        current = nodeloc.back();
230
3.96k
        next = current->leafPosInParent() + 1;
231
3.96k
      }
232
233
869
      if (nodeloc.empty() || current->parent_ == nullptr) {
234
424
        nodeloc.pop_back();
235
445
      } else {
236
445
        nodeloc.pop_back();
237
445
        if (!nodeloc.empty() && next < nodeloc.back()->leaves_.size()) {
238
445
          nodeloc.push_back(nodeloc.back()->leaves_[next].get());
239
445
        } else {
240
0
          std::cerr << "ERROR: AsBranchVectors next index " << next
241
0
                    << " out of bounds\n";
242
0
          break;
243
0
        }
244
445
      }
245
869
    }
246
4.83k
  }
247
424
  return branches;
248
424
}
249
250
316
bool Node::IsComplete() const {
251
316
  auto branches = AsBranchVectors(this);
252
353
  return std::ranges::any_of(branches, [](auto branch) {
253
353
    int pair_count = 0;
254
353
    return std::none_of(branch.begin(), branch.end(),
255
418
                        [&pair_count](auto node) {
256
418
                          if (node->type_ == SetType::kPair) {
257
6
                            pair_count += 1;
258
6
                          }
259
418
                          return node->type_ == SetType::kSingle;
260
418
                        }) &&
261
353
           (pair_count == 1 || pair_count == 7);
262
353
  });
263
316
}
264
}  // namespace mahjong