riichi_elements/meld/
packed.rs

1use bitfield_struct::bitfield;
2
3use crate::tile::Tile;
4use super::{Meld, Chii, Pon, Kakan, Daiminkan, Ankan};
5
6/// Defines the bit-fields for packing `Meld` into `u16`:
7///
8/// - `[5:0]` -- [`Tile`] (normal)
9/// - `[7:6]` -- dir
10/// - `[11:8]` -- red (see below)
11/// - `[14:12]` -- [`PackedMeldKind`]
12///
13/// ## Dir
14///
15/// - [`Chii`]: The normal number of the called tile is `min_num + dir`
16/// - [`Pon`]/[`Kakan`]/[`Daiminkan`]: The tile is called from `this_player + dir` (mod 4)
17///
18/// ## Red tiles encoding
19///
20/// | bit | Chii    | Pon     | Kakan   | Daiminkan | Ankan   |
21/// |-----|---------|---------|---------|-----------|---------|
22/// | 0   | any     | own0    | own0    | own0      | own0    |
23/// | 1   | 0       | own1    | own1    | own1      | own1    |
24/// | 2   | 0       | called  | called  | own2      | own2    |
25/// | 3   | 0       | 0       | added   | called    | own3    |
26///
27/// Note that the order of "own" tiles does not matter, but we will always normalize to use the
28/// smallest bit representation; e.g. (`0b0011` instead of `0b1010`).
29///
30/// Examples:
31/// - [`Chii`]: If _any_ tile is red, `0b0001`; otherwise `0b0000`.
32/// - [`Pon`]:
33///     - Use 55 to call 0 => `0b0100`
34///     - Use 05 to call 5 => `0b0001`
35///     - Use 05 to call 0 => `0b0101`
36/// - [`Kakan`]:
37///     - Add 0 to (55 pon 0) => `0b1100`
38///     - Add 5 to (55 pon 0) => `0b0100` (unchanged)
39///     - Add 0 to (05 pon 0) => `0b1101`
40/// - [`Daiminkan`]:
41///     - Use 055 to call 5 => `0b0001`
42///     - Use 005 to call 0 => `0b1011`
43/// - [`Ankan`]: 0005 => `0b0111`
44///
45#[bitfield(u16)]
46pub(crate) struct PackedMeld {
47    #[bits(6)]
48    pub tile: u8,
49
50    #[bits(2)]
51    pub dir: u8,
52
53    #[bits(4)]
54    pub red: u8,
55 
56    #[bits(3)]
57    pub kind: u8,
58
59    #[bits(1)]
60    pub _reserved0: u8,
61}
62
63impl PackedMeld {
64    pub fn get_tile(self) -> Option<Tile> {
65        Tile::from_encoding(self.tile()).map(|t| t.to_normal())
66    }
67}
68
69/// Encode the meld kind in 3 bits.
70/// Note that `0` is deliberately reserved so that any valid packing is never `0`.
71#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, strum::FromRepr)]
72#[repr(u8)]
73pub(crate) enum PackedMeldKind {
74    #[default]
75    Chii = 1,
76    Pon = 2,
77    Kakan = 3,
78    Daiminkan = 4,
79    Ankan = 5,
80}
81
82impl TryFrom<PackedMeld> for Meld {
83    type Error = ();
84
85    fn try_from(raw: PackedMeld) -> Result<Self, Self::Error> {
86        match PackedMeldKind::from_repr(raw.kind()).ok_or(())? {
87            PackedMeldKind::Chii =>
88                Chii::try_from(raw).map(Meld::Chii),
89            PackedMeldKind::Pon =>
90                Pon::try_from(raw).map(Meld::Pon),
91            PackedMeldKind::Kakan =>
92                Kakan::try_from(raw).map(Meld::Kakan),
93            PackedMeldKind::Daiminkan =>
94                Daiminkan::try_from(raw).map(Meld::Daiminkan),
95            PackedMeldKind::Ankan =>
96                Ankan::try_from(raw).map(Meld::Ankan),
97        }
98    }
99}
100
101impl From<Meld> for PackedMeld {
102    fn from(meld: Meld) -> Self {
103        match meld {
104            Meld::Chii(chii) => PackedMeld::from(chii),
105            Meld::Pon(pon) => PackedMeld::from(pon),
106            Meld::Kakan(kakan) => PackedMeld::from(kakan),
107            Meld::Daiminkan(daiminkan) => PackedMeld::from(daiminkan),
108            Meld::Ankan(ankan) => PackedMeld::from(ankan),
109        }
110    }
111}
112
113impl Meld {
114    /// Parse from the packed representation.
115    pub fn from_packed(packed: u16) -> Option<Self> {
116        Meld::try_from(PackedMeld::try_from(packed).ok()?).ok()
117    }
118    /// Convert to the packed representation.
119    pub fn packed(self) -> u16 {
120        u16::from(PackedMeld::from(self))
121    }
122}
123
124// Any function from u4 to u4 can be represented as a u64 (2^4 x 4).
125// Here we take advantage of this to efficiently normalize (a slice of) red bits.
126
127const fn normalize_bits(x: u8, n: u8) -> u8 {
128    let lsbs = x & ((1 << n) - 1);
129    let msbs = x & !((1 << n) - 1);
130    let new_lsbs = (1u8 << lsbs.count_ones()) - 1u8;
131    msbs | new_lsbs
132}
133
134const fn normalize_mask(n: u8) -> u64 {
135    let mut mask = 0u64;
136    let mut x = 0u8;
137    while x < 16u8 {
138        mask |= (normalize_bits(x, n) as u64) << (x * 4);
139        x += 1;
140    }
141    mask
142}
143
144const MASK_PON_KAKAN: u64 = normalize_mask(2);
145const MASK_DAIMINKAN: u64 = normalize_mask(3);
146const MASK_ANKAN: u64 = normalize_mask(4);
147
148pub const fn normalize_pon(x: u8) -> u8 { ((MASK_PON_KAKAN >> (x * 4)) & 0b0111) as u8 }
149pub const fn normalize_kakan(x: u8) -> u8 { ((MASK_PON_KAKAN >> (x * 4)) & 0b1111) as u8 }
150pub const fn normalize_daiminkan(x: u8) -> u8 { ((MASK_DAIMINKAN >> (x * 4)) & 0b1111) as u8 }
151pub const fn normalize_ankan(x: u8) -> u8 { ((MASK_ANKAN >> (x * 4)) & 0b1111) as u8 }
152