use std::convert::Infallible;
use event_simulation::{OwnedSimulation, Simulation, SimulationInfo, SimulationState};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct Choice(usize);
struct Passage {
text: String,
from: usize,
to: usize,
}
struct Adventure {
rooms: Vec<String>,
passages: Vec<Passage>,
}
impl Adventure {
fn choices_from(&self, room: usize) -> Vec<Choice> {
self.matching(|passage| passage.from == room)
}
fn choices_into(&self, room: usize) -> Vec<Choice> {
self.matching(|passage| passage.to == room)
}
fn matching(&self, predicate: impl Fn(&Passage) -> bool) -> Vec<Choice> {
self.passages
.iter()
.enumerate()
.filter(|(_, passage)| predicate(passage))
.map(|(index, _)| Choice(index))
.collect()
}
}
#[derive(Clone)]
struct Position {
room: usize,
callables: Vec<Choice>,
revertables: Vec<Choice>,
}
impl Position {
fn at(adventure: &Adventure, room: usize) -> Self {
Self {
room,
callables: adventure.choices_from(room),
revertables: adventure.choices_into(room),
}
}
}
impl SimulationInfo for Adventure {
type State = Position;
type StateLoadingError = Infallible;
type AccessData = usize;
type LoadData = usize;
type Event = Choice;
type EventContainer<'a> = std::vec::IntoIter<Choice>;
fn default_state(&self) -> Position {
Position::at(self, 0)
}
fn load_state(&self, room: usize) -> Result<Position, Infallible> {
Ok(Position::at(self, room.min(self.rooms.len() - 1)))
}
unsafe fn clone_state(&self, state: &Position) -> Position {
state.clone()
}
unsafe fn data<'a>(&self, state: &'a Position) -> &'a usize {
&state.room
}
fn callables(state: &Position) -> std::vec::IntoIter<Choice> {
state.callables.clone().into_iter()
}
fn revertables(state: &Position) -> std::vec::IntoIter<Choice> {
state.revertables.clone().into_iter()
}
fn callable(state: &Position, event: Choice) -> bool {
state.callables.contains(&event)
}
fn revertable(state: &Position, event: Choice) -> bool {
state.revertables.contains(&event)
}
unsafe fn call(&self, state: &mut Position, Choice(index): Choice) {
*state = Position::at(self, self.passages[index].to);
}
unsafe fn revert(&self, state: &mut Position, Choice(index): Choice) {
*state = Position::at(self, self.passages[index].from);
}
}
fn passage(text: &str, from: usize, to: usize) -> Passage {
Passage {
text: text.into(),
from,
to,
}
}
fn main() {
let adventure = Adventure {
rooms: vec![
"You stand at a crossroads.".into(),
"A dark cave yawns before you.".into(),
"Sunlight breaks over a quiet meadow.".into(),
],
passages: vec![
passage("Enter the cave", 0, 1),
passage("Walk to the meadow", 0, 2),
passage("Retreat from the cave", 1, 0),
],
};
let mut simulation = OwnedSimulation::<Adventure>::new(adventure);
let describe =
|simulation: &OwnedSimulation<Adventure>| simulation.rooms[*simulation.data()].clone();
println!("{}", describe(&simulation));
for choice in simulation.callables() {
println!(" → {}", simulation.passages[choice.0].text);
}
let Some(first) = simulation.callables().next() else {
panic!("crossroads has choices");
};
assert!(simulation.try_call(first));
assert_eq!(simulation.state.room, 1);
println!("\n{}", describe(&simulation));
let Some(back) = simulation.revertables().next() else {
panic!("cave can be left");
};
assert!(simulation.try_revert(back));
assert_eq!(simulation.state.room, 0);
println!("\nBack: {}", describe(&simulation));
}