Skip to content

Instantly share code, notes, and snippets.

@blueset
Last active September 9, 2015 15:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save blueset/8b5747abcd9acb8282ac to your computer and use it in GitHub Desktop.
Save blueset/8b5747abcd9acb8282ac to your computer and use it in GitHub Desktop.
# 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