Skip to content

Commit

Permalink
Report solution random seed
Browse files Browse the repository at this point in the history
  • Loading branch information
janper committed May 15, 2021
1 parent b1fdad8 commit 7aeed28
Show file tree
Hide file tree
Showing 6 changed files with 1,261 additions and 109 deletions.
235 changes: 129 additions & 106 deletions Components/Solver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public ComponentSolver( ) : base("Monoceros WFC Solver",
"Main") {
}

public override Guid ComponentGuid => new Guid("4C19261E-4137-41F5-A118-A977021B2FA2");
public override Guid ComponentGuid => new Guid("97FF0AFD-0FA8-42ED-82C2-6D14B7A629CE");

protected override System.Drawing.Bitmap Icon => Properties.Resources.solver;

Expand Down Expand Up @@ -48,6 +48,11 @@ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pM
"Maximum Number of Solver Attempts",
GH_ParamAccess.item,
10);
pManager.AddIntegerParameter("Max Observations",
"O",
"Maximum Number of Solver Observations per Attempt (leave default for virtually unlimited)",
GH_ParamAccess.item,
Int32.MaxValue);
pManager.AddIntegerParameter("Max Time",
"T",
"Maximum Time spent with Attempts (milliseconds). Negative and 0 = infinity",
Expand All @@ -58,11 +63,6 @@ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pM
"Whether to use Shannon Entropy instead of the simpler linear entropy calculations",
GH_ParamAccess.item,
false);
pManager.AddIntegerParameter("Max Observations",
"O",
"Maximum Number of Solver Observations per Attempt (leave default for virtually unlimited)",
GH_ParamAccess.item,
Int32.MaxValue);
}

/// <summary>
Expand All @@ -75,10 +75,6 @@ protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager
"S",
"Solved Monoceros Slots",
GH_ParamAccess.list);
pManager.AddIntegerParameter("Attempts",
"A",
"Number of attempts needed to find a solution",
GH_ParamAccess.item);
pManager.AddBooleanParameter("Deterministic",
"OK",
"Did the Monoceros WFC Solver find a solution that can be Materialized?",
Expand All @@ -87,9 +83,17 @@ protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager
"C",
"Did the Monoceros WFC Solver end with a contradictory solution?",
GH_ParamAccess.item);
pManager.AddIntegerParameter("Seed",
"S",
"Random seed of the solution",
GH_ParamAccess.item);
pManager.AddIntegerParameter("Attempts",
"A",
"Number of attempts needed to find the solution",
GH_ParamAccess.item);
pManager.AddIntegerParameter("Observations",
"O",
"Number of observations needed to find a solution",
"Number of observations needed to find the solution",
GH_ParamAccess.item);
}

Expand Down Expand Up @@ -132,15 +136,15 @@ protected override void SolveInstance(IGH_DataAccess DA) {
return;
}

