Rules and Processes¶
Tutorial Duration: 20-25 minutes
This tutorial covers entity behavior implementation through rules and processes. You'll learn how entities interact, communicate, and evolve over time using the Brewery model as a practical example.
Learning Objectives¶
By the end of this tutorial, you will understand:
- The difference between rules and processes
- How to implement entity behavior in C++/Python
- Communication patterns between entities
- The Perceive/React design pattern
- Meso-functions for compound entity computations
- Scheduling and task execution
Rules vs Processes¶
In CoSMo, entity behavior is implemented through two main mechanisms:
Type | Purpose | Environment Required | Example Use |
---|---|---|---|
Rules | Environment-dependent behavior | Yes | Social interactions, spatial movement |
Processes | Internal entity updates | No | State updates, internal calculations |
Basic Processes¶
Customer Update Process¶
Let's start with a simple process that updates customer thirst:
<csm:Process name="Update">
<csm:Implementation language="cpp">
<csm:Code><![CDATA[
int test = rand()%10 + GetSatisfaction();
if (!GetThirsty() && test > 5)
{
SetThirsty(true);
}
]]></csm:Code>
</csm:Implementation>
</csm:Process>
Process Breakdown¶
- No Environment: Processes don't require environment context
- State Access: Use
Get[AttributeName]()
andSet[AttributeName](value)
- Logic: Customers become thirsty based on satisfaction and randomness
- Probability: Higher satisfaction = higher chance of becoming thirsty
State Access Methods
CoSMo automatically generates getter/setter methods for all entity attributes:
- GetSatisfaction()
→ reads the Satisfaction attribute
- SetThirsty(true)
→ sets the Thirsty attribute to true
The Perceive/React Pattern¶
For environment-dependent behavior, CoSMo uses the Perceive/React pattern:
- Perceive: Gather information from neighbors
- React: Update state based on perceived information
Customer Perceive Rule¶
<csm:Rule name="Perceive">
<csm:Implementation language="cpp">
<csm:Code><![CDATA[
int satisfaction = 0;
int count = 0;
// Iterate through all Customer neighbors in the graph
csmForEachTypedNeighbor(BE_Customer, neighbor)
{
// Get satisfaction from each neighbor via communicator
satisfaction += neighbor->GetCustomerToCustomer(this);
count++;
}
// Calculate average satisfaction of neighbors
int avrg = int(float(satisfaction) / float(csm::Math::Max(count, 1)));
SetSurroundingSatisfaction(avrg);
]]></csm:Code>
</csm:Implementation>
<csm:EnvironmentReference>CustomerGraph</csm:EnvironmentReference>
</csm:Rule>
Customer React Rule¶
<csm:Rule name="React">
<csm:Implementation language="cpp">
<csm:Code><![CDATA[
int satisfaction = GetSatisfaction();
// Adjust satisfaction based on neighbors
if (satisfaction > GetSurroundingSatisfaction())
{
SetSatisfaction(satisfaction - 1); // Decrease if above average
}
else if (satisfaction < GetSurroundingSatisfaction())
{
SetSatisfaction(satisfaction + 1); // Increase if below average
}
]]></csm:Code>
</csm:Implementation>
<csm:EnvironmentReference>CustomerGraph</csm:EnvironmentReference>
</csm:Rule>
Key Perceive/React Concepts¶
- Environment Reference: Both rules specify which environment they operate in
- Neighbor Iteration:
csmForEachTypedNeighbor
loops through connected entities - Communication: Entities request data from neighbors via communicators
- Separation: Perceive gathers data, React makes decisions
Why Perceive/React?
This pattern ensures simultaneous updates. All entities perceive the current state, then all react based on that snapshot, preventing order-dependent results.
Communicators¶
Communicators define how entities share information:
<csm:Communicator name="CustomerToCustomer">
<csm:Implementation language="cpp">
<csm:Code><![CDATA[
return GetSatisfaction();
]]></csm:Code>
</csm:Implementation>
<csm:CommunicatedReference>Integer</csm:CommunicatedReference>
<csm:Destination>Customer</csm:Destination>
</csm:Communicator>
Communicator Components¶
- Implementation: Code that computes the shared value
- CommunicatedReference: Data type being shared
- Destination: Which entity types can receive this data
Using Communicators¶
In the Perceive rule, neighbors are accessed via the communicator:
neighbor->GetCustomerToCustomer(this)
This calls the CustomerToCustomer
communicator on the neighbor, passing this
customer as the destination.
Compound Entity Processes¶
Compound entities can have processes that operate on their sub-entities:
Bar Serve Process¶
<csm:Process name="Serve">
<csm:Implementation language="cpp">
<csm:Code><![CDATA[
// Calculate service capacity based on waiters and demand
int thirsty = GetNbThirstyCustomers();
int waiters = GetNbWaiters();
int threshold = int(10.f * float(waiters) / float(csm::Math::Max(thirsty, 1)));
// Iterate through all customer sub-entities
csmForEachTypedSubEntity(BE_Customer, customer)
{
if (customer->GetThirsty())
{
// Probability of service based on waiter availability
int test = rand()%10 + 1;
if (test <= threshold && GetStock() > 0)
{
// Successful service
SetStock(GetStock() - 1);
customer->SetThirsty(false);
customer->SetSatisfaction(customer->GetSatisfaction() + 1);
}
else if (customer->GetSatisfaction() > 0)
{
// Poor service reduces satisfaction
customer->SetSatisfaction(customer->GetSatisfaction() - 1);
}
}
}
]]></csm:Code>
</csm:Implementation>
</csm:Process>
Bar Restock Process¶
<csm:Process name="Restock">
<csm:Implementation language="cpp">
<csm:Code><![CDATA[
SetStock(GetStock() + GetRestockQty());
]]></csm:Code>
</csm:Implementation>
</csm:Process>
Compound Process Features¶
- Sub-entity Access:
csmForEachTypedSubEntity
iterates through contained entities - Direct Manipulation: Compound entities can modify sub-entity state
- Business Logic: Complex algorithms involving multiple entities
- Resource Management: Stock tracking and consumption
Meso-Functions¶
Meso-functions compute aggregate information about sub-entities:
Automatic Meso-Functions¶
<csm:MesoFunction name="UpdateAverageSatisfaction">
<csm:Implementation language="cpp">
<csm:Code><![CDATA[]]></csm:Code>
</csm:Implementation>
<csm:Operations>
<csm:Operation xsi:type="csm:MesoAverage"
target="AverageSatisfaction"
subEntityAttribute="Satisfaction"/>
</csm:Operations>
</csm:MesoFunction>
<csm:MesoFunction name="UpdateNbThirstyCustomers">
<csm:Implementation language="cpp">
<csm:Code><![CDATA[]]></csm:Code>
</csm:Implementation>
<csm:Operations>
<csm:Operation xsi:type="csm:MesoValueCount"
target="NbThirstyCustomers"
subEntityAttribute="Thirsty"
attributeValue="true"/>
</csm:Operations>
</csm:MesoFunction>
Built-in Meso-Operations¶
Operation | Purpose | Example |
---|---|---|
MesoAverage |
Calculate mean value | Average customer satisfaction |
MesoValueCount |
Count specific values | Number of thirsty customers |
MesoSum |
Sum all values | Total satisfaction points |
MesoMin/MesoMax |
Find extremes | Most/least satisfied customer |
Custom Meso-Functions¶
You can also implement custom logic:
<csm:MesoFunction name="CalculateServiceQuality">
<csm:Implementation language="cpp">
<csm:Code><![CDATA[
float totalSatisfaction = 0;
int customerCount = 0;
csmForEachTypedSubEntity(BE_Customer, customer)
{
totalSatisfaction += customer->GetSatisfaction();
customerCount++;
}
float avgSatisfaction = totalSatisfaction / csm::Math::Max(customerCount, 1);
float serviceQuality = avgSatisfaction * GetStock() / 10.0f;
SetServiceQuality(serviceQuality);
]]></csm:Code>
</csm:Implementation>
</csm:MesoFunction>
Advanced Behavior Patterns¶
Conditional Logic¶
// State-dependent behavior
if (GetStock() < 5)
{
// Emergency restocking
SetRestockQty(20);
}
else if (GetStock() > 50)
{
// Reduce waste
SetRestockQty(5);
}
Probabilistic Behavior¶
// Random events with weighted probability
int satisfaction = GetSatisfaction();
int randomValue = rand() % 100;
int threshold = 20 + satisfaction * 5; // Higher satisfaction = higher chance
if (randomValue < threshold)
{
// Positive event occurs
PerformAction();
}
Neighbor-Dependent Actions¶
// Actions based on neighbor count and state
int happyNeighbors = 0;
int totalNeighbors = 0;
csmForEachTypedNeighbor(BE_Customer, neighbor)
{
totalNeighbors++;
if (neighbor->GetSatisfaction() > 7)
{
happyNeighbors++;
}
}
float happyRatio = float(happyNeighbors) / float(csm::Math::Max(totalNeighbors, 1));
if (happyRatio > 0.7f)
{
// Surrounded by happy customers
SetSatisfaction(GetSatisfaction() + 2);
}
Code Organization Best Practices¶
1. Clear Variable Names¶
// Good
int currentSatisfaction = GetSatisfaction();
int neighborSatisfactionSum = 0;
int neighborCount = 0;
// Avoid
int s = GetSatisfaction();
int sum = 0;
int c = 0;
2. Separate Concerns¶
// Calculate neighbor influence
int neighborInfluence = CalculateNeighborInfluence();
// Apply influence to self
ApplyInfluence(neighborInfluence);
// Update internal state
UpdateInternalState();
3. Use Helper Functions¶
// In a meso-function or complex process
bool IsBarBusy()
{
return GetNbThirstyCustomers() > GetNbWaiters() * 3;
}
float CalculateServiceEfficiency()
{
return float(GetNbWaiters()) / float(csm::Math::Max(GetNbThirstyCustomers(), 1));
}
Common Implementation Patterns¶
1. Threshold-Based Behavior¶
const int SATISFACTION_THRESHOLD = 5;
const int STOCK_LOW_THRESHOLD = 3;
if (GetSatisfaction() < SATISFACTION_THRESHOLD)
{
// Low satisfaction behavior
}
if (GetStock() < STOCK_LOW_THRESHOLD)
{
// Emergency restocking
}
2. State Machine Pattern¶
enum CustomerState { CONTENT, THIRSTY, DRINKING, LEAVING };
switch (GetCurrentState())
{
case CONTENT:
if (ShouldBecomeThirsty()) TransitionTo(THIRSTY);
break;
case THIRSTY:
if (IsServed()) TransitionTo(DRINKING);
break;
// ... other states
}
3. Weighted Decision Making¶
struct Decision {
std::string action;
float weight;
};
std::vector<Decision> decisions = {
{"stay", GetSatisfaction() * 0.1f},
{"leave", (10 - GetSatisfaction()) * 0.2f},
{"order", GetThirsty() ? 0.8f : 0.1f}
};
// Select action based on weights
std::string chosenAction = SelectWeightedRandom(decisions);
Debugging and Testing¶
Adding Debug Output¶
// Use CoSMo logging
csm::Log().Info("Customer {} satisfaction: {}", GetId(), GetSatisfaction());
csm::Log().Debug("Neighbor count: {}", neighborCount);
Validation Checks¶
// Ensure valid ranges
if (GetSatisfaction() < 0) SetSatisfaction(0);
if (GetSatisfaction() > 10) SetSatisfaction(10);
// Check preconditions
assert(GetStock() >= 0);
assert(GetNbWaiters() > 0);
Exercise: Implementing New Behaviors¶
Try implementing these additional behaviors:
1. Customer Loyalty System¶
Add a Loyalty
attribute and implement:
- Loyalty increases with good service
- Loyal customers are more forgiving of poor service
- Loyalty affects tip amounts
2. Peak Hours Simulation¶
Implement time-based behavior:
- More customers become thirsty during peak hours
- Bar adjusts waiter count based on time
- Price changes based on demand
3. Social Influence Propagation¶
Enhance the social network:
- Extremely satisfied customers create "viral" satisfaction
- Dissatisfied customers have stronger negative influence
- Implement influence decay over network distance
Next Steps¶
In the next tutorial, you'll learn about:
- Model Instantiation: Creating entity instances
- Environment Setup: Defining spatial relationships
- Scheduler Configuration: Controlling when behaviors execute
- Initialization: Setting initial conditions
The rules and processes you've learned here define how entities behave - the next tutorial covers when and where they behave!