uCalc API Version: 2.1.3-preview.2 Released: 6/17/2026

Warning

uCalc API Preview Release Notice:The documentation describes the intended behavior of the API. The current preview build contains incomplete features, unoptimized performance, and is subject to breaking changes.

Project: Creating a DSL for Board Game Rules

Product: 

Class: 

A step-by-step project to build a simple Domain-Specific Language (DSL) for board game rules using the uCalc Transformer.

Remarks

🎲 Project: Building a DSL for Board Game Rules

This project will guide you through building a simple but powerful Domain-Specific Language (DSL) for processing board game actions. It's a perfect real-world example of how the declarative power of the uCalc.Transformer can create a readable, maintainable, and easily modifiable rules engine.

The Goal: A Readable Game Script

Instead of hardcoding complex if/else or switch statements in your application, we want to process a simple, human-readable script that represents a player's turn:

PLAYER 1 MOVES 5 SPACESPLAYER 2 GAINS 10 GOLDPLAYER 1 DRAWS 2 CARDS

This script is far more intuitive and can be easily modified for different game variants or expansions without recompiling the main application.

The Strategy: Transforming DSL into uCalc Expressions

The key to building our DSL is to teach the uCalc engine to understand our new keywords (MOVE, GAIN, DRAW). We'll use the ExpressionTransformer to transform our DSL syntax into standard mathematical expressions before they are evaluated.

For example, the line PLAYER 1 MOVES 5 SPACES will be automatically rewritten to player1_position = player1_position + 5.


Step 1: Setting Up the Game State

First, we need variables within our uCalc instance to represent the game state. We'll create these using DefineVariable().

// Player 1's stateuc.DefineVariable("player1_position = 0");uc.DefineVariable("player1_gold = 20");uc.DefineVariable("player1_cards = 5");// Player 2's stateuc.DefineVariable("player2_position = 0");uc.DefineVariable("player2_gold = 20");uc.DefineVariable("player2_cards = 5");

Step 2: Defining the DSL Rules

Next, we'll get the ExpressionTransformer and add rules for each of our keywords using FromTo(). We use the {@Number} category matcher to capture the dynamic values for the player number and the action amount.

// Get the transformer that pre-processes expressions.var t = uc.ExpressionTransformer;// Rule for MOVEt.FromTo("PLAYER {@Number:p} MOVES {@Number:n} SPACES", "player{p}_position = player{p}_position + {n}");// Rule for GAINt.FromTo("PLAYER {@Number:p} GAINS {@Number:n} GOLD", "player{p}_gold = player{p}_gold + {n}");// Rule for DRAWt.FromTo("PLAYER {@Number:p} DRAWS {@Number:n} CARDS", "player{p}_cards = player{p}_cards + {n}");

Step 3: Processing a Game Turn

With our transformation rules in place, we can now pass an entire multi-line script to EvalStr(). Because newlines are treated as statement separators by default, uCalc will automatically pre-process and execute each line sequentially.

After the script runs, we can inspect the variables to see the final game state.

⚖️ Why uCalc? (Comparative Analysis)

  • vs. Hardcoding Logic: A hardcoded approach is rigid. If a rule changes (e.g., a special space doubles the gold gained), you must recompile the application. A DSL allows rules to be loaded from a text file, making the game easily modifiable for house rules or expansions.
  • vs. Full Scripting Engine (Lua/Python): Embedding a full scripting engine is heavyweight and complex. uCalc provides a lightweight, sandboxed environment perfect for this kind of task-specific language. The ExpressionTransformer is a simpler tool for this job than writing a full parser for the DSL from scratch.

Examples

A minimal example of a DSL command to move a player piece.
				
					using uCalcSoftware;

var uc = new uCalc();
uc.DefineVariable("player1_position = 0");

// Define a rule to translate the MOVE command
var t = uc.ExpressionTransformer;
t.FromTo("PLAYER {@Number:p} MOVES {@Number:n} SPACES", "player{p}_position = player{p}_position + {n}");

// Execute a single command
uc.EvalStr("PLAYER 1 MOVES 5 SPACES");

Console.WriteLine($"Player 1 is now at position: {uc.EvalStr("player1_position")}");
				
			
Player 1 is now at position: 5
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

int main() {
   uCalc uc;
   uc.DefineVariable("player1_position = 0");

   // Define a rule to translate the MOVE command
   auto t = uc.ExpressionTransformer();
   t.FromTo("PLAYER {@Number:p} MOVES {@Number:n} SPACES", "player{p}_position = player{p}_position + {n}");

   // Execute a single command
   uc.EvalStr("PLAYER 1 MOVES 5 SPACES");

   cout << "Player 1 is now at position: " << uc.EvalStr("player1_position") << endl;
}
				
			
Player 1 is now at position: 5
				
					Imports System
Imports uCalcSoftware
Public Module Program
   Public Sub Main()
      Dim uc As New uCalc()
      uc.DefineVariable("player1_position = 0")
      
      '// Define a rule to translate the MOVE command
      Dim t = uc.ExpressionTransformer
      t.FromTo("PLAYER {@Number:p} MOVES {@Number:n} SPACES", "player{p}_position = player{p}_position + {n}")
      
      '// Execute a single command
      uc.EvalStr("PLAYER 1 MOVES 5 SPACES")
      
      Console.WriteLine($"Player 1 is now at position: {uc.EvalStr("player1_position")}")
   End Sub
