Halve lesson gain when the bag has no textbook for the subject.
Locker and home do not count. A pack without the field keeps vanilla 0.5. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using HSchool.Content;
|
||||
using HSchool.People;
|
||||
|
||||
namespace HSchool.Ai.Tests;
|
||||
|
||||
@@ -97,6 +98,40 @@ public class LessonLearningTests
|
||||
|
||||
Assert.Equal(explicitWarm, implied);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MissingTextbook_HalvesGain()
|
||||
{
|
||||
var withBook = LessonLearning.Gain(50, Math, 1, 0.05f, 1f, 1f, 0, 100, warmth: 1f, textbookFactor: 1f);
|
||||
var without = LessonLearning.Gain(50, Math, 1, 0.05f, 1f, 1f, 0, 100, warmth: 1f, textbookFactor: 0.5f);
|
||||
|
||||
Assert.Equal((withBook - 50) * 0.5f, without - 50, precision: 5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TextbookInBag_OnlyThatSubjectCounts()
|
||||
{
|
||||
var items = new[]
|
||||
{
|
||||
new InventoryItem("Textbook", null, 1f, ItemLocations.Bag, "Literature"),
|
||||
new InventoryItem("Textbook", null, 1f, ItemLocations.Locker, "Mathematics"),
|
||||
};
|
||||
|
||||
Assert.False(LessonLearning.HasTextbookInBag(items, "Mathematics"));
|
||||
Assert.True(LessonLearning.HasTextbookInBag(items, "Literature"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TextbookAtHome_DoesNotCount()
|
||||
{
|
||||
var items = new[]
|
||||
{
|
||||
new InventoryItem("Textbook", null, 1f, ItemLocations.Home, "Mathematics"),
|
||||
};
|
||||
|
||||
Assert.False(LessonLearning.HasTextbookInBag(items, "Mathematics"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MissingTeacherSkill_UsesRangeMin_AndStillGains()
|
||||
{
|
||||
|
||||
@@ -64,6 +64,39 @@ public class BehaviorDefTests
|
||||
Assert.Contains("carry mass", error.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MissingLessonNoTextbookFactor_DefaultsToHalf()
|
||||
{
|
||||
var catalog = _loader.Load(
|
||||
[CatalogLoader.CorePackId],
|
||||
[
|
||||
PackDocuments.Def(
|
||||
CatalogLoader.CorePackId,
|
||||
"behavior",
|
||||
"rules",
|
||||
"""{ "defName": "Behavior", "needThreshold": 0.35, "lessonSkillPerHour": 0.05, "commuteSlackMin": 0, "commuteSlackMax": 6, "switchMargin": 0.15 }"""),
|
||||
]);
|
||||
|
||||
Assert.NotNull(catalog.BehaviorRules);
|
||||
Assert.Equal(0.5f, catalog.BehaviorRules.LessonNoTextbookFactor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LessonNoTextbookFactor_OutOfRange_FailsTheCatalog()
|
||||
{
|
||||
var error = Assert.Throws<ContentLoadException>(() => _loader.Load(
|
||||
[CatalogLoader.CorePackId],
|
||||
[
|
||||
PackDocuments.Def(
|
||||
CatalogLoader.CorePackId,
|
||||
"behavior",
|
||||
"rules",
|
||||
"""{ "defName": "Behavior", "lessonNoTextbookFactor": 1.5 }"""),
|
||||
]));
|
||||
|
||||
Assert.Contains("lessonNoTextbookFactor", error.Message, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NegativeApparelWear_FailsTheCatalog()
|
||||
{
|
||||
|
||||
@@ -238,6 +238,96 @@ public class LessonTeacherPresentTests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TextbookInBag_GainsFull()
|
||||
{
|
||||
var (school, homeroom, pupilId, teacherId) = StaffedMath();
|
||||
using (school)
|
||||
{
|
||||
AdvanceTo(school, LessonStart);
|
||||
SetPlace(school, pupilId, homeroom);
|
||||
SetPlace(school, teacherId, homeroom);
|
||||
PlaceTextbook(school, pupilId, "Mathematics", ItemLocations.Bag);
|
||||
var before = SkillOf(school, pupilId, "Mathematics");
|
||||
|
||||
LessonLearningSystem.Apply(school, 45);
|
||||
|
||||
Assert.True(SkillOf(school, pupilId, "Mathematics") > before);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ItemLocations.Locker)]
|
||||
[InlineData(ItemLocations.Home)]
|
||||
public void TextbookNotInBag_GainsHalf(string location)
|
||||
{
|
||||
var (school, homeroom, pupilId, teacherId) = StaffedMath();
|
||||
using (school)
|
||||
{
|
||||
AdvanceTo(school, LessonStart);
|
||||
SetPlace(school, pupilId, homeroom);
|
||||
SetPlace(school, teacherId, homeroom);
|
||||
PlaceTextbook(school, pupilId, "Mathematics", ItemLocations.Bag);
|
||||
var start = SkillOf(school, pupilId, "Mathematics");
|
||||
LessonLearningSystem.Apply(school, 45);
|
||||
var bagGain = SkillOf(school, pupilId, "Mathematics") - start;
|
||||
|
||||
SetSkill(school, pupilId, "Mathematics", start);
|
||||
PlaceTextbook(school, pupilId, "Mathematics", location);
|
||||
LessonLearningSystem.Apply(school, 45);
|
||||
var awayGain = SkillOf(school, pupilId, "Mathematics") - start;
|
||||
|
||||
Assert.True(bagGain > 0);
|
||||
Assert.Equal(bagGain * 0.5f, awayGain, precision: 4);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OtherSubjectInBag_DoesNotCoverThisLesson()
|
||||
{
|
||||
var (school, homeroom, pupilId, teacherId) = StaffedMath();
|
||||
using (school)
|
||||
{
|
||||
AdvanceTo(school, LessonStart);
|
||||
SetPlace(school, pupilId, homeroom);
|
||||
SetPlace(school, teacherId, homeroom);
|
||||
PlaceTextbook(school, pupilId, "Mathematics", ItemLocations.Bag);
|
||||
var start = SkillOf(school, pupilId, "Mathematics");
|
||||
LessonLearningSystem.Apply(school, 45);
|
||||
var mathGain = SkillOf(school, pupilId, "Mathematics") - start;
|
||||
|
||||
SetSkill(school, pupilId, "Mathematics", start);
|
||||
PlaceTextbook(school, pupilId, "Literature", ItemLocations.Bag);
|
||||
LessonLearningSystem.Apply(school, 45);
|
||||
var otherGain = SkillOf(school, pupilId, "Mathematics") - start;
|
||||
|
||||
Assert.Equal(mathGain * 0.5f, otherGain, precision: 4);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoTextbook_LogsOncePerSubject()
|
||||
{
|
||||
var (school, homeroom, pupilId, teacherId) = StaffedMath();
|
||||
using (school)
|
||||
{
|
||||
AdvanceTo(school, LessonStart);
|
||||
SetPlace(school, pupilId, homeroom);
|
||||
SetPlace(school, teacherId, homeroom);
|
||||
PlaceTextbook(school, pupilId, "Mathematics", ItemLocations.Home);
|
||||
|
||||
LessonLearningSystem.Apply(school, 5);
|
||||
LessonLearningSystem.Apply(school, 5);
|
||||
|
||||
var rows = school.DayLog
|
||||
.Where(row => row.PersonId == pupilId && row.Type == PersonLogTypes.LessonNoTextbook)
|
||||
.ToArray();
|
||||
Assert.Single(rows);
|
||||
Assert.Equal("Mathematics", rows[0].ThingDef);
|
||||
Assert.Equal("нет учебника: Математика", rows[0].Caption(school.Catalog!, "ru"));
|
||||
}
|
||||
}
|
||||
|
||||
private static (School School, string Homeroom, string PupilId, string TeacherId) StaffedMath(
|
||||
string? teacherId = null)
|
||||
{
|
||||
@@ -263,6 +353,25 @@ public class LessonTeacherPresentTests
|
||||
return (school, schoolClass.RoomId, pupil.Id, hiredId);
|
||||
}
|
||||
|
||||
private static void PlaceTextbook(School school, string personId, string subject, string location)
|
||||
{
|
||||
var person = school.Roster!.People.First(row => row.Id.Equals(personId, StringComparison.Ordinal));
|
||||
if (person.Items is not IList<InventoryItem> items || items.IsReadOnly)
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot mutate items for {personId}.");
|
||||
}
|
||||
|
||||
for (var i = items.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (items[i].Subject is not null)
|
||||
{
|
||||
items.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
items.Add(new InventoryItem("Textbook", Color: null, Condition: 1f, location, subject));
|
||||
}
|
||||
|
||||
private static void AdvanceTo(School school, DateTime until)
|
||||
{
|
||||
while (school.Clock.Time < until)
|
||||
|
||||
Reference in New Issue
Block a user