Level: Library Tactician help


As you can see, the default code on Library Tactician doesn’t work proberly with the glasses im wearing.

The only thing i’ve added is the commandArcher() in line 50.

Am i overseeing anything or is it just a bug?

This is the hole code:

# Hushbaum has been ambushed by ogres!
# She is busy healing her soldiers, you should command them to fight!
# The ogres will send more troops if they think they can get to Hushbaum or your archers, so keep them inside the circle!

# Soldiers spread out in a circle and defend.
def commandSoldier(soldier, soldierIndex, numSoldiers):
    angle = Math.PI * 2 * soldierIndex / numSoldiers
    defendPos = {"x": 41, "y": 40}
    defendPos.x += 10 * Math.cos(angle)
    defendPos.y += 10 * Math.sin(angle)
    self.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.
def findStrongestTarget():
    mostHealth = 0
    bestTarget = None
    enemies = self.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 and bestTarget.health > 15:
        return bestTarget
    else:
        return None


# If the strongestTarget has more than 15 health, attack that target. Otherwise, attack the nearest target.
def commandArcher(archer):
    nearest = archer.findNearestEnemy()
    if archerTarget:
        self.command(archer, "attack", archerTarget)
    elif nearest:
        self.command(archer, "attack", nearest)

archerTarget = None
loop:
    # If archerTarget is dead or doesn't exist, find a new one.
    if not archerTarget or archerTarget.health <= 0:
        # Set archerTarget to be the target that is returned by findStrongestTarget()
        archerTarget = findStrongestTarget()

    friends = self.findFriends()
    soldiers = self.findByType("soldier")
    for i in range(len(soldiers)):
        soldier = soldiers[i]
        commandSoldier(soldier, i, len(soldiers));

    # use commandArcher() to command your archers
        commandArcher()
def commandArcher(archer):
    # much code removed

# use commandArcher() to command your archers
    commandArcher()

You aren’t commanding an archer: commandArcher(anArcherGoesHere)

not sure if i misunderstood you @Vlevo , but whenever i add archer into the commandArcher() at the bottom, so it goes: commandArcher(archer), it just says it not defined.

well, that would be because you never defined an archer…
you have code to define a soldier:

    soldiers = self.findByType("soldier")
    for i in range(len(soldiers)):
        soldier = soldiers[i]
        commandSoldier(soldier, i, len(soldiers));

but you have no similar code to define the archer…

right… my bad, just tunneled too hard on the explanation text, thx alot for your help ^^ @Vlevo

still the findnextenemy code doesnt work and we cant change classes. So i could put in the next enemy to me, but currentcy this is not enough to win the level.

Is it correct that ALL humans have to survive?

I think I got the archers to attack next enemy if there is no besttarget but I still cant get more than 55 seconds. If i smaller the circle down to 6 or 8 its get better. I also summon new soldiers, but no idea what to do now… :frowning:

Yes, if a single human dies, you fail the level. You have to focus fire correctly, and if this isn’t enough maybe you’re unlucky on the random seed. Try to send it several times. It could help if you think your code is correct.

    // Hushbaum has been ambushed by ogres!

this.summonSoldier = function() {
if(this.gold >= this.costOf("soldier"))
this.summon("soldier");
};

// She is busy healing her soldiers, you should command them to fight!
// The ogres will send more troops if they think they can get to Hushbaum or your archers, so keep them inside the circle!
// 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 += 8 * Math.cos(angle);
    defendPos.y += 8 * 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(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) {
    var enomies = this.findEnemies();
        var nearest = null;
        var near = 999999;
    for(k=0;k<enomies.length;k++){
        if (archer.distanceTo(enomies[k])< near){
            near =archer.this.distanceTo(enomies[k]);
            nearest = enomies[k];
        }
        

    if(archerTarget && archer) {
        this.command(archer, "attack", archerTarget);
    } else if(nearest && archer) {
        this.command(archer, "attack", nearest);
    }
}
};

var archerTarget = null;
loop {
  this.summonSoldier();
    // 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.commandSoldier(soldier, i, soldiers.length);
    }
    // use commandArcher() to command your archers
    var archers = this.findByType("archer");
    for(var j=0; j < archers.length; j++) {
        var archer = archers[j];
        this.commandArcher(archer[j]);
    }

    
}

Am I right that the mountain levels are very new and there could be bug. I think in this level the aim is to teach the student functions and in this level how to let the archers focus right.

So i think the not working findnextenemy function is on purpose. I want to suggest a correction here.

Im also not sure if I should change the commandsoldier function and if I should summon new soldiers.

I have now three creative solutions that could help me beat this level, but Im not sure how far it is intended to let the stundent going here. I read in another thread that they made the opponents more difficult, because someone was winning with the basic code. Maybe this was too much of a change and its now to hard.

If creativity is wanted by codecombat me as a student would really appreciate a small tip, because in some level creativity is not wanted and just the correct way to do it and if you use creativity in these levels. There are barriers.

I hope you get what i want -> a clearer task description. If you need creativity here or if its just we help you to learn new things of levels.

So my 3 creative solutions would be.

attack the nearest opponent to the center, not the nearest to every archer. I could use simple algebra here if its not possible to get the nearest enemy to Hushbaum
2)
let all archers attack the same guy always
3)
attack the nearest enemy to the weakest friend.
(4)) get weak soldiers out of the circle into the center.

Ok for 1 i got this code:

