Files
Leonid PershinandCursor 9972b30257 Keep defender-victim opinions as allies after a quarrel.
The clash penalty applied to every pair except defender looking at the victim, so the person being helped disliked the defender like a second enemy.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-20 18:15:55 +03:00

238 lines
9.2 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace HSchool.People.Tests;
public class AffinityTests
{
private static readonly DateTime AsOf = new(2012, 3, 31, 6, 0, 0, DateTimeKind.Utc);
[Fact]
public void SixteenYearOlds_NeverPair_AtAnyOpinion()
{
var catalog = Fixtures.RomanceCatalog();
var boy = Pupil("a", year: 10, age: 16, female: false, "Bisexual");
var girl = Pupil("b", year: 10, age: 16, female: true, "Bisexual");
SetOpinion(boy, girl.Id, 100);
SetOpinion(girl, boy.Id, 100);
var roster = RosterOf(boy, girl, classYear: 10);
Affinity.Refresh(catalog, roster, AsOf);
Assert.Contains("b", boy.Bonds!.Crushes);
Assert.Contains("a", girl.Bonds!.Crushes);
Assert.Null(boy.Bonds.PartnerId);
Assert.Null(girl.Bonds.PartnerId);
Assert.False(Affinity.CanFormPair(catalog, roster, boy, girl, AsOf));
}
[Fact]
public void FirstYear_DoesNotCrushEleventh()
{
var catalog = Fixtures.RomanceCatalog();
var young = Pupil("c1", year: 1, age: 7, female: true, "Bisexual", classId: "y1");
var old = Pupil("c11", year: 11, age: 17, female: false, "Bisexual", classId: "y11");
SetOpinion(young, old.Id, 100);
var roster = new Roster(
[young, old],
[],
[
new SchoolClass("y1", 1, "А", "r1", 16, [young.Id]),
new SchoolClass("y11", 11, "А", "r11", 16, [old.Id]),
]);
Affinity.Refresh(catalog, roster, AsOf);
Assert.DoesNotContain("c11", young.Bonds!.Crushes);
Assert.False(Affinity.CanHaveSympathy(catalog, roster, young, old, AsOf));
}
[Fact]
public void Staff_NeverGetsCrushOnStudent()
{
var catalog = Fixtures.RomanceCatalog();
var teacher = Staff("t", female: false, "Bisexual");
var pupil = Pupil("p", year: 8, age: 14, female: true, "Bisexual");
SetOpinion(teacher, pupil.Id, 100);
SetOpinion(pupil, teacher.Id, 100);
var roster = RosterOf(teacher, pupil, classYear: 8);
Affinity.Refresh(catalog, roster, AsOf);
Assert.DoesNotContain(pupil.Id, teacher.Bonds!.Crushes);
Assert.Contains(teacher.Id, pupil.Bonds!.Crushes);
Assert.Null(teacher.Bonds.PartnerId);
Assert.Null(pupil.Bonds.PartnerId);
Assert.False(Affinity.CanHaveSympathy(catalog, roster, teacher, pupil, AsOf));
Assert.True(Affinity.CanHaveSympathy(catalog, roster, pupil, teacher, AsOf));
}
[Fact]
public void StaffPair_IsAllowedWhenBothAdults()
{
var catalog = Fixtures.RomanceCatalog();
var left = Staff("s1", female: false, "Bisexual");
var right = Staff("s2", female: true, "Bisexual");
SetOpinion(left, right.Id, 80);
SetOpinion(right, left.Id, 80);
var roster = new Roster([left, right], [], []);
var events = Affinity.Refresh(catalog, roster, AsOf);
Assert.Equal(right.Id, left.Bonds!.PartnerId);
Assert.Equal(left.Id, right.Bonds!.PartnerId);
Assert.Contains(events, row => row.Type == Affinity.Pair);
}
[Fact]
public void PairBreak_BlocksNewPairUntilNextDay()
{
var catalog = Fixtures.RomanceCatalog();
var left = Staff("s1", female: false, "Bisexual");
var right = Staff("s2", female: true, "Bisexual");
var third = Staff("s3", female: true, "Bisexual");
SetOpinion(left, right.Id, 80);
SetOpinion(right, left.Id, 80);
var roster = new Roster([left, right, third], [], []);
Affinity.Refresh(catalog, roster, AsOf);
Assert.Equal(right.Id, left.Bonds!.PartnerId);
SetOpinion(left, right.Id, 0);
Affinity.Refresh(catalog, roster, AsOf);
Assert.Null(left.Bonds.PartnerId);
Assert.NotNull(left.Bonds.PairEndedOn);
SetOpinion(left, third.Id, 80);
SetOpinion(third, left.Id, 80);
Affinity.Refresh(catalog, roster, AsOf);
Assert.Null(left.Bonds.PartnerId);
Affinity.Refresh(catalog, roster, AsOf.AddDays(1));
Assert.Equal(third.Id, left.Bonds.PartnerId);
}
[Fact]
public void PupilTalkWithTeacherCrush_WritesRebuffAndDropsOpinion()
{
var catalog = Fixtures.RomanceCatalog();
var teacher = Staff("t", female: false, "Bisexual");
var pupil = Pupil("p", year: 8, age: 14, female: true, "Bisexual");
SetOpinion(pupil, teacher.Id, 80);
var roster = RosterOf(teacher, pupil, classYear: 8);
Affinity.Refresh(catalog, roster, AsOf);
Assert.Contains(teacher.Id, pupil.Bonds!.Crushes);
var before = OpinionStore.Get(pupil, teacher.Id) ?? 0;
var events = Affinity.RebuffTalk(catalog, roster, [pupil.Id, teacher.Id], AsOf);
Assert.Contains(events, row => row.Type == Affinity.Rebuff && row.PersonId == pupil.Id);
Assert.True((OpinionStore.Get(pupil, teacher.Id) ?? 0) < before);
}
[Fact]
public void RosterJson_RoundTripsCrushesAndPartner()
{
var catalog = Fixtures.RomanceCatalog();
var left = Staff("s1", female: false, "Bisexual");
var right = Staff("s2", female: true, "Bisexual");
SetOpinion(left, right.Id, 80);
SetOpinion(right, left.Id, 80);
var roster = new Roster([left, right], [], []);
Affinity.Refresh(catalog, roster, AsOf);
Assert.Equal(right.Id, left.Bonds!.PartnerId);
var json = RosterJson.Serialize(RosterDocument.From(1, roster));
Assert.Contains("\"partnerId\"", json, StringComparison.Ordinal);
Assert.Contains("\"crushes\"", json, StringComparison.Ordinal);
var loaded = RosterJson.Parse(json).ToRoster();
var loadedLeft = loaded.People.First(person => person.Id.Equals(left.Id, StringComparison.Ordinal));
var loadedRight = loaded.People.First(person => person.Id.Equals(right.Id, StringComparison.Ordinal));
Assert.Equal(right.Id, loadedLeft.Bonds?.PartnerId);
Assert.Equal(left.Id, loadedRight.Bonds?.PartnerId);
Assert.Contains(right.Id, loadedLeft.Bonds!.Crushes);
Assert.Contains(left.Id, loadedRight.Bonds!.Crushes);
}
[Fact]
public void AdjacentYearStudents_CanHaveSympathy()
{
var catalog = Fixtures.RomanceCatalog();
var younger = Pupil("y", year: 9, age: 15, female: true, "Bisexual", classId: "c9");
var older = Pupil("o", year: 10, age: 18, female: false, "Bisexual", classId: "c10");
SetOpinion(younger, older.Id, 80);
var roster = new Roster(
[younger, older],
[],
[
new SchoolClass("c9", 9, "А", "r9", 16, [younger.Id]),
new SchoolClass("c10", 10, "А", "r10", 16, [older.Id]),
]);
Assert.True(Affinity.CanHaveSympathy(catalog, roster, younger, older, AsOf));
Affinity.Refresh(catalog, roster, AsOf);
Assert.Contains(older.Id, younger.Bonds!.Crushes);
}
private static void SetOpinion(Person person, string targetId, int value)
{
if (value == 0)
{
OpinionStore.Remove(person, targetId);
return;
}
OpinionStore.Set(person, targetId, value);
}
private static Roster RosterOf(Person first, Person second, int classYear)
{
var pupils = new[] { first, second }.Where(person => person.IsStudent).Select(person => person.Id).ToArray();
return new Roster(
[first, second],
[],
[new SchoolClass("c", classYear, "А", "room", 16, pupils)]);
}
private static Person Pupil(string id, int year, int age, bool female, string orientation, string? classId = null)
{
_ = year;
var birth = AsOf.AddYears(-age);
return AdultLike(id, female, orientation, isStudent: true, isStaff: false, birth, classId ?? "c");
}
private static Person Staff(string id, bool female, string orientation) =>
AdultLike(id, female, orientation, isStudent: false, isStaff: true, AsOf.AddYears(-30), classId: null);
private static Person AdultLike(
string id,
bool female,
string orientation,
bool isStudent,
bool isStaff,
DateTime birth,
string? classId)
{
var cases = new CaseTable
{
Nom = id,
Gen = id,
Dat = id,
Acc = id,
Ins = id,
Pre = id,
};
return new Person
{
Id = id,
FamilyId = "f",
Female = female,
BirthDate = DateTime.SpecifyKind(birth, DateTimeKind.Utc),
Name = new PersonName(id, id, id, cases, cases, cases),
IsStudent = isStudent,
IsStaff = isStaff,
IsParent = false,
ClassId = classId,
Numbers = new Dictionary<string, int>(StringComparer.Ordinal),
Choices = new Dictionary<string, string>(StringComparer.Ordinal),
Skills = new Dictionary<string, int>(StringComparer.Ordinal),
Traits = [],
Needs = new Dictionary<string, float>(StringComparer.Ordinal),
Opinions = new Dictionary<string, int>(StringComparer.Ordinal),
Orientation = orientation,
Bonds = new PersonBonds(),
};
}
}