HarvestingCore is a deterministic, tick-driven multi-agent simulation core for a crop-harvesting scenario: Harvester agents work assigned areas of a grid, Tractor agents ferry crop to dump sites and refuel harvesters, and an area distributor keeps work assignments balanced as the field is cleared.
It ships as a plain netstandard2.1 class library with zero external dependencies (no NuGet packages, no UnityEngine reference), which makes the compiled DLL safe to drop directly into a Unity project.
HarvestingCore.sln
├── src/HarvestingCore/ netstandard2.1 class library (no dependencies)
├── src/HarvestingCore.Transport/ net8.0 WebSocket server + message protocol
└── src/HarvestingCore.Host/ net8.0 console entry point
Six layers inside the core assembly, dependencies pointing strictly downward:
HarvestingCore- theSimulationWorldfaçade. The only entry point a host (e.g. a UnityMonoBehaviour) needs: register agents, callTick(), read state back out.HarvestingCore.Coordination-AgentManager,AreaDistributor,PendingMutations.HarvestingCore.Agents/HarvestingCore.Agents.States-Harvester,Tractor, and their finite-state machines.HarvestingCore.Pathfinding-PathFinder(Dijkstra / A*),DeterministicMinHeap,Heuristics.HarvestingCore.World-WorldModel,Cell,GridPosition.HarvestingCore.Configuration-SimulationConfig,DeterministicRandom.
| Harvester | Tractor |
|---|---|
![]() |
![]() |
The core pathfinding and area-assignment logic is translated from the reference C++ implementations in reference/algorithms:
| Reference | C# component | Algorithm |
|---|---|---|
area_distribution.cpp |
AreaDistributor |
Multi-source BFS, stamps owner ids |
path_to_best.cpp |
PathFinder.PathToBestCell |
Dijkstra to the nearest cell matching a target state |
path_to_cell.cpp |
PathFinder.PathToCell |
A* to a specific cell, pluggable heuristic |
dotnet build HarvestingCore.slndotnet run --project src/HarvestingCore.HostThe server starts on ws://localhost:8765/ by default.
dotnet run --project src/HarvestingCore.Host -- [port] [seed]
| Argument | Default | Description |
|---|---|---|
port |
8765 |
TCP port the WebSocket server listens on |
seed |
20240101 |
Deterministic RNG seed for grid generation |
Example — port 9000, seed 42:
dotnet run --project src/HarvestingCore.Host -- 8765 42Press Ctrl+C to shut down gracefully.
Connect to ws://localhost:<port>/ with any WebSocket client.
Client → Server
Request ticks:
{ "type": "tick_request", "count": 1 }Request current state without ticking:
{ "type": "state_request" }Server → Client
After each tick:
{ "type": "tick_response", "tick": 3, "snapshot": { ... } }Reply to state_request:
{ "type": "state_response", "tick": 3, "snapshot": { ... } }Error:
{ "type": "error_response", "code": "...", "message": "..." }Snapshot shape
{
"tick": 3,
"isHalted": false,
"dischargedTotal": 12,
"agents": [
{ "id": "H1", "role": "Harvester", "state": "Harvest", "x": 2, "y": 3, "fuel": 980, "load": 5 }
],
"cells": [
{ "x": 0, "y": 0, "state": "Empty", "ownerId": "H1" }
]
}HarvestingCore targets netstandard2.1, the highest .NET Standard version Unity's scripting runtime consumes natively (Unity 2021.2+ with the .NET Standard 2.1 API compatibility level, which is the default for modern Unity versions). No Unity APIs are referenced anywhere in the library, so it can be imported in either of the two ways below.
- Build the library in Release mode:
dotnet build src/HarvestingCore/HarvestingCore.csproj -c ReleaseThis produces src/HarvestingCore/bin/Release/netstandard2.1/HarvestingCore.dll.
- In your Unity project, create a folder for third-party plugins, e.g.
Assets/Plugins/HarvestingCore/. - Copy
HarvestingCore.dll(andHarvestingCore.pdbif you want debug symbols) into that folder. - Switch back to Unity and let it re-import. The classes under the
HarvestingCore,HarvestingCore.Agents,HarvestingCore.Coordination,HarvestingCore.Pathfinding,HarvestingCore.World, andHarvestingCore.Configurationnamespaces are now available in any script.
- Copy the contents of
src/HarvestingCore/(everything exceptbin/andobj/) into a folder inside your Unity project'sAssets/, e.g.Assets/HarvestingCore/. - Delete or ignore
HarvestingCore.csproj— Unity compiles the.csfiles directly via its own generated project files, it doesn't need the standalone csproj. - Unity's compiler already targets .NET Standard 2.1 by default, so no project settings need to change.
Once imported (either option), a minimal Unity script that drives the simulation looks like this:
using UnityEngine;
using HarvestingCore;
using HarvestingCore.Agents;
using HarvestingCore.Configuration;
using HarvestingCore.World;
public class HarvestingSimulationDriver : MonoBehaviour
{
private SimulationWorld _world;
void Start()
{
var config = SimulationConfig.Default;
var random = new DeterministicRandom(config.Seed);
var model = new WorldModel(
width: 32, height: 32,
refuelStations: new[] { new GridPosition(0, 0) },
dumpSites: new[] { new GridPosition(31, 31) });
_world = new SimulationWorld(model, config, random);
_world.GenerateGrid();
_world.Register(new Harvester("H1", new GridPosition(1, 1), model, config));
_world.Register(new Tractor ("T1", new GridPosition(2, 1), model, config));
_world.RedistributeAreas();
}
void FixedUpdate()
{
if (!_world.IsHalted)
_world.Tick();
}
}Attach the script to any GameObject and enter play mode. If it compiles and ticks without errors, the import worked. From there, read _world.Cells and _world.Agents each frame to drive your own rendering/view layer — the library itself never touches UnityEngine.
dotnet build HarvestingCore.sln
dotnet test HarvestingCore.sln

