Writing your own Planner

The objective of this tutorial is to design a simple planner based on A* search.

  1. Create a simple Java project with PDDL4J

  2. Create the main class of your planner

  3. Get the planner arguments from the command line

  4. Searching for a solution plan

  5. Write your own A* search strategy

  6. Make your planner configurable by programming

Pre-requisite Installations

For this tutorial you need:

In the following, we will give the commands line so that the tutorial can be done independently of any IDE.

Step 1. Create a simple Java project with PDDL4J

First, open a terminal and create your development directory ASP (A* Planner)

mkdir ASP

Then, create the sub-directories of your project

cd ASP
mkdir -p src/fr/uga/pddl4j/examples/asp
mkdir classes
mkdir lib

Finally, get the last binary of PDDL4J and save it in the lib directory

wget http://pddl4j.imag.fr/repository/pddl4j/binaries/pddl4j-4.0.0.jar
mv pddl4j-4.0.0.jar lib/pddl4j-4.0.0.jar

You are now ready to write your own A* planner.

Step 2. Create the main class of our planner

Create and edit a file called ASP.java in the directory src/fr/uga/pddl4j/examples/asp. The skeleton of this class is given bellow:

 1package fr.uga.pddl4j.examples.asp;
 2
 3import fr.uga.pddl4j.heuristics.state.StateHeuristic;
 4import fr.uga.pddl4j.parser.DefaultParsedProblem;
 5import fr.uga.pddl4j.parser.RequireKey;
 6import fr.uga.pddl4j.plan.Plan;
 7import fr.uga.pddl4j.plan.SequentialPlan;
 8import fr.uga.pddl4j.planners.AbstractPlanner;
 9import fr.uga.pddl4j.planners.Planner;
10import fr.uga.pddl4j.planners.PlannerConfiguration;
11import fr.uga.pddl4j.planners.ProblemNotSupportedException;
12import fr.uga.pddl4j.planners.SearchStrategy;
13import fr.uga.pddl4j.planners.statespace.search.StateSpaceSearch;
14import fr.uga.pddl4j.problem.DefaultProblem;
15import fr.uga.pddl4j.problem.Problem;
16import fr.uga.pddl4j.problem.State;
17import fr.uga.pddl4j.problem.operator.Action;
18import fr.uga.pddl4j.problem.operator.ConditionalEffect;
19import org.apache.logging.log4j.LogManager;
20import org.apache.logging.log4j.Logger;
21import picocli.CommandLine;
22
23import java.util.Comparator;
24import java.util.HashSet;
25import java.util.List;
26import java.util.PriorityQueue;
27import java.util.Set;
28
29/**
30 * The class is an example. It shows how to create a simple A* search planner able to
31 * solve an ADL problem by choosing the heuristic to used and its weight.
32 *
33 * @author D. Pellier
34 * @version 4.0 - 30.11.2021
35 */
36@CommandLine.Command(name = "ASP",
37    version = "ASP 1.0",
38    description = "Solves a specified planning problem using A* search strategy.",
39    sortOptions = false,
40    mixinStandardHelpOptions = true,
41    headerHeading = "Usage:%n",
42    synopsisHeading = "%n",
43    descriptionHeading = "%nDescription:%n%n",
44    parameterListHeading = "%nParameters:%n",
45    optionListHeading = "%nOptions:%n")
46public class ASP extends AbstractPlanner {
47
48    /**
49     * The class logger.
50     */
51    private static final Logger LOGGER = LogManager.getLogger(ASP.class.getName());
52
53    /**
54     * Instantiates the planning problem from a parsed problem.
55     *
56     * @param problem the problem to instantiate.
57     * @return the instantiated planning problem or null if the problem cannot be instantiated.
58     */
59    @Override
60    public Problem instantiate(DefaultParsedProblem problem) {
61        final Problem pb = new DefaultProblem(problem);
62        pb.instantiate();
63        return pb;
64    }
65
66    /**
67     * Search a solution plan to a specified domain and problem using A*.
68     *
69     * @param problem the problem to solve.
70     * @return the plan found or null if no plan was found.
71     */
72    @Override
73    public Plan solve(final Problem problem) {
74    }
75
76    /**
77     * The main method of the <code>ASP</code> planner.
78     *
79     * @param args the arguments of the command line.
80     */
81    public static void main(String[] args) {
82        try {
83            final ASP planner = new ASP();
84            CommandLine cmd = new CommandLine(planner);
85            cmd.execute(args);
86        } catch (IllegalArgumentException e) {
87            LOGGER.fatal(e.getMessage());
88        }
89    }
90}

