Agent 2 - a step by step tutorial

Agent 2 is an extension of Agent 1

In addition to the "turning to follow" behavior defined in Agent 1, Agent 2 assess its distance from a target and if it judges that it is not "too close", then it will strafe (walk or run) towards that target.

Agent 2 introduces the following concepts:

  1. Subsumption of simultaneous behaviors
  2. Extending control logic through lisp functions

Adding another Use Case

In the UML Chart on the right, we'll want to create a new Use Case for our new behavior.
Our new behavior seems to comprise of three sub-steps

  1. Checking how far we are from a target
  2. Deciding if we are far enough away to move closer
  3. Actually moving closer

Q: Why are the connections between Assess Range, Find Outside Range and Strafe to Target dashed instead of solid?

Defining the Top-level control structure

Agent2 retains the same top-level control structure as Agent1 -

  1. Sense
  2. Determine whether to Act
  3. Act

The "Choose Action" control structure

This control structure becomes a bit more complicated, as Agent2 will have multiple paths of behavior that will execute in parallel and will have to yield to each other.
As a result, we introduce a number of new pieces of control structure:

  1. Forks - these split a State Chart into multiple parallel branches of execution.
  2. Simple Relays - these delay a branch of a state chart. Since the framework steps through the State Chart diagram in a breadth first manner, sometimes we would like one branch of the state chart to wait for another branch to finish before continuing.
  3. Subsumption - this allows one branch of the state chart to suppress another branch to prioritize behaviors and prevent conflicts.
  4. Threshold Devices - these are state chart elements that pass on branch activation depending on whether their input values exceed a certain threshold. In this example only simple threshold devices are used.

Since we would like Agent2 to Strafe to target and Rotate to follow agent, we know that the state chart should have two branches of execution, and hence, a fork.

Now we would like a state diagram that does the following:

  1. Sense our range to a target
  2. Deliberate whether we are out of range of a target
  3. Either (a.) Strafe towards a target if we are not in range and suppress the "turn-to location" behavior. OR (b.) If we are in range...don't continue execution (thereby allowing other branches of behavior to possibly complete)

So 3 definite states have been described here -

Clearly, we would like these states to occur in the order shown above, but we must somehow account for the "rotate/follow" branch. If state activation in our new branch continues beyond "outside-range", then we would like to supress the "rotate/follow" branch. We'll do this by utilizing subsumption through a couple of threshold devices. These are represented in the UML as follows:

Now we have all the important states, but remember that the framework will step through each branch in a breadth first fashion. In other words, the framework will execute one step of rotate/follow, then one step of assess-range, then one more step of rotate/follow, and another of assess range. So as your state chart is now, anytime the agent decides it is outside of range and should *not* rotate/follow, it has already rotate/followed...not so good.

So...to fix this problem, we insert relays to be sure that each branch has an equal amount of states. A simple relay is a "decision" element with an "sr" stereotype.

Your completed state chart should now look something like this:

Rules

Each State we've just created - assess-range, outside-range and strafeto-target - each need rules for passing on activation. Recall how we created the state chart diagrames for the rotate/follow and turnto-location rules:

  1. Right Click > Sub Diagrams > New Statechart
  2. Set the stereotype of this new statechart to "rule" (double click on State/Activity Modelx in the left hand pane)
  3. Create Start State, End State
  4. Create intermediate states that express some piece of knowledge asserted.
  5. Add conditionals to state transitions that express a probe into percepts for a piece of information

We'll walk through one example together, and then it will be up to you to finish the rest. We'll start with the rule for 2-outside-range:
IF target is found to NOT be within range, THEN assert an intention to strafe towards the target.

(=> (and (within-range ?agent ?target no)
	 (location ?target ?x ?y ?z))
    (strafeto-target ?x ?y ?z 1 ?target))

Now we'll create the statechart for this rule:

The completed rule for outside-range should look like this:

Please complete the rules for strafeto-target and assess-range in a similar fashion - feel free to consult the Agent 1 Tutorial for additional guidance. The rules are given below:

2-assess-range:
IF agent is > 200 game units from a target, THEN assert that agent is NOT within range, ELSE assert that agent IS within range.

(=> (and (target ?target ?agent)
     	 (location ?target ?tx ?ty ?tz)
     	 (location ?agent ?ax ?ay ?az)
      	 (func within-n-units? ?ax ?ay ?tx ?ty 200 ?result))
    (within-range ?agent ?target ?result)
For 2-strafeto-target:
Assert an intention to execute a strafeto-target action.
(=>  (strafeto-target ?x ?y ?z ?priority ?target)
     (upload-cmd strafeto-target))
Notice that the rule for assess-range contains the following
(func within-n-units? ?ax ?ay ?tx ?ty 200 ?result)

This is a lisp function call - given the arguments ax ay tx ty and n - it places the return value in ?result. Because the actual definition of functions is not really integral to the UML description, we place these in a seperate file. As a result, in the UML we can treat functions the same as predicates, nothing special must be done.

Once you have completed the Agent2 specification, save your changes, go to lisp and run the following (don't forget to (mk:compile-system :ut-bots))

If your UML is correct, this command will complete without errors, otherwise, you missed something and will have to dig through the debugger to try and figure out what it was, which doesn't exactly offer many useful suggestions. Most likely you forgot a stereotype or used an invalid lisp expression for a name somewhere. Opening up the file to which you directed the generated code can also give you some clues.

Once you have generated code successfully, you can load it (File > Load). Remember that we used a function in one of our rules, so we must define it.

(defun within-n-units? (ax ay tx ty n)
  (if (<= (vec-length (vector (- ax tx) (- ay ty))) n)
      'yes
      'no))

You can put this in a seperate file and load that if you get sick of typing it in.

Now to insert your agent into UT...make sure UT server is running, and use (manage-agents) to get things started.