/* Notes: - Standard GraphPlan cannot handle negated preconditions. - This version can handle them to a limited extent. To be safe, only use negative preconditions that do not get changed by other operators (i.e., only those that should match the initial state) - I ended up NOT using this planner, as it is too limited. But I keep it in the repo just because it took a while to code, and it's a shame to lose it, hehe */ class GraphPlanLayer { mutex:boolean[][]; links:number[][] = null; // where do the elements come from in the previous layer signature() : number[] { return []; } toStringMutex() : string { let str:string = "mutex: [ "; let first:boolean = true; for(let idx:number = 0; idx= 0) { l.links[found].push(i); } else { l.predicates.push(effect); l.links.push([i]); } } } // mutex: l.mutex = []; for(let i:number = 0;i= goal.length) { // make sure the negated predicates are also satisfied: let b:Bindings = new Bindings(); // we reuse one, to prevent slow calls to "new" for(let goal_predicate of goal) { if (goal_predicate.sign) continue; let positiveMatches:number[] = []; let negativeMatches:number[] = []; for(let predicateIdx:number = 0; predicateIdx 0) { return; } } // add the new solution: let predicatesUsed_clone:number[] = []; for(let p of predicatesUsed) predicatesUsed_clone.push(p); solutions.push(predicatesUsed_clone); } else { let goal_predicate:PlanningPredicate = goal[nextPredicate]; if (goal_predicate.sign) { let b:Bindings = new Bindings(); // we reuse one, to prevent slow calls to "new" for(let predicateIdx:number = 0; predicateIdx goal_predicate: " + goal_predicate.term.toString() + " satisfied"); let len:number = predicatesUsed.length; predicatesUsed.push(predicateIdx); // instantiate the goal let new_goal:PlanningPredicate[] = []; if (b.l.length == 0) { new_goal = goal; } else { for(let gp of goal) { new_goal.push(gp.applyBindings(b)); } } this.conjunctiveGoalAchievable(new_goal, nextPredicate+1, predicatesUsed, solutions, occursCheck); while(predicatesUsed.length > len) predicatesUsed.pop(); } } } else { this.conjunctiveGoalAchievable(goal, nextPredicate+1, predicatesUsed, solutions, occursCheck); } } } signature() : number[] { let nl:number = 0; let nm:number = 0; for(let i:number = 0; i= operator.precondition.length) { // make sure the negated preconditions are also satisfied: let b:Bindings = new Bindings(); // we reuse one, to prevent slow calls to "new" for(let precondition of operator.precondition) { if (precondition.sign) continue; let positiveMatches:number[] = []; let negativeMatches:number[] = []; for(let predicateIdx:number = 0; predicateIdx=1) console.log(" removing " + operator.signature + " because ~" + precondition.term + " is not achievable ("+positiveMatches.length+"/"+negativeMatches.length+"), used: " + predicatesUsed); return; } */ if (positiveMatches.length > 0) { if (GraphPlanPlanner.DEBUG>=1) console.log(" removing " + operator.signature + " because ~" + precondition.term + " is not achievable ("+positiveMatches.length+"/"+negativeMatches.length+"), used: " + predicatesUsed); return; } } // add the new action: this.actions.push(operator); let predicatesUsed_clone:number[] = []; for(let p of predicatesUsed) predicatesUsed_clone.push(p); this.links.push(predicatesUsed_clone); } else { let precondition:PlanningPredicate = operator.precondition[nextPrecondition]; if (precondition.sign) { let b:Bindings = new Bindings(); // we reuse one, to prevent slow calls to "new" for(let predicateIdx:number = 0; predicateIdx precondition: " + precondition.term.toString() + " satisfied"); let len:number = predicatesUsed.length; predicatesUsed.push(predicateIdx); this.possibleActions(operator.instantiate(b), cl, nextPrecondition+1, predicatesUsed); while(predicatesUsed.length > len) predicatesUsed.pop(); } } } else { this.possibleActions(operator, cl, nextPrecondition+1, predicatesUsed); } } } signature() : number[] { let nl:number = 0; let nm:number = 0; for(let i:number = 0; i=1) { console.log("predicates signature: " + graph[0].signature()); console.log(graph[0].toString()); } let cl:GraphPlanPredicateLayer = graph[graph.length-1]; let solutions:number[][] = cl.goalAchievable(goal, this.occursCheck); if (solutions.length > 0) { if (GraphPlanPlanner.DEBUG>=1) console.log("GraphPlanPlanner.plan: goal achievable with " + graph.length + " layers. " + solutions.length + " possible solutions."); // search for a plan: for(let solution of solutions) { let plan:PlanningPlan = this.searchPlan(graph, solution); if (plan != null) { plan.autoCausalLinks(s0, this.occursCheck); return plan; } } } do{ this.addPlanGraphLayer(graph); if (GraphPlanPlanner.DEBUG>=1) { let al:GraphPlanActionLayer = graph[graph.length-2]; console.log("action signature: " + al.signature()); console.log((graph.length-2) + ": " + al.toString()); cl = graph[graph.length-1]; console.log("predicates signature: " + cl.signature()); console.log((graph.length-1) + ": " + cl.toString()); } cl = graph[graph.length-1]; let solutions:number[][] = cl.goalAchievable(goal, this.occursCheck); if (solutions.length > 0) { if (GraphPlanPlanner.DEBUG>=1) console.log("GraphPlanPlanner.plan: goal achievable with " + graph.length + " layers. " + solutions.length + " possible solutions."); // search for a plan: for(let solution of solutions) { let plan:PlanningPlan = this.searchPlan(graph, solution); if (plan != null) { plan.autoCausalLinks(s0, this.occursCheck); return plan; } } } } while(!this.fixedPointAchieved(graph) && graph.length < (maxDepth*2+1)); return null; } initPlanGraph(s0:PlanningState) : GraphPlanLayer[] { let graph:GraphPlanLayer[] = []; let cl:GraphPlanPredicateLayer = GraphPlanPredicateLayer.fromPlanningState(s0); graph.push(cl); return graph; } addPlanGraphLayer(graph:GraphPlanLayer[]) { let cl:GraphPlanPredicateLayer = (graph[graph.length-1]); let al:GraphPlanActionLayer = GraphPlanActionLayer.fromPredicateLayer(cl, this.operators, this.occursCheck); graph.push(al); graph.push(GraphPlanPredicateLayer.fromActionLayer(al, this.occursCheck)); } fixedPointAchieved(graph:GraphPlanLayer[]) : boolean { if (graph.length<3) return false; return (graph[graph.length-3]).equals(graph[graph.length-1]); } searchPlan(graph:GraphPlanLayer[], targetPredicates:number[]) : PlanningPlan { let plan:PlanningPlan = new PlanningPlan(); // search for a plan: if (this.searchPlanInternal(graph, targetPredicates, plan, graph.length-1)) return plan; return null; } searchPlanInternal(graph:GraphPlanLayer[], targetPredicates:number[], plan:PlanningPlan, layer_idx:number) : boolean { // trivial case of goal satisfied at start: if (layer_idx == 0) return true; if (GraphPlanPlanner.DEBUG>=1) console.log("searchPlanInternal " + layer_idx + ": " + targetPredicates); let cl:GraphPlanPredicateLayer = graph[layer_idx]; let al:GraphPlanActionLayer = graph[layer_idx-1]; let solutions:number[][] = []; this.actionsToAchievePredicates(targetPredicates, 0, [], cl, al, solutions); for(let solution of solutions) { let nextTargetPredicates:number[] = []; let len:number = plan.actions.length; for(let action of solution) { for(let link of al.links[action]) { nextTargetPredicates.push(link); } if (al.actions[action].signature != null) plan.actions.unshift(al.actions[action]); } if (this.searchPlanInternal(graph, nextTargetPredicates, plan, layer_idx-2)) return true; // backtrack: while(plan.actions.length > len) plan.actions.splice(0,1); } return false; } actionsToAchievePredicates(targetPredicates:number[], idx:number, solution:number[], l:GraphPlanPredicateLayer, l_prev:GraphPlanActionLayer, solutions:number[][]) { if (idx >= targetPredicates.length) { let solution_clone:number[] = []; let atLeastOneRealAction:boolean = false; for(let action of solution) { solution_clone.push(action); if (l_prev.actions[action].signature != null) atLeastOneRealAction = true; } if (atLeastOneRealAction) { solutions.push(solution_clone); if (GraphPlanPlanner.DEBUG>=1) console.log(" solution: " + solution_clone); } return; } let links:number[] = l.links[targetPredicates[idx]]; for(let link of links) { let mutex:boolean = false; for(let previous of solution) { if (l_prev.mutex[link][previous]) { mutex = true; break; } } if (mutex) continue; if (solution.indexOf(link) == -1) { solution.push(link); this.actionsToAchievePredicates(targetPredicates, idx+1, solution, l, l_prev, solutions); solution.pop(); } else { this.actionsToAchievePredicates(targetPredicates, idx+1, solution, l, l_prev, solutions); } } } static DEBUG:number = 0; }