use std::fmt::{Display, Formatter};
use std::str::FromStr;
use itertools::Itertools;
#[cfg(feature = "serde")]
use serde_with::{
serde_as, As, DisplayFromStr,
};
use riichi_elements::prelude::*;
use super::{
Discard,
RoundBegin,
};
#[derive(Clone, Debug, Default)]
pub struct State {
pub core: StateCore,
pub melds: [Vec<Meld>; 4],
pub closed_hands: [TileSet37; 4],
pub discards: [Vec<Discard>; 4],
pub discard_sets: [TileMask34; 4],
}
#[derive(Copy, Clone, Debug, Default)]
#[cfg_attr(test, derive(type_layout::TypeLayout))]
#[cfg_attr(feature = "serde", serde_as)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StateCore {
pub seq: u8,
pub actor: Player,
pub num_drawn_head: u8,
pub num_drawn_tail: u8,
pub num_dora_indicators: u8,
pub draw: Option<Tile>,
pub incoming_meld: Option<Meld>,
pub furiten: [FuritenFlags; 4],
#[cfg_attr(feature = "serde", serde(with = "As::<[Option<DisplayFromStr>; 4]>"))]
pub riichi: [Option<Riichi>; 4],
}
impl State {
pub fn new(begin: &RoundBegin) -> Self {
let button = begin.round_id.button();
Self {
core: StateCore::new(begin),
closed_hands: wall::deal(&begin.wall, button),
melds: Default::default(),
discards: Default::default(),
discard_sets: Default::default(),
}
}
}
impl StateCore {
pub fn new(begin: &RoundBegin) -> Self {
let button = begin.round_id.button();
Self {
seq: 0,
actor: button,
num_drawn_head: 53, num_drawn_tail: 0,
num_dora_indicators: 1,
draw: Some(begin.wall[52]),
incoming_meld: None,
furiten: Default::default(),
riichi: Default::default(),
}
}
}
impl Display for StateCore {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{{#{} P{} draw[{}|{}]={} meld={:?} dora={} riichi=[{}] furiten=[{}]}}",
self.seq,
self.actor.to_usize(),
self.num_drawn_head,
self.num_drawn_tail,
self.draw.map(|t| t.as_str()).unwrap_or("NA"),
self.incoming_meld.map(|x| x.to_string()),
self.num_dora_indicators,
self.riichi.into_iter().map(maybe_riichi_as_str).join(","),
self.furiten.into_iter().map(|f| f.any() as u8).join(","),
)
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct Riichi {
pub is_double: bool,
pub is_ippatsu: bool,
}
impl Riichi {
pub fn as_str(self) -> &'static str {
match (self.is_double, self.is_ippatsu) {
(false, false) => "r",
(false, true) => "R",
(true, false) => "d",
(true, true) => "D",
}
}
}
pub fn maybe_riichi_as_str(r: Option<Riichi>) -> &'static str {
match r {
Some(r) => r.as_str(),
None => "_",
}
}
impl FromStr for Riichi {
type Err = UnspecifiedError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let r = match s {
"r" => Riichi { is_double: false, is_ippatsu: false },
"R" => Riichi { is_double: false, is_ippatsu: true },
"d" => Riichi { is_double: true, is_ippatsu: false },
"D" => Riichi { is_double: true, is_ippatsu: true },
_ => return Err(UnspecifiedError),
};
Ok(r)
}
}
impl Display for Riichi {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde_tuple::Serialize_tuple, serde_tuple::Deserialize_tuple))]
pub struct FuritenFlags {
pub by_discard: bool,
pub miss_temporary: bool,
pub miss_permanent: bool,
}
impl FuritenFlags {
pub const fn any(self) -> bool { self.by_discard || self.miss_temporary || self.miss_permanent }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn print_layout() {
use type_layout::TypeLayout;
println!("{}", StateCore::type_layout());
}
}