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