Help: I can't beat Grim Determination

Here’s the link for the level.

And this is my code.

# Your goal is to protect Reynaldo

# Find the paladin with the lowest health.
def lowestHealthPaladin():
    lowestHealth = 99999
    lowestFriend = None
    friends = hero.findFriends()
    for friend in friends:
        if friend.type != "paladin":
            continue
        if friend.health < lowestHealth and friend.health < friend.maxHealth:
            lowestHealth = friend.health
            lowestFriend = friend
    return lowestFriend

def commandPaladin(paladin):
    # Heal the paladin with the lowest health using lowestHealthPaladin()
    # You can use paladin.canCast("heal") and command(paladin, "cast", "heal", target)
    # Paladins can also shield: command(paladin, "shield")
    # And don't forget, they can attack, too!
    inNeed = lowestHealthPaladin()
    if inNeed and paladin.canCast('heal'):
        hero.command(paladin, 'cast', 'heal', inNeed)
    enemy = paladin.findNearestEnemy()
    if enemy:
        hero.command(paladin, 'attack', enemy)
    else:
        hero.command(paladin, 'shield')
    pass

def findBestVOD(man, bunch, exclu):
    bestCoin = None
    bestVOD = 0
    for i in bunch:
        distance = man.distanceTo(i)
        if i in exclu:
            continue
        elif i.value / distance > bestVOD:
            bestCoin = i
            bestVOD = i.value / distance
    return bestCoin

def commandPeasant(p, excluded):
    items = hero.findItems()
    if items:
        target = findBestVOD(p, items, excluded)
        excluded.append(target)
        hero.command(p, 'move',  target.pos)
    
def commandGriffin(g):
    hero.command(g, 'defend', {'x':86, 'y':38}) 
    
        
def commandFriends(ex):
    # Command your friends.
    friends = hero.findFriends()
    for friend in friends:
        if friend.type == "peasant":
            #commandPeasant(friend)
            commandPeasant(friend, ex)
            pass
        elif friend.type == "griffin-rider":
            #commandGriffin(friend)
            commandGriffin(friend)
            pass
        elif friend.type == "paladin":
            commandPaladin(friend)

while True:
    claimed = []
    commandFriends(claimed)
    # Summon griffin riders!
    if hero.gold > hero.costOf('griffin-rider'):
        hero.summon('griffin-rider')

First of all, my paladins never cast healing.
Not even once.
Here’s the same code part from the above.

def commandPaladin(paladin):
    inNeed = lowestHealthPaladin()
    if inNeed and paladin.canCast('heal'):
        hero.command(paladin, 'cast', 'heal', inNeed)
    enemy = paladin.findNearestEnemy()
    if enemy:
        hero.command(paladin, 'attack', enemy)
    else:
        hero.command(paladin, 'shield')
    pass

I’m not sure but the second if condition, for checking whether there’s an enemy or not, is disturbing the first if, I guess. But shouldn’t they be executed separately since they are not related?

Secondly, I have no idea what to do to control my griffin riders.
I did notice that I should kill the enemy casters first.
So I tried this below.

def commandGriffin(g):
    priorTarget = hero.findNearest(hero.findByType("warlock"))
    if priorTarget:
        while priorTarget.health > 0:
            hero.command(g, 'attack', priorTarget)
    else:
        hero.command(g, 'defend', {'x':86, 'y':38}) 

Then, once the first born griffin rider gets to the defending point, they just stuck in an infinite loop.

I just found out that if I delete the ‘else’ part from the commandPaladin function, my paladins heal just one or two times. But they never cast any after that. I still have no clues for the problems.

Mod edit: solution removed

I managed to beat the level with this code above. I get the sense that the relation between hero.attack(enemy) and hero.command(someone, ‘attack’, enemy) is like the one between hero.move(pos) and hero.moveXY(x,y). Only the one time attack code and one step forward code go well together with while loop. So the griffin riders worked as I intended.

And my paladins did their job perfectly. But still, I don’t get the exact idea behind the paladin part. Please if you could give any advice I’d be very happy.

One of the details you want to be aware of with attack command, is that the hero needs to complete that code before it will move on to another one. If you are using a warrior, that means the warrior needs to get close enough to attack before it can attack. So as you hinted at, it treats it like a moveXY() to get within range.

The hero.command() is commanding other allies and once they get their marching orders they will follow through until it is complete or you change the command. If not commanded, their default code is attack the nearest enemy in most levels.

I think you are right. Since your paladin gets an attack command before completing the healing, the attack may be interfering with the healing. So do one or the other when you call that function.
Didn’t try this but, to keep the paladins healing, you may want to do and if - else statement to prioritize healing in the commandPaladin(). If not healing then follow through with your enemy attack or shield.

Happy Cakeday brooksy :tada: