1use core::fmt::{Display, Formatter};
9
10use crate::{
11 hand_group::HandGroup,
12 player::*,
13 tile::Tile,
14 tile_set::*,
15};
16
17mod chii;
18mod pon;
19mod kakan;
20mod daiminkan;
21mod ankan;
22mod packed;
23mod utils;
24
25pub use chii::Chii;
26pub use pon::Pon;
27pub use kakan::Kakan;
28pub use daiminkan::Daiminkan;
29pub use ankan::Ankan;
30
31#[derive(Copy, Clone, Debug, Eq, PartialEq)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50#[cfg_attr(feature = "serde", serde(tag = "type"))]
51pub enum Meld {
52 Chii(Chii),
54 Pon(Pon),
56 Kakan(Kakan),
58 Daiminkan(Daiminkan),
60 Ankan(Ankan),
62}
63
64impl Meld {
65 pub fn is_closed(&self) -> bool {
67 matches!(self, Meld::Ankan(_))
68 }
69
70 pub fn is_kan(&self) -> bool {
72 matches!(self, Meld::Kakan(_) | Meld::Daiminkan(_) | Meld::Ankan(_))
73 }
74
75 pub fn called(&self) -> Option<Tile> {
77 match self {
78 Self::Chii(chii) => Some(chii.called),
79 Self::Pon(pon) => Some(pon.called),
80 Self::Daiminkan(daiminkan) => Some(daiminkan.called),
81 Self::Kakan(kakan) => Some(kakan.pon.called),
82 Self::Ankan(_) => None,
83 }
84 }
85
86 pub fn dir(&self) -> Option<Player> {
90 match self {
91 Self::Chii(_) => Some(P3),
92 Self::Pon(pon) => Some(pon.dir),
93 Self::Daiminkan(daiminkan) => Some(daiminkan.dir),
94 Self::Kakan(kakan) => Some(kakan.pon.dir),
95 Self::Ankan(_) => None,
96 }
97 }
98
99 pub fn to_equivalent_group(&self) -> HandGroup {
103 use HandGroup::*;
104 match self {
105 Meld::Chii(chii) => Shuntsu(chii.min),
106 Meld::Pon(pon) => Koutsu(pon.called.to_normal()),
107 Meld::Kakan(kakan) => Koutsu(kakan.added.to_normal()),
108 Meld::Daiminkan(daiminkan) => Koutsu(daiminkan.called.to_normal()),
109 Meld::Ankan(ankan) => Koutsu(ankan.own[0].to_normal()),
110 }
111 }
112
113 pub fn consume_from_hand(&self, hand: &mut TileSet37) {
116 match self {
117 Meld::Chii(chii) => chii.consume_from_hand(hand),
118 Meld::Pon(pon) => pon.consume_from_hand(hand),
119 Meld::Daiminkan(daiminkan) => daiminkan.consume_from_hand(hand),
120 Meld::Kakan(kakan) => kakan.consume_from_hand(hand),
121 Meld::Ankan(ankan) => ankan.consume_from_hand(hand),
122 }
123 }
124}
125
126impl Display for Meld {
127 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
128 match self {
130 Meld::Chii(chii) => write!(f, "{}", chii),
131 Meld::Pon(pon) => write!(f, "{}", pon),
132 Meld::Kakan(kakan) => write!(f, "{}", kakan),
133 Meld::Daiminkan(daiminkan) => write!(f, "{}", daiminkan),
134 Meld::Ankan(ankan) => write!(f, "{}", ankan),
135 }
136 }
137}
138
139#[cfg(test)]
140mod test {
141 extern crate std;
142 use std::{
143 string::ToString,
144 };
145
146 use super::*;
147 use crate::t;
148
149 #[test]
150 fn chii_example() {
151 let chii = Chii::from_tiles(
152 t!("4s"), t!("6s"), t!("0s")).unwrap();
153 let meld = Meld::Chii(chii);
154 let packed = 0x1155;
155 assert_eq!(Meld::from_packed(packed), Some(meld));
156 assert_eq!(meld.packed(), packed);
157 assert_eq!(chii.to_string(), "C046s");
158 assert_eq!(meld.to_string(), "C046s");
159
160 assert_eq!(meld.called(), Some(t!("0s")));
161 assert_eq!(meld.dir(), Some(P3));
162 assert_eq!(meld.to_equivalent_group(), HandGroup::Shuntsu(t!("4s")));
163 }
164
165 #[test]
166 fn pon_example() {
167 let pon = Pon::from_tiles_dir(
168 t!("5p"), t!("0p"), t!("0p"), P2).unwrap();
169 let meld = Meld::Pon(pon);
170 let packed = 0x258D;
171 assert_eq!(Meld::from_packed(packed), Some(meld));
172 assert_eq!(meld.packed(), packed);
173 assert_eq!(pon.to_string(), "0P05p");
174 assert_eq!(meld.to_string(), "0P05p");
175
176 assert_eq!(meld.called(), Some(t!("0p")));
177 assert_eq!(meld.dir(), Some(P2));
178 assert_eq!(meld.to_equivalent_group(), HandGroup::Koutsu(t!("5p")));
179 }
180
181 #[test]
182 fn kakan_example() {
183 let kakan = Kakan::from_pon_added(
184 Pon::from_tiles_dir(
185 t!("5p"), t!("0p"), t!("0p"), P1).unwrap(),
186 t!("5p"),
187 ).unwrap();
188 let meld = Meld::Kakan(kakan);
189 let packed = 0x354D;
190 assert_eq!(Meld::from_packed(packed), Some(meld));
191 assert_eq!(meld.packed(), packed);
192 assert_eq!(kakan.to_string(), "05K(5/0)p");
193 assert_eq!(meld.to_string(), "05K(5/0)p");
194
195 assert_eq!(meld.called(), Some(t!("0p")));
196 assert_eq!(meld.dir(), Some(P1));
197 assert_eq!(meld.to_equivalent_group(), HandGroup::Koutsu(t!("5p")));
198 }
199
200 #[test]
201 fn daiminkan_example() {
202 let daiminkan = Daiminkan::from_tiles_dir(
203 [t!("5s"), t!("0s"), t!("5s")], t!("0s"), P3).unwrap();
204 let meld = Meld::Daiminkan(daiminkan);
205 let packed = 0x49D6;
206 assert_eq!(Meld::from_packed(packed), Some(meld));
207 assert_eq!(meld.packed(), packed);
208 assert_eq!(daiminkan.to_string(), "D0055s");
209 assert_eq!(meld.to_string(), "D0055s");
210
211 assert_eq!(meld.called(), Some(t!("0s")));
212 assert_eq!(meld.dir(), Some(P3));
213 assert_eq!(meld.to_equivalent_group(), HandGroup::Koutsu(t!("5s")));
214 }
215
216 #[test]
217 fn ankan_example() {
218 let ankan = Ankan::from_tiles(
219 [t!("4z"), t!("4z"), t!("4z"), t!("4z")]).unwrap();
220 let meld = Meld::Ankan(ankan);
221 let packed = 0x501E;
222 assert_eq!(Meld::from_packed(packed), Some(meld));
223 assert_eq!(meld.packed(), packed);
224 assert_eq!(ankan.to_string(), "A4444z");
225 assert_eq!(meld.to_string(), "A4444z");
226
227 assert_eq!(meld.called(), None);
228 assert_eq!(meld.dir(), None);
229 assert_eq!(meld.to_equivalent_group(), HandGroup::Koutsu(t!("4z")));
230 }
231
232 #[test]
233 fn null_example() {
234 assert_eq!(Meld::from_packed(0), None);
235 }
236
237 #[test]
238 fn sizeof() {
239 std::println!("Meld={} (align={}), Option<Meld>={} (align={})",
240 core::mem::size_of::<Meld>(),
241 core::mem::align_of::<Meld>(),
242 core::mem::size_of::<Option<Meld>>(),
243 core::mem::align_of::<Option<Meld>>(),
244 );
245 }
246
247 #[cfg(all(feature = "serde", feature = "std"))]
248 mod serde_tests{
249 use super::*;
250 use assert_json_diff::assert_json_eq;
251 #[test]
252 fn serde_chii() {
253 let meld = Meld::Chii(Chii::from_tiles(
254 t!("4s"), t!("6s"), t!("0s")).unwrap());
255 let json = serde_json::json!(
256 {"type": "Chii", "own": ["4s", "6s"], "called": "0s", "min": "4s"});
257 let serialized = serde_json::to_value(meld).unwrap();
258 let deserialized = serde_json::from_value::<Meld>(json.clone()).unwrap();
259 assert_json_eq!(serialized, json);
260 assert_eq!(deserialized, meld);
261 }
262
263 #[test]
264 fn serde_pon() {
265 let meld = Meld::Pon(Pon::from_tiles_dir(
266 t!("5p"), t!("0p"), t!("0p"), P2).unwrap());
267 let json = serde_json::json!(
268 {"type": "Pon", "own": ["0p", "5p"], "called": "0p", "dir": 2});
269 let serialized = serde_json::to_value(meld).unwrap();
270 let deserialized = serde_json::from_value::<Meld>(json.clone()).unwrap();
271 assert_json_eq!(serialized, json);
272 assert_eq!(deserialized, meld);
273 }
274
275 #[test]
276 fn serde_kakan() {
277 let meld = Meld::Kakan(Kakan::from_pon_added(
278 Pon::from_tiles_dir(
279 t!("5p"), t!("0p"), t!("0p"), P1).unwrap(),
280 t!("5p"),
281 ).unwrap());
282 let json = serde_json::json!(
283 {"type": "Kakan", "own": ["0p", "5p"], "called": "0p", "dir": 1, "added": "5p"});
284 let serialized = serde_json::to_value(meld).unwrap();
285 let deserialized = serde_json::from_value::<Meld>(json.clone()).unwrap();
286 assert_json_eq!(serialized, json);
287 assert_eq!(deserialized, meld);
288 }
289
290 #[test]
291 fn serde_daiminkan() {
292 let meld = Meld::Daiminkan(Daiminkan::from_tiles_dir(
293 [t!("5s"), t!("0s"), t!("5s")], t!("0s"), P3).unwrap());
294 let json = serde_json::json!(
295 {"type": "Daiminkan", "own": ["0s", "5s", "5s"], "called": "0s", "dir": 3});
296 let serialized = serde_json::to_value(meld).unwrap();
297 let deserialized = serde_json::from_value::<Meld>(json.clone()).unwrap();
298 assert_json_eq!(serialized, json);
299 assert_eq!(deserialized, meld);
300 }
301
302 #[test]
303 fn serde_ankan() {
304 let meld = Meld::Ankan(Ankan::from_tiles(
305 [t!("4z"), t!("4z"), t!("4z"), t!("4z")]).unwrap());
306 let json = serde_json::json!(
307 {"type": "Ankan", "own": ["4z", "4z", "4z", "4z"]});
308 let serialized = serde_json::to_value(meld).unwrap();
309 let deserialized = serde_json::from_value::<Meld>(json.clone()).unwrap();
310 assert_json_eq!(serialized, json);
311 assert_eq!(deserialized, meld);
312 }
313 }
314}