riichi_elements/meld/
ankan.rs

1use core::fmt::{Display, Formatter};
2
3use crate::{
4    tile::Tile,
5    tile_set::*,
6    utils::{pack4, unpack4},
7};
8
9use super::{
10    packed::{normalize_ankan, PackedMeld, PackedMeldKind},
11    utils::{ankan_tiles, count_for_kan},
12};
13
14/// Closed Kan, formed by setting aside 4 tiles of the same kind in a player's closed hand (暗槓).
15/// This can be done during this player's own turn.
16///
17/// Declaring Ankan does not _technically_ open one's hand, although it _is_ revealed to others.
18#[derive(Copy, Clone, Debug, Eq, PartialEq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[non_exhaustive]
21pub struct Ankan {
22    pub own: [Tile; 4],
23}
24
25impl Ankan {
26    pub const fn num(self) -> u8 {
27        self.own[0].normal_num()
28    }
29    pub const fn suit(self) -> u8 {
30        self.own[0].suit()
31    }
32
33    /// Constructs from 4 own tiles.
34    pub fn from_tiles(mut own: [Tile; 4]) -> Option<Self> {
35        if own[0].to_normal() != own[1].to_normal()
36            || own[0].to_normal() != own[2].to_normal()
37            || own[0].to_normal() != own[3].to_normal()
38        {
39            return None;
40        }
41        own.sort();
42        Some(Ankan { own })
43    }
44
45    /// Constructs from the closed hand for the specified tile.
46    pub fn from_hand(hand: &TileSet37, tile: Tile) -> Option<Self> {
47        let normal = tile.to_normal();
48        let (num_normal, num_red) = count_for_kan(hand, normal);
49        if num_normal + num_red != 4 {
50            return None;
51        }
52        Self::from_tiles(ankan_tiles(normal, num_red))
53    }
54
55    /// Removes all own tiles from the hand (where this was constructed from).
56    pub fn consume_from_hand(self, hand: &mut TileSet37) {
57        hand[self.own[0]] = 0;
58        hand[self.own[3]] = 0;
59    }
60}
61
62impl Display for Ankan {
63    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
64        let (n0, n1, n2, n3, s) = (
65            self.own[0].num(),
66            self.own[1].num(),
67            self.own[2].num(),
68            self.own[3].num(),
69            self.own[0].suit_char(),
70        );
71        write!(f, "A{}{}{}{}{}", n0, n1, n2, n3, s)
72    }
73}
74
75// Parse from the unpacked bitfields
76impl TryFrom<PackedMeld> for Ankan {
77    type Error = ();
78
79    fn try_from(raw: PackedMeld) -> Result<Self, Self::Error> {
80        if raw.kind() != PackedMeldKind::Ankan as u8 {
81            return Err(());
82        }
83        let t = raw.get_tile().ok_or(())?;
84        let (mut own0, mut own1, mut own2, mut own3) = (t, t, t, t);
85        let (r0, r1, r2, r3) = unpack4(normalize_ankan(raw.red()));
86        if r0 { own0 = own0.to_red(); }
87        if r1 { own1 = own1.to_red(); }
88        if r2 { own2 = own2.to_red(); }
89        if r3 { own3 = own3.to_red(); }
90        Ankan::from_tiles([own0, own1, own2, own3]).ok_or(())
91    }
92}
93
94impl From<Ankan> for PackedMeld {
95    fn from(ankan: Ankan) -> Self {
96        let [own0, own1, own2, own3] = ankan.own;
97        PackedMeld::new()
98            .with_tile(own0.normal_encoding())
99            .with_dir(0)
100            .with_red(pack4(own0.is_red(), own1.is_red(), own2.is_red(), own3.is_red()))
101            .with_kind(PackedMeldKind::Ankan as u8)
102    }
103}