Skip to content

Commit

Permalink
Change validation logic to be group-specific.
Browse files Browse the repository at this point in the history
  • Loading branch information
homothetyhk committed Feb 14, 2022
1 parent a5d4bde commit 4dbfbed
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 80 deletions.
3 changes: 0 additions & 3 deletions RandomizerCore/Json/LMConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@ public class LMConverter : JsonConverter<LogicManager>
{
public override LogicManager ReadJson(JsonReader reader, Type objectType, LogicManager existingValue, bool hasExistingValue, JsonSerializer serializer)
{


LogicManagerBuilder lmb = new();
JObject lm = JObject.Load(reader);
lmb.LP = lm[nameof(LogicManager.LP)].ToObject<LogicProcessor>(serializer);
lmb.VariableResolver = lm[nameof(LogicManager.VariableResolver)].ToObject<VariableResolver>(serializer);
Log($"Deserialized VR as {lmb.VariableResolver.GetType().Name}");

lmb.DeserializeJson(LogicManagerBuilder.JsonType.Terms, lm["Terms"]);
lmb.DeserializeJson(LogicManagerBuilder.JsonType.Waypoints, lm["Waypoints"]);
Expand Down
4 changes: 1 addition & 3 deletions RandomizerCore/Logic/OptimizedLogicDef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ public OptimizedLogicDef(OptimizedLogicDef def)
private readonly int[] logic;
private readonly LogicManager lm;

// recycling the stack saves approx 0.03s in 129000 calls of CanGet (i.e. standard preset)
// we don't ever clear the stack -- this causes no issues if all logic has correct syntax.
static readonly Stack<bool> stack = new();
private readonly Stack<bool> stack = new();

public bool CanGet(ProgressionManager pm)
{
Expand Down
1 change: 1 addition & 0 deletions RandomizerCore/Randomization/RandomizationGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class RandomizationGroup
public IRandoLocation[] Locations;
public string Label;
public GroupPlacementStrategy Strategy;
public Validator Validator = new();

/// <summary>
/// An event invoked after the items and locations of the group are permuted and have their priorities set.
Expand Down
85 changes: 11 additions & 74 deletions RandomizerCore/Randomization/Randomizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,97 +198,34 @@ public void Reset()
/// <exception cref="ValidationException"></exception>
public void Validate()
{
// First, check that each RandomizationGroup matches the output, by checking that the counts of items and locations by name match.
Dictionary<string, int> nameCounts = new();
for (int i = 0; i < stages.Length; i++)
{
RandomizationStage stage = stages[i];
for (int j = 0; j < stage.groups.Length; j++)
{
RandomizationGroup group = stage.groups[j];
List<RandoPlacement> ps = stagedPlacements[i][j];

if (group.Items.Length > ps.Count)
{
throw new ValidationException($"Items deleted from final placement for randomization group {group.Label}. Group expected {group.Items.Length} placements, but has {ps.Count} placements.");
}
else if (group.Items.Length < ps.Count)
{
throw new ValidationException($"Too many items found in final placement for randomization group {group.Label}. Group expected {group.Items.Length} placements, but has {ps.Count} placements.");
}

foreach (IRandoItem r in group.Items)
{
nameCounts.TryGetValue(r.Name, out int value);
nameCounts[r.Name] = value + 1;
}
foreach (RandoPlacement p in ps)
{
nameCounts.TryGetValue(p.Item.Name, out int value);
nameCounts[p.Item.Name] = value - 1;
}
foreach (KeyValuePair<string, int> kvp in nameCounts)
{
if (kvp.Value != 0) throw new ValidationException($"Improper item counts found in randomization group {group.Label}. Item {kvp.Key} was accounted for a net {kvp.Value} times");
}
nameCounts.Clear();

foreach (IRandoLocation r in group.Locations)
{
nameCounts.TryGetValue(r.Name, out int value);
nameCounts[r.Name] = value + 1;
}
foreach (RandoPlacement p in ps)
{
nameCounts.TryGetValue(p.Location.Name, out int value);
nameCounts[p.Location.Name] = value - 1;
}
foreach (KeyValuePair<string, int> kvp in nameCounts)
{
if (kvp.Value != 0) throw new ValidationException($"Improper item counts found in randomization group {group.Label}. Item {kvp.Key} was accounted for a net {kvp.Value} times");
}
nameCounts.Clear();
}
}

pm.Reset();
MainUpdater mu = InitializeUpdater();

List<PrePlacedItemUpdateEntry> entries = new();
foreach (List<RandoPlacement>[] arr in stagedPlacements)
List<List<PrePlacedItemUpdateEntry>[]> entries = new();
for (int i = 0; i < stages.Length; i++)
{
foreach (List<RandoPlacement> ps in arr)
entries.Add(new List<PrePlacedItemUpdateEntry>[stages[i].groups.Length]);
for (int j = 0; j < stages[i].groups.Length; j++)
{
foreach (RandoPlacement p in ps)
List<PrePlacedItemUpdateEntry> groupEntries = entries[i][j] = new();
foreach (RandoPlacement p in stagedPlacements[i][j])
{
PrePlacedItemUpdateEntry e = new(p.Item, p.Location);
entries.Add(e);
groupEntries.Add(e);
mu.AddEntry(e);
}
}
}

mu.Hook(pm);
foreach (PrePlacedItemUpdateEntry e in entries)
{
if (!e.obtained) throw new ValidationException($"Unreachable item placement detected: {e.item.Name} at {e.location.Name}");
}

#if DEBUG
Log();
Log("Placements:");
foreach (var l in stagedPlacements)

for (int i = 0; i < stages.Length; i++)
{
foreach (var m in l)
for (int j = 0; j < stages[i].groups.Length; j++)
{
foreach (var p in m)
{
Log($"({p.Item.Priority}) {{{p.Item.Name}}} at ({p.Location.Priority}) {{{p.Location.Name}}}");
}
stages[i].groups[j].Validator.Validate(stages[i].groups[j], pm, stagedPlacements[i][j], entries[i][j]);
}
}
Log();
#endif
}
}
}
69 changes: 69 additions & 0 deletions RandomizerCore/Randomization/Validator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using RandomizerCore.Exceptions;
using RandomizerCore.Logic;
using System.Text;

namespace RandomizerCore.Randomization
{
/// <summary>
/// Base class which handles determining whether errors exist in the randomizer output for a group.
/// <br/>By default, checks that the placement list has the right counts by name, and that all locations are reachable.
/// </summary>
public class Validator
{
/// <summary>
/// Tests the randomizer output for the given group and sends an exception if it is invalid.
/// </summary>
/// <exception cref="ValidationException"></exception>
public virtual void Validate(RandomizationGroup group, ProgressionManager pm, List<RandoPlacement> placements, List<PrePlacedItemUpdateEntry> entries)
{
ValidateCounts(group, pm, placements, entries);
ValidateNameCounts(group, pm, placements, entries);
ValidateAllLocationsReachable(group, pm, placements, entries);
}

protected virtual void ValidateCounts(RandomizationGroup group, ProgressionManager pm, List<RandoPlacement> placements, List<PrePlacedItemUpdateEntry> entries)
{
if (group.Items.Length > placements.Count)
{
throw new ValidationException($"Items deleted from final placement for randomization group {group.Label}. Group expected {group.Items.Length} placements, but has {placements.Count} placements.");
}
else if (group.Items.Length < placements.Count)
{
throw new ValidationException($"Too many items found in final placement for randomization group {group.Label}. Group expected {group.Items.Length} placements, but has {placements.Count} placements.");
}
}

protected virtual void ValidateNameCounts(RandomizationGroup group, ProgressionManager pm, List<RandoPlacement> placements, List<PrePlacedItemUpdateEntry> entries)
{
Dictionary<string, int> nameCounts = new();
foreach (IRandoItem r in group.Items)
{
nameCounts.TryGetValue(r.Name, out int value);
nameCounts[r.Name] = value + 1;
}
foreach (RandoPlacement p in placements)
{
nameCounts.TryGetValue(p.Item.Name, out int value);
nameCounts[p.Item.Name] = value - 1;
}
foreach (KeyValuePair<string, int> kvp in nameCounts)
{
if (kvp.Value != 0) throw new ValidationException($"Improper item counts found in randomization group {group.Label}. Item {kvp.Key} was accounted for a net {kvp.Value} times");
}
}

protected virtual void ValidateAllLocationsReachable(RandomizationGroup group, ProgressionManager pm, List<RandoPlacement> placements, List<PrePlacedItemUpdateEntry> entries)
{
StringBuilder sb = null;
foreach (PrePlacedItemUpdateEntry entry in entries)
{
if (!entry.obtained)
{
sb ??= new StringBuilder($"Unreachable placement(s) detected in group {group.Label}:").AppendLine();
sb.AppendLine($" {entry.item.Name} at {entry.location.Name}");
}
}
if (sb != null) throw new ValidationException(sb.ToString());
}
}
}
39 changes: 39 additions & 0 deletions RandomizerCore/Randomization/WeakTransitionValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using RandomizerCore.Logic;
using System.Text;

namespace RandomizerCore.Randomization
{
/// <summary>
/// Validator which assumes that its group is composed of RandoTransitions, and only checks that their terms are obtained, rather than that they are reachable.
/// </summary>
public class WeakTransitionValidator : Validator
{
public override void Validate(RandomizationGroup group, ProgressionManager pm, List<RandoPlacement> placements, List<PrePlacedItemUpdateEntry> entries)
{
ValidateCounts(group, pm, placements, entries);
ValidateNameCounts(group, pm, placements, entries);
WeakValidateTransitionsObtained(group, pm, placements, entries);
}

protected virtual void WeakValidateTransitionsObtained(RandomizationGroup group, ProgressionManager pm, List<RandoPlacement> placements, List<PrePlacedItemUpdateEntry> entries)
{
StringBuilder sb = null;
foreach (RandoPlacement p in placements)
{
RandoTransition source = (RandoTransition)p.Location;
RandoTransition target = (RandoTransition)p.Item;

if (!pm.Has(source.lt.term))
{
sb ??= new StringBuilder($"Inaccessible transitions(s) detected in group {group.Label}:").AppendLine();
sb.AppendLine($" {p.Location.Name} in {p.Location.Name} --> {p.Item.Name}");
}
if (!pm.Has(target.lt.term))
{
sb ??= new StringBuilder($"Inaccessible transitions(s) detected in group {group.Label}:").AppendLine();
sb.AppendLine($" {p.Item.Name} in {p.Location.Name} --> {p.Item.Name}");
}
}
}
}
}

0 comments on commit 4dbfbed

Please sign in to comment.