IndiGolog Basics


This is a guide on the steps I took set-up a IndiGolog Program.

I prefer to divide up the sections of an IndiGolog program into different files. The different sections of an IndiGolog program are:

  1. Exogenous Actions
    1. Contains the code to handle exogenous actions from then environment.
  2. Fluents
    1. Fluents handle the current state of the environment relative to the agent.
  3. Initial Conditions
    1. The initial conditions of the agent
  4. Primitive Actions
    1. The primitive actions of the agent. These are the lowest level actions that the agent can make
  5. Procedures 
    1. The procedures of the agents. Procedures are sequences of primitive actions that are created from knowledge of the Agent.

The exogenous actions code can be small but will grow depending design. For a basic set of exogenous actions this short list could be used.



/ exog_action(reached(Location)).
    Lets the Agent know when it has fully arived at a location it was navigating
to.
/
exog_action(reached()).

/
    catch all to make sure system does not crash
/
exog_action(
).

Fluents are one of the most important parts of the Agents design. fluents are supposed to evolve according to actions that occur. Then these fluents can be used in procedures to make good choices or in any conditions.


% Used to keep track of the state of the Entity
% When the controlled Entity dies this will be set to false.
rel_fluent(alive).
causes_val(died, alive, false, true) :-
    nl, write('Entity died'), nl.


There are different kinds of fluents in IndiGolog rel_fluent and fun_fluent which I believe stand for relational fluent and functional fluent. I am not totally sure what the difference is...


% Will be used to keep track of the inventory of the Entity.
/    hasItem(item, player)
    Use to keep track of the many game items and players with an
    association of players with items.
/
rel_fluent(hasItem(,)).
causes_val(pickup(Item,Player), hasItem(Item, Player), true, true).
causes_val(drop(Item,Player), hasItem(Item, Player), false, hasItem(Item, Player)).
 
% -- causes_true(action,fluent,cond)
% when cond holds, doing act causes relational fluent to hold
% doing this because predicate does not exist
% these need to be defined so that the causes_val will work properly because it is
% defined to use these two predicates in ../../Eval/evalbat.pl
% because of relational fluents
causes_true(action,fluent,false).

causes_false(action, fluent, false).


The initial conditions describe the state of the agents world when the program is started. This section can also be use to test the agent by adjusting initial conditions to inspect of the agent still preforms a reasonable set of actions. 

Some predicates can be defined to help solidify the definition of initial conditions



enemies([valerie, will, amy, player]).
friends([glensnemesis, heather, jim, bob, self]).
isEnemy(Player) :- domain(Player, enemies).
isFriend(Player) :- domain(Player, friends).
isPlayer(Player) :- isFriend(Player); isEnemy(Player). 




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Initial conditions
%% Initial values of fluents
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

initially(alive, true).
initially(hasItem(Item, Player), false) :-
    isItem(Item),
    isPlayer(Player).


initially(name, none).



Primitive actions as said are the lowest actions that an Agent can take. The reason for the primitive actions are to define what is a acceptable action and to define when a particular action is indeed possible in a given situation.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Primitive actions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


prim_action(die).
prim_action(look()).


prim_action(startgoto(
)).



These are preconditions for the primitive actions. These are very important in a search so the search knows that the actions it selects are valid in the situation thet are selected for.


/
    Preconditions for primitive actions
/

poss(check_for_enemy, true).

poss(look(), true).



poss(startgoto(Location), location(Location)). 



Now procedures are more involved than any of the other pieces of code but they are heavily depandant on them. Some can be as simple as,


proc(defendCapturer,
    pi(teammate,
        [?(hasItem(enemyflag, teammate)),
            defend(teammate)
        ]
    )
).


This procedure is to defend the capturer (comes form an agent written to play capture the flag). Essentially it uses pi (pronounced "pick") to choose the teammate of the agent for which the condition hasItem(enemyflag, teammate) is true. Choose the teammate that has the enemyflag and defend that teammate. Pretty awesome for really only 4 lines of code. 

For the functionality of the IndiGolog agent this line needs to be added and is very importand


% THIS IS THE MAIN PROCEDURE FOR INDIGOLOG
proc(main,  mainControl(N)) :- controller(N), !.


When the "main" method is called it will try all of controllers defined for the agent. 

Some examples


/

    This procedures includes parts of the last. This one will loop while

    the Entity is still alive. It will prioritize these action states

    1. If there is enemyContact engage and attack that enemy.

    2. If an enemy is seen follow that enemy.

    3. Otherwise wonder the map.

/

proc(mainControl(2),

%    [goto(home), sleep]

     prioritized_interrupts(

    [

        interrupt(contactedEnemy=glen, attack(
, [target,glen], )),

         interrupt(seenEnemy=glen, follow(
, [target,glen])),

         interrupt(true, pi(start, pi(location, [relocate(start,location)])))

     ]) % end of interupts

).


prioritize interrupts chooses the interrupt for which its condition is true first.


% Controller for the Wumpus:

%    1. If agent knows where the wumpus is, she is in line with it and

%       the wumpus may be alive, then aim to wumpus and shoot arrow

%    2. If agent knows that there is gold in the current square, pick it up

%    3. Otherwise: sense everything and take a randomWalk

proc(mainControl(3),

   prioritized_interrupts(

         [interrupt([dir], and(aliveWumpus=true,

                     in_line(locRobot,dir,locWumpus)), [shoot(dir)] ),

      interrupt(isGold(locRobot)=true, [pickGold]),

      interrupt(inDungeon=true, [smell,senseBreeze,senseGold,

                wndet(newRandomWalk, [goto(loc(1,1)),climb])])

         ])  % END OF INTERRUPTS

).





proc(mainControl(4),

    while(alive, searchForEnemy(glen))

).





proc(mainControl(9),

%    [goto(home), sleep]

     prioritized_interrupts(

    [

        interrupt(and(role=capdef, neg(hasenemyflag=false)), defendCapturer),

        interrupt(and(role=capdef, some([enemy, friend], and(attacking(enemy, friend)=yes, neg(isEnemy(friend))))),

            pi(enemy, attack( , [target, enemy], ))),

        interrupt(and(role=capdef, neg(seeEnemy=false)),    pi(enemy,

            if(bAttackedBy(enemy),

                attack( , [target,enemy], ),

                avoidEnemy))),

        interrupt(role=capdef, startgoto(_, [destination, enemyflag2]))

    ]%,

        %if  % end of if

     )% end of while

).



References:
  1. http://www.cs.toronto.edu/~alexei/ig-oaa/indigolog.htm
  2. http://www.cse.yorku.ca/~lesperan/IndiGolog/
  3. http://sourceforge.net//projects/indigolog/