Library Tactician Dec '15/ Apr '21

Here is Version 2 of my code:

`// Soldiers spread out in a circle and defend.
this.commandSoldier = function(soldier, soldierIndex, numSoldiers) {
    var angle = Math.PI * 2 * soldierIndex / numSoldiers;
    var defendPos = {x: 41, y: 40};
    defendPos.x += 10 * Math.cos(angle);
    defendPos.y += 10 * Math.sin(angle);
    this.command(soldier, "defend", defendPos);
};`

// Find the strongest target (most health)
// This function returns something! When you call the function, you will get some value back.
this.findStrongestTarget = function() {
    var mostHealth = 0;
    var bestTarget = null;
    var enemies = this.findEnemies();
    // Figure out which enemy has the most health, and set bestTarget to be that enemy.

    // Only focus archers' fire if there is a big ogre.
    if (bestTarget && bestTarget.health > 15) {
        return bestTarget;
    } else {
        return null;
    }
};


// If the strongestTarget has more than 15 health, attack that target. Otherwise, attack the nearest target.
this.commandArcher = function(archer) {
    var nearest = archer.findNearest(archer.findEnemies());
    if(archerTarget) {
        this.command(archer, "attack", archerTarget);
    } else if(nearest) {
        this.command(archer, "attack", nearest);
    }
};

var archerTarget = null;
loop {
    // If archerTarget is dead or doesn't exist, find a new one.
    if(!archerTarget || archerTarget.health <= 0) {
        // Set archerTarget to be the target that is returned by findStrongestTarget()
        archerTarget = this.findStrongestTarget();
    }
    var friends = this.findFriends();
    var soldiers = this.findByType("soldier");
    for(var i=0; i < soldiers.length; i++) {
        var soldier = soldiers[i];
        var librarian = this.findByType("librarian");
        var distance = soldier.distanceTo(librarian);
        if (distance >= 5){
            this.command(soldier, "move", librarian.pos);
        }
        else {
            this.commandSoldier(soldier, i, soldiers.length);
        }
    }
    // use commandArcher() to command your archers
    var archers = this.findByType("archer");
    for (var a=0; a < archers.length; a++) {
        this.commandArchers(archer, a, archers.length);
    }
}

Everything is still wrong, with soldiers running away, and now archers are getting killed by throwers.

  1. In this.findStrongestTarget you have not wrote the code as hinted by the comments. You need to iterate through the enemies array and find the strongest enemy. This is a commonly used algorithm:
for every element in the list
    if is better than what i have
           replace what i have with what i  found
// Figure out which enemy has the most health, and set bestTarget to be that enemy.
for(var i=0;i<enemies.length;i++)
    if(enemies[i].health > mostHealth){
        mostHealth=enemies[i].health;
        bestTarget=enemies[i];
    }
  1. Before calling this.commandArchers(archer, a, archers.length) you forgot the set up the value of archer.
    Look how you set up the value of solider before in the code above
    Function name and number of parameter are also wrong: this.commandArcher(archer)
Solution
archer=archers[i];
this.commandArcher(archer);
  1. the function this.commandSoldier(soldier, i, soldiers.length); does everything you need to do with the soldier
    Get rid of the if(distance >= 5) blocks and just leave this.commandSoldier(soldier, i, soldiers.length);

See what you did for the archers: no if there.

Okay, Mk.3 of my code: now it’s “Fix your code: try this.findEnemies”

// Soldiers spread out in a circle and defend.
this.commandSoldier = function(soldier, soldierIndex, numSoldiers) {
    var angle = Math.PI * 2 * soldierIndex / numSoldiers;
    var defendPos = {x: 41, y: 40};
    defendPos.x += 10 * Math.cos(angle);
    defendPos.y += 10 * Math.sin(angle);
    this.command(soldier, "defend", defendPos);
};

// Find the strongest target (most health)
// This function returns something! When you call the function, you will get some value back.
this.findStrongestTarget = function() {
    var mostHealth = 0;
    var bestTarget = null;
    var enemies = this.findEnemies();
    // Figure out which enemy has the most health, and set bestTarget to be that enemy.
    for(var i=0;i<enemies.length;i++)
        if(enemies[i].health > mostHealth){
            mostHealth=enemies[i].health;
            bestTarget=enemies[i];
    }
    // Only focus archers' fire if there is a big ogre.
    if (bestTarget && bestTarget.health > 15) {
        return bestTarget;
    } else {
        return null;
    }
};


// If the strongestTarget has more than 15 health, attack that target. Otherwise, attack the nearest target.
this.commandArcher = function(archer, archerIndex, numArchers) {
    var nearest = archer.findNearest(archer.findEnemies());
    if(archerTarget) {
        this.command(archer, "attack", archerTarget);
    } else if(nearest) {
        this.command(archer, "attack", nearest);
    }
};

