Skip to content

Commit

Permalink
Clear and reuse NodeSet
Browse files Browse the repository at this point in the history
  • Loading branch information
eelstork committed Oct 30, 2019
1 parent a15dd5b commit 5c22b20
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 34 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ Parametric actions are supported; they are concise and type safe. Check the [Bak

Quick and simple Unity integration via [GameAI.cs](Runtime/GameAI.cs) - for details, [read here](Documentation/BakerUnity.md).

Ready to GOAP? [follow the guide](Documentation/Overview.md).
Ready to GOAP? [Follow the guide](Documentation/Overview.md).

## Getting involved

Expand Down
33 changes: 16 additions & 17 deletions Runtime/Details/NodeSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,34 @@ public class NodeSet<T> : Base{
HashSet<T> states = new HashSet<T>();
List<Node<T>> list = new List<Node<T>>();

public NodeSet(T x, Heuristic<T> h, bool sorted = true,
int capacity = 128,
float precision = 0){
public NodeSet(){}

public NodeSet<T> Init(T x, Heuristic<T> h, bool sorted = true,
int capacity = 128,
float precision = 0){
if(visited > 0 || count > 0)
throw new Exception("Clear NodeSet first");
this.h = h;
this.sorted = sorted;
this.capacity = capacity;
this.precision = precision;
states.Add(Assert(x, "Initial state"));
list.Add(new Node<T>(INITIAL_STATE, x));
return this;
}

public bool capacityExceeded => count > capacity;

public static implicit operator bool(NodeSet<T> self)
=> self.count > 0 && self.count <= self.capacity;

internal int count => list.Count;
public bool capacityExceeded => count > capacity;
public int visited => states.Count;
public int count => list.Count;

public bool Insert(Node<T> n){
if(!states.Add(n.state)) return false;
if(sorted){
n.value = n.cost + (h != null ? h(n.state) : 0);
if(precision > 0) n.value = (int)(n.value / precision);
// NOTE: In actual use, tested 4x faster than SortedSet;
// Likely bottleneck with SortedSet is the API, not the
// algorithm; SortedSet needs total, ordering:
// - a.CompareTo(a) == 0
// - a.CompareTo(b) must positive if b.CompareTo(a) is
// negative.
// Superficially these rules are sound. In practice to
// just order elements by cost, this is overkill.
// Also getting Min/Max item is cheap, but there's no way
// to combine this with a 'pop'
for(int i = list.Count-1; i >= 0; i--){
if(n.value <= list[i].value){
list.Insert(i + 1, n);
Expand All @@ -55,8 +50,12 @@ public bool Insert(Node<T> n){
}

public Node<T> Pop(){
int i = list.Count-1; var n = list[i]; list.RemoveAt(i);
int i = list.Count-1;
var n = list[i];
list.RemoveAt(i);
return n;
}

public void Clear(){ states.Clear(); list.Clear(); }

}}
14 changes: 6 additions & 8 deletions Runtime/Solver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class Solver<T> : SolverStats where T : class{
T initialState;
Dish<T> dish;
Goal<T> goal;
NodeSet<T> avail = null;
NodeSet<T> avail = new NodeSet<T>();

public bool isRunning => status == S.Running;

Expand All @@ -27,8 +27,7 @@ public Node<T> Next(T s, in Goal<T> goal, int cap=-1){
initialState = s;
this.goal = goal;
iteration = 0;
avail = new NodeSet<T>(s, goal.h, !brfs, maxNodes,
tolerance);
avail.Init(s, goal.h, !brfs, maxNodes, tolerance);
return Iterate(cap);
}

Expand All @@ -41,19 +40,18 @@ public Node<T> Iterate(int cap=-1){
var current = avail.Pop();
if(goal.match(current.state)){
status = S.Done;
avail.Clear();
return current;
}
ExpandActions(current, avail);
ExpandMethods(current, avail);
if(avail.count > peak) peak = avail.count;
}
if(avail.capacityExceeded){
status = S.CapacityExceeded;
}else{
status = avail
status = avail.capacityExceeded ? S.CapacityExceeded
: status = avail
? (iteration < maxIter ? S.Running : S.MaxIterExceeded)
: S.Failed;
}
if(status != S.Running) avail.Clear();
return null;
}

Expand Down
31 changes: 23 additions & 8 deletions Tests/Editor/NodeSetTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,22 @@ public class NodeSetTest : TestBase{
Idler idler = new Idler();

[SetUp] public void Setup()
=> x = new NodeSet<Idler>(idler, null);
=> x = new NodeSet<Idler>().Init(idler, null);

[Test] public void NeedsClearBeforeInit()
=> Assert.Throws<Exception>( () => x.Init(idler, null) );

[Test] public void InitStateMustExist ()
=> Assert.Throws<NullReferenceException>(
() => new NodeSet<Idler>(null, null));
() => new NodeSet<Idler>().Init(null, null));

[Test] public void TrueWithinCapacity()
{ if(x){ } else Assert.Fail(); }

[Test] public void FalseOverCapacity()
{ o((bool)new NodeSet<Idler>(idler, null, capacity: 0), false); }
[Test] public void FalseOverCapacity(){
o((bool)new NodeSet<Idler>().Init(idler, null, capacity: 0),
false);
}

[Test] public void FalseWhenEmpty()
{ x.Pop(); o( (bool)x, false); }
Expand All @@ -28,20 +33,30 @@ [Test] public void InsertAndSkipExisting()
{ x.Insert(new Node<Idler>("x", idler)); o( x.count, 1); }

[Test] public void InsertUnsorted(){
var z = new NodeSet<T>(new T(), null);
var z = newWith_T;
z.sorted = false;
z.Insert(new Node<T>("x", new T())); o( z.count, 2);
z.Insert(newNode_T); o( z.count, 2);
}

[Test] public void InsertAndSort(){
var z = new NodeSet<T>(new T(), null);
z.Insert(new Node<T>("x", new T())); o( z.count, 2); }
var z = newWith_T;
z.Insert(newNode_T); o( z.count, 2); }

[Test] public void Pop(){
var z = x.Pop(); o( x.count, 0 ); o( z.state is Idler );
o( z.action, INITIAL_STATE);
}

[Test] public void Clear(){
var z = newWith_T;
o( z.count, 1 ); o( z.visited, 1 );
x.Clear();
o( x.count, 0 ); o( x.visited, 0 );
}

NodeSet<T> newWith_T => new NodeSet<T>().Init(new T(), null);
Node<T> newNode_T => new Node<T>("x", new T());

class T{
int x = 0; public T() {} public T(int x){ this.x = x; }
}
Expand Down

0 comments on commit 5c22b20

Please sign in to comment.