Lesson 7 - Orbs
Introduction
Now that you're getting the hang of coding, things are going to get interesting :) This Quest introduces a new element to the Arena: Orbs.Orbs
Orbs are juicy bundles of pure energy that all Dragons need. They are essential to the Dragon life-cycle.Whenever Orbs are in play in the Arena, the team that captures the most Orbs wins. To capture an Orb, a Dragon flies to the Orb, picks it up and returns it to a friendly Base.
Dragons fly slower when carrying an Orb. This makes Orb-carriers more vulnerable to attacks. If a Dragon is defeated while carrying an Orb, it is released and can be captured by another Dragon.
Helper.getClosestOrb()
To find the closest orb to a Dragon, call this function:
Helper.getClosestOrb(state, dragon)
- Pass this function the
state
from yourrun
function and one of your Dragons - It will return you the closest Orb.
Example:
// get Orb closest to Dragon
var closestOrb = Helper.getClosestOrb(state, dragon);
// move to Orb
Game.moveTo(dragon.id, closestOrb.orb.pos.x, closestOrb.orb.pos.y);
Game.carryOrb()
To pick up an orb, call this function:
Game.carryOrb(unitID)
- This orders your Dragon to pick up an Orb if it is close enough. If there are no Orbs close by, this function does nothing.
Helper.getClosestBase()
To capture an Orb that your Dragon has picked up, call this function to find your nearest Base:
Helper.getClosestBase(state, dragon)
- Pass this function the
state
from yourrun
function and one of your Dragons - It will return you the closest Base.
- You can then call
Game.moveTo()
to fly to the Base. - The Orb will automatically be captured when your Dragon flies over the Base.
Example:
// get Base closest to Dragon
var closestBase = Helper.getClosestBase(state, dragon);
// move to Base
Game.moveTo(dragon.id, closestBase.base.pos.x, closestBase.base.pos.y);
Capture Orb
Let's combine all the actions above into a neat function and call itcaptureOrb()
. It is always a good idea to make functions to organize your code.
This captureOrb()
function will execute the following algorithm:
- If there are enemies in the Arena, do not chase Orbs. Attack enemies.
- If there are no enemies in the Arena, move Dragon to the closest Orb and pick it up.
- If Dragon is carrying an Orb, return to closest Base.
The way to know if your Dragon is carrying an Orb is by checking its
cargo
property with an if
statement. For example...
if (dragon.cargo)
{
// this Dragon is carrying an Orb
}
else
{
// this Dragon is not carrying an Orb
}
Now let's write our
captureOrb()
function...
function captureOrb(state)
{
var myUnits = Helper.getMyUnits(state);
var dragon = myUnits[0];
if (!dragon.cargo)
{
// Dragon is not carrying anything. Check for enemies...
var enemyFound = Helper.getClosestEnemy(state, dragon);
if (!enemyFound)
{
// No enemies in sight. Safe to search for Orbs :)
var orbFound = Helper.getClosestOrb(state, dragon);
if (orbFound)
{
// found Orb!
var orb = orbFound.orb;
// move to Orb
Game.moveTo(dragon.id, orb.pos.x, orb.pos.y);
// pick it up
Game.carryOrb(dragon.id);
}
}
}
else
{
// Dragon is already carrying an Orb. Find closeset Base...
var baseDist = Helper.getClosestBase(state, dragon);
var base = baseDist.base;
// return to base
Game.moveTo(dragon.id, base.pos.x, base.pos.y);
}
}
Orb hunter
Let's put it all together and update your Dragon A.I.!We'll also take this chance to tidy up your existing code by making new functions for
attack
and advanceOrRetreat
.Real code can get complex. Professional coders have to always try hard to keep their code tidy and organized. Good code is easy to read, makes sense, doesn't grow out of control and is easy for fellow coders to work on.
function captureOrb(state)
{
var myUnits = Helper.getMyUnits(state);
var dragon = myUnits[0];
if (!dragon.cargo)
{
// Dragon is not carrying anything. Check for enemies...
var enemyFound = Helper.getClosestEnemy(state, dragon);
if (!enemyFound)
{
// No enemies in sight. Safe to search for Orbs :)
var orbFound = Helper.getClosestOrb(state, dragon);
if (orbFound)
{
// found Orb!
var orb = orbFound.orb;
// move to Orb
Game.moveTo(dragon.id, orb.pos.x, orb.pos.y);
// pick it up
Game.carryOrb(dragon.id);
}
}
}
else
{
// Dragon is already carrying an Orb. Find closeset Base...
var baseDist = Helper.getClosestBase(state, dragon);
var base = baseDist.base;
// return to base
Game.moveTo(dragon.id, base.pos.x, base.pos.y);
}
}
function attack(state)
{
// get array of all my units
var myUnits = Helper.getMyUnits(state);
// get the first Dragon in the array
var dragon = myUnits[0];
// get first Dragon's ID
var dragonId = dragon.id;
// get this Dragon's closest enemy
var enemyFound = Helper.getClosestEnemy(state, dragon);
if (!enemyFound)
{
// no enemies around to attack
return;
}
// get enemy Dragon's ID
var enemyId = enemyFound.unit.id;
// attack!
Game.attack(dragonId, enemyId);
}
function advanceOrRetreat(state)
{
// get array of all my units
var myUnits = Helper.getMyUnits(state);
// get the first Dragon in the array
var dragon = myUnits[0];
var dragonId = dragon.id;
var enemyFound = Helper.getClosestEnemy(state, dragon);
if (!enemyFound)
{
// no enemies to attack or retreat from
return;
}
var enemy = enemyFound.unit;
// Choose offense or defense.
// Decide to move Dragon toward enemy or away from enemy
if (isShieldEnergyLow(dragon))
{
// shield is low, retreat!
var escapePos = Helper.computeRetreatPoint(dragon.pos, enemy.pos);
Game.moveTo(dragon.id, escapePos.x, escapePos.y);
}
else
{
// move Dragon to enemy Dragon's position
// (but only if enemy is too far away)
if (enemyFound.dist > 350)
{
// move toward enemy -- it's more than 350 units away
Game.moveTo(dragonId, enemy.pos.x, enemy.pos.y);
}
}
}
function isShieldEnergyLow(dragon)
{
if (dragon.maxShields == 0)
{
// this Dragon has no shields at all
return true;
}
// compute percent shield energy remaining
var shieldPct = dragon.shields / dragon.maxShields * 100;
// if shield energy is less than 20%,
// say that shields are low
if (shieldPct < 20)
return true;
// shields are greater than 20%,
// say that shields are not low
return false;
}
function run(state)
{
advanceOrRetreat(state);
attack(state);
captureOrb(state);
}
Note that your
run
function is still the main function that's called on each round. Your run
function then calls the 3 new functions we created: advanceOrRetreat
, attack
and captureOrb
.
Phew! HIGH FIVE! Give yourself a hug and a pat on the back for making this far. It's okay to feel a little frazzled at this point because we have learned a lot. What you're learning here is similar to a college-level introductory programming course. And don't worry, even the best professional engineers question themselves on a regular basis.
So don't sell yourself short! Remind yourself that you have just learned a new language. That is a mighty feat. And like any new language, it takes time to become fluent. You should feel proud that you can now read code and understand most of what's going on. Compare that to before you started this and you didn't know any of this. See, you've made big progress and you're doing great!
Alright, after this Quest, you'll have learned almost all you need to play real Games in the Arena with your Dragon. But not yet. Get ready, the next Quest will be your first real Test.
Cheatcode
Spoiler alert! Click to reveal full solution.
<!DOCTYPE html>
<html>
<body>
<div id="display">hello</div>
<script>
function captureOrb(state)
{
var myUnits = Helper.getMyUnits(state);
var dragon = myUnits[0];
if (!dragon.cargo)
{
// Dragon is not carrying anything. Check for enemies...
var enemyFound = Helper.getClosestEnemy(state, dragon);
if (!enemyFound)
{
// No enemies in sight. Safe to search for Orbs :)
var orbFound = Helper.getClosestOrb(state, dragon);
if (orbFound)
{
// found Orb!
var orb = orbFound.orb;
// move to Orb
Game.moveTo(dragon.id, orb.pos.x, orb.pos.y);
// pick it up
Game.carryOrb(dragon.id);
}
}
}
else
{
// Dragon is already carrying an Orb. Find closeset Base...
var baseDist = Helper.getClosestBase(state, dragon);
var base = baseDist.base;
// return to base
Game.moveTo(dragon.id, base.pos.x, base.pos.y);
}
}
function attack(state)
{
// get array of all my units
var myUnits = Helper.getMyUnits(state);
// get the first Dragon in the array
var dragon = myUnits[0];
// get first Dragon's ID
var dragonId = dragon.id;
// get this Dragon's closest enemy
var enemyFound = Helper.getClosestEnemy(state, dragon);
if (!enemyFound)
{
// no enemies around to attack
return;
}
// get enemy Dragon's ID
var enemyId = enemyFound.unit.id;
// attack!
Game.attack(dragonId, enemyId);
}
function advanceOrRetreat(state)
{
// get array of all my units
var myUnits = Helper.getMyUnits(state);
// get the first Dragon in the array
var dragon = myUnits[0];
var dragonId = dragon.id;
var enemyFound = Helper.getClosestEnemy(state, dragon);
if (!enemyFound)
{
// no enemies to attack or retreat from
return;
}
var enemy = enemyFound.unit;
// Choose offense or defense.
// Decide to move Dragon toward enemy or away from enemy
if (isShieldEnergyLow(dragon))
{
// shield is low, retreat!
var escapePos = Helper.computeRetreatPoint(dragon.pos, enemy.pos);
Game.moveTo(dragon.id, escapePos.x, escapePos.y);
}
else
{
// move Dragon to enemy Dragon's position
// (but only if enemy is too far away)
if (enemyFound.dist > 350)
{
// move toward enemy -- it's more than 350 units away
Game.moveTo(dragonId, enemy.pos.x, enemy.pos.y);
}
}
}
function isShieldEnergyLow(dragon)
{
if (dragon.maxShields == 0)
{
// this Dragon has no shields at all
return true;
}
// compute percent shield energy remaining
var shieldPct = dragon.shields / dragon.maxShields * 100;
// if shield energy is less than 20%,
// say that shields are low
if (shieldPct < 20)
return true;
// shields are greater than 20%,
// say that shields are not low
return false;
}
function run(state)
{
advanceOrRetreat(state);
attack(state);
captureOrb(state);
}
Game.register(run);
</script>
</body>
</html>