Files
h-school/tests/HSchool.Ai.Tests/TalkCirclesTests.cs
T

248 lines
8.4 KiB
C#

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 StudyOnlyPolicy_NeverPicksNonStudyTopic()
{
var (catalog, _, _) = World();
var picker = Person("a", ["RussianLanguage"]);
for (var i = 0; i < 80; i++)
{
var id = TalkCircles.PickTopic(catalog, picker, age: 14, seed: i * 137, speechPolicy: SpeechPolicies.StudyOnly);
Assert.NotNull(id);
Assert.Contains(TopicTags.Study, catalog.Topics[id].Tags, StringComparer.Ordinal);
}
}
[Fact]
public void NoRudePolicy_NeverPicksRudeTopic()
{
var (catalog, _, _) = World();
var picker = Person("a", ["RussianLanguage"]);
var rudeUnderFree = 0;
for (var i = 0; i < 11_000; i++)
{
var freeId = TalkCircles.PickTopic(catalog, picker, age: 14, seed: i, speechPolicy: SpeechPolicies.Free);
if (freeId is not null && catalog.Topics[freeId].Tags.Contains(TopicTags.Rude, StringComparer.Ordinal))
{
rudeUnderFree++;
}
var filtered = TalkCircles.PickTopic(catalog, picker, age: 14, seed: i, speechPolicy: SpeechPolicies.NoRude);
Assert.NotNull(filtered);
Assert.DoesNotContain(TopicTags.Rude, catalog.Topics[filtered].Tags, StringComparer.Ordinal);
}
Assert.True(rudeUnderFree > 0);
}
[Fact]
public void FamilyTaggedTopic_IsFromTopicDef()
{
var (catalog, _, _) = World();
var id = TalkCircles.PickTaggedTopic(catalog, TopicTags.Family, seed: 1);
Assert.NotNull(id);
Assert.Contains(TopicTags.Family, catalog.Topics[id].Tags, StringComparer.Ordinal);
var schoolShift = catalog.Topics[id].OpinionShift;
var home = (int)Math.Round(schoolShift * catalog.BehaviorRules!.HomeTalkOpinionScale, MidpointRounding.AwayFromZero);
Assert.True(Math.Abs(home) < Math.Abs(schoolShift) || schoolShift == 0);
}
[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),
};
}
}