Infinite loop error (Python)


#1

I don’t understand the infinite loop error and here what it look like:
35 PM
If anyone know what it mean can you please tell me.
Here is my code:

def castinvisibility():
    if hero.canCast("invisibility"):
        hero.cast("invisibility", hero)
def flag():
    flag = hero.findFlag("green")
    if flag:
        hero.pickUpFlag(flag)
def commandArcher():
    friends = hero.findFriends()
    archers = (friend for friend in friends if friend.type == "archer")
    patrolCenter = {'x': hero.pos.x, 'y': hero.pos.y}
    patrolTimeScale = 0.40
    archerrs = (friend for friend in friends if friend.type == "archer")
    radius = 2 + 2.5 * Math.log1p(len(archers)) 
    angleStep = Math.PI * 2 / len(archers)
    for index, archer in enumerate(archers):
        enemy = archer.findNearestEnemy()
        if enemy and archer.distanceTo(enemy) <= 25:
            hero.command(archer, "attack", enemy)
        else:
            hero.command(archer, "move",
                {'x': patrolCenter.x + radius * Math.cos(angleStep * index - hero.time * patrolTimeScale),
                'y': patrolCenter.y + radius * Math.sin(angleStep * index - hero.time * patrolTimeScale)})
def commandPaladin():
    friends = hero.findFriends()
    paladins = (friend for friend in friends if friend.type == "paladin")
    patrolCenter = {'x': hero.pos.x, 'y': hero.pos.y}
    patrolTimeScale = 0.30
    archerrs = (friend for friend in friends if friend.type == "paladin")
    radius = 2 + 2.5 * Math.log1p(len(paladins)) 
    angleStep = Math.PI * 2 / len(paladins)
    for index, paladin in enumerate(paladins):
        enemy = paladin.findNearestEnemy()
        if enemy and paladin.distanceTo(enemy) <= 10:
            hero.command(paladin, "attack", enemy)
        else:
            hero.command(paladin, "move",
                {'x': patrolCenter.x + radius * Math.cos(angleStep * index - hero.time * patrolTimeScale),
                'y': patrolCenter.y + radius * Math.sin(angleStep * index - hero.time * patrolTimeScale)})
loop:
    enemy = hero.findNearestEnemy()
    castinvisibility()
    flag()
    commandArcher()
    if enemy and enemy.type == "shaman":
        hero.scattershot(enemy)
    if enemy and hero.distanceTo(enemy) <= 30:
        hero.scattershot(enemy)
    patrolCenter = {'x': hero.pos.x, 'y': hero.pos.y}
    patrolTimeScale = 0.60 # set to 0.0 if you don't want the soldiers to walk around while in the circle
    summonTypes = ["archer", "archer", "paladin", "paladin", "soldier", "soldier", "soldier", "soldier", "soldier", "soldier", "soldier", "archer", "archer", "archer", "archer"]
    summonType = summonTypes[len(hero.built) % len(summonTypes)]
    if hero.gold >= hero.costOf(summonType):
        hero.summon(summonType)
    friends = hero.findFriends()
    soldiers = (friend for friend in friends if friend.type == "soldier")
    radius = 2 + 2.5 * Math.log1p(len(soldiers)) 
    angleStep = Math.PI * 2 / len(soldiers)
    for index, soldier in enumerate(soldiers):
        enemy = soldier.findNearestEnemy()
        if enemy and soldier.distanceTo(enemy) <= 15:
            hero.command(soldier, "attack", enemy)
        else:
            hero.command(soldier, "move",
                {'x': patrolCenter.x + radius * Math.cos(angleStep * index - hero.time * patrolTimeScale),
                'y': patrolCenter.y + radius * Math.sin(angleStep * index - hero.time * patrolTimeScale)})
    for friend in hero.findFriends():
        if friend.type == "paladin":
            enemy = hero.findNearestEnemy()
            if (friend.canCast("heal")) and hero.health < hero.maxHealth:
                self.command(friend, "cast", "heal", self)
            elif (friend.canCast("heal")) and hero.health == hero.maxHealth and soldier and soldier.health < soldier.maxHealth:
                self.command(friend, "cast", "heal", soldier)
            elif (friend.canCast("heal")) and hero.health == hero.maxHealth and friend and friend.health < friend.maxHealth:
                self.command(friend, "cast", "heal", friend)
            else:
                commandPaladin()

Here is my gear:
22 PM
The level that I am playing is Cavern Survival.


#2

@Minh You’re way ahead of me, but maybe it has to do with the archerrs variable that appears in both commandArchers() and commandPaladin(), both pointing to the same units. If I’m following your code right (no guarantees there), it could be you could delete that line from both functions; maybe it was a leftover from a different idea or a cut and paste?