The class ASP extends the abstract class AbstractPlanner that contains the basic methods of any planners.

Two methods must be overridden at least:
  • The method instantiate(DefaultParsedProblem problem) is an abstract method of the class AbstractPlanner. This method takes as parameter an instance of parsed problem and return the corresponding instantiated or grounding problem. The problem returned contains all the information related to the problem, i.e., the actions, the initial state, the goal of the problem, etc.

  • The method solve(Problem problem) is the main method of the planner. The method takes as parameter the instantiated problem returned by the previous method overridden and returns a plan solution of null if no plan was found.

Note

The given skeleton contains also a main() method to launch the planner from the command line. We will return to this method in the next section.

Note

Every planner can have a logger instance. This logger is based on Log4j library developed by Apache and specialized in logging. A great benefit of Log4j is that different levels of logging can be set for your planner. The levels are hierarchical and are as follows: TRACE, DEBUG, INFO, WARN, ERROR, and FATAL. Have a look to the web site of Log4j for more details. The declaration of the logger is done line 38. To see an example use of the logger see line 71 of the main() method.

Step 3. Get the planner arguments from the command line

Before writing the search algorithm let’s first look at how to get the arguments from the command line. Your planner takes as inputs at least a domain file that contains the description of the planning operators and a problem file that define the initial state and the goal to reach. Both files domain and problem rely on PDDL (Planning Domain Description Language). For those who are not familiar with PDDL, first have a look to the tutorial PDDL Tutorial. To deal with complex command line arguments, PDDL4J used picocli library. Picocli allow to create rich command line applications just by adding annotation on the main class of our planner.

Step 3.1. Activate the default command line arguments

By default, the class AbstractPlanner already handles the common arguments of all planners: the domain and the problem description, the time allocated to the search and the log level. The domain and the problem descriptions are mandatory parameters. The log level and the time allocated to search are optional.

To activate the default command line for your planner you have just to add the following annotation before the class declaration:

 1/**
 2 * The class is an example. It shows how to create a simple A* search planner able to
 3 * solve an ADL problem by choosing the heuristic to used and its weight.
 4 *
 5 * @author D. Pellier
 6 * @version 4.0 - 30.11.2021
 7 */
 8@CommandLine.Command(name = "ASP",
 9    version = "ASP 1.0",
10    description = "Solves a specified planning problem using A* search strategy.",
11    sortOptions = false,
12    mixinStandardHelpOptions = true,
13    headerHeading = "Usage:%n",
14    synopsisHeading = "%n",
15    descriptionHeading = "%nDescription:%n%n",
16    parameterListHeading = "%nParameters:%n",
17    optionListHeading = "%nOptions:%n")

and complete the main() method with the code below:

 1    /**
 2     * The main method of the <code>ASP</code> planner.
 3     *
 4     * @param args the arguments of the command line.
 5     */
 6    public static void main(String[] args) {
 7        try {
 8            final ASP planner = new ASP();
 9            CommandLine cmd = new CommandLine(planner);
10            cmd.execute(args);
11        } catch (IllegalArgumentException e) {
12            LOGGER.fatal(e.getMessage());
13        }
14    }

To test, first compile your planner:

javac -d classes -cp lib/pddl4j-4.0.0.jar src/fr/uga/pddl4j/examples/asp/ASP.java

and run it with the command line:

java -cp classes:lib/pddl4j-4.0.0.jar fr.uga.pddl4j.examples.asp.ASP --help

