NorthernStars Mixed-Reality Framework
MR-Framework & Example AI:
This project is maintained by NorthernStars
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 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 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 ) { } }
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.
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()
.
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).