So I thought I was making progress but now I’m feeling defeated
I got my peasants to get gold based on distance and value, and tried to make them ignore each others targets, but they still go after each others gold.
I made a function called pickTarget(friend) the plan was to return a warlock if present or closest enemy if not.
I wanted paladins and griffin-riders to be able to use it == fail
the last thing that I’m stuck on is I can’t seem to make the paladins heal the weakest. They were at one point but not now. I don’t know what changed.
Any help would be great.
on a side note I really like CodeCombat, but sometimes really with I had a debugger, or at least a log of what happened
thanks again
# Your goal is to protect Reynaldo
# Find the paladin with the lowest health.
def lowestHealthPaladin():
lowestHealth = 99999
lowestFriend = None
friends = self.findFriends()
for friend in friends:
if friend.type != "paladin":
continue
if friend.health < lowestHealth and friend.health < friend.maxHealth:
lowestHealth = friend.health
lowestFriend = friend
#self.say("mosthurt "+lowestFriend)
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")
mostHurt=lowestHealthPaladin()
if mostHurt and mostHurt.health<400 and paladin.canCast("heal"):
#self.say(paladin +" can heal "+mostHurt)
self.command(paladin, "cast", "heal", mostHurt)
if paladin.health < 99:
self.command(paladin, "shield")
else:
target=pickTarget(paladin)
if target:
self.command(paladin, "attack", target)
def pickTarget(friend):
warlocks=self.findByType("warlock")
nearEnemy=friend.findNearest(friend.findEnemies())
targetWarlock=friend.findNearest(warlocks)
if targetWarlock:
return targetWarlock
else:
if nearEnemy:
return nearEnemy
def commandFriends():
# Command your friends.
friends = self.findFriends()
for friend in friends:
if friend.type == "peasant":
self.command(friend, "move", targetGold(friend).pos)
elif friend.type == "griffin-rider":
target=pickTarget(friend)
if target:
self.command(friend, "attack", target)
elif friend.type == "paladin":
commandPaladin(friend)
def targetGold(friend):
items=friend.findItems()
if items:
peasants=self.findByType("peasant")
activeMoneyTarget=[]
for peasant in peasants:
highValue=-1
if not item in activeMoneyTarget:
for item in items:
distanceToItemValue=(item.value/friend.distanceTo(item))
if distanceToItemValue>highValue:
target=item
highValue=distanceToItemValue
activeMoneyTarget.append(target)
return target
def summonGriffins():
if self.costOf("griffin-rider")<self.gold:
self.summon("griffin-rider")
loop:
commandFriends()
summonGriffins()
First, please post your code with radiant, harmonious formatting (surrounding it in triple backticks) as explained in the FAQ
Second, please mention the level name – I remember a level about protecting a guy named Reynaldo, but I don’t recall the level name… that makes it harder to help you.
Now, about your code (as much as I can make it out):
Your summonGriffins function is missing essential things. It should be something like:
if self.gold >= costOf(...): # comparison, colon
self.summon(...)
The targetGold function is also flawed: you check item before you define it:
[...]
highValue=-1
if not item in activeMoneyTarget: # 'item' is not yet defined
for item in items: # you only define 'item' here
distanceToItemValue=(item.value/friend.distanceTo(item))
[...]
You also don’t return anything if there is no coin at all (just in case).
The pickTarget function seems to be OK (although it could be optimized).
Please read the FAQ. It teaches you how to format your code properly. I’ve done it for you this time, but learn how to do it yourself.
In commandPaladin:
I believe the attack command overrides the heal. You’d be better off just commanding them to shield where they are, healing when necessary.
In pickTarget:
There will almost always be a nearest warlock, so your minions will head straight for that warlock, before attacking the nearest enemy. Is this intentional?
So I thought I was being clever and redid my get targetGold to use a global array, but found that won’t work with filbert forum with nick about global
so my goal is to have any number of peasants collect money based on item.value/distanceToItemValue and also make sure they won’t go after the same items. I guess what i"m stuck on is if the item is very far away the loop might run many times, how would i keep my list of active targets? I’m sure it should be obvious, but I seem to be stuck.
Thanks again for the help
Create your array in the main loop (or even before that) and pass it to your functions.
def findBestCoin(coins): # note that you expect an input array
# your code here
# ...
return bestCoin, coinsClaimed # note that you return an item and an array
coins = self.findItems() # array created before the main loop
# so it's "global" (kind of...)
loop:
# your code here
# ...
bestCoin, coinsClaimed = findBestCoin(coins)
# note that you store the returned 'bestCoin' and 'coinsClaimed'
# also note that you send the 'coins' array to the function
so I think I get it.
my main loop had commandFriends() and summonGriffins() in it.
First I added activeMoneyTarget[] to the top of my code.
then added activeMoneyTarget to these lines
commandFriends(activeMoneyTarget)
def commandFriends(activeMoneyTarget):
self.command(friend, “move”, targetGold(friend, activeMoneyTarget).pos)
def targetGold(friend, activeMoneyTarget):
It seems to work without needing to return the array.
does this seem like I’m over complicating things? Should I just be combining more into larger functions?
full code if you would like to take a look
btw I haven’t had a chance to look at the rest, so just look at the money
Thanks for the help. CodeCombat is good times
activeMoneyTarget=[]
# Your goal is to protect Reynaldo
# Find the paladin with the lowest health.
def lowestHealthPaladin():
lowestHealth = 99999
lowestFriend = None
friends = self.findFriends()
for friend in friends:
if friend.type != "paladin":
continue
if friend.health < lowestHealth and friend.health < friend.maxHealth:
lowestHealth = friend.health
lowestFriend = friend
#self.say("mosthurt "+lowestFriend)
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")
mostHurt=lowestHealthPaladin()
if mostHurt and mostHurt.health<400 and paladin.canCast("heal"):
#self.say(paladin +" can heal "+mostHurt)
self.command(paladin, "cast", "heal", mostHurt)
if paladin.health < 99:
self.command(paladin, "shield")
else:
target=pickTarget(paladin)
if target:
self.command(paladin, "attack", target)
def pickTarget(friend):
warlocks=self.findByType("warlock")
nearEnemy=friend.findNearest(friend.findEnemies())
targetWarlock=friend.findNearest(warlocks)
if targetWarlock:
return targetWarlock
else:
if nearEnemy:
return nearEnemy
def commandFriends(activeMoneyTarget):
# Command your friends.
friends = self.findFriends()
for friend in friends:
if friend.type == "peasant":
self.command(friend, "move", targetGold(friend, activeMoneyTarget).pos)
elif friend.type == "griffin-rider":
target=pickTarget(friend)
if target:
self.command(friend, "attack", target)
elif friend.type == "paladin":
commandPaladin(friend)
def targetGold(friend, activeMoneyTarget):
items=friend.findItems()
if items:
peasants=self.findByType("peasant")
for peasant in peasants:
highValue=-1
if not item in activeMoneyTarget:
for item in items:
distanceToItemValue=(item.value/friend.distanceTo(item))
if distanceToItemValue>highValue:
target=item
highValue=distanceToItemValue
activeMoneyTarget.append(target)
return target
def summonGriffins():
if self.costOf("griffin-rider")<self.gold:
self.summon("griffin-rider")
loop:
commandFriends(activeMoneyTarget)
summonGriffins()
So I think I found out why I couldn’t attack the warlocks, my glasses can only see 20m.
so I thought I would play some other levels to earn some gems. The first level I tried was zero sum and what I thought would work to get money makes my guy go all crazy. I thought it was almost the same code, but maybe item.value/self.distanceTo(item) is not the way to go.
loop:
self.move(targetGold())
def targetGold():
hiVal=-1
items=self.findItems()
for item in items:
disVal=item.value/self.distanceTo(item)
if disVal>hiVal:
hiVal==disVal
currentTarget=item
return currentTarget.pos
not sure if this is what you mean, but like this he seems to go all random and crazy like.
loop:
self.move(targetGold())
def targetGold():
hiVal=-1
items=self.findItems()
for item in items:
disVal=item.value/self.distanceTo(item)
if disVal>hiVal:
hiVal==disVal
currentTarget=item
if currentTarget:
return currentTarget.pos
I tried that, but the target still jumps all around the map. Sometimes he will get high value right next to him and other times he will get low value all the way across the map passing many high value items. He will also change directions, for unknown reasons. Unknown to me at least.
Thanks again for the help.
BTW I finally beat Grim Determination, didn’t even lose any paladins
By the way, using value/distance is a very dangerous weight and can cause problems when distance “gets close to” 0. I strongly suggest that you get in the habit of using value/(1+distance) instead. Given that the coins get picked up automatically when “too close” it is not likely you will hit a division by zero error, but when you use the same weight with distances to enemies, you can get some strange results. The good news is that 1/(1+distance) and 1/distance really produces the same rank ordering, so the change is one behind the scenes and would not effect which coin/enemy/etc. gets selected.