class PlanningPredicate { constructor(t:Term, s:boolean) { this.term = t; this.sign = s; } unify(p:PlanningPredicate, occursCheck:boolean, bindings:Bindings) : boolean { if (this.sign != p.sign) return false; return this.term.unify(p.term, occursCheck, bindings); } subsumes(p:PlanningPredicate, occursCheck:boolean, bindings:Bindings) : boolean { if (this.sign != p.sign) return false; return this.term.subsumes(p.term, occursCheck, bindings); } equalsNoBindings(p:PlanningPredicate) : boolean { if (this.sign != p.sign) return false; return this.term.equalsNoBindings(p.term) == 1; } applyBindings(b:Bindings) : PlanningPredicate { if (b.l.length == 0) return this; return new PlanningPredicate(this.term.applyBindings(b), this.sign); } static fromString(str:string, o:Ontology) : PlanningPredicate { return PlanningPredicate.fromStringInternal(str, o, [], []); } static fromStringInternal(str:string, o:Ontology, variableNames:string[], variableValues:TermAttribute[]) : PlanningPredicate { let sign:boolean = true; if (str[0] == '~') { sign = false; str = str.substring(1); } return new PlanningPredicate(Term.fromStringInternal(str, o, variableNames, variableValues).term, sign); } toString() : string { if (this.sign) { return this.term.toString(); } else { return "~" + this.term.toString(); } } term:Term; sign:boolean; } class PlanningState { toString() : string { let str:string = "[ "; for(let i:number = 0; i= this.number_constraint) return true; } } return false; } /* // returns [missing, alreadySatisfied][] checkStateDetailed(state:PlanningState, occursCheck:boolean) : [PlanningPredicate[],PlanningPredicate[]][] { let goalStateMatch:[PlanningPredicate[],PlanningPredicate[]][] = [] for(let conjunction of this.predicates) { let missing:PlanningPredicate[] = []; let alreadySatisfied:PlanningPredicate[] = []; for(let predicate of conjunction) { let match:boolean = false; for(let term of state.terms) { if (predicate.term.unify(term, occursCheck, new Bindings())) { match = true; break; } } if (predicate.sign) { if (match) { alreadySatisfied.push(predicate); } else { missing.push(predicate); } } else { if (match) { missing.push(predicate); } else { alreadySatisfied.push(predicate); } } } goalStateMatch.push([missing,alreadySatisfied]); if (missing.length == 0) return goalStateMatch; } return goalStateMatch; } */ static fromString(str:string, o:Ontology) : PlanningCondition { return PlanningCondition.fromStringInternal(str, o, [], []); } static fromStringInternal(str:string, o:Ontology, variableNames:string[], variableValues:TermAttribute[]) : PlanningCondition { let tokens:string[] = []; let token:string = ""; let c:string; let state:number = 0; // 0: no token character yet, 1: inside a token let parenthesis:number = 0; let squareBrackets:number = 0; let quotation:boolean = false; let number_constraint:number = 1; if (str[0] >= '0' && str[0] <= '9') { let idx:number = str.indexOf(':'); number_constraint = Number(str.substring(0, idx)) str = str.substring(idx+1); } // separate the string in tokens: // each token can be: semicolon, colon, ~, or a term for(let i:number = 0;i 0) { s.predicates.push(conjunction); } return s; } // disjunction of conjunctions: predicates:PlanningPredicate[][] = []; number_constraint:number = 1; // minimum number of disjunctions that need to be true } class PlanningOperator { constructor(a_s:Term, a_p:PlanningPredicate[], a_e:PlanningPredicate[]) { this.signature = a_s; this.precondition = a_p; this.effect = a_e; } instantiate(b:Bindings) : PlanningOperator { let op:PlanningOperator = new PlanningOperator(this.signature.applyBindings(b), [], []); for(let precondition of this.precondition) { op.precondition.push(precondition.applyBindings(b)); } for(let effect of this.effect) { op.effect.push(effect.applyBindings(b)); } return op; } applyOperator(state:PlanningState, occursCheck:boolean) : PlanningState { let state2:PlanningState = new PlanningState(); let b:Bindings = new Bindings() // we reuse the same Bindings, to avoid slow calls to "new" for(let term of state.terms) { let toDelete:boolean = false; for(let effect of this.effect) { if (effect.sign) continue; b.l = [] if (term.unify(effect.term, occursCheck, b)) { toDelete = true; break; } } if (!toDelete) state2.terms.push(term); } for(let effect of this.effect) { if (!effect.sign) continue; state2.terms.push(effect.term); } return state2; } toString() : string { return this.signature.toString() + "\n\tprecondition: " + this.precondition + "\n\teffect: " + this.effect; } static fromString(signature_str:string, precondition_str_l:string[], effect_str_l:string[], o:Ontology) : PlanningOperator { let variableNames:string[] = []; let variableValues:TermAttribute[] = []; let operator:PlanningOperator = new PlanningOperator(Term.fromStringInternal(signature_str, o, variableNames, variableValues).term, [], []); for(let add_str of precondition_str_l) { operator.precondition.push(PlanningPredicate.fromStringInternal(add_str, o, variableNames, variableValues)); } for(let delete_str of effect_str_l) { operator.effect.push(PlanningPredicate.fromStringInternal(delete_str, o, variableNames, variableValues)); } return operator; } signature:Term; precondition:PlanningPredicate[]; effect:PlanningPredicate[] } class PlanningPlan { toString() : string { let str:string = ""; for(let action of this.actions) { str += action.signature.toString() + "\n"; } return str; } autoCausalLinks(s0:PlanningState, occursCheck:boolean) { this.causalLinks = []; for(let i:number = 0;i= 0) { this.causalLinks.push([satisfiedOn, i, precondition]); // console.log(" CL: " + satisfiedOn + " -> " + i + " (" + precondition.toString() + ")"); } } } } } actions:PlanningOperator[] = []; causalLinks:[number,number,PlanningPredicate][] = []; } class PlanningPlanner { constructor(a_o:PlanningOperator[], occursCheck:boolean) { this.operators = a_o; this.occursCheck = occursCheck; } plan(s0:PlanningState, goal:PlanningCondition, maxDepth:number) : PlanningPlan { return new PlanningPlan(); } generateChildren(operator:PlanningOperator, state:PlanningState, nextPrecondition:number, children:[PlanningOperator,PlanningState][]) { if (nextPrecondition >= 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; for(let term of state.terms) { b.l = [] if (precondition.term.unify(term, this.occursCheck, b)) { // console.log(" Action " + operator.signature.toString() + " removed as precondition " + precondition.toString() + " is not satisfied"); return; } } } // apply the operator: let newState:PlanningState = operator.applyOperator(state, this.occursCheck); if (newState != null) children.push([operator, newState]); } 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 term of state.terms) { b.l = [] if (precondition.term.subsumes(term, this.occursCheck, b)) { // console.log(" -> precondition: " + precondition.term.toString() + " satisfied"); this.generateChildren(operator.instantiate(b), state, nextPrecondition+1, children); } } } else { this.generateChildren(operator, state, nextPrecondition+1, children); } } } DEBUG:number = 0; occursCheck:boolean = false; operators:PlanningOperator[]; } class PlanningForwardSearchPlanner extends PlanningPlanner { constructor(a_o:PlanningOperator[], occursCheck:boolean) { super(a_o, occursCheck); } plan(s0:PlanningState, goal:PlanningCondition, maxDepth:number) : PlanningPlan { let plan:PlanningPlan = new PlanningPlan(); // iterative deepening: for(let depth:number = 1;depth<=maxDepth;depth++) { if (this.DEBUG >= 1) console.log("- plan -------- max depth: " + depth + " - "); if (this.planInternal(s0, goal, plan, depth)) { plan.autoCausalLinks(s0, this.occursCheck); return plan; } } return null; } planInternal(state:PlanningState, goal:PlanningCondition, plan:PlanningPlan, maxDepth:number) : boolean { if (this.DEBUG >= 1) { console.log("- planInternal -------- depth left: " + maxDepth + " - "); if (this.DEBUG >= 2) { console.log("State:"); console.log(state.toString()); } } // check if we are done: if (goal.checkState(state, this.occursCheck)) return true; if (maxDepth <= 0) return false; // obtain candidate actions: let children:[PlanningOperator,PlanningState][] = []; for(let operator of this.operators) { this.generateChildren(operator, state, 0, children); } if (this.DEBUG >= 1) { for(let tmp of children) { console.log(" candidate action: " + tmp[0].signature.toString()); } } // search: for(let [action,next_state] of children) { plan.actions.push(action) if (this.DEBUG >= 1) console.log("Executing action: " + action.signature.toString()); if (this.planInternal(next_state, goal, plan, maxDepth-1)) return true; plan.actions.pop(); } return false; } }