Persist talk opinions and pick topics by weight.
A finished circle never set Tick's peopleChanged bit, so people.json kept pre-talk numbers until morning drift. PickTopic compared a 0..total roll to a 0..1 cursor, so gossip almost never changed the subject. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -113,6 +113,112 @@ public class TalkCirclesTests
|
||||
Assert.True(high > low);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NodeFriendScore_PullsTowardFriends_NotEnemies()
|
||||
{
|
||||
var (catalog, _, _) = World();
|
||||
var opinions = new Dictionary<string, int>(StringComparer.Ordinal)
|
||||
{
|
||||
["friend"] = 50,
|
||||
["enemy"] = -50,
|
||||
};
|
||||
var nodes = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["friend"] = "corridor-a",
|
||||
["enemy"] = "corridor-b",
|
||||
};
|
||||
var context = new TalkPlannerContext("self", opinions, nodes, null, false, true, catalog.BehaviorRules);
|
||||
Assert.True(TalkCircles.NodeFriendScore("corridor-a", "self", context) > 0);
|
||||
Assert.True(TalkCircles.NodeFriendScore("corridor-b", "self", context) < 0);
|
||||
Assert.Equal(0f, TalkCircles.NodeFriendScore("corridor-a", "self", context with { FriendPullActive = false }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LunchNodeBlockedByEnemy_OnlyWhenAnotherSeatExists()
|
||||
{
|
||||
var (catalog, _, _) = World();
|
||||
var opinions = new Dictionary<string, int>(StringComparer.Ordinal) { ["enemy"] = -50 };
|
||||
var nodes = new Dictionary<string, string>(StringComparer.Ordinal) { ["enemy"] = "canteen-1" };
|
||||
var context = new TalkPlannerContext("self", opinions, nodes, null, false, true, catalog.BehaviorRules);
|
||||
Assert.True(TalkCircles.LunchNodeBlockedByEnemy("canteen-1", "self", context, hasAlternative: true));
|
||||
Assert.False(TalkCircles.LunchNodeBlockedByEnemy("canteen-1", "self", context, hasAlternative: false));
|
||||
Assert.False(TalkCircles.LunchNodeBlockedByEnemy("canteen-2", "self", context, hasAlternative: true));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GossipAndOutgoing_UseTraitFields_NotHardcodedIdLists()
|
||||
{
|
||||
var (catalog, _, _) = World();
|
||||
Assert.True(catalog.Traits["Gossip"].TalkOpinionMultiplier > 1f);
|
||||
Assert.Contains(catalog.Traits["Gossip"].TalkTagWeights, bias => bias.Tag.Equals("gossip", StringComparison.Ordinal));
|
||||
Assert.True(catalog.Traits["Outgoing"].TalkInitiative > 1f);
|
||||
Assert.Equal(1, catalog.Traits["Outgoing"].TalkCircleBonus);
|
||||
Assert.True(TalkCircles.OpinionMultiplier(["Gossip"], catalog) > TalkCircles.OpinionMultiplier([], catalog));
|
||||
Assert.True(TalkCircles.Initiative(["Outgoing"], catalog) > TalkCircles.Initiative([], catalog));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GossipTrait_BiasesPickTopicTowardGossipTag()
|
||||
{
|
||||
var (catalog, _, _) = World();
|
||||
var gossip = Person("g", ["RussianLanguage"]) with { Traits = ["Gossip"] };
|
||||
var plain = Person("p", ["RussianLanguage"]);
|
||||
var gossipHits = 0;
|
||||
var plainHits = 0;
|
||||
for (var i = 0; i < 200; i++)
|
||||
{
|
||||
var seed = Seed.Mix(i, 17, Seed.ApparelSalt);
|
||||
var gossipId = TalkCircles.PickTopic(catalog, gossip, age: 14, seed);
|
||||
var plainId = TalkCircles.PickTopic(catalog, plain, age: 14, seed);
|
||||
if (gossipId is not null && catalog.Topics[gossipId].Tags.Contains("gossip", StringComparer.Ordinal))
|
||||
{
|
||||
gossipHits++;
|
||||
}
|
||||
|
||||
if (plainId is not null && catalog.Topics[plainId].Tags.Contains("gossip", StringComparer.Ordinal))
|
||||
{
|
||||
plainHits++;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.True(gossipHits > plainHits);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickTopic_IsNotAlwaysTheLastNameAlphabetically()
|
||||
{
|
||||
var (catalog, _, _) = World();
|
||||
var picker = Person("a", ["RussianLanguage"]);
|
||||
var picked = new HashSet<string>(StringComparer.Ordinal);
|
||||
for (var i = 0; i < 80; i++)
|
||||
{
|
||||
var id = TalkCircles.PickTopic(catalog, picker, age: 14, Seed.Mix(i, 3, Seed.ApparelSalt));
|
||||
Assert.NotNull(id);
|
||||
picked.Add(id!);
|
||||
}
|
||||
|
||||
Assert.True(picked.Count >= 3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppearanceTopic_UsesDressAppropriateness()
|
||||
{
|
||||
Assert.Equal(2, TalkCircles.AppearanceModifier(ApparelIssue.None));
|
||||
Assert.Equal(-2, TalkCircles.AppearanceModifier(ApparelIssue.Formality));
|
||||
Assert.Equal(0, TalkCircles.AppearanceModifier(ApparelIssue.Color));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CommunicationGain_IsLessThanLessonSkillPerHour()
|
||||
{
|
||||
var (catalog, _, _) = World();
|
||||
var rules = catalog.BehaviorRules!;
|
||||
Assert.True(rules.TalkSkillPerHour < rules.LessonSkillPerHour);
|
||||
var skill = catalog.Skills["Communication"];
|
||||
var afterTalkHour = TalkCircles.CommunicationGain(50f, skill, 1f, rules);
|
||||
Assert.Equal(50f + rules.TalkSkillPerHour, afterTalkHour);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enemies_AreNotRankedAsInvitees()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user