if (!DA.GetData(5, ref maxTime)) {
if (!DA.GetData(5, ref maxObservations)) {
return;
}

if (!DA.GetData(6, ref useShannonEntropy)) {
if (!DA.GetData(6, ref maxTime)) {
return;
}

if (!DA.GetData(7, ref maxObservations)) {
if (!DA.GetData(7, ref useShannonEntropy)) {
return;
}

Expand Down Expand Up @@ -510,10 +514,11 @@ protected override void SolveInstance(IGH_DataAccess DA) {

DA.SetData(0, stats.ToString());
DA.SetDataList(1, slotsSolved);
DA.SetData(2, stats.solveAttempts);
DA.SetData(3, stats.deterministic);
DA.SetData(4, stats.contradictory);
DA.SetData(5, stats.observations);
DA.SetData(2, stats.deterministic);
DA.SetData(3, stats.contradictory);
DA.SetData(4, stats.seed);
DA.SetData(5, stats.solveAttempts);
DA.SetData(6, stats.observations);
}

private static void ComputeWorldRelativeBounds(IEnumerable<Slot> slots,
Expand Down Expand Up @@ -742,108 +747,122 @@ private Stats Solve(List<RuleForSolver> rules,
}
}

//
// -- Random seed --
//
// wfc_rng_state_init needs 128 bits worth of random seed, but that
// is tricky to provide from GH. We let GH provide an int, use it
// to seed a C# Random, get 16 bytes of data from that and copy
// those into two u64's.

var random = new Random(randomSeed);
byte[] rngSeedLowArr = new byte[8];
byte[] rngSeedHighArr = new byte[8];
random.NextBytes(rngSeedLowArr);
random.NextBytes(rngSeedHighArr);

if (!BitConverter.IsLittleEndian) {
// If we are running on a BE machine, we need to reverse the bytes,
// because low and high are defined to always be LE.
Array.Reverse(rngSeedLowArr);
Array.Reverse(rngSeedHighArr);
}

ulong rngSeedLow = BitConverter.ToUInt64(rngSeedLowArr, 0);
ulong rngSeedHigh = BitConverter.ToUInt64(rngSeedHighArr, 0);

//
// -- Max attempts --
//

uint attempts = 0;
WfcObserveResult observationResult = WfcObserveResult.Contradiction;
uint spentObservations;
uint maxAttempts = (uint)maxAttemptsInt;

//
// -- Run the thing and **pray** --
//

var wfcRngStateHandle = IntPtr.Zero;
var wfcWorldStateHandle = IntPtr.Zero;
var wfcWorldStateHandleBackup = IntPtr.Zero;
unsafe {
Native.wfc_rng_state_init(&wfcRngStateHandle, rngSeedLow, rngSeedHigh);

fixed (AdjacencyRule* adjacencyRulesPtr = &adjacencyRules[0]) {
var result = Native.wfc_world_state_init(&wfcWorldStateHandle,
adjacencyRulesPtr,
(UIntPtr)adjacencyRules.Length,
worldX,
worldY,
worldZ,
entropy);

switch (result) {
case WfcWorldStateInitResult.Ok:
// All good
break;
case WfcWorldStateInitResult.ErrTooManyModules:
stats.report = "Monoceros Solver failed: Rules refer to Modules occupying " +
"too many Slots.";
return stats;
case WfcWorldStateInitResult.ErrWorldDimensionsZero:
stats.report = "Monoceros Solver failed: World dimensions are zero.";
return stats;
default:
stats.report = "Monoceros Solver failed with unknown error.";
return stats;
}

var randomMajor = new Random(randomSeed);
var firstAttempt = true;

while (true) {

//
// -- Random seed --
//
// wfc_rng_state_init needs 128 bits worth of random seed, but that
// is tricky to provide from GH. We let GH provide an int, use it
// to seed a C# Random, get 16 bytes of data from that and copy
// those into two u64's.

int currentSeed;

if (firstAttempt) {
currentSeed = randomSeed;
firstAttempt = false;
} else {
currentSeed = randomMajor.Next();
}

fixed (SlotState* worldStateSlotsPtr = &worldStateSlots[0]) {
var result = Native.wfc_world_state_slots_set(wfcWorldStateHandle,
worldStateSlotsPtr,
(UIntPtr)worldStateSlots.Length);
switch (result) {
case WfcWorldStateSlotsSetResult.Ok:
// All good
stats.worldNotCanonical = false;
break;
case WfcWorldStateSlotsSetResult.OkWorldNotCanonical:
// All good, but we the slots we gave were not
// canonical. wfc_world_state_slots_set fixed that for us.
stats.worldNotCanonical = true;
break;
case WfcWorldStateSlotsSetResult.ErrWorldContradictory:
stats.report = "Monoceros Solver failed: World state is contradictory. " +
"Try changing Slots, Modules, Rules or add boundary Rules. Changing " +
"random seed or max attempts will not help.";
return stats;
}
stats.seed = currentSeed;

var random = new Random(currentSeed);
byte[] rngSeedLowArr = new byte[8];
byte[] rngSeedHighArr = new byte[8];
random.NextBytes(rngSeedLowArr);
random.NextBytes(rngSeedHighArr);

if (!BitConverter.IsLittleEndian) {
// If we are running on a BE machine, we need to reverse the bytes,
// because low and high are defined to always be LE.
Array.Reverse(rngSeedLowArr);
Array.Reverse(rngSeedHighArr);
}

Native.wfc_world_state_init_from(&wfcWorldStateHandleBackup, wfcWorldStateHandle);
}
ulong rngSeedLow = BitConverter.ToUInt64(rngSeedLowArr, 0);
ulong rngSeedHigh = BitConverter.ToUInt64(rngSeedHighArr, 0);

uint spentObservations = 0;
stats.averageObservations = 0;

WfcObserveResult observationResult = WfcObserveResult.Contradiction;
//
// -- Run the thing and **pray** --
//

uint attempts = 0;

var timeStart = DateTime.UtcNow;
unsafe {
Native.wfc_rng_state_init(&wfcRngStateHandle, rngSeedLow, rngSeedHigh);

fixed (AdjacencyRule* adjacencyRulesPtr = &adjacencyRules[0]) {
var result = Native.wfc_world_state_init(&wfcWorldStateHandle,
adjacencyRulesPtr,
(UIntPtr)adjacencyRules.Length,
worldX,
worldY,
worldZ,
entropy);

switch (result) {
case WfcWorldStateInitResult.Ok:
// All good
break;
case WfcWorldStateInitResult.ErrTooManyModules:
stats.report = "Monoceros Solver failed: Rules refer to Modules occupying " +
"too many Slots.";
return stats;
case WfcWorldStateInitResult.ErrWorldDimensionsZero:
stats.report = "Monoceros Solver failed: World dimensions are zero.";
return stats;
default:
stats.report = "Monoceros Solver failed with unknown error.";
return stats;
}
}

fixed (SlotState* worldStateSlotsPtr = &worldStateSlots[0]) {
var result = Native.wfc_world_state_slots_set(wfcWorldStateHandle,
worldStateSlotsPtr,
(UIntPtr)worldStateSlots.Length);
switch (result) {
case WfcWorldStateSlotsSetResult.Ok:
// All good
stats.worldNotCanonical = false;
break;
case WfcWorldStateSlotsSetResult.OkWorldNotCanonical:
// All good, but we the slots we gave were not
// canonical. wfc_world_state_slots_set fixed that for us.
stats.worldNotCanonical = true;
break;
case WfcWorldStateSlotsSetResult.ErrWorldContradictory:
stats.report = "Monoceros Solver failed: World state is contradictory. " +
"Try changing Slots, Modules, Rules or add boundary Rules. Changing " +
"random seed or max attempts will not help.";
return stats;
}
}

Native.wfc_world_state_init_from(&wfcWorldStateHandleBackup, wfcWorldStateHandle);
}

spentObservations = 0;
stats.averageObservations = 0;

var timeStart = DateTime.UtcNow;

unsafe {

unsafe {
while (true) {
observationResult = Native.wfc_observe(wfcWorldStateHandle,
wfcRngStateHandle,
maxObservations,
Expand Down Expand Up @@ -999,6 +1018,7 @@ internal struct Stats {
public uint partCount;
public uint slotCount;
public uint solveAttempts;
public int seed;
public uint observationLimit;
public uint observations;
public uint averageObservations;
Expand All @@ -1023,6 +1043,9 @@ public override string ToString( ) {
b.Append("Slot count (including outer Slots): ");
b.Append(slotCount);
b.AppendLine();
b.Append("Solution random seed: ");
b.Append(seed);
b.AppendLine();
b.Append("Solve attempts: ");
b.Append(solveAttempts);
b.AppendLine();
Expand Down
1 change: 0 additions & 1 deletion Components/SolverObsolete1235.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public ComponentSolverObsolete1235( ) : base("Monoceros WFC Solver",
public override Guid ComponentGuid => new Guid("DD1A1FA6-ACD4-4202-8B1A-9840949644B3");

protected override System.Drawing.Bitmap Icon => Properties.Resources.solver;

public override bool Obsolete => true;
public override GH_Exposure Exposure => GH_Exposure.hidden;

Expand Down
Loading

0 comments on commit 7aeed28

Please sign in to comment.