/Domains-SoarTextIO

The purpose of this tool is to allow the Soar programmer to interact with a Soar agent while it is running. SoarTextIO allows you to communicates with an agent via plain sentences that it translates into a linked list of words.

Primary LanguageC++

Soar TextIO

By Taylor Lafrinere

##Introduction

The purpose of this tool is to allow the Soar programmer to interact with a Soar agent while it is running. SoarTextIO communicates with the agent via the input-link in the form of plain sentences (this will be elaborated on later). Why is this useful? Imagine you have written a Soar agent that solves algebra problems; SoarTextIO provides a way for you to give the agent equations to solve on the fly. Or, say that you have written an exploration bot in a simulation who's goal is to find different locations on a map, SoarTextIO allows you to continually give the agent different locations to find.

##Communication

In order to allow communication between the user and the agent via the input and output links, the format of how a sentence will show up on either side needs to be specified. Here are those specifications:

Input-Link Map:

^input-link
    ^text
        ^text-input-number <num>
        ^length <num-words>
        ^next
            ^value <word-one>
            ^next
                ^value <word-two>
                ^next 
                    ^value <word-three>
                    ^next
                        ^value nil

The meaning and usage of each WME are specified below:

  • ^input-link.text and ^output-link.text: All SoarTextIO input and output will happen under these top identifiers.
  • ^input-link.text.text-input-number: Increments each time there is new input.
  • ^input-link.text.length: The length of the message in words.
  • ^input-link.text.next: Each word falls under a "next" identifier.
  • ^input-link.text.next.value: Each word in the message is the value of a "Value" attribute.
  • ^input-link.text.next.value...next.value nil: Each sentence is terminated by a ^value nil pair.

Output-Link Map:

^output-link
    ^text
        ^text-input-number 1 # Optional
        ^length 5          # Optional
        ^get next-line     # Optional - used for loading from files
        ^next
            ^value <word-one>
            ^next
                ^value <word-two>
                ^next 
                    ^value <word-three>
                    ^next
                        ^value nil

Thus, for a Soar agent to respond to input generated by SoarTextIO, its rules must be tailored to the specifications above. For instance, let's say you wanted to write a simple program in Soar that takes two numbers as input from SoarTextIO and computes the sum of them. To do this, you will need two rules (along with the wait operator). A VisualSoar project has been created in the "agents" (named "Adder") folder within SoarTextIO. The two rules used to accomplish this are listed below for discussion purposes:

sp {Adder*propose*add
  (state <s> ^name Adder
    ^io.input-link.text <text> # Look for the "text" identifier off of the input-link
    -^last-input-number <num>) # Make sure this sentence hasn't been processed already.
  # The first argument and second argument to our adder function.
  (<text> ^text-input-number <num>
          ^next <word1>)
  (<word1> ^value <num1>
           ^next <word2>)
  (<word2> ^value <num2>)
-->
# Propose an operator with the two arguments and the input-number.
  (<s> ^operator <op> + =)
  (<op> ^name add
        ^num1 <num1>
        ^num2 <num2>
        ^input-number <num>)
}

sp {apply*add
  (state <s> ^operator <op>
             ^io.output-link <out>)
  # Test the operator and get the two arguments along with the input number.
  (<op> ^name add
        ^num1 <num1>
        ^num2 <num2>
        ^input-number <input-num>)
-->
  # Mark this input as processed by updating the state.
  (<s> ^last-input-number <input-num>)
  # Create a new text identifier on the output-link.
  (<out> ^text <t>)
  (<t> ^next <n>)
  # Perform the addition and place the value on the attribute
  (<n> ^value (+ <num1> <num2>))
}

Let's run this agent along with SoarTextIO to see the behavior of the program. To do this, launch the debugger and load in the adder rules found in SoarTextIO/agents/. Next, start the SoarTextIO executable. You should see a command line window pop up that looks like the following:

A connection to the kernel has been made.
You are connected to agent soar1

Please make sure your productions are loaded before giving Soar input.

The prompt is fairly simple. It reports the agent you are connected to and provides a reminder to load your Soar productions. Let's put SoarTextIO to work: type the numbers 4 5 into the command-line and press enter. Nothing happens! Well, actually, the input is scheduled to be added to input-link, but it won't actually be added until the input phase has been reached.

##More Commands

Soar can be controlled both by using the debugger and through the SoarTextIO command line. In general, commands in SoarTextIO are prefixed with -- to distinguish them from words that should be put on the input-link. To step Soar, type --step and press enter. Notice that Soar steps one decision cycle, causing the adder to initialize. Type --step again. Print the input-link with a depth of at least 5 in the debugger, it should look like this:

print --depth 20 i2

(I2 ^text T1)
  (T1 ^length 1 ^next N1 ^text-input-number 1)
    (N1 ^next N2 ^value 4)
      (N2 ^next nil ^value 5)

Notice that 4 and 5 have been added to the input-link in the form that was described above.

Type --step one more time and press enter. Two things should have happened: If you print the output-link in the debugger, you should now see a structure added to the output-link that contains the value 9. Also in the command-line window, you should see the following:

