use std::fmt::{Display, Formatter};
use itertools::Itertools;
use riichi_elements::prelude::*;
use super::{
action::*,
action_result::*,
boundary::*,
reaction::*,
state::*,
};
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ActionReaction {
pub actor: Player,
pub action: Action,
pub reactor_reaction: Option<(Player, Reaction)>,
}
impl Display for ActionReaction {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "P{}:{}", self.actor, self.action)?;
if let Some((reactor, reaction)) = self.reactor_reaction {
write!(f, " => P{}:{}", reactor, reaction)?;
}
Ok(())
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GameStep {
pub actor: Player,
pub action: Action,
pub reactor_reaction: Option<(Player, Reaction)>,
pub action_result: ActionResult,
pub next_state_core: Option<StateCore>,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RoundHistory {
pub begin: RoundBegin,
pub steps: Vec<GameStep>,
pub ron: [bool; 4],
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RoundHistoryLite {
pub begin: RoundBegin,
pub action_reactions: Vec<ActionReaction>,
pub ron: [bool; 4],
}
impl Display for RoundHistoryLite {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{:?}, pot={}, points={:?}, multi_ron={:?}",
self.begin.round_id,
self.begin.pot,
self.begin.points,
self.ron,
)?;
for action_reaction in self.action_reactions.iter() {
writeln!(f, "{}", action_reaction)?;
}
Ok(())
}
}
impl State {
pub fn evolve(&mut self, action: Action, next: StateCore) {
let actor = self.core.actor;
let actor_i = actor.to_usize();
let next_actor = next.actor;
let next_actor_i = next_actor.to_usize();
if let Some(draw) = self.core.draw {
self.closed_hands[actor_i][draw] += 1;
log::debug!("P{}:Draw({}) => hand={}", actor_i, draw, self.closed_hands[actor_i]);
}
match action {
Action::Discard(mut discard) => {
self.closed_hands[actor_i][discard.tile] -= 1;
discard.called_by =
if next.incoming_meld.is_some() { next_actor } else { actor };
self.discards[actor_i].push(discard);
self.discard_sets[actor_i].set(discard.tile);
log::debug!("P{}:{} => hand={}", actor_i, discard, self.closed_hands[actor_i]);
}
Action::Kakan(_) | Action::Ankan(_) => {} _ => panic!("inconsistent")
}
if let Some(meld) = next.incoming_meld {
meld.consume_from_hand(&mut self.closed_hands[next_actor_i]);
if let Meld::Kakan(kakan) = meld {
let (pon_i, _) = self.melds[next_actor_i].iter()
.find_position(|&&meld| meld == Meld::Pon(kakan.pon))
.unwrap();
self.melds[next_actor_i][pon_i] = meld;
} else {
self.melds[next_actor_i].push(meld);
}
log::debug!("P{}:Meld({}) => hand={}",
next_actor_i, meld, self.closed_hands[next_actor_i]);
}
self.core = next;
}
pub fn apply_step(&mut self, game_step: &GameStep) {
if let Some(next) = game_step.next_state_core {
self.evolve(game_step.action, next);
}
}
pub fn apply_steps<'a>(&mut self, game_step: impl IntoIterator<Item=&'a GameStep>) {
for step in game_step {
self.apply_step(step);
}
}
}