Merge branch 'phase/42-talk-circles'
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # docs/phases/README.md # src/HSchool.Simulation/PersonLog.cs
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
using HSchool.Ai;
|
||||
using HSchool.Ai.Tests;
|
||||
using HSchool.Content;
|
||||
using HSchool.People;
|
||||
|
||||
namespace HSchool.Ai.Tests;
|
||||
|
||||
public class TalkCirclesTests
|
||||
{
|
||||
[Fact]
|
||||
public void BoundToLesson_BlocksChatLeisure()
|
||||
{
|
||||
var (catalog, map, walks) = World();
|
||||
var corridor = map.Rooms.First(room => room.Def == "Corridor").Id;
|
||||
var decision = DecisionPlanner.Decide(
|
||||
catalog,
|
||||
map,
|
||||
walks,
|
||||
new ActorState(
|
||||
corridor,
|
||||
corridor,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
corridor,
|
||||
Needs(),
|
||||
Intent.None,
|
||||
Talk: new TalkPlannerContext("p1", new Dictionary<string, int>(), new Dictionary<string, string>(), null, false, true, catalog.BehaviorRules)),
|
||||
(_, _) => 0);
|
||||
|
||||
Assert.NotEqual(TalkActions.Chat, decision.StartAction);
|
||||
Assert.NotEqual(TalkActions.StaffChat, decision.StartAction);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Student_DoesNotPickStaffChat()
|
||||
{
|
||||
var (catalog, map, walks) = World();
|
||||
var teachersRoom = map.Rooms.First(room => room.Def == "TeachersRoom").Id;
|
||||
var decision = DecisionPlanner.Decide(
|
||||
catalog,
|
||||
map,
|
||||
walks,
|
||||
new ActorState(
|
||||
teachersRoom,
|
||||
teachersRoom,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
teachersRoom,
|
||||
Needs(social: 0.2f),
|
||||
Intent.None,
|
||||
Talk: new TalkPlannerContext("p1", new Dictionary<string, int>(), new Dictionary<string, string>(), null, false, true, catalog.BehaviorRules)),
|
||||
(_, _) => 0);
|
||||
|
||||
Assert.NotEqual(TalkActions.StaffChat, decision.StartAction);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PhoneChat_NotOfferedWithoutPhone()
|
||||
{
|
||||
var (catalog, map, walks) = World();
|
||||
var corridor = map.Rooms.First(room => room.Def == "Corridor").Id;
|
||||
var decision = DecisionPlanner.Decide(
|
||||
catalog,
|
||||
map,
|
||||
walks,
|
||||
Actor(corridor, hasPhone: false),
|
||||
(_, _) => 0);
|
||||
|
||||
Assert.NotEqual(TalkActions.PhoneChat, decision.StartAction);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasPhone_DetectsBagPhone()
|
||||
{
|
||||
Assert.False(TalkCircles.HasPhone([]));
|
||||
Assert.True(TalkCircles.HasPhone([new InventoryItem("Phone", "Black", 1f, ItemLocations.Bag)]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoSharedLanguage_WeakensOpinionShift()
|
||||
{
|
||||
var (catalog, _, _) = World();
|
||||
var rules = catalog.BehaviorRules!;
|
||||
var topic = catalog.Topics["TopicStudy"];
|
||||
var from = Person("a", ["RussianLanguage"]);
|
||||
var to = Person("b", ["English"]);
|
||||
var withLanguage = TalkCircles.OpinionDelta(from, to, topic, 50f, sharedLanguage: true, TalkActions.Chat, ApparelIssue.None, rules, catalog);
|
||||
var without = TalkCircles.OpinionDelta(from, to, topic, 50f, sharedLanguage: false, TalkActions.Chat, ApparelIssue.None, rules, catalog);
|
||||
|
||||
Assert.True(withLanguage > without);
|
||||
Assert.True(without <= 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HighCommunication_MovesOpinionMoreThanLow_OnSameTopic()
|
||||
{
|
||||
var (catalog, _, _) = World();
|
||||
var rules = catalog.BehaviorRules!;
|
||||
var topic = catalog.Topics["TopicSport"];
|
||||
var from = Person("a", ["RussianLanguage"]);
|
||||
var to = Person("b", ["RussianLanguage"]);
|
||||
var high = TalkCircles.OpinionDelta(from, to, topic, 80f, true, TalkActions.Chat, ApparelIssue.None, rules, catalog);
|
||||
var low = TalkCircles.OpinionDelta(from, to, topic, 20f, true, TalkActions.Chat, ApparelIssue.None, rules, catalog);
|
||||
|
||||
Assert.True(high > low);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enemies_AreNotRankedAsInvitees()
|
||||
{
|
||||
var (catalog, _, _) = World();
|
||||
var opinions = new Dictionary<string, int>(StringComparer.Ordinal)
|
||||
{
|
||||
["b"] = 50,
|
||||
["c"] = -50,
|
||||
};
|
||||
var initiator = Person("a", []) with { Opinions = opinions };
|
||||
var ranked = TalkCircles.RankInvitees(
|
||||
initiator,
|
||||
[
|
||||
new TalkCircles.Candidate("b", "c1", true, false, false),
|
||||
new TalkCircles.Candidate("c", "c1", true, false, false),
|
||||
],
|
||||
catalog.BehaviorRules);
|
||||
|
||||
Assert.Equal(["b"], ranked);
|
||||
}
|
||||
|
||||
private static ActorState Actor(string node, bool hasPhone)
|
||||
{
|
||||
var (catalog, _, _) = World();
|
||||
return new ActorState(
|
||||
node,
|
||||
node,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
node,
|
||||
Needs(social: 0.2f),
|
||||
Intent.None,
|
||||
Talk: new TalkPlannerContext("p1", new Dictionary<string, int>(), new Dictionary<string, string>(), null, hasPhone, true, catalog.BehaviorRules));
|
||||
}
|
||||
|
||||
private static Dictionary<string, float> Needs(float social = 1f) => new(StringComparer.Ordinal)
|
||||
{
|
||||
["Social"] = social,
|
||||
["Hunger"] = 1f,
|
||||
["Sleep"] = 1f,
|
||||
["Toilet"] = 1f,
|
||||
};
|
||||
|
||||
private static (DefCatalog Catalog, MapLayout Map, WalkGraph Walks) World()
|
||||
{
|
||||
var (catalog, map) = Fixtures.Vanilla();
|
||||
return (catalog, map, WalkGraph.Build(catalog, map));
|
||||
}
|
||||
|
||||
private static Person Person(string id, string[] languages)
|
||||
{
|
||||
var skills = languages.ToDictionary(skill => skill, _ => 60, StringComparer.Ordinal);
|
||||
skills["Communication"] = 50;
|
||||
var cases = new CaseTable
|
||||
{
|
||||
Nom = "A",
|
||||
Gen = "A",
|
||||
Dat = "A",
|
||||
Acc = "A",
|
||||
Ins = "A",
|
||||
Pre = "A",
|
||||
};
|
||||
return new Person
|
||||
{
|
||||
Id = id,
|
||||
FamilyId = "f1",
|
||||
Female = false,
|
||||
BirthDate = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc),
|
||||
Name = new PersonName("A", "B", "C", cases, cases, cases),
|
||||
IsStudent = true,
|
||||
IsStaff = false,
|
||||
IsParent = false,
|
||||
Numbers = new Dictionary<string, int>(StringComparer.Ordinal),
|
||||
Choices = new Dictionary<string, string>(StringComparer.Ordinal),
|
||||
Skills = skills,
|
||||
Traits = [],
|
||||
Needs = new Dictionary<string, float>(StringComparer.Ordinal),
|
||||
Opinions = new Dictionary<string, int>(StringComparer.Ordinal),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user