Zone of danger - A little help :)


#1

Hi,

I’m sorry if is not the right category to open this ticket.

I’m kind of blocked in the Zone of danger level… it seems so easy as a level but i’m having hard time with it :frowning:

I don’t want the solution I just don’t know why my variable don’t have any value in them

My DistanceTo code :


var xx=Math.pow(target.pos.x - this.pos.x,2);
var yy=Math.pow(target.pos.y - this.pos.y,2);
return Math.sqrt(xx+yy);


My getNearestEnemy code :


var enemies = this.getEnemies();
var nearestEnemy = null;

// TODO: Find the nearest enemy with the distanceTo method.
for ( var i = 0; i<= enemies.length;i++) {
var a = this.distanceTo(enemies[0]);
var b = this.distanceTo(enemies[1]);
var c = this.distanceTo(enemies[2]);
var d = this.distanceTo(enemies[3]);
var e = this.distanceTo(enemies[i]);
}

//while(enemies!==null){
//for( var i =0;i <= enemies.length;i++){

// if(this.distanceTo(enemies[i])>=this.distanceTo(enemies[i+1])){

// OR

// if(this.distanceTo(enemies[i]<=this.distanceTo(enemies[i+1])){

// OR

// if(this.distanceTo(enemies[i]<=this.attackRange){

  //  nearestEnemy = enemies[i];

   //return nearestEnemy;

  //  

   // }else {

   //  nearestEnemy = enemies[j];   

   //  return nearestEnemy;

  //  }

//}

//}


when i debug , my a, b, c, d and e are undefined ? do you have any idea why ?

Thanks,
Omar,


#2

Each time you loop through enemies, you are accessing the 0th, 1st, 2nd, 3rd, and ith entries in the array, but most of the time there aren’t that many enemies (you can hover over enemies to see how many are in there), so some of those are undefined. You need to make sure that you don’t try to find this.distanceTo an undefined value, usually by only accessing enemies[i] in each iteration through the loop. Yet you still need to be able to compare the distance of all the enemies in the enemies array. A puzzle!

It’s not actually that easy a level–it’s one of the hardest we have so far, made more as a challenge than a learning level, which is why it’s not in the beginner campaign.


#3

It’s funny, I spent an hour looking at that and while my own solution was working, if I copy yours I can reproduce the problem and I think I now understand the problem more precisely:
you cannot call distanceTo() with different values in very short order. I’m not entirely sure how close is minimum, but I looped through enemies[i] and it was flawless, but if I just call distanceTo() without any loop two times after another with different arguments I get this problem.
This is something @nick should look into. Seems like the distanceTo() function is “too slow”.

Anyway, a “hack” for now would be to not call it with different targets too fast. (Spoiler; Better try Nick’s hint) The code you commented out shows why you got that problem in the first place comparing the distance of target i with i+1.
Try for example looping through all enemies and using an if clause to compare the distance of i to a variable and when the distance if target i smaller is than that variable, then replace that variable by the smaller number.

By the way, I noticed that if you have an array with (for example) 5 elements you have index numbers 0-4, but array.length will give you a 5.your for loop stops at “i <= array.length” which means that it will run for i = 5. put differently enemies[enemies.length] is always undefined.


#4

I’m almost certain that there’s a bug in this level.

Specifically, the first shot at the flying enemy always misses, with a “whoosh” sound effect. There is no explanation for this, and it doesn’t happen with any other unit. Because of this (I assume, I can’t test without this condition), my tower always takes a hit from that flyer before the second shot actually kills it, and because my tower isn’t full, it dies from a single attack of the final ogre.

There’s no way to kill that flier before it attacks me, even if I add a bunch of special cases in the code.

I even tried using the code from distanceTo directly in getNearestEnemy, because apparently this level is an optimisation exercise and I figured avoiding the method call might make it faster. Nope!

// getNearestEnemy()

var enemies = this.getEnemies();
var enemiesLength = enemies.length;
var nearestEnemyDistance = 25;
var result = null;

for (var i = 0; i < enemiesLength; i++)
{
    var enemy = enemies[i];
    if (enemy)
    {
        var distance = this.distanceTo(enemy);
        
        if (distance < nearestEnemyDistance)
        {
            nearestEnemyDistance = distance;
            result = enemy;
        }
    }
}

return result;

// distanceTo(target)

return Math.sqrt(Math.pow(target.pos.x - this.pos.x, 2) + Math.pow(target.pos.y - this.pos.y, 2));

#5

Seriously, how am I supposed to optimize a method without knowing what any of the operations cost?

At some point I even managed to kill all of the ogres, but my tower died at the same time as the last ogre. I don’t even know how to change my code back to that. I spent like an hour shotgun debugging this, no amount of special cases or ugly hacks seems to change anything.

The code I just posted is correct and by all rights it should solve the level, unless there are some really important explanations missing.


#6

Same problem here. I added a few optimizations wrt your code:

  • no need to evaluate any distance if enemiesLength is < 2;
  • no need to evaluate the square root in the distanceTo - it’s a monotonic function so it doesn’t change the order.

Still, I get killed by the last ogre (who dies in turn).

Perhaps the “shoot-the-nearest” strategy is wrong?
I also tried to always return “Dreek” (the flying thing) as the nearest enemy whenever it is within attack range, but it still manage to strike once before I kill it…

P.S. I paste my code here as I don’t low how to save it:

var enemies = this.getEnemies();
var nearestEnemy = enemies[0]; // it only fails if there are
                               // no enemies, so who cares!
var l = enemies.length;
if (l == 1) return nearestEnemy;

