Merge branch 'phase/46-speech-home'

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	docs/phases/09-social/README.md
#	src/HSchool.Server/Game/SchoolWorker.cs
#	tests/HSchool.Ai.Tests/TalkCirclesTests.cs
#	tests/HSchool.Simulation.Tests/MorningOpinionsTests.cs
#	tests/HSchool.Simulation.Tests/TalkCircleTests.cs
This commit is contained in:
Leonid Pershin
2026-08-20 14:06:29 +03:00
35 changed files with 910 additions and 36 deletions
@@ -219,6 +219,53 @@ public class TalkCirclesTests
Assert.Equal(50f + rules.TalkSkillPerHour, afterTalkHour);
}
[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()
{