text_adventure/
text_adventure.rs1use std::convert::Infallible;
16
17use event_simulation::{OwnedSimulation, Simulation, SimulationInfo, SimulationState};
18
19#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
21struct Choice(usize);
22
23struct Passage {
25 text: String,
26 from: usize,
27 to: usize,
28}
29
30struct Adventure {
32 rooms: Vec<String>,
33 passages: Vec<Passage>,
34}
35
36impl Adventure {
37 fn choices_from(&self, room: usize) -> Vec<Choice> {
38 self.matching(|passage| passage.from == room)
39 }
40
41 fn choices_into(&self, room: usize) -> Vec<Choice> {
42 self.matching(|passage| passage.to == room)
43 }
44
45 fn matching(&self, predicate: impl Fn(&Passage) -> bool) -> Vec<Choice> {
46 self.passages
47 .iter()
48 .enumerate()
49 .filter(|(_, passage)| predicate(passage))
50 .map(|(index, _)| Choice(index))
51 .collect()
52 }
53}
54
55#[derive(Clone)]
59struct Position {
60 room: usize,
61 callables: Vec<Choice>,
62 revertables: Vec<Choice>,
63}
64
65impl Position {
66 fn at(adventure: &Adventure, room: usize) -> Self {
67 Self {
68 room,
69 callables: adventure.choices_from(room),
70 revertables: adventure.choices_into(room),
71 }
72 }
73}
74
75impl SimulationInfo for Adventure {
76 type State = Position;
77 type StateLoadingError = Infallible;
78 type AccessData = usize;
79 type LoadData = usize;
80 type Event = Choice;
81 type EventContainer<'a> = std::vec::IntoIter<Choice>;
82
83 fn default_state(&self) -> Position {
84 Position::at(self, 0)
85 }
86
87 fn load_state(&self, room: usize) -> Result<Position, Infallible> {
88 Ok(Position::at(self, room.min(self.rooms.len() - 1)))
89 }
90
91 unsafe fn clone_state(&self, state: &Position) -> Position {
92 state.clone()
93 }
94
95 unsafe fn data<'a>(&self, state: &'a Position) -> &'a usize {
96 &state.room
97 }
98
99 fn callables(state: &Position) -> std::vec::IntoIter<Choice> {
100 state.callables.clone().into_iter()
101 }
102
103 fn revertables(state: &Position) -> std::vec::IntoIter<Choice> {
104 state.revertables.clone().into_iter()
105 }
106
107 fn callable(state: &Position, event: Choice) -> bool {
108 state.callables.contains(&event)
109 }
110
111 fn revertable(state: &Position, event: Choice) -> bool {
112 state.revertables.contains(&event)
113 }
114
115 unsafe fn call(&self, state: &mut Position, Choice(index): Choice) {
116 *state = Position::at(self, self.passages[index].to);
117 }
118
119 unsafe fn revert(&self, state: &mut Position, Choice(index): Choice) {
120 *state = Position::at(self, self.passages[index].from);
121 }
122}
123
124fn passage(text: &str, from: usize, to: usize) -> Passage {
125 Passage {
126 text: text.into(),
127 from,
128 to,
129 }
130}
131
132fn main() {
133 let adventure = Adventure {
134 rooms: vec![
135 "You stand at a crossroads.".into(),
136 "A dark cave yawns before you.".into(),
137 "Sunlight breaks over a quiet meadow.".into(),
138 ],
139 passages: vec![
140 passage("Enter the cave", 0, 1),
141 passage("Walk to the meadow", 0, 2),
142 passage("Retreat from the cave", 1, 0),
143 ],
144 };
145
146 let mut simulation = OwnedSimulation::<Adventure>::new(adventure);
147
148 let describe =
149 |simulation: &OwnedSimulation<Adventure>| simulation.rooms[*simulation.data()].clone();
150
151 println!("{}", describe(&simulation));
152 for choice in simulation.callables() {
153 println!(" → {}", simulation.passages[choice.0].text);
154 }
155
156 let Some(first) = simulation.callables().next() else {
157 panic!("crossroads has choices");
158 };
159 assert!(simulation.try_call(first));
160 assert_eq!(simulation.state.room, 1);
161 println!("\n{}", describe(&simulation));
162
163 let Some(back) = simulation.revertables().next() else {
164 panic!("cave can be left");
165 };
166 assert!(simulation.try_revert(back));
167 assert_eq!(simulation.state.room, 0);
168 println!("\nBack: {}", describe(&simulation));
169}