Concepts
Introduction
Orbs

Code
getClosestOrb()
carryOrb()
getClosestBase()
Capture Orb

Game hints
Orb hunter

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)

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)

Helper.getClosestBase()

To capture an Orb that your Dragon has picked up, call this function to find your nearest Base:

Helper.getClosestBase(state, dragon)

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 it captureOrb(). It is always a good idea to make functions to organize your code. This captureOrb() function will execute the following algorithm:
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>