use bitfield_struct::bitfield;
use crate::tile::Tile;
use super::{Meld, Chii, Pon, Kakan, Daiminkan, Ankan};
#[bitfield(u16)]
pub(crate) struct PackedMeld {
#[bits(6)]
pub tile: u8,
#[bits(2)]
pub dir: u8,
#[bits(4)]
pub red: u8,
#[bits(3)]
pub kind: u8,
#[bits(1)]
pub _reserved0: u8,
}
impl PackedMeld {
pub fn get_tile(self) -> Option<Tile> {
Tile::from_encoding(self.tile()).map(|t| t.to_normal())
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, strum::FromRepr)]
#[repr(u8)]
pub(crate) enum PackedMeldKind {
#[default]
Chii = 1,
Pon = 2,
Kakan = 3,
Daiminkan = 4,
Ankan = 5,
}
impl TryFrom<PackedMeld> for Meld {
type Error = ();
fn try_from(raw: PackedMeld) -> Result<Self, Self::Error> {
match PackedMeldKind::from_repr(raw.kind()).ok_or(())? {
PackedMeldKind::Chii =>
Chii::try_from(raw).map(Meld::Chii),
PackedMeldKind::Pon =>
Pon::try_from(raw).map(Meld::Pon),
PackedMeldKind::Kakan =>
Kakan::try_from(raw).map(Meld::Kakan),
PackedMeldKind::Daiminkan =>
Daiminkan::try_from(raw).map(Meld::Daiminkan),
PackedMeldKind::Ankan =>
Ankan::try_from(raw).map(Meld::Ankan),
}
}
}
impl From<Meld> for PackedMeld {
fn from(meld: Meld) -> Self {
match meld {
Meld::Chii(chii) => PackedMeld::from(chii),
Meld::Pon(pon) => PackedMeld::from(pon),
Meld::Kakan(kakan) => PackedMeld::from(kakan),
Meld::Daiminkan(daiminkan) => PackedMeld::from(daiminkan),
Meld::Ankan(ankan) => PackedMeld::from(ankan),
}
}
}
impl Meld {
pub fn from_packed(packed: u16) -> Option<Self> {
Meld::try_from(PackedMeld::try_from(packed).ok()?).ok()
}
pub fn packed(self) -> u16 {
u16::from(PackedMeld::from(self))
}
}
const fn normalize_bits(x: u8, n: u8) -> u8 {
let lsbs = x & ((1 << n) - 1);
let msbs = x & !((1 << n) - 1);
let new_lsbs = (1u8 << lsbs.count_ones()) - 1u8;
msbs | new_lsbs
}
const fn normalize_mask(n: u8) -> u64 {
let mut mask = 0u64;
let mut x = 0u8;
while x < 16u8 {
mask |= (normalize_bits(x, n) as u64) << (x * 4);
x += 1;
}
mask
}
const MASK_PON_KAKAN: u64 = normalize_mask(2);
const MASK_DAIMINKAN: u64 = normalize_mask(3);
const MASK_ANKAN: u64 = normalize_mask(4);
pub const fn normalize_pon(x: u8) -> u8 { ((MASK_PON_KAKAN >> (x * 4)) & 0b0111) as u8 }
pub const fn normalize_kakan(x: u8) -> u8 { ((MASK_PON_KAKAN >> (x * 4)) & 0b1111) as u8 }
pub const fn normalize_daiminkan(x: u8) -> u8 { ((MASK_DAIMINKAN >> (x * 4)) & 0b1111) as u8 }
pub const fn normalize_ankan(x: u8) -> u8 { ((MASK_ANKAN >> (x * 4)) & 0b1111) as u8 }