1pub mod error;
2use std::collections::HashSet;
3
4use crate::fu::Fu;
5use crate::suit::Suit;
6use crate::tile::*;
7use crate::tile_group::{GroupType, TileGroup};
8use error::HandErr;
9
10#[derive(Debug)]
11pub struct Hand {
12 groups: Vec<TileGroup>,
13 win_tile: Tile,
14 seat_tile: Tile,
15 prev_tile: Tile,
17 isopen: bool,
18}
19
20impl std::fmt::Display for Hand {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 let mut out = String::new();
23 for group in self.groups.iter() {
24 out.push_str(&group.to_string());
25 out.push(' ');
26 }
27 out.push_str("| ");
28 out.push_str(&self.win_tile.to_string());
29 write!(f, "{out}")
30 }
31}
32
33impl Hand {
34 pub fn new(
35 groups: Vec<TileGroup>,
36 win_tile: Tile,
37 seat_tile: Tile,
38 prev_tile: Tile,
39 ) -> Result<Self, HandErr> {
40 if let Some(x) = validate_hand_shape(&groups) {
41 return Err(x);
42 }
43 let isopen = groups.iter().filter(|g| g.isopen()).count() != 0;
44
45 let mut pair_count = 0;
48 let mut no_shape_count = 0;
49 for group in groups.iter() {
50 match group.group_type() {
51 GroupType::Pair => pair_count += 1,
52 GroupType::None => no_shape_count += 1,
53 _ => {}
54 }
55 }
56
57 let tilecount: u8 = groups.iter().map(|s| s.group_type().tile_count()).sum();
58 if tilecount == 14 {
59 let last_group = groups.last().unwrap();
60 if last_group.isopen() {
62 return Err(HandErr::InvalidShape);
63 }
64 match last_group.group_type() {
65 GroupType::Sequence => {
66 if win_tile.suit() != last_group.suit() {
67 return Err(HandErr::InvalidShape);
68 }
69
70 let win_int = win_tile.parse_u8()?;
71 let last_int = last_group.parse_u8()?;
72
73 if win_int != last_int && win_int != last_int + 1 && win_int != last_int + 2 {
74 return Err(HandErr::InvalidShape);
75 }
76 }
77 GroupType::Triplet | GroupType::Pair => {
78 if last_group.value() != win_tile.value()
79 || last_group.suit() != win_tile.suit()
80 {
81 return Err(HandErr::InvalidShape);
82 }
83 }
84 GroupType::Kan => return Err(HandErr::InvalidShape),
85 GroupType::None => {
86 if !(no_shape_count == 12 && pair_count == 1) {
89 return Err(HandErr::InvalidShape);
90 }
91 }
92 }
93 }
94
95 Ok(Hand {
96 groups,
97 win_tile,
98 seat_tile,
99 prev_tile,
100 isopen,
101 })
102 }
103
104 pub fn new_from_strings(
105 tiles: Vec<String>,
106 win: String,
107 prev: String,
108 seat: String,
109 ) -> Result<Self, HandErr> {
110 let mut tile_groups: Vec<TileGroup> = Vec::new();
111
112 for i in &tiles {
114 let tile: TileGroup = i.to_string().try_into()?;
115 tile_groups.push(tile);
116 }
117
118 let win_tile: Tile = win.try_into()?;
119 let seat_tile: Tile = seat.try_into()?;
120 let prev_tile: Tile = prev.try_into()?;
121
122 let hand = Hand::new(tile_groups, win_tile, seat_tile, prev_tile)?;
123
124 Ok(hand)
125 }
126
127 pub fn calculate_fu(&self, tsumo: bool) -> Vec<Fu> {
129 let mut fu_types: Vec<Fu> = vec![];
130 if self.is_chiitoitsu() {
131 return vec![Fu::BasePointsChitoi];
132 }
133
134 fu_types.push(Fu::BasePoints);
135
136 if tsumo {
137 fu_types.push(Fu::Tsumo);
138 } else if !self.is_open() {
139 fu_types.push(Fu::ClosedRon);
140 }
141
142 for tile_group in self.triplets() {
144 let group_is_terminal_or_honor = tile_group.is_honor() || tile_group.is_terminal();
145
146 if tile_group == self.groups.last().unwrap() {
147 if tsumo {
148 if group_is_terminal_or_honor {
149 fu_types.push(Fu::NonSimpleClosedTriplet);
150 } else {
151 fu_types.push(Fu::SimpleClosedTriplet);
152 }
153 } else if group_is_terminal_or_honor {
154 fu_types.push(Fu::NonSimpleOpenTriplet);
155 } else {
156 fu_types.push(Fu::SimpleOpenTriplet);
157 }
158 continue;
159 }
160
161 if !group_is_terminal_or_honor && tile_group.isopen() {
162 fu_types.push(Fu::SimpleOpenTriplet);
163 }
164
165 if !tile_group.isopen() {
166 if group_is_terminal_or_honor {
167 fu_types.push(Fu::NonSimpleClosedTriplet);
168 } else {
169 fu_types.push(Fu::SimpleClosedTriplet);
170 }
171 } else if group_is_terminal_or_honor {
172 fu_types.push(Fu::NonSimpleOpenTriplet);
173 }
174 }
175
176 for kan in &self.kans() {
177 let group_is_terminal_or_honor = kan.is_honor() || kan.is_terminal();
178
179 if group_is_terminal_or_honor {
180 if !kan.isopen() {
181 fu_types.push(Fu::NonSimpleClosedKan);
182 } else {
183 fu_types.push(Fu::NonSimpleOpenKan);
184 }
185 } else if !kan.isopen() {
186 fu_types.push(Fu::SimpleClosedKan);
187 } else {
188 fu_types.push(Fu::SimpleOpenKan);
189 }
190 }
191
192 for pair in self.pairs() {
193 if *pair.tiles()[0] == self.prev_tile
194 || *pair.tiles()[0] == self.seat_tile
195 || pair.tiles()[0].suit() == Suit::Dragon
196 {
197 fu_types.push(Fu::Toitsu);
198 }
199 }
200
201 if let Some(group) = self.groups.last() {
203 match group.group_type() {
204 GroupType::Pair => fu_types.push(Fu::SingleWait),
205 GroupType::Sequence => {
206 let mid_tile = group.tiles()[0].clone().get_next();
207 if *self.win_tile() == mid_tile
208 || !self.win_tile().is_terminal() && group.is_terminal()
209 {
210 fu_types.push(Fu::SingleWait);
211 }
212 }
213 _ => {}
214 }
215 }
216
217 if self.groups.len() != 13 && !self.isopen {
219 if tsumo {
220 if fu_types
221 .iter()
222 .all(|fu| matches!(fu, Fu::BasePoints | Fu::ClosedRon | Fu::Tsumo))
223 {
224 return vec![Fu::BasePoints];
225 }
226 } else if fu_types
227 .iter()
228 .all(|fu| matches!(fu, Fu::BasePoints | Fu::ClosedRon))
229 {
230 return vec![Fu::BasePoints, Fu::ClosedRon];
231 }
232 }
233
234 fu_types
235 }
236
237 pub fn get_dora_count(&self, dora_indicator_tiles: &Option<Vec<Tile>>) -> u32 {
269 let mut count = 0;
270 for group in &self.groups {
271 for tile in group.tiles().iter() {
272 if tile.is_aka() {
273 count += 1;
274 }
275 }
276 }
277 if dora_indicator_tiles.is_none() {
278 return count;
279 }
280 for tile in dora_indicator_tiles.clone().unwrap() {
281 let dora_tile = tile.clone().get_next();
282 for triplet in self.triplets() {
283 if *triplet.tiles()[0] == dora_tile {
284 count += 3;
285 }
286 }
287 for kan in self.kans() {
288 if *kan.tiles()[0] == dora_tile {
289 count += 4;
290 }
291 }
292 for pair in self.pairs() {
293 if *pair.tiles()[0] == dora_tile {
294 count += 2;
295 }
296 }
297 for sequence in self.sequences() {
298 for tile in sequence.tiles() {
299 if *tile == dora_tile {
300 count += 1
301 }
302 }
303 }
304 }
305 count
306 }
307
308 pub fn groups(&self) -> Vec<&TileGroup> {
310 self.groups.iter().collect()
311 }
312
313 pub fn sequences(&self) -> Vec<&TileGroup> {
315 self.groups
316 .iter()
317 .filter(|group| matches!(group.group_type(), GroupType::Sequence))
318 .collect()
319 }
320
321 pub fn triplets(&self) -> Vec<&TileGroup> {
323 self.groups
324 .iter()
325 .filter(|group| matches!(group.group_type(), GroupType::Triplet))
326 .collect()
327 }
328
329 pub fn kans(&self) -> Vec<&TileGroup> {
331 self.groups
332 .iter()
333 .filter(|group| matches!(group.group_type(), GroupType::Kan))
334 .collect()
335 }
336
337 pub fn pairs(&self) -> Vec<&TileGroup> {
339 self.groups
340 .iter()
341 .filter(|group| matches!(group.group_type(), GroupType::Pair))
342 .collect()
343 }
344
345 pub fn singles(&self) -> Vec<&TileGroup> {
349 self.groups
350 .iter()
351 .filter(|group| matches!(group.group_type(), GroupType::None))
352 .collect()
353 }
354
355 pub fn win_tile(&self) -> &Tile {
357 &self.win_tile
358 }
359
360 pub fn seat_tile(&self) -> &Tile {
362 &self.seat_tile
363 }
364
365 pub fn prev_tile(&self) -> &Tile {
367 &self.prev_tile
368 }
369
370 pub fn is_open(&self) -> bool {
372 self.isopen
373 }
374
375 pub fn is_tanyao(&self) -> bool {
379 self.groups
380 .iter()
381 .all(|group| !group.is_terminal() && !group.is_honor())
382 }
383
384 pub fn is_ryanpeikou(&self) -> bool {
386 if self.is_open() {
387 return false;
388 }
389
390 let mut seqs: Vec<&TileGroup> = self.sequences();
391
392 if seqs.len() != 4 {
393 return false;
394 }
395
396 seqs.sort();
397 if seqs[1] == seqs[2] {
398 seqs.dedup();
399 seqs.len() == 1
400 } else {
401 seqs.dedup();
402 seqs.len() == 2
403 }
404 }
405
406 pub fn is_iipeikou(&self) -> bool {
408 if self.is_open() {
409 return false;
410 }
411
412 let mut seqs: Vec<&TileGroup> = self.sequences();
413 seqs.sort();
414
415 let mut seqs_dedup: Vec<&TileGroup> = seqs.clone();
416 seqs_dedup.dedup();
417
418 match seqs.len() - seqs_dedup.len() {
419 1 => true,
420 2 => seqs[1] == seqs[2],
421 _ => false,
422 }
423 }
424
425 pub fn is_yakuhai(&self) -> u16 {
427 let mut count = 0;
429
430 for group in self.triplets().iter().chain(self.kans().iter()) {
431 if group.value() == self.prev_tile.value() {
432 count += 1;
433 }
434 if group.value() == self.seat_tile.value() {
435 count += 1;
436 }
437 if group.suit() == Suit::Dragon {
438 count += 1;
439 }
440 }
441
442 count
443 }
444
445 pub fn is_toitoi(&self) -> bool {
447 self.triplets().len() + self.kans().len() == 4
448 }
449
450 pub fn is_sanankou(&self, tsumo: bool) -> bool {
454 let mut closed_triplet_count = 0;
455
456 for triplet_group in self.triplets().iter().chain(self.kans().iter()) {
457 if !triplet_group.isopen() {
458 closed_triplet_count += 1;
459 }
460 }
461
462 if !tsumo && self.groups.last().unwrap().group_type() == GroupType::Triplet {
463 closed_triplet_count -= 1;
464 }
465
466 closed_triplet_count == 3
467 }
468
469 pub fn is_sanshokudoujun(&self) -> bool {
471 if self.sequences().len() < 3 {
472 return false;
473 }
474
475 let binding = self.sequences();
476 let mut seqs: Vec<_> = binding
477 .iter()
478 .map(|group| (group.value(), group.suit().clone()))
479 .collect();
480
481 seqs.sort();
482 seqs.dedup();
483
484 match seqs.len() {
485 3 => seqs[1].0 == seqs[2].0 && seqs[0].0 == seqs[1].0,
486 4 => seqs[1].0 == seqs[2].0 && (seqs[0].0 == seqs[1].0 || seqs[2].0 == seqs[3].0),
487 _ => false,
488 }
489 }
490
491 pub fn is_honitsu(&self) -> bool {
495 if self.groups.len() == 13 {
496 return false;
497 }
498
499 let mut has_honor = false;
500 let mut has_normal = false;
501 let mut suit: Option<Suit> = None;
502 for group in &self.groups {
503 if group.is_honor() {
504 has_honor = true;
505 } else {
506 has_normal = true;
507 suit = Some(group.suit());
508 }
509 }
510
511 if !has_normal || !has_honor {
512 return false;
513 }
514
515 if let Some(s) = suit {
516 for group in &self.groups {
517 if group.suit() != s && !group.is_honor() {
518 return false;
519 }
520 }
521 } else {
522 return false;
523 }
524
525 true
526 }
527
528 pub fn is_shousangen(&self) -> bool {
530 let dragon_count = self
531 .triplets()
532 .iter()
533 .chain(self.kans().iter())
534 .filter(|group| group.suit() == Suit::Dragon)
535 .count();
536
537 dragon_count == 2 && self.pairs()[0].suit() == Suit::Dragon
538 }
539
540 pub fn is_junchantaiyao(&self) -> bool {
542 if self.groups.len() == 13 {
543 return false;
544 }
545
546 if self
547 .groups
548 .iter()
549 .any(|group| group.is_honor() || !group.is_terminal())
550 {
551 return false;
552 }
553
554 !self.sequences().is_empty()
555 }
556
557 pub fn is_honroutou(&self) -> bool {
559 if self.groups.len() == 13 {
560 return false;
561 }
562
563 if !self.sequences().is_empty() {
564 return false;
565 }
566
567 let mut has_terminal: bool = false;
568 let mut has_honor: bool = false;
569 for group in self.groups.clone() {
570 for tile in group.tiles().iter() {
571 if tile.is_honor() {
572 has_honor = true
573 } else if tile.is_terminal() {
574 has_terminal = true
575 } else {
576 return false;
577 }
578 }
579 }
580
581 has_terminal && has_honor
582 }
583
584 pub fn is_sankantsu(&self) -> bool {
586 self.kans().len() == 3
587 }
588
589 pub fn is_ittsuu(&self) -> bool {
591 let suits = [Suit::Pinzu, Suit::Manzu, Suit::Souzu];
593 suits.iter().any(|suit| {
594 let values: Vec<String> = self
595 .sequences()
596 .iter()
597 .filter(|&x| x.suit() == *suit)
598 .map(|x| x.value().to_string())
599 .collect::<_>();
600
601 values.contains(&ONE_VALUE.to_string())
602 && values.contains(&FOUR_VALUE.to_string())
603 && values.contains(&SEVEN_VALUE.to_string())
604 })
605 }
606
607 pub fn is_chantaiyao(&self) -> bool {
611 if self.groups.len() == 13 {
612 return false;
613 }
614
615 if self.sequences().is_empty() {
616 return false;
617 }
618
619 let mut has_terminal = false;
620 let mut has_honor = false;
621 for group in self.groups.clone() {
622 if group.is_terminal() {
623 has_terminal = true;
624 } else if group.is_honor() {
625 has_honor = true;
626 } else {
627 return false;
628 }
629 }
630
631 has_terminal && has_honor
632 }
633
634 pub fn is_chiitoitsu(&self) -> bool {
636 self.pairs().len() == 7
637 }
638
639 pub fn is_menzentsumo(&self, tsumo: bool) -> bool {
643 !self.isopen && tsumo
644 }
645
646 pub fn is_pinfu(&self) -> bool {
650 if self.groups.len() == 13 {
651 return false;
652 }
653
654 if self.isopen {
655 return false;
656 }
657
658 self.calculate_fu(false)
659 .iter()
660 .all(|fu| matches!(fu, Fu::BasePoints | Fu::ClosedRon))
661 }
662
663 pub fn is_sanshokudoukou(&self) -> bool {
665 if self.triplets().len() + self.kans().len() < 3 {
666 return false;
667 }
668
669 let mut list_of_vals: Vec<String> = vec![];
670 for group in self.triplets().iter().chain(self.kans().iter()) {
671 list_of_vals.push(group.value().to_string());
672 }
673 list_of_vals.sort();
674
675 if list_of_vals[1] == list_of_vals[2] {
676 if list_of_vals[0] == list_of_vals[1] {
677 return true;
678 }
679 if list_of_vals.len() == 4 && list_of_vals[2] == list_of_vals[3] {
680 return true;
681 }
682 }
683
684 false
685 }
686
687 pub fn is_chinitsu(&self) -> bool {
689 if self.groups.len() == 13 {
690 return false;
691 }
692
693 let mut suits: Vec<Suit> = self.groups.iter().map(|x| x.suit().clone()).collect();
694 suits.dedup();
695
696 suits.len() == 1
697 }
698
699 pub fn is_daisangen(&self) -> bool {
703 let trips: Vec<Tile> = self
704 .triplets()
705 .iter()
706 .chain(self.kans().iter())
707 .map(|x| *x.tiles()[0])
708 .collect();
709
710 trips.contains(&Tile::Dragon(DValue::Red))
711 && trips.contains(&Tile::Dragon(DValue::Green))
712 && trips.contains(&Tile::Dragon(DValue::White))
713 }
714
715 pub fn is_suuankou(&self, tsumo: bool) -> bool {
719 if self.groups.len() == 13 {
720 return false;
721 }
722
723 if self.triplets().len() + self.kans().len() != 4 || self.isopen {
724 return false;
725 }
726
727 if !tsumo && self.groups.last().unwrap().group_type() == GroupType::Triplet {
728 return false;
729 }
730
731 true
732 }
733
734 pub fn is_suuankoutankiwait(&self) -> bool {
738 if self.groups.len() == 13 {
739 return false;
740 }
741
742 if self.triplets().len() + self.kans().len() != 4 || self.isopen {
743 return false;
744 }
745
746 if self.groups.last().unwrap().group_type() == GroupType::Pair {
747 return true;
748 }
749
750 false
751 }
752
753 pub fn is_chinroutou(&self) -> bool {
755 if self.groups.len() == 13 {
756 return false;
757 }
758
759 if self.kans().len() + self.triplets().len() != 4 {
760 return false;
761 }
762
763 for group in self.groups.clone() {
764 if !group.is_terminal() {
765 return false;
766 }
767 }
768
769 true
770 }
771
772 pub fn is_ryuuiisou(&self) -> bool {
776 if self.groups.len() == 13 {
777 return false;
778 }
779
780 for groups in self.groups.iter() {
781 for tile in groups.tiles().iter() {
782 if ![
783 Tile::Dragon(DValue::Green),
784 Tile::Sou(MpsValue::Two),
785 Tile::Sou(MpsValue::Three),
786 Tile::Sou(MpsValue::Four),
787 Tile::Sou(MpsValue::Six),
788 Tile::Sou(MpsValue::Eight),
789 ]
790 .contains(tile)
791 {
792 return false;
793 }
794 }
795 }
796 true
797 }
798
799 pub fn is_chuurenpoutou(&self) -> bool {
801 if self.groups.len() == 13 {
802 return false;
803 }
804
805 let suit: Suit = self.groups[0].suit().clone();
806 if self.triplets().len() != 2 || self.sequences().len() != 2 || self.pairs().len() != 1 {
807 return false;
808 }
809
810 for group in self.groups.clone() {
811 if group.suit() != suit {
812 return false;
813 }
814 }
815
816 let has_1 = self
817 .triplets()
818 .clone()
819 .iter()
820 .any(|i| i.value() == ONE_VALUE);
821 let has_9 = self
822 .triplets()
823 .clone()
824 .iter()
825 .any(|i| i.value() == NINE_VALUE);
826 if !has_1 || !has_9 {
827 return false;
828 }
829
830 let mut vals: Vec<u8> = vec![];
831 for sequence_group in self.sequences() {
832 let int = sequence_group.value().to_string().parse::<u8>().unwrap();
833 vals.push(int);
834 vals.push(int + 1);
835 vals.push(int + 2);
836 }
837
838 for pair_group in self.pairs() {
839 let int = pair_group.value().to_string().parse::<u8>().unwrap();
840 vals.push(int);
841 }
842
843 vals.sort();
844 if vals != [2, 3, 4, 5, 6, 7, 8] {
845 return false;
846 }
847
848 true
849 }
850
851 pub fn is_chuurenpoutou9sided(&self) -> bool {
855 if self.groups.len() == 13 {
856 return false;
857 }
858
859 if !self.is_chuurenpoutou() {
860 return false;
861 }
862
863 if self.groups.last().unwrap().group_type() != GroupType::Pair {
864 return false;
865 }
866
867 true
868 }
869
870 pub fn is_tsuuiisou(&self) -> bool {
872 if self.groups.len() == 13 {
873 return false;
874 }
875
876 for group in self.groups.clone() {
877 if !group.is_honor() {
878 return false;
879 }
880 }
881
882 true
883 }
884
885 pub fn is_daichiishin(&self) -> bool {
887 self.is_tsuuiisou() && self.pairs().len() == 7
888 }
889
890 pub fn is_suukantsu(&self) -> bool {
892 self.kans().len() == 4
893 }
894
895 pub fn is_shousuushii(&self) -> bool {
897 if self.pairs()[0].suit() != Suit::Wind {
898 return false;
899 }
900 self.triplets()
901 .iter()
902 .chain(self.kans().iter())
903 .filter(|i| i.suit() == Suit::Wind)
904 .count()
905 == 3
906 }
907
908 pub fn is_daisuushii(&self) -> bool {
910 self.triplets()
911 .iter()
912 .chain(self.kans().iter())
913 .filter(|i| i.suit() == Suit::Wind)
914 .count()
915 == 4
916 }
917
918 pub fn is_kokushi(&self) -> bool {
920 if self.singles().len() != 12 || self.pairs().len() != 1 {
921 return false;
922 }
923
924 let mut orphans = vec![
925 Tile::Man(MpsValue::One),
926 Tile::Man(MpsValue::Nine),
927 Tile::Sou(MpsValue::One),
928 Tile::Sou(MpsValue::Nine),
929 Tile::Pin(MpsValue::One),
930 Tile::Pin(MpsValue::Nine),
931 Tile::Wind(WValue::East),
932 Tile::Wind(WValue::South),
933 Tile::Wind(WValue::West),
934 Tile::Wind(WValue::North),
935 Tile::Dragon(DValue::Red),
936 Tile::Dragon(DValue::White),
937 Tile::Dragon(DValue::Green),
938 ];
939
940 for tile in self.groups.iter() {
941 if let Some(pos) = orphans.iter().position(|orphan| orphan == tile.tiles()[0]) {
942 orphans.remove(pos);
943 } else {
944 return false;
945 }
946 }
947
948 orphans.is_empty()
949 }
950
951 pub fn is_kokushi13sided(&self) -> bool {
953 self.is_kokushi() && self.groups.last().unwrap().group_type() == GroupType::Pair
954 }
955
956 pub fn is_tenhou(&self, tenhou: bool) -> bool {
960 if tenhou && *self.seat_tile() == Tile::Wind(WValue::East) {
961 return true;
962 }
963 false
964 }
965
966 pub fn is_chiihou(&self, tenhou: bool) -> bool {
970 if tenhou && *self.seat_tile() == Tile::Wind(WValue::East) {
971 return true;
972 }
973 false
974 }
975
976 pub fn get_emoji(&self) -> String {
978 let mut out = String::new();
979 for group in self.groups.iter() {
980 out.push_str(&group.get_emoji());
981 out.push(' ');
982 }
983 out.push_str("| ");
984 out.push_str(self.win_tile.get_emoji());
985 out
986 }
987}
988
989pub fn validate_hand_shape(groups: &Vec<TileGroup>) -> Option<HandErr> {
990 let mut full_shape_count = 0;
991 let mut pair_count = 0;
992 let mut no_shape_count = 0;
993
994 for group in groups {
995 match group.group_type() {
996 GroupType::Triplet | GroupType::Sequence | GroupType::Kan => full_shape_count += 1,
997 GroupType::Pair => pair_count += 1,
998 GroupType::None => no_shape_count += 1,
999 }
1000 }
1001
1002 let mut duplicate_group = false;
1003 let mut group_set = HashSet::new();
1004 for group in groups.iter() {
1005 if !group_set.insert(group) {
1006 duplicate_group = true
1007 }
1008 }
1009
1010 if !(full_shape_count == 4 && pair_count == 1
1011 || pair_count == 7 && !duplicate_group
1012 || pair_count == 1 && no_shape_count == 12)
1013 {
1014 return Some(HandErr::InvalidShape);
1015 }
1016 None
1017}
1018
1019#[cfg(test)]
1020mod tests {
1021 use super::Hand;
1022 use crate::{calc::get_valid_hand_shapes, hand::error::HandErr};
1023
1024 #[test]
1025 fn yaku_kokushi() {
1026 let out = Hand::new_from_strings(
1027 vec![
1028 "1s".to_string(),
1029 "2s".to_string(),
1030 "1m".to_string(),
1031 "9m".to_string(),
1032 "1p".to_string(),
1033 "9p".to_string(),
1034 "Ew".to_string(),
1035 "Sw".to_string(),
1036 "Ww".to_string(),
1037 "Nw".to_string(),
1038 "gd".to_string(),
1039 "rd".to_string(),
1040 "wwd".to_string(),
1041 ],
1042 "wd".to_string(),
1043 "Ew".to_string(),
1044 "Ww".to_string(),
1045 )
1046 .unwrap();
1047 assert!(!out.is_kokushi());
1048 let out = Hand::new_from_strings(
1049 vec![
1050 "1s".to_string(),
1051 "2s".to_string(),
1052 "1m".to_string(),
1053 "9m".to_string(),
1054 "1p".to_string(),
1055 "9p".to_string(),
1056 "Ew".to_string(),
1057 "Sw".to_string(),
1058 "Ww".to_string(),
1059 "Nw".to_string(),
1060 "gd".to_string(),
1061 "rd".to_string(),
1062 "wwd".to_string(),
1063 ],
1064 "wd".to_string(),
1065 "Ew".to_string(),
1066 "Ww".to_string(),
1067 )
1068 .unwrap();
1069 assert!(!out.is_kokushi());
1070 let out = Hand::new_from_strings(
1071 vec![
1072 "1s".to_string(),
1073 "9s".to_string(),
1074 "1m".to_string(),
1075 "9m".to_string(),
1076 "1p".to_string(),
1077 "9p".to_string(),
1078 "Ew".to_string(),
1079 "Sw".to_string(),
1080 "Ww".to_string(),
1081 "Nw".to_string(),
1082 "gd".to_string(),
1083 "rd".to_string(),
1084 "wwd".to_string(),
1085 ],
1086 "wd".to_string(),
1087 "Ew".to_string(),
1088 "Ww".to_string(),
1089 )
1090 .unwrap();
1091 assert!(out.is_kokushi());
1092 assert!(out.is_kokushi13sided());
1093 let tiles: Vec<_> = out
1094 .groups()
1095 .iter()
1096 .flat_map(|m| m.tiles())
1097 .map(|t| *t)
1098 .collect();
1099 assert_eq!(get_valid_hand_shapes(&tiles).len(), 1);
1100
1101 let out = Hand::new_from_strings(
1102 vec![
1103 "1s".to_string(),
1104 "9s".to_string(),
1105 "1m".to_string(),
1106 "9m".to_string(),
1107 "1p".to_string(),
1108 "9p".to_string(),
1109 "Ew".to_string(),
1110 "Sw".to_string(),
1111 "Ww".to_string(),
1112 "Nw".to_string(),
1113 "gd".to_string(),
1114 "wwd".to_string(),
1115 "rd".to_string(),
1116 ],
1117 "rd".to_string(),
1118 "Ew".to_string(),
1119 "Ww".to_string(),
1120 )
1121 .unwrap();
1122 assert!(out.is_kokushi());
1123 assert!(!out.is_kokushi13sided());
1124 let out = Hand::new_from_strings(
1125 vec![
1126 "9s".to_string(),
1127 "9s".to_string(),
1128 "1m".to_string(),
1129 "9m".to_string(),
1130 "1p".to_string(),
1131 "9p".to_string(),
1132 "Ew".to_string(),
1133 "Sw".to_string(),
1134 "Ww".to_string(),
1135 "Nw".to_string(),
1136 "gd".to_string(),
1137 "rd".to_string(),
1138 "wwd".to_string(),
1139 ],
1140 "wd".to_string(),
1141 "Ew".to_string(),
1142 "Ww".to_string(),
1143 )
1144 .unwrap();
1145 assert!(!out.is_kokushi());
1146 }
1147
1148 #[test]
1149 fn yaku_daisuushii() {
1150 let out = Hand::new_from_strings(
1151 vec![
1152 "EEEEw".to_string(),
1153 "SSSw".to_string(),
1154 "WWWw".to_string(),
1155 "NNNw".to_string(),
1156 "99s".to_string(),
1157 ],
1158 "9s".to_string(),
1159 "Ew".to_string(),
1160 "Ww".to_string(),
1161 )
1162 .unwrap();
1163 assert!(out.is_daisuushii());
1164 let tiles: Vec<_> = out
1165 .groups()
1166 .iter()
1167 .flat_map(|m| m.tiles())
1168 .map(|t| *t)
1169 .collect();
1170 assert_eq!(get_valid_hand_shapes(&tiles).len(), 1);
1171 let out = Hand::new_from_strings(
1172 vec![
1173 "EEEEw".to_string(),
1174 "SSw".to_string(),
1175 "WWWw".to_string(),
1176 "NNNw".to_string(),
1177 "999s".to_string(),
1178 ],
1179 "9s".to_string(),
1180 "Ew".to_string(),
1181 "Ww".to_string(),
1182 )
1183 .unwrap();
1184 assert!(!out.is_daisuushii());
1185 }
1186
1187 #[test]
1188 fn yaku_shousuushi() {
1189 let out = Hand::new_from_strings(
1190 vec![
1191 "EEEEw".to_string(),
1192 "SSw".to_string(),
1193 "WWWw".to_string(),
1194 "NNNw".to_string(),
1195 "999s".to_string(),
1196 ],
1197 "9s".to_string(),
1198 "Ew".to_string(),
1199 "Ww".to_string(),
1200 )
1201 .unwrap();
1202 assert!(out.is_shousuushii());
1203 let out = Hand::new_from_strings(
1204 vec![
1205 "EEEEw".to_string(),
1206 "22s".to_string(),
1207 "WWWw".to_string(),
1208 "NNNw".to_string(),
1209 "999s".to_string(),
1210 ],
1211 "9s".to_string(),
1212 "Ew".to_string(),
1213 "Ww".to_string(),
1214 )
1215 .unwrap();
1216 assert!(!out.is_shousuushii());
1217 let out = Hand::new_from_strings(
1218 vec![
1219 "EEEEw".to_string(),
1220 "SSSw".to_string(),
1221 "WWWw".to_string(),
1222 "NNNw".to_string(),
1223 "99s".to_string(),
1224 ],
1225 "9s".to_string(),
1226 "Ew".to_string(),
1227 "Ww".to_string(),
1228 )
1229 .unwrap();
1230 assert!(!out.is_shousuushii());
1231 }
1232
1233 #[test]
1234 fn yaku_suukantsu() {
1235 let out = Hand::new_from_strings(
1236 vec![
1237 "EEEEw".to_string(),
1238 "2222p".to_string(),
1239 "1111mo".to_string(),
1240 "7777s".to_string(),
1241 "99s".to_string(),
1242 ],
1243 "9s".to_string(),
1244 "Ew".to_string(),
1245 "Ww".to_string(),
1246 )
1247 .unwrap();
1248 assert!(out.is_suukantsu());
1249 let tiles: Vec<_> = out
1250 .groups()
1251 .iter()
1252 .flat_map(|m| m.tiles())
1253 .map(|t| *t)
1254 .collect();
1255 assert_eq!(get_valid_hand_shapes(&tiles).len(), 1);
1256
1257 let out = Hand::new_from_strings(
1258 vec![
1259 "EEEw".to_string(),
1260 "2222p".to_string(),
1261 "1111mo".to_string(),
1262 "7777s".to_string(),
1263 "99s".to_string(),
1264 ],
1265 "9s".to_string(),
1266 "Ew".to_string(),
1267 "Ww".to_string(),
1268 )
1269 .unwrap();
1270 assert!(!out.is_suukantsu());
1271 }
1272
1273 #[test]
1274 fn yaku_daichiishin() {
1275 let out = Hand::new_from_strings(
1276 vec![
1277 "SSw".to_string(),
1278 "rrd".to_string(),
1279 "ggd".to_string(),
1280 "wwd".to_string(),
1281 "NNw".to_string(),
1282 "SSw".to_string(),
1283 "EEw".to_string(),
1284 ],
1285 "Ew".to_string(),
1286 "Ew".to_string(),
1287 "Ww".to_string(),
1288 )
1289 .unwrap_err();
1290 assert_eq!(out, HandErr::InvalidShape);
1291
1292 let out = Hand::new_from_strings(
1293 vec![
1294 "EEw".to_string(),
1295 "SSw".to_string(),
1296 "WWw".to_string(),
1297 "NNw".to_string(),
1298 "rrd".to_string(),
1299 "wwd".to_string(),
1300 "ggd".to_string(),
1301 ],
1302 "gd".to_string(),
1303 "Ew".to_string(),
1304 "Ww".to_string(),
1305 )
1306 .unwrap();
1307 assert!(out.is_daichiishin());
1308
1309 let out = Hand::new_from_strings(
1310 vec![
1311 "WWw".to_string(),
1312 "NNNw".to_string(),
1313 "gggd".to_string(),
1314 "rrrd".to_string(),
1315 "EEEw".to_string(),
1316 ],
1317 "Ew".to_string(),
1318 "Ew".to_string(),
1319 "Ww".to_string(),
1320 )
1321 .unwrap();
1322 assert!(!out.is_daichiishin());
1323 }
1324
1325 #[test]
1326 fn yaku_tsuuiisou() {
1327 let out = Hand::new_from_strings(
1328 vec![
1329 "SSw".to_string(),
1330 "NNNw".to_string(),
1331 "gggd".to_string(),
1332 "rrrd".to_string(),
1333 "EEEw".to_string(),
1334 ],
1335 "Ew".to_string(),
1336 "Ew".to_string(),
1337 "Ww".to_string(),
1338 )
1339 .unwrap();
1340 assert!(out.is_tsuuiisou());
1341 let out = Hand::new_from_strings(
1342 vec![
1343 "11s".to_string(),
1344 "NNNw".to_string(),
1345 "gggd".to_string(),
1346 "rrrd".to_string(),
1347 "EEEw".to_string(),
1348 ],
1349 "Ew".to_string(),
1350 "Ew".to_string(),
1351 "Ww".to_string(),
1352 )
1353 .unwrap();
1354 assert!(!out.is_tsuuiisou());
1355 }
1356
1357 #[test]
1358 fn yaku_chuurenpoutou() {
1359 let out = Hand::new_from_strings(
1360 vec![
1361 "111s".to_string(),
1362 "234s".to_string(),
1363 "55s".to_string(),
1364 "678s".to_string(),
1365 "999s".to_string(),
1366 ],
1367 "9s".to_string(),
1368 "Ew".to_string(),
1369 "Ww".to_string(),
1370 )
1371 .unwrap();
1372 assert!(out.is_chuurenpoutou());
1373 let out = Hand::new_from_strings(
1374 vec![
1375 "111s".to_string(),
1376 "234s".to_string(),
1377 "55m".to_string(),
1378 "678s".to_string(),
1379 "999s".to_string(),
1380 ],
1381 "9s".to_string(),
1382 "Ew".to_string(),
1383 "Ww".to_string(),
1384 )
1385 .unwrap();
1386 assert!(!out.is_chuurenpoutou());
1387 let out = Hand::new_from_strings(
1388 vec![
1389 "123s".to_string(),
1390 "234s".to_string(),
1391 "555s".to_string(),
1392 "678s".to_string(),
1393 "99s".to_string(),
1394 ],
1395 "9s".to_string(),
1396 "Ew".to_string(),
1397 "Ww".to_string(),
1398 )
1399 .unwrap();
1400 assert!(!out.is_chuurenpoutou());
1401 let out = Hand::new_from_strings(
1402 vec![
1403 "111s".to_string(),
1404 "234s".to_string(),
1405 "678s".to_string(),
1406 "999s".to_string(),
1407 "55s".to_string(),
1408 ],
1409 "5s".to_string(),
1410 "Ew".to_string(),
1411 "Ww".to_string(),
1412 )
1413 .unwrap();
1414 assert!(out.is_chuurenpoutou9sided());
1415 let out = Hand::new_from_strings(
1416 vec![
1417 "111s".to_string(),
1418 "234s".to_string(),
1419 "678s".to_string(),
1420 "55s".to_string(),
1421 "999s".to_string(),
1422 ],
1423 "9s".to_string(),
1424 "Ew".to_string(),
1425 "Ww".to_string(),
1426 )
1427 .unwrap();
1428 assert!(!out.is_chuurenpoutou9sided());
1429 }
1430
1431 #[test]
1432 fn yaku_ryuuiisou() {
1433 let out = Hand::new_from_strings(
1434 vec![
1435 "234p".to_string(),
1436 "234s".to_string(),
1437 "66s".to_string(),
1438 "gggd".to_string(),
1439 "888s".to_string(),
1440 ],
1441 "8s".to_string(),
1442 "Ew".to_string(),
1443 "Ww".to_string(),
1444 )
1445 .unwrap();
1446 assert!(!out.is_ryuuiisou());
1447 let out = Hand::new_from_strings(
1448 vec![
1449 "234s".to_string(),
1450 "234s".to_string(),
1451 "66s".to_string(),
1452 "gggd".to_string(),
1453 "888s".to_string(),
1454 ],
1455 "8s".to_string(),
1456 "Ew".to_string(),
1457 "Ww".to_string(),
1458 )
1459 .unwrap();
1460 assert!(out.is_ryuuiisou());
1461 let out = Hand::new_from_strings(
1462 vec![
1463 "345s".to_string(),
1464 "234s".to_string(),
1465 "66s".to_string(),
1466 "gggd".to_string(),
1467 "888s".to_string(),
1468 ],
1469 "8s".to_string(),
1470 "Ew".to_string(),
1471 "Ww".to_string(),
1472 )
1473 .unwrap();
1474 assert!(!out.is_ryuuiisou());
1475
1476 let out = Hand::new_from_strings(
1477 vec![
1478 "234s".to_string(),
1479 "234s".to_string(),
1480 "666s".to_string(),
1481 "gggd".to_string(),
1482 "99s".to_string(),
1483 ],
1484 "9s".to_string(),
1485 "Ew".to_string(),
1486 "Ww".to_string(),
1487 )
1488 .unwrap();
1489 assert!(!out.is_ryuuiisou());
1490 }
1491
1492 #[test]
1493 fn yaku_chinroutou() {
1494 let out = Hand::new_from_strings(
1495 vec![
1496 "111so".to_string(),
1497 "1111m".to_string(),
1498 "999s".to_string(),
1499 "999p".to_string(),
1500 "11s".to_string(),
1501 ],
1502 "1s".to_string(),
1503 "Ew".to_string(),
1504 "Ww".to_string(),
1505 )
1506 .unwrap();
1507 assert!(out.is_chinroutou());
1508
1509 let out = Hand::new_from_strings(
1510 vec![
1511 "rrrd".to_string(),
1512 "1111m".to_string(),
1513 "999s".to_string(),
1514 "999p".to_string(),
1515 "11s".to_string(),
1516 ],
1517 "1s".to_string(),
1518 "Ew".to_string(),
1519 "Ww".to_string(),
1520 )
1521 .unwrap();
1522 assert!(!out.is_chinroutou());
1523
1524 let out = Hand::new_from_strings(
1525 vec![
1526 "111s".to_string(),
1527 "1111m".to_string(),
1528 "789s".to_string(),
1529 "999p".to_string(),
1530 "11s".to_string(),
1531 ],
1532 "1s".to_string(),
1533 "Ew".to_string(),
1534 "Ww".to_string(),
1535 )
1536 .unwrap();
1537 assert!(!out.is_chinroutou());
1538 }
1539
1540 #[test]
1541 fn yaku_suuankoutankiwait() {
1542 let out = Hand::new_from_strings(
1543 vec![
1544 "rrrd".to_string(),
1545 "888p".to_string(),
1546 "777s".to_string(),
1547 "111s".to_string(),
1548 "11m".to_string(),
1549 ],
1550 "1m".to_string(),
1551 "Ew".to_string(),
1552 "Ww".to_string(),
1553 )
1554 .unwrap();
1555 assert!(out.is_suuankoutankiwait());
1556 let out = Hand::new_from_strings(
1557 vec![
1558 "rrrd".to_string(),
1559 "888p".to_string(),
1560 "777s".to_string(),
1561 "11m".to_string(),
1562 "111s".to_string(),
1563 ],
1564 "1s".to_string(),
1565 "Ew".to_string(),
1566 "Ww".to_string(),
1567 )
1568 .unwrap();
1569 assert!(!out.is_suuankoutankiwait());
1570 }
1571
1572 #[test]
1573 fn yaku_suuankou() {
1574 let out = Hand::new_from_strings(
1575 vec![
1576 "rrrd".to_string(),
1577 "888p".to_string(),
1578 "777s".to_string(),
1579 "111s".to_string(),
1580 "11m".to_string(),
1581 ],
1582 "1m".to_string(),
1583 "Ew".to_string(),
1584 "Ww".to_string(),
1585 )
1586 .unwrap();
1587 assert!(out.is_suuankou(false));
1588
1589 let out = Hand::new_from_strings(
1590 vec![
1591 "rrrd".to_string(),
1592 "888p".to_string(),
1593 "777s".to_string(),
1594 "11m".to_string(),
1595 "111s".to_string(),
1596 ],
1597 "1s".to_string(),
1598 "Ew".to_string(),
1599 "Ww".to_string(),
1600 )
1601 .unwrap();
1602 assert!(out.is_suuankou(true));
1603 let out = Hand::new_from_strings(
1604 vec![
1605 "rrrd".to_string(),
1606 "888p".to_string(),
1607 "777s".to_string(),
1608 "11m".to_string(),
1609 "111s".to_string(),
1610 ],
1611 "1s".to_string(),
1612 "Ew".to_string(),
1613 "Ww".to_string(),
1614 )
1615 .unwrap();
1616 assert!(!out.is_suuankou(false));
1617 }
1618
1619 #[test]
1620 fn yaku_daisangen() {
1621 let out = Hand::new_from_strings(
1622 vec![
1623 "rrrd".to_string(),
1624 "gggd".to_string(),
1625 "wwwwd".to_string(),
1626 "88p".to_string(),
1627 "567p".to_string(),
1628 ],
1629 "6p".to_string(),
1630 "Ew".to_string(),
1631 "Ww".to_string(),
1632 )
1633 .unwrap();
1634 assert!(out.is_daisangen());
1635 let out = Hand::new_from_strings(
1636 vec![
1637 "rrrrd".to_string(),
1638 "ggggd".to_string(),
1639 "wwwwd".to_string(),
1640 "88p".to_string(),
1641 "567p".to_string(),
1642 ],
1643 "6p".to_string(),
1644 "Ew".to_string(),
1645 "Ww".to_string(),
1646 )
1647 .unwrap();
1648 assert!(out.is_daisangen());
1649
1650 let out = Hand::new_from_strings(
1651 vec![
1652 "rrrrd".to_string(),
1653 "gggd".to_string(),
1654 "wwd".to_string(),
1655 "888p".to_string(),
1656 "567p".to_string(),
1657 ],
1658 "6p".to_string(),
1659 "Ew".to_string(),
1660 "Ww".to_string(),
1661 )
1662 .unwrap();
1663 assert!(!out.is_daisangen());
1664 }
1665
1666 #[test]
1667 fn yaku_chinitsu() {
1668 let out = Hand::new_from_strings(
1669 vec![
1670 "222p".to_string(),
1671 "123p".to_string(),
1672 "345p".to_string(),
1673 "88p".to_string(),
1674 "567p".to_string(),
1675 ],
1676 "6p".to_string(),
1677 "Ew".to_string(),
1678 "Ww".to_string(),
1679 )
1680 .unwrap();
1681 assert!(out.is_chinitsu());
1682 let out = Hand::new_from_strings(
1683 vec![
1684 "222p".to_string(),
1685 "123p".to_string(),
1686 "345p".to_string(),
1687 "88s".to_string(),
1688 "567p".to_string(),
1689 ],
1690 "6p".to_string(),
1691 "Ew".to_string(),
1692 "Ww".to_string(),
1693 )
1694 .unwrap();
1695 assert!(!out.is_chinitsu());
1696 }
1697
1698 #[test]
1699 fn yaku_sanshokudoukou() {
1700 let out = Hand::new_from_strings(
1701 vec![
1702 "222p".to_string(),
1703 "222m".to_string(),
1704 "222s".to_string(),
1705 "11s".to_string(),
1706 "456m".to_string(),
1707 ],
1708 "6m".to_string(),
1709 "Ew".to_string(),
1710 "Ww".to_string(),
1711 )
1712 .unwrap();
1713 assert!(out.is_sanshokudoukou());
1714 let out = Hand::new_from_strings(
1715 vec![
1716 "222p".to_string(),
1717 "2222m".to_string(),
1718 "222s".to_string(),
1719 "3333s".to_string(),
1720 "11s".to_string(),
1721 ],
1722 "1s".to_string(),
1723 "Ew".to_string(),
1724 "Ww".to_string(),
1725 )
1726 .unwrap();
1727 assert!(out.is_sanshokudoukou());
1728 let out = Hand::new_from_strings(
1729 vec![
1730 "222p".to_string(),
1731 "333m".to_string(),
1732 "222s".to_string(),
1733 "11s".to_string(),
1734 "333s".to_string(),
1735 ],
1736 "3s".to_string(),
1737 "Ew".to_string(),
1738 "Ww".to_string(),
1739 )
1740 .unwrap();
1741 assert!(!out.is_sanshokudoukou());
1742 let out = Hand::new_from_strings(
1743 vec![
1744 "444s".to_string(),
1745 "444m".to_string(),
1746 "222s".to_string(),
1747 "11p".to_string(),
1748 "222p".to_string(),
1749 ],
1750 "2p".to_string(),
1751 "Ew".to_string(),
1752 "Ew".to_string(),
1753 )
1754 .unwrap();
1755 assert!(!out.is_sanshokudoukou());
1756 }
1757
1758 #[test]
1759 fn yaku_pinfu() {
1760 let out = Hand::new_from_strings(
1761 vec![
1762 "123p".to_string(),
1763 "678p".to_string(),
1764 "345s".to_string(),
1765 "11s".to_string(),
1766 "456m".to_string(),
1767 ],
1768 "6m".to_string(),
1769 "Ew".to_string(),
1770 "Ww".to_string(),
1771 )
1772 .unwrap();
1773 assert!(out.is_pinfu());
1774
1775 let out = Hand::new_from_strings(
1776 vec![
1777 "123p".to_string(),
1778 "678po".to_string(),
1779 "345s".to_string(),
1780 "11s".to_string(),
1781 "456m".to_string(),
1782 ],
1783 "5m".to_string(),
1784 "Ew".to_string(),
1785 "Ww".to_string(),
1786 )
1787 .unwrap();
1788 assert!(!out.is_pinfu());
1789 let out = Hand::new_from_strings(
1790 vec![
1791 "123p".to_string(),
1792 "678p".to_string(),
1793 "345s".to_string(),
1794 "11s".to_string(),
1795 "456m".to_string(),
1796 ],
1797 "5m".to_string(),
1798 "Ew".to_string(),
1799 "Ww".to_string(),
1800 )
1801 .unwrap();
1802 assert!(!out.is_pinfu());
1803 let out = Hand::new_from_strings(
1804 vec![
1805 "123p".to_string(),
1806 "678p".to_string(),
1807 "345s".to_string(),
1808 "11s".to_string(),
1809 "rrrd".to_string(),
1810 ],
1811 "rd".to_string(),
1812 "Ew".to_string(),
1813 "Ww".to_string(),
1814 )
1815 .unwrap();
1816 assert!(!out.is_pinfu());
1817 }
1818
1819 #[test]
1820 fn yaku_menzentsumo() {
1821 let out = Hand::new_from_strings(
1822 vec![
1823 "11s".to_string(),
1824 "22p".to_string(),
1825 "33p".to_string(),
1826 "44p".to_string(),
1827 "55p".to_string(),
1828 "66p".to_string(),
1829 "77p".to_string(),
1830 ],
1831 "7p".to_string(),
1832 "Ew".to_string(),
1833 "Ww".to_string(),
1834 )
1835 .unwrap();
1836 assert!(out.is_menzentsumo(true));
1837
1838 let out = Hand::new_from_strings(
1839 vec![
1840 "11s".to_string(),
1841 "22p".to_string(),
1842 "33p".to_string(),
1843 "44p".to_string(),
1844 "55p".to_string(),
1845 "66p".to_string(),
1846 "77p".to_string(),
1847 ],
1848 "7p".to_string(),
1849 "Ew".to_string(),
1850 "Ww".to_string(),
1851 )
1852 .unwrap();
1853 assert!(!out.is_menzentsumo(false));
1854
1855 let out = Hand::new_from_strings(
1856 vec![
1857 "123p".to_string(),
1858 "999p".to_string(),
1859 "111p".to_string(),
1860 "rrrdo".to_string(),
1861 "11s".to_string(),
1862 ],
1863 "1s".to_string(),
1864 "Ew".to_string(),
1865 "Ww".to_string(),
1866 )
1867 .unwrap();
1868 assert!(!out.is_menzentsumo(false));
1869 }
1870
1871 #[test]
1872 fn yaku_chiitoitsu() {
1873 let out = Hand::new_from_strings(
1874 vec![
1875 "11s".to_string(),
1876 "22p".to_string(),
1877 "33p".to_string(),
1878 "44p".to_string(),
1879 "55p".to_string(),
1880 "66p".to_string(),
1881 "77p".to_string(),
1882 ],
1883 "7p".to_string(),
1884 "Ew".to_string(),
1885 "Ww".to_string(),
1886 )
1887 .unwrap();
1888 assert!(out.is_chiitoitsu());
1889 let out = Hand::new_from_strings(
1890 vec![
1891 "123p".to_string(),
1892 "999p".to_string(),
1893 "111p".to_string(),
1894 "rrrdo".to_string(),
1895 "11s".to_string(),
1896 ],
1897 "1s".to_string(),
1898 "Ew".to_string(),
1899 "Ww".to_string(),
1900 )
1901 .unwrap();
1902 assert!(!out.is_chiitoitsu());
1903
1904 let out = Hand::new_from_strings(
1905 vec![
1906 "11s".to_string(),
1907 "22s".to_string(),
1908 "33s".to_string(),
1909 "44s".to_string(),
1910 "rrd".to_string(),
1911 "EEw".to_string(),
1912 "11s".to_string(),
1913 ],
1914 "1s".to_string(),
1915 "Ew".to_string(),
1916 "Ww".to_string(),
1917 )
1918 .unwrap_err();
1919 assert_eq!(out, HandErr::InvalidShape);
1920 }
1921
1922 #[test]
1923 fn yaku_chantaiyao() {
1924 let out = Hand::new_from_strings(
1925 vec![
1926 "123p".to_string(),
1927 "999p".to_string(),
1928 "111p".to_string(),
1929 "rrrdo".to_string(),
1930 "11s".to_string(),
1931 ],
1932 "1s".to_string(),
1933 "Ew".to_string(),
1934 "Ww".to_string(),
1935 )
1936 .unwrap();
1937 assert!(out.is_chantaiyao());
1938
1939 let out = Hand::new_from_strings(
1940 vec![
1941 "123p".to_string(),
1942 "999p".to_string(),
1943 "111p".to_string(),
1944 "999m".to_string(),
1945 "11s".to_string(),
1946 ],
1947 "1s".to_string(),
1948 "Ew".to_string(),
1949 "Ww".to_string(),
1950 )
1951 .unwrap();
1952 assert!(!out.is_chantaiyao());
1953 let out = Hand::new_from_strings(
1954 vec![
1955 "111s".to_string(),
1956 "999p".to_string(),
1957 "111p".to_string(),
1958 "999m".to_string(),
1959 "11s".to_string(),
1960 ],
1961 "1s".to_string(),
1962 "Ew".to_string(),
1963 "Ww".to_string(),
1964 )
1965 .unwrap();
1966 assert!(!out.is_chantaiyao());
1967 }
1968
1969 #[test]
1970 fn yaku_ittsuu() {
1971 let out = Hand::new_from_strings(
1972 vec![
1973 "123p".to_string(),
1974 "456p".to_string(),
1975 "789p".to_string(),
1976 "rrrdo".to_string(),
1977 "11s".to_string(),
1978 ],
1979 "1s".to_string(),
1980 "Ew".to_string(),
1981 "Ww".to_string(),
1982 )
1983 .unwrap();
1984 assert!(out.is_ittsuu());
1985
1986 let out = Hand::new_from_strings(
1987 vec![
1988 "789m".to_string(),
1989 "456m".to_string(),
1990 "123m".to_string(),
1991 "234m".to_string(),
1992 "11s".to_string(),
1993 ],
1994 "1s".to_string(),
1995 "Ew".to_string(),
1996 "Ww".to_string(),
1997 )
1998 .unwrap();
1999 assert!(out.is_ittsuu());
2000 let out = Hand::new_from_strings(
2001 vec![
2002 "123s".to_string(),
2003 "789s".to_string(),
2004 "456s".to_string(),
2005 "234m".to_string(),
2006 "11s".to_string(),
2007 ],
2008 "1s".to_string(),
2009 "Ew".to_string(),
2010 "Ww".to_string(),
2011 )
2012 .unwrap();
2013 assert!(out.is_ittsuu());
2014 let out = Hand::new_from_strings(
2015 vec![
2016 "123m".to_string(),
2017 "456m".to_string(),
2018 "678m".to_string(),
2019 "234m".to_string(),
2020 "11s".to_string(),
2021 ],
2022 "1s".to_string(),
2023 "Ew".to_string(),
2024 "Ww".to_string(),
2025 )
2026 .unwrap();
2027 assert!(!out.is_ittsuu());
2028 }
2029
2030 #[test]
2031 fn yaku_sankantsu() {
2032 let out = Hand::new_from_strings(
2033 vec![
2034 "9999so".to_string(),
2035 "123p".to_string(),
2036 "SSSSw".to_string(),
2037 "EEEEw".to_string(),
2038 "11s".to_string(),
2039 ],
2040 "1s".to_string(),
2041 "Ew".to_string(),
2042 "Ww".to_string(),
2043 )
2044 .unwrap();
2045 assert!(out.is_sankantsu());
2046 let out = Hand::new_from_strings(
2047 vec![
2048 "9999so".to_string(),
2049 "123p".to_string(),
2050 "SSSw".to_string(),
2051 "EEEEw".to_string(),
2052 "11s".to_string(),
2053 ],
2054 "1s".to_string(),
2055 "Ew".to_string(),
2056 "Ww".to_string(),
2057 )
2058 .unwrap();
2059 assert!(!out.is_sankantsu());
2060 }
2061
2062 #[test]
2063 fn yaku_honroutou() {
2064 let out = Hand::new_from_strings(
2065 vec![
2066 "999s".to_string(),
2067 "111p".to_string(),
2068 "SSSw".to_string(),
2069 "EEEw".to_string(),
2070 "11s".to_string(),
2071 ],
2072 "1s".to_string(),
2073 "Ew".to_string(),
2074 "Ww".to_string(),
2075 )
2076 .unwrap();
2077 assert!(out.is_honroutou());
2078
2079 let out = Hand::new_from_strings(
2080 vec![
2081 "999s".to_string(),
2082 "123p".to_string(),
2083 "SSSw".to_string(),
2084 "EEEw".to_string(),
2085 "11s".to_string(),
2086 ],
2087 "1s".to_string(),
2088 "Ew".to_string(),
2089 "Ww".to_string(),
2090 )
2091 .unwrap();
2092 assert!(!out.is_honroutou());
2093
2094 let out = Hand::new_from_strings(
2095 vec![
2096 "999s".to_string(),
2097 "111p".to_string(),
2098 "111s".to_string(),
2099 "999m".to_string(),
2100 "99p".to_string(),
2101 ],
2102 "9p".to_string(),
2103 "Ew".to_string(),
2104 "Ww".to_string(),
2105 )
2106 .unwrap();
2107 assert!(!out.is_honroutou());
2108 }
2109
2110 #[test]
2111 fn yaku_junchantaiyao() {
2112 let out = Hand::new_from_strings(
2113 vec![
2114 "123p".to_string(),
2115 "123p".to_string(),
2116 "999m".to_string(),
2117 "789s".to_string(),
2118 "11s".to_string(),
2119 ],
2120 "1s".to_string(),
2121 "Ew".to_string(),
2122 "Ww".to_string(),
2123 )
2124 .unwrap();
2125 assert!(out.is_junchantaiyao());
2126
2127 let out = Hand::new_from_strings(
2128 vec![
2129 "123p".to_string(),
2130 "123p".to_string(),
2131 "999m".to_string(),
2132 "789s".to_string(),
2133 "ggd".to_string(),
2134 ],
2135 "gd".to_string(),
2136 "Ew".to_string(),
2137 "Ww".to_string(),
2138 )
2139 .unwrap();
2140 assert!(!out.is_junchantaiyao());
2141
2142 let out = Hand::new_from_strings(
2143 vec![
2144 "111s".to_string(),
2145 "111m".to_string(),
2146 "999m".to_string(),
2147 "999s".to_string(),
2148 "11p".to_string(),
2149 ],
2150 "1p".to_string(),
2151 "Ew".to_string(),
2152 "Ww".to_string(),
2153 )
2154 .unwrap();
2155 assert!(!out.is_junchantaiyao());
2156 }
2157
2158 #[test]
2159 fn yaku_shousangen() {
2160 let out = Hand::new_from_strings(
2161 vec![
2162 "123p".to_string(),
2163 "222p".to_string(),
2164 "wwwwd".to_string(),
2165 "rrd".to_string(),
2166 "gggd".to_string(),
2167 ],
2168 "gd".to_string(),
2169 "Ew".to_string(),
2170 "Ww".to_string(),
2171 )
2172 .unwrap();
2173 assert!(out.is_shousangen());
2174 let out = Hand::new_from_strings(
2175 vec![
2176 "123p".to_string(),
2177 "222p".to_string(),
2178 "wwwwd".to_string(),
2179 "rrd".to_string(),
2180 "234p".to_string(),
2181 ],
2182 "4p".to_string(),
2183 "Ew".to_string(),
2184 "Ww".to_string(),
2185 )
2186 .unwrap();
2187 assert!(!out.is_shousangen());
2188 }
2189
2190 #[test]
2191 fn yaku_honitsu() {
2192 let out = Hand::new_from_strings(
2193 vec![
2194 "123p".to_string(),
2195 "222p".to_string(),
2196 "567p".to_string(),
2197 "rrd".to_string(),
2198 "gggd".to_string(),
2199 ],
2200 "gd".to_string(),
2201 "Ew".to_string(),
2202 "Ww".to_string(),
2203 )
2204 .unwrap();
2205 assert!(out.is_honitsu());
2206
2207 let out = Hand::new_from_strings(
2208 vec![
2209 "123p".to_string(),
2210 "222p".to_string(),
2211 "567m".to_string(),
2212 "rrd".to_string(),
2213 "gggd".to_string(),
2214 ],
2215 "gd".to_string(),
2216 "Ew".to_string(),
2217 "Ww".to_string(),
2218 )
2219 .unwrap();
2220 assert!(!out.is_honitsu());
2221
2222 let out = Hand::new_from_strings(
2223 vec![
2224 "123p".to_string(),
2225 "222p".to_string(),
2226 "567p".to_string(),
2227 "111p".to_string(),
2228 "33p".to_string(),
2229 ],
2230 "3p".to_string(),
2231 "Ew".to_string(),
2232 "Ww".to_string(),
2233 )
2234 .unwrap();
2235 assert!(!out.is_honitsu());
2237 }
2238
2239 #[test]
2240 fn yaku_sanankou() {
2241 let out = Hand::new_from_strings(
2242 vec![
2243 "123p".to_string(),
2244 "222p".to_string(),
2245 "555m".to_string(),
2246 "11s".to_string(),
2247 "333p".to_string(),
2248 ],
2249 "3p".to_string(),
2250 "Ew".to_string(),
2251 "Ww".to_string(),
2252 )
2253 .unwrap();
2254 assert!(!out.is_sanankou(false));
2255
2256 let out = Hand::new_from_strings(
2257 vec![
2258 "123p".to_string(),
2259 "222p".to_string(),
2260 "555m".to_string(),
2261 "11s".to_string(),
2262 "333p".to_string(),
2263 ],
2264 "3p".to_string(),
2265 "Ew".to_string(),
2266 "Ww".to_string(),
2267 )
2268 .unwrap();
2269 assert!(out.is_sanankou(true));
2270
2271 let out = Hand::new_from_strings(
2272 vec![
2273 "123p".to_string(),
2274 "123s".to_string(),
2275 "555m".to_string(),
2276 "11s".to_string(),
2277 "333p".to_string(),
2278 ],
2279 "3p".to_string(),
2280 "Ew".to_string(),
2281 "Ww".to_string(),
2282 )
2283 .unwrap();
2284 assert!(!out.is_sanankou(true));
2285 }
2286
2287 #[test]
2288 fn yaku_sanshokudoujun() {
2289 let out = Hand::new_from_strings(
2290 vec![
2291 "rrrdo".to_string(),
2292 "234p".to_string(),
2293 "234m".to_string(),
2294 "234s".to_string(),
2295 "11s".to_string(),
2296 ],
2297 "1s".to_string(),
2298 "Ew".to_string(),
2299 "Ww".to_string(),
2300 )
2301 .unwrap();
2302 assert!(out.is_sanshokudoujun());
2303
2304 let out = Hand::new_from_strings(
2305 vec![
2306 "678s".to_string(),
2307 "234p".to_string(),
2308 "234m".to_string(),
2309 "234s".to_string(),
2310 "11s".to_string(),
2311 ],
2312 "1s".to_string(),
2313 "Ew".to_string(),
2314 "Ww".to_string(),
2315 )
2316 .unwrap();
2317 assert!(out.is_sanshokudoujun());
2318
2319 let out = Hand::new_from_strings(
2320 vec![
2321 "111p".to_string(),
2322 "234p".to_string(),
2323 "234m".to_string(),
2324 "rrrd".to_string(),
2325 "11s".to_string(),
2326 ],
2327 "1s".to_string(),
2328 "Ew".to_string(),
2329 "Ww".to_string(),
2330 )
2331 .unwrap();
2332 assert!(!out.is_sanshokudoujun());
2333 let out = Hand::new_from_strings(
2334 vec![
2335 "678p".to_string(),
2336 "234s".to_string(),
2337 "234p".to_string(),
2338 "234p".to_string(),
2339 "11p".to_string(),
2340 ],
2341 "1p".to_string(),
2342 "Ew".to_string(),
2343 "Ew".to_string(),
2344 )
2345 .unwrap();
2346 assert!(!out.is_sanshokudoujun());
2347 let out = Hand::new_from_strings(
2348 vec![
2349 "234m".to_string(),
2350 "234s".to_string(),
2351 "234p".to_string(),
2352 "234p".to_string(),
2353 "11p".to_string(),
2354 ],
2355 "1p".to_string(),
2356 "Ew".to_string(),
2357 "Ew".to_string(),
2358 )
2359 .unwrap();
2360 assert!(out.is_sanshokudoujun());
2361 }
2362
2363 #[test]
2364 fn yaku_toitoi() {
2365 let out = Hand::new_from_strings(
2366 vec![
2367 "rrrdo".to_string(),
2368 "EEEw".to_string(),
2369 "gggd".to_string(),
2370 "222p".to_string(),
2371 "11s".to_string(),
2372 ],
2373 "1s".to_string(),
2374 "Ew".to_string(),
2375 "Ww".to_string(),
2376 )
2377 .unwrap();
2378 assert!(out.is_toitoi());
2379
2380 let out = Hand::new_from_strings(
2381 vec![
2382 "rrrdo".to_string(),
2383 "2222m".to_string(),
2384 "EEEw".to_string(),
2385 "11s".to_string(),
2386 "gggd".to_string(),
2387 ],
2388 "gd".to_string(),
2389 "Ew".to_string(),
2390 "Ww".to_string(),
2391 )
2392 .unwrap();
2393 assert!(out.is_toitoi());
2394
2395 let out = Hand::new_from_strings(
2396 vec![
2397 "rrrdo".to_string(),
2398 "2222m".to_string(),
2399 "EEEw".to_string(),
2400 "11s".to_string(),
2401 "456so".to_string(),
2402 ],
2403 "5s".to_string(),
2404 "Ew".to_string(),
2405 "Ww".to_string(),
2406 )
2407 .unwrap();
2408 assert!(!out.is_toitoi());
2409 }
2410
2411 #[test]
2412 fn yaku_yakuhai() {
2413 let out = Hand::new_from_strings(
2414 vec![
2415 "rrrdo".to_string(),
2416 "234m".to_string(),
2417 "22s".to_string(),
2418 "234m".to_string(),
2419 "678m".to_string(),
2420 ],
2421 "7m".to_string(),
2422 "Ew".to_string(),
2423 "Ww".to_string(),
2424 )
2425 .unwrap();
2426 assert_eq!(out.is_yakuhai(), 1);
2427 let out = Hand::new_from_strings(
2428 vec![
2429 "EEEEwo".to_string(),
2430 "234m".to_string(),
2431 "22s".to_string(),
2432 "234m".to_string(),
2433 "rrrd".to_string(),
2434 ],
2435 "rd".to_string(),
2436 "Ew".to_string(),
2437 "Ww".to_string(),
2438 )
2439 .unwrap();
2440 assert_eq!(out.is_yakuhai(), 2);
2441 let out = Hand::new_from_strings(
2442 vec![
2443 "3333mo".to_string(),
2444 "WWWw".to_string(),
2445 "22s".to_string(),
2446 "234m".to_string(),
2447 "678m".to_string(),
2448 ],
2449 "7m".to_string(),
2450 "Ew".to_string(),
2451 "Ww".to_string(),
2452 )
2453 .unwrap();
2454 assert_eq!(out.is_yakuhai(), 1);
2455 let out = Hand::new_from_strings(
2456 vec![
2457 "3333mo".to_string(),
2458 "222m".to_string(),
2459 "22s".to_string(),
2460 "234m".to_string(),
2461 "678m".to_string(),
2462 ],
2463 "7m".to_string(),
2464 "Ew".to_string(),
2465 "Ww".to_string(),
2466 )
2467 .unwrap();
2468 assert_eq!(out.is_yakuhai(), 0);
2469 }
2470
2471 #[test]
2472 fn yaku_ryanpeikou() {
2473 let out = Hand::new_from_strings(
2474 vec![
2475 "123s".to_string(),
2476 "123s".to_string(),
2477 "789m".to_string(),
2478 "789m".to_string(),
2479 "77m".to_string(),
2480 ],
2481 "7m".to_string(),
2482 "Ew".to_string(),
2483 "Ww".to_string(),
2484 )
2485 .unwrap();
2486 assert!(out.is_ryanpeikou());
2488
2489 let out = Hand::new_from_strings(
2490 vec![
2491 "123s".to_string(),
2492 "123s".to_string(),
2493 "789m".to_string(),
2494 "678m".to_string(),
2495 "77m".to_string(),
2496 ],
2497 "7m".to_string(),
2498 "Ew".to_string(),
2499 "Ww".to_string(),
2500 )
2501 .unwrap();
2502 assert!(!out.is_ryanpeikou());
2504
2505 let out = Hand::new_from_strings(
2506 vec![
2507 "123s".to_string(),
2508 "123s".to_string(),
2509 "123s".to_string(),
2510 "678m".to_string(),
2511 "77m".to_string(),
2512 ],
2513 "7m".to_string(),
2514 "Ew".to_string(),
2515 "Ww".to_string(),
2516 )
2517 .unwrap();
2518 assert!(!out.is_ryanpeikou());
2519 }
2520
2521 #[test]
2522 fn yaku_iipeko() {
2523 let out = Hand::new_from_strings(
2524 vec![
2525 "123s".to_string(),
2526 "123s".to_string(),
2527 "789m".to_string(),
2528 "789m".to_string(),
2529 "77m".to_string(),
2530 ],
2531 "7m".to_string(),
2532 "Ew".to_string(),
2533 "Ww".to_string(),
2534 )
2535 .unwrap();
2536 assert!(!out.is_iipeikou());
2538
2539 let out = Hand::new_from_strings(
2540 vec![
2541 "rrrdo".to_string(),
2542 "234m".to_string(),
2543 "22s".to_string(),
2544 "234m".to_string(),
2545 "678m".to_string(),
2546 ],
2547 "7m".to_string(),
2548 "Ew".to_string(),
2549 "Ww".to_string(),
2550 )
2551 .unwrap();
2552 assert!(!out.is_iipeikou());
2554
2555 let out = Hand::new_from_strings(
2556 vec![
2557 "rrrd".to_string(),
2558 "234m".to_string(),
2559 "22s".to_string(),
2560 "234m".to_string(),
2561 "678m".to_string(),
2562 ],
2563 "7m".to_string(),
2564 "Ew".to_string(),
2565 "Ww".to_string(),
2566 )
2567 .unwrap();
2568 assert!(out.is_iipeikou());
2569 }
2570
2571 #[test]
2572 fn yaku_tanyao() {
2573 let out = Hand::new_from_strings(
2574 vec![
2575 "rrrdo".to_string(),
2576 "5555mo".to_string(),
2577 "22s".to_string(),
2578 "8888s".to_string(),
2579 "678m".to_string(),
2580 ],
2581 "7m".to_string(),
2582 "Ew".to_string(),
2583 "Ww".to_string(),
2584 )
2585 .unwrap();
2586 assert!(!out.is_tanyao());
2587 let out = Hand::new_from_strings(
2588 vec![
2589 "333mo".to_string(),
2590 "5555mo".to_string(),
2591 "11s".to_string(),
2592 "8888s".to_string(),
2593 "678m".to_string(),
2594 ],
2595 "7m".to_string(),
2596 "Ew".to_string(),
2597 "Ww".to_string(),
2598 )
2599 .unwrap();
2600 assert!(!out.is_tanyao());
2601 let out = Hand::new_from_strings(
2602 vec![
2603 "555mo".to_string(),
2604 "678p".to_string(),
2605 "22s".to_string(),
2606 "333s".to_string(),
2607 "345m".to_string(),
2608 ],
2609 "4m".to_string(),
2610 "Ew".to_string(),
2611 "Ww".to_string(),
2612 )
2613 .unwrap();
2614 assert!(out.is_tanyao());
2615 }
2616
2617 #[test]
2618 fn invalid_group_sequence_not_in_order() {
2619 let out = Hand::new_from_strings(
2620 vec![
2621 "135m".to_string(),
2622 "SSSw".to_string(),
2623 "SSSw".to_string(),
2624 "SSSw".to_string(),
2625 "Sw".to_string(),
2626 ],
2627 "3s".to_string(),
2628 "3s".to_string(),
2629 "3s".to_string(),
2630 );
2631 assert_eq!(out.unwrap_err(), HandErr::InvalidGroup);
2632 }
2633
2634 #[test]
2635 fn invalid_group_size_too_small() {
2636 let out = Hand::new_from_strings(
2637 vec![
2638 "SSSw".to_string(),
2639 "SSSw".to_string(),
2640 "SSSw".to_string(),
2641 "Sw".to_string(),
2642 "ShSo".to_string(),
2643 ],
2644 "3s".to_string(),
2645 "3s".to_string(),
2646 "3s".to_string(),
2647 );
2648 assert_eq!(out.unwrap_err(), HandErr::InvalidSuit);
2649 }
2650
2651 #[test]
2652 fn invalid_group_size_too_big() {
2653 let out = Hand::new_from_strings(
2654 vec![
2655 "SSSSSw".to_string(),
2656 "SSSw".to_string(),
2657 "SSSw".to_string(),
2658 "SSw".to_string(),
2659 "ShSo".to_string(),
2660 ],
2661 "3s".to_string(),
2662 "3s".to_string(),
2663 "3s".to_string(),
2664 );
2665 assert_eq!(out.unwrap_err(), HandErr::InvalidGroup);
2666 }
2667
2668 #[test]
2669 fn invalid_suit() {
2670 let out = Hand::new_from_strings(
2671 vec![
2672 "hhhjo".to_string(),
2673 "SSSw".to_string(),
2674 "SSSw".to_string(),
2675 "SSSw".to_string(),
2676 "SSw".to_string(),
2677 ],
2678 "3s".to_string(),
2679 "3s".to_string(),
2680 "3s".to_string(),
2681 );
2682 assert_eq!(out.unwrap_err(), HandErr::InvalidSuit);
2683 }
2684
2685 #[test]
2686 fn hand_too_small() {
2687 let out = Hand::new_from_strings(
2688 vec!["SSSw".to_string()],
2689 "3s".to_string(),
2690 "3s".to_string(),
2691 "3s".to_string(),
2692 );
2693 assert_eq!(out.unwrap_err(), HandErr::InvalidShape);
2694 let out = Hand::new_from_strings(
2695 vec!["SSSw".to_string()],
2696 "3s".to_string(),
2697 "3s".to_string(),
2698 "3s".to_string(),
2699 );
2700 assert_eq!(out.unwrap_err(), HandErr::InvalidShape);
2701 }
2702 #[test]
2703 fn hand_too_big() {
2704 let out = Hand::new_from_strings(
2705 vec![
2706 "SSSw".to_string(),
2707 "SSSw".to_string(),
2708 "SSSw".to_string(),
2709 "SSSw".to_string(),
2710 "SSSw".to_string(),
2711 "SSw".to_string(),
2712 "SSSw".to_string(),
2713 ],
2714 "Sw".to_string(),
2715 "3s".to_string(),
2716 "3s".to_string(),
2717 );
2718 assert_eq!(out.unwrap_err(), HandErr::InvalidShape);
2719 }
2720}
2721
2722#[cfg(test)]
2723mod tile_group_tests {
2724 use super::Hand;
2725 use crate::suit::Suit;
2726 use crate::tile::*;
2727 use crate::tile_group::GroupType;
2728
2729 #[test]
2730 fn identify_pair() {
2731 let out = Hand::new_from_strings(
2732 vec![
2733 "SSSw".to_string(),
2734 "SSSw".to_string(),
2735 "SSSw".to_string(),
2736 "SSw".to_string(),
2737 "SSSw".to_string(),
2738 ],
2739 "Sw".to_string(),
2740 "3s".to_string(),
2741 "3s".to_string(),
2742 )
2743 .unwrap();
2744 assert_eq!(out.pairs()[0].value(), SOUTH_VALUE);
2745 assert_eq!(out.pairs()[0].group_type(), GroupType::Pair);
2746 assert_eq!(out.pairs()[0].suit(), Suit::Wind);
2747 assert!(!out.pairs()[0].isopen());
2748 }
2749
2750 #[test]
2751 #[allow(non_snake_case)]
2752 fn identify_tilegroup_closed_wind_trip_SSS() {
2753 let out = Hand::new_from_strings(
2754 vec![
2755 "SSSw".to_string(),
2756 "SSSw".to_string(),
2757 "SSSw".to_string(),
2758 "SSw".to_string(),
2759 "SSSw".to_string(),
2760 ],
2761 "Sw".to_string(),
2762 "3s".to_string(),
2763 "3s".to_string(),
2764 )
2765 .unwrap();
2766 assert_eq!(out.triplets()[0].value(), SOUTH_VALUE);
2767 assert_eq!(out.triplets()[0].group_type(), GroupType::Triplet);
2768 assert_eq!(out.triplets()[0].suit(), Suit::Wind);
2769 assert!(!out.triplets()[0].isopen());
2770 }
2771
2772 #[test]
2773 #[allow(non_snake_case)]
2774 fn identify_tilegroup_open_wind_kan_EEEE() {
2775 let out = Hand::new_from_strings(
2776 vec![
2777 "EEEEwo".to_string(),
2778 "SSSw".to_string(),
2779 "SSSw".to_string(),
2780 "SSw".to_string(),
2781 "SSSw".to_string(),
2782 ],
2783 "Sw".to_string(),
2784 "3s".to_string(),
2785 "3s".to_string(),
2786 )
2787 .unwrap();
2788 assert_eq!(out.kans()[0].value(), EAST_VALUE);
2789 assert_eq!(out.kans()[0].group_type(), GroupType::Kan);
2790 assert_eq!(out.kans()[0].suit(), Suit::Wind);
2791 assert!(out.kans()[0].isopen());
2792 }
2793
2794 #[test]
2795 #[allow(non_snake_case)]
2796 fn identify_tilegroup_closed_dragon_kan_RRRR() {
2797 let out = Hand::new_from_strings(
2798 vec![
2799 "rrrrd".to_string(),
2800 "rrrrd".to_string(),
2801 "SSw".to_string(),
2802 "rrrrd".to_string(),
2803 "rrrd".to_string(),
2804 ],
2805 "rd".to_string(),
2806 "3s".to_string(),
2807 "3s".to_string(),
2808 )
2809 .unwrap();
2810 assert_eq!(out.kans()[0].value(), RED_VALUE);
2811 assert_eq!(out.kans()[0].group_type(), GroupType::Kan);
2812 assert_eq!(out.kans()[0].suit(), Suit::Dragon);
2813 assert!(!out.kans()[0].isopen());
2814 }
2815
2816 #[test]
2817 fn identify_tilegroup_closed_manzu_trip_111() {
2818 let out = Hand::new_from_strings(
2819 vec![
2820 "111m".to_string(),
2821 "SSSw".to_string(),
2822 "SSSw".to_string(),
2823 "SSw".to_string(),
2824 "SSSw".to_string(),
2825 ],
2826 "Sw".to_string(),
2827 "3s".to_string(),
2828 "3s".to_string(),
2829 )
2830 .unwrap();
2831 assert_eq!(out.triplets()[0].value(), ONE_VALUE);
2832 assert_eq!(out.triplets()[0].group_type(), GroupType::Triplet);
2833 assert_eq!(out.triplets()[0].suit(), Suit::Manzu);
2834 assert!(!out.triplets()[0].isopen());
2835 }
2836
2837 #[test]
2838 fn identify_tilegroup_closed_souzu_seq_789() {
2839 let out = Hand::new_from_strings(
2840 vec![
2841 "789s".to_string(),
2842 "SSSw".to_string(),
2843 "SSw".to_string(),
2844 "SSSw".to_string(),
2845 "SSSw".to_string(),
2846 ],
2847 "Sw".to_string(),
2848 "3s".to_string(),
2849 "3s".to_string(),
2850 )
2851 .unwrap();
2852 assert_eq!(out.sequences()[0].value(), SEVEN_VALUE);
2853 assert_eq!(out.sequences()[0].group_type(), GroupType::Sequence);
2854 assert_eq!(out.sequences()[0].suit(), Suit::Souzu);
2855 assert!(!out.sequences()[0].isopen());
2856 }
2857
2858 #[test]
2859 fn identify_tilegroup_open_pinzu_234() {
2860 let out = Hand::new_from_strings(
2861 vec![
2862 "234po".to_string(),
2863 "SSSw".to_string(),
2864 "SSw".to_string(),
2865 "SSSw".to_string(),
2866 "SSSw".to_string(),
2867 ],
2868 "Sw".to_string(),
2869 "3s".to_string(),
2870 "3s".to_string(),
2871 )
2872 .unwrap();
2873 assert_eq!(out.sequences()[0].value(), TWO_VALUE);
2874 assert_eq!(out.sequences()[0].group_type(), GroupType::Sequence);
2875 assert_eq!(out.sequences()[0].suit(), Suit::Pinzu);
2876 assert!(out.sequences()[0].isopen());
2877 }
2878 #[test]
2879 fn dora_count_all() {
2880 let out = Hand::new_from_strings(
2881 vec![
2882 "123p".to_string(),
2883 "505s".to_string(),
2884 "EEEw".to_string(),
2885 "9999m".to_string(),
2886 "rrd".to_string(),
2887 ],
2888 "rd".to_string(),
2889 "Ew".to_string(),
2890 "Ew".to_string(),
2891 )
2892 .unwrap();
2893 let dora_1: Tile = "1p".to_string().try_into().unwrap();
2894 let dora_2: Tile = "4s".to_string().try_into().unwrap();
2895 let dora_3: Tile = "Nw".to_string().try_into().unwrap();
2896 let dora_4: Tile = "8m".to_string().try_into().unwrap();
2897 let dora_5: Tile = "gd".to_string().try_into().unwrap();
2898 let doras = vec![dora_1, dora_2, dora_3, dora_4, dora_5];
2899
2900 let dora = out.get_dora_count(&Some(doras));
2901 assert_eq!(dora, 14);
2902 }
2903 #[test]
2904 fn dora_count_aka() {
2905 let out = Hand::new_from_strings(
2906 vec![
2907 "123p".to_string(),
2908 "505s".to_string(),
2909 "EEEw".to_string(),
2910 "9999m".to_string(),
2911 "rrd".to_string(),
2912 ],
2913 "rd".to_string(),
2914 "Ew".to_string(),
2915 "Ew".to_string(),
2916 )
2917 .unwrap();
2918 let dora = out.get_dora_count(&None);
2919 assert_eq!(dora, 1);
2920 }
2921}