Concepts
Introduction

Code
for loop


Game hints
Tug of War
Good code

Lesson 8 - Tug of War

Introduction

Welcome to the final test of your training. Conquer this quest and your training as a Novice will be complete.

Up until now, you have only commanded one Dragon. In this quest, you will learn how to command many Dragons to battle against many enemy Dragons.

for loop

In order to command many dragons, you will need to learn a new statment: the for loop. for loops are handy when you want to run the same code many times. E.g., if you want to run the same code for many Dragons :) For example...

In your old code, since you had only one Dragon, you could always pick the first Dragon:

var myUnits = Helper.getMyUnits(state); // Pick the first Dragon // (first dragon is at index 0 of "myUnits" // second dragon is at index 1, and so on...) var dragon = myUnits[0]; // Do something with "dragon" // ...

But now that you have many Dragons, you can't just pick the first Dragon using myUnits[0] anymore. You have to use the for loop to loop over all your Dragon units. Like this:

var myUnits = Helper.getMyUnits(state); // "myUnits" is an array that can contain many Dragons. // We want to visit each Dragon, using the "for" loop: for (var i=0; i < myUnits.length; i++) { // Pick the next Dragon in the "myUnits" array. // Notice that instead of using myUnits[0] like before, // we now say myUnits[i] // In this "for" loop, "i" starts at 0. // E.g. if you have 3 Dragons, the "for" loop will // execute the code below 3 times, once for each Dragon. // Each time, "i" will automatically change like this: // i = 0 // i = 1 // i = 2 var dragon = myUnits[i]; // Do something with this "dragon" // ... }

Notice that the for loop syntax is made up of 3 parts:
for (var i=0; i < myUnits.length; i++)

Let's break that down...
for ( var i=0; // 1) Start "i" at 0. i < myUnits.length; // 2) Keep going for as long as "i" is less than // than the number of Dragon units // (that'll be 3 Dragons in this Quest). i++) // 3) Add 1 to "i" after each round of the loop // (each round of the for loop is also known // as an "iteration").

Tug of War

Finally, let's upgrade your A.I. to use for loops so that it can control any number of Dragons -- instead of just one.

Look for all the places in the code below with the comment "for-loop added". function captureOrb(state) { var myUnits = Helper.getMyUnits(state); // for-loop added to visit all Dragon units for (var i=0; i < myUnits.length; i++) { var dragon = myUnits[i]; 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); // for-loop added to visit all Dragon units for (var i=0; i < myUnits.length; i++) { // get the next Dragon in the array var dragon = myUnits[i]; // get this 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); // for-loop added to visit all Dragon units for (var i=0; i < myUnits.length; i++) { // get the next Dragon in the array var dragon = myUnits[i]; 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); }

Upload your new A.I. and unleash the full power of your Dragon army! Load up the game, then sit back and admire your beautiful code in action.

Tips for writing Good Code

And now an important lesson. By now, you have seen that code tends to grow over time, because goals change and it's common to need to make your code do more.

Part of being a good coder is learning how to manage your code as it grows. Any time software engineers need to add code, they work hard to tame it so that doesn't get too messy, complex or out of control. This means:

Congratulations, with this quest conquered, you graduate from Novice to Apprentice!

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); // for-loop added to visit all Dragon units for (var i=0; i < myUnits.length; i++) { var dragon = myUnits[i]; 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); // for-loop added to visit all Dragon units for (var i=0; i < myUnits.length; i++) { // get the next Dragon in the array var dragon = myUnits[i]; // get this 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); // for-loop added to visit all Dragon units for (var i=0; i < myUnits.length; i++) { // get the next Dragon in the array var dragon = myUnits[i]; 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>