MR-FWNS

NorthernStars Mixed-Reality Framework


MR-Framework & Example AI:

This project is maintained by NorthernStars

Creating an artifical intelligence

This tutorial shows, how to create an artificial intelligence (AI). We mainly use Eclipse for programming. But this tutorial should also work with any other IDE. This tutorial requires downloaded and extracted MR-FWNS-complete archive (download MR-Framework on the left side).

Create project.

Create a new java project and name it however you like, for example MyAI. Set up a source folder and a main package that will contain your AI. Let's say you named your main package default.

Add the following jar libraries from extracted MixedRealityBot to your build path:

Create AI class

Create a new java file with a class inside the default package that extends the class Thread and implements the interface ArtificialIntelligence from essentials.core.
To add all missing functions from extendes class and interface as well as some other variables, use the following template to complete your AI class:

public class MyAI extends Thread implements ArtificialIntelligence {

    BotInformation mSelf = null;
    RawWorldData mWorldState = null;
    Action mAction = null;
    
    boolean mNeedNewAction = true;    
    boolean mIsStarted = false;
    boolean mIsPaused = false;
    
    @Override
    public void initializeAI( BotInformation aOneSelf ) {        
        mSelf = aOneSelf; 
        mIsStarted = true;
        start();        
    }

    @Override
    public void resumeAI() {        
        mIsPaused = false;        
    }
    
    @Override
    public void suspendAI() {        
        mIsPaused = true;           
    }
    
    /**
     * Main function of AI
     */
    public void run(){
        
        RawWorldData vWorldState = null;
        Action vBotAction = null;
        
        while ( mIsStarted ){
            
            while ( mIsPaused ){ try { sleep( 10 ); } catch ( InterruptedException e ) { e.printStackTrace(); } }

            try {             
                if ( mNeedNewAction && mWorldState != null  ){
                    synchronized ( this ) {
                        vWorldState = mWorldState;
                    }
                    
                    // Getting current play mode
                    PlayMode vPlayMode = mWorldState.getPlayMode();
                    
                    // Check for kick off
                    if ( vPlayMode == PlayMode.KickOff
                    		|| (vPlayMode == PlayMode.KickOffYellow && mSelf.getTeam() == Teams.Yellow)
                    		|| (vPlayMode == PlayMode.KickOffBlue && mSelf.getTeam() == Teams.Blue) ){
                    	
                    	// --------------- KICK OFF ---------------
                    	// --------------- KICK OFF END ---------------
                    	
                    }
                    // No kick off
                    else {
                    	
                    	// --------------- START AI -------------------	                    
                    	// ---------------- END AI --------------------
                    
                    }
                    
                    // Set action
                    synchronized ( this ) {
                        mAction = vBotAction;
                        mNeedNewAction = false;
                    }                  
                }
                Thread.sleep( 1 );                
            } catch ( Exception e ) {
                e.printStackTrace();
            }            
            
        }
        
    }

    
    @Override
    public synchronized Action getAction() {
        synchronized ( this ) {
            if( mAction != null)
                return mAction;
        }
        return (Action) Movement.NO_MOVEMENT;        
    }

    @Override
    public void putWorldState( WorldData aWorldState) {
        synchronized ( this ) {
        	if( aWorldState instanceof RawWorldData ){
        		mWorldState = (RawWorldData) aWorldState;
        	}
            if( aWorldState != null && mWorldState.getReferencePoints() != null && !mWorldState.getReferencePoints().isEmpty() ){            
            	mNeedNewAction = true;
            } else {
            	mNeedNewAction = false;
            }
        }        
    }

    @Override
    public void disposeAI() {        
        mIsStarted = false;
        mIsPaused = false;        
    }
    
    @Override
    public boolean isRunning() {
        return mIsStarted && !mIsPaused;        
    }

	@Override
	public boolean wantRestart() {
		return false;
	}

    @Override
    public void executeCommand( String arg0 ) {
    }

}     		

Dealing with play modes

The template above does nothing. The right place to add an intelligent behavior is after

synchronized ( this ) {
    vWorldState = mWorldState;
}

inside the surrounding if statement. The template above already contains some stuff there. It gets the current play mode (PlayMode vPlayMode = mWorldState.getPlayMode();).
The play mode could be a value from enum PlayMode from package essentials.communication.worlddata_server2008. Currently two cases are implemented. The first one occurs if the play mode is a general KickOff, KickOffYellow or KickOffBlue.

if ( vPlayMode == PlayMode.KickOff
	|| (vPlayMode == PlayMode.KickOffYellow && mSelf.getTeam() == Teams.Yellow)
	|| (vPlayMode == PlayMode.KickOffBlue && mSelf.getTeam() == Teams.Blue) )

You can add your own behavior between the two comments KICK OFF and KICK OFF END.

For example add the following lines:

if ( vWorldState.getBallPosition() != null ){
	vBotAction = MoveLib.runTo( vWorldState.getBallPosition()  );
}
else {
	vBotAction = MoveLib.runTo( vWorldState.getFieldCenter() );
}
During kick off this makes the agent running to the current ball position, if there's a ball or to run to the fields center, if there's no ball.

Main behavior

The else statement after the kick off section contains the behavior if the play mode isn't a kick off. Here you can add an behavior between the comments START AI and END AI. So add the following code:

// check if ball is available
if ( vWorldState.getBallPosition() != null ){
	                    	
	// get ball position
	BallPosition ballPos = vWorldState.getBallPosition();
		                    	
	// check if bot can kick
	if ( ballPos.getDistanceToBall() < mSelf.getGamevalue( GamevalueNames.KickRange ) ){                 
		// kick
		ReferencePoint goalMid = PositionLib.getMiddleOfGoal( vWorldState, mSelf.getTeam() );
		vBotAction = KickLib.kickTo( goalMid ); 
	
	} else {
		// move to ball
		vBotAction = MoveLib.runTo( ballPos );
	}	 
	                    	
}

The first if statement checks if there's a ball. If there's no ball the agent does nothing. If there's a ball the current ball position is saved in the variable ballPos.

The next if statement checks if the agent can kick the ball. For that the distance to the ball is compared to a game value called KickRange. The kick range is the range the ball has to be within so that the agent can kick the ball.

To get a game value the object mSelf storing information about the current agent is used. All available game values are stored in the enum GamevalueNames from package essentials.core. The function mSelf.getGamevalue() gets a specific value.

So if the agent can kick the ball, it retrieves the middle of the enemies goal using the function PositionLib.getMiddleOfGoal() that requires the current world model, stored in vWorldState and the team of the current agent. After the agent got the position of the goal middle it kicks the ball into that direction using the function KickLib.kickTo().

If the agent couldn't kick the ball it moves toward the balls position using the function MoveLib.runTo().

Agents origin

The current world model stored inside vWorldState (using the template above) has it's origin in the middle of the current agent. So the center of the world for a robot is itself. Other objects in the world like several reference points like goal poles, field center and so on are described in polar coordinate space using an angle and a distance to the object. The agent always views toward zero degree. You can also use cartesian coordinates by calling the function getXOfPoint() and getYOfPoint on any reference point. Doing this the cartesian coordinates are calculated so it's recommended to use polar coordinate space as much as possible (sometimes it's not and therefor you can use catersian coordinates).

Back