Veggerby.Boards.DeckBuilding
0.1.0-prerelease0212
dotnet add package Veggerby.Boards.DeckBuilding --version 0.1.0-prerelease0212
NuGet\Install-Package Veggerby.Boards.DeckBuilding -Version 0.1.0-prerelease0212
<PackageReference Include="Veggerby.Boards.DeckBuilding" Version="0.1.0-prerelease0212" />
<PackageVersion Include="Veggerby.Boards.DeckBuilding" Version="0.1.0-prerelease0212" />
<PackageReference Include="Veggerby.Boards.DeckBuilding" />
paket add Veggerby.Boards.DeckBuilding --version 0.1.0-prerelease0212
#r "nuget: Veggerby.Boards.DeckBuilding, 0.1.0-prerelease0212"
#:package Veggerby.Boards.DeckBuilding@0.1.0-prerelease0212
#addin nuget:?package=Veggerby.Boards.DeckBuilding&version=0.1.0-prerelease0212&prerelease
#tool nuget:?package=Veggerby.Boards.DeckBuilding&version=0.1.0-prerelease0212&prerelease
Veggerby.Boards.DeckBuilding
Deck-building core module built on Veggerby.Boards and Veggerby.Boards.Cards providing deterministic supply management, player zones (deck/hand/discard/in-play), and phase-based gameplay foundation.
Depends on
Veggerby.BoardsandVeggerby.Boards.Cards. Use when building deck-building games like Dominion, Ascension, or Star Realms.
Install
Package publishes with the rest of the suite. Until then, reference the project directly.
Overview
This module provides a deterministic foundation for deck-building games with:
- CardDefinition: Metadata artifacts (name, types, cost, victory points)
- Player Zones: Immutable pile management (Deck, Hand, Discard, InPlay)
- Deterministic Shuffles: Seeded RNG for reproducible gameplay
- Phase Structure: Setup → Action → Buy → Cleanup
- Supply Management: Centralized card pool with depletion tracking
- Scoring & Termination: Victory point computation and game-end detection
Quick Start
using Veggerby.Boards.DeckBuilding;
// Create a deck-building game
var builder = new DeckBuildingGameBuilder();
var progress = builder.Compile();
// Initialize a player's deck
var player = progress.Game.GetPlayer("player-1");
var deck = progress.Game.GetArtifact<Deck>("deck-1");
var piles = new Dictionary<string, IList<Card>>
{
[DeckBuildingGameBuilder.Piles.Draw] = new List<Card>(),
[DeckBuildingGameBuilder.Piles.Discard] = new List<Card>(),
[DeckBuildingGameBuilder.Piles.Hand] = new List<Card>(),
[DeckBuildingGameBuilder.Piles.InPlay] = new List<Card>(),
};
var supply = new Dictionary<string, int> { ["copper"] = 7, ["estate"] = 3 };
progress = progress.HandleEvent(new CreateDeckEvent(deck, piles, supply));
// Draw cards (with automatic reshuffle when deck empty)
progress = progress.HandleEvent(new DrawWithReshuffleEvent(deck, count: 5));
// Gain a card from supply
progress = progress.HandleEvent(new GainFromSupplyEvent(
player, deck, "copper", DeckBuildingGameBuilder.Piles.Discard));
// Trash cards from hand
progress = progress.HandleEvent(new TrashFromHandEvent(deck, cardsToTrash));
// End turn cleanup
progress = progress.HandleEvent(new CleanupToDiscardEvent(deck));
Key Concepts
Phases
| Phase | Purpose | Events |
|---|---|---|
| Setup | Initialize deck and supply | CreateDeckEvent |
| Action | Draw cards and trash | DrawWithReshuffleEvent, TrashFromHandEvent |
| Buy | Gain cards from supply | GainFromSupplyEvent |
| Cleanup | Move cards to discard, compute scores, end game | CleanupToDiscardEvent, ComputeScoresEvent, EndGameEvent |
Events & Mutators
| Event | Mutator | Description |
|---|---|---|
RegisterCardDefinitionEvent |
RegisterCardDefinitionStateMutator |
Register card metadata (name, types, cost, VP) |
CreateDeckEvent |
CreateDeckStateMutator |
Initialize player zones and supply |
GainFromSupplyEvent |
GainFromSupplyStateMutator |
Move card from supply to player pile, decrement supply |
DrawWithReshuffleEvent |
DrawWithReshuffleStateMutator |
Draw cards, reshuffle discard if needed |
TrashFromHandEvent |
TrashFromHandStateMutator |
Remove cards from hand (permanent) |
CleanupToDiscardEvent |
CleanupToDiscardStateMutator |
Move Hand + InPlay to Discard |
ComputeScoresEvent |
ComputeScoresStateMutator |
Calculate victory points per player |
EndGameEvent |
EndGameStateMutator |
Mark game as ended |
Zones (Player Piles)
Each player has four standard piles:
- Draw: Cards to be drawn
- Hand: Cards currently available for play
- InPlay: Cards played this turn
- Discard: Used cards awaiting reshuffle
All pile operations are immutable; mutators return new state snapshots.
Deterministic Shuffles
Shuffles use GameState.Random with seeded RNG:
var builder = new DeckBuildingGameBuilder();
builder.WithSeed(12345); // Reproducible shuffle order
var progress = builder.Compile();
Same seed → same shuffle sequence across all runs.
Supply Management
The supply is a shared pool of available cards:
- Tracked in
DeckSupplyStatsfor O(1) empty pile detection - Decremented on
GainFromSupplyEvent - Used for game-end trigger (see below)
Testing
Run the module tests:
cd test/Veggerby.Boards.Tests
dotnet test --filter "FullyQualifiedName~DeckBuilding"
Test Coverage:
- Deck initialization and supply setup
- Gain from supply (happy path and insufficient supply)
- Draw with automatic reshuffle (determinism verified)
- Trash validation (card presence in hand)
- Cleanup behavior (all cards moved to discard)
- Scoring computation (victory point aggregation)
- End-game trigger conditions
- Structural invariants (phase ordering, event presence)
Game-End Trigger
Configure supply depletion trigger:
builder.WithEndTrigger(new DeckBuildingEndTriggerOptions(
emptySupplyPilesThreshold: 3, // optional > 0 threshold
keySupplyPileIds: new[]{"province", "colony"} // optional specific key piles that must all be empty
));
Validation rule (enforced in constructor): at least one of the following must be configured:
emptySupplyPilesThreshold > 0- A non-empty
keySupplyPileIdscollection
If neither is provided, an ArgumentException is thrown. Negative thresholds throw ArgumentOutOfRangeException.
At runtime the EndGameEventCondition requires scores to have been computed (via a preceding ComputeScoresEvent) before a depletion-triggered end may validate; otherwise the end event is ignored until scoring occurs.
Error Modes
Gain From Supply Failures
GainFromSupplyEventCondition returns Fail (raising InvalidGameEventException) with these reasons:
- Unknown pile: Target pile id not present in the deck state
- Insufficient supply: Supply dictionary does not contain the card id or its count is zero
- Unknown card id: Card artifact not registered in the game even though supply references it
The exception message includes the failing reason (e.g., Invalid game event GainFromSupplyEvent: Unknown pile).
Known Limitations
- Card Effects: No action card effects implemented (beyond basic zone transitions)
- Attack/Reaction: Interaction between players not modeled
- Duration Cards: Multi-turn effect persistence not supported
- Cost Reduction: Dynamic pricing not implemented
- Benchmarks: Partial coverage only (shuffle, gain, condition gating)
Extending This Module
Common extension scenarios:
Adding Action Cards
public sealed record PlayActionEvent(Deck Deck, Card ActionCard) : IGameEvent;
public sealed class PlayActionStateMutator : IStateMutator<PlayActionEvent>
{
public GameState MutateState(GameEngine engine, GameState state, PlayActionEvent @event)
{
// Move card from Hand to InPlay
// Apply card-specific effects (draw, trash, coins, etc.)
}
}
Implementing Card Effects
public interface ICardEffect
{
GameState Apply(GameEngine engine, GameState state, Deck deck);
}
public sealed class DrawCardsEffect : ICardEffect
{
private readonly int _count;
public GameState Apply(GameEngine engine, GameState state, Deck deck)
{
// Execute draw logic
}
}
Custom End-Game Triggers
public sealed class VictoryPointThresholdCondition : IGameStateCondition
{
private readonly int _threshold;
public ConditionResponse Evaluate(Game game, GameState state)
{
// Check if any player has reached VP threshold
}
}
References
- Core Documentation: See /docs/core-concepts.md for engine fundamentals
- Deck Building Guide: See /docs/deck-building.md for detailed module documentation
- Cards Module: See /src/Veggerby.Boards.Cards/README.md for underlying card primitives
- Phase Patterns: See /docs/phase-based-design.md for phase design guidelines
Versioning
Semantic versioning aligned with repository releases. Breaking event or rule changes bump MAJOR.
Contributing
Open issues & PRs at https://github.com/veggerby/Veggerby.Boards. Follow contributor guidelines.
License
MIT License. See root LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Veggerby.Algorithm (>= 1.0.0)
- Veggerby.Boards (>= 0.1.0-prerelease0212)
- Veggerby.Boards.Cards (>= 0.1.0-prerelease0212)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0-prerelease0212 | 78 | 4/13/2026 |
| 0.1.0-prerelease0211 | 79 | 2/8/2026 |
| 0.1.0-prerelease0210 | 76 | 1/26/2026 |
| 0.1.0-prerelease0209 | 74 | 1/12/2026 |
| 0.1.0-prerelease0208 | 73 | 1/11/2026 |
| 0.1.0-prerelease0207 | 78 | 1/11/2026 |
| 0.1.0-prerelease0206 | 78 | 1/11/2026 |
| 0.1.0-prerelease0205 | 78 | 1/10/2026 |
| 0.1.0-prerelease0204 | 84 | 1/9/2026 |
| 0.1.0-prerelease0203 | 82 | 1/9/2026 |
| 0.1.0-prerelease0202 | 73 | 1/9/2026 |
| 0.1.0-prerelease0201 | 79 | 1/8/2026 |
| 0.1.0-prerelease0200 | 78 | 1/8/2026 |
| 0.1.0-prerelease0199 | 77 | 1/7/2026 |
| 0.1.0-prerelease0198 | 76 | 1/6/2026 |
| 0.1.0-prerelease0197 | 74 | 1/6/2026 |
| 0.1.0-prerelease0196 | 75 | 1/6/2026 |
| 0.1.0-prerelease0195 | 75 | 1/5/2026 |
| 0.1.0-prerelease0194 | 84 | 1/4/2026 |
| 0.1.0-prerelease0193 | 244 | 12/17/2025 |