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