-
Notifications
You must be signed in to change notification settings - Fork 10
/
planner.js
58 lines (53 loc) · 1.55 KB
/
planner.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import { merge } from "lodash/fp/object";
import PriorityQueue from "fastpriorityqueue";
class Node {
constructor(parent, cost, state, action) {
this.cost = cost;
this.parent = parent;
this.state = merge({}, state);
this.key = action ? action.key : null;
this.action = action;
}
}
const mapActions = actions => {
actions = merge({}, actions)
return Object.keys(actions).map(key => {
return { ...actions[key], key };
});
};
const buildGraph = (parent, leaves, actions, goal) => {
actions.forEach(action => {
if (action.condition(parent.state)) {
let nextState = action.effect(merge({}, parent.state));
const cost = parent.cost + action.cost(nextState);
const node = new Node(parent, cost, nextState, action);
if (goal.validate(parent.state, nextState)) {
leaves.add(node);
} else {
const subset = actions.filter(a => a.key !== action.key);
return buildGraph(node, leaves, subset, goal);
}
}
});
return leaves;
};
const getPlanFromLeaf = (node, goal) => {
const plan = [];
const cost = node.cost;
while (node) {
if (node.action) plan.unshift(node.action);
node = node.parent;
}
return {
cost,
goal,
actions: plan.map(n => n.key)
};
};
export const createPlan = (state, actions, goal) => {
const root = new Node(null, 0, state, null);
const leaves = new PriorityQueue((a, b) => a.cost < b.cost);
buildGraph(root, leaves, mapActions(actions), goal);
if (!leaves.isEmpty()) return getPlanFromLeaf(leaves.poll(), goal);
return null;
};