var nearestDist = this.distanceTo(nearestEnemy);
// begin from 1, don't compare element 0 with itself!
for (var i=1; i<l; i++) {
    var cur = this.distanceTo(enemies[i]);
    if (cur < nearestDist) {
        nearestEnemy = enemies[i];
        nearestDist = cur;
    }
}
return nearestEnemy;

#7

Hah! Thanks for the optimisations, I managed to get back to killing that final ogre (at the same time as he kills my tower).

no need to evaluate the square root in the distanceTo - it’s a monotonic function so it doesn’t change the order.

You’re right, and for that matter we don’t even need to square the components:

// distanceTo(target)

return Math.abs(target.pos.x - this.pos.x) + Math.abs(target.pos.y - this.pos.y);

Unless we need to compare w/ our tower’s range (even then, we can probably compensate), but in any case I don’t see a use for range-checking. That damn chooseAction() method always shoots even if you don’t supply a target.

Perhaps the “shoot-the-nearest” strategy is wrong?

I think it’s the “shoot-even-if-there-aren’t-targets-in-range” strategy that’s wrong, it probably wastes time between attacks, but unfortunately it’s hardcoded.

I also tried to always return “Dreek” (the flying thing) as the nearest enemy whenever it is within attack range, but it still manage to strike once before I kill it…

I maintain that this is a bug, our solutions would work if it wasn’t for that inevitable hit from the Dreek. Why does the first attack make a “whoosh” sound? This doesn’t happen w/ any other unit, even when you target units outside of the tower’s range.

Uh… it even makes that “whoosh” sound if you prevent the tower from ever targeting Dreek (he dies anyway from it shooting at the ogre behind it???)

EDIT:

I can’t post anymore in this thread, apparently, but thanks for the fix, Nick!

Thanks for explaining the “whoosh” sound, too. Is there anything like a guide to the monster/unit types, for players? Could be useful for this kind of situation where we’re not sure how a unit is supposed to work.


#8

Looks like something changed ever so slightly in the combat simulation in our game engine. Your code should be able to solve the level, but the last ogre proved slightly too tough to kill. I’ve lowered his health by 10% and now it’s winnable again.

The whoosh noice is Dreek’s spear sound as he throws. He is supposed to get one hit in on the tower, yes.


#9

I finally got this level and I don’t really know what fixed it.

var enemies = this.getEnemies();
var nearestEnemy = null;

// TODO: Find the nearest enemy with the distanceTo method.
if (enemies.length == 1 ) return enemies[0];
if(enemies.length > 1){
    nearestEnemy = enemies[0];
    var nearest = this.distanceTo(enemies[0]);
    for(var i=1; i<enemies.length; ++i){
        if(this.distanceTo(enemies[i]) < nearest){
            nearestEnemy = enemies[i];
            nearest = this.distanceTo(enemies[i]);
        }
    }
}

return nearestEnemy;

return Math.abs(target.pos.x - this.pos.x) + Math.abs(target.pos.y - this.pos.y);

The only thing that gave me trouble (I think) was that instead of using math.abs( ) I used
return ((target.pos.x - this.pos.x) * (target.pos.x - this.pos.x)) + ((target.pos.y - this.pos.y) * (target.pos.y - this.pos.y));
but before that I also did a restart. So I’m not really sure if it was that.

Hope this helps someone else if they get stuck at this level and can’t figure out whats wrong with it.


#10

It looks like something may be broken in Zone of Danger. If you put the following in the distanceToTarget method, it blows up with a “TypeError: tmp12 is undefined”

return Math.abs(target.pos.x - this.pos.x) + Math.abs(target.pos.y - this.pos.y);

I’ve tried other ways of doing it with Vectors and these too blow up with similar errors.


#11

Hmm, I’m not seeing the same problem, Wayne. Are you sure you’re passing a Thang with a pos to the distanceTo(target) method?


#12

Isn’t it faster to use “for in” construct?
like this:

var enemies = this.getEnemies();
if (enemies.length == 1 ) { return enemies[0] }
var nearestEnemy = enemies[0];

// TODO: Find the nearest enemy with the distanceTo method.
for(var enemy_index in enemies) {
    if (this.distanceTo(enemies[enemy_index]) < this.distanceTo(nearestEnemy)) {
        nearestEnemy = enemies[enemy_index];
    }
}
return nearestEnemy;

#13

Nope, it’s tremendously slower: http://jsperf.com/fastest-array-loops-in-javascript/206

Using for-in loops to iterate over arrays is not recommended.


#14

So I am a year 10 student and studying javascript at the moment using code combat as help. I was able to complete the beginnert levels easily as well as a few of the harders ones however this one I found I was really stuck on. Is there any chance someone could explian to me the code for this level and how it works?


#15

@ott0003 the idea is that you need to find the enemy with the smallest distance from this. You can use a for-loop to loop over each enemy in the this.getEnemies() array. So for each enemy, you find the distance with this.distanceTo(enemy).

You then compare the distance with the closest distance you’ve seen so far. If this enemy is closer, then you store it in a variable, and you can also store the new closest distance.

After you have looped through every enemy, you return the closest enemy you have seen.

You also need to implement the distanceTo method to calculate the distance between two points.

So yeah, it’s pretty tricky, and we haven’t really helped you learn for-loops, variables, or if-statements yet. Playing some of the player-created levels would help you there until we can make a longer beginner campaign: http://codecombat.com/play


#16

Nick, thanks for your answer and the links!! I guess being a long time python programmer gives me to much of a pythonic thinking… I have a lot to learn about javascript :wink: