Backwoods Brawl in Python - Error "isPathClear is not defined"


#1

Ok . this is one of the few things stopping a win for Backwoods Brawl 4. The code doesn’t error out all of the time - just on some weird spots on the edge. I’m just going to include the relevant “run-away” code. It is one of those maddening sporadic errors which do not always occur. What I suspect is that self.isPathClear(s,t) produces an error when one of the coordinates for s or t is out of bounds … but I can’t prove it. :wink:

Separate point/possible backdoor solution - went looking for Python exception handling and it appears the “try:” method is not supported. Is there a way within the code to catch and handle the exception? (Do I need an item to use that method? … Currently, Programmaticon IV equiped.)

---
#This function tests 9 paths to make sure the path from tarA to self.pos are clear, checking 1m in each direction from the character's center and the destination center:
def SafePath(tarA):
    vecA = Vector.subtract(tarA, self.pos)
    # This creates a 90° rotated, 2 m long vector relative to vec.
    normV = Vector.multiply(Vector.normalize(Vector.rotate(vecA, 1.5708)), 2)
    targetL = [tarA,   Vector.add(tarA, normV),   Vector.subtract(tarA, normV)]
    startL  = [self.pos, Vector.add(self.pos, normV), Vector.subtract(self.pos, normV)]
    isFree = True
    for s in startL:            #Testing the 3 positions around the self.pos
        for t in targetL:        #Testing the 3 positions around the tarA
            if s and t:
                isFree = isFree and self.isPathClear(s, t)    #this is the line which gives errors sometimes
            else:
                isFree = False
    return isFree

#Moves 1 step towards tarA.  If the path to tarA is obstructed, rotates around 28 degrees and tries again.
def SafeMove(tarA):
    tarLoc = tarA
    cAngle = 0
    adjAngle = 0.5
    while (not( SafePath(tarLoc)) and (cAngle < 2*Math.PI)):
        vecA = Vector.normalize(Vector.subtract(tarLoc, self.pos))
        vecB = Vector.normalize(Vector.rotate(vecA, adjAngle))
        tarLoc = Vector.add(self.pos, vecB)
        cAngle = cAngle + adjAngle
    self.move(tarLoc)
    
loop:
    break
   
loop:
    enemies = self.findEnemies()
    MoveVector = Vector(0,0) 
   #This next loop calculates the destination Vector based on the weighted sum of each enemy vector.  And yes, I do have an additional 28 degree turn added. 
    for enemy in enemies:
        weight = enemy.health / (self.distanceTo(enemy)^2)
        eVector = Vector.multiply(Vector.rotate(Vector.subtract(self.pos, enemy.pos),0.5),weight)
        MoveVector = Vector.add(MoveVector, eVector)
    tarA = Vector.add(self.pos, Vector.multiply(Vector.normalize(MoveVector),2))
    SafeMove(tarA)

Sir Robin Bravely Ran Away - looked for a more elegant answer and received a class in using Vector
#2

I think it’s likely that try/catch in Python aren’t supported, given our transpiler. Sorry!

Can you look in the JavaScript console to see if there’s any more info as to why it thinks isPathClear is not defined? I recall some other weird error happening where it really meant that something else wasn’t defined, but it decided to say isPathClear was it, so you might be onto something with a problem with s or t.


#3

God.coffee:70 Anshar: Let there be light upon Backwoods Brawl! (preload: false)
Angel.coffee:55 |Anshar’s Cheryl| Generated random seed 1840587932 of type submissionCount from sessionIDs 560fe05d08aafaa724f3a114 submissionCount 9
Angel.coffee:55 |Anshar’s Cheryl| Assigned power 160 to wave initial with difficulty 5
Angel.coffee:55 |Anshar’s Cheryl| Assigned power 480 to wave throughout with difficulty 5
(program):7 |Anshar’s Cheryl| Assigned power 1600 to wave punishers with difficulty 5
(program):7 |Anshar’s Cheryl| Assigned power 30 to wave cosmicBackgroundMunchkinRadiation with difficulty 5
(program):7 |Anshar’s Cheryl| Loaded 27 of 900 (+1099ms)
(program):7 |Anshar’s Cheryl| Loaded 78 of 900 (+1006ms)
(program):7 |Anshar’s Cheryl| Loaded 181 of 900 (+1045ms)
(program):7 |Anshar’s Cheryl| Loaded 279 of 900 (+1083ms)
(program):7 |Anshar’s Cheryl| Hero Placeholder had new Programmable problem: plan Line 17: isPathClear is not defined 19.8
(program):7 |Anshar’s Cheryl| The world ended in lost on frame 404
(program):7 |Anshar’s Cheryl| Loaded 376 of 404 (+1054ms)
(program):7 |Anshar’s Cheryl| And it was so: (14.250ms per frame, 404 frames)
Simulation : 5757ms
Serialization: 51ms
Delivery : 80ms
(program):1381 No frames were changed out of all 404
https://analytics.codecombat.com/analytics Failed to load resource: net::ERR_CONNECTION_TIMED_OUT
app.js:517 Analytics post failed!(anonymous function) @ app.js:517u @ jquery.js:3099c.fireWith @ jquery.js:3211i @ jquery.js:8266(anonymous function) @ jquery.js:8599
https://analytics.codecombat.com/analytics Failed to load resource: net::ERR_CONNECTION_TIMED_OUT
app.js:517 Analytics post failed!