You will obtain the following message:

ASP [-hiV] [-l=<logLevel>] [-t=<timeout>] <domain> <problem>

Description:

Solves a specified planning problem using a A* search strategy.

Parameters:
    <domain>              The domain file.
    <problem>             The problem file.

Options:
    -t, --timeout=<timeout>   Set the time out of the planner in seconds (preset 600s).
    -l, --log=<logLevel>      Set the level of trace of the planner: ALL, DEBUG,
                                INFO, ERROR, FATAL, OFF, TRACE (preset INFO).
    -h, --help                Show this help message and exit.
    -V, --version             Print version information and exit.

Step 3.2 Adding new command line arguments

Now, we have to add the specific arguments of our planner to allow to choose the heuristic function to used and set the weight of the heuristic. This step is relatively simple and straightforward. We just need to declare two new attributes: one for the heuristic and one for its weight. The heuristic is of type GoalCostHeuristic.Name and the weight of type double. Note that the weight must be greater than 0.

1    /**
2     * The weight of the heuristic.
3     */
4    private double heuristicWeight;
5
6    /**
7     * The name of the heuristic used by the planner.
8     */
9    private StateHeuristic.Name heuristic;

To complete, we also add the corresponding getters and setters:

 1    /**
 2     * Sets the weight of the heuristic.
 3     *
 4     * @param weight the weight of the heuristic. The weight must be greater than 0.
 5     * @throws IllegalArgumentException if the weight is strictly less than 0.
 6     */
 7    @CommandLine.Option(names = {"-w", "--weight"}, defaultValue = "1.0",
 8        paramLabel = "<weight>", description = "Set the weight of the heuristic (preset 1.0).")
 9    public void setHeuristicWeight(final double weight) {
10        if (weight <= 0) {
11            throw new IllegalArgumentException("Weight <= 0");
12        }
13        this.heuristicWeight = weight;
14    }
15
16    /**
17     * Set the name of heuristic used by the planner to the solve a planning problem.
18     *
19     * @param heuristic the name of the heuristic.
20     */
21    @CommandLine.Option(names = {"-e", "--heuristic"}, defaultValue = "FAST_FORWARD",
22        description = "Set the heuristic : AJUSTED_SUM, AJUSTED_SUM2, AJUSTED_SUM2M, COMBO, "
23            + "MAX, FAST_FORWARD SET_LEVEL, SUM, SUM_MUTEX (preset: FAST_FORWARD)")
24    public void setHeuristic(StateHeuristic.Name heuristic) {
25        this.heuristic = heuristic;
26    }
27
28    /**
29     * Returns the name of the heuristic used by the planner to solve a planning problem.
30     *
31     * @return the name of the heuristic used by the planner to solve a planning problem.
32     */
33    public final StateHeuristic.Name getHeuristic() {
34        return this.heuristic;
35    }
36
37    /**
38     * Returns the weight of the heuristic.
39     *
40     * @return the weight of the heuristic.
41     */
42    public final double getHeuristicWeight() {
43        return this.heuristicWeight;
44    }

To test, your complete command line compile once again your planner:

javac -d classes -cp lib/pddl4j-4.0.0.jar src/fr/uga/pddl4j/examples/asp/ASP.java

and run it with for instance the command line:

java -cp classes:lib/pddl4j-4.0.0.jar fr.uga.pddl4j.examples.ASP
     src/test/resources/benchmarks/pddl/ipc2002/depots/strips-automatic/domain.pddl
     src/test/resources/benchmarks/pddl/ipc2002/depots/strips-automatic/p01.pddl
     -e FAST_FORWARD
     -w 1.2
     -t 1000

Now the command line is set. The final command line of your planner is as follows:

ASP [-hV] [-e=<heuristic>] [-l=<logLevel>] [-t=<timeout>] [-w=<weight>]
    <domain> <problem>

Description:

Solves a specified planning problem using A* search strategy.

Parameters:
    <domain>              The domain file.
    <problem>             The problem file.

