1 /*********************************************************************
2 Copyright 2012, Ralph Ronnquist.
4 This file is part of GORITE.
6 GORITE is free software: you can redistribute it and/or modify it
7 under the terms of the Lesser GNU General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GORITE is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the Lesser GNU General Public
17 License along with GORITE. If not, see <http://www.gnu.org/licenses/>.
18 **********************************************************************/
20 package com.intendico.gorite;
22 import com.intendico.data.Query;
23 import com.intendico.data.Ref;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.Vector;
28 import java.util.Random;
31 * A BDIGoal is achieved by means of finding and performing a goal
32 * hierarchy as named by the BDI goal in the {@link Capability} that
33 * is the value of the {@link Data.Element} named by the BDIGoal's
34 * {@link #control} attribute. By default, the {@link #PERFORMER} data
35 * name is used, which is maintained by the execution machinery to
36 * resolve to the current performer. The BDIGoals created by the
37 * {@link TeamGoal} execution uses the role name as data name, which
38 * then is set up to be the role filling.
40 * <p> A BDIGoal is executed in a sequence of steps in an attempt to
41 * find a goal hierarchy for the named goal that acheives it, or more
42 * precisely, that executes without returning FAILED. The goal
43 * hierarchy alternatives are found via the {@link
44 * Capability#lookup(String)} method of the executing {@link
45 * Performer}. Any such alternative that implements the {@link
46 * Context} interface is considered a plan whose {@link
47 * Context#context(Data)} method defines its applicable variants.
49 * <p> The BDIGoal collates all applicable plan options, then selects
50 * one of them as an attempt to achieve the goal. If that fails, then
51 * the plan option is remembered, and the BDIGoal again collates all
52 * applicable, non-failed plan options, picks one and tries that. This
53 * repeats until either one plan execution succeeds (returns PASSED),
54 * or the plan options are exhausted, in which case the BDIGoal fails.
56 * <p> The choice of which among the applicable plan options to use
57 * can be modified via the plan choice settings. By default, the plan
58 * options are queried for their {@link Precedence#precedence(Data)}
59 * values, and the first of highest precedence option is
60 * choosen. However, if the goal is associated with a {@link Random}
61 * object, then the option is choosen by a random draw among the
62 * highest precedence options. Further, if the goal is associated with
63 * a plan choice goal name, then that plan choice goal is performed
64 * for effectuating the plan choice (instead of using precedence
67 * <p> A BDIGoal that has a {@link #specific} object set, makes that
68 * object available for instance execution as a data element named by
69 * the BDIInstance head name, which for invoked sub goals is
70 * everything up to the last '*' of their heads.
75 * @see Performer#getPlanChoice(String)
76 * @see Performer#setPlanChoice(String,Object)
79 public class BDIGoal extends Goal {
82 * The name of the data element whose value is the name of the
83 * current role for the executing performer.
85 public static final String ROLE = "current role";
90 public BDIGoal(String n) {
92 setGoalControl( PERFORMER );
96 * Cache of construction object (when not a String).
98 public Object specific;
101 * Constructor using an object other than String. Then the class
102 * name of the object is used as goal name, and the object is held
103 * as {@link #specific}. However, if the given object is a {@link
104 * String}, then its value (rather than its type) is used as goal
107 public BDIGoal(Object x) {
108 this( ( x instanceof String )? (String) x : x.getClass().getName() );
109 specific = x instanceof String? null : x;
113 * Creates and returns an instance object for achieving
116 public Instance instantiate(String head,Data d) {
117 return new BDIInstance( head );
121 * Utility method to add a Goal unless it's contained among
124 static public void maybeAdd(
125 Goal g,Vector/*<Goal>*/ v,Vector/*<Goal>*/ failed) {
126 if ( failed == null || ! failed.contains( g ) ) {
132 * Expand context sensitive alternative plans. This method gets
133 * invoked with the current selection of plans matching to the
134 * BDIGoal to achieve, the current collection of tried but failed
135 * plan variants, and the current {@link Data}. It processes all
136 * plan contexts so as to produce the currently possible
137 * alternative contextual plan invocations, by determining
138 * validating bindings for the plan's context queries.
140 * <p> The class {@link ContextualGoal} is used to represent a
141 * plan variant, which consists of the plan together with the
142 * query {@link Ref} object binding. This takes care of
143 * presenting the binding in the {@link Data} when the plan is
144 * invoked, to unset this binding from the {@link Data} if the
145 * plan execution fails, and to extract new bindings from the
146 * {@link Data} when the plan succeeds.
148 * <p> This method recognises the {@link Context#EMPTY} query
149 * as marker that a plan does not have any applicable variant,
150 * and it also catches the {@link Context.None} exception for
153 * <p> Plan variants are filtered against the failed set, to
154 * avoid the same plan variant be attempted more than once.
157 * @see ContextualGoal
158 * @see BDIInstance#action
160 static public Vector/*<Goal>*/ applicable(
161 Vector/*<Goal>*/ plans, Vector/*<Goal>*/ failed, Data data) {
162 Vector/*<Goal>*/ v = new Vector/*<Goal>*/();
164 for ( Iterator/*<Goal>*/ i = plans.iterator(); i.hasNext(); ) {
165 Goal goal = (Goal) i.next();
166 if ( ! ( goal instanceof Context ) ) {
168 new ContextualGoal( null, null, goal, data ),
173 Query query = ((Context) goal).context( data );
174 if ( query == null ) {
176 new ContextualGoal( null, null, goal, data ),
180 if ( query != Context.EMPTY ) {
181 Vector/*<Ref>*/ vars =
182 query.getRefs( new Vector/*<Ref>*/() );
183 Vector/*<Ref>*/ orig = Ref.copy( vars );
185 while ( query.next() ) {
187 new ContextualGoal( orig, vars, goal, data ),
191 } catch (Context.None e) {
192 // No applicable context
193 } catch (Throwable e) {
195 System.err.println( "Ignored plan " + goal );
202 * Return the plan choice for this goal, relative a given root
203 * capability executing the goal. This treats
205 public Object getPlanChoice(Capability root) {
206 return root.getPerformer().getPlanChoice( getGoalName() );
210 * Implements a BDI method choice.
212 public class BDIInstance extends Instance {
215 * The goal alternatives. (Called "relevant set" in BDI
218 public Vector/*<Goal>*/ relevant;
221 * The capability hierarchy offering goal methods.
223 public Capability root;
226 * The tried and failed goals.
228 public Vector/*<Goal>*/ failed = new Vector/*<Goal>*/();
231 * The count of attempts.
233 public int count = 0;
241 * The current goal instance.
243 Instance instance = null;
246 * Determine the context sensitive alternatives by invoking
247 * {@link #applicable}
249 * <p> At end, this method invokes {@link #precedenceOrder} on
250 * the collection of applicable plan variants, which sorts the
251 * plan variants by descending precedence.
253 * @see ContextualGoal
254 * @see #precedenceOrder
255 * @see BDIInstance#action
257 public Goal contextual(
258 Vector/*<Goal>*/ set,Vector/*<Goal>*/ failed,Data data) {
259 Vector/*<Goal>*/ v = applicable( set, failed, data );
261 // If there is a plan choice goal for this goal, then let
262 // it make the choice.
263 if ( plan_choice instanceof String ) {
264 return new PlanChoiceGoal(
265 root.getPerformer(), (String) plan_choice, v, failed );
267 return precedenceOrder( v, data );
271 * Apply precedence ordering destructively to the given goal
276 public Goal precedenceOrder(Vector/*<Goal>*/ set,Data data) {
278 Vector/*<Goal>*/ best = new Vector();
279 for ( Iterator/*<Goal>*/ i = set.iterator(); i.hasNext(); ) {
280 Goal g = (Goal) i.next();
281 int p = g instanceof Precedence?
282 ((Precedence) g).precedence( data ) :
283 Plan.DEFAULT_PRECEDENCE ;
284 if ( best.size() == 0 ) {
287 } else if ( p > level ) {
291 } else if ( p == level ) {
295 if ( best.size() == 0 )
297 if ( best.size() == 1 || ! ( plan_choice instanceof Random ) )
298 return (Goal) best.get( 0 );
299 return (Goal) best.get(
300 ((Random) plan_choice).nextInt( best.size() ) );
304 * Holds the plan choice goal, or null.
306 public Object plan_choice;
311 public BDIInstance(String h) {
316 * Cancel the execution.
318 public void cancel() {
319 if ( instance != null )
324 * Instantiates and performs sub goals in sequence, until the
325 * first one that does not fail. If a sub goal fails, then
326 * that is caught, and the next sub goal in sequence is
327 * instantiated and performed.
329 public States action(String head,Data data)
330 throws LoopEndException, ParallelEndException {
331 if ( root == null ) {
332 root = (Capability) data.getValue( getGoalControl() );
333 if ( root == null ) {
335 "** Missing capability '" + getGoalControl() +
336 "' for " + BDIGoal.this.toString( head ) );
337 return States.FAILED;
339 plan_choice = getPlanChoice( root );
341 if ( goal == null ) {
344 "** Lookup \"" + getGoalName() + "\"" );
346 data.setArgs( head, specific );
348 root.lookup( getGoalName() ), failed, data );
349 if ( goal == null && isTracing() ) {
351 "** Goal \"" + getGoalName() + "\" unknown " );
355 while ( goal != null ) {
356 if ( instance == null ) {
357 instance = goal.instantiate( head + "*" + count, data );
362 "** " + nameString( getGoalName() ) +
363 " attempt " + count );
365 String was_role = (String) data.getValue( ROLE );
367 data.setValue( ROLE, getGoalControl() );
369 b = instance.perform( data );
371 data.restoreValue( ROLE, was_role );
374 // Note, the following doesn't happen if instance.perform()
375 // above throws an exception.
377 if ( b != States.FAILED )
380 if ( goal instanceof PlanChoiceGoal ) {
381 PlanChoiceGoal pg = (PlanChoiceGoal) goal;
382 if ( pg.choice == null )
384 if ( pg.done == States.PASSED )
385 failed.add( pg.choice );
390 root.lookup( getGoalName() ), failed, data );
392 return States.FAILED;