Skip to content

The Zombie Invasion

bonzaiferroni edited this page Jan 5, 2017 · 16 revisions

With bonzAI picking its own harvest rooms now, it was time to deal with the case where a potential harvest room is already inhabited. I already have code for a more decisive raid where tower damage is healed even at close range, rather than trying to drain the tower. In quite a few cases, these players will be inactive, a.k.a. "zombies", who won't put up much of a fight. I thought it worthwhile to try to write code that better scales to this situation.

This is the room I used as a test-case and also the type of situation I wanted to be able to handle with this code: test case

ZombieOperation.ts link

"braaaains..."

My solution was to fight fire with fire and code some zombies of my own. These creeps will be slow and stupid. The pseudocode in a nutshell:

if health is above threshold
    approach nearest spawn in enemy room
    ignore most structures while pathing, attack any structures in the way
if health is below threshold
    approach fallback position

I also wanted tower-draining behavior to emerge from this pattern, having the creep utilize the exit mechanic to assist with healing. When a creep is standing an exit, it is in the room only every other tick, effectively halving the damage received. So I added this to the creep logic:

if standing on the exit for the enemy room, wait for at least 10 consecutive ticks with full health before stepping into the room

Making an entrance

Tower damage is graded by distance, certain exits will be preferable above others based on their average distance to towers in the room. My solution was to look at every exit in the room and figure out how much damage it would receive, and picking the lowest one. I'd ensure my zombies would use this exit by blocking off all the other exits in the CostMatrix.

Here is my first attempt to construct such a matrix:

let exitPositions: RoomPosition[] = [];
for (let x = 0; x < 50; x ++) {
    for (let y = 0; y < 50; y++) {
        if (x !== 0 && y !== 0 && x !== 49 && y !== 49) { continue; }
        if (Game.map.getTerrainAt(x, y, this.room.name) === "wall") { continue; }
        exitPositions.push(new RoomPosition(x, y, this.room.name));
        matrix.set(x, y, 0xff);
    }
}

let bestExit = _(exitPositions)
    .sortBy((p: RoomPosition) => -_.sum(towers, (t: Structure) => p.getRangeTo(t)))
    .head();
matrix.set(bestExit.x, bestExit.y, 1);

Here is the room that I am using for testing: