using HSchool.Ai; using HSchool.Content; using HSchool.People; using HSchool.Schedule; namespace HSchool.Simulation.Tests; public class WeatherTests { private static readonly DateTime JanuaryMorning = new(2012, 1, 15, 6, 0, 0, DateTimeKind.Utc); private static readonly DateTime JulyAfternoon = new(2012, 7, 15, 15, 0, 0, DateTimeKind.Utc); [Fact] public void SamePresetSeedAndTimestamp_YieldTheSameStreet() { var preset = Vanilla().ClimatePresets["TemperateContinental"]; var first = WeatherSampler.Sample(preset, schoolSeed: 7, JanuaryMorning); var second = WeatherSampler.Sample(preset, schoolSeed: 7, JanuaryMorning); Assert.Equal(first, second); } [Fact] public void January_IsColderThanJuly_OnTheVanillaPreset() { var preset = Vanilla().ClimatePresets["TemperateContinental"]; var january = WeatherSampler.Sample(preset, schoolSeed: 3, JanuaryMorning); var july = WeatherSampler.Sample(preset, schoolSeed: 3, JulyAfternoon); Assert.True(january.TemperatureC < july.TemperatureC, $"{january.TemperatureC} vs {july.TemperatureC}"); } [Fact] public void PrecipitationBelowZero_IsSnowNotRain() { var wet = new ClimatePresetDef { DefName = "AlwaysWet", MonthlyNorms = [-12, -10, -4, 5, 12, 17, 20, 18, 12, 5, -1, -8], DaySpread = 0, HourSpread = 0, PrecipitationChance = 1f, }; var january = WeatherSampler.Sample(wet, schoolSeed: 1, JanuaryMorning); var july = WeatherSampler.Sample(wet, schoolSeed: 1, JulyAfternoon); Assert.True(january.TemperatureC < 0); Assert.Equal(Precipitation.Snow, january.Precipitation); Assert.True(july.TemperatureC > 0); Assert.Equal(Precipitation.Rain, july.Precipitation); } [Fact] public void VanillaJanuary_SnowIsNeverRain() { var preset = Vanilla().ClimatePresets["TemperateContinental"]; for (var seed = 1; seed <= 40; seed++) { var weather = WeatherSampler.Sample(preset, seed, JanuaryMorning); if (weather.Precipitation == Precipitation.None) { continue; } Assert.True(weather.TemperatureC < 0); Assert.Equal(Precipitation.Snow, weather.Precipitation); } } [Fact] public void ExtraCommuteMinutes_FollowTheStreet() { var rules = Vanilla().BehaviorRules!; Assert.Equal(6, new OutdoorWeather(-5f, Precipitation.Snow).ExtraCommuteMinutes(rules)); Assert.Equal(3, new OutdoorWeather(8f, Precipitation.Rain).ExtraCommuteMinutes(rules)); Assert.Equal(0, new OutdoorWeather(8f, Precipitation.None).ExtraCommuteMinutes(rules)); Assert.Equal(0, new OutdoorWeather(-5f, Precipitation.Snow).ExtraCommuteMinutes(null)); } [Fact] public void SnowOnStreet_PlansAppearEarlierThanClear() { // Tick would SyncWeather and overwrite ForceWeather; Apply is the plan-build path. using var clear = ComingOnTuesday(); using var snow = ComingOnTuesday(); clear.ForceWeather(new OutdoorWeather(8f, Precipitation.None)); snow.ForceWeather(new OutdoorWeather(-5f, Precipitation.Snow)); PresenceSystem.Apply(clear, 1); PresenceSystem.Apply(snow, 1); var pupilId = FirstComingPupil(clear); var extra = clear.Catalog!.BehaviorRules!.CommuteSnowMinutes; Assert.Equal(6, extra); Assert.True(clear.Plans[pupilId].Comes); Assert.Equal(clear.Plans[pupilId].AppearAt!.Value.AddMinutes(-extra), snow.Plans[pupilId].AppearAt); } [Fact] public void SkipEmpty_SetsMondayMorningWeather_NotSaturdays() { var saturday = new DateTime(2012, 1, 14, 22, 0, 0, DateTimeKind.Utc); var monday = new DateTime(2012, 1, 16, 6, 0, 0, DateTimeKind.Utc); using var school = Open(saturday); var evening = school.Weather; Assert.True(school.TrySkipEmpty().Succeeded); Assert.Equal(monday, school.Clock.Time); var expected = WeatherSampler.Sample( school.Catalog!.ClimatePresets["TemperateContinental"], school.PeopleSeed, monday); Assert.Equal(expected, school.Weather); Assert.NotEqual(evening, school.Weather); } [Fact] public void Indoor_IsWarmerThanTheYard_ButNotComfortableInJanuary() { using var school = Open(JanuaryMorning); var outdoor = PlaceClimate.TemperatureC(school, "yard"); var porch = PlaceClimate.TemperatureC(school, "porch"); var room = PlaceClimate.TemperatureC(school, "classroom-101"); Assert.Equal(outdoor, porch); Assert.True(room > outdoor); Assert.True(room < 18f, $"walls only, got {room}"); } private static readonly DateTime TuesdayMorning = new(2012, 4, 3, 6, 0, 0, DateTimeKind.Utc); private static School ComingOnTuesday() { var school = Open(TuesdayMorning); var schoolClass = school.Roster!.Classes.First(); school.SetTimetable(new Timetable( [new LessonPlacement(schoolClass.Id, "Mathematics", "t1", schoolClass.RoomId, Day: 1, Period: 1)], [])); return school; } private static string FirstComingPupil(School school) { var pupil = school.Roster!.People.First(person => person.IsStudent && person.ClassId == school.Timetable!.Lessons[0].ClassId); return pupil.Id; } private static School Open(DateTime start) { var (catalog, map) = VanillaMap(); var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Russia", start); var pool = ApplicantPool.Create(catalog, roster, schoolSeed: 1, "Russia", start); var school = School.Create(1, "Погода", start, catalog, map); school.InstallPeople(roster, seed: 1, "Russia", pool, climatePresetId: "TemperateContinental"); school.ConfigurePresence(); return school; } private static DefCatalog Vanilla() { var (catalog, _) = VanillaMap(); return catalog; } private static (DefCatalog Catalog, MapLayout Map) VanillaMap() { var root = Path.Combine(AppContext.BaseDirectory, "vanilla"); var documents = new List(); foreach (var path in Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories)) { if (!path.EndsWith(".jsonc", StringComparison.OrdinalIgnoreCase) && !path.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) { continue; } var relative = Path.GetRelativePath(root, path).Replace('\\', '/'); documents.Add(new ContentDocument(CatalogLoader.CorePackId, relative, File.ReadAllText(path))); } var catalog = new CatalogLoader().Load([CatalogLoader.CorePackId], documents); var map = CatalogLoader.LastDefaultMap([CatalogLoader.CorePackId], documents); Assert.NotNull(map); return (catalog, map); } }