1mod hand_creator;
2
3use crate::constants::hand::{Hand, Mentsu};
4use crate::constants::tiles::{Tile, TileType};
5use crate::parser::pi_input::hand_creator::create_hand;
6use crate::parser::ValidationError::{InvalidHand, InvalidTileNumber};
7use crate::parser::{InputBase, PiInput, ValidationError};
8
9#[derive(Clone, Debug, Default)]
10struct PiHandColor {
11 dragon: Vec<u8>,
12 manzu: Vec<u8>,
13 pinzu: Vec<u8>,
14 souzu: Vec<u8>,
15 wind: Vec<u8>,
16}
17
18impl InputBase for PiInput {
21 fn validate(&self) -> Result<(), ValidationError> {
22 if self.naki.len() * 3 + self.hand.len() != 13 {
23 return Err(InvalidHand("Using too many tiles".to_string()));
24 }
25
26 for tile in &self.hand {
27 if tile.number < 1 {
28 return Err(InvalidTileNumber(
29 "Tile number should be greater than or equal to 1".to_string(),
30 tile.number,
31 ));
32 }
33 match tile.tile_type {
34 TileType::Wind => {
35 if tile.number > 4 {
36 return Err(InvalidTileNumber(
37 "Invalid wind number".to_string(),
38 tile.number,
39 ));
40 }
41 }
42 TileType::Dragon => {
43 if tile.number > 3 {
44 return Err(InvalidTileNumber(
45 "Invalid dragon number".to_string(),
46 tile.number,
47 ));
48 }
49 }
50 _ => {
51 if tile.number > 10 {
52 return Err(InvalidTileNumber(
53 "Invalid number tile number".to_string(),
54 tile.number,
55 ));
56 }
57 }
58 }
59 }
60
61 if self.naki.len() > 4 {
62 return Err(InvalidHand("Too many naki".to_string()));
63 }
64
65 for furo in &self.naki {
66 match furo {
67 Mentsu::Koutsu(_, x) | Mentsu::Shuntsu(_, x) => {
68 if !x {
69 return Err(InvalidHand(
70 "Unopened koutsu or shuntsu in naki".to_string(),
71 ));
72 }
73 }
74 Mentsu::Janto(_) => {
75 return Err(InvalidHand("Janto cannot be in naki".to_string()));
76 }
77 Mentsu::Kantsu(_, _) => continue,
78 }
79 }
80
81 Ok(())
82 }
83}
84
85impl PiInput {
86 pub fn to_mentsu(&self) -> Option<(Vec<Hand>, u8)> {
87 let (colors, red_count) = self.to_hand_color();
88 let head_candidate = self.find_toitu();
89 let mut menzen_hand: Vec<Hand> = Vec::new();
90 for head in head_candidate.iter() {
91 let hand = create_hand(&mut colors.clone(), head, &self.naki);
92 if hand.is_some() {
93 menzen_hand.push(hand.unwrap());
94 }
95 }
96
97 if menzen_hand.is_empty() {
98 None
99 } else {
100 Some((menzen_hand, red_count))
101 }
102 }
103
104 fn to_hand_color(&self) -> (PiHandColor, u8) {
105 let mut hand = PiHandColor::default();
106 let mut red_count = 0;
107
108 for &pi in self.hand.iter() {
109 let pi = if pi.number == 10 {
110 red_count += 1;
111 Tile {
112 number: 5,
113 tile_type: pi.tile_type.clone(),
114 }
115 } else {
116 pi
117 };
118
119 match pi.tile_type.clone() {
120 TileType::Dragon => hand.dragon.push(pi.number),
121 TileType::Manzu => hand.manzu.push(pi.number),
122 TileType::Pinzu => hand.pinzu.push(pi.number),
123 TileType::Souzu => hand.souzu.push(pi.number),
124 TileType::Wind => hand.wind.push(pi.number),
125 }
126 }
127
128 match self.hora.tile_type {
129 TileType::Dragon => hand.dragon.push(self.hora.number),
130 TileType::Manzu => hand.manzu.push(self.hora.number),
131 TileType::Pinzu => hand.pinzu.push(self.hora.number),
132 TileType::Souzu => hand.souzu.push(self.hora.number),
133 TileType::Wind => hand.wind.push(self.hora.number),
134 }
135
136 (hand, red_count)
137 }
138
139 fn find_toitu(&self) -> Vec<Tile> {
140 let mut seen = Vec::new();
141 let mut duplicates = Vec::new();
142
143 for tile in &self.hand {
144 if seen.contains(tile) {
145 if !duplicates.contains(tile) {
146 duplicates.push(tile.clone());
147 }
148 } else {
149 seen.push(tile.clone());
150 }
151 }
152
153 if seen.contains(&self.hora) {
154 duplicates.push(self.hora.clone());
155 }
156
157 duplicates
158 }
159}
160
161#[cfg(test)]
162mod validation_test {
163 use crate::constants::hand::Mentsu;
164 use crate::constants::tiles::{Tile, TileType};
165 use crate::parser::pi_input::PiInput;
166 use crate::parser::InputBase;
167 use crate::parser::ValidationError::{InvalidHand, InvalidTileNumber};
168
169 #[test]
170 fn menzen_input() {
171 let input = PiInput {
172 hand: vec![
173 Tile {
174 number: 1,
175 tile_type: TileType::Manzu,
176 },
177 Tile {
178 number: 1,
179 tile_type: TileType::Manzu,
180 },
181 Tile {
182 number: 1,
183 tile_type: TileType::Manzu,
184 },
185 Tile {
186 number: 3,
187 tile_type: TileType::Manzu,
188 },
189 Tile {
190 number: 3,
191 tile_type: TileType::Manzu,
192 },
193 Tile {
194 number: 3,
195 tile_type: TileType::Manzu,
196 },
197 Tile {
198 number: 5,
199 tile_type: TileType::Manzu,
200 },
201 Tile {
202 number: 5,
203 tile_type: TileType::Manzu,
204 },
205 Tile {
206 number: 5,
207 tile_type: TileType::Manzu,
208 },
209 Tile {
210 number: 7,
211 tile_type: TileType::Manzu,
212 },
213 Tile {
214 number: 7,
215 tile_type: TileType::Manzu,
216 },
217 Tile {
218 number: 7,
219 tile_type: TileType::Manzu,
220 },
221 Tile {
222 number: 9,
223 tile_type: TileType::Manzu,
224 },
225 ],
226 naki: vec![],
227 hora: Tile {
228 number: 9,
229 tile_type: TileType::Manzu,
230 },
231 };
232
233 assert_eq!(input.validate(), Ok(()));
234 }
235
236 #[test]
237 fn furo_input() {
238 let input = PiInput {
239 hand: vec![
240 Tile {
241 number: 1,
242 tile_type: TileType::Manzu,
243 },
244 Tile {
245 number: 1,
246 tile_type: TileType::Manzu,
247 },
248 Tile {
249 number: 1,
250 tile_type: TileType::Manzu,
251 },
252 Tile {
253 number: 3,
254 tile_type: TileType::Manzu,
255 },
256 Tile {
257 number: 3,
258 tile_type: TileType::Manzu,
259 },
260 Tile {
261 number: 3,
262 tile_type: TileType::Manzu,
263 },
264 Tile {
265 number: 5,
266 tile_type: TileType::Manzu,
267 },
268 Tile {
269 number: 5,
270 tile_type: TileType::Manzu,
271 },
272 Tile {
273 number: 5,
274 tile_type: TileType::Manzu,
275 },
276 Tile {
277 number: 9,
278 tile_type: TileType::Manzu,
279 },
280 ],
281 naki: vec![Mentsu::Koutsu(
282 Tile {
283 number: 7,
284 tile_type: TileType::Manzu,
285 },
286 true,
287 )],
288 hora: Tile {
289 number: 9,
290 tile_type: TileType::Manzu,
291 },
292 };
293
294 assert_eq!(input.validate(), Ok(()));
295 }
296
297 #[test]
298 fn invalid_pi() {
299 let too_big_suhai_input = PiInput {
300 hand: vec![
301 Tile {
302 number: 1,
303 tile_type: TileType::Manzu,
304 },
305 Tile {
306 number: 1,
307 tile_type: TileType::Manzu,
308 },
309 Tile {
310 number: 1,
311 tile_type: TileType::Manzu,
312 },
313 Tile {
314 number: 3,
315 tile_type: TileType::Manzu,
316 },
317 Tile {
318 number: 3,
319 tile_type: TileType::Manzu,
320 },
321 Tile {
322 number: 3,
323 tile_type: TileType::Manzu,
324 },
325 Tile {
326 number: 5,
327 tile_type: TileType::Manzu,
328 },
329 Tile {
330 number: 5,
331 tile_type: TileType::Manzu,
332 },
333 Tile {
334 number: 5,
335 tile_type: TileType::Manzu,
336 },
337 Tile {
338 number: 90,
339 tile_type: TileType::Manzu,
340 },
341 ],
342 naki: vec![Mentsu::Koutsu(
343 Tile {
344 number: 7,
345 tile_type: TileType::Manzu,
346 },
347 true,
348 )],
349 hora: Tile {
350 number: 9,
351 tile_type: TileType::Manzu,
352 },
353 };
354
355 assert_eq!(
356 too_big_suhai_input.validate(),
357 Err(InvalidTileNumber(
358 "Invalid number tile number".to_string(),
359 90
360 ))
361 );
362
363 let too_big_wind_input = PiInput {
364 hand: vec![
365 Tile {
366 number: 5,
367 tile_type: TileType::Wind,
368 },
369 Tile {
370 number: 1,
371 tile_type: TileType::Manzu,
372 },
373 Tile {
374 number: 1,
375 tile_type: TileType::Manzu,
376 },
377 Tile {
378 number: 3,
379 tile_type: TileType::Manzu,
380 },
381 Tile {
382 number: 3,
383 tile_type: TileType::Manzu,
384 },
385 Tile {
386 number: 3,
387 tile_type: TileType::Manzu,
388 },
389 Tile {
390 number: 5,
391 tile_type: TileType::Manzu,
392 },
393 Tile {
394 number: 5,
395 tile_type: TileType::Manzu,
396 },
397 Tile {
398 number: 5,
399 tile_type: TileType::Manzu,
400 },
401 Tile {
402 number: 9,
403 tile_type: TileType::Manzu,
404 },
405 ],
406 naki: vec![Mentsu::Koutsu(
407 Tile {
408 number: 7,
409 tile_type: TileType::Manzu,
410 },
411 true,
412 )],
413 hora: Tile {
414 number: 9,
415 tile_type: TileType::Manzu,
416 },
417 };
418
419 assert_eq!(
420 too_big_wind_input.validate(),
421 Err(InvalidTileNumber("Invalid wind number".to_string(), 5))
422 );
423
424 let too_big_dragon_input = PiInput {
425 hand: vec![
426 Tile {
427 number: 4,
428 tile_type: TileType::Dragon,
429 },
430 Tile {
431 number: 1,
432 tile_type: TileType::Manzu,
433 },
434 Tile {
435 number: 1,
436 tile_type: TileType::Manzu,
437 },
438 Tile {
439 number: 3,
440 tile_type: TileType::Manzu,
441 },
442 Tile {
443 number: 3,
444 tile_type: TileType::Manzu,
445 },
446 Tile {
447 number: 3,
448 tile_type: TileType::Manzu,
449 },
450 Tile {
451 number: 5,
452 tile_type: TileType::Manzu,
453 },
454 Tile {
455 number: 5,
456 tile_type: TileType::Manzu,
457 },
458 Tile {
459 number: 5,
460 tile_type: TileType::Manzu,
461 },
462 Tile {
463 number: 9,
464 tile_type: TileType::Manzu,
465 },
466 ],
467 naki: vec![Mentsu::Koutsu(
468 Tile {
469 number: 7,
470 tile_type: TileType::Manzu,
471 },
472 true,
473 )],
474 hora: Tile {
475 number: 9,
476 tile_type: TileType::Manzu,
477 },
478 };
479
480 assert_eq!(
481 too_big_dragon_input.validate(),
482 Err(InvalidTileNumber("Invalid dragon number".to_string(), 4))
483 );
484
485 let too_small_suhai_input = PiInput {
486 hand: vec![
487 Tile {
488 number: 0,
489 tile_type: TileType::Manzu,
490 },
491 Tile {
492 number: 1,
493 tile_type: TileType::Manzu,
494 },
495 Tile {
496 number: 1,
497 tile_type: TileType::Manzu,
498 },
499 Tile {
500 number: 3,
501 tile_type: TileType::Manzu,
502 },
503 Tile {
504 number: 3,
505 tile_type: TileType::Manzu,
506 },
507 Tile {
508 number: 3,
509 tile_type: TileType::Manzu,
510 },
511 Tile {
512 number: 5,
513 tile_type: TileType::Manzu,
514 },
515 Tile {
516 number: 5,
517 tile_type: TileType::Manzu,
518 },
519 Tile {
520 number: 5,
521 tile_type: TileType::Manzu,
522 },
523 Tile {
524 number: 9,
525 tile_type: TileType::Manzu,
526 },
527 ],
528 naki: vec![Mentsu::Koutsu(
529 Tile {
530 number: 7,
531 tile_type: TileType::Manzu,
532 },
533 true,
534 )],
535 hora: Tile {
536 number: 9,
537 tile_type: TileType::Manzu,
538 },
539 };
540
541 assert_eq!(
542 too_small_suhai_input.validate(),
543 Err(InvalidTileNumber(
544 "Tile number should be greater than or equal to 1".to_string(),
545 0
546 ))
547 );
548 }
549
550 #[test]
551 fn menzen_naki() {
552 let naki_anko_input = PiInput {
553 hand: vec![
554 Tile {
555 number: 1,
556 tile_type: TileType::Manzu,
557 },
558 Tile {
559 number: 1,
560 tile_type: TileType::Manzu,
561 },
562 Tile {
563 number: 1,
564 tile_type: TileType::Manzu,
565 },
566 Tile {
567 number: 3,
568 tile_type: TileType::Manzu,
569 },
570 Tile {
571 number: 3,
572 tile_type: TileType::Manzu,
573 },
574 Tile {
575 number: 3,
576 tile_type: TileType::Manzu,
577 },
578 Tile {
579 number: 5,
580 tile_type: TileType::Manzu,
581 },
582 Tile {
583 number: 5,
584 tile_type: TileType::Manzu,
585 },
586 Tile {
587 number: 5,
588 tile_type: TileType::Manzu,
589 },
590 Tile {
591 number: 9,
592 tile_type: TileType::Manzu,
593 },
594 ],
595 naki: vec![Mentsu::Koutsu(
596 Tile {
597 number: 7,
598 tile_type: TileType::Manzu,
599 },
600 false,
601 )],
602 hora: Tile {
603 number: 9,
604 tile_type: TileType::Manzu,
605 },
606 };
607
608 assert_eq!(
609 naki_anko_input.validate(),
610 Err(InvalidHand(
611 "Unopened koutsu or shuntsu in naki".to_string()
612 ))
613 );
614
615 let naki_janto_input = PiInput {
616 hand: vec![
617 Tile {
618 number: 1,
619 tile_type: TileType::Manzu,
620 },
621 Tile {
622 number: 1,
623 tile_type: TileType::Manzu,
624 },
625 Tile {
626 number: 1,
627 tile_type: TileType::Manzu,
628 },
629 Tile {
630 number: 3,
631 tile_type: TileType::Manzu,
632 },
633 Tile {
634 number: 3,
635 tile_type: TileType::Manzu,
636 },
637 Tile {
638 number: 3,
639 tile_type: TileType::Manzu,
640 },
641 Tile {
642 number: 5,
643 tile_type: TileType::Manzu,
644 },
645 Tile {
646 number: 5,
647 tile_type: TileType::Manzu,
648 },
649 Tile {
650 number: 5,
651 tile_type: TileType::Manzu,
652 },
653 Tile {
654 number: 9,
655 tile_type: TileType::Manzu,
656 },
657 ],
658 naki: vec![Mentsu::Janto(Tile {
659 number: 7,
660 tile_type: TileType::Manzu,
661 })],
662 hora: Tile {
663 number: 9,
664 tile_type: TileType::Manzu,
665 },
666 };
667
668 assert_eq!(
669 naki_janto_input.validate(),
670 Err(InvalidHand("Janto cannot be in naki".to_string()))
671 );
672 }
673}
674
675#[cfg(test)]
676mod convertor_test {
677 use crate::constants::hand::Mentsu::{Janto, Shuntsu};
678 use crate::constants::tiles::{Tile, TileType};
679 use crate::parser::pi_input::PiInput;
680 use crate::parser::InputBase;
681
682 #[test]
683 fn all_shuntsu_pinfu_iipeco() {
684 let input = PiInput {
685 hand: vec![
686 Tile {
687 number: 1,
688 tile_type: TileType::Manzu,
689 },
690 Tile {
691 number: 2,
692 tile_type: TileType::Manzu,
693 },
694 Tile {
695 number: 3,
696 tile_type: TileType::Manzu,
697 },
698 Tile {
699 number: 1,
700 tile_type: TileType::Manzu,
701 },
702 Tile {
703 number: 2,
704 tile_type: TileType::Manzu,
705 },
706 Tile {
707 number: 3,
708 tile_type: TileType::Manzu,
709 },
710 Tile {
711 number: 4,
712 tile_type: TileType::Pinzu,
713 },
714 Tile {
715 number: 5,
716 tile_type: TileType::Pinzu,
717 },
718 Tile {
719 number: 6,
720 tile_type: TileType::Pinzu,
721 },
722 Tile {
723 number: 9,
724 tile_type: TileType::Pinzu,
725 },
726 Tile {
727 number: 6,
728 tile_type: TileType::Souzu,
729 },
730 Tile {
731 number: 7,
732 tile_type: TileType::Souzu,
733 },
734 Tile {
735 number: 8,
736 tile_type: TileType::Souzu,
737 },
738 ],
739 naki: vec![],
740 hora: Tile {
741 number: 9,
742 tile_type: TileType::Pinzu,
743 },
744 };
745 let hand = vec![[
746 Janto(Tile {
747 number: 9,
748 tile_type: TileType::Pinzu,
749 }),
750 Shuntsu(
751 Tile {
752 number: 4,
753 tile_type: TileType::Pinzu,
754 },
755 false,
756 ),
757 Shuntsu(
758 Tile {
759 number: 1,
760 tile_type: TileType::Manzu,
761 },
762 false,
763 ),
764 Shuntsu(
765 Tile {
766 number: 1,
767 tile_type: TileType::Manzu,
768 },
769 false,
770 ),
771 Shuntsu(
772 Tile {
773 number: 6,
774 tile_type: TileType::Souzu,
775 },
776 false,
777 ),
778 ]];
779 assert!(input.validate().is_ok());
780 assert_eq!(input.to_mentsu(), Some((hand, 0)))
781 }
782
783 #[test]
784 fn all_shuntsu_pinfu_iipeco_red() {
785 let input = PiInput {
786 hand: vec![
787 Tile {
788 number: 1,
789 tile_type: TileType::Manzu,
790 },
791 Tile {
792 number: 2,
793 tile_type: TileType::Manzu,
794 },
795 Tile {
796 number: 3,
797 tile_type: TileType::Manzu,
798 },
799 Tile {
800 number: 1,
801 tile_type: TileType::Manzu,
802 },
803 Tile {
804 number: 2,
805 tile_type: TileType::Manzu,
806 },
807 Tile {
808 number: 3,
809 tile_type: TileType::Manzu,
810 },
811 Tile {
812 number: 4,
813 tile_type: TileType::Pinzu,
814 },
815 Tile {
816 number: 10,
817 tile_type: TileType::Pinzu,
818 },
819 Tile {
820 number: 6,
821 tile_type: TileType::Pinzu,
822 },
823 Tile {
824 number: 9,
825 tile_type: TileType::Pinzu,
826 },
827 Tile {
828 number: 6,
829 tile_type: TileType::Souzu,
830 },
831 Tile {
832 number: 7,
833 tile_type: TileType::Souzu,
834 },
835 Tile {
836 number: 8,
837 tile_type: TileType::Souzu,
838 },
839 ],
840 naki: vec![],
841 hora: Tile {
842 number: 9,
843 tile_type: TileType::Pinzu,
844 },
845 };
846 let hand = vec![[
847 Janto(Tile {
848 number: 9,
849 tile_type: TileType::Pinzu,
850 }),
851 Shuntsu(
852 Tile {
853 number: 4,
854 tile_type: TileType::Pinzu,
855 },
856 false,
857 ),
858 Shuntsu(
859 Tile {
860 number: 1,
861 tile_type: TileType::Manzu,
862 },
863 false,
864 ),
865 Shuntsu(
866 Tile {
867 number: 1,
868 tile_type: TileType::Manzu,
869 },
870 false,
871 ),
872 Shuntsu(
873 Tile {
874 number: 6,
875 tile_type: TileType::Souzu,
876 },
877 false,
878 ),
879 ]];
880 assert!(input.validate().is_ok());
881 assert_eq!(input.to_mentsu(), Some((hand, 1)))
882 }
883}