UNPKG

11.3 kBMarkdownView Raw
1AI Planning with STRIPS
2=======================
3
4Basic AI planning with STRIPS and PDDL. For details, see the [introduction](https://github.com/primaryobjects/strips) or try the online [demo](https://stripsfiddle.herokuapp.com).
5
6Install
7-------
8
9```
10npm install strips
11```
12
13Usage
14-----
15
16You'll first need to have a domain and problem file in PDDL format. You can create these yourself (see [examples](https://github.com/primaryobjects/strips/tree/master/examples)) or find them online. The features :strips :typing are supported.
17
18Here is an [example](https://github.com/primaryobjects/strips/blob/master/examples/blocksworld2/problem.txt) that loads a domain and problem from the "Blocks World" domain. This problem involves stacking blocks A, B on one table to a stack AB on another table (where A is on top of B).
19
20### Example
21
22```javascript
23var strips = require('strips');
24
25// Load the domain and problem.
26strips.load('./examples/blocksworld2/domain.txt', './examples/blocksworld2/problem.txt', function(domain, problem) {
27 // Run the problem against the domain.
28 var solutions = strips.solve(domain, problem);
29
30 // Display each solution.
31 for (var i in solutions) {
32 var solution = solutions[i];
33
34 console.log('- Solution found in ' + solution.steps + ' steps!');
35 for (var i = 0; i < solution.path.length; i++) {
36 console.log((i + 1) + '. ' + solution.path[i]);
37 }
38 }
39});
40```
41
42### Output
43
44```
45- Solution found in 3 steps!
461. move a x y
472. move b x y
483. stack a b y
49```
50
51Methods
52-------
53
54#### load(domainPath, problemPath, callback, isCode)
55
56Loads a domain and problem in PDDL and returns a domain and problem object in the callback. By default, domainPath and problemPath are expected to be file paths, containing PDDL code. To load from strings instead (if you've already loaded the contents of a file), set domainPath and problemPath to the raw PDDL strings and isCode = true.
57
58#### solve(domain, problem, isDepthFirstSearch = true, maxSolutions = 1, cost = null)
59
60Searches for a solution to the given problem by using depth-first-search, breadth-first-search, or A* search.
61
62The default setting uses depth-first-search and returns the first solution found. To return more solutions, set maxSolutions to a higher value.
63
64To use A* search, provide a function for the "cost" parameter, using the format cost(state), to serve as a heuristic for A*. The function should return an integer, representing the cost of the given state. See [starcraft.js](https://github.com/primaryobjects/strips/blob/master/starcraft.js) for an example.
65
66The cost function may also be passed as the 3rd parameter (ie., solve(domain, problem, cost)). Note, you can also write your own solution algorithm by using the methods below.
67
68#### graph(domain, problem, minLayers = 0, maxLayers = 0, isSkipNegativeLiterals = false)
69
70Returns a planning [graph](https://github.com/primaryobjects/strips/blob/master/examples/dinner/images/birthday-dinner.jpg) for a [domain](https://github.com/primaryobjects/strips/blob/master/examples/dinner/domain.pddl) and [problem](https://github.com/primaryobjects/strips/blob/master/examples/dinner/problem.pddl).
71
72The planning graph is returned as an array of actions in JSON. In each action, 'precondition' represents parent literals. 'effect' represents child literals. Any action not named 'noop' (no-operation) represents an applicable action, based on the preceding literals. 'noop' is simply a placeholder action to carry each literal forward, from one layer to the next.
73
74Each layer consists of 3-tiers: P0 (literals), A1 (actions), P1 (literals). The format is: P0 = precondition, A1 = actions, P1 = effect.
75
76The planning graph continues adding layers until no new literals and no new actions are discovered. The resulting graph can be used with [GraphPlan](http://en.wikipedia.org/wiki/Graphplan) or other search algorithms. For details on using GraphPlan, see [here](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-825-techniques-in-artificial-intelligence-sma-5504-fall-2002/lecture-notes/Lecture12FinalPart1.pdf) and [here](http://www.grastien.net/ban/teaching/06-planning5.pdf).
77
78If isSkipNegativeLiterals = true, negative literals (mutex) created from an action will be ignored. If you want to include complementary actions in the graph (such as 'Action A on A'), set strips.fast = false.
79
80See [example](https://github.com/primaryobjects/strips/blob/master/graph.js#L19-L30).
81
82#### getChildStates(domain, state)
83
84Returns an array of all valid child states from a given parent state. Each child state is returned in the format { state: state, action: action }. State is the child state. Action is the applicable action and parameter values on the parent that produced the child state.
85
86#### applicableActions(domain, state)
87
88Returns a list of applicable actions on the current state. This method uses all possible parameter values and runs each valid action that is defined in the domain against the current state. All actions that satisfy the preconditions are included in the resulting list.
89
90#### applyAction(action, state)
91
92Applies the action on the state and returns the new (child) state. It is assumed that the action's precondition has already been tested.
93
94#### isGoal(state, goalState)
95
96Returns true if the state contains the goal state conditions.
97
98#### isEqual(action1, action2)
99
100Returns true if two actions are equal. Two actions are equal if they contain the same name and parameter values.
101
102#### stateToString(state)
103
104Converts a JSON state object to a string. Since two states may have the same predicates in different orderings, this method sorts the predicates before returning the string object so they'll always look the same.
105
106#### actionToString(action)
107
108Converts an action operation to a string. For example: move a b
109
110Settings
111--------
112
113#### strips.fast
114
115Defaults to true, which uses permutationCombination to calculate possible parameter values for actions. Set this property to false to use baseN instead. Using baseN is slower, but will allow strips to utilize all possible solutions. This allows rendering of 'complementary actions', such as 'Action A on A', where normally you would want 'Action A on B'. Changing this setting is only necessary if you are unable to find a solution plan using the default setting.
116
117#### strips.verbose
118
119Set to true to display status information on the console while searching for a solution.
120
121#### strips.output
122
123Function to allow redirecting verbose output to different stream. By default: strips.output = function(text) { console.log(text); }
124
125#### strips.grammarDomainPath
126
127Allows changing the default path to the PEG.js domain grammar file. This file is used to enable parsing of the PDDL domain file.
128
129#### strips.grammarProblemPath
130
131Allows changing the default path to the PEG.js problem grammar file. This file is used to enable parsing of the PDDL problem file.
132
133Finding Solutions
134-----------------
135
136The default method, strips.solve(...), searches for all solutions using depth-first-search. You can use your own search method by calling the strips methods (listed above) to identify child states and actions yourself. In this way, you can implement A* search, breadth-first-search, or any other type of heuristic solution.
137
138For example, to find all applicable actions for the initial problem state:
139
140```javascript
141var strips = require('strips');
142var util = require('util');
143
144// Load the domain and problem.
145strips.load('./examples/blocksworld2/domain.txt', './examples/blocksworld2/problem.txt', function(domain, problem) {
146 // Get all applicable actions for the initial state.
147 var actions = strips.applicableActions(domain, problem.states[0]);
148
149 // Display the JSON result.
150 console.log(util.inspect(actions, true, 100, true));
151});
152```
153
154Similarly, to see the result of applying the first applicable action against the initial state, you can do the following:
155
156```javascript
157// Get all applicable actions for the initial state.
158var actions = strips.applicableActions(domain, problem.states[0]);
159
160// Apply first action on the initial state.
161var childState = strips.applyAction(actions[0], problem.states[0]);
162
163// Display the JSON result.
164console.log(util.inspect(childState, true, 100, true));
165```
166
167Here is a similar example that outputs the state and action to the console:
168
169```javascript
170// Get all applicable actions for the initial state.
171var actions = strips.applicableActions(domain, problem.states[0]);
172
173// Display the current state and action that we're going to apply.
174console.log('Current state');
175console.log(strips.stateToString(problem.states[0]));
176console.log('Applying action');
177console.log(strips.actionToString(actions[0]));
178
179// Apply first action on the initial state.
180var childState = strips.applyAction(actions[0], problem.states[0]);
181
182// Display the resulting modified state.
183console.log('New child state');
184console.log(strips.stateToString(childState));
185```
186
187Of course, the real power is in designing your own search algorithm using the strips methods. See the [default](https://github.com/primaryobjects/strips/blob/master/strips/strips.js#L566) search routine for an idea of how to use the methods to search.
188
189### A* Search
190
191A* search works by using a heuristic to guide it down the path of possible moves in the domain. In this manner, it is much faster than simple breadth-first or depth-first search. It will also find an optimal solution that contains the least number of steps.
192
193Strips comes with a built-in A* search algorithm that accepts your own cost function to use as a heuristic. See the section "methods" above. You simply write your own cost function that takes "state" as input and returns an integer as the resulting cost. Here is an [example](https://github.com/primaryobjects/strips/blob/master/starcraft.js) cost function for the starcraft [domain](https://github.com/primaryobjects/strips/blob/master/examples/starcraft/domain.txt) to train a [marine](https://github.com/primaryobjects/strips/blob/master/examples/starcraft/marine.txt):
194
195```javascript
196function cost(state) {
197 // This is our A* heuristic method to calculate the cost of a state.
198 // For Starcraft, the heuristic will be how many required buildings have been built. Subtract x from cost for each correct building, with 0 meaning all required buildings have been made and we're done.
199 var cost = 10;
200
201 for (var i in state.actions) {
202 var action = state.actions[i].action;
203
204 if (action == 'depot') {
205 cost -= 5;
206 }
207 else if (action == 'barracks') {
208 cost -= 5;
209 }
210 }
211
212 return cost;
213}
214```
215
216You can also implement your own A* search to find a solution. Since the strips library exposes its internal methods, you can implement your own search algorithm. The core idea to a custom search method is to use the strips methods isGoal() and getChildStates() to iterate through all states and actions. Once you have a list of child states, apply your heuristic to calculate a cost for each state. Then sort the states by cost so that A* can choose the next cheapest state to move to. You can see the details in the solve() methods in strips.
217
218Have fun!
219
220License
221-------
222
223MIT
224
225Copyright (c) 2015 Kory Becker
226http://primaryobjects.com/kory-becker