use std::{
collections::{HashMap, HashSet},
thread::current,
};
use iddqd::{IdHashMap, id_hash_map::Entry};
use crate::{
Action, Game, GameError, SimEnd, View,
core::{GameContext, GameTags, PageHandle, PageId, PageStack, Response, game_state::GameState},
utils::_dbg,
view::Object,
};
use super::Interactable;
impl<C: GameContext> Game<C> {
pub fn simulate<F>(&self, mut visitor: F) -> Simulation
where
F: FnMut(&mut SimulationState<C>) -> bool,
{
let mut ret = Simulation::new();
let tun_id = self
.pages
.current()
.unwrap()
.id
.rsplit("::")
.next()
.unwrap()
.into(); let mut start = self.clone();
start.simulating = true;
let mut tunnels_queue = vec![(tun_id, start)];
while let Some((tun_id, start)) = tunnels_queue.pop() {
let records = ret.runs.entry(tun_id).or_insert(PageRecords::new());
let mut queue = vec![SimulationState::new(start)];
let _ = Self::simulate_impl(&mut queue, records, &mut tunnels_queue, &mut visitor);
}
ret
}
fn interact_sim(&mut self, e: Interactable<'_>, pageid: &PageId) -> Result<(), SimEnd> {
match e {
Interactable::Choice(key, _, index) => {
self.handle_choice((pageid.clone(), *key), index);
Ok(())
}
Interactable::Span(_, s) => {
let action = s.action.as_ref().unwrap();
match action {
Action::Tunnel(next) => {
let fork_name = next.id.rsplit("::").next().unwrap().into();
let mut next = next.clone();
next.id.clear();
self.pages = PageStack::new_with_page(next);
return Err(SimEnd::Tunnel(fork_name));
}
Action::Exit => Err(SimEnd::TunnelExit),
_ => self
.handle_action(action.clone())
.map(|_| {})
.map_err(|e| e.into()),
}
}
}
}
fn simulate_impl<F>(
queue: &mut Vec<SimulationState<C>>,
records: &mut PageRecords,
tunnels_queue: &mut Vec<(String, Self)>,
visitor: &mut F,
) where
F: FnMut(&mut SimulationState<C>) -> bool,
{
while let Some(mut s) = queue.pop() {
let Some(mut page) = s.pages.current() else {
return;
};
if page.id.is_empty() {
s.pages.pop(); }
_dbg!(&page);
if !visitor(&mut s) {
continue; }
let v_res = loop {
let r = page.call(&mut s);
match r {
Response::View(view) => {
page.id = view.pageid.clone(); _dbg!(&page.id);
break s.pages.push(page).map(|_| view).map_err(|e| e.into()); }
Response::Switch(next) => {
page = next;
}
Response::Back(n) => match s.pages.pop_n(n) {
Ok(p) => page = p,
Err(e) => break Err(e.into()),
},
Response::Tunnel(mut next) => {
let mut fork = s.game.clone();
let fork_name = next.id.rsplit("::").next().unwrap().to_string(); next.id.clear();
fork.pages = PageStack::new_with_page(next);
tunnels_queue.push((fork_name.clone(), fork));
break Err(SimEnd::Tunnel(fork_name));
}
Response::Exit => {
break Err(SimEnd::TunnelExit);
}
Response::End => break Err(GameError::End.into()),
}
};
match v_res {
Ok(mut v) => {
let curr_id = v.pageid.clone();
records.insert_view(&s, &mut v);
let mut to_queue = vec![];
for e in v.interactables_sim() {
_dbg!(&e.content());
let mut next = s.next(curr_id.clone());
match next.interact_sim(e, &curr_id) {
Ok(()) => {
to_queue.push(next);
_dbg!(to_queue.len());
}
Err(e) => {
if let SimEnd::Tunnel(fork_name) = &e {
_dbg!("tun");
tunnels_queue.push((fork_name.clone(), next.game));
}
records.push_sim_end(&curr_id, e.into());
}
}
}
queue.extend(to_queue.into_iter().rev());
}
Err(e) => {
if let Some(last) = s.last.as_ref() {
records.push_sim_end(last, e)
}
}
};
}
}
}
#[derive(Debug, Clone)]
pub struct Simulation {
pub runs: HashMap<String, PageRecords>,
}
#[derive(Debug, Clone)]
pub struct SimulationState<C> {
pub game: Game<C>,
pub depth: usize,
pub last: Option<PageId>,
}
impl<C: Clone> SimulationState<C> {
fn new(game: Game<C>) -> Self {
Self {
game,
depth: 0,
last: None,
}
}
fn next(&self, curr_id: PageId) -> Self {
let mut ret = self.clone();
ret.depth += 1;
ret.last = Some(curr_id);
ret
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PageRecord {
pub id: PageId,
pub ends: HashSet<SimEnd>,
pub tags: GameTags,
pub incoming: HashSet<PageId>,
pub min_depth: usize,
pub outgoing_tunnels: HashSet<PageId>,
}
#[derive(Debug, Clone)]
pub struct PageRecords(pub IdHashMap<PageRecord>);
impl PageRecord {
pub fn new(id: PageId) -> Self {
PageRecord {
id,
ends: Default::default(),
tags: Default::default(),
incoming: Default::default(),
min_depth: usize::MAX,
outgoing_tunnels: Default::default(),
}
}
pub fn split(mut self) -> (Self, HashSet<PageId>) {
let incoming = std::mem::take(&mut self.incoming);
(self, incoming)
}
pub fn is_empty(&self) -> bool {
self.ends.is_empty() && self.tags.is_empty()
}
pub fn compute_display_width(&self) -> usize {
let mut max = 6;
for end in &self.ends {
let len = format!("{:?}", end).len();
if len > max {
max = len;
}
}
for tag in &self.tags {
let len = tag.len();
if len > max {
max = len;
}
}
max
}
}
impl PageRecords {
pub fn insert_view<C>(&mut self, s: &SimulationState<C>, v: &mut View) {
let pageid = v.pageid.clone();
let prev = s.last.clone();
match self.entry(&pageid) {
Entry::Occupied(mut occ) => {
let mut record = occ.get_mut();
record.tags.extend(v.tags.drain(0..v.tags.len()));
if let Some(prev) = prev {
record.incoming.insert(prev);
}
record.min_depth = record.min_depth.min(s.depth);
}
Entry::Vacant(vac) => {
let mut record = PageRecord::new(pageid);
record.tags.extend(v.tags.drain(0..v.tags.len()));
if let Some(prev) = prev {
record.incoming.insert(prev);
}
record.min_depth = record.min_depth.min(s.depth);
vac.insert(record);
}
}
}
pub fn push_sim_end(&mut self, pageid: &PageId, e: SimEnd) {
if let Some(mut record) = self.0.get_mut(pageid) {
record.ends.insert(e.into());
}
}
pub fn depth(&self) -> usize {
self.0.iter().map(|r| r.min_depth).max().unwrap_or(0)
}
}
impl std::ops::Deref for PageRecords {
type Target = IdHashMap<PageRecord>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for PageRecords {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<C> std::ops::Deref for SimulationState<C> {
type Target = Game<C>;
fn deref(&self) -> &Self::Target {
&self.game
}
}
impl<C> std::ops::DerefMut for SimulationState<C> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.game
}
}
impl PageRecords {
pub fn new() -> Self {
PageRecords(Default::default())
}
}
impl Simulation {
pub fn new() -> Self {
Simulation {
runs: Default::default(),
}
}
}
impl iddqd::IdHashItem for PageRecord {
type Key<'a> = &'a PageId;
fn key(&self) -> Self::Key<'_> {
&self.id
}
iddqd::id_upcast!();
}