Last active
December 23, 2015 16:18
-
-
Save PtrMan/6660803 to your computer and use it in GitHub Desktop.
STRIPS solver in D using generic to speed it up a bit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std.stdio; | |
struct EnqueueDequeuePolicy; | |
mixin template QueueStrategy(Type, PolicyQueueStrategy : EnqueueDequeuePolicy) | |
{ | |
public final void enqueue(Type Element, out bool Success) | |
{ | |
this.storageEnqueue(Element, Success); | |
} | |
public final Type dequeue(out bool Success) | |
{ | |
return this.storageDequeue(Success); | |
} | |
final public bool isEmpty() | |
{ | |
return this.storageIsEmpty(); | |
} | |
final public bool isNotEmpty() | |
{ | |
return !this.isEmpty(); | |
} | |
} | |
import std.algorithm : remove; | |
struct DynamicArrayStoreStrategy; | |
mixin template ListStoreStrategy(Type, PolicyListStoreStrategy : DynamicArrayStoreStrategy, bool IteratorEnabled) | |
{ | |
protected final void storageEnqueue(Type Element, out bool Success) | |
{ | |
Success = true; | |
Data ~= Element; | |
} | |
protected final Type storageDequeue(out bool Success) | |
{ | |
Type Result; | |
Success = false; | |
if( this.Data.length == 0 ) | |
{ | |
return Type.init; | |
} | |
Result = this.Data[0]; | |
this.Data = remove(this.Data, 0); | |
Success = true; | |
return Result; | |
} | |
protected final void deleteAll() | |
{ | |
this.Data.length = 0; | |
} | |
final protected bool storageIsEmpty() | |
{ | |
return this.Data.length == 0; | |
} | |
private Type[] Data; | |
private alias typeof(this) OuterThisType; | |
static if(IteratorEnabled) | |
{ | |
final public Test0 getIterator() | |
{ | |
return Test0(this); | |
} | |
private struct Test0 | |
{ | |
public this(OuterThisType Storage) | |
{ | |
this.Storage = Storage; | |
} | |
public final @property bool empty() const | |
{ | |
return this.CurrentIndex >= this.Storage.Data.length; | |
} | |
public final @property ref Type front() | |
{ | |
return this.Storage.Data[this.CurrentIndex]; | |
} | |
public final void popFront() | |
{ | |
this.CurrentIndex++; | |
} | |
private OuterThisType Storage; | |
private size_t CurrentIndex = 0; | |
} | |
} | |
} | |
struct FlushableStrategy; | |
struct UnflushableStrategy; | |
mixin template FlushStrategy(PolicyFlushStrategy : FlushableStrategy) | |
{ | |
public final void flush() | |
{ | |
this.Storage.deleteAll(); | |
} | |
} | |
/* | |
mixin template AccessStrategy(PolicyAccessStrategy : ) | |
{ | |
final public @property bool empty() const | |
{ | |
return this.Storage.isEmpty(); | |
} | |
} | |
*/ | |
template Queue(Type, PolicyQueueStrategy, PolicyListStoreStrategy, bool IteratorEnabled, PolicyFlushStrategy) | |
{ | |
class Queue | |
{ | |
mixin ListStoreStrategy!(Type, PolicyListStoreStrategy, IteratorEnabled) Storage; | |
mixin QueueStrategy!(Type, PolicyQueueStrategy); | |
mixin FlushStrategy!(PolicyFlushStrategy); | |
} | |
} | |
/* | |
class PredicateWithBitfieldIndex | |
{ | |
this(string Predicate, uint BitfieldIndex) | |
{ | |
this.Predicate = Predicate; | |
this.BitfieldIndex = BitfieldIndex; | |
} | |
public string Predicate; | |
public uint BitfieldIndex; | |
} | |
*/ | |
template StripsState(uint PredicateBitfieldSize, uint NumbersCount) | |
{ | |
struct StripsState | |
{ | |
public bool[PredicateBitfieldSize] PredicateBitfield; | |
public int[NumbersCount] Numbers; | |
} | |
} | |
template StripsTreeNode(uint PredicateBitfieldSize, uint NumbersCount) | |
{ | |
class StripsTreeNode | |
{ | |
public uint DoneActionIndex; | |
public StripsTreeNode!(PredicateBitfieldSize, NumbersCount) Parent; | |
public StripsState!(PredicateBitfieldSize, NumbersCount) StateAfterAction; | |
public bool NoPossibleActions = false; | |
public uint Depth; | |
public int[] Variables; // the variables for the action | |
public StripsTreeNode!(PredicateBitfieldSize, NumbersCount)[] Childrens; | |
public this(StripsTreeNode!(PredicateBitfieldSize, NumbersCount) Parent, uint Depth) | |
{ | |
this.Parent = Parent; | |
this.Depth = Depth; | |
} | |
} | |
} | |
/** \brief all classes which inherit from this interface implement a Action for the STRIPS solver | |
* | |
*/ | |
template StripsAction(uint PredicateBitfieldSize, uint NumbersCount) | |
{ | |
interface StripsAction | |
{ | |
/** \brief returns true if all Post conditions of this action do apply | |
* | |
* \param PostState is the state after the action would be applied | |
* \return ... | |
*/ | |
public bool doesPostConditionsMatch(StripsState!(PredicateBitfieldSize, NumbersCount) PostState); | |
/** \brief returns if the action needs/calculates variables | |
* | |
* \return ... | |
*/ | |
public bool hasVariable(); | |
/** \brief returns the possible variables for this poststate | |
* | |
* call to this is only valid if this.hasVariable() returns true | |
* | |
* \param PostState ... | |
* \param PossibleVariables | |
*/ | |
public void getVariablesForPostCondition(StripsState!(PredicateBitfieldSize, NumbersCount) PostState, ref int[] PossibleVariables); | |
/** \brief does do the action (in reverse) on the PostState with a variable | |
* | |
* Success is false if the action doesn't have a variable | |
* | |
* \param PostState is the state after the action would be applied | |
* \param Success ... | |
* \return the state after applying the reversed action (the pre condition) with the variable | |
*/ | |
public StripsState!(PredicateBitfieldSize, NumbersCount) doActionReverseWithVariable(StripsState!(PredicateBitfieldSize, NumbersCount) PostState, int Variable, out bool Success); | |
/** \brief does do the action (in reverse) on the PostState | |
* | |
* Success is false if the action does have a variable | |
* | |
* \param PostState is the state after the action would be applied | |
* \param Success ... | |
* \return the state after applying the reversed action (the pre condition) | |
*/ | |
public StripsState!(PredicateBitfieldSize, NumbersCount) doActionReverseWithoutVariable(StripsState!(PredicateBitfieldSize, NumbersCount) PostState, out bool Success); | |
/** \brief ... | |
* | |
* \return ... | |
*/ | |
public string getName(); | |
} | |
} | |
/* | |
class Action1 : StripsAction!(2) | |
{ | |
// allready documentated | |
public bool doesPostConditionsMatch(StripsState!(2) PostState) | |
{ | |
writeln(PostState.PredicateBitfield); | |
return !PostState.PredicateBitfield[0] && PostState.PredicateBitfield[1]; | |
} | |
// allready documentated | |
public StripsState!(2) doActionReverse(StripsState!(2) PostState) | |
{ | |
StripsState!(2) Result; | |
Result.PredicateBitfield[0] = true; | |
Result.PredicateBitfield[1] = false; | |
return Result; | |
} | |
} | |
*/ | |
// Bitfield meaning: | |
// 0 : have bananas | |
// 1 : level high | |
// | |
// Numbers meaning: | |
// 0 : BananasAt | |
// 1 : Location (At) | |
// 2 : BoxAt | |
class ActionTakeBananas : StripsAction!(2, 3) | |
{ | |
public bool doesPostConditionsMatch(StripsState!(2, 3) PostState) | |
{ | |
bool Result; | |
Result = true; | |
Result = Result && PostState.PredicateBitfield[0]; // have bananas | |
Result = Result && (PostState.Numbers[0] == PostState.Numbers[1]); // bananasAt == At | |
Result = Result && PostState.PredicateBitfield[1]; // level is high | |
return Result; | |
} | |
public bool hasVariable() | |
{ | |
return true; | |
} | |
public void getVariablesForPostCondition(StripsState!(2, 3) PostState, ref int[] PossibleVariables) | |
{ | |
PossibleVariables = [PostState.Numbers[0]]; | |
} | |
public StripsState!(2, 3) doActionReverseWithVariable(StripsState!(2, 3) PostState, int Variable, out bool Success) | |
{ | |
int Location; | |
StripsState!(2, 3) Result; | |
Success = true; | |
// TODO< copy operation > | |
Result = PostState; | |
Location = Variable; | |
Result.PredicateBitfield[0] = false; // don't have the bananas before | |
Result.PredicateBitfield[1] = true; // level before is high | |
Result.Numbers[0] = Location; | |
Result.Numbers[1] = Location; | |
return Result; | |
} | |
public StripsState!(2, 3) doActionReverseWithoutVariable(StripsState!(2, 3) PostState, out bool Success) | |
{ | |
Success = false; | |
return PostState; | |
} | |
public string getName() | |
{ | |
return "TakeBananas"; | |
} | |
} | |
class ActionMoveBox : StripsAction!(2, 3) | |
{ | |
public bool doesPostConditionsMatch(StripsState!(2, 3) PostState) | |
{ | |
// BoxAt(Y) != At(Y) and level is low | |
return (PostState.Numbers[1] == PostState.Numbers[2]) && !PostState.PredicateBitfield[1]; | |
} | |
public bool hasVariable() | |
{ | |
return true; | |
} | |
public void getVariablesForPostCondition(StripsState!(2, 3) PostState, ref int[] PossibleVariables) | |
{ | |
// all possible variables are the positions except the old position | |
foreach( i; 0..4 ) | |
{ | |
if( i != PostState.Numbers[1] ) | |
{ | |
PossibleVariables ~= i; | |
} | |
} | |
} | |
public StripsState!(2, 3) doActionReverseWithVariable(StripsState!(2, 3) PostState, int Variable, out bool Success) | |
{ | |
StripsState!(2, 3) Result; | |
int Location; | |
Success = true; | |
Location = Variable; | |
// TODO< copy operation > | |
Result = PostState; | |
Result.Numbers[1] = Location; | |
Result.Numbers[2] = Location; | |
Result.PredicateBitfield[1] = false; // level=low | |
return Result; | |
} | |
public StripsState!(2, 3) doActionReverseWithoutVariable(StripsState!(2, 3) PostState, out bool Success) | |
{ | |
Success = false; | |
return PostState; | |
} | |
public string getName() | |
{ | |
return "MoveBox"; | |
} | |
} | |
class ActionClimbDown : StripsAction!(2, 3) | |
{ | |
public bool doesPostConditionsMatch(StripsState!(2, 3) PostState) | |
{ | |
// return true if level is low and at == boxAt | |
return !PostState.PredicateBitfield[1] && PostState.Numbers[1] == PostState.Numbers[2]; | |
} | |
public bool hasVariable() | |
{ | |
return true; | |
} | |
public void getVariablesForPostCondition(StripsState!(2, 3) PostState, ref int[] PossibleVariables) | |
{ | |
PossibleVariables = [PostState.Numbers[2]]; | |
} | |
public StripsState!(2, 3) doActionReverseWithVariable(StripsState!(2, 3) PostState, int Variable, out bool Success) | |
{ | |
StripsState!(2, 3) Result; | |
int Location; | |
Success = true; | |
Location = Variable; | |
// TODO< copy operation > | |
Result = PostState; | |
// can be optimized away | |
Result.Numbers[1] = Location; | |
Result.Numbers[2] = Location; | |
Result.PredicateBitfield[1] = true; | |
return Result; | |
} | |
public StripsState!(2, 3) doActionReverseWithoutVariable(StripsState!(2, 3) PostState, out bool Success) | |
{ | |
Success = false; | |
return PostState; | |
} | |
public string getName() | |
{ | |
return "ClimbDown"; | |
} | |
} | |
class ActionClimbUp : StripsAction!(2, 3) | |
{ | |
public bool doesPostConditionsMatch(StripsState!(2, 3) PostState) | |
{ | |
// return true if level is high and at == boxAt | |
return PostState.PredicateBitfield[1] && PostState.Numbers[1] == PostState.Numbers[2]; | |
} | |
public bool hasVariable() | |
{ | |
return true; | |
} | |
public void getVariablesForPostCondition(StripsState!(2, 3) PostState, ref int[] PossibleVariables) | |
{ | |
PossibleVariables = [PostState.Numbers[2]]; | |
} | |
public StripsState!(2, 3) doActionReverseWithVariable(StripsState!(2, 3) PostState, int Variable, out bool Success) | |
{ | |
StripsState!(2, 3) Result; | |
int Location; | |
Success = true; | |
Location = Variable; | |
// TODO< copy operation > | |
Result = PostState; | |
// can be optimized away | |
Result.Numbers[1] = Location; | |
Result.Numbers[2] = Location; | |
Result.PredicateBitfield[1] = false; | |
return Result; | |
} | |
public StripsState!(2, 3) doActionReverseWithoutVariable(StripsState!(2, 3) PostState, out bool Success) | |
{ | |
Success = false; | |
return PostState; | |
} | |
public string getName() | |
{ | |
return "ClimbUp"; | |
} | |
} | |
class ActionMove : StripsAction!(2, 3) | |
{ | |
public bool doesPostConditionsMatch(StripsState!(2, 3) PostState) | |
{ | |
// return true if the level is low | |
return !PostState.PredicateBitfield[1]; | |
} | |
public bool hasVariable() | |
{ | |
return true; | |
} | |
public void getVariablesForPostCondition(StripsState!(2, 3) PostState, ref int[] PossibleVariables) | |
{ | |
foreach( PossiblePosition; 0..4 ) | |
{ | |
int Distance = PossiblePosition - PostState.Numbers[1]; | |
if( Distance < 0 ) | |
{ | |
Distance *= -1; | |
} | |
if( Distance != 0 ) | |
{ | |
PossibleVariables ~= PossiblePosition; | |
} | |
} | |
} | |
public StripsState!(2, 3) doActionReverseWithVariable(StripsState!(2, 3) PostState, int Variable, out bool Success) | |
{ | |
StripsState!(2, 3) Result; | |
Success = true; | |
Result = PostState; | |
Result.Numbers[1] = Variable; | |
return Result; | |
} | |
public StripsState!(2, 3) doActionReverseWithoutVariable(StripsState!(2, 3) PostState, out bool Success) | |
{ | |
Success = false; | |
return PostState; | |
} | |
public string getName() | |
{ | |
return "Move"; | |
} | |
} | |
interface InitialChecker(size_t PredicateBitfieldSize, size_t NumbersCount) | |
{ | |
public bool isSimilarToInitialState(StripsState!(PredicateBitfieldSize, NumbersCount) State, StripsState!(PredicateBitfieldSize, NumbersCount) InitialState); | |
} | |
class MyInitialChecker : InitialChecker!(2, 3) | |
{ | |
public bool isSimilarToInitialState(StripsState!(2, 3) State, StripsState!(2, 3) InitialState) | |
{ | |
bool Result; | |
Result = true; | |
Result = Result && (State.Numbers[0] == InitialState.Numbers[0]); | |
Result = Result && (State.Numbers[1] == InitialState.Numbers[1]); | |
Result = Result && (State.Numbers[2] == InitialState.Numbers[2]); | |
Result = Result && (State.PredicateBitfield[0] == InitialState.PredicateBitfield[0]); | |
Result = Result && (State.PredicateBitfield[1] == InitialState.PredicateBitfield[1]); | |
return Result; | |
} | |
} | |
class DoneAction | |
{ | |
this(uint ActionNumber, int[] ActionVariables) | |
{ | |
this.ActionNumber = ActionNumber; | |
this.ActionVariables = ActionVariables; | |
} | |
public uint ActionNumber; | |
public int[] ActionVariables; | |
} | |
import std.algorithm : reverse; | |
import std.stdio : writeln; | |
/* | |
* | |
* the state of the world is composed out of | |
* - predicates | |
* are implemented as a bitfield because it speeds up the algorithm extremly | |
*/ | |
template StripsSolver(size_t PredicateBitfieldSize, size_t NumbersCount) | |
{ | |
class StripsSolver | |
{ | |
public enum EnumResult | |
{ | |
INTERNALERROR, | |
NOSOLUTIONFOUND, | |
OUTOFMEMORY, | |
SOLUTIONFOUND, | |
FAULTYACTION, // a action is malformed | |
SEARCHING | |
} | |
// call example ["Level(high)", "Level(low)"] | |
// this is not used at runtime! | |
/* | |
static final public PredicateWithBitfieldIndex[] compiling_ConvertPredicatesToBifieldIndices(string []Predicates) | |
{ | |
PredicateWithBitfieldIndex[] Result; | |
uint BitfieldIndex; | |
BitfieldIndex = 0; | |
foreach( CurrentPredicate; Predicates ) | |
{ | |
Result ~= new PredicateWithBitfieldIndex(CurrentPredicate, BitfieldIndex); | |
BitfieldIndex++; | |
} | |
return Result; | |
} | |
*/ | |
public this() | |
{ | |
this.TopmostSolverNodes = new TopmostQueueType(); | |
} | |
final public void configure(uint MaxDepth, InitialChecker!(PredicateBitfieldSize, NumbersCount) OfInitialChecker) | |
in | |
{ | |
assert(MaxDepth > 0); | |
assert(OfInitialChecker !is null); | |
} | |
body | |
{ | |
this.MaxDepth = MaxDepth; | |
this.OfInitialChecker = OfInitialChecker; | |
} | |
/** \brief ... | |
* | |
*/ | |
final public void debugTree() | |
{ | |
foreach( CurrentChild; this.SolverTree.Childrens ) | |
{ | |
this.debugTreeRecursive(CurrentChild, 0); | |
} | |
} | |
/** | |
* method | |
*/ | |
final private void debugTreeRecursive(StripsTreeNode!(PredicateBitfieldSize, NumbersCount) Node, uint Depth) | |
{ | |
string Space; | |
foreach( i; 0..Depth ) | |
{ | |
Space ~= " "; | |
} | |
writeln(Space, "Action:", this.Actions[Node.DoneActionIndex].getName()); | |
writeln(Space, "Variables:", Node.Variables); | |
foreach( CurrentChild; Node.Childrens ) | |
{ | |
this.debugTreeRecursive(CurrentChild, Depth + 1); | |
} | |
} | |
/** \brief ... | |
* | |
* \param Actions all actions which the solver can apply | |
*/ | |
final public void setActions(StripsAction!(PredicateBitfieldSize, NumbersCount) []Actions) | |
{ | |
this.Actions = Actions; | |
} | |
final public void setGoalState(StripsState!(PredicateBitfieldSize, NumbersCount) GoalState) | |
{ | |
this.GoalState = GoalState; | |
this.GoalStateValid = true; | |
} | |
final public void setInitalState(StripsState!(PredicateBitfieldSize, NumbersCount) InitialState) | |
{ | |
this.InitialState = InitialState; | |
this.InitialStateValid = true; | |
} | |
final public void restart(out bool Success) | |
{ | |
bool CalleeSuccess; | |
Success = false; | |
if( !this.InitialStateValid ) | |
{ | |
return; | |
} | |
this.FoundSolution = false; | |
this.SolutionActionSequence.length = 0; | |
this.SolverTree = new TreeNodeType(null, 0); | |
this.TopmostSolverNodes.flush(); | |
this.TopmostSolverNodes.enqueue(this.SolverTree, CalleeSuccess); | |
if( !CalleeSuccess ) | |
{ | |
return; | |
} | |
// TODO< generic equal operation > | |
this.SolverTree.StateAfterAction = this.GoalState; | |
this.SolverTree.Depth = 0; | |
Success = true; | |
} | |
/** \brief returns the sequence of the actions for the solution | |
* | |
* does assert if no solution was found and this is non the less called | |
* | |
* \return ... | |
*/ | |
final public DoneAction[] getSolution() | |
{ | |
assert(this.FoundSolution); | |
return this.SolutionActionSequence; | |
} | |
// TODO< remember the taken actions and the PredicateBitfield in a tree > | |
final public void think(out bool Success, out EnumResult Result, uint Iterations = uint.max) | |
{ | |
Success = false; | |
assert( this.SolverTree !is null ); | |
if( !this.InitialStateValid ) | |
{ | |
return; | |
} | |
if( !this.GoalStateValid ) | |
{ | |
return; | |
} | |
foreach( CurrentIteration; 0..Iterations ) | |
{ | |
this.doOneIteration(Result); | |
if( Result == EnumResult.SOLUTIONFOUND ) | |
{ | |
break; | |
} | |
else if( Result == EnumResult.SEARCHING ) | |
{ | |
writeln("seaching..."); | |
} | |
else | |
{ | |
writeln("errorous"); | |
return; | |
} | |
} | |
Success = true; | |
} | |
/** \brief ... | |
* | |
*/ | |
final private void doOneIteration(out EnumResult Result) | |
{ | |
bool CalleeSuccess; | |
if( this.doesntExistAnySolution() ) | |
{ | |
Result = EnumResult.NOSOLUTIONFOUND; | |
return; | |
} | |
if( this.isDepthOfAllPossibleSolutionsGreaterThan(this.MaxDepth) ) | |
{ | |
Result = EnumResult.NOSOLUTIONFOUND; | |
return; | |
} | |
// get the next topmost Node we have to check | |
StripsTreeNode!(PredicateBitfieldSize, NumbersCount) CurrentNode; | |
CurrentNode = this.TopmostSolverNodes.dequeue(CalleeSuccess); | |
if( !CalleeSuccess ) | |
{ | |
Result = EnumResult.INTERNALERROR; | |
return; | |
} | |
// search for all possible actions, apply them and store the childs into the tree | |
// and append it to the TopmostSolverNodes | |
bool WasAnyActionApplied; | |
WasAnyActionApplied = false; | |
// TODO< if this is a memory block move this outside as a member > | |
// NOTE< this is outside because it will be maybe much more faster if this is a not GC'ed array > | |
// containts all possible variables for the matching of the post condition | |
int[] PossibleVariables; | |
foreach( ActionIndex; 0..this.Actions.length ) | |
{ | |
StripsAction!(PredicateBitfieldSize, NumbersCount) CurrentAction; | |
CurrentAction = this.Actions[ActionIndex]; | |
if( CurrentAction.doesPostConditionsMatch(CurrentNode.StateAfterAction) ) | |
{ | |
StripsState!(PredicateBitfieldSize, NumbersCount) StateAfterAction; | |
StripsTreeNode!(PredicateBitfieldSize, NumbersCount) NodeForPreAction; | |
bool IsSimilarToInitialState; | |
if( CurrentAction.hasVariable() ) | |
{ | |
PossibleVariables.length = 0; | |
// saves data into PossibleVariables | |
CurrentAction.getVariablesForPostCondition(CurrentNode.StateAfterAction, PossibleVariables); | |
} | |
WasAnyActionApplied = true; | |
if( CurrentAction.hasVariable() ) | |
{ | |
foreach( CurrentVariable; PossibleVariables ) | |
{ | |
CalleeSuccess = false; | |
StateAfterAction = CurrentAction.doActionReverseWithVariable(CurrentNode.StateAfterAction, CurrentVariable, CalleeSuccess); | |
if( !CalleeSuccess ) | |
{ | |
Result = EnumResult.FAULTYACTION; | |
return; | |
} | |
NodeForPreAction = new StripsTreeNode!(PredicateBitfieldSize, NumbersCount)(CurrentNode, CurrentNode.Depth + 1); | |
NodeForPreAction.DoneActionIndex = ActionIndex; | |
// TODO< equal operation > | |
NodeForPreAction.StateAfterAction = StateAfterAction; | |
NodeForPreAction.Variables = [CurrentVariable]; | |
// add to tree | |
CurrentNode.Childrens ~= NodeForPreAction; | |
IsSimilarToInitialState = this.OfInitialChecker.isSimilarToInitialState(StateAfterAction, this.InitialState); | |
if( IsSimilarToInitialState ) | |
{ | |
this.setSolution(NodeForPreAction); | |
Result = EnumResult.SOLUTIONFOUND; | |
return; | |
} | |
this.TopmostSolverNodes.enqueue(NodeForPreAction, CalleeSuccess); | |
if( !CalleeSuccess ) | |
{ | |
Result = EnumResult.OUTOFMEMORY; | |
return; | |
} | |
} | |
} | |
else | |
{ | |
StateAfterAction = CurrentAction.doActionReverseWithoutVariable(CurrentNode.StateAfterAction, CalleeSuccess); | |
if( !CalleeSuccess ) | |
{ | |
Result = EnumResult.FAULTYACTION; | |
return; | |
} | |
NodeForPreAction = new StripsTreeNode!(PredicateBitfieldSize, NumbersCount)(CurrentNode, CurrentNode.Depth + 1); | |
NodeForPreAction.DoneActionIndex = ActionIndex; | |
// TODO< equal operation > | |
NodeForPreAction.StateAfterAction = StateAfterAction; | |
// add to tree | |
CurrentNode.Childrens ~= NodeForPreAction; | |
IsSimilarToInitialState = this.OfInitialChecker.isSimilarToInitialState(StateAfterAction, this.InitialState); | |
if( IsSimilarToInitialState ) | |
{ | |
this.setSolution(NodeForPreAction); | |
Result = EnumResult.SOLUTIONFOUND; | |
return; | |
} | |
this.TopmostSolverNodes.enqueue(NodeForPreAction, CalleeSuccess); | |
if( !CalleeSuccess ) | |
{ | |
Result = EnumResult.OUTOFMEMORY; | |
return; | |
} | |
} | |
} | |
} | |
if( !WasAnyActionApplied ) | |
{ | |
//writeln("no action was applied"); | |
CurrentNode.NoPossibleActions = true; | |
} | |
Result = EnumResult.SEARCHING; | |
} | |
/** \brief checks if no possible solution exists | |
* | |
* \return ... | |
*/ | |
final private bool doesntExistAnySolution() | |
{ | |
return this.TopmostSolverNodes.isEmpty(); | |
} | |
/** \brief checks if the maximal depth of each node is greater than the max depth | |
* | |
* \param MaxDepth ... | |
* \return ... | |
*/ | |
final private bool isDepthOfAllPossibleSolutionsGreaterThan(uint MaxDepth) | |
{ | |
foreach( CurrentNode; this.TopmostSolverNodes.getIterator() ) | |
{ | |
if( CurrentNode.Depth < MaxDepth ) | |
{ | |
return false; | |
} | |
} | |
return true; | |
} | |
/** \brief sets the solution to the (reversed) action sequence to this node | |
* | |
* \param Node ... | |
*/ | |
final private void setSolution(TreeNodeType Node) | |
{ | |
DoneAction[] ActionSequence; | |
ActionSequence = this.getReversedActionSequenceFromNode(Node); | |
reverse(ActionSequence); | |
this.FoundSolution = true; | |
this.SolutionActionSequence = ActionSequence; | |
} | |
final private DoneAction[] getReversedActionSequenceFromNode(TreeNodeType Node) | |
{ | |
TreeNodeType IterationNode; | |
DoneAction[] ActionSequence; | |
IterationNode = Node; | |
for(;;) | |
{ | |
uint VariablesCount; | |
assert(IterationNode !is null); | |
if( Node is this.SolverTree ) | |
{ | |
break; | |
} | |
// NOTE< we need to rewrite the api that it returns the number of parameters > | |
VariablesCount = 0; | |
if( this.Actions[Node.DoneActionIndex].hasVariable() ) | |
{ | |
VariablesCount = 1; | |
} | |
ActionSequence ~= new DoneAction(Node.DoneActionIndex, Node.Variables); | |
Node = Node.Parent; | |
} | |
return ActionSequence; | |
} | |
// Type aliases | |
private alias StripsTreeNode!(PredicateBitfieldSize, NumbersCount) TreeNodeType; | |
private alias Queue!(TreeNodeType, EnqueueDequeuePolicy, DynamicArrayStoreStrategy, true, FlushableStrategy) TopmostQueueType; | |
private StripsAction!(PredicateBitfieldSize, NumbersCount)[] Actions; | |
private StripsState!(PredicateBitfieldSize, NumbersCount) GoalState; | |
private bool GoalStateValid = false; | |
private StripsState!(PredicateBitfieldSize, NumbersCount) InitialState; | |
private bool InitialStateValid = false; | |
private TreeNodeType SolverTree; | |
private TopmostQueueType TopmostSolverNodes; | |
private uint MaxDepth; | |
private InitialChecker!(PredicateBitfieldSize, NumbersCount) OfInitialChecker; | |
private bool FoundSolution = false; | |
private DoneAction[] SolutionActionSequence; | |
} | |
} | |
int main() { | |
StripsSolver!(2, 3) OfSolver; | |
StripsState!(2, 3) GoalState; | |
StripsState!(2, 3) InitialState; | |
bool CalleeSuccess; | |
OfSolver = new StripsSolver!(2, 3)(); | |
StripsAction!(2, 3)[] Actions; | |
Actions ~= new ActionMove(); | |
Actions ~= new ActionClimbDown(); | |
Actions ~= new ActionClimbUp(); | |
Actions ~= new ActionMoveBox(); | |
Actions ~= new ActionTakeBananas(); | |
OfSolver.setActions(Actions); | |
GoalState.PredicateBitfield = [true, false]; // wished final state | |
InitialState.PredicateBitfield = [false, false]; // state now | |
GoalState.Numbers = [2, 2, 2]; | |
InitialState.Numbers = [2, 0, 1]; | |
OfSolver.setGoalState(GoalState); | |
OfSolver.setInitalState(InitialState); | |
OfSolver.configure(9, new MyInitialChecker()); | |
OfSolver.restart(CalleeSuccess); | |
if( !CalleeSuccess ) | |
{ | |
// TODO | |
return 0; | |
} | |
StripsSolver!(2, 3).EnumResult OfResult; | |
OfSolver.think(CalleeSuccess, OfResult); | |
//OfSolver.debugTree(); | |
if( OfResult == StripsSolver!(2, 3).EnumResult.SOLUTIONFOUND ) | |
{ | |
writeln("found solution"); | |
DoneAction[] ActionSequence = OfSolver.getSolution(); | |
foreach( Action; ActionSequence ) | |
{ | |
writeln("Action:", Actions[Action.ActionNumber].getName()); | |
writeln("Variables:", Action.ActionVariables); | |
writeln("---"); | |
} | |
} | |
else | |
{ | |
writeln("found no solution"); | |
} | |
DoneAction[] SolutionSequence; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment