• Looking for something?

Cheating AI from Hardcourt – Code the Plans

Behavior

Plan your behavior

So I have introduced how to plan out simple AI behavior from my previous post – Cheating AI from Hardcourt – Plan it out – if you haven’t, read that one first before this.

Our next step would be writing the data charts into codes. It might look quite easy when you see the simple flow charts but remember that the AI must process this through out the game with a character’s lifespan.

What I’m about to share is simply a psuedo codish (partly actionscript syntax) of the AI’s logic. Eventually you can port it into which ever approach you are comfortable with, like OOP or the old school timeline.

Remember that this is my first published game where as I still used AS2 using prototypes and functions but I’ll discuss this in a little of close objective approach. First of we prepare a few world declarations & preparation in a quick manner.

Prepare the world

In the game world object or whichever the game’s entities reside, we’ll be having a calculateDistance method that pre-calculates distances of each of the characters then stores each of their enemies distance on an enemyDist container object.

// from the game world
 // called within the main loop
function calculateDistances() {
  // pre-calculate distances of:
  //   ball to char - store distance to the character's ballDist variable
  //   char to char - store all other character's (enemy's) distance to character's enemyDist container variable
}

The code below acts as base methods for all characters. This is primarily an advantage from inheritance whereas properties and methods of the base class is inherited by the class that extends it. Basically all the characters will be capable of methods such as moving (moveChar), getting the ball (getBall), shooting it (shoot) and punching their enemies (punch).

  // used to store the ball object also do determine if the we have the ball; set to null if we not
var myBall:Ball;
  // the distance of the ball from character in Vector2D (x, y)
var ballDist:Vector2D;
  // contains enemies distances
var enemyDist:Object;
  // assuming that 25 is the range where the character can punch it's enemies
var punchRange:Number = 25;
 
 // moves the character
function moveChar( xdir, ydir ) {
  //  xdir - move left (-1) or right (1)
  //  ydir - move up (-1) or down (1)
}
 
 // determines if we have the ball else if the ball is close & not owned (by enemy) then get it
function getBall():Object {
  //  use ballDist for the calculated distance;
  //  returns: ball object - if our char already has it
  // 	      null - if the ball is nowhere near
}
 
 // shoots the ball
function shoot() {
  // release the ball towards the ring
  // set myBall to null
}
 
 // attacks the enemy
 //  params: enemy:Object - the enemy instance to punch
function punch( enemy:BaseChar ):Boolean {
  //  returns: true - if successful hit
  //           false - else not
}

Note: for the Vector2D, some might be wondering what it is, well it is a class whose purpose is to calculate Vector operations in two dimensions.

Now we have prepared the initial methods, let’s go with the proposed coding of the flow charts.

Basic Behaviors

The AI controlled character behavior:

  // runs with the main loop for each AI character
function thinkAI(){
  var xdir = 0;
  var ydir = 0;
 
    // if we have the ball
  if ( getBall() ) {
      // then shoot it!!
    shoot();
    // if not
  }else {
      // get the direction of the ball
    xdir = ballDist.x ? ballDist.x / Math.abs( ballDist.x ) : 0;
    ydir = ballDist.y ? ballDist.y / Math.abs( ballDist.y ) : 0;
      // and move towards it
    moveChar( xdir, ydir );
  }
}
Basic Chart - Get & shoot

Basic Chart - Get & shoot

We used the method getBall() to determine if we have the ball or not, if so shoot it. Else, we get the distance from ballDist (which contains the distance in x & y values), have it’s polarity from it’s the absolute value and move towards it.

This is only the first chart (Basic Chart), our AI only considers that in the game world there is only the character himself and the ball, so whenever he gets the ball he shoots it immediately and repeats the process.

Expanding for more Interactions

What we do next is implement the second chart (Interactive Chart) and consider situations with the enemy and interact with it.

So improving the thinkAI() method:

function thinkAI() {
  var xdir = 0;
  var ydir = 0;
 
    // if we have the ball
  if ( getBall() ) {
      // then shoot it!!
    shoot();
    // if not,
  }else {
    var ball = GameWorld.ball;
    var dist:Vector2D;
      // check if another char has the ball, since definitely it isn't this char
    if ( ball.owner ) {
        // store the enemy who has the ball in a local var
      var targetEnemy = enemyDist[ ball.owner.id ];
        // this is the distance of the enemy char with respect to this char
      dist = targetEnemy.dist;
        // if this enemy is within punching range
      if ( dist.len < punchRange ) {
        punch( ball.owner );
          // no need to do the processes below...
        return;
      }
    } else {
      dist = ballDist;
    }
      // get the direction
    xdir = dist.x ? dist.x / Math.abs( dist.x ) : 0;
    ydir = dist.y ? dist.y / Math.abs( dist.y ) : 0;
      // and move towards it
    moveChar( xdir, ydir );
  }
}
Interactive Chart - Steal the ball

Interactive Chart - Steal the ball

After determining that the ball is not in our character’s possession, we check if the ball is owned by another character. As it is owned, it certainly is not our character who has it as we already tested. So from the enemyDist container object (this has the pre-calculated distances of the character’s enemies) we get the enemies distance. See if the enemy is within stealing (ok, punching) range and punch if so, else move towards the enemy.

Now we have considered enemies too, if they have possession of the ball, our AI char simply goes after him and if he is within range we steal/punch.

Take note that when a character is punched he losses the ball so I also call punching stealing the ball. :)

Then we take this into the 2nd chart.

function thinkAI() {
  var xdir = 0;
  var ydir = 0;
 
    // if we have the ball
  if ( getBall() ) {
      // then shoot it!!
    shoot();
      // if not,
  } else {
    var ball = GameWorld.ball;
    var dist:Vector2D;
      // check if another char has the ball, since definitely it isn't this char
    if ( ball.owner ) {
        // store the enemy who has the ball in a local var
      var targetEnemy = enemyDist[ ball.owner.id ];
        // targetEnemy.dist is the distance of the enemy char with respect to this char
      dist = targetEnemy.dist;
        // if this enemy is within punching range
      if ( dist.len < punchRange ) {
        punch( ball.owner );
          // no need to do the processes below...
        return;
      }
    } else {
        // default the distance from the ball
      var dist = ballDist;
      var ds;
        // loop through enemies
      for ( c in enemyDist ) {
        ds = enemyDist[ c ].dist;
          // see if they are closer
        if ( ds.len < dist.len )
          dist = ds;
          // it seems that this enemy is already in range, no need to check the others
        if ( ds.len < punchRange )
          break;
      }
        // if this is not the ball distance then it could only be the enemy
      if ( dist != ballDist ) {
          // if this enemy is within punching range
        if ( dist.len < punchRange ) {
            // attack!!
          punch( ball.owner );
            // no need to do the processes below...
          return;
        }
      }
    }
      // get the direction
    xdir = dist.x ? dist.x / Math.abs( dist.x ) : 0;
    ydir = dist.y ? dist.y / Math.abs( dist.y ) : 0;
      // and move towards it
    moveChar( xdir, ydir );
  }
}
Interactive Chart 2 - Go offensive, attack!

Interactive Chart 2 - Go offensive, attack!

What I added here is for our AI character to consider not only the ball itself and it’s possible owner, but also nearby enemies. Checking through all enemies and determining if they are close enough or within range, therefore stopping them with a punch.

You can also see clearly the advantage of pre-calculating the distances, as I used the enemy and ball distances enough to avoid recalculating them again and again when needed.

Now we have our competitive AI ready! Or not. Feeling like something is missing? Yes, its “difficulty!” Basically as you might have suspected that this AI behavior has the perfect reflex (well not so perfect really). It does what it has to depending on exact situations at the exact time on the next loop.

Example this:

Just at the same moment you grab the ball the enemy would instantly steal the ball or, simply saying, you can’t even run away, your character’s very change in position and angle is updated and read by the AI at the same time or yet exactly at the next frame therefore it knows where exactly you are same way the moment you move anywhere.

And another problem with the perfect reflex is same way we planned it, the moment the AI character grabs the ball he just shoots it at once.

For this problem, a good fix I did was based around with numbers. Yes, “numbers” will help with everything, you can imagine setting a level of intelligence for the AI and how much well it executes it’s task depends on the numbers assigned. But I’ll leave that to another post to discuss about it more…

What do you think? Play hardcourt now! to experience the AI in action.


Leave a Comment


 (not published)


= Sum of 9 + 10 ? If this is your first post, your comment will be moderated.
Please use english and keep it punctual.