var archerTarget = null;
loop {
    // If archerTarget is dead or doesn't exist, find a new one.
    if(!archerTarget || archerTarget.health <= 0) {
        // Set archerTarget to be the target that is returned by findStrongestTarget()
        archerTarget = this.findStrongestTarget();
    }
    var friends = this.findFriends();
    var soldiers = this.findByType("soldier");
    for(var i=0; i < soldiers.length; i++) {
        var soldier = soldiers[i];
        this.commandArcher(soldier, i, soldiers.length);
    }
    // use commandArcher() to command your archers
    var archers = this.findByType("archer");
    for (var a=0; a < archers.length; a++) {
        var archer = archers[i];
        this.commandArcher(archer, a, archers.length);
    }
}

You must learn to debug you code.

  1. Check the error message and scroll to your code to find the red marked line on which the error occurs.

  2. Try to understand what the compiler complains about. Here it complains that a variable does not have a valid value

  3. Try to understand what could have caused it. Did you forgot to give a value to that variable?
    Did you misspelled its name? Is there a conditional statement (if, for …) that is not executing were it should ? …

  4. Look at the other code you have. Chances are that you have written code to solve a similar problem many times already. What are the differences between the code with the error and your similar but correct code?

  5. Insert a debug message just before the instruction with error.
    For example, does the compiler complains that enemy is not valid?
    Put an this.say(enemy); just before. Your hero will say the value of the variable and you can confirm if it is correct or not. If not, follow up the code and see where you last assigned the value. Put another say there. And so on …

It says “Cannot read property ‘findEnemies’ of undefined”, on the var nearest = archer.findNearest(archer.findEnemies()); part in the commandArcher area.

I believe the archer should be able to use archer.findNearestEnemy();
No need to nest two different functions.

Imperial, lets debug:
"Cannot read property ‘findEnemies’ of undefined"
means archer is undefined so it can’t find the property archer.findEnemies of it

  1. who is archer? We go up from the error point, line by line until we find the last time we have given a value to it. It is the 1st parameter of the function:
this.commandArcher  = function(archer, archerIndex, numArchers)
  1. Where you have called this function? Now we have to search all our code. Luckly, we have called the function only from the last line of your main loop:
this.commandArcher(archer, a, archers.length);

You have called it with** archer** as 1st parameter so now we have to look for the value of archer in the main loop. If you have called it with:
this.commandArcher(sneakyGuy, a, archers.length)
then we would have to look for sneakyGuy from now on.

  1. Who is archer? We look again just above:
archer = archers[i];

So now we need to see who is archers and i

  1. Who they are? Well, the last time you have given a value to i is in the loop over the soldiers values:
for(var i=0; i < soldiers.length; i++)

Since you exited this loop this means i=solders.length
So:

archer= archers[soldiers.length];

Now if the archers list is smaller than the soldiers list: (archers.length < soldiers.length) then
archers[soldier.slength] will not be in the list and you’ll get an error.

For “BrokenSyntax”'s question, it shows up as tmp98[tmp99] is not a function
For “AdrianCgw”: in the for loop, yes, I did use i for soldiers.length, but I used a for archers.length.

Please take a picture of what items you have equipped.

a is never used to get the archer. For all purpose is like the for bellow does not exits at all.
Shortly you either use a in both lines or i in both.

for (var a=0; a < archers.length; a++) {
        var archer = archers[i];
```

throwers are sooo anoying:angry:

As for Serg:


Do you also happen to know how I can get a profile picture similar to yours?
For Steven_Kang2003: Yes. Yes they are.
For AdrianCgw: So I would change var archer = archers[i]; to var archer = archers[a];?

Thanks, was making sure you had suitable glasses and the like.

  1. If you are seeing Throwers, you are not properly commanding your archers/soldiers. A thrower spawns if an ogre ‘targets’ an Archer or Husbaum.
  2. The required code is highlighted when you ‘reload’ the code. The comments above the arrows explain what you should need to do.
  3. Number of lines to write to beat the level: 9
  4. Code you need to write to beat the level: 2 for loops. One to command the archers, one to find the highest health enemy.

• You don’t need to rewrite the commandArcher function. Soldiers need the extra arguments to position correctly in a circle. Archers don’t need that.

• You have archers[i] instead of archers[a] may be why you’re seeing undefined errors. Look at the value of i at the end of the soldier loop. You are trying to access the archer at archers[8] which is undefined.

So I would reload the code?

THIS LEVEL IS PURE EVIL :imp:

If you change archers[i] into archers[a] you will finish the level.
But yet, it is recommended that you really understand the solution.
So after you win the level, save the solution in a file, reload the level code and try to write the solution again.

If you pass levels without really understanding the fundamentals, at some point you will became really stuck.
You can’t wing it anymore and the only change of progress is going back and going through all the levels again

ok then… here goes…

I’ve changed it to archers[a], but now var nearest = archer.findNearestEnemies(); is not a function.

archer.findNearestEnemy()

i think i cheated i just built two archers and i won first try :grin: