pub struct GrammarSet {
pub rules: HashMap<String, GrammarRule>,
}Expand description
A set of named grammar rules.
Fields§
§rules: HashMap<String, GrammarRule>Implementations§
Source§impl GrammarSet
impl GrammarSet
Sourcepub fn load_from_ron(path: &Path) -> Result<GrammarSet, GrammarError>
pub fn load_from_ron(path: &Path) -> Result<GrammarSet, GrammarError>
Load a grammar set from a RON file.
Examples found in repository?
examples/dinner_party.rs (line 18)
15fn main() {
16 // --- Load Social Drama genre template ---
17 let grammars =
18 GrammarSet::load_from_ron(std::path::Path::new("genre_data/social_drama/grammar.ron"))
19 .expect("Failed to load social drama grammar");
20
21 let mut voices = VoiceRegistry::new();
22 voices
23 .load_from_ron(std::path::Path::new("genre_data/social_drama/voices.ron"))
24 .expect("Failed to load social drama voices");
25
26 // --- Train Markov model from social drama corpus ---
27 let corpus = std::fs::read_to_string("genre_data/social_drama/corpus.txt")
28 .expect("Failed to read social drama corpus");
29 let markov_model = MarkovTrainer::train(&corpus, 3);
30
31 let mut markov_models = HashMap::new();
32 markov_models.insert("social_drama".to_string(), markov_model);
33
34 let mut engine = NarrativeEngine::builder()
35 .seed(2026)
36 .with_grammars(grammars)
37 .with_voices(voices)
38 .with_markov_models(markov_models)
39 .build()
40 .expect("Failed to build engine");
41
42 // --- Define entities ---
43 let mut entities = HashMap::new();
44
45 // Margaret — the anxious host
46 entities.insert(
47 EntityId(1),
48 Entity {
49 id: EntityId(1),
50 name: "Margaret".to_string(),
51 pronouns: Pronouns::SheHer,
52 tags: [
53 "host".to_string(),
54 "anxious".to_string(),
55 "wealthy".to_string(),
56 ]
57 .into_iter()
58 .collect(),
59 relationships: Vec::new(),
60 voice_id: Some(VoiceId(100)), // host voice
61 properties: HashMap::from([(
62 "title".to_string(),
63 narrative_engine::schema::entity::Value::String("Lady".to_string()),
64 )]),
65 },
66 );
67
68 // James — her husband, harboring a secret
69 entities.insert(
70 EntityId(2),
71 Entity {
72 id: EntityId(2),
73 name: "James".to_string(),
74 pronouns: Pronouns::HeHim,
75 tags: ["guest".to_string(), "secretive".to_string()]
76 .into_iter()
77 .collect(),
78 relationships: Vec::new(),
79 voice_id: Some(VoiceId(103)), // provocateur voice
80 properties: HashMap::new(),
81 },
82 );
83
84 // Eleanor — old friend, sharp-tongued gossip
85 entities.insert(
86 EntityId(3),
87 Entity {
88 id: EntityId(3),
89 name: "Eleanor".to_string(),
90 pronouns: Pronouns::SheHer,
91 tags: [
92 "guest".to_string(),
93 "perceptive".to_string(),
94 "caustic".to_string(),
95 ]
96 .into_iter()
97 .collect(),
98 relationships: Vec::new(),
99 voice_id: Some(VoiceId(101)), // gossip voice
100 properties: HashMap::new(),
101 },
102 );
103
104 // Robert — the peacemaker, caught in the middle
105 entities.insert(
106 EntityId(4),
107 Entity {
108 id: EntityId(4),
109 name: "Robert".to_string(),
110 pronouns: Pronouns::HeHim,
111 tags: ["guest".to_string(), "diplomatic".to_string()]
112 .into_iter()
113 .collect(),
114 relationships: Vec::new(),
115 voice_id: Some(VoiceId(102)), // peacemaker voice
116 properties: HashMap::new(),
117 },
118 );
119
120 // The Dining Room — the setting
121 entities.insert(
122 EntityId(100),
123 Entity {
124 id: EntityId(100),
125 name: "the dining room".to_string(),
126 pronouns: Pronouns::ItIts,
127 tags: [
128 "location".to_string(),
129 "formal".to_string(),
130 "elegant".to_string(),
131 ]
132 .into_iter()
133 .collect(),
134 relationships: Vec::new(),
135 voice_id: None,
136 properties: HashMap::new(),
137 },
138 );
139
140 let world = WorldState {
141 entities: &entities,
142 };
143
144 // --- Title ---
145 println!("========================================");
146 println!(" THE DINNER PARTY");
147 println!(" A Social Drama in Six Scenes");
148 println!("========================================");
149 println!();
150
151 // --- Scene 1: Small Talk (Alliance — warm, low stakes) ---
152 let event1 = Event {
153 event_type: "small_talk".to_string(),
154 participants: vec![
155 EntityRef {
156 entity_id: EntityId(1),
157 role: "subject".to_string(),
158 },
159 EntityRef {
160 entity_id: EntityId(4),
161 role: "object".to_string(),
162 },
163 ],
164 location: Some(EntityRef {
165 entity_id: EntityId(100),
166 role: "location".to_string(),
167 }),
168 mood: Mood::Warm,
169 stakes: Stakes::Low,
170 outcome: None,
171 narrative_fn: NarrativeFunction::Alliance,
172 metadata: HashMap::new(),
173 };
174 print_scene(
175 1,
176 "Small Talk",
177 &["Margaret", "Robert"],
178 &mut engine,
179 &event1,
180 &world,
181 );
182
183 // --- Scene 2: A Whispered Alliance (Eleanor and Robert align) ---
184 let event2 = Event {
185 event_type: "whispered_aside".to_string(),
186 participants: vec![
187 EntityRef {
188 entity_id: EntityId(3),
189 role: "subject".to_string(),
190 },
191 EntityRef {
192 entity_id: EntityId(4),
193 role: "object".to_string(),
194 },
195 ],
196 location: Some(EntityRef {
197 entity_id: EntityId(100),
198 role: "location".to_string(),
199 }),
200 mood: Mood::Neutral,
201 stakes: Stakes::Medium,
202 outcome: None,
203 narrative_fn: NarrativeFunction::Alliance,
204 metadata: HashMap::new(),
205 };
206 print_scene(
207 2,
208 "A Whispered Aside",
209 &["Eleanor", "Robert"],
210 &mut engine,
211 &event2,
212 &world,
213 );
214
215 // --- Scene 3: Tension Builds (Confrontation — tense, rising) ---
216 let event3 = Event {
217 event_type: "tension_rises".to_string(),
218 participants: vec![
219 EntityRef {
220 entity_id: EntityId(3),
221 role: "subject".to_string(),
222 },
223 EntityRef {
224 entity_id: EntityId(1),
225 role: "object".to_string(),
226 },
227 ],
228 location: Some(EntityRef {
229 entity_id: EntityId(100),
230 role: "location".to_string(),
231 }),
232 mood: Mood::Tense,
233 stakes: Stakes::Medium,
234 outcome: None,
235 narrative_fn: NarrativeFunction::Confrontation,
236 metadata: HashMap::new(),
237 };
238 print_scene(
239 3,
240 "Tension Builds",
241 &["Eleanor", "Margaret"],
242 &mut engine,
243 &event3,
244 &world,
245 );
246
247 // --- Scene 4: The Accusation (Confrontation — tense, high stakes) ---
248 let event4 = Event {
249 event_type: "accusation".to_string(),
250 participants: vec![
251 EntityRef {
252 entity_id: EntityId(3),
253 role: "subject".to_string(),
254 },
255 EntityRef {
256 entity_id: EntityId(2),
257 role: "object".to_string(),
258 },
259 ],
260 location: Some(EntityRef {
261 entity_id: EntityId(100),
262 role: "location".to_string(),
263 }),
264 mood: Mood::Tense,
265 stakes: Stakes::High,
266 outcome: None,
267 narrative_fn: NarrativeFunction::Confrontation,
268 metadata: HashMap::new(),
269 };
270 print_scene(
271 4,
272 "The Accusation",
273 &["Eleanor", "James"],
274 &mut engine,
275 &event4,
276 &world,
277 );
278
279 // --- Scene 5: The Revelation (James's secret comes out) ---
280 let event5 = Event {
281 event_type: "confession".to_string(),
282 participants: vec![
283 EntityRef {
284 entity_id: EntityId(2),
285 role: "subject".to_string(),
286 },
287 EntityRef {
288 entity_id: EntityId(1),
289 role: "object".to_string(),
290 },
291 ],
292 location: Some(EntityRef {
293 entity_id: EntityId(100),
294 role: "location".to_string(),
295 }),
296 mood: Mood::Somber,
297 stakes: Stakes::Critical,
298 outcome: None,
299 narrative_fn: NarrativeFunction::Revelation,
300 metadata: HashMap::new(),
301 };
302 print_scene(
303 5,
304 "The Revelation",
305 &["James", "Margaret"],
306 &mut engine,
307 &event5,
308 &world,
309 );
310
311 // --- Scene 6: Comic Relief (Robert breaks the tension) ---
312 let event6 = Event {
313 event_type: "comic_relief".to_string(),
314 participants: vec![
315 EntityRef {
316 entity_id: EntityId(4),
317 role: "subject".to_string(),
318 },
319 EntityRef {
320 entity_id: EntityId(3),
321 role: "object".to_string(),
322 },
323 ],
324 location: Some(EntityRef {
325 entity_id: EntityId(100),
326 role: "location".to_string(),
327 }),
328 mood: Mood::Neutral,
329 stakes: Stakes::Low,
330 outcome: None,
331 narrative_fn: NarrativeFunction::ComicRelief,
332 metadata: HashMap::new(),
333 };
334 print_scene(
335 6,
336 "The Aftermath",
337 &["Robert", "Eleanor"],
338 &mut engine,
339 &event6,
340 &world,
341 );
342
343 // --- Scene 7: Betrayal (Margaret realizes James and Eleanor) ---
344 let event7 = Event {
345 event_type: "betrayal_realized".to_string(),
346 participants: vec![
347 EntityRef {
348 entity_id: EntityId(1),
349 role: "subject".to_string(),
350 },
351 EntityRef {
352 entity_id: EntityId(2),
353 role: "object".to_string(),
354 },
355 ],
356 location: Some(EntityRef {
357 entity_id: EntityId(100),
358 role: "location".to_string(),
359 }),
360 mood: Mood::Somber,
361 stakes: Stakes::Critical,
362 outcome: None,
363 narrative_fn: NarrativeFunction::Betrayal,
364 metadata: HashMap::new(),
365 };
366 print_scene(
367 7,
368 "The Betrayal",
369 &["Margaret", "James"],
370 &mut engine,
371 &event7,
372 &world,
373 );
374
375 println!("========================================");
376 println!(" FIN");
377 println!("========================================");
378}More examples
examples/dino_park.rs (lines 21-23)
19fn main() {
20 // --- Load Survival Thriller genre template ---
21 let grammars = GrammarSet::load_from_ron(std::path::Path::new(
22 "genre_data/survival_thriller/grammar.ron",
23 ))
24 .expect("Failed to load survival thriller grammar");
25
26 let mut voices = VoiceRegistry::new();
27 voices
28 .load_from_ron(std::path::Path::new(
29 "genre_data/survival_thriller/voices.ron",
30 ))
31 .expect("Failed to load survival thriller voices");
32
33 // --- Train Markov model from survival thriller corpus ---
34 let corpus = std::fs::read_to_string("genre_data/survival_thriller/corpus.txt")
35 .expect("Failed to read survival thriller corpus");
36 let markov_model = MarkovTrainer::train(&corpus, 3);
37
38 let mut markov_models = HashMap::new();
39 markov_models.insert("survival_thriller".to_string(), markov_model);
40
41 let mut engine = NarrativeEngine::builder()
42 .seed(1993)
43 .with_grammars(grammars)
44 .with_voices(voices)
45 .with_markov_models(markov_models)
46 .build()
47 .expect("Failed to build engine");
48
49 // --- Define entities ---
50 let mut entities = HashMap::new();
51
52 // Dr. Grant — paleontologist, survivor instinct
53 entities.insert(
54 EntityId(1),
55 Entity {
56 id: EntityId(1),
57 name: "Dr. Grant".to_string(),
58 pronouns: Pronouns::HeHim,
59 tags: [
60 "scientist".to_string(),
61 "determined".to_string(),
62 "field_expert".to_string(),
63 ]
64 .into_iter()
65 .collect(),
66 relationships: Vec::new(),
67 voice_id: Some(VoiceId(202)), // scientist voice
68 properties: HashMap::new(),
69 },
70 );
71
72 // Dr. Malcolm — chaos theorist, always right at the worst time
73 entities.insert(
74 EntityId(2),
75 Entity {
76 id: EntityId(2),
77 name: "Dr. Malcolm".to_string(),
78 pronouns: Pronouns::HeHim,
79 tags: [
80 "scientist".to_string(),
81 "skeptic".to_string(),
82 "charismatic".to_string(),
83 ]
84 .into_iter()
85 .collect(),
86 relationships: Vec::new(),
87 voice_id: Some(VoiceId(202)), // scientist voice
88 properties: HashMap::new(),
89 },
90 );
91
92 // Muldoon — game warden, knows the danger
93 entities.insert(
94 EntityId(3),
95 Entity {
96 id: EntityId(3),
97 name: "Muldoon".to_string(),
98 pronouns: Pronouns::HeHim,
99 tags: [
100 "hunter".to_string(),
101 "pragmatic".to_string(),
102 "alert".to_string(),
103 ]
104 .into_iter()
105 .collect(),
106 relationships: Vec::new(),
107 voice_id: Some(VoiceId(201)), // survivor voice
108 properties: HashMap::new(),
109 },
110 );
111
112 // Control Room — the nerve center
113 entities.insert(
114 EntityId(10),
115 Entity {
116 id: EntityId(10),
117 name: "Control Room".to_string(),
118 pronouns: Pronouns::ItIts,
119 tags: [
120 "location".to_string(),
121 "technology".to_string(),
122 "enclosed".to_string(),
123 ]
124 .into_iter()
125 .collect(),
126 relationships: Vec::new(),
127 voice_id: None,
128 properties: HashMap::new(),
129 },
130 );
131
132 // Rex Paddock — T. rex enclosure
133 entities.insert(
134 EntityId(11),
135 Entity {
136 id: EntityId(11),
137 name: "Rex Paddock".to_string(),
138 pronouns: Pronouns::ItIts,
139 tags: [
140 "location".to_string(),
141 "dangerous".to_string(),
142 "perimeter".to_string(),
143 ]
144 .into_iter()
145 .collect(),
146 relationships: Vec::new(),
147 voice_id: None,
148 properties: HashMap::new(),
149 },
150 );
151
152 // Raptor Pen — velociraptors
153 entities.insert(
154 EntityId(12),
155 Entity {
156 id: EntityId(12),
157 name: "Raptor Pen".to_string(),
158 pronouns: Pronouns::ItIts,
159 tags: [
160 "location".to_string(),
161 "dangerous".to_string(),
162 "high_security".to_string(),
163 ]
164 .into_iter()
165 .collect(),
166 relationships: Vec::new(),
167 voice_id: None,
168 properties: HashMap::new(),
169 },
170 );
171
172 // Security System — abstract entity
173 entities.insert(
174 EntityId(20),
175 Entity {
176 id: EntityId(20),
177 name: "Security System".to_string(),
178 pronouns: Pronouns::ItIts,
179 tags: ["system".to_string(), "automated".to_string()]
180 .into_iter()
181 .collect(),
182 relationships: Vec::new(),
183 voice_id: None,
184 properties: HashMap::new(),
185 },
186 );
187
188 let world = WorldState {
189 entities: &entities,
190 };
191
192 // --- Title ---
193 println!("========================================");
194 println!(" DINO PARK INCIDENT REPORT");
195 println!(" [CLASSIFIED — Park Security]");
196 println!("========================================");
197 println!();
198
199 // --- Scene 1: Routine Status (StatusChange — neutral, low stakes) ---
200 // radio_operator voice — terse, technical
201 let event1 = Event {
202 event_type: "routine_check".to_string(),
203 participants: vec![EntityRef {
204 entity_id: EntityId(3),
205 role: "subject".to_string(),
206 }],
207 location: Some(EntityRef {
208 entity_id: EntityId(10),
209 role: "location".to_string(),
210 }),
211 mood: Mood::Neutral,
212 stakes: Stakes::Low,
213 outcome: None,
214 narrative_fn: NarrativeFunction::StatusChange,
215 metadata: HashMap::new(),
216 };
217 print_scene(
218 1,
219 "0600 — Morning Status Report",
220 "RADIO OPERATOR",
221 &mut engine,
222 &event1,
223 &world,
224 Some(VoiceId(200)),
225 );
226
227 // --- Scene 2: Power Warning (Foreshadowing — neutral/dread, medium stakes) ---
228 // narrator_omniscient voice — atmospheric
229 let event2 = Event {
230 event_type: "power_fluctuation".to_string(),
231 participants: vec![EntityRef {
232 entity_id: EntityId(1),
233 role: "subject".to_string(),
234 }],
235 location: Some(EntityRef {
236 entity_id: EntityId(10),
237 role: "location".to_string(),
238 }),
239 mood: Mood::Neutral,
240 stakes: Stakes::Medium,
241 outcome: None,
242 narrative_fn: NarrativeFunction::Foreshadowing,
243 metadata: HashMap::new(),
244 };
245 print_scene(
246 2,
247 "1430 — Power Fluctuation Detected",
248 "NARRATOR",
249 &mut engine,
250 &event2,
251 &world,
252 Some(VoiceId(203)),
253 );
254
255 // --- Scene 3: Perimeter Breach (Escalation — dread, high stakes) ---
256 // radio_operator voice
257 let event3 = Event {
258 event_type: "perimeter_breach".to_string(),
259 participants: vec![EntityRef {
260 entity_id: EntityId(3),
261 role: "subject".to_string(),
262 }],
263 location: Some(EntityRef {
264 entity_id: EntityId(11),
265 role: "location".to_string(),
266 }),
267 mood: Mood::Dread,
268 stakes: Stakes::High,
269 outcome: None,
270 narrative_fn: NarrativeFunction::Escalation,
271 metadata: HashMap::new(),
272 };
273 print_scene(
274 3,
275 "2247 — Perimeter Breach: Rex Paddock",
276 "RADIO OPERATOR",
277 &mut engine,
278 &event3,
279 &world,
280 Some(VoiceId(200)),
281 );
282
283 // --- Scene 4: Escalation (Escalation — dread/chaotic, critical) ---
284 // narrator_omniscient — full atmospheric dread
285 let event4 = Event {
286 event_type: "systems_failing".to_string(),
287 participants: vec![
288 EntityRef {
289 entity_id: EntityId(1),
290 role: "subject".to_string(),
291 },
292 EntityRef {
293 entity_id: EntityId(2),
294 role: "object".to_string(),
295 },
296 ],
297 location: Some(EntityRef {
298 entity_id: EntityId(10),
299 role: "location".to_string(),
300 }),
301 mood: Mood::Chaotic,
302 stakes: Stakes::Critical,
303 outcome: None,
304 narrative_fn: NarrativeFunction::Escalation,
305 metadata: HashMap::new(),
306 };
307 print_scene(
308 4,
309 "2253 — Multiple System Failures",
310 "NARRATOR",
311 &mut engine,
312 &event4,
313 &world,
314 Some(VoiceId(203)),
315 );
316
317 // --- Scene 5: Discovery of Damage (Discovery — dread, high stakes) ---
318 // Dr. Grant (scientist voice) discovers the extent
319 let event5 = Event {
320 event_type: "damage_assessment".to_string(),
321 participants: vec![EntityRef {
322 entity_id: EntityId(1),
323 role: "subject".to_string(),
324 }],
325 location: Some(EntityRef {
326 entity_id: EntityId(12),
327 role: "location".to_string(),
328 }),
329 mood: Mood::Dread,
330 stakes: Stakes::High,
331 outcome: None,
332 narrative_fn: NarrativeFunction::Discovery,
333 metadata: HashMap::new(),
334 };
335 print_scene(
336 5,
337 "2301 — Discovery: Raptor Pen Integrity",
338 "DR. GRANT",
339 &mut engine,
340 &event5,
341 &world,
342 None,
343 );
344
345 // --- Scene 6: Loss (Loss — somber, critical) ---
346 // radio_operator — the final status
347 let event6 = Event {
348 event_type: "critical_failure".to_string(),
349 participants: vec![EntityRef {
350 entity_id: EntityId(3),
351 role: "subject".to_string(),
352 }],
353 location: Some(EntityRef {
354 entity_id: EntityId(10),
355 role: "location".to_string(),
356 }),
357 mood: Mood::Somber,
358 stakes: Stakes::Critical,
359 outcome: None,
360 narrative_fn: NarrativeFunction::Loss,
361 metadata: HashMap::new(),
362 };
363 print_scene(
364 6,
365 "2315 — Critical Failure: All Systems",
366 "RADIO OPERATOR",
367 &mut engine,
368 &event6,
369 &world,
370 Some(VoiceId(200)),
371 );
372
373 // --- Scene 7: Final atmospheric beat ---
374 // narrator_omniscient — the island at night
375 let event7 = Event {
376 event_type: "aftermath".to_string(),
377 participants: vec![EntityRef {
378 entity_id: EntityId(2),
379 role: "subject".to_string(),
380 }],
381 location: Some(EntityRef {
382 entity_id: EntityId(11),
383 role: "location".to_string(),
384 }),
385 mood: Mood::Dread,
386 stakes: Stakes::Critical,
387 outcome: None,
388 narrative_fn: NarrativeFunction::Loss,
389 metadata: HashMap::new(),
390 };
391 print_scene(
392 7,
393 "2330 — Final Log Entry",
394 "NARRATOR",
395 &mut engine,
396 &event7,
397 &world,
398 Some(VoiceId(203)),
399 );
400
401 println!("========================================");
402 println!(" [END OF INCIDENT REPORT]");
403 println!(" [STATUS: FACILITY ABANDONED]");
404 println!("========================================");
405}Sourcepub fn parse_ron(input: &str) -> Result<GrammarSet, GrammarError>
pub fn parse_ron(input: &str) -> Result<GrammarSet, GrammarError>
Parse a grammar set from a RON string.
Sourcepub fn merge(&mut self, other: GrammarSet)
pub fn merge(&mut self, other: GrammarSet)
Merge another grammar set into this one. Rules from other
override rules in self with the same name.
Sourcepub fn find_matching_rules<'a, 'b>(
&'a self,
ctx: &SelectionContext<'b>,
) -> Vec<&'a GrammarRule>
pub fn find_matching_rules<'a, 'b>( &'a self, ctx: &SelectionContext<'b>, ) -> Vec<&'a GrammarRule>
Find all rules whose requires tags are a subset of the context’s
active tags, and whose excludes tags have no intersection.
Sourcepub fn expand(
&self,
rule_name: &str,
ctx: &mut SelectionContext<'_>,
rng: &mut StdRng,
) -> Result<String, GrammarError>
pub fn expand( &self, rule_name: &str, ctx: &mut SelectionContext<'_>, rng: &mut StdRng, ) -> Result<String, GrammarError>
Expand a named rule into text using the given context and RNG.
Trait Implementations§
Source§impl Clone for GrammarSet
impl Clone for GrammarSet
Source§fn clone(&self) -> GrammarSet
fn clone(&self) -> GrammarSet
Returns a duplicate of the value. Read more
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Debug for GrammarSet
impl Debug for GrammarSet
Source§impl Default for GrammarSet
impl Default for GrammarSet
Source§fn default() -> GrammarSet
fn default() -> GrammarSet
Returns the “default value” for a type. Read more
Source§impl<'de> Deserialize<'de> for GrammarSet
impl<'de> Deserialize<'de> for GrammarSet
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Deserialize this value from the given Serde deserializer. Read more
Auto Trait Implementations§
impl Freeze for GrammarSet
impl RefUnwindSafe for GrammarSet
impl Send for GrammarSet
impl Sync for GrammarSet
impl Unpin for GrammarSet
impl UnsafeUnpin for GrammarSet
impl UnwindSafe for GrammarSet
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more