Circular Soldier Patrol


#1

I thought this was a neat formation for my little army.

The circle is incomplete because more soldiers from across the arena had just spawned, so spots in the circle were saved for them.

circle

Click here for the code (python)
patrolCenter = {'x': 80, 'y': 45}
#patrolCenter = hero.pos # here, they have a hard time keeping up whenever you move
patrolTimeScale = 0.25 # set to 0.0 if you don't want the soldiers to walk around while in the circle

while True:
    #...summon soldiers, collect coins, etc...
    
    friends = hero.findFriends()
    soldiers = (friend for friend in friends if friend.type == "soldier")
    radius = 2 + 2.5 * Math.log1p(len(soldiers)) #or use Math.log(len(soldiers) + 1)
    angleStep = Math.PI * 2 / len(soldiers)
    for index, soldier in enumerate(soldiers):
        enemy = soldier.findNearestEnemy()
        if enemy:
            hero.command(soldier, "attack", enemy)
        else:
            hero.command(soldier, "move",
                {'x': patrolCenter.x + radius * Math.cos(angleStep * index - hero.time * patrolTimeScale),
                'y': patrolCenter.y + radius * Math.sin(angleStep * index - hero.time * patrolTimeScale)})
    
    #...etc...


#2

Can you do this formation with griffin rider because I try to do it but it doesn’t work at all.


#3

Might be able to, but griffin-riders cost a lot more so you might not have enough riders to defend with.


#4

Here, I turned it into a function:

def circularPatrol(units, targetPos=hero.pos, clockwise = False, timeScale=0.3):
    radius = 2 + 2.5 * Math.log1p(len(units))
    angleStep = Math.PI * 2 / len(units)
    for index, unit in enumerate(units):
        angle = angleStep * index + (-1)**(clockwise+1) * hero.time * timeScale
        hero.command(unit, "move", {
            'x': targetPos.x + radius * Math.cos(angle),
            'y': targetPos.y + radius * Math.sin(angle)
        })

You pass the array of units you want to command. You can also optionally pass a position that you want the units to circle around.

Example
def commandGriffins(griffins):
    idleGriffins = []
    
    for griffin in griffins:
        enemy = griffin.findNearestEnemy()
        if enemy:
            if griffin.distanceTo(enemy) < 30:
                griffin.attack(enemy)
            else:
                idleGriffins.append(griffin) # griffin does not attack enemy; it is idle
        else:
            idleGriffins.append(griffin) # no enemies; griffin is idle
    
    # done iterating through all griffins and idleGriffins is ready to be used
    circularPatrol(idleGriffins, {"x": 50, "y": 38})
    #circularPatrol(idleGriffins) # idleGriffins will circle around the hero if no position is provided

#5

And here it is in action:
[CUT] [490 x 400] [890 x 400] circle2

Code (I used a custom level)
def circularPatrol(units, targetPos=hero.pos, clockwise = False, timeScale=0.3):
    radius = 2 + 2.5 * Math.log1p(len(units))
    angleStep = Math.PI * 2 / len(units)
    for index, unit in enumerate(units):
        angle = angleStep * index + (-1)**(clockwise+1) * hero.time * timeScale
        hero.command(unit, "move", {
            'x': targetPos.x + radius * Math.cos(angle),
            'y': targetPos.y + radius * Math.sin(angle)
        })

# the level gives me enough gold to summon 15 of these things
for i in range(15):
    hero.summon("griffin-rider")

friends = hero.findFriends()
groups = []
for i in range(3):
    groups[i] = {'units': [], 'pos':None}
    for k in range(5):
        groups[i].units.append(friends.pop(0))

while True:
    for index in range(3):
        angle = (Math.PI * 2 * index / 3) + hero.time * 0.2
        radius = 15 + 5 * Math.sin(hero.time * 0.8)
        groups[index].pos = {
            'x': hero.pos.x + radius * Math.cos(angle),
            'y': hero.pos.y + radius * Math.sin(angle)
            }
        circularPatrol(groups[index].units, groups[index].pos, timeScale = 0.5)
    flags = hero.findFlags()
    if len(flags) > 0:
        hero.pickUpFlag(flags[0])

#6

Thank.
20 characters