Options:
    -t, --timeout=<timeout>   Set the time out of the planner in seconds (preset
                                600s).
    -l, --log=<logLevel>      Set the level of trace of the planner: ALL, DEBUG,
                                INFO, ERROR, FATAL, OFF, TRACE (preset INFO).
    -w, --weight=<weight>     Set the weight of the heuristic (preset 1.0).
    -e, --heuristic=<heuristic>
                              Set the heuristic : AJUSTED_SUM, AJUSTED_SUM2,
                                AJUSTED_SUM2M, COMBO, MAX, FAST_FORWARD,
                                SET_LEVEL, SUM, SUM_MUTEX (preset: FAST_FORWARD)
    -h, --help                Show this help message and exit.
    -V, --version             Print version information and exit.

Step 4. Searching for a solution plan

You finally think we’re here. How write my search procedure ? Two possibilities or the search procedure you want to use already exists in PDDL4J. In this case, it’s extremely simple just call the right procedure in the solve() method. Otherwise, you have to write your own procedure. Let us first consider the first case. The second will consider in last part of this tutorial.

All the search strategies for state space planning already implemented in PDDL4J are available in the package fr.uga.pddl4j.planners.statespace.search.strategy search strategies. Thus, your solve() must look like as follows:

 1    /**
 2     * Search a solution plan to a specified domain and problem using A*.
 3     *
 4     * @param problem the problem to solve.
 5     * @return the plan found or null if no plan was found.
 6     */
 7    @Override
 8    public Plan solve(final Problem problem) {
 9        // Creates the A* search strategy
10        StateSpaceSearch search = StateSpaceSearch.getInstance(SearchStrategy.Name.ASTAR,
11            this.getHeuristic(), this.getHeuristicWeight(), this.getTimeout());
12        LOGGER.info("* Starting A* search \n");
13        // Search a solution
14        Plan plan = search.searchPlan(problem);
15        // If a plan is found update the statistics of the planner and log search information
16        if (plan != null) {
17            LOGGER.info("* A* search succeeded\n");
18            this.getStatistics().setTimeToSearch(search.getSearchingTime());
19            this.getStatistics().setMemoryUsedToSearch(search.getMemoryUsed());
20        } else {
21            LOGGER.info("* A* search failed\n");
22        }
23        // Return the plan found or null if the search fails.
24        return plan;
25    }

First, we create an instance of the search strategy for the problem to solve and then, we try to find a plan for this problem.

Note

If you need to get the goal node for printing for instance returned by the search strategy you can replace the call to searchPlan() line 14 by:

final Node goal = search.searchSolutionNode(problem);
Planner.getLogger().trace(problem.toString(goal));
Plan plan = search.extractPlan(goal, problem);

Now, your planner is ready to solve problems. After compiling the project run your planner with the command line to make a test:

java -cp classes:lib/pddl4j-4.0.0.jar fr.uga.pddl4j.examples.ASP
     src/test/resources/benchmarks/pddl/ipc2002/depots/strips-automatic/domain.pddl
     src/test/resources/benchmarks/pddl/ipc2002/depots/strips-automatic/p01.pddl
     -e FAST_FORWARD
     -w 1.2
     -t 1000

The output should be:

parsing domain file "domain.pddl" done successfully
parsing problem file "p01.pddl" done successfully

problem instantiation done successfully (90 actions, 46 fluents)

* Starting A* search
* A* search succeeded

found plan as follows:

00: (       lift hoist0 crate1 pallet0 depot0) [0]
01: ( lift hoist1 crate0 pallet1 distributor0) [0]
02: (        load hoist0 crate1 truck1 depot0) [0]
03: (        drive truck1 depot0 distributor0) [0]
04: (  load hoist1 crate0 truck1 distributor0) [0]
05: (unload hoist1 crate1 truck1 distributor0) [0]
06: (  drive truck1 distributor0 distributor1) [0]
07: ( drop hoist1 crate1 pallet1 distributor0) [0]
08: (unload hoist2 crate0 truck1 distributor1) [0]
09: ( drop hoist2 crate0 pallet2 distributor1) [0]

