@antogin/werewolf-engine
v1.0.5
Published
Event-sourced werewolf game engine
Readme
Werewolf Game Engine
Welcome to the Werewolf Game Engine, a sophisticated implementation of the classic social deduction game using Event Sourcing and Command Query Responsibility Segregation (CQRS) patterns. This document explains our architectural decisions and how they enable a robust, scalable, and maintainable game engine.
Core Architecture
Our Werewolf Game Engine is built on two fundamental architectural patterns: Event Sourcing and CQRS. Let's explore why we chose these patterns and how they benefit our implementation.
Event Sourcing: Capturing Game State
In traditional game implementations, you might store the current state of the game in a database. However, Werewolf is a game where the history of actions is crucial. Players need to know what happened during the night, who voted for whom, and how the game progressed. Event Sourcing addresses these needs perfectly by storing every game action as an immutable event.
Consider this example of how events tell the story of a game:
[
{ type: "GameCreated", timestamp: "2024-01-01T20:00:00Z", moderatorId: "mod1" },
{ type: "PlayerJoined", timestamp: "2024-01-01T20:01:00Z", playerId: "p1" },
{ type: "RoleAssigned", timestamp: "2024-01-01T20:05:00Z", playerId: "p1", role: "WEREWOLF" },
{ type: "NightPhaseStarted", timestamp: "2024-01-01T20:10:00Z", nightNumber: 1 }
]This approach provides several benefits:
- Complete audit trail of the game
- Ability to reconstruct game state at any point
- Built-in support for game replay and spectator mode
- Natural handling of game saves and loading
CQRS: Separating Game Actions from Views
Werewolf is a game of hidden information. Werewolves know who other werewolves are, the Seer learns player roles gradually, and villagers operate with limited information. CQRS allows us to maintain these different views efficiently by separating how we handle game actions (commands) from how we present information to players (queries).
Our command side handles game actions:
- Starting the game
- Casting votes
- Using special abilities
- Making accusations
While our query side manages different views for:
- Werewolf team members
- Individual special roles (Seer, Witch)
- Villagers
- Spectators
- Game moderators
Event Design Philosophy
Our events are designed around several key principles:
Complete Information
Every event contains all the information needed to understand what happened. For example, a WerewolfVoteCast event includes:
- Who cast the vote (werewolfId)
- Who was voted for (targetId)
- When it happened (timestamp)
- The game context (gameId)
Immutable History
Events are never modified or deleted. If a player changes their vote, we create a new VoteChanged event rather than modifying the original vote. This preserves the complete game history and allows for accurate replay.
Temporal Consistency
All events include timestamps and version numbers to maintain proper ordering. This is crucial for night actions where the Witch must act after the Werewolves, or for resolving simultaneous day phase votes.
State Management
The game state is managed through distinct phases, each with its own set of valid commands and resulting events:
Game Setup Phase
- Player joining and role assignment
- Game configuration and settings
- Initial state validation
Night Phase
- Sequential special role actions
- Coordinated werewolf voting
- Death and save resolutions
Day Phase
- Discussion and accusation tracking
- Village voting system
- Execution resolution
Win Condition Checking
- Team balance verification
- Special win conditions
- Game end state management
Handling Edge Cases
Our architecture specifically addresses common game challenges:
Disconnections
The event-sourced nature of our system means players can disconnect and reconnect seamlessly. Their game state is reconstructed from the event stream, ensuring they don't miss any information they're allowed to know.
Concurrent Actions
We handle multiple werewolves voting simultaneously through a two-phase voting system:
- Individual votes are recorded as WerewolfVoteCast events
- Once all votes are in, a WerewolfVoteCompleted event determines the final victim
Time Management
Game phases can have optional time limits, managed through:
- Phase start events with duration
- Timer events for phase progression
- Forced resolution events for expired timers
Read Models
Our read models are tailored to different player roles and game phases:
Player-Specific Views
Each role gets a customized view of the game state. For example:
- Werewolves see other werewolves but not the Seer's discoveries
- The Seer accumulates knowledge of player roles over time
- Villagers only see public information and voting results
Game Progress Views
- Current game phase and timer status
- Player alive/dead status
- Vote tallies and results
- Game history for spectators
Game Flow
Here is diagram of the game flow:
stateDiagram-v2
[*] --> GameCreated: CreateGame
GameCreated --> PlayersJoining: StartJoining
state PlayersJoining {
[*] --> WaitingForPlayers
WaitingForPlayers --> EnoughPlayers: PlayerJoined
EnoughPlayers --> [*]: AssignRoles
}
PlayersJoining --> NightPhase: GameStarted
state NightPhase {
[*] --> WerewolvesAction
WerewolvesAction --> SeerAction: WerewolvesChoseVictim
SeerAction --> WitchAction: SeerCheckedPlayer
state WitchAction {
[*] --> PotionChoice
PotionChoice --> HealingChoice: WitchWakeCalled
HealingChoice --> PoisonChoice: WitchHealingDecided
PoisonChoice --> [*]: WitchPoisonDecided
}
}
NightPhase --> DayPhase: NightResolved
state DayPhase {
[*] --> DeathAnnouncement
DeathAnnouncement --> Discussion: DeathsAnnounced
Discussion --> Voting: DiscussionEnded
Voting --> VoteResolution: VotesCast
VoteResolution --> [*]: VoteResolved
}
DayPhase --> WinCheck: DayResolved
state WinCheck {
[*] --> Counting
Counting --> WerewolvesWin: WerewolvesReachedMajority
Counting --> VillageWin: AllWerewolvesDead
Counting --> GameContinues: NoWinCondition
}
WinCheck --> NightPhase: NextNightStarted
WinCheck --> [*]: GameEndedTesting Strategy
Our event-sourced architecture makes testing straightforward:
Command Validation Tests
- Verify that commands produce expected events
- Check that invalid commands are rejected
Event Application Tests
- Ensure events properly update game state
- Verify that event sequences maintain consistency
Read Model Tests
- Confirm that different roles see appropriate information
- Check that game history is properly reconstructed
Getting Started
[Installation and setup instructions will go here]
Contributing
[Contribution guidelines will go here]
License
[License information will go here]