#3

Thank for helping I am trying it now.


#4

Work yay thank @Shmoogy.


#5

Great! A pleasure!

(20 char)


#6

Do you understand it?


#7

To be perfectly honest, not really.

I had an instinct that the archerrs were the problem, due to being referenced identically in the two functions, but I can’t explain it. I mean, archerrs wasn’t even called throughout the code, so I don’t know why it doesn’t just remain a local variable inside each function, not communicating with the other. I hope someone else can offer some insight.


#8

Hi @Shmoogy I just edit my code a little bit and I got a new error and here is my code:

def castinvisibility():
    if hero.canCast("invisibility"):
        hero.cast("invisibility", hero)
def flag():
    flag = hero.findFlag("green")
    if flag:
        hero.pickUpFlag(flag)
def commandArcher():
    friends = hero.findFriends()
    archers = (friend for friend in friends if friend.type == "archer")
    patrolCenter = {'x': hero.pos.x, 'y': hero.pos.y}
    patrolTimeScale = 0.40
    archerrs = (friend for friend in friends if friend.type == "archer")
    radius = 2 + 2.5 * Math.log1p(len(archers)) 
    angleStep = Math.PI * 2 / len(archers)
    for index, archer in enumerate(archers):
        enemy = archer.findNearestEnemy()
        if enemy and archer.distanceTo(enemy) <= archer.attackRange:
            hero.command(archer, "attack", enemy)
        else:
            hero.command(archer, "move",
                {'x': patrolCenter.x + radius * Math.cos(angleStep * index - hero.time * patrolTimeScale),
                'y': patrolCenter.y + radius * Math.sin(angleStep * index - hero.time * patrolTimeScale)})
def commandPaladin():
    friends = hero.findFriends()
    paladins = (friend for friend in friends if friend.type == "paladin")
    patrolCenter = {'x': hero.pos.x, 'y': hero.pos.y}
    patrolTimeScale = 0.30
    archers = (friend for friend in friends if friend.type == "paladin")
    radius = 2 + 2.5 * Math.log1p(len(paladins)) 
    angleStep = Math.PI * 2 / len(paladins)
    for index, paladin in enumerate(paladins):
        enemy = paladin.findNearestEnemy()
        if enemy and paladin.distanceTo(enemy) <= 10:
            hero.command(paladin, "attack", enemy)
        else:
            hero.command(paladin, "move",
                {'x': patrolCenter.x + radius * Math.cos(angleStep * index - hero.time * patrolTimeScale),
                'y': patrolCenter.y + radius * Math.sin(angleStep * index - hero.time * patrolTimeScale)})
