JSoarUsersGuide - soartech/jsoar GitHub Wiki
See also:
- Javadoc
- Faq
- JSoarDebugger
- JSoarTclSupport
- JSoarScripting
- JSoarPrinting
- JSoarEvents
- JSoarInput
- JSoarOutput
- JSoarRhsFunctions
- JSoarCommands
- JSoarSystemProperties
- JSoarLegilimens - Remote, web-based debugging
- JSoarSemanticMemory
- JSoarEpisodicMemory
For information about using the JSoar debugger, see JSoarDebugger
JSoar is essentially a library for constructing agent-based systems. It includes many conveniences, but you must still write both Soar code do define your agent's behavior as well as Java (or some JVM-based language) code to connect the agent to some environment.
JSoar's API is broken up into a number of classes and interfaces. Although this makes testing and extension easier, it can lead to confusion and code that's more verbose than necessary. In the spirit of helper classes like java.util.Collections and java.util.Arrays, JSoar provides a number of classes which should almost always be favored over their raw interface counterparts:
- Wmes - Methods for searching and filtering WMEs
- InputWmes - Methods for adding and updating input WMEs
- Symbols - Methods for converting symbols to and from Java obejcts
- SoarCommands - Methods for executing interpreter commands like "source"
- SoarEvents - Methods for common Soar event handling patterns.
Setting up a JSoar project depends a lot on your environment, build mechanism, and how you want JSoar to fit into your system. We recommend using a dependency management system like Maven or Gradle; see the README for more information.
If you want to directly include the dependencies yourself, you can get them from the Releases. A typical project will include jsoar-core, jsoar-debugger, jsoar-tcl, and jsoar-soarunit. It's typically best, to put the jars in a lib
folder in your project and then add them rather than referring to an external copy of JSoar
Regardless of your setup, create a class with a main
and create an agent as described below.
Additionally, the JSoar distribution comes with "source" and "javadoc" jars. These are generally useful for both debugging and figuring out what methods do.
Constructing a new agent in jsoar is very straightforward:
import org.jsoar.kernel.Agent;
...
Agent agent = new Agent();
agent.setName("My Agent");
agent.getPrinter().pushWriter(new OutputStreamWriter(System.out));
agent.initialize();
agent.runForever(); // Call blocks until agent interrupts or halts
...
This constructs and initializes a raw Soar agent. The Agent class includes base functions for running the agent (runFor() and runForever()) as well as interfaces for loading productions, modifying input, etc. The Agent class makes no assumptions about how the agent will be run and is not thread-safe.
When you're done with the agent, clean it up:
agent.dispose();
The ThreadedAgent class is a wrapper around a base Agent instance which provides some higher-level run control. In particular it creates a new thread for the agent to run in and provides primitives for interacting with the running agent in a thread-safe way:
import org.jsoar.runtime.ThreadedAgent;
...
ThreadedAgent threaded = ThreadedAgent.create();
threaded.setName("My Agent");
threaded.getPrinter().pushWriter(new OutputStreamWriter(System.out));
threaded.runForever(); // Agent begins running in its own thread. Returns immediately.
...
Because the agent managed by ThreadedAgent
is run in its own thread, ThreadedAgent
provides an asynchronous interface for interacting with the running agent. This style ensures that the agent's data structures are only accessed from a single thread and also prevents deadlock. To execute code in the agent's thread, pass a Callable
to the execute()
method:
final Agent agent = threaded.getAgent();
threaded.execute(new Callable<Void>() {
public Void call() throws Exception {
System.out.println("WMEs: " + agent.getAllWmesInRete());
return null;
}
}, null);
note that the call()
method is called in the agent thread. execute()
also takes an optional second callable which is always called after the first command is executed. It is also called in the agent's thread. Note that org.jsoar.runtime.SwingCompletionHandler
can be used in this context to marshal data back from the agent's thread to the Swing UI thread.
When you're done with the threaded agent, clean it up:
threaded.dispose();
The JSoar debugger is not displayed by default in a custom project. To open it, you can use the openDebugger()
(or openDebuggerAndWait()
) method on your ThreadedAgent
object (sorry, the debugger only works on ThreadedAgent
). Alternatively, you can start your agent running and have it call the (debug)
RHS function.
JSoar uses an instance of org.jsoar.util.commands.SoarCommandInterpreter
to process commands and load files. The interpreter is accessible through the getInterpreter()
method of the Agent
or ThreadedAgent
class. A default interpreter (implemented by org.jsoar.util.commands.DefaultInterpreter
) is created the first time this method is called.
JSoar includes an alternate, Tcl-based interpreter if you're so inclined. To enable it, make sure jsoar-tcl.jar
is on your classpath, and set the jsoar.agent.interpreter
system property to "tcl". See JSoarSystemProperties
The interpreter can source either normal files, or files accessible through a URL:
import org.jsoar.util.commands.SoarCommands;
...
SoarCommands.source(agent.getInterpreter(), "/path/to/file.soar");
... or ...
SoarCommands.source(agent.getInterpreter(), "http://darevay.com/jsoar/waterjugs.soar");
SoarCommands.source()
is a helper method which automatically determines whether the string (or object) given is a File or URL. Also, keep in mind that Java resources are accessible through a "jar" URL so your source code can be embedded in a jar or just present on the classpath:
SoarCommands.source(agent.getInterpreter(), MyClass.getResource("/path/to/resource.soar"));
The source command keeps track of the "current working directory", even with URLs, so code on a web-server, or embedded in a jar can refer to additional files through relative paths.
Note: that when using ThreadedAgent
all interpreter commands should be executed on the agent's thread since they normally manipulate agent state.
Currently, JSoar includes at least partial implementations of most of the built-in Soar commands.
Arbitrary properties can be associated with an agent. See org.jsoar.kernel.Agent.getProperties()
and org.jsoar.kernel.SoarProperties
for examples of built-in properties.
Property access is generally thread-safe, but may return structures that are not thread-safe.
Use the properties
command to get a list of the current values of all agent properties.
See JSoarRhsFunctions for more info on JSoar-specific RHS functions and creating new RHS functions.
See JSoarInput for more info on generating agent input.
See JSoarOutput for more info on handling agent output.
See JSoarSystemProperties for info on system properties used to control JSoar.
See JSoarTclSupport for info on Tcl support in JSoar.