use core::fmt::{Display, Formatter};
use crate::tile::Tile;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", content = "tile"))]
pub enum HandGroup {
Koutsu(Tile),
Shuntsu(Tile),
}
impl HandGroup {
pub fn from_packed(packed: u8) -> Option<Self> {
let num = ((packed & 0b1111) >> 1) + 1;
let suit = (packed >> 4) & 0b11;
let tile = Tile::from_num_suit(num, suit)?;
if (packed & 1) == 1 {
if num == 8 {
Some(HandGroup::Koutsu(tile.succ().unwrap()))
} else if suit < 3 {
Some(HandGroup::Shuntsu(tile))
} else {
None
}
} else {
Some(HandGroup::Koutsu(tile))
}
}
pub fn packed(self) -> u8 {
match self {
HandGroup::Koutsu(tile) => {
let n = tile.num() - 1;
let s = tile.suit();
(s << 4) | ((n << 1) - ((n == 8) as u8))
}
HandGroup::Shuntsu(tile) => {
let n = tile.num() - 1;
let s = tile.suit();
(s << 4) | ((n << 1) + 1)
}
}
}
pub fn min_tile(self) -> Tile {
match self {
HandGroup::Koutsu(tile) => tile,
HandGroup::Shuntsu(tile) => tile,
}
}
}
impl Display for HandGroup {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
HandGroup::Koutsu(tile) => {
let n = tile.normal_num();
let s = tile.suit_char();
write!(f, "{}{}{}{}", n, n, n, s)
},
HandGroup::Shuntsu(tile) => {
let n = tile.normal_num();
let s = tile.suit_char();
write!(f, "{}{}{}{}", n, n + 1, n + 2, s)
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_hand_groups_are_correctly_encoded() {
let t = |enc| Tile::from_encoding(enc).unwrap();
let k = |x| Some(HandGroup::Koutsu(t(x)));
let s = |x| Some(HandGroup::Shuntsu(t(x)));
let all = [
k(0), s(0), k(1), s(1), k(2), s(2), k(3), s(3),
k(4), s(4), k(5), s(5), k(6), s(6), k(7), k(8),
k(9), s(9), k(10), s(10), k(11), s(11), k(12), s(12),
k(13), s(13), k(14), s(14), k(15), s(15), k(16), k(17),
k(18), s(18), k(19), s(19), k(20), s(20), k(21), s(21),
k(22), s(22), k(23), s(23), k(24), s(24), k(25), k(26),
k(27), None, k(28), None, k(29), None, k(30), None,
k(31), None, k(32), None, k(33), None, None, None,
];
for (i, ans) in all.into_iter().enumerate() {
let i = i as u8;
let unpacked = HandGroup::from_packed(i as u8);
assert_eq!(unpacked, ans);
if let Some(g) = unpacked {
assert_eq!(g.packed(), i);
}
}
}
}