time spent:       0,02 seconds parsing
                  0,03 seconds encoding
                  0,01 seconds searching
                  0,07 seconds total time

memory used:      0,00 MBytes for problem representation
                  0,00 MBytes for searching
                  0,00 MBytes total

Step 5. Write your own A* search strategy

Before writing your own A* search strategy, you need to create a class Node. This class represents a node of search tree developed by A*.

Step 5.1 Writing your own class Node

For state space planning a Node is a data structure with 5 components:
  1. A state, i.e., the state in the state space to which the node corresponds;

  2. A parent node, i.e., the node in the search tree that generated this node;

  3. An action, i.e., the action that was applied to the parent node to produce this node;

  4. A cost, i.e., the cost of the path from the initial state to the node, as indicated by the parent pointer; and

  5. A heuristics value, i.e., a estimation of the cost from this node to a solution one.

The easiest way to write your own node class is to inherit the State class that models a state in a compact way. To do so, start creating a file Node.java in the repertory src/fr/uga/pddl4j/examples/asp/ and copy and paste the the skeleton of the class Node is given below:

  1package fr.uga.pddl4j.examples.asp;
  2
  3import fr.uga.pddl4j.problem.State;
  4
  5/**
  6 * This class implements a node of the tree search.
  7 *
  8 * @author D. Pellier
  9 * @version 1.0 - 02.12.2021
 10 */
 11public final class Node extends State {
 12
 13    /**
 14     * The parent node of this node.
 15     */
 16    private Node parent;
 17
 18    /**
 19     * The action apply to reach this node.
 20     */
 21    private int action;
 22
 23    /**
 24     * The cost to reach this node from the root node.
 25     */
 26    private double cost;
 27
 28    /**
 29     * The estimated distance to the goal from this node.
 30     */
 31    private double heuristic;
 32
 33    /**
 34     * The depth of the node.
 35     */
 36    private int depth;
 37
 38    /**
 39     * Creates a new node from a specified state.
 40     *
 41     * @param state the state.
 42     */
 43    public Node(State state) {
 44        super(state);
 45    }
 46
 47    /**
 48     * Creates a new node with a specified state, parent node, operator,
 49     * cost and heuristic value.
 50     *
 51     * @param state     the logical state of the node.
 52     * @param parent    the parent node of the node.
 53     * @param action   the action applied to reached the node from its parent.
 54     * @param cost      the cost to reach the node from the root node.
 55     * @param heuristic the estimated distance to reach the goal from the node.
 56     */
 57    public Node(State state, Node parent, int action, double cost, double heuristic) {
 58        super(state);
 59        this.parent = parent;
 60        this.action = action;
 61        this.cost = cost;
 62        this.heuristic = heuristic;
 63        this.depth = -1;
 64    }
 65
 66    /**
 67     * Creates a new node with a specified state, parent node, operator, cost,
 68     * depth and heuristic value.
 69     *
 70     * @param state     the logical state of the node.
 71     * @param parent    the parent node of the node.
 72     * @param action    the action applied to reached the node from its parent.
 73     * @param cost      the cost to reach the node from the root node.
 74     * @param depth     the depth of the node.
 75     * @param heuristic the estimated distance to reach the goal from the node.
 76     */
 77    public Node(State state, Node parent, int action, double cost, int depth, double heuristic) {
 78        super(state);
 79        this.parent = parent;
 80        this.action = action;
 81        this.cost = cost;
 82        this.depth = depth;
 83        this.heuristic = heuristic;
 84    }
 85
 86    /**
 87     * Returns the action applied to reach the node.
 88     *
 89     * @return the action applied to reach the node.
 90     */
 91    public final int getAction() {
 92        return this.action;
 93    }
 94
 95    /**
 96     * Sets the action applied to reach the node.
 97     *
 98     * @param action the action to set.
 99     */
100    public final void setAction(final int action) {
101        this.action = action;
102    }
103
104    /**
105     * Returns the parent node of the node.
106     *
107     * @return the parent node.
108     */
109    public final Node getParent() {
110        return parent;
111    }
112
113    /**
114     * Sets the parent node of the node.
115     *
116     * @param parent the parent to set.
117     */
118    public final void setParent(Node parent) {
119        this.parent = parent;
120    }
121
122    /**
123     * Returns the cost to reach the node from the root node.
124     *
125     * @return the cost to reach the node from the root node.
126     */
127    public final double getCost() {
128        return cost;
129    }
130
131    /**
132     * Sets the cost needed to reach the node from the root node.
133     *
134     * @param cost the cost needed to reach the node from the root nod to set.
135     */
136    public final void setCost(double cost) {
137        this.cost = cost;
138    }
139
140    /**
141     * Returns the estimated distance to the goal from the node.
142     *
143     * @return the estimated distance to the goal from the node.
144     */
145    public final double getHeuristic() {
146        return heuristic;
147    }
148
149    /**
150     * Sets the estimated distance to the goal from the node.
151     *
152     * @param estimates the estimated distance to the goal from the node to set.
153     */
154    public final void setHeuristic(double estimates) {
155        this.heuristic = estimates;
156    }
157
158    /**
159     * Returns the depth of this node.
160     *
161     * @return the depth of this node.
162     */
163    public int getDepth() {
164        return this.depth;
165    }
166
167    /**
168     * Set the depth of this node.
169     *
170     * @param depth the depth of this node.
171     */
172    public void setDepth(final int depth) {
173        this.depth = depth;
174    }
175
176    /**
177     * Returns the value of the heuristic function, i.e.,
178     * <code>this.node.getCost() + this.node.getHeuristic()</code>.
179     *
180     * @param weight the weight of the heuristic.
181     * @return the value of the heuristic function, i.e.,
182     * <code>this.node.getCost() + this.node.getHeuristic()</code>.
183     */
184    public final double getValueF(double weight) {
185        return weight * this.heuristic + this.cost;
186    }
187
188}

