Visual Soar is a development environment for Soar applications. It is written in Java which means in order to run Visual Soar you must have a Java Runtime Environment installed.
Visual Soar contains tools that directly support many Soar related programming tasks. For example, creation of the operator heirarchy, maintence of the source tree, along with support for templates. In addition, upon supplying Visual Soar with information about what you expect to be in working memory, Visual Soar can check to make sure your productions are consistent, and in some cases complete attribute or values for you.
The three key components of Visual Soar are the operator window, rule editor, and data map. The operator window displays the heirarchy of operators you have in your system. The rule editor allows you to edit and manipulate rules for the operators. The data map displays the all possible structures that might exist in working memory when the rules are supposed to fire.
The operator window is where you define the structure of your system. It directly supports subgoaling and a heirarchy of operators. Here is where you directly manipulate the logical structure of your system. Entities that exist in this system are:
The root, the basis of the project, and will be named the same thing as what you called your project. You can add suboperators to the root, and access the top-state view of the data map from the root.
Operators come in three flavors: high-level, low-level, and linked. High-level operators are ones that produce a substate and have suboperators. Low-level operators produce no substate. Linked operators are operators with the same name as another high-level operator in the system, and produce the same substate. Every type of operator has an associated rule file where productions associated with that operator are saved. High-level operators also have an associated view of the data map where the suboperator productions will be checked against.
Files aid organization. Files are where you place things that are to be read into the system, but are not productions, productions that are not assciated with any operator, and productions that relate to state rather than operators (elaborations) among others. The only place where user's can add file entities is under the elaborations folder. An elaborations file is automatically created for you under a high-level operator. If you do not want the file parsed by a Check All Productions Command, start the name of the file with an "_". Note, this will also work for operators.
Folders are also organizational aids. There are always two, elaborations and all. All is a place to put operators that don't belong in any particular place in heirarchy because they can fire in all states. Elaborations is a folder that contain files with productions that relate to the top-state, fire in all possible states, or other productions that do not fit well within the hierarchy.
The rule editor allows you to edit the actual productions associated with operator or file. It provides basic text editing facilities. It also allows for template insertion along with a tab-completion type facility called "soar complete" which will complete some attribute or values for you.
The data map provides a way to enter information about what you expect to be in working memory when a production fires. It is supposed to represent the superset of all things that can be in working memory. Visual Soar uses this information to make sure your productions are consistent with this data map. The data map also uses type information to make sure values have the right type.
The different value types you can create in the data map are:
An identifier is equivalent to a soar identifier and can have a value.
An enumeration is usually an enumeration of strings that represent possible values for that attribute.
An integer is an integer number that is either ranged or not.
A float is a floating point number that is either ranged or not.
A string can be basically anything besides an identifier.
Note on mixed types: sometimes attributes have mixed-type values. You can simulate that by creating more than one attribute-value with the same attribute name but different value types.
To see how these components interact in the creation of a real system, we will be creating a wandering agent for Tank Soar.
The first thing to do to create your agent is select New Project... from the File menu (or hit ctrl n). A new agent dialog will come up; type in the name of your agent. I'm going to call mine Alphonse. Then either browse or type in your agent's path. Since I have tanksoar installed in my c:\temp\tank\TankSoar24\ directory, I chose c:\temp\tank\TankSoar24\agents for my agent's path. Then hit New to create the agent.
The operator window opens with our new agent. By default Visual Soar creates two folders: all, and elaborations. Along with the _firstload file, which will be the first file to load. The all folder is used to create operators that don't fit within the operator hierarchy and the elaboration folder is used to add elaborations that are either associated with the top-state or common to all states.
Next, we will open the data map for our top-state, so right-click on the root node of the operator window, which in my case is Alphonse and select Open DataMap from the context menu.
The DataMap window opens in the right view pane. Now, we will add common attribute-values. Expand the io attribute. You should see input-link and output-link. Right-click on input-link. A context menu will come up asking you what attribute-values you want to add. Select Add Identifier.... In the dialog, type in for the attribute name: blocked and hit Ok. You should now see the attribute blocked hanging off the input-link. You might need to expand the input-link to see it. You now have the basics of adding attribute values to the data map. Something to note, attribute-values can be linked, that is a new attribute is created but it points to the same value as another attribute. Attributes can be linked by ctrl-shift dragging attribute-values to the new desired parent in the data map (this also works across different views/windows of the data map). If a link is made to a primitive WME (anything besides an identifier) it's behavior is the same as making an independent copy. However, if a link is made to an identifier, the name of the link and original node will be independent, but their children will be shared.
When Visual Soar created your project, it created some productions to set up the type of task decomposition that Visual Soar supports. These productions create a top-state link on all states. Name the substate the same thing as the selected superstate operator. The last thing they do is name the top-state the same thing as your project name. New data map views are created assuming these productions exist. So you probably shouldn't modify them. These productions are found in the elaborations folder. They are in the _all and top-state files. When you right-click on _all and select Open Rules You should see a rule editor open with the following productions:
sp {elaborate*state*name
(state <s> ^superstate.operator.name <name>)
-->
(<s> ^name <name>)
}
sp {elaborate*state*top-state
(state <s> ^superstate.top-state <ts>)
-->
(<s> ^top-state <ts>)
}
The elaborate*state*name production copies the operator name from the superstate down to the substate. The elaborate*state*top-state similarly copies down the top-state link. You can now close the rule editor.
You might want to open the top-state file also, in this file you will find the following productions:
sp {elaborate*top-state*name
(state <s> ^superstate nil)
-->
(<s> ^name Alphonse)
}
sp {elaborate*top-state*top-state
(state <s> ^name Alphonse)
-->
(<s> ^top-state <s>)
}
The elaborate*top-state*name names the top-state the same as our project name. The elaborate*top-state*top-state production makes the initial link from the top-state to itself so that the elaborate*state*top-state will work.
Re-open the _all rule file. Our tank will be using a general io mechanism. In order for this to work we need to enter the following productions in the _all rule window.
sp {apply*operator*create-action-command
(state <s> ^operator <o>
^top-state.io.output-link <ol>)
(<o> ^actions <act>)
(<act> ^<att> <value>)
-->
(<ol> ^<att> <value>)
}
sp {apply*operator*remove-command
(state <s> ^operator <o>
^top-state.io.output-link <ol>)
(<ol> ^<att> <value>)
(<value> ^status complete)
-->
(<ol> ^<att> <value> -)
}
The apply*operator*create-action-command production copies the actions off an operator and places them on the output-link through the top-state. (Note that the tutorial does this by copying a link to io to all substates and copying items to that. I feel that going through the top-state is a more general way of dealing with the problem and only slightly more verbose. But the big advantage is that it is the way that Visual Soar directly supports so it saves you some work manipulating the data map.) The apply*operator*remove-command checks the output-link for any completed commands and frees up their memory.
Now that we have some ground rules set, we are ready to start specifying behavior for our tank. For this example, we will just build a wandering tank. So first thing we need to do is create a operator, so we right-click on Alphonse in the operator window and select Add a Suboperator... from the context menu, for the operator name type in wander.
Now wander is in our operator hierarchy, so right click on wander and select Open Rules.
Since all our tank is going to do is wander, we will make that the only proposal.
Note: only proposals for operator wander go into the wander operator file. These rules must match against the top-state.
sp {propose*wander
(state <s> ^name Alphonse)
-->
(<s> ^operator <o>)
(<o> ^name wander)
}
You can check this production against the data map to see if there are any mistakes.
Since wander is a high-level goal, we need to decide exactly what is wandering. I decided on the following behavior:
Each one of these will be its own operator:
So right click on wander and select Add a Suboperator... from the context menu, and type in powerdown. Now do the same for move, turn and turn-around.
Next, we need to set up the DataMap for wander, since wander has suboperators it also has a DataMap, so right-click on wander and select Open DataMap and tile it with the top-state DataMap named Alphonse.
Since these are low-level operators, and they will perform actions using our generalized application rules. We will need to create an actions attribute underneath each operator in the wander data map. To do this, expand top-state and io, ctrl-shift drag the output-link to one of the operator attributes in the wander DataMap. Now, operator has an attribute named output-link, right-click on output-link, select Rename... from the context menu and type actions into the input dialog box. Link this attribute value (ctlr-shift drag) to the rest of the operators.
Our concern in wandering is that we don't want to use any power. Visual Soar created an elaborations file, because wander is a subgoal. So open this elaborations file, and type in the following production
sp {wander*elaborate*using-power
(state <s> ^name wander
^top-state.io.input-link <il>)
(<il> ^<< radar-status shield-status >> on)
-->
(<s> ^using-power true)
}
Now, add ^using-power true to the wander DataMap.
Now we know if we are using power, so close elaborations, open powerdown and type in the following production:
sp {wander*propse*powerdown
(state <s> ^name wander
^using-power)
-->
(<s> ^operator <o>)
(<o> ^name powerdown)
(<o> ^actions <a>)
(<a> ^radar.switch off
^shields.switch off)
}
open move's rule file and type in
sp {wander*propose*move
(state <s> ^name wander
-^using-power
^top-state.io.input-link.blocked.forward no)
-->
(<s> ^operator <o>)
(<o> ^name move)
(<o> ^actions <a>)
(<a> ^move.direction forward)
}
similarly, for turn type in
sp {wander*propose*turn
(state <s> ^name wander
-^using-power
^top-state.io.input-link.blocked <b>)
(<b> ^forward yes
^{ << left right >> <dir> } no)
-->
(<s> ^operator <o> + =)
(<o> ^name turn
^actions.rotate.direction <dir>)
}
Finally, for turn-around type in
sp {wander*propose*turn-around
(state <s> ^name wander
-^using-power
^top-state.io.input-link.blocked <b>)
(<b> ^forward yes ^left yes ^right yes)
-->
(<s> ^operator <o>)
(<o> ^name turn-around
^actions.rotate.direction left)
}
Now we have finished our wandering Tank Agent. To be able to load our agent into TankSoar you must save it at least once. Alphonse is now able to be loaded into Tank Soar environment at Alphonse.soar.