#4

I did a few tests to see if I can reproduce the problem. Under normal circumstances there seems to be no issue: e.g. self.isPathClear(self.pos, Vector(1234.567, -987.654)) returns false as expected, etc.

However, when I tested my escape algorithm (which worked flawlessly on Backwoods Brawl, because it’s perfect :wink:) on a more crowded level: Backwoods Treasure, after a few seconds I received the same error message as @Msakr: isPathClear is not defined. In the console output I also found this:
Hero Placeholder had new Programmable problem: plan Line 27: isPathClear is not defined 75.33333333333333

So I tested this: self.isPathClear(self.pos, 12345)
Same error message, similar console output:
Hero Placeholder had new Programmable problem: plan Line 1: isPathClear is not defined 0

So it looks like that in the algorithm sometimes, somehow a vector is misinterpreted. The strange thing is that there are absolutely only vectors in it… My piece of code is:

    while not self.isPathClear(self.pos, newPos):
        fleeVec = Vector.rotate(fleeVec, angle)
        newPos = Vector.add(self.pos, fleeVec)
        if angle > 0: angle *= -1
        else: angle = angle * -1 + angleAdj

How could we help to debug this?

update: I adjusted my code - but not the fleeing function! - and now I don’t get the error anymore. Poor slow Tharin tries to outrun the ogre hordes, and valiantly fails after about 30 seconds…

update 2: I suspect that the while function may be the source of the issue - in both of our examples the isPathClear is part of a while condition.

@Msakr: try to replace it with a loop and an if ... break


#5

Output of self.debug(self.now(), self.pos, newPos) at the critical time:

34.333333333333336 {x: 30.72, y: 18.19, z: 1.00} {x: 38.81, y: 24.07, z: 1.00}
34.4 {x: 30.80, y: 18.72, z: 1.00} {x: 38.55, y: 25.04, z: 1.00}
34.46666666666667 {x: 31.02, y: 19.24, z: 1.00} {x: 38.68, y: 25.67, z: 1.00}
34.53333333333333 {x: 31.06, y: 19.77, z: 1.00} {x: NaN, y: NaN, z: 1.00}
Hero Placeholder had new Programmable problem: plan Line 33: isPathClear is not defined 34.53333333333333

So suddenly my newPos vector becomes undefined. I’ll try to find more details…

update: OK, I found the source of my problem: while calculating my newPos I use weighting based on the enemy type. However, this weight is not calculated, but stored in a dictionary. At the critical moment a new enemy appears, which is not defined in my dictionary… thus, the weight will be None and so my newPos becomes undefined. So it turns out that it’s not a problem of isPathClear but a coding issue :pensive:


#6

@Msakr: I debugged your code. In your main loop, the weight becomes infinity at a certain time

        weight = enemy.health / (self.distanceTo(enemy)^2)
        if self.now() > 9: self.debug(enemy.health, self.distanceTo(enemy), weight)

The output of the above shows:

14 7.835243960629522 2.8
14 9.317114691760876 1.2727272727272727
14 10.250228335667611 1.75
250 10.525626666473913 31.25
14 2.641386929406519 Infinity
[health   distance    weight]

At this moment your vector coordinates become infinity and you get the isPathClear error.

However, the strange thing is that if you do the math, it’s far from infinity:
14 / (2.64..)^2 = 2.00..

If the distance would be very small or zero, I could understand the issue. But this way it just doesn’t work out…


So started to strip down the code…

…and found the real issue!

        weight = enemy.health / (self.distanceTo(enemy)^2)

Now look closer:

^2

What’s wrong with it? In python the ^ operator is a bitwise xor!

What you need is the ** operator (power):
#**2

Tadadadaaa!!! :clap: :smile:


But why would an xor operation result in “infinity” still escapes my mind… any idea, @nick?


#7

Wow … Much thanks for the heavy lifting! I would never have guessed that was my problem.

Incidentally, the key fix was to make the weight equal to enemy.health/(1+self.distanceTo(enemy)**2) as that avoids completely the risk that the distance would be too close to zero… and computers hate division by something close to zero.