riichi_elements/meld/
pon.rs1use core::fmt::{Display, Formatter};
2
3use crate::{
4 player::*,
5 tile::Tile,
6 tile_set::*,
7 utils::{pack4, sort2, unpack4},
8};
9use super::packed::{normalize_pon, PackedMeld, PackedMeldKind};
10
11#[derive(Copy, Clone, Debug, Eq, PartialEq)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[non_exhaustive]
16pub struct Pon {
17 pub own: [Tile; 2],
19
20 pub called: Tile,
22
23 pub dir: Player,
25}
26
27impl Pon {
28 pub const fn num(self) -> u8 { self.called.normal_num() }
29 pub const fn suit(self) -> u8 { self.called.suit() }
30
31 pub fn from_tiles_dir(own0: Tile, own1: Tile, called: Tile, dir: Player) -> Option<Self> {
33 if own0.to_normal() != called.to_normal() ||
34 own1.to_normal() != called.to_normal() ||
35 dir.to_u8() == 0 { return None; }
36 let (own0, own1) = sort2(own0, own1);
37 Some(Pon { own: [own0, own1], called, dir })
38 }
39
40 pub fn is_in_hand(self, hand: &TileSet37) -> bool {
42 if self.own[0] != self.own[1] { hand[self.own[0]] >= 1 && hand[self.own[1]] >= 1 }
43 else { hand[self.own[0]] >= 2 }
44 }
45
46 pub fn consume_from_hand(self, hand: &mut TileSet37) {
48 hand[self.own[0]] -= 1;
49 hand[self.own[1]] -= 1;
50 }
51}
52
53impl Display for Pon {
54 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
55 let (n0, n1, nc, s) = (
56 self.own[0].num(),
57 self.own[1].num(),
58 self.called.num(),
59 self.called.suit_char(),
60 );
61 match self.dir.to_u8() {
62 1 => write!(f, "{}{}P{}{}", n0, n1, nc, s),
63 2 => write!(f, "{}P{}{}{}", n0, nc, n1, s),
64 3 => write!(f, "P{}{}{}{}", nc, n0, n1, s),
65 _ => Err(core::fmt::Error::default()),
66 }
67 }
68}
69
70impl TryFrom<PackedMeld> for Pon {
71 type Error = ();
72
73 fn try_from(raw: PackedMeld) -> Result<Self, Self::Error> {
74 if raw.kind() != PackedMeldKind::Pon as u8 { return Err(()); }
75 let t = raw.get_tile().ok_or(())?;
76 let (mut own0, mut own1, mut called) = (t, t, t);
77 let (r0, r1, r2, _) = unpack4(normalize_pon(raw.red()));
78 if r0 { own0 = own0.to_red(); }
79 if r1 { own1 = own1.to_red(); }
80 if r2 { called = called.to_red(); }
81 Pon::from_tiles_dir(own0, own1, called, Player::new(raw.dir())).ok_or(())
82 }
83}
84
85impl From<Pon> for PackedMeld {
86 fn from(pon: Pon) -> Self {
87 let [own0, own1] = pon.own;
88 PackedMeld::new()
89 .with_tile(own0.normal_encoding())
90 .with_dir(pon.dir.to_u8())
91 .with_red(pack4(own0.is_red(),
92 own1.is_red(),
93 pon.called.is_red(),
94 false))
95 .with_kind(PackedMeldKind::Pon as u8)
96 }
97}