loop:
    enemy = hero.findNearestEnemy()
    castinvisibility()
    flag()
    commandArcher()
    if hero.pos.x != 51 and hero.pos.y != 69 and hero.isReady("blink"):
        hero.blink(Vector(51, 69)
    elif hero.pos.x != 51 and hero.pos.y != 69:
        hero.moveXY(51, 69)
    if enemy and enemy.type == "shaman":
        hero.scattershot(enemy)
    elif enemy and hero.distanceTo(enemy) <= 30:
        hero.scattershot(enemy)
    patrolCenter = {'x': hero.pos.x, 'y': hero.pos.y}
    patrolTimeScale = 0.60
    summonTypes = ["archer", "archer", "archer", "archer", "paladin", "paladin", "soldier", "soldier", "soldier", "soldier", "soldier", "soldier", "soldier", "archer", "archer", "archer", "archer"]
    summonType = summonTypes[len(hero.built) % len(summonTypes)]
    if hero.gold >= hero.costOf(summonType):
        hero.summon(summonType)
    friends = hero.findFriends()
    soldiers = (friend for friend in friends if friend.type == "soldier")
    radius = 2 + 2.5 * Math.log1p(len(soldiers)) 
    angleStep = Math.PI * 2 / len(soldiers)
    for index, soldier in enumerate(soldiers):
        enemy = soldier.findNearestEnemy()
        if enemy and soldier.distanceTo(enemy) <= 15:
            hero.command(soldier, "attack", enemy)
        else:
            hero.command(soldier, "move",
                {'x': patrolCenter.x + radius * Math.cos(angleStep * index - hero.time * patrolTimeScale),
                'y': patrolCenter.y + radius * Math.sin(angleStep * index - hero.time * patrolTimeScale)})
    for friend in hero.findFriends():
        if friend.type == "paladin":
            enemy = hero.findNearestEnemy()
            if (friend.canCast("heal")) and hero.health < hero.maxHealth:
                self.command(friend, "cast", "heal", self)
            elif (friend.canCast("heal")) and hero.health == hero.maxHealth and soldier and soldier.health < soldier.maxHealth:
                self.command(friend, "cast", "heal", soldier)
            elif (friend.canCast("heal")) and hero.health == hero.maxHealth and archer and archer.health < archer.maxHealth:
                self.command(friend, "cast", "heal", archer)
            elif (friend.canCast("heal")) and hero.health == hero.maxHealth and friend and friend.health < friend.maxHealth:
                self.command(friend, "cast", "heal", friend)
            else:
                commandPaladin()

Error:
20 PM
I think the error is coming from these lines:

if hero.pos.x != 51 and hero.pos.y != 69 and hero.isReady("blink"):
    hero.blink(Vector(51, 69)
elif hero.pos.x != 51 and hero.pos.y != 69:
    hero.moveXY(51, 69)

Also if you know what the error mean can you explain it to me please?


#9

Yes! I got it, after going over things carefully a few times. You’re smack on - in the little excerpt of code you put on the bottom, because you thought it was suspicious, look closely at the line under the if; you’re missing a ) at the end. I’m guessing (and hoping someone who gets the guts of the language will help out) that since the parentheses never closes, Python doesn’t register that the if is there at all, so it looks like the next line, the elif doesn’t have an if with it.


#10

Thank you soooooo much it work.

I feel soo dumb that I miss that when I’m way ahead of you.:joy:


#11

@Minh, don’t feel bad. Typos happen to us all. One of the books I’m using in conjunction talks about the values of having someone else look at your code, no matter who they are.

I think part of getting better at a language is getting comfortable with the knowledge that little mistakes are inevitable, accepting that and working with it, as opposed to believing in some Ideal Programmer who Will Never Typo, whom bugs Will Not Come Near. But, like I said, I’m just a beginner.


#12

Programming is easier when the code is organized… although sometimes that means starting over from scratch.


Readability Remodeling (using your code)
# insert a blank line between each function

# added this function, as it appears several times throughout your original code.
# bookmark link: https://discourse.codecombat.com/t/circular-soldier-patrol/12138/4
# example pseudocode usage:
# if enemy is sighted and within attack range, then command units to attack enemy
# else do circularPatrol(units)
def circularPatrol(units, targetPos=hero.pos, clockwise = False, timeScale=0.3):
    radius = 2 + 2.5 * Math.log1p(len(units)) # you might want to fiddle with this.
    angleStep = Math.PI * 2 / len(units)
    for index, unit in enumerate(units):
        angle = angleStep * index + (-1)**(clockwise+1) * hero.time * timeScale
        hero.command(unit, "move", {
            'x': targetPos.x + radius * Math.cos(angle),
            'y': targetPos.y + radius * Math.sin(angle)
        })

def castinvisibility():
    if hero.canCast("invisibility"):
        hero.cast("invisibility", hero)

# add descriptions to a function if you think you are going to forget what it does.
# example description for flag():
# "If I place a green flag on the field, the hero will run there and pick it up"
def flag():
    flag = hero.findFlag("green")
    if flag:
        hero.pickUpFlag(flag)

# another example description:
# This commands all friendly archers.
# They attack enemies that get too close to the hero.
# Otherwise, they patrol around the hero.
def commandArcher(): # maybe rename this to commandArchers()? Since we are dealing with more than 1 archer at a time
    friends = hero.findFriends()
    archers = (friend for friend in friends if friend.type == "archer")
    
    # new code added:
    enemy = hero.findNearestEnemy()
    if enemy and hero.distanceTo(enemy) < 30:
        for archer in archers:
            hero.command(
                archer,
                # notice how the arguments to this function are each on their own separate line?
                "attack",
                archer.findNearestEnemy() # so archers all won't target a single enemy and perform overkill (which wastes time)
            )
    else:
        circularPatrol(
            units = archers,
            targetPos = hero.pos,
            clockwise = True,
            timeScale = 0.40,
        ) # take note of this closing ')' -- it is on the same indentation level as "circularPatrol("
    
    return      # everything else written in the function below this line will never get executed ####################################################
    hey = "this doesn't execute"
    
    # this old code is redundant; you can delete it.
    patrolCenter = {'x': hero.pos.x, 'y': hero.pos.y}
    patrolTimeScale = 0.40
    archerrs = (friend for friend in friends if friend.type == "archer")
    radius = 2 + 2.5 * Math.log1p(len(archers)) 
    angleStep = Math.PI * 2 / len(archers)
    for index, archer in enumerate(archers):
        enemy = archer.findNearestEnemy()
        if enemy and archer.distanceTo(enemy) <= archer.attackRange:
            hero.command(archer, "attack", enemy)
        else:
            hero.command(archer, "move",
                {'x': patrolCenter.x + radius * Math.cos(angleStep * index - hero.time * patrolTimeScale),
                'y': patrolCenter.y + radius * Math.sin(angleStep * index - hero.time * patrolTimeScale)})

# skipping function:
# commandPaladin()
# you can rewrite it yourself

loop:
    enemy = hero.findNearestEnemy()
    castinvisibility()
    flag()
    commandArcher()
    
    # perhaps wrap this section into a function? like heroAction()
    if hero.pos.x != 51 and hero.pos.y != 69 and hero.isReady("blink"):
        hero.blink(Vector(51, 69))
    elif hero.pos.x != 51 and hero.pos.y != 69:
        hero.moveXY(51, 69)
    if enemy and enemy.type == "shaman":
        hero.scattershot(enemy)
    elif enemy and hero.distanceTo(enemy) <= 30:
        hero.scattershot(enemy)
    
    # unneccessary, refer to the case inside commandArcher()
    patrolCenter = {'x': hero.pos.x, 'y': hero.pos.y}
    patrolTimeScale = 0.60
    
    # I would personally put the summonTypes array outside of the loop
    # perhaps even at the top of the program
    summonTypes = ["archer", "archer", "archer", "archer", "paladin", "paladin", "soldier", "soldier", "soldier", "soldier", "soldier", "soldier", "soldier", "archer", "archer", "archer", "archer"]
    
    # can be wrapped into a function. Maybe named it trySummon()?
    # if so, you need to put the summonTypes array above it.
    summonType = summonTypes[len(hero.built) % len(summonTypes)]
    if hero.gold >= hero.costOf(summonType):
        hero.summon(summonType)
    
    # make another function, like commandArcher(), but for soldier ... --> commandSoldiers()
    friends = hero.findFriends()
    soldiers = (friend for friend in friends if friend.type == "soldier")
    radius = 2 + 2.5 * Math.log1p(len(soldiers)) 
    angleStep = Math.PI * 2 / len(soldiers)
    for index, soldier in enumerate(soldiers):
        enemy = soldier.findNearestEnemy()
        if enemy and soldier.distanceTo(enemy) <= 15:
            hero.command(soldier, "attack", enemy)
        else:
            hero.command(soldier, "move",
                {'x': patrolCenter.x + radius * Math.cos(angleStep * index - hero.time * patrolTimeScale),
                'y': patrolCenter.y + radius * Math.sin(angleStep * index - hero.time * patrolTimeScale)})
    
    # fit the rest of this code into commandPaladin()
    for friend in hero.findFriends():
        if friend.type == "paladin":
            enemy = hero.findNearestEnemy()
            
            # unnecessary parentheses: (friend.canCast("heal")) --> friend.canCast("heal")
            if (friend.canCast("heal")) and hero.health < hero.maxHealth:
                self.command(friend, "cast", "heal", self)
            elif (friend.canCast("heal")) and hero.health == hero.maxHealth and soldier and soldier.health < soldier.maxHealth:
                self.command(friend, "cast", "heal", soldier)
            elif (friend.canCast("heal")) and hero.health == hero.maxHealth and archer and archer.health < archer.maxHealth:
                self.command(friend, "cast", "heal", archer)
            elif (friend.canCast("heal")) and hero.health == hero.maxHealth and friend and friend.health < friend.maxHealth:
                self.command(friend, "cast", "heal", friend)
            else:
                
                # commandPaladin() commands all paladins already
                # side note: rename commandPaladin() to commandPaladins()
                commandPaladin()

What a nice loop looks like
loop:
    trySummon()
    commandArchers()
    commandSoldiers()
    commandPaladins()
    heroActions()

# it's so much more readable, and therefore easier to debug!

My Zero-Sum loop, a real example

Link to level

# my functions are already defined; I am only showing the loop:
while True:
    global enemies
    enemies = (x for x in hero.findEnemies() if x.type != "yeti" and x.type != "crate")
    
    trySummon()
    tryCommand()
    
    nearestEnemy = hero.findNearest(enemies)
    if not trySpell(nearestEnemy):
        if not tryAttack(nearestEnemy): pass
        #else:
        #    hero.move(findBestCoin().pos)
    hero.move(findBestCoin().pos)

Nice and tidy :nerd_face:


The technique I’m focusing on:


[Help] Kelvintaph Defiler!
#13

Thank for the tip. I try to do that next time before I post.:slightly_smiling_face: