use core::fmt::{Display, Formatter};
use crate::{
player::*,
tile::Tile,
tile_set::*,
utils::{pack4, unpack4},
};
use super::{
packed::{normalize_kakan, PackedMeld, PackedMeldKind},
utils::count_for_kan,
Pon,
};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct Kakan {
#[cfg_attr(feature = "serde", serde(flatten))]
pub pon: Pon,
pub added: Tile,
}
impl Kakan {
pub const fn num(self) -> u8 {
self.added.normal_num()
}
pub const fn suit(self) -> u8 {
self.added.suit()
}
pub fn from_pon_added(pon: Pon, added: Tile) -> Option<Self> {
if added.to_normal() != pon.called.to_normal() {
return None;
}
Some(Kakan { pon, added })
}
pub fn from_pon_hand(pon: Pon, hand: &TileSet37) -> Option<Self> {
let normal = pon.called.to_normal();
let (num_normal, num_red) = count_for_kan(hand, normal);
match (num_normal, num_red) {
(1, 0) => Some(Kakan { pon, added: normal }),
(0, 1) => Some(Kakan { pon, added: normal.to_red() }),
_ => None,
}
}
pub fn consume_from_hand(self, hand: &mut TileSet37) {
hand[self.added] -= 1;
}
}
impl Display for Kakan {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let (n0, n1, nc, na, suit) = (
self.pon.own[0].num(),
self.pon.own[1].num(),
self.pon.called.num(),
self.added.num(),
self.added.suit_char(),
);
match self.pon.dir.to_u8() {
1 => write!(f, "{}{}K({}/{}){}", n0, n1, na, nc, suit),
2 => write!(f, "{}K({}/{}){}{}", n0, na, nc, n1, suit),
3 => write!(f, "K({}/{}){}{}{}", na, nc, n0, n1, suit),
_ => Err(core::fmt::Error::default()),
}
}
}
impl TryFrom<PackedMeld> for Kakan {
type Error = ();
fn try_from(raw: PackedMeld) -> Result<Self, Self::Error> {
if raw.kind() != PackedMeldKind::Kakan as u8 {
return Err(());
}
let t = raw.get_tile().ok_or(())?;
let (mut own0, mut own1, mut called, mut added) = (t, t, t, t);
let (r0, r1, r2, r3) = unpack4(normalize_kakan(raw.red()));
if r0 { own0 = own0.to_red(); }
if r1 { own1 = own1.to_red(); }
if r2 { called = called.to_red(); }
if r3 { added = added.to_red(); }
let pon = Pon::from_tiles_dir(own0, own1, called, Player::new(raw.dir()))
.ok_or(())?;
Kakan::from_pon_added(pon, added).ok_or(())
}
}
impl From<Kakan> for PackedMeld {
fn from(kakan: Kakan) -> Self {
let [own0, own1] = kakan.pon.own;
PackedMeld::new()
.with_tile(own0.normal_encoding())
.with_dir(kakan.pon.dir.to_u8())
.with_red(pack4(
own0.is_red(),
own1.is_red(),
kakan.pon.called.is_red(),
kakan.added.is_red(),
))
.with_kind(PackedMeldKind::Kakan as u8)
}
}