>--step

>--step

>--step

: 9

>

The answer of the addition is displayed. Things that start with a ":" are written by SoarTextIO.

Now add the numbers 6 and 7 and then 8 and 9 to test out the system a little more. This will also help show another feature in the next section.

Scripting

Inputs to SoarTextIO can be scripted for ease of use. We already inputted 3 sets of numbers that our Adder agent has added. To save these inputs to a file, type --save adder.txt. You should receive notification that the file has been saved. Go to SoarLibrary/bin/ and open the adder.txt file, you should see the three inputs given to SoarTextIO each on a different line.

Running Scripts

Let's now run this script that we just saved. To do this type: --load adder.txt and press enter. The first inputs are automatically read from the file and scheduled to be put on the input-link. Step twice now (either using SoarTextIO or the debugger). The result 9 is once again displayed. To get the next set of inputs, use the command: --nextline this will enter in the next set of inputs for you. Step twice again to see the results. Get the last set of inputs using the nextline command and step twice to see the final output. If you were to try and type --nextline now, you will be notified that nothing could be read from the file.

More Automation

Typing --nextline repeatedly can get pretty annoying, and makes it hard to automate large testcases. SoarTextIO is set up so that this bottleneck can be bypassed by allowing the Soar agent itself to ask for more input. To do this, we are going to have to change one of the rules in our Adder agent. Open the Adder VisualSoar project within VisualSoar and double-click the "add" operator.

You should see two rules, the second one looking similar to this:

sp {apply*add
  (state <s> ^operator <op>
            ^io.output-link <out>)
  (<op> ^name add
        ^num1 <num1>
        ^num2 <num2>
        ^input-number <input-num>)
-->
  (<s>   ^last-input-number <input-num>)
  (<out> ^text <t>)
  (<t>   ^next <n>
         ^get next-line)
  (<n> ^value (+ <num1> <num2>))
}

This will add ^get next-line to the output-link, under the text identifier. SoarTextIO will be watching for this, and if it has a file open with input left in the file, it will put it on the input-link.

Add the ^get next-line line as show above. Make sure to save the changes you have made to the agent. At this point, you could close the debugger along with SoarTextIO, open up new versions of each as before and load the new version of the productions. You could also reset things another way. In the VisualSoar window, under Soar Runtime, click Connect. Now, click on the title of the apply*add production, and under Runtime, click Send Production. This sends our updated production to the debugger, you should see the production displayed in the debugger's trace window.

Type --load adder.txt into SoarTextIO and press enter. This, of course, opens the same file we have been using all along. Now begin pressing the step button in the debugger. Notice that the first input gets put on the input-link just as before and output for it is generated. As you continue to press the step button, you should notice that our inputs are continually being loaded in and evaluated without having to receive any explicit calls from the user. Of course, you don't have to step through inputs as we have been doing. You can now set up input files (like adder.txt) and press run and the inputs will be sequentially given to the agent whenever SoarTextIO sees ^get next-line on the output-link.

Handwritten Scripts

If you open up the adder.txt file, you will notice that its format is very simple: each input is in the exact format as it was when you typed it in to SoarTextIO and is on its own line. This simple format was chosen for a reason. Instead of having to enter each input into SoarTextIO and then having to save the inputs to a file, you can create your own script files in a simple text editor. A few things must be noted though: 1) Each command must be on its own line and in the exact format that it would be in if it were put directly on SoarTextIO's command line. 2) Comments can be added in the scripts by starting a line with the '#' symbol but they must reside on their OWN LINE.

Throughout this tutorial we have been single stepping through each of Soar's decision cycles in order to understand how SoarTextIO interacts with Soar. The real power of SoarTextIO lies in actually running (not stepping) the Soar agent and giving it input when you want to. For instance, our Adder agent could be used like a simple calculator: simply load the agent and press run. As you come across numbers you want to add together, just enter them via SoarTextIO and the answer will be given to you immediately.

Command Summary

All commands are case-insensitive.

Command Example Operation
--CLEAR --clear Clears the current text-input on the input-link.
--CMDLIN --cmdlin init-soar The will be given to Soar directly to be used on its own command line.
--DEBUG --Debug Launches and instance of the SoarJavaDebugger and connects it to your current agent.
--LOAD --load file.txt Opens file.txt. Lines from the file are read in one at a time using --nextline
--NEXTLINE --nextline Reads in the next line from the input file. Previous to using this a file must be opened using –load.
--REMOTE --remote Destroys the current Kernel and creates a remote connection to a Kernel that is already created on the default port. This used to connect to simulations like TankSoar.
--RUN --RUN Executes the command RunAllAgentsForever().
--SAVE --save fileout.txt Saves all of the text-inputs (not "--" commands) to fileout.txt. This file can now be loaded and used via --nextline.
--STEP --Step Runs Soar for one cycle
--STOP --STOP Executes the commandStopAllAgents().
--QUIT or --EXIT --quit or --exit Quits the program

Building

The build instructions are the same as those for any C++ SML application. See the directions on the Soar homepage.