better combat: 4 easy steps

(number 3 will blow your mind!)

right now the bot is pretty good at figuring out what the opponent is doing (be it either in broader terms with a general counter or if it can detect a specific strategy with a very specific counter play). what it is not good at yet is the execution of said strategies. lack of quality of execution is mostly noticable in combat. the bot loses battles even if it has a clear advantage in body parts or positioning. it loses track of priority targets, it lets hostiles retreat and heal up, amoung other bad micro.

the plan is to eventually work on proper combat simulation to help with these decisions. the idea of that workflow is to simulate the next n ticks of a given scenario, find the best simulated result and execute the move set that hopefully gets us the closest to that result in the actual game.

before that there are many easy pickings in terms of efficiency to be had though, so it is time to make a plan:

  1. have the right creeps in the right place (“what do we spawn?”)
  2. pick the right fights at the right time (“should we engage?”)
  3. pick the right targets in the fight (“what can we focus?”)
  4. retreat properly (if possible)

just having a basic solution for each of these steps should vastly improve on the combat code the bot has currently.

better combat step 1: the right creep in the right place

with the squad and cluster logic we have pretty good tools in place to write code to actually have combat creeps move where we need them to be generally. this is currently also working fairly well, sometimes they still get a bit too distracted following creeps outside their given target (outside combat).

having the right creep in the right place does not only require moving to the right place though, it also requires a setting up a military creep composition that ideally counters the opponents setup. it is time to detect what our opponent is doing specifically!

some of the strategy detection code already uses sums of body part counts in hostile creeps, but i want to detect the creep strategy in a less naive way (it will still be naive, but at least a bit less). the idea: assign creep bodies a “type” and reason with those creep types instead of body parts across all of the hostiles.

creep taxonomy

for the first iteration we will just look at whether a creep is fast (<non-move parts> / <move parts> < 1) and whether its a melee unit (mostly attack), a ranged unit (mostly ranged_attack) or a healer (mostly heal). later these categories can all be expanded on, so we will just ignore all potential edge cases for now.

it is then as easy as


export const getCreepType = (creep: Creep): CreepType => {
  const moveSpeed = creep.speed;
  const attack = creep.body.filter(b => b.type === ATTACK).length;
  const rangedAttack = creep.body.filter(b => b.type === RANGED_ATTACK).length;
  const heal = creep.body.filter(b => b.type === HEAL).length;
  const fast = moveSpeed < 1;
  const military = attack > 0 || rangedAttack > 0 || heal > 0;
  const melee = attack > rangedAttack;
  if (!military) {
    return CreepType.Civilian;
  }
  if (fast) {
    if (melee) {
      return CreepType.FastMelee;
    } else {
      return CreepType.FastRanged;
    }
  } else {
    if (melee) {
      return CreepType.SlowMelee;
    } else {
      return CreepType.SlowRanged;
    }
  }
  // return CreepType.Unknown;
}

next up: picking what to spawn to beat the detected types. this is basically the part where something like getBestCounterFor(type:CreepType):CreepType comes in to tell us what we need to spawn to stand the best chance against whatever the opponent is spawning. what i am currently doing (again a fairly naive approach that i can later expand on) is to just literally count all creep types we find in the hostile creeps and pick the counter for whatever type comes up the most.

last step: going from type to spawn request. here i just set up parameters for my body generator depending on the creep type. the body generator then gives back the best it can do given the current energy situation and given params, and then that body gets thrown into a spawn request to be hopefully spawned.

all our main military plays indiscriminately grab (and deal with) whatever military creep they can get, so whatever gets spawned will be picked up and used.

success! (unless the strategy layer has a very specific game plan,) we can now spawn creeps depending on what we think beats the opponent best in a given situation.

next up: pre-fight decision making.

stats

  • commit: 664db3b8cd2ea3dece1c6253db99f6ef933e9c19
  • ctf: rank #9, v30
  • sas: rank #71, v41