use std::collections::{HashMap, HashSet};
use crate::core::game_state::{GameState, InternalKey};
use crate::core::{Page, PageHandle, PageId, Response};
use crate::view::View;
use crate::{Action, GameError};
pub type StringMap = HashMap<String, String>;
pub type GameTags = HashSet<PageId>;
pub trait GameContext: Default + Clone + std::fmt::Debug + 'static {}
impl<T> GameContext for T where T: Default + Clone + std::fmt::Debug + 'static {}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GameInner {
pub state: GameState,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) pages: PageStack,
fresh: bool,
last_id: PageId,
pub(crate) iterations: usize, }
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Game<C = StringMap> {
pub inner: GameInner,
pub context: C,
pub tags: GameTags,
pub(crate) simulating: bool,
}
impl<C: GameContext> Game<C> {
pub fn new_with_page(page_name: impl Into<PageId>, page: Page<C>) -> Self {
let last_id = page_name.into();
let widget = PageHandle::new(last_id.clone(), page);
let inner = GameInner {
state: GameState::new(),
pages: PageStack::new_with_page(widget),
fresh: true, last_id,
iterations: 0,
};
Self {
context: Default::default(),
tags: Default::default(),
inner,
simulating: false,
}
}
pub fn simulating(&self) -> bool {
self.simulating
}
pub fn view(&mut self) -> Result<View, GameError> {
let Some(mut page) = self.pages.current() else {
return Err(GameError::NoPage);
};
if page.id.is_empty() {
self.pages.pop(); }
let view = loop {
let r = page.call(self);
match r {
Response::View(view) => {
page.id = view.pageid.clone(); self.fresh = self.last_id != page.id;
if self.fresh {
self.iterations += 1;
self.last_id = page.id.clone()
}
self.pages.push(page)?;
break view;
}
Response::Switch(next) => {
page = next;
}
Response::Back(n) => {
page = self.pages.pop_n(n)?;
}
Response::Tunnel(next) => {
self.pages.adv_stack();
page = next;
}
Response::Exit => {
self.pages.pop_stack().ok_or(GameError::End)?;
page = self.pages.current().ok_or(GameError::NoPage)?;
}
Response::End => return Err(GameError::End),
}
};
Ok(view)
}
pub fn id(&self) -> Option<PageId> {
let mut test = self.clone();
let Response::View(view) = self.pages.current()?.call(&mut test) else {
return None;
};
Some(view.pageid)
}
}
impl GameInner {
pub fn handle_choice(&mut self, key: InternalKey, index: u8) {
self.state.set_bit(key, index)
}
pub fn handle_action(&mut self, action: Action) -> Result<(), GameError> {
match action {
Action::None => {}
Action::SetBit(k, v) => {
self.state.set_bit(k, v);
}
Action::Set(k, v) => {
self.state.insert(k, v);
}
Action::Inc(k) => {
self.state.inc(&k);
}
Action::Reset(k) => {
self.state.remove(&k);
}
Action::Next(mut page) => {
page.id = "".into(); self.pages.push(page)?;
}
Action::Back(n) => {
self.pages.pop_n(n)?;
}
Action::Tunnel(mut page) => {
self.pages.adv_stack();
page.id = "".into(); self.pages.push(page)?;
}
Action::Exit => {
self.pages.pop_stack().ok_or(GameError::End)?;
}
}
Ok(())
}
pub fn fresh(&self) -> bool {
self.fresh
}
pub fn iterations(&self) -> usize {
self.iterations
}
pub fn page_depth(&self) -> usize {
self.pages.0.last().map(|x| x.len()).unwrap_or_default()
}
}
#[macro_export]
macro_rules! Game {
($f:path) => {
$crate::core::Game::new_with_page("", $f)
};
}
#[derive(Default, Debug, Clone)]
pub struct PageStack(Vec<Vec<PageHandle>>);
impl PageStack {
pub fn new_with_page(page: PageHandle) -> Self {
Self(vec![vec![page]])
}
pub fn current(&self) -> Option<PageHandle> {
self.0.last()?.last().cloned()
}
pub fn current_mut(&mut self) -> Option<&mut PageHandle> {
self.0.last_mut()?.last_mut()
}
pub fn push(&mut self, page: PageHandle) -> Result<(), GameError> {
let stack = self.0.last_mut().ok_or(GameError::NoStack)?;
let fresh = match stack.last() {
Some(last) => last.id != page.id,
None => true,
};
if fresh {
stack.push(page);
}
Ok(())
}
pub fn clear(&mut self) {
let Some(stack) = self.0.last_mut() else {
return;
};
stack.clear();
}
pub fn len(&self) -> usize {
let Some(stack) = self.0.last() else {
return 0;
};
stack.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn pop(&mut self) -> Option<PageHandle> {
self.0.last_mut()?.pop()
}
pub fn pop_n(&mut self, n: usize) -> Result<PageHandle, GameError> {
let stack = self.0.last_mut().ok_or(GameError::NoStack)?;
if n < stack.len() && n > 0 {
stack.truncate(stack.len() - n);
stack.last().cloned().ok_or(GameError::NoPage)
} else {
Err(GameError::NoPage)
}
}
pub fn adv_stack(&mut self) {
self.0.push(vec![]);
}
pub fn pop_stack(&mut self) -> Option<Vec<PageHandle>> {
self.0.pop()
}
}
impl<C: GameContext> std::ops::Deref for Game<C> {
type Target = GameInner;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<C: GameContext> std::ops::DerefMut for Game<C> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}