Step 6. Make your planner configurable by programming

By default, your planner is configurable by programming (see MISSING REF for more details) because it inherits the class AbstractPlanner. But only for the common configurable properties of all planners, i.e., the domain, the problem, the timeout and the log level.

Note

The common configurable properties and their values are defined in the interface Planner.

In order to allow the new properties of your planner to be configurable by programming, you have to :
  1. declare for each new property a name and a default value

  2. redefined the setter and the getter method to set and get the configuration of your planner

  3. redefined a method getDefaultConfiguration()

  4. redefined the method hasValidConfiguration()

  5. redefined the constructors of your planner and deal with the class PlannerConfiguration

Step 6.1 Declaration of new properties

In the case of your planner, you have two new properties that you want configure: the heuristic and the weight the weight associated with it. The following code:

 1    /**
 2     * The HEURISTIC property used for planner configuration.
 3     */
 4    public static final String HEURISTIC_SETTING = "HEURISTIC";
 5
 6    /**
 7     * The default value of the HEURISTIC property used for planner configuration.
 8     */
 9    public static final StateHeuristic.Name DEFAULT_HEURISTIC = StateHeuristic.Name.FAST_FORWARD;
10
11    /**
12     * The WEIGHT_HEURISTIC property used for planner configuration.
13     */
14    public static final String WEIGHT_HEURISTIC_SETTING = "WEIGHT_HEURISTIC";
15
16    /**
17     * The default value of the WEIGHT_HEURISTIC property used for planner configuration.
18     */
19    public static final double DEFAULT_WEIGHT_HEURISTIC = 1.0;

Step 6.2 Setting and getting the configuration of your planner

