Search code examples
javaartificial-intelligencemulti-agent

Synchronise all agent actions with TimeSteppedEnvironment in Jason AgentSpeak


I am currently exploring how Jason deals with a synchronized environment in which one agent must wait till others perform an action. Besides, when all agents have done, env needs to save info to environment models as well. From the document, Jason exposes a way via TimeSteppedEnvironment.

My problem is, at reasoningCycle (class LocalAgArch), there is an error where an agent can't get percepts. Error message is below, I am not sure what I missed to let requests not be null.

[test1] *** ERROR in the transition system (sense). Circumstance:
  E =[+!start[source(self)], +kqml::clear_source_self([],[])[hide_in_mind_inspector,source(self)]]
  I =[]
  A =null
  MB=[]
  RP=null
  AP=null
  SE=null
  SO=null
  SI=null
  AI=null
  AE=null
  PA={}
  PI={}
  FA=[].
Creating a new C!
java.lang.NullPointerException: Cannot enter synchronized block because "this.requests" is null
    at jason.environment.TimeSteppedEnvironment.getPercepts(TimeSteppedEnvironment.java:303)
    at jason.infra.local.LocalAgArch.perceive(LocalAgArch.java:330)
    at jason.asSemantics.TransitionSystem.sense(TransitionSystem.java:1673)
    at jason.infra.local.LocalAgArch.sense(LocalAgArch.java:213)
    at jason.infra.local.LocalAgArch.reasoningCycle(LocalAgArch.java:250)
    at jason.infra.local.LocalAgArch.run(LocalAgArch.java:273)
    at java.base/java.lang.Thread.run(Thread.java:833)

For details, I share some code snippets below. FYI, I borrowed code from Jason's Miners-II example, then simplified it.

MAS2J project

MAS test {
    environment: env.MARLEnv(1000) // timeout in milliseconds
    agents: test_agent #3;
}

ASL code

!start.
/* Plans */
+!start : true <-
   move("down");
   !start.

My env is a grid 5x5

public class SwitchEnvV1 extends TimeSteppedEnvironment {
    public static final int GRID_SIZE = 5;
    private EnvModel model = null;
    private EnvView view = null;

    public SwitchEnvV1(){
        setOverActionsPolicy(OverActionsPolicy.queue);
    }
    @Override
    public void init(String[] args) {
        model = new EnvModel();
        model.setAgPos(0, 0, 0);
        model.setAgPos(1, GRID_SIZE-1, GRID_SIZE-1);
        view  = new EnvView(model);
        model.setView(view);
        clearPercepts(); // clear the percepts of the agents
    }
    @Override
    public boolean executeAction(String ag, Structure action) {
        return  model.dummy_act(ag, action);
    }
    class EnvModel extends GridWorldModel { //GridWorldModel from Jason
        private EnvModel() {
            super(GRID_SIZE, GRID_SIZE, 2);
        }
        synchronized public boolean dummy_act(String agId, Structure action){
            // synchronized as I want to update mode
            System.out.println("["+agId+"] doing: "+action);
            return true;
        }
    }
    class EnvView extends GridWorldView { //GridWorldView from Jason
        @Serial
        private static final long serialVersionUID = 1L;
        public EnvView(EnvModel model) {
            super(model, "SwitchEnvV1", 600);
            defaultFont = new Font("Arial", Font.BOLD, 18);
            setVisible(true);
            repaint();
        }

    }
}

Solution

  • In class SwicthEnvV1, the method init is incorrectly overwritten because in its parent, TimeSteppedEnvironment, request is created inside. Hence, SwicthEnvV1 should mimic the same thing.

    A safer solution is to create a constructor at the child class, then init model and view inside it. For example

    public SwitchEnvV1(){
            // use queue policy when an agent tries more than one action in the same cycle,
            setOverActionsPolicy(OverActionsPolicy.queue);
    
            model = new EnvModel();
            model.setAgPos(0, 0, 0);
            model.setAgPos(1, GRID_SIZE-1, GRID_SIZE-1);
            view  = new EnvView(model);
            model.setView(view);
            clearPercepts(); // clear the percepts of the agents
    
    }