riichi_elements/meld/
kakan.rs1use core::fmt::{Display, Formatter};
2
3use crate::{
4 player::*,
5 tile::Tile,
6 tile_set::*,
7 utils::{pack4, unpack4},
8};
9
10use super::{
11 packed::{normalize_kakan, PackedMeld, PackedMeldKind},
12 utils::count_for_kan,
13 Pon,
14};
15
16#[derive(Copy, Clone, Debug, Eq, PartialEq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[non_exhaustive]
21pub struct Kakan {
22 #[cfg_attr(feature = "serde", serde(flatten))]
24 pub pon: Pon,
25
26 pub added: Tile,
28}
29
30impl Kakan {
31 pub const fn num(self) -> u8 {
32 self.added.normal_num()
33 }
34 pub const fn suit(self) -> u8 {
35 self.added.suit()
36 }
37
38 pub fn from_pon_added(pon: Pon, added: Tile) -> Option<Self> {
40 if added.to_normal() != pon.called.to_normal() {
41 return None;
42 }
43 Some(Kakan { pon, added })
44 }
45
46 pub fn from_pon_hand(pon: Pon, hand: &TileSet37) -> Option<Self> {
49 let normal = pon.called.to_normal();
50 let (num_normal, num_red) = count_for_kan(hand, normal);
51 match (num_normal, num_red) {
52 (1, 0) => Some(Kakan { pon, added: normal }),
53 (0, 1) => Some(Kakan { pon, added: normal.to_red() }),
54 _ => None,
55 }
56 }
57
58 pub fn consume_from_hand(self, hand: &mut TileSet37) {
60 hand[self.added] -= 1;
61 }
62}
63
64impl Display for Kakan {
65 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
66 let (n0, n1, nc, na, suit) = (
67 self.pon.own[0].num(),
68 self.pon.own[1].num(),
69 self.pon.called.num(),
70 self.added.num(),
71 self.added.suit_char(),
72 );
73 match self.pon.dir.to_u8() {
74 1 => write!(f, "{}{}K({}/{}){}", n0, n1, na, nc, suit),
75 2 => write!(f, "{}K({}/{}){}{}", n0, na, nc, n1, suit),
76 3 => write!(f, "K({}/{}){}{}{}", na, nc, n0, n1, suit),
77 _ => Err(core::fmt::Error::default()),
78 }
79 }
80}
81
82impl TryFrom<PackedMeld> for Kakan {
83 type Error = ();
84
85 fn try_from(raw: PackedMeld) -> Result<Self, Self::Error> {
86 if raw.kind() != PackedMeldKind::Kakan as u8 {
87 return Err(());
88 }
89 let t = raw.get_tile().ok_or(())?;
90 let (mut own0, mut own1, mut called, mut added) = (t, t, t, t);
91 let (r0, r1, r2, r3) = unpack4(normalize_kakan(raw.red()));
92 if r0 { own0 = own0.to_red(); }
93 if r1 { own1 = own1.to_red(); }
94 if r2 { called = called.to_red(); }
95 if r3 { added = added.to_red(); }
96 let pon = Pon::from_tiles_dir(own0, own1, called, Player::new(raw.dir()))
97 .ok_or(())?;
98 Kakan::from_pon_added(pon, added).ok_or(())
99 }
100}
101
102impl From<Kakan> for PackedMeld {
103 fn from(kakan: Kakan) -> Self {
104 let [own0, own1] = kakan.pon.own;
105 PackedMeld::new()
106 .with_tile(own0.normal_encoding())
107 .with_dir(kakan.pon.dir.to_u8())
108 .with_red(pack4(
109 own0.is_red(),
110 own1.is_red(),
111 kakan.pon.called.is_red(),
112 kakan.added.is_red(),
113 ))
114 .with_kind(PackedMeldKind::Kakan as u8)
115 }
116}