mahc/
hand.rs

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    /// Prevalent or round wind.
16    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        // check if last group contains the winning tile
46        // FUCK handling kokuushi
47        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            // last group should not be open
61            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                    // TODO(zhiyao): A hacky way to check for kokushi.
87                    // Maybe we should find a more explicit way.
88                    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        // NOTE: Strings are complicated in Rust and needs evaluation about how to iterate over one. Because the string is expected to contain ASCII characters, `.chars()` should be okay.
113        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    /// Calculate the fu types in the hand.
128    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        //meld fu cal
143        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        //fu wait cal
202        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        //handling pinfu :(
218        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    /// Get the dora count in the hand from dora indicator tiles.
238    ///
239    /// # Examples
240    ///
241    /// ```rust
242    /// use mahc::hand::Hand;
243    /// use mahc::tile::Tile;
244    /// let hand = Hand::new_from_strings(
245    ///     vec![
246    ///         "123p".to_string(),
247    ///         "505s".to_string(),
248    ///         "EEEw".to_string(),
249    ///         "9999m".to_string(),
250    ///         "rrd".to_string(),
251    ///     ],
252    ///     "rd".to_string(),
253    ///     "Ew".to_string(),
254    ///     "Ew".to_string(),
255    /// )
256    /// .unwrap();
257    /// let doras: Vec<Tile> = vec![
258    ///    "1p".to_string().try_into().unwrap(),
259    ///    "4s".to_string().try_into().unwrap(),
260    ///    "Nw".to_string().try_into().unwrap(),
261    ///    "8m".to_string().try_into().unwrap(),
262    ///    "gd".to_string().try_into().unwrap(),
263    /// ];
264    ///
265    /// let dora = hand.get_dora_count(&Some(doras));
266    /// assert_eq!(dora, 14);
267    /// ```
268    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    /// Get the groups in the hand.
309    pub fn groups(&self) -> Vec<&TileGroup> {
310        self.groups.iter().collect()
311    }
312
313    /// Get the sequence groups in the hand.
314    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    /// Get the triplet groups in the hand.
322    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    /// Get the kan groups in the hand.
330    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    /// Get the pair groups in the hand.
338    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    /// Get the groups with no shape in the hand.
346    ///
347    /// This can be used to check for kokushi musou (thirteen orphans).
348    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    /// Get the winning tile the completes the hand.
356    pub fn win_tile(&self) -> &Tile {
357        &self.win_tile
358    }
359
360    /// Get the seat wind.
361    pub fn seat_tile(&self) -> &Tile {
362        &self.seat_tile
363    }
364
365    /// Get the prevalent wind.
366    pub fn prev_tile(&self) -> &Tile {
367        &self.prev_tile
368    }
369
370    /// Get the state of whether or not the hand has been opened.
371    pub fn is_open(&self) -> bool {
372        self.isopen
373    }
374
375    //yaku validation
376
377    /// Check if the hand only contains simple tiles -- no terminal or honor tiles.
378    pub fn is_tanyao(&self) -> bool {
379        self.groups
380            .iter()
381            .all(|group| !group.is_terminal() && !group.is_honor())
382    }
383
384    /// Check if the hand contains two unique identical sequences.
385    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    /// Check if the hand contains two identical sequences.
407    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    /// Check if the hand contains value honors.
426    pub fn is_yakuhai(&self) -> u16 {
427        // i do it like this because a single group can have multiple yakuhai
428        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    /// Check if the hand contains all triplets and kans.
446    pub fn is_toitoi(&self) -> bool {
447        self.triplets().len() + self.kans().len() == 4
448    }
449
450    /// Check if the hand contains three concealed triplets (including kans).
451    ///
452    /// If the last necessary triplet is formed from a ron, it is not considered concealed and sanankou is not granted.
453    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    /// Check if the hand contains a mixed triple sequence (ex: `123m 123p 123s`).
470    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    /// Check if the hand only contains tiles of one suit and any honor tiles.
492    ///
493    /// This is commonly referred to as a "half flush".
494    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    /// Check if the hand has two dragon triplets or quads and a pair of dragon tiles.
529    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    /// Check if the hand only contains groups with at least one terminal tile.
541    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    /// Check if the hand only contains terminal and honor tiles.
558    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    /// Check if the hand has three quads.
585    pub fn is_sankantsu(&self) -> bool {
586        self.kans().len() == 3
587    }
588
589    /// Check if the hand has three exact sequences of 1-2-3, 4-5-6, and 7-8-9 in the same suit.
590    pub fn is_ittsuu(&self) -> bool {
591        //there has GOTTO be a better way to do this
592        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    /// Check if the hand only contains groups with terminal or honor tiles; sequences are permitted as long as they contain a terminal.
608    ///
609    /// There must be at least one sequence, otherwise the hand would either be junchan or tsuuiisou.
610    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    /// Check if the hand consists of 7 unique pairs.
635    pub fn is_chiitoitsu(&self) -> bool {
636        self.pairs().len() == 7
637    }
638
639    /// Check if the hand has won with a self-draw.
640    ///
641    /// The hand must be closed.
642    pub fn is_menzentsumo(&self, tsumo: bool) -> bool {
643        !self.isopen && tsumo
644    }
645
646    /// Check if the hand has won with all sequences, with the last shape waiting to complete a sequence.
647    ///
648    /// The hand must be closed.
649    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    /// Check if the hand contains three triplets (or quads) of the same value across the three numerical suits (manzu, pinzu, and souzu).
664    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    /// Check if the hand only contains tiles of a single suit.
688    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    //yakuman
700
701    /// Check if the hand contains three dragon triplets (or quads).
702    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    /// Check if the hand contains four concealed triplets.
716    ///
717    /// If winning by ron, the last completed shape cannot be the triplet (e.g. only the pair).
718    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    /// Check if the hand contains four concealed triplets, with the last completed shape being the pair.
735    ///
736    /// This yaku can be awarded on a ron with a tanki (single pair) wait.
737    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    /// Check if the hand only contains terminal tiles.
754    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    /// Check if the hand only contains the 2-3-4-6-8 sou (bamboo) tiles and green dragon tile.
773    ///
774    /// This is commonly known as "all greens".
775    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    /// Check if the hand consists of 1112345678999 in the same suit, plus one additional tile of that suit.
800    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    /// Check if the hand consists of 1112345678999 in the same suit, plus one additional tile of that suit.
852    ///
853    /// This variant checks that the hand was completed with a 9-sided wait.
854    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    /// Check if the hand only consists of honor tiles.
871    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    /// Check if the hand only consists of honor tiles as seven pairs.
886    pub fn is_daichiishin(&self) -> bool {
887        self.is_tsuuiisou() && self.pairs().len() == 7
888    }
889
890    /// Check if the hand has four quads.
891    pub fn is_suukantsu(&self) -> bool {
892        self.kans().len() == 4
893    }
894
895    /// Check if the hand has three wind triplets (or quads) and a wind pair.
896    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    /// Check if the hand has four wind triplets (or quads).
909    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    /// Check if the hand has one of each type of terminal and honor tile and one additional terminal or honor tile.
919    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    /// Check if the hand has one of each type of terminal and honor tile and one additional terminal or honor tile, on a 13-sided wait.
952    pub fn is_kokushi13sided(&self) -> bool {
953        self.is_kokushi() && self.groups.last().unwrap().group_type() == GroupType::Pair
954    }
955
956    /// Check if the player is the dealer and has a winning hand in the uninterrupted first turn.
957    ///
958    /// Calling a kan counts as interrupting the turn order.
959    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    /// Check if the player is in a non-dealer seat and has a winning hand in the first non-interrupted turn.
967    ///
968    /// Calling a kan counts as interrupting the turn order.
969    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    /// get emoji format for hand
977    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        //should be chinitsu not honitsu
2236        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        //is open
2487        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        //is open
2503        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        //is open
2537        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        //is open
2553        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}