To deal with the two properties and make your planner configurable by programming, it is necessary to redefined the setter and the getter of the class AbstractPlanner. This can be done in your case using the code below:

 1    /**
 2     * Returns the configuration of the planner.
 3     *
 4     * @return the configuration of the planner.
 5     */
 6    @Override
 7    public PlannerConfiguration getConfiguration() {
 8        final PlannerConfiguration config = super.getConfiguration();
 9        config.setProperty(ASP.HEURISTIC_SETTING, this.getHeuristic().toString());
10        config.setProperty(ASP.WEIGHT_HEURISTIC_SETTING, Double.toString(this.getHeuristicWeight()));
11        return config;
12    }
13
14    /**
15     * Sets the configuration of the planner. If a planner setting is not defined in
16     * the specified configuration, the setting is initialized with its default value.
17     *
18     * @param configuration the configuration to set.
19     */
20    @Override
21    public void setConfiguration(final PlannerConfiguration configuration) {
22        super.setConfiguration(configuration);
23        if (configuration.getProperty(ASP.WEIGHT_HEURISTIC_SETTING) == null) {
24            this.setHeuristicWeight(ASP.DEFAULT_WEIGHT_HEURISTIC);
25        } else {
26            this.setHeuristicWeight(Double.parseDouble(configuration.getProperty(
27                ASP.WEIGHT_HEURISTIC_SETTING)));
28        }
29        if (configuration.getProperty(ASP.HEURISTIC_SETTING) == null) {
30            this.setHeuristic(ASP.DEFAULT_HEURISTIC);
31        } else {
32            this.setHeuristic(StateHeuristic.Name.valueOf(configuration.getProperty(
33                ASP.HEURISTIC_SETTING)));
34        }
35    }

The code is quite simple. It call the method getConfigration() and setConfiguration() from the parent class AbstractPlanner. and set of get the new properties to an instance of the class PlannerConfiguration.

Step 6.3 Defining the default configuration of your planner

By convention all planner have a static method which returns the default configuration of a planner. In your case, the method getDefaultConfiguration() calls the eponymous method from the parent class AbstractPlanner. Then, in the same way as the previous method getConfiguration() it creates an instance of the class PlannerConfiguration with the default values.

 1     *
 2     * @return the default arguments of the planner.
 3     * @see PlannerConfiguration
 4     */
 5    public static PlannerConfiguration getDefaultConfiguration() {
 6        PlannerConfiguration config = Planner.getDefaultConfiguration();
 7        config.setProperty(ASP.HEURISTIC_SETTING, ASP.DEFAULT_HEURISTIC.toString());
 8        config.setProperty(ASP.WEIGHT_HEURISTIC_SETTING,
 9            Double.toString(ASP.DEFAULT_WEIGHT_HEURISTIC));
10        return config;
11    }

Step 6.4 Defining the method that checks if a configuration is valid or not

The hasValideConfiguration() method calls the eponymous method of the parent class AbstractPlanner and adds the checks on the added properties. For your planner, it checks that the weight associated to the heuristic is strictly greater than 0 and that a heuristic has been chosen among the GoalCostHeuristics already defined in the library

 1    /**
 2     * Checks the planner configuration and returns if the configuration is valid.
 3     * A configuration is valid if (1) the domain and the problem files exist and
 4     * can be read, (2) the timeout is greater than 0, (3) the weight of the
 5     * heuristic is greater than 0 and (4) the heuristic is a not null.
 6     *
 7     * @return <code>true</code> if the configuration is valid <code>false</code> otherwise.
 8     */
 9    public boolean hasValidConfiguration() {
10        return super.hasValidConfiguration()
11            && this.getHeuristicWeight() > 0.0
12            && this.getHeuristic() != null;
13    }

Step 6.5 Redefining the constructors of your planner

Redefining the constructors of your planner to create your planner from a PlannerConfiguration can be done with the code below:

 1    /**
 2     * Creates a new A* search planner with the default configuration.
 3     */
 4    public ASP() {
 5        this(ASP.getDefaultConfiguration());
 6    }
 7
 8    /**
 9     * Creates a new A* search planner with a specified configuration.
10     *
11     * @param configuration the configuration of the planner.
12     */
13    public ASP(final PlannerConfiguration configuration) {
14        super();
15        this.setConfiguration(configuration);
16    }

Note

The final code of the planner code is available here.