1use std::{
2 cmp::Ordering,
3 fmt::{Display, Formatter},
4};
5
6use itertools::Itertools;
7
8use riichi_elements::prelude::*;
9use riichi_decomp_table::{
10 WaitingKind
11};
12
13pub(crate) type RegularWaitGroups = nanovec::NanoDequeBit<u32, u8, 8>;
14
15#[derive(Copy, Clone, Debug)]
46pub struct RegularWait {
47 pub(crate) raw_groups: RegularWaitGroups,
49
50 pub pair: Option<Tile>,
52
53 pub waiting_kind: WaitingKind,
55
56 pub pattern_tile: Tile,
64
65 pub waiting_tile: Tile,
67}
68
69impl RegularWait {
70 #[cfg(test)]
72 pub fn new(groups: &[HandGroup], pair: Option<Tile>,
73 waiting_kind: WaitingKind, pattern_tile: Tile, waiting_tile: Tile) -> Self {
74 Self {
75 raw_groups: groups.iter().map(|g| g.packed()).collect(),
76 pair,
77 waiting_kind,
78 pattern_tile,
79 waiting_tile,
80 }
81 }
82
83 pub fn groups(&self) -> impl Iterator<Item = HandGroup> {
85 self.raw_groups.map(|x| HandGroup::from_packed(x).unwrap())
86 }
87
88 fn sorted_raw_groups(&self) -> [u8; 4] {
91 let mut result = self.raw_groups.packed().to_le_bytes();
92 sortnet::sortnet4(&mut result);
93 result
94 }
95
96 pub fn has_pair_or_tanki(&self) -> bool {
98 self.pair.is_some() || self.waiting_kind == WaitingKind::Tanki
99 }
100
101 pub fn pair_or_tanki(&self) -> Option<Tile> {
103 self.pair.or_else(||
104 (self.waiting_kind == WaitingKind::Tanki).then_some(self.waiting_tile))
105 }
106
107 pub fn is_true_ryanmen(&self) -> bool {
115 matches!((self.waiting_kind, self.pattern_tile.normal_num()),
116 (WaitingKind::RyanmenLow, 2..=7) | (WaitingKind::RyanmenHigh, 2..=7) | (WaitingKind::RyanmenBoth, _)
119 )
120 }
121}
122
123impl PartialEq<Self> for RegularWait {
127 fn eq(&self, other: &Self) -> bool {
128 self.sorted_raw_groups() == other.sorted_raw_groups()
129 && self.pair == other.pair
130 && self.waiting_kind == other.waiting_kind
131 && self.pattern_tile == other.pattern_tile
132 && self.waiting_tile == other.waiting_tile
133 }
134}
135
136impl Eq for RegularWait {}
137
138impl PartialOrd<Self> for RegularWait {
139 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
140 Some(self.cmp(other))
141 }
142}
143
144impl Ord for RegularWait {
145 fn cmp(&self, other: &Self) -> Ordering {
146 let o =
147 self.sorted_raw_groups().cmp(&other.sorted_raw_groups());
148 if o != Ordering::Equal { return o; }
149 let o = self.pair.cmp(&other.pair);
150 if o != Ordering::Equal { return o; }
151 let o = self.waiting_kind.cmp(&other.waiting_kind);
152 if o != Ordering::Equal { return o; }
153 let o = self.pattern_tile.cmp(&other.pattern_tile);
154 if o != Ordering::Equal { return o; }
155 self.waiting_tile.cmp(&other.waiting_tile)
156 }
157}
158
159impl Display for RegularWait {
160 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
161 use WaitingKind::*;
162 write!(f, "{}", self.groups().sorted().map(|g| g.to_string()).join(" "))?;
163 if let Some(pair) = self.pair {
164 write!(f, " {}{}", pair.num(), pair)?;
165 }
166 let p = self.pattern_tile;
167 let t = self.waiting_tile;
168 match self.waiting_kind {
169 Tanki =>
170 write!(f, " {}+{}", p.num(), t),
171 Shanpon =>
172 write!(f, " {}{}+{}", p.num(), p.num(), t),
173 Kanchan =>
174 write!(f, " {}{}+{}", p.num(), p.succ2().unwrap().num(), t),
175 RyanmenHigh | RyanmenLow | RyanmenBoth =>
176 write!(f, " {}{}+{}", p.num(), p.succ().unwrap().num(), t),
177 }
178 }
179}
180
181#[cfg(feature = "serde")]
184mod regular_wait_serde {
185 use serde::ser::SerializeStruct;
186 use serde::Serializer;
187 use super::*;
188 impl serde::Serialize for RegularWait {
189 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> where S: Serializer {
190 let mut st = s.serialize_struct("RegularWait", 5)?;
191 st.serialize_field("groups", &self.groups().sorted().collect_vec())?;
192 st.serialize_field("pair", &self.pair)?;
193 st.serialize_field("kind", self.waiting_kind.as_ref())?;
194 st.serialize_field("pattern", &self.pattern_tile)?;
195 st.serialize_field("waiting", &self.waiting_tile)?;
196 st.end()
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 use HandGroup::{Koutsu, Shuntsu};
206
207 #[allow(unused)]
208 fn k(str: &str) -> HandGroup { Koutsu(t!(str)) }
209 #[allow(unused)]
210 fn s(str: &str) -> HandGroup { Shuntsu(t!(str)) }
211
212 #[cfg(feature = "serde")]
213 mod serde_tests {
214 use assert_json_diff::assert_json_eq;
215 use WaitingKind::*;
216 use RegularWait as W;
217 use super::*;
218
219 #[test]
220 fn serialize_regular_wait() {
221 let w = W::new(&[k("1m"), k("2m"), s("7m")], Some(t!("6z")),
222 Shanpon, t!("7z"), t!("7z"));
223 let json = serde_json::json!({
224 "groups": [
225 {"type": "Koutsu", "tile": "1m"},
226 {"type": "Koutsu", "tile": "2m"},
227 {"type": "Shuntsu", "tile": "7m"}
228 ],
229 "pair": "6z",
230 "kind": "Shanpon",
231 "pattern": "7z",
232 "waiting": "7z"
233 });
234 let serialized = serde_json::to_value(w).unwrap();
235 assert_json_eq!(serialized, json);
236 }
237 }
238}