Libsaki
Core library of Pancake Mahjong
tile.h
1 #ifndef SAKI_TILE_H
2 #define SAKI_TILE_H
3 
4 #include "../util/assume.h"
5 #include "../util/stactor.h"
6 #include "../util/int_iter.h"
7 
8 #include <ostream>
9 #include <cctype>
10 #include <cstring>
11 #include <array>
12 #include <bitset>
13 
14 
15 
16 namespace saki
17 {
18 
19 
20 
21 enum class Suit { M = 0, P = 1, S = 2, F = 3, Y = 4 };
22 
23 enum class Wait { NONE, ISORIDE, BIBUMP, CLAMP, SIDE, BIFACE };
24 
25 class T34
26 {
27 public:
28  static char charOf(Suit s)
29  {
30  switch (s) {
31  case Suit::M: return 'm';
32  case Suit::P: return 'p';
33  case Suit::S: return 's';
34  case Suit::F: return 'f';
35  case Suit::Y: return 'y';
36  default: unreached();
37  }
38  }
39 
40  static Suit suitOf(char c)
41  {
42  switch (std::tolower(c)) {
43  case 'm': return Suit::M;
44  case 'p': return Suit::P;
45  case 's': return Suit::S;
46  case 'f': return Suit::F;
47  case 'y': return Suit::Y;
48  default: unreached();
49  }
50  }
51 
52  static bool isValidSuit(char c)
53  {
54  c = static_cast<char>(std::tolower(c));
55  return c == 'm' || c == 'p' || c == 's' || c == 'f' || c == 'y';
56  }
57 
59  T34()
60 #ifndef NDEBUG
61  : mInitialized(false)
62 #endif
63  {
64  }
65 
66  explicit T34(int id34)
67  : mId34(id34)
68  {
69  assert(0 <= id34 && id34 < 34);
70  assume_opt_out(0 <= id34 && id34 < 34);
71  }
72 
73  explicit T34(Suit suit, int val)
74  : mId34(id34Of(suit, val))
75  {
76  }
77 
78  constexpr explicit T34(int val, Suit suit)
79  : mId34(id34Of(val, suit))
80  {
81  }
82 
83  explicit T34(const char *str)
84  : mId34(id34Of(suitOf(str[1]), str[0] - '0'))
85  {
86  assert(str[2] == '\0');
87  }
88 
89  T34(const T34 &copy) = default;
90  T34 &operator=(const T34 &assign) = default;
91  ~T34() = default;
92 
93  int id34() const
94  {
95 #ifndef NDEBUG
96  assert(mInitialized);
97 #endif
98  return mId34;
99  }
100 
101  unsigned uId34() const
102  {
103  return static_cast<unsigned>(id34());
104  }
105 
106  Suit suit() const
107  {
108 #ifndef NDEBUG
109  assert(mInitialized);
110 #endif
111  if (0 <= mId34 && mId34 < 9)
112  return Suit::M;
113 
114  if (9 <= mId34 && mId34 < 18)
115  return Suit::P;
116 
117  if (18 <= mId34 && mId34 < 27)
118  return Suit::S;
119 
120  if (27 <= mId34 && mId34 < 31)
121  return Suit::F;
122 
123  if (31 <= mId34 && mId34 < 34)
124  return Suit::Y;
125 
126  unreached();
127  }
128 
129  int val() const
130  {
131 #ifndef NDEBUG
132  assert(mInitialized);
133 #endif
134  if (0 <= mId34 && mId34 < 27)
135  return mId34 % 9 + 1;
136 
137  if (27 <= mId34 && mId34 < 31)
138  return mId34 - 27 + 1;
139 
140  if (31 <= mId34 && mId34 < 34)
141  return mId34 - 31 + 1;
142 
143  unreached();
144  }
145 
146  const char *str34() const
147  {
148  static const std::array<const char *, 34> STRS {
149  "1m", "2m", "3m", "4m", "5m", "6m", "7m", "8m", "9m",
150  "1p", "2p", "3p", "4p", "5p", "6p", "7p", "8p", "9p",
151  "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s", "9s",
152  "1f", "2f", "3f", "4f",
153  "1y", "2y", "3y"
154  };
155 
156  return STRS[uId34()];
157  }
158 
159  bool isZ() const
160  {
161  return suit() == Suit::F || suit() == Suit::Y;
162  }
163 
164  bool isNum() const
165  {
166  return !isZ();
167  }
168 
169  bool isNum19() const
170  {
171  return isNum() && (val() == 1 || val() == 9);
172  }
173 
174  bool isNum1928() const
175  {
176  return isNum() && (val() == 1 || val() == 2 || val() == 8 || val() == 9);
177  }
178 
179  bool isYao() const
180  {
181  return isZ() || val() == 1 || val() == 9;
182  }
183 
184  bool isYakuhai(int selfWind, int roundWind) const
185  {
186  assert(1 <= selfWind && selfWind <= 4);
187  assert(1 <= roundWind && roundWind <= 4);
188 
189  return suit() == Suit::Y
190  || (suit() == Suit::F && (val() == selfWind || val() == roundWind));
191  }
192 
193  bool operator==(T34 rhs) const
194  {
195  return id34() == rhs.id34();
196  }
197 
198  bool operator!=(T34 rhs) const
199  {
200  return !(*this == rhs);
201  }
202 
203  bool operator<(T34 rhs) const
204  {
205  return id34() < rhs.id34();
206  }
207 
208  bool operator|(T34 up) const
209  {
210  return isNum() && suit() == up.suit() && val() + 1 == up.val();
211  }
212 
213  bool operator||(T34 up) const
214  {
215  return isNum() && suit() == up.suit() && val() + 2 == up.val();
216  }
217 
218  bool operator^(T34 up) const
219  {
220  return isNum() && suit() == up.suit() && val() + 3 == up.val();
221  }
222 
223  bool operator%(T34 dora) const
224  {
225  return suit() == dora.suit() && val() % period() + 1 == dora.val();
226  }
227 
228  T34 prev() const
229  {
230  assert(isNum() && val() >= 2);
231  assume_opt_out(isNum() && val() >= 2);
232  return T34(suit(), val() - 1);
233  }
234 
235  T34 pprev() const
236  {
237  assert(isNum() && val() >= 3);
238  assume_opt_out(isNum() && val() >= 3);
239  return T34(suit(), val() - 2);
240  }
241 
242  T34 next() const
243  {
244  assert(isNum() && 1 <= val() && val() <= 8);
245  assume_opt_out(isNum() && 1 <= val() && val() <= 8);
246  return T34(suit(), val() + 1);
247  }
248 
249  T34 nnext() const
250  {
251  assert(isNum() && 1 <= val() && val() <= 7);
252  assume_opt_out(isNum() && 1 <= val() && val() <= 7);
253  return T34(suit(), val() + 2);
254  }
255 
256  T34 dora() const
257  {
258  return T34(suit(), val() % period() + 1);
259  }
260 
261  T34 indicator() const
262  {
263  // first '-1': from 1-index to 0-index
264  // second '+period': prevent being minus
265  // third '-1': previou tile
266  // last '+1': from 0-index to 1-index
267  return T34(suit(), (val() - 1 + period() - 1) % period() + 1);
268  }
269 
270  int period() const
271  {
272  switch (suit()) {
273  case Suit::M:
274  case Suit::P:
275  case Suit::S:
276  return 9;
277  case Suit::F:
278  return 4;
279  case Suit::Y:
280  return 3;
281  default:
282  unreached();
283  }
284  }
285 
286  Wait waitAsSequence(T34 pick) const
287  {
288  if (suit() != pick.suit())
289  return Wait::NONE;
290 
291  int va = val();
292  int vb = pick.val();
293 
294  // 123/3 789/7
295  if ((va == 1 && vb == 3) || (va == 7 && vb == 7))
296  return Wait::SIDE;
297 
298  // xyz/y
299  if (va + 1 == vb)
300  return Wait::CLAMP;
301 
302  // xyz/x xyz/z
303  if (va == vb || va + 2 == vb)
304  return Wait::BIFACE;
305 
306  return Wait::NONE;
307  }
308 
309 private:
310  static int id34Of(Suit suit, int val)
311  {
312  assert(1 <= val && val <= 9);
313  assume_opt_out(1 <= val && val <= 9);
314 
315  return id34Of(val, suit);
316  }
317 
318  static constexpr int id34Of(int val, Suit suit)
319  {
320  return suit == Suit::F ? 27 + val - 1
321  : suit == Suit::Y ? 31 + val - 1
322  : 9 * static_cast<int>(suit) + val - 1;
323  }
324 
325 private:
326  int mId34;
327 #ifndef NDEBUG
328  bool mInitialized = true;
329 #endif
330 };
331 
332 inline std::ostream &operator<<(std::ostream &os, T34 t)
333 {
334  return os << t.str34();
335 }
336 
337 template<size_t MAX>
338 inline std::ostream &operator<<(std::ostream &os, const util::Stactor<T34, MAX> &ts)
339 {
340  for (size_t i = 0; i < ts.size(); i++) {
341  os << ts[i].val();
342  if (i < ts.size() - 1 && ts[i].suit() != ts[i + 1].suit())
343  os << T34::charOf(ts[i].suit());
344  }
345 
346  os << T34::charOf(ts.back().suit());
347 
348  return os;
349 }
350 
351 
352 
353 class T37 : public T34
354 {
355 public:
356  static bool isValidStr(const char *str)
357  {
358  if (str[2] != '\0')
359  return false;
360 
361  switch (str[1]) {
362  case 'm':
363  case 'p':
364  case 's':
365  return '0' <= str[0] && str[0] <= '9';
366  case 'f':
367  return '1' <= str[0] && str[0] <= '4';
368  case 'y':
369  return '1' <= str[0] && str[0] <= '3';
370  default:
371  return false;
372  }
373  }
374 
376  T37()
377  : T34()
378  {
379  }
380 
381  T37(const T37 &copy) = default;
382 
383  explicit T37(int id34)
384  : T34(id34)
385  , mAka5(false)
386  {
387  }
388 
389  explicit T37(Suit suit, int val)
390  : T34(suit, val == 0 ? 5 : val)
391  , mAka5(val == 0)
392  {
393  }
394 
395  constexpr explicit T37(int val, Suit suit)
396  : T34(val == 0 ? 5 : val, suit)
397  , mAka5(val == 0)
398  {
399  }
400 
401  explicit T37(const char *str)
402  : T34(suitOf(str[1]), str[0] == '0' ? 5 : str[0] - '0')
403  , mAka5(str[0] == '0')
404  {
405  assert(str[2] == '\0');
406  }
407 
413  const char *str37() const
414  {
415  if (isAka5()) {
416  switch (suit()) {
417  case Suit::M: return "0m";
418  case Suit::P: return "0p";
419  case Suit::S: return "0s";
420  default: unreached();
421  }
422  } else {
423  return T34::str34();
424  }
425  }
426 
427  bool isAka5() const
428  {
429  return mAka5;
430  }
431 
432  bool looksSame(const T37 &rhs) const
433  {
434  return id34() == rhs.id34() && mAka5 == rhs.mAka5;
435  }
436 
437  T37 toAka5() const
438  {
439  assert(val() == 5);
440  assume_opt_out(val() == 5);
441  return T37(id34(), true);
442  }
443 
444  T37 toInverse5() const
445  {
446  assert(val() == 5);
447  assume_opt_out(val() == 5);
448  return T37(id34(), !mAka5);
449  }
450 
451 private:
452  explicit T37(int id34, bool aka5)
453  : T34(id34)
454  , mAka5(aka5)
455  {
456  }
457 
458 private:
459  bool mAka5;
460 };
461 
462 inline int operator%(const util::Stactor<T37, 5> &inds, const T37 &d)
463 {
464  int s = 0;
465  for (const T37 &ind : inds)
466  s += (ind % d);
467 
468  return s;
469 }
470 
471 inline std::ostream &operator<<(std::ostream &os, const T37 &t)
472 {
473  return os << t.str37();
474 }
475 
476 template<size_t MAX>
477 inline std::ostream &operator<<(std::ostream &os, const util::Stactor<T37, MAX> &ts)
478 {
479  for (size_t i = 0; i < ts.size(); i++) {
480  os << (ts[i].isAka5() ? 0 : ts[i].val());
481  if (i < ts.size() - 1 && ts[i].suit() != ts[i + 1].suit())
482  os << T34::charOf(ts[i].suit());
483  }
484 
485  os << T34::charOf(ts.back().suit());
486 
487  return os;
488 }
489 
490 
491 
492 namespace tiles34
493 {
494 
495 
496 
497 constexpr T34 operator""_m(unsigned long long val)
498 {
499  return T34(static_cast<int>(val), Suit::M);
500 }
501 
502 constexpr T34 operator""_p(unsigned long long val)
503 {
504  return T34(static_cast<int>(val), Suit::P);
505 }
506 
507 constexpr T34 operator""_s(unsigned long long val)
508 {
509  return T34(static_cast<int>(val), Suit::S);
510 }
511 
512 constexpr T34 operator""_f(unsigned long long val)
513 {
514  return T34(static_cast<int>(val), Suit::F);
515 }
516 
517 constexpr T34 operator""_y(unsigned long long val)
518 {
519  return T34(static_cast<int>(val), Suit::Y);
520 }
521 
522 const std::array<T34, 13> YAO13
523 {
524  1_m, 9_m, 1_p, 9_p, 1_s, 9_s,
525  1_f, 2_f, 3_f, 4_f, 1_y, 2_y, 3_y
526 };
527 
528 const std::array<T34, 7> Z7
529 {
530  1_f, 2_f, 3_f, 4_f, 1_y, 2_y, 3_y
531 };
532 
533 constexpr IntRange<T34, 34> ALL34;
534 
535 inline util::Stactor<T34, 34> toStactor(std::bitset<34> bs)
536 {
538  for (T34 t : ALL34)
539  if (bs[t.uId34()])
540  res.emplaceBack(t);
541 
542  return res;
543 }
544 
545 
546 
547 } // namespace tiles34
548 
549 
550 
551 namespace tiles37
552 {
553 
554 
555 
556 constexpr T37 operator""_m(unsigned long long val)
557 {
558  return T37(static_cast<int>(val), Suit::M);
559 }
560 
561 constexpr T37 operator""_p(unsigned long long val)
562 {
563  return T37(static_cast<int>(val), Suit::P);
564 }
565 
566 constexpr T37 operator""_s(unsigned long long val)
567 {
568  return T37(static_cast<int>(val), Suit::S);
569 }
570 
571 constexpr T37 operator""_f(unsigned long long val)
572 {
573  return T37(static_cast<int>(val), Suit::F);
574 }
575 
576 constexpr T37 operator""_y(unsigned long long val)
577 {
578  return T37(static_cast<int>(val), Suit::Y);
579 }
580 
581 const std::array<T37, 37> ORDER37
582 {
583  1_m, 2_m, 3_m, 4_m, 0_m, 5_m, 6_m, 7_m, 8_m, 9_m,
584  1_p, 2_p, 3_p, 4_p, 0_p, 5_p, 6_p, 7_p, 8_p, 9_p,
585  1_s, 2_s, 3_s, 4_s, 0_s, 5_s, 6_s, 7_s, 8_s, 9_s,
586  1_f, 2_f, 3_f, 4_f, 1_y, 2_y, 3_y
587 };
588 
589 
590 
591 } // namespace tiles37
592 
593 
594 
595 } // namespace saki
596 
597 
598 
599 #endif // SAKI_TILE_H
T37()
Garbage value.
Definition: tile.h:376
Definition: tile.h:25
Definition: int_iter.h:54
const char * str37() const
String representation of the tile.
Definition: tile.h:413
Definition: ai.cpp:18
Definition: tile.h:353
Stactor = statically allocated vector.
Definition: stactor.h:247