mod end_info;
mod entry;
mod meld;
mod recovery;
mod round;
mod scoring;
pub mod test_utils;
pub mod strings;
mod tile;
use serde::{
Serialize, Deserialize,
};
pub use self::{
end_info::*,
entry::*,
meld::*,
recovery::*,
round::*,
scoring::*,
tile::*,
};
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[cfg_attr(test, derive(Eq, PartialEq))] pub struct TenhouLog {
#[serde(rename = "log")]
pub rounds: Vec<TenhouRoundRaw>,
pub rule: TenhouRule,
#[serde(rename = "ref", skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(rename = "name", skip_serializing_if = "Option::is_none")]
pub player_names: Option<Vec<String>>,
}
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[cfg_attr(test, derive(Eq, PartialEq))] pub struct TenhouRule {
#[serde(rename = "disp")]
pub raw_rule_str: String,
#[serde(rename = "aka51")]
pub num_reds_0: Option<u8>,
#[serde(rename = "aka52")]
pub num_reds_1: Option<u8>,
#[serde(rename = "aka53")]
pub num_reds_2: Option<u8>,
#[serde(rename = "aka")]
pub num_reds_each: Option<u8>,
}
impl TenhouRule {
pub fn num_reds(&self) -> [u8; 3] {
if let (Some(m), Some(p), Some(s)) = (self.num_reds_0, self.num_reds_1, self.num_reds_2) {
[m, p, s]
} else if let Some(a) = self.num_reds_each {
[a, a, a]
} else {
[0, 0, 0]
}
}
pub fn allows_red(&self) -> Option<bool> {
(!self.raw_rule_str.is_empty()).then(|| self.raw_rule_str.contains("喰"))
}
pub fn allows_kuitan(&self) -> bool {
self.raw_rule_str.contains("喰")
}
pub fn num_kyokus(&self) -> Option<u8> {
if self.raw_rule_str.contains("東") {
Some(4)
} else if self.raw_rule_str.contains("南") {
Some(8)
} else {
None
}
}
}