var enomies = this.findEnemies();
var nearest = null;
var near = 999999;
for(k=0;k>enomies.length;k++){
    // a2 + b2 = c2 c
    a = (enomies[k].pos.x - 41) * (enomies[k].pos.x - 41) + (enomies[k].pos.y - 40) *(enomies[k].pos.j - 40) ;
    if (a<near){
        near = a;
        nearest = enomies[k];
    }
}

It basically leads to the same result as before, but distanceto to was leading to an error so i could not summon new soldiers.

im not trying to build archers instead of soldiers, but my just summoned archer just moves and doesnt change to attack.

why?

EDIT:

I made it. //I think I should not post the solution or?

I don’t think so. Your post bothered me about the non working function, so I tried 3 things :

1 ) Rerun the code I wrote to check it was working fine.
2 ) Tried to restard level and redo it as if it was the first time I played it.
3 ) Tried your code.

First of all : the function

var nearest = archer.findNearestEnemy();

DOES work. Maybe it’s a problem of glasses, but I’m not even sure about it.

Second of all … Why are you even summoning units ?

Third : Your code isn’t actually running as is. I had to read it carefully to debug it myself, and that was somewhat painful. Add spaces and format it better for readibility.

First problem :

for(var j=0; j < archers.length; j++) {
    var archer = archers[j];
    this.commandArcher(archer[j]); // archer is a variable, not an array.
}

Second problem :

near =archer.this.distanceTo(enomies[k]); // What does *this* has to do in this line.

I know it’s a typo, but you should at least debug before calling something not working. After that, no need to even summon anybody. And I’m fairly sure your problem with findNearestEnemy was also a typo.

var nearest = archer.findNearestEnemy();
DOES work. Maybe it's a problem of glasses, but I'm not even sure about it.

It is, because I have to use a pair of glasses that dont support findnextenemy

for(var j=0; j < archers.length; j++) {
var archer = archers[j];
this.commandArcher(archer[j]); // archer is a variable, not an array.
}

Ok i get this one and corrected it. Thanks.

near =archer.this.distanceTo(enomies[k]); // What does *this* has to do in this line.

Nah its not a typo, but i was not sure if i had enemy anywhere else and while coding fast i was choosing any name for the array.
The this. was always suggested by the editor. Anyway I changed it to:

 enomies = this.findEnemies();
    var nearest = null;
    var near = 999999;
    for(k=0;k>enomies.length;k++){
        // a2 + b2 = c2 c
        a = (enomies[k].pos.x - 41) * (enomies[k].pos.x - 41) + (enomies[k].pos.y - 40) *(enomies[k].pos.y - 40) ;
        if (a<near){
            near = a;
            nearest = enomies[k];
        }
    }

to get the nearest enemy to the center.

Im sorry. Its my first time asking for help in a coding forum as im just starting coding :wink:

It was a try to win the level. And it improved my time.

So ok thanks to your suggestion I can now win the level without summarizing units and without changing the radius of the defend circle. Before I used the function correct to smaller the circle was improving my time.

And to summarize it my first solution was to complex as the task in this level is just to use array + function correct:

I won first with:

  • smaller the circle to 8
  • use a character that can shoot with a good weapon
  • summon more units
  • use algebra to find nearesttocenterenemy

And the simpler solution is:

  • give the function the right arguments
    - correct the findnextenemy part.

And even you helped me a lot and this lvl was way easier than i thought i still think the findnextenemy stuff is a bug as I have to use a glass that does not support findnextenemy

Thanks anyway for your help. I think I can take away for me:
First debug your code than try creativity :slight_smile:

One last question:

Did i understand it right that you always have to create a variable first before you use it in a loop, because you should not write var xx multiply times. And there should always be just one definition of the object and then just a change of values?

1 Like

Good job. Sorry my post of yesterday wasn’t as nice as I should have been. Have a good day :smile:

I dont mind. Mine was a bit of whining so I do understand :slight_smile:

The thing with findNearestEnemy is a difference between you (this) and your units (“soldier/archer/etc”)

You, may have glasses that don’t support findNearestEnemy anymore, but that doesn’t mean that they have the same equipment that you do, so to speak…

this.findNearestEnemy() errors for you (because of your cool glasses) but works fine for them (only as far as they can see of course).
archer.findNearest(archer.findEnemies()) may error for them (I believe it used to, I haven’t tried it in a while).

I am receiving the error about not having the proper equipment to use findNearestEnemy() even when it is called ‘by’ the archer.

# If the strongestTarget has more than 15 health, attack that target. Otherwise, attack the nearest target.
def commandArcher(archer):
    nearest = archer.findNearestEnemy()
    if archerTarget:
        self.command(archer, "attack", archerTarget)
    elif nearest:
        self.command(archer, "attack", nearest)

and when I change it to

nearest = archer.findNearest(self.findEnemies())

I’m receiving a “cannot read property ‘findNearest’ of null” which I wasn’t expecting for a declaration.

can anyone shed light on this, I hate to drag an old topic up but this is frustrating the hell outta me.

When you call commandArcher, do you have an input argument? If not, then archer would be null, which would explain your problem.

I’m using archerTarget as defined earlier in the loop’s default code… which is probably the problem, it’s calling on the enemy instead of the archer. back to the drawing board!

that did the trick, thanks for the quick response!

Can someone explain to me why this code does not work? It says the archers don’t have that function, Is there another way to gets friends to find enemies to attack?

Code:


Please help.

When you pass archer, “archer” is only an array. TO return an OBJECT, which you want in this case: Make sure you do archer[i]

1 Like