Enhance protocol and simulation features with activity tracking and behavior definitions
- Updated protocol documentation to include new `activity` and `activityLabel` fields in the person card response, reflecting real-time activity status. - Introduced `BehaviorDef` to define behavior rules, including need thresholds and lesson skill gains, enhancing AI decision-making. - Revised the `DefCatalog` to incorporate behavior definitions and updated validation logic to ensure proper behavior handling. - Enhanced the simulation to manage presence and activity states, allowing for more dynamic interactions within the school environment. - Updated tests to validate the new activity tracking and behavior functionalities, ensuring robust performance and reliability. - Improved localization strings to support new activity and behavior features, enhancing user experience.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
using Arch.Core;
|
||||
using HSchool.Ai;
|
||||
using HSchool.Content;
|
||||
|
||||
namespace HSchool.Simulation.Tests;
|
||||
|
||||
public class NeedDecayTests
|
||||
{
|
||||
[Fact]
|
||||
public void Hunger_DropsByDecayPerHourOnCampus()
|
||||
{
|
||||
var catalog = VanillaCatalog();
|
||||
var hunger = catalog.Needs["Hunger"];
|
||||
var world = World.Create();
|
||||
try
|
||||
{
|
||||
world.Create(
|
||||
new PersonNeeds(new Dictionary<string, float>(StringComparer.Ordinal) { ["Hunger"] = hunger.Initial }),
|
||||
new Presence("cafeteria", 0f, "cafeteria", false, []));
|
||||
NeedDecay.Apply(world, catalog, gameMinutes: 60);
|
||||
|
||||
var query = new QueryDescription().WithAll<PersonNeeds>();
|
||||
world.Query(
|
||||
in query,
|
||||
(ref PersonNeeds needs) =>
|
||||
Assert.Equal(hunger.Initial - hunger.DecayPerHour, needs.Values["Hunger"], precision: 4));
|
||||
}
|
||||
finally
|
||||
{
|
||||
World.Destroy(world);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sleep_ReturnsToMaxOffCampus()
|
||||
{
|
||||
var catalog = VanillaCatalog();
|
||||
var world = World.Create();
|
||||
try
|
||||
{
|
||||
world.Create(
|
||||
new PersonNeeds(new Dictionary<string, float>(StringComparer.Ordinal)
|
||||
{
|
||||
["Sleep"] = 0.2f,
|
||||
["Hunger"] = 0.4f,
|
||||
}),
|
||||
Presence.OffCampus);
|
||||
NeedDecay.Apply(world, catalog, gameMinutes: 60);
|
||||
|
||||
var query = new QueryDescription().WithAll<PersonNeeds>();
|
||||
world.Query(in query, (ref PersonNeeds needs) =>
|
||||
{
|
||||
Assert.Equal(catalog.Needs["Sleep"].Max, needs.Values["Sleep"]);
|
||||
Assert.Equal(0.4f, needs.Values["Hunger"]);
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
World.Destroy(world);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SameInputs_LeaveTheSameNeedValues()
|
||||
{
|
||||
var catalog = VanillaCatalog();
|
||||
var first = HungerAfterHour(catalog);
|
||||
var second = HungerAfterHour(catalog);
|
||||
Assert.Equal(first, second);
|
||||
}
|
||||
|
||||
private static float HungerAfterHour(DefCatalog catalog)
|
||||
{
|
||||
var world = World.Create();
|
||||
try
|
||||
{
|
||||
world.Create(
|
||||
new PersonNeeds(new Dictionary<string, float>(StringComparer.Ordinal) { ["Hunger"] = 1f }),
|
||||
new Presence("cafeteria", 0f, "cafeteria", false, []));
|
||||
NeedDecay.Apply(world, catalog, gameMinutes: 60);
|
||||
var value = 0f;
|
||||
var query = new QueryDescription().WithAll<PersonNeeds>();
|
||||
world.Query(in query, (ref PersonNeeds needs) => value = needs.Values["Hunger"]);
|
||||
return value;
|
||||
}
|
||||
finally
|
||||
{
|
||||
World.Destroy(world);
|
||||
}
|
||||
}
|
||||
|
||||
private static DefCatalog VanillaCatalog()
|
||||
{
|
||||
var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
|
||||
var documents = new List<ContentDocument>();
|
||||
foreach (var path in Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (!path.EndsWith(".jsonc", StringComparison.OrdinalIgnoreCase)
|
||||
&& !path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var relative = Path.GetRelativePath(root, path).Replace('\\', '/');
|
||||
documents.Add(new ContentDocument(CatalogLoader.CorePackId, relative, File.ReadAllText(path)));
|
||||
}
|
||||
|
||||
return new CatalogLoader().Load([CatalogLoader.CorePackId], documents);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user