End Module
				
			
Player 1 is now at position: 5
A complete game turn script demonstrating multiple DSL commands for moving, gaining resources, and drawing cards.
				
					using uCalcSoftware;

var uc = new uCalc();
// 1. Define the initial game state
uc.DefineVariable("p1_pos = 0");
uc.DefineVariable("p1_gold = 10");
uc.DefineVariable("p2_pos = 0");
uc.DefineVariable("p2_gold = 10");

// 2. Define the DSL rules
var t = uc.ExpressionTransformer;
t.FromTo("PLAYER {@Number:p} MOVES {@Number:n}", "p{p}_pos = p{p}_pos + {n}");
t.FromTo("PLAYER {@Number:p} GAINS {@Number:n} GOLD", "p{p}_gold = p{p}_gold + {n}");

// 3. Define the script for the turn
var game_turn_script = """

PLAYER 1 MOVES 4
PLAYER 2 MOVES 2
PLAYER 1 GAINS 5 GOLD
PLAYER 2 MOVES 3

""";

// 4. Execute the script
uc.EvalStr(game_turn_script);

// 5. Display the final state
Console.WriteLine("--- End of Turn State ---");
Console.WriteLine($"Player 1 Position: {uc.EvalStr("p1_pos")}");
Console.WriteLine($"Player 1 Gold: {uc.EvalStr("p1_gold")}");
Console.WriteLine($"Player 2 Position: {uc.EvalStr("p2_pos")}");
Console.WriteLine($"Player 2 Gold: {uc.EvalStr("p2_gold")}");
				
			
--- End of Turn State ---
Player 1 Position: 4
Player 1 Gold: 15
Player 2 Position: 5
Player 2 Gold: 10
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

int main() {
   uCalc uc;
   // 1. Define the initial game state
   uc.DefineVariable("p1_pos = 0");
   uc.DefineVariable("p1_gold = 10");
   uc.DefineVariable("p2_pos = 0");
   uc.DefineVariable("p2_gold = 10");

   // 2. Define the DSL rules
   auto t = uc.ExpressionTransformer();
   t.FromTo("PLAYER {@Number:p} MOVES {@Number:n}", "p{p}_pos = p{p}_pos + {n}");
   t.FromTo("PLAYER {@Number:p} GAINS {@Number:n} GOLD", "p{p}_gold = p{p}_gold + {n}");

   // 3. Define the script for the turn
   auto game_turn_script = R"(
PLAYER 1 MOVES 4
PLAYER 2 MOVES 2
PLAYER 1 GAINS 5 GOLD
PLAYER 2 MOVES 3
)";

   // 4. Execute the script
   uc.EvalStr(game_turn_script);

   // 5. Display the final state
   cout << "--- End of Turn State ---" << endl;
   cout << "Player 1 Position: " << uc.EvalStr("p1_pos") << endl;
   cout << "Player 1 Gold: " << uc.EvalStr("p1_gold") << endl;
   cout << "Player 2 Position: " << uc.EvalStr("p2_pos") << endl;
   cout << "Player 2 Gold: " << uc.EvalStr("p2_gold") << endl;
}
				
			
--- End of Turn State ---
Player 1 Position: 4
Player 1 Gold: 15
Player 2 Position: 5
Player 2 Gold: 10
				
					Imports System
Imports uCalcSoftware
Public Module Program
   Public Sub Main()
      Dim uc As New uCalc()
      '// 1. Define the initial game state
      uc.DefineVariable("p1_pos = 0")
      uc.DefineVariable("p1_gold = 10")
      uc.DefineVariable("p2_pos = 0")
      uc.DefineVariable("p2_gold = 10")
      
      '// 2. Define the DSL rules
      Dim t = uc.ExpressionTransformer
      t.FromTo("PLAYER {@Number:p} MOVES {@Number:n}", "p{p}_pos = p{p}_pos + {n}")
      t.FromTo("PLAYER {@Number:p} GAINS {@Number:n} GOLD", "p{p}_gold = p{p}_gold + {n}")
      
      '// 3. Define the script for the turn
      Dim game_turn_script = "
PLAYER 1 MOVES 4
PLAYER 2 MOVES 2
PLAYER 1 GAINS 5 GOLD
PLAYER 2 MOVES 3
"
      
      '// 4. Execute the script
      uc.EvalStr(game_turn_script)
      
      '// 5. Display the final state
      Console.WriteLine("--- End of Turn State ---")
      Console.WriteLine($"Player 1 Position: {uc.EvalStr("p1_pos")}")
      Console.WriteLine($"Player 1 Gold: {uc.EvalStr("p1_gold")}")
      Console.WriteLine($"Player 2 Position: {uc.EvalStr("p2_pos")}")
      Console.WriteLine($"Player 2 Gold: {uc.EvalStr("p2_gold")}")
   End Sub
End Module
				
			
--- End of Turn State ---
Player 1 Position: 4
Player 1 Gold: 15
Player 2 Position: 5
Player 2 Gold: 10