Last active
September 9, 2015 15:56
-
-
Save blueset/8b5747abcd9acb8282ac to your computer and use it in GitHub Desktop.
Translation of https://gist.github.com/MHeasell/bb9f7253da45b5140a83
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Translation of https://gist.github.com/MHeasell/bb9f7253da45b5140a83 | |
def vadd (s, o): | |
return {'x': s['x']+o['x'], 'y': s['y']+o['y']} | |
def vsub (s, o): | |
return {'x': s['x']-o['x'], 'y': s['y']-o['y']} | |
def vmul (s, o): | |
return {'x': s['x']*o, 'y': s['y']*o} | |
def vlen (s): | |
return (s['x']**2+s['y']**2)**0.5 | |
def vdir (s, o): | |
return vnorm(vsub(o, s)) | |
def vdist (s, o): | |
return vlen(vsub(o, s)) | |
def vnorm(s): | |
l = len(s) | |
if (l == 0): | |
return {'x': 1, 'y': 1} | |
else: | |
return {'x': s['x']/l, 'y': s['y']/l} | |
def makeBins(): | |
return [[] for _ in range(30)].copy() | |
def putInBins(items): | |
b = makeBins() | |
for i in items: | |
p = i['pos'] | |
bx = int(p['x']/(40/3)) | |
by = int(p['y']/14) | |
if (by >= 5 or bx >= 6): | |
continue | |
b[by*6+bx].push(item) | |
return b | |
def findBestBinPos(bins): | |
wini = None | |
winS = -flaot("inf") | |
for i, b in bins: | |
if (len(b) > winS): | |
winS = len(b) | |
wini = b | |
return {"pos": {"x": wini % 6, "y": int(wini/6)}, "score": winS} | |
def findBestBinPosAvgCoords(bins): | |
wini = None | |
winS = -flaot("inf") | |
for i, b in bins: | |
if (len(b) > winS): | |
winS = len(b) | |
wini = i | |
winx = int(sum([i['pos']['x'] for i in bins[wini]])/len(bins[wini])) | |
winy = int(sum([i['pos']['y'] for i in bins[wini]])/len(bins[wini])) | |
return {"pos": {"x": winx, "y": winy}, "score": winS} | |
def findLargestGroupPosAvg(i): return findBestBinPos(putInBins(i)) | |
def findLargestGroupPos(i): | |
r = findLargestGroupPosAvg(i) | |
return {"score": r['score'], "pos":{"x": r['pos']['x'] * (40/3) + (20/3), "y": r['pos']['y']*14+7}} | |
def filterType(t, arr): | |
n = [] | |
for i in arr: | |
if (i['type'] == t): | |
n.append(i) | |
return n | |
def filterNotType(t, arr): | |
n = [] | |
for i in arr: | |
if (not i['type'] == t): | |
n.append(i) | |
return n | |
def filterAllies(t, arr): | |
n = [] | |
for i in arr: | |
if (i['type'] in self.team): | |
n.append(i) | |
return n | |
def firstOfType(t, arr): | |
if (len(filterType(t, arr))>0): | |
return filterType(t, arr)[0] | |
return None | |
def findInRadius(r, arr): | |
n = [] | |
for i in arr: | |
if (self.distanceTo(i) < r): | |
n.append(i) | |
return n | |
def findInTargetRadius(t, r, arr): | |
n = [] | |
for i in arr: | |
if (t.distanceTo(i) < r): | |
n.append(i) | |
return n | |
def findLowestHealth(u): | |
ir = None | |
ih = float('inf') | |
for i in u: | |
if (i['health'] < ih): | |
ih = i['health'] | |
ir = i | |
return i | |
# Global variables | |
g = {'enemies': [],'filteredEnemies': [],'enemySoldiers': [],'enemyArtillery': [],'enemyArchers': [],'enemyGriffins': [],'enemySorc': None,'items': [],'corpses': [],'friends': [],'friendlySoldiers': [],'friendlyArchers': [],'friendlyGriffins': [],'friendlyArtillery': [],'yeti': None,'cachedRaiseDeadTime': -float('inf'),'cachedRaiseDeadData': None,'cachedManaBlastTime': -float('inf'),'cachedManaBlastData': None,'lastGoldstormTime': -float('inf'),'lastSorcFearTime': -float('inf'),'lastJumpTime': -float('inf')} | |
def getManaBlastData(): | |
t = self.now() | |
if (time - g['cachedManaBlastTime'] >= 0.5): | |
g['cachedManaBlastData'] = findLargestGroupPosAvg(g['filteredEnemies']) | |
g['cachedManaBlastTime'] = t | |
return g['cachedManaBlastData'] | |
def getManaBlastData(): | |
t = self.now() | |
if (time - g['cachedRaiseDeadTime'] >= 0.5): | |
g['cachedRaiseDeadData'] = findLargestGroupPos(g['corpses']) | |
g['cachedRaiseDeadTime'] = t | |
return g['cachedRaiseDeadData'] | |
def updateGlobals(): | |
g['enemies'] = self.findEnemies() | |
g['filteredEnemies'] = filterNotType('yeti', filterNotType('cage', g['enemies'])) | |
g['enemySorc'] = firstOfType('sorcerer', g['enemies']) | |
g['enemySoldiers'] = filterType('soldier', g['enemies']) | |
g['enemyArchers'] = filterType('archer', g['enemies']) | |
g['enemyGriffins'] = filterType('griffin-rider', g['enemies']) | |
g['enemyArtillery'] = filterType('artillery', g['enemies']) | |
g['yeti'] = firstOfType('yeti', g['enemies']) | |
g['items'] = self.findItems() | |
g['corpses'] = self.findCorpses() | |
g['friends'] = self.findFriends() | |
g['friendlySoldiers'] = filterType('soldier', g['friends']) | |
g['friendlyArchers'] = filterType('archer', g['friends']) | |
g['friendlyGriffins'] = filterType('griffin-rider', g['friends']) | |
g['friendlyArtillery'] = filterType('artillery', g['friends']) | |
def findGoalCoinPositionGravity (coins): | |
forceX = 0 | |
forceY = 0 | |
pos = self.pos | |
posX = pos['x'] | |
posY = pos['y'] | |
enemySorc = g['enemySorc'] | |
yeti = g['yeti'] | |
enemyIsFeared = self.now() - g['lastSorcFearTime'] < 5 | |
for i in coins: | |
d = self.distanceTo(i) | |
if (d == 0): | |
continue | |
eDist = enemySorc.distanceTo(i) | |
attrStrn = i.value | |
eMult = (d + eDist)/eDist | |
if (eMult > (20/9)): | |
if (enemyIsFeared): | |
attrStrn *= (20/9) | |
else: | |
attrStrn /= 2 | |
else: | |
attrStrn *= eMult | |
finalStrength = attrStrn / d**3 | |
coinPos = i['pos'] | |
forceX += (coinPos['x'] - posX) * finalStrength | |
forceY += (coinPos['y'] - posY) * finalStrength | |
if (yeti): | |
yetiDist = self.distanceTo(yeti) | |
if (yetiDist): | |
yetiStr = -20/yetiDist**3 | |
yetiPos = yeti['pos'] | |
forceX += (yetiPos.x - posX) * yetiStrength | |
forceY += (yetiPos.y - posY) * yetiStrength | |
left = pos['x'] | |
right = 85 - left | |
bottom = pos['y'] | |
top = 72 - bottom | |
forceX += 1/(left**2) - 1/(right**2) | |
forceY += 1/(top**2) - 1/(bottom**2) | |
finalScale = 10/(forceX**2+forceY**2)**0.5 | |
return {'x': posX + forceX * finalScale, "y": posY + forceY * finalScale} | |
def findGoalCoinPositionGreedy (coins): | |
w = None | |
wS = -float("inf") | |
for i in coins: | |
d = self.distanceTo(i) | |
s = i['value'] | |
if (s == 3): s = 6 | |
score = s / d | |
eDist = g['enemySorc'].distanceTo(i) | |
if (d > eDist): score /= 2 | |
if (g['yeti']): | |
if (d > g['yeti'].distanceTo(i)): | |
score /= 4 | |
if (score > wS ): | |
w = i | |
wS = score | |
if (w): | |
return w['pos'] | |
return {"x": 40, "y": 38} | |
def findGoalCoinPositionGoldFilter(coin): | |
if coin['value'] == 3: s = 9 | |
else: s = coin['value'] | |
return s/self.distanceTo(coin) | |
def findGoalCoinPositionGold(coins): | |
iv = None | |
ic = 0 | |
for i in coins: | |
k = findGoalCoinPositionGoldFilter(i) | |
if (k > ic): | |
iv = i | |
ic = k | |
return iv | |
def collectCoinAction(): return {"action": "collec-coins"} | |
def goldstormAction(): | |
if (not self.canCast("goldstorm") or self.distanceTo(g['enemySorc']) < 40): | |
return None | |
return {"action": 'goldstorm'} | |
def raiseDeadAction(): | |
if (self.now() < 5): return None | |
if (not self.canCast("raise-dead") and not self.isReady('reset-cooldown')): return None | |
nearbyCorpse = findInRadius(20, g['corpses']) | |
if (not firstOfType('artillery', nearbyCorpse) == None): return None | |
if (len(nearbyCorpse) > 2): return None | |
if (not self.canCast('raise-dead')): | |
return {"action": 'reset-cooldown', "target": 'raise-dead'} | |
else: | |
return {"action": 'raise-dead'} | |
def attackArchersAction(): | |
t = findLowestHealth(findInRadius(25, g['enemyArchers'])) | |
if (t): | |
return {"action": 'attack', "target": t} | |
return None | |
def attackSorcInEndGameAction(): | |
if (self.now() < 110): return None | |
sorc = g['enemySorc'] | |
if (sorc.health >= self.health()): return None | |
return {"action": 'attack', 'target': sorc} | |
def attackArtilleryAction(): | |
if (len(g['enemyArtillery']) < 1): return None | |
t = self.findNearest(g['enemyArtillery']) | |
if (not t): return None | |
if (self.distanceTo(t) >= 40): return None | |
return {"action": 'attack', 'target': t} | |
def aggressiveManaBlastAction(): | |
if (self.now() < 5): return None | |
if ((not self.isReady('mana-blast')) and (not self.isReady('reset-cooldown'))): | |
return None | |
mbData = getManaBlastData() | |
eCount = mbData['score'] | |
if (eCount <= 3): return None | |
mbPos = mbData['pos'] | |
d = vdist(self.pos, mbPos) | |
if (d>10): return None | |
if (not self.isReady('mana-blast')): | |
return {"action": 'reset-cooldown', 'target': "mana-blast"} | |
elif (d > 3): | |
return {"action": 'move', 'target': mbPos} | |
else: | |
return {"action": 'mana-blast'} | |
def defensiveManaBlastAction(): | |
if ((not self.isReady('mana-blast')) and (not self.isReady('reset-cooldown'))): | |
return None | |
nearEnemies = findInRadius(10, g['filteredEnemies']) | |
if (len(nearEnemies) < 5): return None | |
if (not self.isReady('mana-blast')): | |
return {"action": 'reset-cooldown', 'target': "mana-blast"} | |
else: | |
return {"action": 'mana-blast'} | |
def drainLifeAction(): | |
if (not self.canCast('drain-life')): return None | |
if ((self.health / self.macHealth) > 0.3): return None | |
target = findLowestHealth(findInRadius(15, g['enemies'])) | |
if (not target): return None | |
return {"action": 'drain-life', "target": target} | |
def fearEnemySorcererAction(): | |
if (self.canCast('fear')): | |
if (not self.canCast('fear', g['enemySorc'])): | |
return None | |
elif (not self.isReady('reset-cooldown')): | |
return None | |
if (self.distanceTo(g['enemySorc']) > 35): return None | |
if (not self.canCast('fear')): | |
return {"action": 'reset-cooldown', 'target': "fear"} | |
else: | |
return {"action": 'fear', 'target': g['enemySorc']} | |
def fearYetiAction(): | |
if (not g['yeti']): return None | |
if (self.canCast('fear')): | |
if (not self.canCast('fear', g['yeti'])): | |
return None | |
elif (not self.isReady('reset-cooldown')): | |
return None | |
if (self.distanceTo(g['yeti']) > 20): return None | |
if (not g['yeti'].target == self): return None | |
if (not self.canCast('fear')): | |
return {"action": 'reset-cooldown', 'target': "fear"} | |
else: | |
return {"action": 'fear', 'target': g['yeti']} | |
def avoidYetiAction(): | |
if (not g['yeti']): return None | |
if (self.distanceTo(g['yeti']) > 20): return None | |
return {'action': 'collect-coins'} | |
def pathAroundBox(pos): | |
box = firstOfType('cage',g['enemies']) | |
if (not box): return pos | |
if (self.distanceTo(box) > 7): return pos | |
if ((self.pos.y < box.pos.y and pos.y > box.pos.y or self.pos.y > box.pos.y and pos.y < box.pos.y) ): | |
if ((self.pos.x <= box.pos.x + 4 and self.pos.x >= box.pos.x - 4) ): | |
if ((pos.x > box.pos.x) ): | |
return {'x': pos.x + 10, 'y': pos.y} | |
else : | |
return {'x': pos.x - 10, 'y': pos.y} | |
if ((self.pos.x < box.pos.x and pos.x > box.pos.x or self.pos.x > box.pos.x and pos.y < box.pos.x) ): | |
if ((self.pos.y <= box.pos.y + 4 and self.pos.y >= box.pos.y - 4) ): | |
if ((pos.y > box.pos.y) ): | |
return {'x': pos.x, 'y': pos.y + 10} | |
else : | |
return {'x': pos.x, 'y': pos.y - 10} | |
def goToTarget(pos): | |
pos = pathAroundBox(pos) | |
if (self.isReady('jump') and vdist(self.pos, pos) >= 8): | |
self.jumpTo(pos) | |
g['lastJumpTime'] = self.now() | |
else: | |
self.move(pos) | |
def selectAction(): | |
a = avoidYetiAction() | |
if (a): return a | |
a = raiseDeadAction() | |
if (a): return a | |
a = aggressiveManaBlastAction() | |
if (a): return a | |
a = defensiveManaBlastAction() | |
if (a): return a | |
a = attackArtilleryAction() | |
if (a): return a | |
a = attackSorcInEndGameAction() | |
if (a): return a | |
a = goldstormAction() | |
if (a): return a | |
a = collectCoinAction() | |
if (a): return a | |
def executeAction(a): | |
c = a['action'] | |
t = a['target'] | |
if (c == 'move'): | |
self.goToTarget(t) | |
elif (c == 'collect-coins'): | |
timeSinceGS = self.now - g['lastGoldstormTime'] | |
if (0.5 < timeSinceGS < 10): | |
coinPos = findGoalCoinPositionGold(g['items']) | |
else: | |
coinPos = findGoalCoinPositionGravity(g['items']) | |
self.goToTarget(coinPos) | |
elif (c == 'attack'): | |
self.attack(t) | |
elif (c == 'raise-dead'): | |
self.cast(c) | |
elif (c == 'mana-blast'): | |
self.manaBlast() | |
elif (c == 'drain-life'): | |
self.cast(c,t) | |
elif (c == 'fear'): | |
self.cast(c,t) | |
if (t == g['enemySorc']): | |
g['lastSorcFearTime'] = self.now() | |
elif (c == 'goldstorm'): | |
self.cast(c) | |
g['lastGoldstormTime'] = self.now() | |
else: | |
self.debug('Unknown Action: %s' % a) | |
def doAbilityLogic(): return executeAction(selectAction()) | |
def percentageSummon(): | |
gCount = len(g['friendlyGriffins']) | |
sCount = len(g['friendlySoldiers']) | |
if (gCount < 3): return 'griffin-rider' | |
if (sCount < 5): return 'soldier' | |
if (sCount/gCount < (2/3)): return 'soldier' | |
return 'griffin-rider' | |
def shouldDoEndGameAttack(): | |
return self.now() > 105 and len(g['friends']) > len(g['filteredEnemies']) | |
def shouldGoOffensive(): | |
return len(g['friends']) > 9 and len(g['friends']) / len(g['filteredEnemies']) > 2; | |
def decideSummon(): | |
nearestE = self.findNearest(g['filteredEnemies']) | |
if (self.distanceTo(nearestE) < 30 and (self.now()< 45 or len(g['filteredEnemies']) > 1)): | |
return percentageSummon() | |
if (shouldDoEndGameAttack()): return percentageSummon() | |
if (shouldGoOffensive()): return percentageSummon() | |
if (self.gold >= 150): return 'griffin-rider' | |
return None | |
def doSummonLogic(): | |
sType = decideSummon() | |
tSinceJump = self.now() - g['lastJumpTime'] | |
if (tSinceJump > 0.5): | |
while not sType == None and self.gold > self.costOf(sType): | |
self.summon(sType) | |
updateGlobals() | |
sType = decideSummon() | |
return True | |
return False | |
def doAttackNearestAI(e, units): | |
for i in units: | |
nearestE = u.findNearest(e) | |
self.command(u, 'attack', nearestE) | |
def selectRoamTarget(u): | |
t = u.findNearest(g['enemyArtillery']) | |
if (t): return t | |
t = u.findNearest(g['enemyArchers']) | |
if (t): return t | |
t = u.findNearest(g['enemyGriffins']) | |
if (t): return t | |
return g['enemySorc'] | |
def pickRandomPosition(): | |
return {'x': Math.random()*83, 'y': Math.random()*70} | |
def maybeKiteSoldiers(leader, units): | |
lPos = leader.pos | |
nearestS = leader.findNearest(g['enemySoldiers']) | |
if (nearestS): | |
sDist = leader.distanceTo(nearestS) | |
if (sDist < 10): | |
sPos = nearestS.pos | |
if (sDist == 0): offsetScale = 10 | |
else: offsetScale = 10 / sDist | |
newPos = {'x': lPos['x'] + (lPos['x'] - sPos['x']) * offsetScale, 'y': lPos['y'] + (lPos['y'] - sPos['y']) * offsetScale} | |
for i in units: | |
self.command(i, 'move', newPos) | |
return True | |
return False | |
def doRoamAttackAI(u): | |
if (len(u) == 0): return | |
leader = u[0] | |
if (maybeKiteSoldiers(leader, u)): return | |
target = selectRoamTarget(leader) | |
for i in u: | |
self.command(i, 'attack', target) | |
def doAttackTargetAndKiteAI(t, u): | |
if (len(u) == 0): return | |
l = u[0] | |
if (maybeKiteSoldiers(l, u)): return | |
for i in u: | |
self.command(i, 'attack', t) | |
def doAttackTargetAI(t, u): | |
if (len(u) == 0): return | |
for i in u: | |
self.command(i, 'attack', t) | |
def doAttackSpecificAI(u): | |
if (len(u) == 0): return | |
t = u[0].findNearest(g['filteredEnemies']) | |
for i in u: | |
self.command(i, 'attack', t) | |
def maybeKiteIndividual(u, e): | |
nearestS = u.findNearest(e) | |
if (nearestS): | |
sDist = u.distanceTo(nearestS) | |
if (sDist < 6): | |
sPos = nearestS.pos | |
if (sDist == 0): offsetScale = 10 | |
else: offsetScale = 10 / sDist | |
uPos = u.pos | |
newPos = {'x': uPos.x + (uPos['x'] - sPos['x']) * offsetScale, 'y': uPos['y'] + (uPos['y'] - sPos['y']) * offsetScale} | |
self.command(u, 'move', newPos) | |
return True | |
return False | |
def doDefensiveGriffinAI(u): | |
eS = g['enemySoldiers'] | |
pos = self.pos | |
offsetPos = {'x': pos['x']+2, 'y': pos['y']+2} | |
yeti = g['yeti'] | |
yetiArr = [yeti] | |
if (len(g['enemyArtillery']) > 0): artillery = g['enemyArtillery'][0] | |
else: artillery = None | |
if (artillery and len(eS) == 0): | |
for i in u: | |
self.command(i, 'attack', artillery) | |
return | |
for i in u: | |
if (maybeKiteIndividual(i, eS)): continue | |
if (yeti and maybeKiteIndividual(i, yetiArr)): continue | |
lowestHealthE = findLowestHealth(findInTargetRadius(i, 20, g['filteredEnemies'])) | |
if (lowestHealthE): | |
self.command(i, 'attack', lowestHealthE) | |
continue | |
self.command(i, 'move', offsetPos) | |
def doOffensiveGriffinAI(u): | |
eS = g['enemySoldiers'] | |
eRanged = g['enemyGriffins']+g['enemyArchers']+g['enemyArtillery'] | |
sorc = g['enemySorc'] | |
yeti = g['yeti'] | |
yetiArr = [yeti] | |
for i in u: | |
if maybeKiteIndividual(i, eS): continue | |
if (yeti and maybeKiteIndividual(i, yetiArr)): continue | |
lowestHealthE = findLowestHealth(findInTargetRadius(i, 20, eRanged)) | |
if (lowestHealthE): | |
self.command(i, 'attack', lowestHealthE) | |
continue | |
if (i.distanceTo(sorc) < 20): | |
self.command(i, 'attack', sorc) | |
continue | |
lowestHealthS = findLowestHealth(findInTargetRadius(i, 20, eS)) | |
if (lowestHealthS): | |
self.command(i, 'attack', lowestHealthS) | |
continue | |
nearestE = self.findNearest(g['filteredEnemies']) | |
self.command(i, 'attack', nearestE) | |
def doDefensiveTroopLogic(): | |
doAttackNearestAI(g['filteredEnemies'], g['friendlySoldiers']) | |
doDefensiveGriffinAI(g['friendlyGriffins']) | |
def doOffensiveTroopLogic(): | |
doAttackNearestAI(g['enemies'], g['friendlySoldiers']) | |
doOffensiveGriffinAI(g['friendlyGriffins']) | |
def doEndGameTroopLogic(): | |
doAttackTargetAI(g['enemySorc'], g['friends']) | |
def doTroopLogic(): | |
if (shouldDoEndGameAttack()): doEndGameTroopLogic() | |
elif (shouldGoOffensive()): doOffensiveTroopLogic() | |
else: doDefensiveTroopLogic() | |
if (self.findEnemies()[0].type == 'knight'): | |
loop: | |
self.attack(self.findEnemies()[0]) | |
while True: | |
updateGlobals(); | |
if (not g['enemySorc']): break | |
doTroopLogic() | |
a = fearYetiAction() | |
if (a): | |
executeAction(a) | |
continue | |
a = fearEnemySorcererAction() | |
if (a): | |
executeAction(a) | |
continue | |
doSummonLogic() | |
doAbilityLogic() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment