You are here

Report #2 - Soar: Controlling WorldServer3D

1. ACTIVITIES

DemoSOAR JavaWebStart application is available for accessing here.

1.1 Controlling WorldServer3D - DemoSOAR Overview

WS3DProxy provides a high-level API for supporting connecting to WordServer3D, receiving commands and translating them to operations in WordServer3D.

DemoSOAR is an application that consumes WS3DProxy API for connecting to WorldServer3D and in addition it uses shared Soar native libraries, written in C++, for using Soar Markup Language (SML) available APIs.

The Code Snippet 1 shows how DemoSOAR deals with various Operating Systems (OS), providing the correct libraries properly compiled for each running target OS. The NativeUtils class, included in DemoSOAR project SoarBridge package,  provides support on "unpacking" and loading resources that are available on the main jar file. Once the code runs and it properly identifies the running OS, NativeUtils unpacks the equivalent OS library into the system temporary directory and loads it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 if(osName.contains("win")){
    if(osArch.contains("64")) {
       System.out.println("Windows 64 bits");
       NativeUtils.loadFileFromJar("/win64/Soar.lib");
       NativeUtils.loadFileFromJar("/win64/Soar.dll");
       NativeUtils.loadFileFromJar("/win64/Java_sml_ClientInterface.dll");
    } else {
       System.out.println("Windows 32 bits");
       NativeUtils.loadFileFromJar("/win32/Soar.lib");
       NativeUtils.loadFileFromJar("/win32/Soar.dll");
       NativeUtils.loadFileFromJar("/win32/Java_sml_ClientInterface.dll");
    } 
 } else 
    if(osName.contains("mac")){
       System.out.println("MacOSX");
       NativeUtils.loadFileFromJar("/macos/libSoar.dylib");
       NativeUtils.loadFileFromJar(
                               "/macos/libJava_sml_ClientInterface.jnilib");
    } else 
       if(osName.contains("nix") || osName.contains("nux")){
          if(osArch.contains("64")) {
             System.out.println("Linux 64 bits");
             NativeUtils.loadFileFromJar("/linux64/libSoar.so");
             NativeUtils.loadFileFromJar(
                               "/linux64/libJava_sml_ClientInterface.so");
          } else {
             System.out.println("Linux 32 bits");
             NativeUtils.loadFileFromJar("/linux32/libSoar.so");
             NativeUtils.loadFileFromJar(
                               "/linux32/libJava_sml_ClientInterface.so");
          }
       } else {
       //Unable to identify
          throw new IllegalStateException(
                      "Unable to determine what the operating system 
                       is, cannot automatically  load native libraries");
       } 

Code Snippet 1 - Platform and OS native library related loading.

After successfully loading the native library, NativeUtils unpacks and loads the SML rules file soar-rules.soar. The rules file contains the rules for controlling the Soar agent responsible for the creature artificial mind.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
    NativeUtils.loadFileFromJar("/soar-rules.soar");
    String soarRulesPath = "soar-rules.soar";
    String soarDebuggerPath = "";
    Integer soarDebuggerPort = 12121;

    //Start enviroment data
    SimulationTask simulationTask = new SimulationTask();
    simulationTask.initializeEnviroment(Boolean.FALSE);
    simulationTask.initializeCreatureAndSOAR(soarRulesPath,true,
    soarDebuggerPath,
    soarDebuggerPort);

    // Run Simulation until some criteria was reached
    Thread.sleep(3000);

    while(true){
       simulationTask.runSimulation();
       Thread.sleep(100);
    } 

Code Snippet 2 - Soar rules file loading and main simulation loop.

The SimulationTask class makes use of WS3Proxy for interfacing with WorldServer3D. The SimulationTask constructor instantiates a WS3DProxy and uses it later when initializing the creature by means of initializeCreatureAndSOAR method. The Code Snippet 2 show how the SimulationTask object is instantiated and also show how the simulation environment and creature are initiated. Notice that the code make reference the a debugger by using the declared variables soarDebuggerPath and soarDebuggerPort. The Soar rules file soar-rules.soar "unpacked" and loaded previously by class NativeUtils is also passed to SimulationTask object by means of method initializeCreatureAndSOAR.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
   public class SimulationTask {
      ...
      public SimulationTask(){
         proxy = new WS3DProxy();
      }

      /**
      * Initialize the Simulation robot and SOAR debugger
      * @param rulesPath The path in file system where the SOAR rules are located
      * @throws SoarBridgeException
      */
      public void initializeCreatureAndSOAR(String rulesPath, 
         Boolean runDebugger, 
         String soarDebuggerPath,
         Integer soarDebuggerPort) throws 
         SoarBridgeException, 
         CommandExecException{
         ...
      }

      /**
      * Initialize the Simulation environment
      */
      public void initializeEnviroment(Boolean prepareEnviromentAndStartGame) 
                                          throws CommandExecException{
         ... 
      }

      /**
      * Start Simulation Method
      * @throws SoarBridgeException
      * @throws NullPointerException
      */
      public void runSimulation() throws SoarBridgeException, 
         NullPointerException, 
         IllegalAccessException, 
         CommandExecException{
        ...
      }

      /***
      * Stop Simulation
      */
      public void stopSimulation() throws CommandExecException{
         ...
      }
      ...
}

Code Snippet 3 - SimulatonTask constructor and main methods.

As shown by Code Snippet 3, which shows only the public main methods implemented, the SimulationTask class is responsible for instantiating a WS3DProxy, preparing the simulation and executing it. When running the simulation, the SimulationTask object also makes use of Soar rules for performing creature actions based on its decisions. The SoarBridge object, instantiated by SimulationTask, is responsible for reading sensing information and sending commands to the creature in WordServer3D.

WS3DProxy is the package that offers an API for connecting the application to WorldServer3D. It uses a TCP/IP connection where both the IP address of WorldServer3D and the port are provided during the instantiation. The default port for WorldServer3D is 4011 and its IP address depends on where it has been deployed. As an example, if it has been deployed on localhost, the IP address and port will be 127.0.0.1 and 4011 respectively.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    public WS3DProxy(String host, int port) {
       this.host = host;
       this.port = port;
       connect();
       this.world = World.getInstance();
    }

    public WS3DProxy() {
       this("localhost", 4011);
       //connect();
       this.world = World.getInstance();
    } 

Code Snippet 4 - WS3DProxy constructors.

The WS3DProxy package has 2 constructors and, when instantiating it, the data regarding host and port can be either provided or not according to the constructors as shown in the Code Snippet 4. SimulationTask class instantiates a WS3DProxy object passing no host or port information, assuming that WorldServer3Dis accessible at localhost (127.0.0.1) and port 4011.

The WS3DProxy package also contains many abstractions of WorldServer3D elements in package ws3dproxy.model like World, Creature, Thing, Bag, Leaflet, among others. Another useful class worth to mention is the CommandUtility class that has many static methods for sending various commands to WorldServer3D for controlling the creature and changing specific attributes.

DemoSOAR, through the SimulationTask as mentioned before, makes use of WSD3Proxy abstractions, mainly the Creature abstraction, together with its internal abstractions of simulated objects like the SimulationRobot. The SimulationTask, when executing the runSimulation method, creates a SimulationRobot object and provides the reference to a previously created Creature object. From that moment all the interactions with the creature in WorldServer3D, mainly regarding positioning and sensing, are made with the support of the SimulationRobot object.

Another important element in the architecture, also mentioned before, is the SoarBridge object. The SoarBridge object is also instantiated by SimulationTask class and makes the bridge between DemoSOAR simulation object and WorldServer3D elements. It makes use of the SimulationRobot for accessing information regarding positioning and sensing and sends commands to the related creature in WordServer3D. Notice that sensing an positioning in SimulationRobot is also provided with the support of WS3DProxy API. The Soar agent, through the native APIs, is also created by SoarBridge.

As shown before, SoarBridge object is an important piece  responsible for binding together internal DemoSOAR simulation data, WordServer3D elements and Soar agent related to the artificial mind.

The artificial mind rules contained in soar-rules.soar is organized by the group of productions presented in the Table 1.

PRODUCTIONDESCRIPTION
  
Wander related productions 
  • propose*wander
propose agent walk ahead at the environment
  • apply*wander
if wander operator is selected, then apply an output command
  • apply*wander*remove*move
once wander operator is selected and completed, them remove it
  
Entity seeing related productions 
  • propose*see*entity*with*memory*count
propose agent holding entities in memory if it has entities in memory
  • apply*see*entity*with*memory*count
if the related operator is selected, then apply an output command
  • propose*see*entity*without*memory*count
agent  holds entities in memory if it does not have entities in memory
  • apply*see*entity*without*memory*count
once the related operator is selected, then apply an output command
  
Food related productions 
  • propose*move*food
propose agent move straight to food
  • apply*move*food
if move operator is selected, then apply an output command
  • apply*moveFood*remove-move
once move food is selected and completed, them remove it
  • apply*moveFood*remove*food
remove move food from memory since it does not exists anymore
  • propose*eat*food
propose eating the food
  • apply*eat*food
if eat food operator is selected, then apply an output command
  • apply*eatFood*remove-eat
once eat food operator is selected and completed, remove it
  
Jewel related productions 
  • propose*move*jewel
propose agent to move straight to the jewel
  • apply*move*jewel
if the move jewel operator is selected, then apply an output command
  • apply*moveJewel*remove-move
once move jewel is selected and completed, them remove it
  • apply*moveJewel*remove*jewel
remove move jewel from memory since it does not exists anymore
  • propose*get*jewel
propose agent to get jewel
  • apply*get*jewel
if the get jewel operator is selected, then apply an output command
  • apply*getJewel*remove-get
once get jewel is selected and completed, them remove it
  
Avoid brick related productions 
  • propose*avoidBrick
propose agent to avoid brick
  • apply*avoidBrick
if the related operator is selected, then apply an output command
  • apply*avoidBrick*remove*entity*memory
remove brick entity from memory
  • apply*avoidBrick*remove-move
once the related operator is selected and completed, them remove it
  
Preference operators 
  • moveJewel*seeEntity*preferences
move jewel or move food (see entity)
  • avoidBrick*seeEntityWithMemory*preferences
see entity with memory avoiding brick
  • seeEntity*without*memory*preferences
see entity without memory preferences
  • moveJewel*getJewel*preferences
move jewel / get jewel
  • getJewel*avoidBrick*preferences
get jewel / avoid brick
  • moveJewel*moveJewel*less*distance
move jewel / move jewel preferences
  • getJewel*getJewel*preferences
get jewel / get jewel preferences
  • moveFood*eatFood*preferences
move food / eat food
  • eatFood*avoidBrick*preferences
eat food / avoid brick
  • moveFood*moveFood*preferences
move food / move food preferences
  • eatFood*eatFood*preferences
eat food vs eat food preferences
  • moveFood*moveJewel*preferences*moveFoodWins
move food / move jewel preferences (move food wins)
  • moveFood*moveJewel*preferences*moveJewelWins
move food / move jewel preferences (move jewel wins)
  • avoidBrick*avoidBrick*without*move*jewel*preferences
avoid brick / avoid brick preferences
  • avoidBrick*moveJewel*moveFood*preferences
avoid brick / move jewel / move food preferences with element in memory
  • wander*preferences
wander preferences

Table 1 - soar-rules.soar productions.

According to The Soar User's Guide [1], the production naming is an almost arbitrary constant that describes the role of the production. By convention the production naming contains important naming elements separated by asterisks (*). From the production name example blocks-world*propose*move-block, important naming elements are the task or goal name blocks-world, architectural function propose and operator/object at issue. In the soar-rules.soar productions shown above, propose and apply architectural functions are related to the operation proposition and application respectively. Issue operators/objects that contains remove are related to removing elements from the working memory once the operation has been executed and completed. Operator that ends with preferences contains solutions for avoiding deadlock situations regarding the proposed operations.

The rules, in general contains productions for moving a creature in WordServer3D searching for jewel or food and eating or getting those objects based on impasse preference analysis.

When the simulation is started and a creature is created, the agent starts wandering based on the wander productions searching for objects with the help from visual sensors.
Having found an object, the seeing related productions provides means for the artificial mind to memorize the object, that can be either jewel or food. Found objects trigger the creation of entities in the working memory of the artificial mind and based on these entities new movements will be proposed for moving the creature to the memorized objects.

Based on the preferences productions, the creature will prefer jewel instead of food unless the available fuel is equal or less a predefined threshold.

1.2 Controlling WorldServer3D - Leaflet Implementation

For supporting Leaflet object the classes SimulationCreature, SimulationTask, SoarBridge and SoarCommand were changed as shown in the code snippets following. A new class was also added to the package with name SoarCommandDelivery, which the code is also shown following. Some productions were changed and others were added in soar-rules.soar file and those productions are shown in Table 2.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
    private HashMap<String, Integer> leadletCounters;
    ...
    public void addLeaflets (List<Leaflet> leaflets){
                 
        for (Leaflet leaflet : leaflets){ 
            String[] types  = {
                Constants.colorRED,
                Constants.colorGREEN,
                Constants.colorBLUE,
                Constants.colorYELLOW,
                Constants.colorMAGENTA,
                Constants.colorWHITE,
                Constants.colorORANGE,
            };
            for (String type : types) {
                // Get "total number"
                int nt = leaflet.getTotalNumberOfType(type);
                // Get "collected number"
                int nc = leaflet.getCollectedNumberOfType(type);
                // Create leaflets counters hash if it does not exist
                // and if counters have values
                if ((nt > 0 || nc > 0) && leadletCounters == null) {
                    leadletCounters = new HashMap();
                }
                int c;
               // Get counters for "total number"
                if (nt > 0){
                    c = 0;
                    if (leadletCounters.containsKey(type + "_tot")) {
                        c = leadletCounters.get(type + "_tot");
                    }
                    leadletCounters.put(type + "_tot", c + nt);
                }
                // Get counters for "collected number"
                if (nc > 0){
                    c = 0;
                    if (leadletCounters.containsKey(type + "_col")) {
                        c = leadletCounters.get(type + "_col");
                    }
                    leadletCounters.put(type + "_col", c + nc);
                }   
            }
        }       
    }

    private int getC(String key){
        int n = 0;
        if (leadletCounters != null){
            n = leadletCounters.get(key);
        }
        return n;        
    }
    
    public int getColor(String color){
        return getC(color+ "_tot");
    }
    
    public int getCollectedColor(String color){
        return getC(color+ "_col");   
    }

Code Snippet 5 - SimulationCreature leaflet code.

1
2
3
4
5
6
7
   private void prepareAndSetupCreatureToSimulation(Creature creature, List<Thing> things) 
                                      throws SoarBridgeException, IllegalAccessException {
      ...
      simulationCreature = new SimulationRobot(creature);
      ...
      simulationCreature.addLeaflets(creature.getLeaflets());
      ...
   }

Code Snippet 6 - SimulationTask leaflet code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
    private Identifier creatureLeaflets;
    private Identifier creatureJewels;
    ...
    public void setupStackHolder(StakeholderType entityType, 
                                 SimulationCreature parameter) 
                                 throws SoarBridgeException {
       ...
       creatureLeaflets = agent.CreateIdWME(creature, "LEAFLETS");
       agent.CreateIntWME(creatureLeaflets, Constants.colorRED, 
                          parameter.getColor(Constants.colorRED));
       agent.CreateIntWME(creatureLeaflets, Constants.colorGREEN, 
                          parameter.getColor(Constants.colorGREEN));
       agent.CreateIntWME(creatureLeaflets, Constants.colorBLUE, 
                          parameter.getColor(Constants.colorBLUE));
       agent.CreateIntWME(creatureLeaflets, Constants.colorYELLOW, 
                          parameter.getColor(Constants.colorYELLOW));
       agent.CreateIntWME(creatureLeaflets, Constants.colorMAGENTA, 
                          parameter.getColor(Constants.colorMAGENTA));
       agent.CreateIntWME(creatureLeaflets, Constants.colorWHITE, 
                          parameter.getColor(Constants.colorWHITE)); 
                        
       creatureJewels = agent.CreateIdWME(creature, "JEWELS");
       agent.CreateIntWME(creatureJewels, Constants.colorRED, 
                          parameter.getCollectedColor(Constants.colorRED));
       agent.CreateIntWME(creatureJewels, Constants.colorGREEN, 
                          parameter.getCollectedColor(Constants.colorGREEN));
       agent.CreateIntWME(creatureJewels, Constants.colorBLUE, 
                          parameter.getCollectedColor(Constants.colorBLUE));
       agent.CreateIntWME(creatureJewels, Constants.colorYELLOW, 
                          parameter.getCollectedColor(Constants.colorYELLOW));
       agent.CreateIntWME(creatureJewels, Constants.colorMAGENTA, 
                          parameter.getCollectedColor(Constants.colorMAGENTA));
       agent.CreateIntWME(creatureJewels, Constants.colorWHITE, 
                          parameter.getCollectedColor(Constants.colorWHITE));
       ...
    } 

Code Snippet 7 - SoarBridge leaflet code.

1
2
3
4
5
6
7
8
9
    public SoarCommand(CommandType _command){
       ...
            switch (commandType)
            ...    
            case DELIVERY:
                commandArgument = new SoarCommandDelivery();
                break;
       ...
    }

Code Snippet 8 - SoarCommand leaflet code.

1
2
3
4
5
6
7
8
package SoarBridge;

class SoarCommandDelivery {

    public SoarCommandDelivery() {
    }
    
}

Code Snippet 9 - SoarCommandDelivery leaflet code.

PRODUCTION
 
Entity seeing related productions
  • propose*see*entity*with*memory*count
  • propose*see*entity*without*memory*count
 
Food related productions
  • propose*eat*food
  • apply*eat*food
 
Jewel leaflet related productions
  • propose*get*jewel*leaflet
  • apply*get*jewel*leaflet
  • apply*getJewel*leaflet*remove-get
 
Jewel related productions
  • propose*get*jewel
  • apply*get*jewel
 
Jewel preference operators
  • moveJewel*getJewelLeaflet*preferences
  • getJewelLeaflet*avoidBrick*preferences
  • getJewelLeaflet*getJewelLeaflet*preferences
  • getJewelLeaflet*getJewel*preferences

Table 2 - soar-rules.soar leaflet impacted and new productions.

The new package with the implemented code above and rule files can be found for accessing here.

For connecting DemoSOAR to another WordServer3D for making more than one creature compete against another one some changes where made in SimulationTask class regarding the new target WordServer3D IP address and port as shown in the code snippet following.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class SimulationTask
{
    Logger logger = Logger.getLogger(SimulationTask.class.getName());
    WS3DProxy proxy = null;
    //Workspace workspace = null;
    SoarBridge soarBridge = null;
    Creature c = null;
    World w = null;

    public SimulationTask()
    {
        proxy = new WS3DProxy("146.250.178.132", 4011);
        //proxy.connect();
    }

 

Code Snippet 9 - Competition address settings.

Some screenshots follows showing both creatures competing with also some information regarding leaflet objects of each creature.

 

2. REFERENCES

[1] The Soar User's Manual, http://web.eecs.umich.edu/~soar/downloads/Documentation/SoarManual.pdf

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer