311 lines
12 KiB
C#
311 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
namespace IdleSpectator;
|
|
|
|
public sealed class CatalogCoverageDiff
|
|
{
|
|
public string Domain = "";
|
|
public int LiveTotal;
|
|
public int AuthoredTotal;
|
|
public int Missing;
|
|
public int Orphan;
|
|
public readonly List<string> MissingIds = new List<string>();
|
|
public readonly List<string> OrphanIds = new List<string>();
|
|
|
|
public bool Passed => Missing == 0 && Orphan == 0;
|
|
|
|
public string Detail =>
|
|
$"{Domain}: live={LiveTotal} authored={AuthoredTotal} missing={Missing} orphan={Orphan}";
|
|
}
|
|
|
|
/// <summary>Shared live-vs-authored coverage audit used by domain event catalogs.</summary>
|
|
public static class CatalogCoverageAudit
|
|
{
|
|
public static CatalogCoverageDiff Diff(
|
|
string domain,
|
|
IEnumerable<string> liveIds,
|
|
IEnumerable<string> authoredIds)
|
|
{
|
|
var live = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
var authored = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
var diff = new CatalogCoverageDiff { Domain = domain ?? "domain" };
|
|
|
|
if (liveIds != null)
|
|
{
|
|
foreach (string id in liveIds)
|
|
{
|
|
if (string.IsNullOrEmpty(id))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
live.Add(id.Trim());
|
|
}
|
|
}
|
|
|
|
if (authoredIds != null)
|
|
{
|
|
foreach (string id in authoredIds)
|
|
{
|
|
if (string.IsNullOrEmpty(id))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
authored.Add(id.Trim());
|
|
}
|
|
}
|
|
|
|
diff.LiveTotal = live.Count;
|
|
diff.AuthoredTotal = authored.Count;
|
|
|
|
foreach (string id in live)
|
|
{
|
|
if (!authored.Contains(id))
|
|
{
|
|
diff.Missing++;
|
|
diff.MissingIds.Add(id);
|
|
}
|
|
}
|
|
|
|
foreach (string id in authored)
|
|
{
|
|
if (!live.Contains(id))
|
|
{
|
|
diff.Orphan++;
|
|
diff.OrphanIds.Add(id);
|
|
}
|
|
}
|
|
|
|
return diff;
|
|
}
|
|
|
|
public static void WriteReport(string outputDirectory, string fileName, string body)
|
|
{
|
|
if (string.IsNullOrEmpty(outputDirectory) || string.IsNullOrEmpty(fileName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
Directory.CreateDirectory(outputDirectory);
|
|
File.WriteAllText(Path.Combine(outputDirectory, fileName), body ?? "");
|
|
}
|
|
catch
|
|
{
|
|
// ignore IO
|
|
}
|
|
}
|
|
}
|
|
|
|
public sealed class DomainEventAuditResult
|
|
{
|
|
public bool Passed;
|
|
public string Detail = "";
|
|
}
|
|
|
|
/// <summary>Coverage audits for relationship discrete events + live plot/era/book libraries.</summary>
|
|
public static class DomainEventHarness
|
|
{
|
|
public static DomainEventAuditResult RunAudit(string outputDirectory)
|
|
{
|
|
var tsv = new StringBuilder();
|
|
tsv.AppendLine("domain\tid\tauthored\towns_camera\tevent_strength\tnotes");
|
|
|
|
CatalogCoverageDiff plots = CatalogCoverageAudit.Diff(
|
|
"plots",
|
|
ActivityAssetCatalog.EnumerateLivePlotIds(),
|
|
EventCatalog.Plot.AuthoredIds);
|
|
AppendLibraryDomain(tsv, "plots", ActivityAssetCatalog.EnumerateLivePlotIds(), plots, EventCatalog.Plot.GetOrFallback);
|
|
|
|
CatalogCoverageDiff eras = CatalogCoverageAudit.Diff(
|
|
"eras",
|
|
ActivityAssetCatalog.EnumerateLiveEraIds(),
|
|
EventCatalog.Era.AuthoredIds);
|
|
AppendLibraryDomain(tsv, "eras", ActivityAssetCatalog.EnumerateLiveEraIds(), eras, EventCatalog.Era.GetOrFallback);
|
|
|
|
CatalogCoverageDiff books = CatalogCoverageAudit.Diff(
|
|
"books",
|
|
ActivityAssetCatalog.EnumerateLiveBookTypeIds(),
|
|
EventCatalog.Book.AuthoredIds);
|
|
AppendLibraryDomain(tsv, "books", ActivityAssetCatalog.EnumerateLiveBookTypeIds(), books, EventCatalog.Book.GetOrFallback);
|
|
|
|
CatalogCoverageDiff traits = CatalogCoverageAudit.Diff(
|
|
"traits",
|
|
ActivityAssetCatalog.EnumerateLiveTraitIds(),
|
|
EventCatalog.Trait.AuthoredIds);
|
|
AppendLibraryDomain(tsv, "traits", ActivityAssetCatalog.EnumerateLiveTraitIds(), traits, EventCatalog.Trait.GetOrFallback);
|
|
|
|
CatalogCoverageDiff spells = CatalogCoverageAudit.Diff(
|
|
"spells",
|
|
ActivityAssetCatalog.EnumerateLiveSpellIds(),
|
|
EventCatalog.Spell.AuthoredIds);
|
|
AppendLibraryDomain(tsv, "spells", ActivityAssetCatalog.EnumerateLiveSpellIds(), spells, EventCatalog.Spell.GetOrFallback);
|
|
|
|
CatalogCoverageDiff powers = CatalogCoverageAudit.Diff(
|
|
"powers",
|
|
ActivityAssetCatalog.EnumerateLivePowerIds(),
|
|
EventCatalog.Power.AuthoredIds);
|
|
AppendLibraryDomain(tsv, "powers", ActivityAssetCatalog.EnumerateLivePowerIds(), powers, EventCatalog.Power.GetOrFallback);
|
|
|
|
CatalogCoverageDiff decisions = CatalogCoverageAudit.Diff(
|
|
"decisions",
|
|
ActivityAssetCatalog.EnumerateLiveDecisionIds(),
|
|
EventCatalog.Decision.AuthoredIds);
|
|
AppendLibraryDomain(tsv, "decisions", ActivityAssetCatalog.EnumerateLiveDecisionIds(), decisions, EventCatalog.Decision.GetOrFallback);
|
|
|
|
CatalogCoverageDiff items = CatalogCoverageAudit.Diff(
|
|
"items",
|
|
ActivityAssetCatalog.EnumerateLiveItemIds(),
|
|
EventCatalog.Item.AuthoredIds);
|
|
AppendLibraryDomain(tsv, "items", ActivityAssetCatalog.EnumerateLiveItemIds(), items, EventCatalog.Item.GetOrFallback);
|
|
|
|
CatalogCoverageDiff subspTraits = CatalogCoverageAudit.Diff(
|
|
"subspecies_traits",
|
|
ActivityAssetCatalog.EnumerateLiveSubspeciesTraitIds(),
|
|
EventCatalog.SubspeciesTrait.AuthoredIds);
|
|
AppendLibraryDomain(
|
|
tsv,
|
|
"subspecies_traits",
|
|
ActivityAssetCatalog.EnumerateLiveSubspeciesTraitIds(),
|
|
subspTraits,
|
|
EventCatalog.SubspeciesTrait.GetOrFallback);
|
|
|
|
CatalogCoverageDiff genes = CatalogCoverageAudit.Diff(
|
|
"genes",
|
|
ActivityAssetCatalog.EnumerateLiveGeneIds(),
|
|
EventCatalog.Gene.AuthoredIds);
|
|
AppendLibraryDomain(tsv, "genes", ActivityAssetCatalog.EnumerateLiveGeneIds(), genes, EventCatalog.Gene.GetOrFallback);
|
|
|
|
CatalogCoverageDiff phenotypes = CatalogCoverageAudit.Diff(
|
|
"phenotypes",
|
|
ActivityAssetCatalog.EnumerateLivePhenotypeIds(),
|
|
EventCatalog.Phenotype.AuthoredIds);
|
|
AppendLibraryDomain(
|
|
tsv,
|
|
"phenotypes",
|
|
ActivityAssetCatalog.EnumerateLivePhenotypeIds(),
|
|
phenotypes,
|
|
EventCatalog.Phenotype.GetOrFallback);
|
|
|
|
CatalogCoverageDiff worldLaws = CatalogCoverageAudit.Diff(
|
|
"world_laws",
|
|
ActivityAssetCatalog.EnumerateLiveWorldLawIds(),
|
|
EventCatalog.WorldLaw.AuthoredIds);
|
|
AppendLibraryDomain(
|
|
tsv,
|
|
"world_laws",
|
|
ActivityAssetCatalog.EnumerateLiveWorldLawIds(),
|
|
worldLaws,
|
|
EventCatalog.WorldLaw.GetOrFallback);
|
|
|
|
CatalogCoverageDiff biomes = CatalogCoverageAudit.Diff(
|
|
"biomes",
|
|
ActivityAssetCatalog.EnumerateLiveBiomeIds(),
|
|
EventCatalog.Biome.AuthoredIds);
|
|
AppendLibraryDomain(tsv, "biomes", ActivityAssetCatalog.EnumerateLiveBiomeIds(), biomes, EventCatalog.Biome.GetOrFallback);
|
|
|
|
int relMissing = 0;
|
|
int relTotal = 0;
|
|
foreach (string id in EventCatalog.Relationship.AuthoredIds)
|
|
{
|
|
relTotal++;
|
|
DiscreteEventEntry entry = EventCatalog.Relationship.GetOrFallback(id);
|
|
bool ok = EventCatalog.Relationship.HasAuthored(id) && !entry.IsFallback;
|
|
if (!ok)
|
|
{
|
|
relMissing++;
|
|
}
|
|
|
|
tsv.Append("relationship\t").Append(id).Append('\t')
|
|
.Append(ok ? "1" : "0").Append('\t')
|
|
.Append(entry.CreatesInterest).Append('\t')
|
|
.Append(entry.EventStrength.ToString("0.#")).Append('\t')
|
|
.Append(ok ? "ok" : "missing_authored").AppendLine();
|
|
}
|
|
|
|
CatalogCoverageAudit.WriteReport(outputDirectory, "domain_event_audit.tsv", tsv.ToString());
|
|
|
|
// Camera decisions must have authored ActionPhrase + EventStrength (event-catalog.json).
|
|
int decisionProseMissing = 0;
|
|
int decisionStrengthMissing = 0;
|
|
int decisionProseTotal = 0;
|
|
List<string> liveDecisions = ActivityAssetCatalog.EnumerateLiveDecisionIds();
|
|
for (int i = 0; i < liveDecisions.Count; i++)
|
|
{
|
|
string id = liveDecisions[i];
|
|
if (!EventCatalog.Decision.IsCameraWorthy(id))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
decisionProseTotal++;
|
|
DiscreteEventEntry entry = EventCatalog.Decision.GetOrFallback(id);
|
|
if (!EventCatalog.Decision.TryGetAuthoredActionPhrase(id, out _))
|
|
{
|
|
decisionProseMissing++;
|
|
tsv.Append("decision_prose\t").Append(id).Append("\t0\t1\t\tmissing_action_phrase").AppendLine();
|
|
}
|
|
else if (entry.EventStrength <= 0f)
|
|
{
|
|
decisionStrengthMissing++;
|
|
tsv.Append("decision_prose\t").Append(id).Append("\t0\t1\t")
|
|
.Append(entry.EventStrength.ToString("0.#"))
|
|
.Append("\tmissing_strength").AppendLine();
|
|
}
|
|
else
|
|
{
|
|
string phrase = EventCatalog.Decision.ActionPhraseFor(id);
|
|
tsv.Append("decision_prose\t").Append(id).Append("\t1\t1\t")
|
|
.Append(entry.EventStrength.ToString("0.#")).Append('\t')
|
|
.Append(phrase).AppendLine();
|
|
}
|
|
}
|
|
|
|
CatalogCoverageAudit.WriteReport(outputDirectory, "domain_event_audit.tsv", tsv.ToString());
|
|
|
|
bool passed = plots.Passed && eras.Passed && books.Passed && traits.Passed
|
|
&& spells.Passed && powers.Passed && decisions.Passed && items.Passed
|
|
&& subspTraits.Passed && genes.Passed && phenotypes.Passed
|
|
&& worldLaws.Passed && biomes.Passed
|
|
&& relMissing == 0 && relTotal > 0
|
|
&& decisionProseMissing == 0 && decisionStrengthMissing == 0
|
|
&& decisionProseTotal > 0;
|
|
string detail =
|
|
$"{plots.Detail}; {eras.Detail}; {books.Detail}; {traits.Detail}; "
|
|
+ $"{spells.Detail}; {powers.Detail}; {decisions.Detail}; {items.Detail}; "
|
|
+ $"{subspTraits.Detail}; {genes.Detail}; {phenotypes.Detail}; "
|
|
+ $"{worldLaws.Detail}; {biomes.Detail}; relationship: authored={relTotal} missing={relMissing}; "
|
|
+ $"decision_prose: camera={decisionProseTotal} missing={decisionProseMissing} "
|
|
+ $"strengthMissing={decisionStrengthMissing} catalog={EventCatalogConfig.LoadedFromFile}";
|
|
return new DomainEventAuditResult { Passed = passed, Detail = detail };
|
|
}
|
|
|
|
private static void AppendLibraryDomain(
|
|
StringBuilder tsv,
|
|
string domain,
|
|
IEnumerable<string> liveIds,
|
|
CatalogCoverageDiff diff,
|
|
Func<string, DiscreteEventEntry> lookup)
|
|
{
|
|
foreach (string id in liveIds)
|
|
{
|
|
DiscreteEventEntry entry = lookup(id);
|
|
bool has = !entry.IsFallback;
|
|
tsv.Append(domain).Append('\t').Append(id).Append('\t')
|
|
.Append(has ? "1" : "0").Append('\t')
|
|
.Append(entry.CreatesInterest).Append('\t')
|
|
.Append(entry.EventStrength.ToString("0.#")).Append('\t')
|
|
.Append(has ? "ok" : "missing_authored").AppendLine();
|
|
}
|
|
|
|
foreach (string id in diff.OrphanIds)
|
|
{
|
|
tsv.Append(domain).Append('\t').Append(id).Append("\t1\t\t0\torphan_authored").AppendLine();
|
|
}
|
|
}
|
|
}
|