1use std::fmt::{Display, Formatter};
2use std::iter::zip;
3
4use riichi_elements::prelude::*;
5
6#[derive(Copy, Clone, Debug, Eq, PartialEq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(feature = "serde", serde(tag = "type", content = "wait"))]
18pub enum IrregularWait {
19 SevenPairs(Tile),
25
26 ThirteenOrphans(Tile),
33
34 ThirteenOrphansAll,
38}
39
40impl IrregularWait {
41 pub fn to_waiting_set(self) -> TileMask34 {
42 match self {
43 IrregularWait::SevenPairs(t) | IrregularWait::ThirteenOrphans(t)
44 => TileMask34(1u64 << (t.encoding() as u64)),
45 IrregularWait::ThirteenOrphansAll
46 => TileMask34(0b1111111_100000001_100000001_100000001),
47 }
48 }
49}
50
51impl Display for IrregularWait {
53 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
54 match self {
55 IrregularWait::SevenPairs(t) => write!(f, "SevenPairs({})", t),
56 IrregularWait::ThirteenOrphans(t) => write!(f, "ThirteenOrphans({})", t),
57 IrregularWait::ThirteenOrphansAll => write!(f, "ThirteenOrphansAll"),
58 }
59 }
60}
61
62pub fn detect_irregular_wait(keys: [u32; 4]) -> Option<IrregularWait> {
66 if let Some(tile) = detect_seven_pairs(keys) {
67 Some(IrregularWait::SevenPairs(tile))
68 } else {
69 detect_thirteen_orphans(keys)
70 }
71}
72
73fn detect_seven_pairs(keys: [u32; 4]) -> Option<Tile> {
75 let d = keys.map(one_two);
76 let num_twos = d[0].3 + d[1].3 + d[2].3 + d[3].3;
77 if num_twos != 6 { return None; }
78 let num_ones = d[0].1 + d[1].1 + d[2].1 + d[3].1;
79 if num_ones != 1 { return None; }
80 for i in 0..4 {
81 let ones = d[i].0;
82 if ones > 0 {
83 return Tile::from_encoding(ones.trailing_zeros() as u8 / 3 + (i as u8) * 9);
84 }
85 }
86 panic!()
87}
88
89fn detect_thirteen_orphans(keys: [u32; 4]) -> Option<IrregularWait> {
94 const MASK: [u32; 4] = [
95 0o700000007,
96 0o700000007,
97 0o700000007,
98 0o7777777,
99 ];
100 if zip(keys, MASK).any(|(key, mask)| key & !mask > 0) {
101 return None;
102 }
103 let d = keys.map(one_two);
104 let num_twos = d[0].3 + d[1].3 + d[2].3 + d[3].3;
105 let num_ones = d[0].1 + d[1].1 + d[2].1 + d[3].1;
106 match (num_ones, num_twos) {
107 (13, 0) => Some(IrregularWait::ThirteenOrphansAll),
108 (11, 1) => {
109 for i in 0..4 {
110 let k = (MASK[i] & 0o111111111) - (d[i].0 | d[i].2);
111 if k > 0 {
112 return Some(IrregularWait::ThirteenOrphans(Tile::from_encoding(
113 k.trailing_zeros() as u8 / 3 + (i as u8) * 9
114 ).unwrap()))
115 }
116 }
117 None
118 }
119 _ => None,
120 }
121}
122
123fn one_two(x: u32) -> (u32, u32, u32, u32) {
126 let over = (x + 0o111111111) & 0o444444444;
128 if over > 0 { return (0, 20, 0, 20); }
129 let twos = (x >> 1) & 0o111111111;
131 let num_twos = twos.count_ones();
132 let ones = x - twos * 2;
134 let num_ones = ones.count_ones();
135 (ones, num_ones, twos, num_twos)
136}
137
138#[cfg(test)]
139mod tests {
140 use std::str::FromStr;
141 use super::*;
142
143 #[test]
144 fn just_seven_pairs() {
145 assert_eq!(
146 detect_seven_pairs([0o202020202, 0o000000002, 0o000000000, 0o0100000]),
147 Some(Tile::from_str("6z").unwrap()));
148 assert_eq!(
149 detect_seven_pairs([0o201020202, 0o000000002, 0o000000000, 0o0200000]),
150 Some(Tile::from_str("7m").unwrap()));
151 assert_eq!(
152 detect_seven_pairs([0o200020202, 0o000000002, 0o000020001, 0o0000000]),
153 Some(Tile::from_str("1s").unwrap()));
154 assert_eq!(
155 detect_seven_pairs([0o202000202, 0o000000002, 0o000000000, 0o0100000]),
156 None);
157 assert_eq!(
158 detect_seven_pairs([0o202040202, 0o000000001, 0o000000000, 0o0000000]),
159 None);
160 assert_eq!(
161 detect_seven_pairs([0o202020202, 0o000000002, 0o000000000, 0o0000110]),
162 None);
163 assert_eq!(
164 detect_seven_pairs([0o202020202, 0o001000000, 0o000001000, 0o0000100]),
165 None);
166 }
167
168 #[test]
169 fn just_thirteen_orphans() {
170 assert_eq!(
171 detect_thirteen_orphans([0o100000001, 0o100000001, 0o100000001, 0o1111111]),
172 Some(IrregularWait::ThirteenOrphansAll));
173 assert_eq!(
174 detect_thirteen_orphans([0o100000000, 0o100000001, 0o100000001, 0o1111112]),
175 Some(IrregularWait::ThirteenOrphans(Tile::from_str("1m").unwrap())));
176 assert_eq!(
177 detect_thirteen_orphans([0o000000002, 0o100000001, 0o100000001, 0o1111111]),
178 Some(IrregularWait::ThirteenOrphans(Tile::from_str("9m").unwrap())));
179 assert_eq!(
180 detect_thirteen_orphans([0o100000001, 0o100000000, 0o100000001, 0o1211111]),
181 Some(IrregularWait::ThirteenOrphans(Tile::from_str("1p").unwrap())));
182 assert_eq!(
183 detect_thirteen_orphans([0o200000001, 0o000000001, 0o100000001, 0o1111111]),
184 Some(IrregularWait::ThirteenOrphans(Tile::from_str("9p").unwrap())));
185 assert_eq!(
186 detect_thirteen_orphans([0o100000002, 0o100000001, 0o100000000, 0o1111111]),
187 Some(IrregularWait::ThirteenOrphans(Tile::from_str("1s").unwrap())));
188 assert_eq!(
189 detect_thirteen_orphans([0o100000002, 0o100000001, 0o000000001, 0o1111111]),
190 Some(IrregularWait::ThirteenOrphans(Tile::from_str("9s").unwrap())));
191 assert_eq!(
192 detect_thirteen_orphans([0o100000001, 0o100000001, 0o100000001, 0o1102111]),
193 Some(IrregularWait::ThirteenOrphans(Tile::from_str("5z").unwrap())));
194
195 assert_eq!(
196 detect_thirteen_orphans([0o100000010, 0o100000001, 0o100000001, 0o1102111]),
197 None);
198 assert_eq!(
199 detect_thirteen_orphans([0o100000003, 0o100000001, 0o100000001, 0o1102111]),
200 None);
201 }
202}