This commit completes the migration started in the previous commit, updating all remaining files: - Lexer: Changed token from Extends to Modifies - Parser: Updated lalrpop grammar rules and AST field names - AST: Renamed Schedule.extends field to modifies - Grammar: Updated tree-sitter grammar.js - Tree-sitter: Regenerated parser.c and node-types.json - Examples: Updated baker-family work schedules - Tests: Updated schedule composition tests and corpus - Docs: Updated all reference documentation and tutorials - Validation: Updated error messages and validation logic - Package: Bumped version to 0.3.1 in all package manifests All 554 tests pass.
9.2 KiB
Design Patterns
This chapter presents proven patterns for structuring Storybook projects. These patterns have emerged from building complex narrative simulations and represent best practices for maintainability, reuse, and clarity.
Behavior Tree Patterns
Priority Fallback Chain
Use a selector to try increasingly desperate options:
behavior Survival {
choose survival_priority {
then optimal {
if(health > 70 and has_supplies)
ProceedNormally
}
then cautious {
if(health > 30)
ProceedCarefully
}
then desperate {
if(health > 10)
SeekHelp
}
LastResortPanic
}
}
The tree naturally degrades: first tries the best option, then falls back through progressively worse alternatives.
Conditional Behavior Switching
Use guards at the top level to switch between behavioral modes:
behavior ModeSwitcher {
choose mode {
if(is_combat_mode) {
include CombatBehavior
}
if(is_exploration_mode) {
include ExplorationBehavior
}
if(is_social_mode) {
include SocialBehavior
}
include IdleBehavior
}
}
Composite Subtree Pattern
Break complex behaviors into focused, reusable subtrees:
// Atomic subtrees
behavior Navigate { then nav { PlanPath, FollowPath } }
behavior Interact { then talk { Approach, Greet, Converse } }
behavior Trade { then exchange { ShowGoods, Negotiate, Exchange } }
// Composed behavior
behavior Merchant_AI {
choose activity {
then serve_customer {
if(customer_present)
include Interact
include Trade
}
then travel_to_market {
if(is_market_day)
include Navigate
}
Idle
}
}
Repeating Patrol with Interrupts
Use a repeating patrol that can be interrupted by higher-priority events:
character Guard {
uses behaviors: [
{
tree: GuardCombat
when: threat_detected
priority: high
},
{
tree: GuardPatrol
priority: normal
}
]
}
behavior GuardPatrol {
repeat {
then patrol_loop {
MoveTo(destination: "Waypoint1")
WaitAndScan(duration: 5s)
MoveTo(destination: "Waypoint2")
WaitAndScan(duration: 5s)
}
}
}
The combat behavior preempts patrol when threats appear, then patrol resumes.
Character Architecture Patterns
Species + Templates Composition
Use species for identity and templates for capabilities:
// Species: What they ARE
species Human { lifespan: 70 }
species Elf { lifespan: 1000 }
// Templates: What they HAVE
template Warrior { strength: 10..20, weapon_skill: 0.5..1.0 }
template Scholar { intelligence: 15..20, books_read: 50..500 }
template Leader { charisma: 12..18, followers: 5..50 }
// Characters: Combine both
character Aragorn: Human from Warrior, Leader {
strength: 18
charisma: 17
}
character Elrond: Elf from Scholar, Leader {
intelligence: 20
charisma: 18
}
Strict Templates for Schema Enforcement
Use strict templates when you need controlled, uniform entities:
template RecipeCard strict {
recipe_name: string
difficulty: Difficulty
prep_time_minutes: 10..180
}
// This works:
character SourdoughRecipe from RecipeCard {
recipe_name: "Classic Sourdough"
difficulty: intermediate
prep_time_minutes: 120
}
// This would error (extra field not allowed):
// character BadRecipe from RecipeCard {
// recipe_name: "Mystery Bread"
// difficulty: easy
// favorite_color: "blue" // Error!
// }
Template Inheritance Chains
Build template hierarchies for progressive specialization:
template Worker {
skill_level: 0.0..1.0
wage: 10..50
}
template SkilledWorker {
include Worker
specialization: "general"
tool_proficiency: 0.5..1.0
}
template MasterCraftsman {
include SkilledWorker
can_teach: true
reputation: 0.7..1.0
}
Relationship Patterns
Bidirectional Perspective
Model relationships where each side sees things differently:
relationship MentorApprentice {
Master as mentor self {
patience: 0.7
investment_in_student: 0.9
} other {
sees_potential: 0.8
frustration_level: 0.3
}
Student as apprentice self {
dedication: 0.8
overwhelmed: 0.4
} other {
respect: 0.95
desire_to_impress: 0.9
}
bond: 0.75
years_together: 3
}
Power Dynamic Pattern
Model unequal power relationships explicitly:
relationship Vassalage {
King as lord self {
authority: 1.0
grants: "protection"
} other {
trusts_vassal: 0.6
}
Knight as vassal self {
loyalty: 0.9
ambition: 0.4
} other {
respects_lord: 0.8
fears_lord: 0.3
}
bond: 0.7
}
Relationship Network
Build social graphs with multiple overlapping relationships:
// Family
relationship BakerMarriage { Martha as spouse, David as spouse, bond: 0.9 }
relationship BakerParenting { Martha as parent, Tommy as child, bond: 0.95 }
// Professional
relationship BakerEmployment { Martha as employer, Elena as employee, bond: 0.8 }
relationship GuildMembership { Martha as member, BakersGuild as org }
// Social
relationship BakerFriendship { Martha, Neighbor, bond: 0.6 }
Schedule Patterns
Base Schedule with Specializations
schedule BaseWorker {
block work { 09:00 - 17:00, action: work::standard }
block lunch { 12:00 - 13:00, action: social::lunch }
}
schedule EarlyBird modifies BaseWorker {
block work { 05:00 - 13:00, action: work::early_shift }
block lunch { 11:00 - 12:00, action: social::lunch }
}
schedule NightOwl modifies BaseWorker {
block work { 14:00 - 22:00, action: work::late_shift }
block lunch { 18:00 - 19:00, action: social::dinner }
}
Seasonal Variation
schedule FarmSchedule {
block spring_work {
06:00 - 18:00
action: farming::plant
on season spring
}
block summer_work {
05:00 - 20:00
action: farming::tend
on season summer
}
block fall_work {
06:00 - 20:00
action: farming::harvest
on season fall
}
block winter_work {
08:00 - 16:00
action: farming::maintain
on season winter
}
}
Life Arc Patterns
Progressive Development
life_arc CareerProgression {
state novice {
on enter { Character.title: "Apprentice" }
on experience > 100 -> intermediate
}
state intermediate {
on enter { Character.title: "Journeyman" }
on experience > 500 -> expert
}
state expert {
on enter { Character.title: "Master", Character.can_teach: true }
}
}
Emotional State Machine
life_arc MoodSystem {
state neutral {
on provoked -> angry
on complimented -> happy
on tired -> sleepy
}
state angry {
on enter { Character.aggression: 0.9 }
on calmed_down -> neutral
on escalated -> furious
}
state furious {
on enter { Character.aggression: 1.0 }
on timeout_elapsed -> angry
}
state happy {
on enter { Character.gives_discounts: true }
on insulted -> neutral
}
state sleepy {
on enter { Character.responsiveness: 0.2 }
on woke_up -> neutral
}
}
Project Organization Patterns
Schema / World Separation
Keep type definitions separate from instance data:
my-project/
schema/ # Types and templates (reusable)
core_enums.sb
templates.sb
beings.sb
world/ # Instances (specific to this story)
characters/
behaviors/
relationships/
locations/
Module per Domain
Group related declarations together:
world/
characters/
heroes.sb # All hero characters
villains.sb # All villain characters
npcs.sb # Background characters
behaviors/
combat.sb # Combat behaviors
social.sb # Social behaviors
exploration.sb # Exploration behaviors
Anti-Patterns to Avoid
Deep nesting: More than 4-5 levels of behavior tree nesting is hard to read. Use include to flatten.
God behaviors: One massive behavior tree doing everything. Break it into focused subtrees.
Deep species hierarchies: More than 2-3 levels of species includes is rarely needed. Use templates for variation.
Duplicated logic: If two behaviors share logic, extract it into a shared subtree.
Unnamed nodes: Always label composite nodes in behavior trees for readability.
Cross-References
- Behavior Trees - Complete behavior syntax
- Characters - Character architecture
- Relationships - Relationship modeling
- Schedules - Schedule composition
- Life Arcs - State machine patterns