Python: max() not working as expected


#1

On the level Mountain Mercenaries in Cloudrip Mountain, I’m attempting to collect coins based on their value per distance.
Using max() fails to select the highest value per distance coin out of all found coins.

def valueOverDistance(coin):
    return coin.value / hero.distanceTo(coin)

useMax = False
useMax = True # comment this line out to use the working solution, which doesn't use max

while True:
    coins = hero.findItems()
    if len(coins) > 0:
        if useMax: # these don't work as expected
            #bestCoin = max(coins, key=valueOverDistance)
            #bestCoin = max(coins, key=lambda coin: valueOverDistance(coin))
            bestCoin = max(coins, key=lambda coin: coin.value / hero.distanceTo(coin)) # how does this not work???
            #bestCoin = max(coins) # all the lines above using max() have the same result as this line!
        else: # this works perfectly as expected, but it doesn't use max()
            bestCoin = coins[0]
            for coin in coins:
                if valueOverDistance(coin) > valueOverDistance(bestCoin):
                    bestCoin = coin
        hero.move(bestCoin.pos)
        #worstCoin = min(coins, key=valueOverDistance) #doesn't work as expected either
        #hero.move(worstCoin.pos)
    ...

Is there a way to use max() so that it works as expected?


#2

I don’t have an answer for you, but I have been playing around with your code and I noticed something.

max() is returning a coin, just not the coin you expect.

I am pretty sure your program is collecting coins by reverse alphabetical order of the coin id.

For example, it would go in this order

Silver Coin 9
Silver Coin 8
Silver Coin 7
Silver Coin 6
Silver Coin 5
Silver Coin 4
Silver Coin 3
Silver Coin 2
Silver Coin 12
Silver Coin 11
Gold Coin 4
Gold Coin 3
Gold Coin 2
Gold Coin 1
Bronze Coin 42
Bronze Coin 41
etc.

I have no idea what this means, but I hope it helps someone else figure out what is going on.


#3

I have a couple more attempts inspired from stackoverflow:

# doesn't work as xrange doesn't seem to be implemented yet:
bestCoin = coins[max(xrange(len(coins)),
                     key = lambda index:
                         coins[index]['value'] / hero.distanceTo(coins[index]))
                )]

# doesn't work; this collects coins in reverse alphabetical order like the previous attempts:
bestCoin = max(enumerate(coins),
               key = lambda indexCoinTuple:
                   indexCoinTuple[1]['value'] / hero.distanceTo(indexCoinTuple[1])
           )[1] # selects the dictionary part of the (index, dict) tuple that comes from enumeration items