Gold Battle KoTH

44

13

This challenge has ended. To see the final scores of the competitors, click here

In this challenge, each submission is one bot. Each bot should be a Javascript function. Bots will fight to obtain the highest total worth in gold. Gold can be farmed, or earned from killing other bots, and is used to upgrade healing, attacking, shielding, and farming.

Objective:

Over a number of rounds containing up to 1000 turns (ends when only one bot is left), the bot with the highest total worth (the sum of all gold obtained) is the winner.

Turns:

In each turn, every bot which is alive (>0 HP) will be run once. It can return a move, which can be one of the following:

  • Heal: Regains HP
  • Attack: Removes HP from another bot
  • Shield: Defends against later attacks
  • Stun: Skips another bot's next turn
  • Farm: Earns gold at the cost of HP
  • Upgrade: Make certain moves better

All bots will return their move before any are executed, so a stun, heal, attack, shield, etc. will not affect any bots moving later in that turn. For example, if Bot A stuns Bot B, and Bot B is after Bot A in the turn order, Bot B will still move later in that same turn and the stun will occur on the next turn.

Combat, Farming, and Upgrading:

Each bot has a maximum HP of 100, and an assigned UID between 0 and 99. This UID changes after every round, and is how bots keep track of each other.

Healing is one of the simplest moves, adding an amount of HP determined by its level (starts at 5 HP). A bot cannot heal past 100 HP.

Attacking a bot by its UID is another possible move, with a base damage of 5 HP at level 0. Bots can also be stunned, skipping their next turn, which also uses UIDs.

Bots have additional shield HP, which has no limit. This shield HP will absorb damage from direct attacks from other bots, and is added by shielding. At level 0, shielding adds 5 shield HP.

Farming will earn 5 gold at level 0, at the cost of 2 HP. This 2 HP cannot be shielded. The only use for gold (beyond winning) is to upgrade moves. Healing, attacking, and shielding have a base value of 5 HP, and farming starts at 5 gold. Each of those moves have individual levels, which start at 0. These formulas will determine the value in HP or gold of a move, where L is the level:

  • Healing: L + 5
  • Attacking: 1.25L + 5
  • Shielding: 1.5L + 5
  • Farming: 2L + 5

The cost of upgrading any move is the same for a certain level, and is determined by 2.5L² + 2.5L + 10, where L is the current level. A bot can use the function cost(currentLevel) as a shortcut to determine this.

Bots start with 25 gold, allowing them to quickly upgrade either two moves to level 1, or one move to level 2. This beginning gold does not count towards a bots total worth. Killing a bot gives you half of its total worth in gold, rounded up, and if two bots kill another in the same turn, they both get the reward.

Input/Output:

In order to communicate with the controller, the return value of the function is used to send move information. One of these should be returned:

  • Heal: heal()
  • Attack: attack(uid)
  • Shield: shield()
  • Stun: stun(uid)
  • Farm: farm()
  • Upgrade: upgrade("heal" / "attack" / "shield" / "farm")

To skip a turn (do nothing), return nothing, or return a falsy value.

To get the current turn number (starts at 1), use turn().

The arguments of your function will include information about your bot, UIDs of other bots, and between-turn storage. The first argument is an object with the following properties: uid, hp, gold, and shield. These are copies of your bot's current information. There is also an nested object levels, with the level numbers of heal, attack, shield, and farm.

The second argument is a shuffled array of all alive bots other than yours, formatted as an object containing properties uid, hp (plus shield), worth, and attack (attack level). The third argument is an empty object which can be used for between-turn storage.

Example Bots:

This bot will farm until it can upgrade its attack to level 5, then attack a random bot each turn until it dies (or wins). Not very effective due to lack of healing/shielding.

function freeTestBotA(me, others, storage) {
    if (me.levels.attack < 5) {
        if (me.gold < cost(me.levels.attack))
            return farm();
        return upgrade("attack");
    }
    return attack(others[0].uid);
}

This bot has two modes: offensive and defensive. It will either stun a random bot or heal when in defensive mode, and it will either attack or shield when in offensive mode. It will attempt to upgrade its attacks whenever possible.

function freeTestBotB(me, others, storage) {
    if (me.gold >= cost(me.levels.attack))
        return upgrade("attack");
    if (me.hp < 50)
        if (Math.random() < 0.5)
            return stun(others[0].uid);
        else
            return heal();
    else
        if (Math.random() < 0.5)
            return attack(others[0].uid);
        else
            return shield();
}

Rules:

  • Standard Loopholes forbidden
  • Bots may not read, modify, or add any variables outside of their scope, may not attempt to cheat, and may not call any controller-defined or DOM functions
  • Return value must be falsy, or one of the above function outputs
  • Bots should not be designed to target a specific bot, but can be designed to take advantage of common strategies
  • Bots may not attack themselves (discovered due to a comment by @Ness)
  • Bots must be sufficiently different from any other bots that they can be reasonably considered separate entries
  • Teaming is now not allowed
  • Controller can be found here
  • Chatroom

New Controller Debugging:

Using the file gold-battle-log.js, you can set the value of the debug property of a bot in botData to 0 (no logging), 1 (log moves), or 2 (log moves, hp, gold, levels, etc.)

Challenge ends at 1700 UTC on Friday, August 9th

Redwolf Programs

Posted 2019-08-01T15:40:21.240

Reputation: 2 561

Comments are not for extended discussion; this conversation has been moved to chat.

– James – 2019-08-02T16:25:37.953

Note: Please use the chatroom instead of commenting here! – Redwolf Programs – 2019-08-02T16:31:03.130

4

Created a gist with all bots. https://gist.github.com/Draco18s/2efbf95edcf98d6b1f264e26bbb669d1 I will endeavor to keep it updated (but if not it's a decent start).

– Draco18s no longer trusts SE – 2019-08-04T14:52:05.540

4Auto-updating controller with bots included: https://www.redwolfprograms.com/koth – Redwolf Programs – 2019-08-07T15:36:41.097

4I'm voting to close this question because it's already de-facto closed to new answers ("This challenge has ended. To see the final scores ...") – pppery – 2019-09-03T20:29:13.907

3@pppery Could you not? I'd be fine with non-competitive answers, and the [closed] at the end is likely to make casual viewers skip over reading my challenge since they;d assume it's low quality or off-topic. – Redwolf Programs – 2019-09-04T03:11:13.823

You are not permitted to override the sitewide policy that answers must be serious contenders. No newly-posted answer can be a serious contender given that it is "non-competiive" Given that, no valid new answer to this question can be posted. The [closed] status serves to enforce that already-present social restriction technically, not to drive away casual viewers. Also, even if I wanted to, I can't "not"; I already case a close vote when I made that comment and 3 other users have agreed with me in the review queue. – pppery – 2019-09-04T03:30:29.980

5@pppery I've never heard of a challenge being closed for being finished until today, and I'd argue the social restriction you want to enforce doesn't even exist. There's no need to close it, and I don't want it closed. To me, that seems like closing for the sake of closing, rather than for the good of the site. If someone wants to post an answer to an old question, they should be able to. There's no note after the serious contender rule saying it has to be a serious contender when it's posted; an answer can still be a serious contender for the challenge even if it's not a contender for winning – Redwolf Programs – 2019-09-04T03:35:48.950

Answers

16

Unkillable

forked from Undyable.

function UnkillableBot(me){
    if(me.hp <= 100 - (me.levels.heal + 5)){
        return heal()
    }else if(turn() % 10 == 0 && me.shield < 800) {
        return shield()
    }else{
        if(me.gold >= cost(me.levels.shield) && me.levels.shield <= 9){
            return upgrade("shield")
        }else if(me.gold >= cost(me.levels.farm)){
            return upgrade("farm")
        }else{
            if(me.shield < 500 && me.levels.shield > 4) {
                return shield()
            }
            return farm()
        }
    }
}

Given the exponential costs of upgrades, may as well upgrade farming if we can't upgrade healing, allowing the bot to collect gold more efficiently.

Draco18s no longer trusts SE

Posted 2019-08-01T15:40:21.240

Reputation: 3 053

Absolutely crushing the competition in my tests – Redwolf Programs – 2019-08-01T19:20:02.567

1I feel this bot might be a little stronger were that first if statement used <= - currently it will never heal to full. – Scoots – 2019-08-02T13:30:26.380

@Scoots Not sure how much it'll matter, but I'll change that. – Draco18s no longer trusts SE – 2019-08-02T13:41:12.953

2@Draco18s I'm sure it matters very very little - but isn't this site all about tiny little practically insignificant improvements? :) – Scoots – 2019-08-02T13:43:04.623

@Scoots Healing to max health does not matter much in this challenge because there are no real attacking threats. The only truly offensive bot is bullybot, and you cannot really do anything about him. It might actually decrease performance to stay at full health. – B0RDERS – 2019-08-02T14:58:33.980

@B0RDERS And even then, bullyBot doesn't attack; it just stuns. Without outside attacks on your bot, it won't even hurt you aside from losing turns. – Redwolf Programs – 2019-08-05T19:11:22.683

13

ThanosBot

function ThanosBot(me, others, storage){
    if(turn()==1){
        storage.origPopulation = others.length;
        return upgrade("attack");
    }

    if (others.length < storage.origPopulation / 2)
    {
        if(me.hp <= 100 - (me.levels.heal + 5)){
            return heal();
        }
        else {
            return farm();
        }
    }

    if(me.hp <= 100 - (me.levels.heal + 5)){
        return heal()
    }else{
        if(me.gold >= cost(me.levels.attack)){
            return upgrade("attack")
        }else if(me.gold >= cost(me.levels.heal)){
            return upgrade("heal")
        }else if(me.gold >= cost(me.levels.farm)){
            return upgrade("farm")
        }else{
            if(Math.random() < 0.5){
                return attack(others[0].uid);
            }
            else{
                return farm();
            }
        }
    }
}

There are too many bots, not enough gold to go around. This bot proposes a solution.

Genocide, yes, but random, dispassionate, fair to rich and poor alike.

They called him a madman.

ThanosBot wants the best for the bot community, and is willing to go all the way. In the beginning, he will upgrade his attack, farming and healing, to more efficiently gather resources and win battles. Prograssively, he will start attacking people randomly while still gathering resources, for upcoming battles. He will keep improving his army, his weapons and himself.

Once 50% of the population has been eliminated, bots born will only know full bellies and clear skies, he will retire to a life of farming, and watch the sun rise on a grateful universe. He will become completely pacifist, only healing himself with vegetable soups and farming.

Kaito Kid

Posted 2019-08-01T15:40:21.240

Reputation: 303

6I'm tempted to rename "attack" to "snap" – Redwolf Programs – 2019-08-05T19:08:00.503

11

Kill Stealer

function killStealer({hp, gold, attack:atck, shield:shld, levels:{heal:lHeal, shield:lShld, farm:lFarm, attack:lAtck}}, es, S) {
  let saneReduce = (a, f, n) => a.length? a.reduce(f) : n;
  let t = turn();
  if (t===1) {
    S.worth = 0;
    S.pHP = 100;
    S.pGold = 0;
    S.stat = {};
    S.pT = 0;
    for (let e of es) S.stat[e.uid] = {kills:0, seen:0};
  }

  let pT = S.pT;
  S.pT = t;

  let shp = shld+hp;

  let healP = lHeal      + 5;
  let shldP = lShld*1.5  + 5;
  let farmP = lFarm*2    + 5;
  let atckP = lAtck*1.25 + 5;
  let pheal = () => hp<5  ||  Math.min(100, hp+healP)-hp > shldP? heal() : shield();

  let attacked = S.pHP-hp-shld > 2;
  S.pHP = hp+shld;

  if (gold>S.pGold  &&  t!=1) S.worth+= gold-S.pGold;
  S.pGold = gold;

  let pes = S.pEs;
  let ces = {};
  for (let e of es) ces[e.uid] = {uid:e.uid, hp:e.hp, worth:e.worth};
  S.pEs = ces;

  if (t === 1) return shield(); // to not break things depending on previous frame

  if (t == pT+1) {
    for (let uidE in pes) {
      let e = pes[uidE];
      if (!ces[uidE]) { // dead
        if (e.worth < 30) continue; // don't bother, because others probably won't
        for (let a of es) {
          let pa = pes[a.uid];
          if (a.worth >= pa.worth + e.worth/2 - 2) {
            S.stat[a.uid].kills++;
          }
          if (a.worth != pa.worth || a.hp > pa.hp) S.stat[a.uid].seen++;
        }
      }
    }
  }


  let attackers = es.filter(c => {
    let k = S.stat[c.uid].kills;
    let s = S.stat[c.uid].seen;
    return k > 1  &&  k > s*.7;
  });
  let maxDmg = es.map(c=>c.attack).reduce((a, b) => Math.max(a, b), 0)*1.25 + 5;
  for (let e of es) {
    if (e.worth < farmP) continue;
    let p = pes[e.uid];
    let dmg = p.hp-e.hp;
    if (e.hp <= atckP) {
      return attack(e.uid);
    }
    if (e.hp-dmg-atckP <= 0) {
      return attack(e.uid);
    }
    if (e.hp-maxDmg-atckP <= 0) {
      return attack(e.uid);
    }
    if (e.hp-maxDmg-dmg <= 0) {
      return attack(e.uid);
    }
  }
  if (attackers.length>0 && t>50) {
    for (let e of es) {
      if (e.hp - maxDmg*2 - atckP <= 0  &&  e.worth > 200) {
        let worst = saneReduce(attackers.filter(c => c.hp > 80), (a, b)=>a.worth>b.worth? a : b, null);
        if (worst) return stun(worst.uid);
      }
    }
  }



  if (t < 60  &&  t%5 == 1) return shield();
  if (t === 2) return upgrade("heal");
  if (t === 3) return upgrade("farm");
  if (t%10 == 1) return shield();

  if (gold>=cost(lShld) && lFarm>-2) return upgrade("shield");
  if (gold>=cost(lFarm) && !attacked) return upgrade("farm");

  if (es.length > 2) {
    let notDead = es.filter(c => c.hp > 20);
    if (notDead.length !== 0) {
      notDead.sort((a, b) => a.hp-b.hp);
      if (notDead[Math.min(2, notDead.length-1)].hp > shp) {
        return pheal();
      }
    }
  }


  if (gold>=cost(lHeal)  &&  lHeal+5 < lFarm) return upgrade("heal");
  if (gold>=cost(lAtck)  &&  lAtck+5 < lFarm  &&  es.every(c=>c.attack<=lAtck+2)) return upgrade("attack");

  if (lShld>5  &&  shp < 205+healP+t  &&  shp < 600+t*5) return pheal();
  if (es.every(c => c.worth < S.worth+farmP) && es.length>2 && t<100 && lShld<6) return pheal();
  if (shp<=120  ||  hp<5) return pheal();
  return farm();
}

Now not only steals kills, but steals stealing kills too!

This bot doesn't do much except farm, and when it notices the possibility, joins in doing the final blow to a dying enemy, and somehow manages to be very good.

dzaima

Posted 2019-08-01T15:40:21.240

Reputation: 19 048

It works because all bots involved in a killing blow get the full reward. – Draco18s no longer trusts SE – 2019-08-04T03:35:31.417

@Draco18s I understand why it could be good, I just didn't expect such a simple idea to get on average 2x the score of the next best bot (at the time of making it). – dzaima – 2019-08-04T03:38:16.727

Hehe, that's fair. I'll have to download all the bots when I can and see if I can find another solution. – Draco18s no longer trusts SE – 2019-08-04T04:02:32.773

9

The Equalizer

This bot seeks to restore peace in the bot community. He relentlessly targets the bots with the highest attack, giving up only if the bot's healing is better than his own attack. Once no bots with healing worse than its attack are left, he will retire to a life of peaceful farming.

function equalizer(me, others, storage){
  if(storage.agroKilled == null)storage.agroKilled = false;
  if(!storage.agroKilled){
    if(storage.blacklist == null)storage.blacklist = [];
    if(storage.lastAttack == null)storage.lastAttack = -1;
    var maxAtk = 0;
    var maxAtkUid = -1;
    var maxAtkHealth = 0;
    for(var i = 0; i < others.length; i++)if(others[i].uid == storage.lastAttack){
      maxAtk = others[i].attack*1.25+5;
      maxAtkUid = storage.lastAttack;
      maxAtkHealth = others[i].hp;
    }
    for(var i = 0; i < others.length; i++){
      if(storage.lastAttack == others[i].uid && others[i].hp >= storage.lastHealth){
        maxAtk = 0;
        maxAtkUid = -1;
        maxAtkHealth = 0;
        storage.blacklist.push(others[i].uid);
      }
    }
    storage.lastAttack = -1;
    var willHeal;
    for(var i = 0; i < others.length; i++)if(others[i].attack*1.25+5 > maxAtk){
      willHeal = false
      for(var j = 0; j < storage.blacklist.length; j++)if(others[i].uid==storage.blacklist[j])willHeal = true;
      if(!willHeal){
        maxAtk = others[i].attack*1.25+5;
        maxAtkUid = others[i].uid;
        maxAtkHealth = others[i].hp;
      }
    }
    if(me.hp < maxAtk) return heal();
    if(me.hp <= 100 - me.levels.heal - 5) return heal();
    var target = -1;
    var targetWorth = me.levels.farm * 2 + 5;
    for(var i = 0; i < others.length; i++) {
      if (others[i].hp <= maxAtk && others[i].worth / 2 > targetWorth) {
        target= others[i].uid;
          targetWorth = others[i].worth / 2;
      }
    }
    if(target!=-1) return attack(target);
    if(me.gold >= cost(me.levels.attack)) return upgrade("attack");
    if(me.levels.heal + 7 < me.levels.attack && me.levels.heal < 9 && me.gold >= cost(me.levels.heal)) return upgrade("heal");
    if(maxAtkUid!=-1){
      storage.lastAttack = maxAtkUid;
      storage.lastHealth = maxAtkHealth;
      return attack(maxAtkUid);
    }
    storage.agroKilled = true;
  }
  if(me.hp < 30) return heal();
  if(me.gold > cost(me.levels.farm)) return upgrade("farm");
  return farm();
}

B0RDERS

Posted 2019-08-01T15:40:21.240

Reputation: 311

8

Optimist

function Optimist(me, others, storage) {
    if (me.hp < 10)
        return heal();
    if ( (me.hp + me.shield) < 50 )
        return shield();
    if (me.gold >= cost(me.levels.farm) && cost(me.levels.farm) < 0.8 * (1000 - turn()))
        return upgrade("farm");
    rich_bots = others.sort( (x,y) => y.worth - x.worth );
    potential_victim = rich_bots.find( bot => bot.hp <= me.levels.attack * 1.25 + 5 );
    if (potential_victim)
        return attack(potential_victim.uid);
    if (me.gold < rich_bots[0].worth + cost(me.levels.farm) + 25)
        return farm();
    if (me.levels.heal < me.levels.farm)
        return upgrade("heal");
    if (me.levels.shield < me.levels.heal)
        return upgrade("shield");
    if (me.levels.attack < me.levels.shield)
        return upgrade("attack");
    return shield();
}

Assumes it will be able to spend 80% of its time peacefully farming, so it starts off by maxing out farming, and only then starts paying any attention to its combat skills. Surely nothing will go wrong!

histocrat

Posted 2019-08-01T15:40:21.240

Reputation: 20 600

8

Kill Assist

function KillAssist(me, others, storage) {
  let t = turn();
  if (t===1) {
    storage.worth = 0;
    storage.pHP = 100;
    storage.pGold = 0;
  }
  let hp = me.hp;
  let gold = me.gold;
  let shld = me.shield;
  let lHeal = me.levels.heal+0.25;
  let lFarm = me.levels.farm;
  let lShld = me.levels.shield;
  let lAtck = me.levels.attack;
  let healPower = lHeal      + 4.75;
  let shldPower = lShld*1.5  + 5;
  let farmPower = lFarm*2    + 5;
  let atckPower = lAtck*1.25 + 5;

  let dmgTaken = storage.pHP-(hp+shld);
  let attacked = dmgTaken > 2;
  storage.pHP = (hp+shld);

  if (gold > storage.pGold) storage.worth+= gold-storage.pGold;
  if (gold-storage.pGold > farmPower+5)  storage.lastAtck = -10;
  storage.pGold = gold;
  let pOthers = storage.pOthers;
  storage.pOthers = {};
  for (let o of others) {
    storage.pOthers[o.uid] = {hp: o.hp, uid: o.uid, worth: o.worth};
  } 

  if (t === 1 || t === 2) return upgrade("shield");
  if (t === 3) return shield();

  let maxdmg = others.map(c=>c.attack).reduce((a, b) => Math.max(a, b))*1.25 + 5;
  let lowhp = others.map(c=>c.hp).reduce((a, b) => Math.min(a, b));
  let lowhpid = others.find(c=>c.hp == lowhp).uid;
  let maxAttacker = others.find(o => o.attack*1.25 + 5 == maxdmg).uid;
  for (let o of others) {
    if (o.hp < atckPower  &&  o.worth > farmPower) {
      storage.dead = o.uid;
      storage.deadWorth = o.worth;
      return attack(o.uid);
    }
    let pO = pOthers[o.uid];
    let dmg = pO.hp - o.hp;
    if (o.hp - dmg - atckPower <= atckPower && o.worth >= farmPower) {
      storage.dead = o.uid;
      storage.deadWorth = o.worth;
      return attack(o.uid);
    }
    if (o.hp - maxdmg - atckPower <= atckPower && o.worth >= farmPower) {
      storage.deadWorth = o.worth;
      return attack(o.uid); 
    }
  }
  let lowhpdiff = Math.max(pOthers[lowhpid].hp - others.find(o => o.uid == lowhpid).hp,0);
  if (others.some(o => o.hp > maxdmg && o.hp < lowhpdiff*2+atckPower+maxdmg && o.worth > farmPower)) {
    let bad = others.reduce((a, b) => a.worth>b.worth? a : b);
    let bad2 = others.reduce((a, b) => bad.uid == b.uid ? a : (bad.uid == a.uid ? b : (a.worth>b.worth ? a : b)));
    if(bad.worth < bad2.worth*3 && bad.hp >= (maxdmg+atckPower)*2 && bad.uid != maxAttacker && bad.uid != lowhpid) {
      return stun(bad.uid);
    }
    if(bad2.hp >= (maxdmg+atckPower)*2 && bad2.uid != maxAttacker && bad.uid != lowhpid) {
      return stun(bad2.uid);
    }
  }

  if (t%10 == 9  &&  lShld>4) return shield(); // slowly build up shield just in case
  if (shld+hp < 100) return shldPower>healPower || hp >= 100-healPower? shield() : heal();

  var bon = shldPower-maxdmg < 3 && t < 700 ? lShld/2 : 0;
  var bon2 = t/100;
  if (gold>=cost(lFarm) && lShld+2 > lFarm && bon == 0 && !attacked) return upgrade("farm"); // farm first, but make sure it doesn't get too far ahead
  if (gold>=cost(lShld) && t>20 && (lShld<10+bon || lShld+5+bon2 < lFarm+bon) && t < 900) return upgrade("shield");
  if (gold>=cost(lFarm)) return upgrade("farm"); // try upgrading farming again, because shield upgrading can be picky
  if (gold>=cost(lHeal) && (lHeal<3)) return upgrade("heal"); // healing isn't that important

  if (shld<200 && attacked || shld<500 && t>20 && others.filter(c=>c.hp>=100).every(o=>o.hp+10 > hp+shld)) return shldPower>healPower || hp >= 100-healPower? shield() : heal();

  let hpdelta = attacked ? dmgTaken+shldPower : maxdmg
  if (shld<lShld*60 && (1000-t)*(hpdelta) > shld+hp) return shield(); // we want to look impressive & terrifying
  if (hp<=100-healPower) return heal();

  return farm();
}

Why upgrade attack value when you can do plink damage and still get full credit?

Once more back to piggybacking off Kill Stealer. I was able to simplify several blocks of code where statements were always true and fiddle with some numbers that resulted in massive gains over the original.

I have to hand it to @dzaima for realizing that stunning a wealthy opponent that is likely to be involved in an assist the turn before a kill happens is quite clever. One of the (very) few times Stun() has a positive sum outcome. Once again I was able to improve upon the idea, as knowing that Kill Stealer will be running similar logic, Kill Assist looks for a "second best" target (with some discretion) and stuns them instead.

Minor update to prevent stunning the bot-about-to-die and prevent stunning the bot-most-likely-to-make-the-kill.

Sample results (truncated top 5 after 1000 games)

VM2406:1629 Kill Assist: 39495.679
VM2406:1629 The Accountant: 29990.267
VM2406:1629 Kill Stealer: 23530.153
VM2406:1629 Unkillable: 12722.604
VM2406:1629 captFarmer: 12232.466

Draco18s no longer trusts SE

Posted 2019-08-01T15:40:21.240

Reputation: 3 053

Wait, in what world does Captain Farmer get 14k gold? – Redwolf Programs – 2019-08-06T21:15:25.300

This one: runGame(1) results: [...] captFarmer: 13768 – Draco18s no longer trusts SE – 2019-08-06T21:23:12.880

That's pretty unexpectedly high...it usually gets around 10k in my tests – Redwolf Programs – 2019-08-07T01:12:38.837

*shrugh* No idea. I'll do an automated gist update just to insure everything's clean. – Draco18s no longer trusts SE – 2019-08-07T02:22:46.513

My favorite bot by the end of the deadline. – Night2 – 2019-08-09T18:27:53.993

7

Undyable Bot (v3)

function undyableBot(me, others, storage){    

    if(me.hp < 100 - (me.levels.heal + 5)*2){
        return heal()
    }else{
        if(me.levels.heal < 10 && cost(me.levels.heal) / 2 < cost(me.levels.farm)){
            if(me.gold >= cost(me.levels.heal)){
                return upgrade("heal")
            }else{
                return farm()
            }
        }else{
            if(me.gold >= cost(me.levels.farm)){
                return upgrade("farm")
            }else{
                return farm()
            }
        }        
    }   
}

Luis felipe De jesus Munoz

Posted 2019-08-01T15:40:21.240

Reputation: 9 639

Don't mind me...I'm going to borrow this. – Draco18s no longer trusts SE – 2019-08-01T17:56:36.423

6

Switzerland

function switzerland(self,others,storage){
    let turnsLeft=999-turn()
    let lowestHpBots=others.sort((a,b)=>a.hp-b.hp)
    if(!storage.worth){
        storage.worth=0
        storage.prevGold=25
    }else if(self.gold>storage.prevGold){
        storage.worth+=self.gold-storage.prevGold
    }
    if(others.length===1&&storage.worth>others[0].worth){
        //stun lock the other bot if there are only 2 left and I can win
        return stun(others[0].uid)
    }else if(self.hp<=(95-self.levels.heal)){
        return heal()
    }else if(lowestHpBots[0]&&lowestHpBots[0].hp<20&&lowestHpBots[0].worth/2>2*self.levels.farm+5&&self.hp+self.shield>=110){
        //kill assist
        return attack(lowestHpBots[0].uid)
    } else if(self.shield<=50||self.shield<=5500/others.length&&self.shield<=1200&&turn()>=20||lowestHpBots[1]&&lowestHpBots[1].hp>self.hp+self.shield){
        return shield()
    }else if(self.gold>=cost(self.levels.shield)&&self.levels.shield<=8){
        return upgrade("shield")
    } else if(self.gold>=cost(self.levels.farm)&&(turnsLeft+1)*(2*(self.levels.farm)+5)<turnsLeft*(2*(self.levels.farm+1)+5)){
        return upgrade("farm")
    } else if(self.gold>=cost(self.levels.heal)&&(turnsLeft+1)/(self.levels.heal+5)*(2*self.levels.farm+5)<turnsLeft/(self.levels.heal+6)*(2*self.levels.farm+5)&&self.levels.heal<=2){
        return upgrade("heal")
    }else{
        return farm()
    }
}

Like the name suggests, this bot is neutral mostly neutral(now it helps kill bots that are going to die) and just farms and heals, slowly building up its gold(just like switzerland)

SuperStormer

Posted 2019-08-01T15:40:21.240

Reputation: 927

6

PatientStrategistBot

I tried to write a bot that starts out framing and defending as needed and then switches to killing other high value bots later in the game.

Currently this doesn't seem to work properly since its either killed by a gang of murder-bots in the start of the game or gets stuck somewhere in its offensive mode.

Still pretty happy with this being my first JS code, so ... (I did steal code snippets from here & there cause that was faster than googling all of the JS basic syntax)

function PatientStratgistBot(me, others, storage) {

    //set up some stuff in first turn
    if (turn() == 1) {
    storage.selfWorth = 0;
    storage.attackMode = false;
    storage.expectHP = 100;
    storage.expectShield = 0;
    storage.shieldTarget = 0;
    storage.targetUid = "None";
    storage.attackRounds = 0;
    storage.targetStartHP = 100;

        return upgrade("farm");
    }

    let farmPower = me.levels.farm * 2 + 5;

    //defensive Actions

    var maxAtk = Math.max(...others.map(o => o.attack));

    storage.shieldTarget = Math.ceil(maxAtk * 1.25 / 1.5) + 1;

    if (me.levels.shield < storage.shieldTarget && me.gold >= cost(me.levels.shield) && me.levels.shield < me.levels.farm)
        return upgrade("shield");

    if (turn() >= 7 && me.shield < 10 && me.levels.shield * 1.5 >= me.levels.heal) return shield();

    if (turn() >= 15 && me.shield < 15 && me.levels.shield * 1.5 >= me.levels.heal) return shield();

    if (turn() >= 30 && me.shield < 20 && me.levels.shield * 1.5 >= me.levels.heal) return shield();

    //attack mode
    // check if there any targets worth to go for

    function findTarget(potentialTargets, baseR){
    var targetUID = "None";
    var best = 0;
    for( var i = 0; i < potentialTargets.length; i++) {
        //We upgrade to attack lvl12, so 20 dmg; assume an enemy can heal/shield up to 15 per round
        var killRounds = Math.ceil(potentialTargets[i].hp / 5)
        var gain = potentialTargets[i].worth / ( 2 * ( killRounds + baseR) )
        //console.log(me, turn(), potentialTargets[i], killRounds, baseR, gain, farmPower)
        if (gain > farmPower * ( killRounds + baseR ) && gain > best)
            targetUID = potentialTargets[i].uid;
            storage.targetStartHP =  potentialTargets[i].hp;
    }
    return targetUID;
    }


    if (turn() >= 600) {


    //check if a current target is dead
    const uids = others.map(x=>x.uid);
        if(storage.targetUid != "None" && !uids.includes(storage.targetUid)) {
        storage.targetUid = "None";
        storage.attackMode = false;
        storage.attackRounds = 0;
    }


    // check if we are doing enough damage to current target
    if (storage.targetUid != "None" && storage.attackRounds >= 3) {

        var deltaHP = storage.targetStartHP - others[storage.targetUid].hp

        if (deltaHP / storage.attackRounds < 5) {
            storage.targetUid = "None";
            storage.attackMode = false;
            storage.attackRounds = 0;

        }

    }

    var investCost = 0
    for( var i = me.levels.attack; i < 12; i++) investCost += cost(i);

    if (storage.attackMode == true && me.gold >= investCost && me.levels.attack < 12) return upgrade("attack");

    if (storage.attackMode == false) {
        baseRounds = investCost / farmPower * 1.2; //overestimation with the heal level we should have at this point

        if (findTarget(others, baseRounds) != "None")
            storage.attackMode = true;

        var betterThanMe = others.filter(o => o.worth >= storage.selfWorth);

        if (betterThanMe.length > 0)
            storage.attackMode = true;

        //storage.attackMode = true;


    }

    }

    if (storage.attackMode == true && me.levels.attack == 12) {

    if (storage.targetUid == "None") {

        var target = findTarget(others, 0)
        storage.targetUid = target;
        storage.attackRounds = 0;
        return attack(target);

    }

    return attack(storage.targetUid)

    }



    //otherwise farm

    if (me.hp < 50) {
    storage.expectHP += 5 + me.levels.heal;
        return heal();
    }

    if (me.gold >= cost(me.levels.farm) && storage.attackMode == false)
        return upgrade("farm");

    //upgrade heal, so we can farm more, but increase farm ability faster
    if (me.levels.farm > 5 && me.levels.heal < 10 && me.gold >= 2*cost(me.levels.heal))
        return upgrade("heal");


   //be opportunistic - check if killing someone is more profitable than farming
    killable = others.filter(o => o.hp < me.levels.attack * 1.25 + 5 && o.worth / 2 > farmPower);
    if (killable.length > 0){
    //ideally check for the most worth target here
        return attack(killable[0].uid);
    }

    storage.expectHP -= 2;
    storage.selfWorth += farmPower;
    return farm();

}

Nicolai

Posted 2019-08-01T15:40:21.240

Reputation: 161

6

The Bot That Farms, Attacks, Shields, And Even Heals But Never Stuns

(Short name is TBTFASAEHBNS, not to be mistaken with TBTPTGCBCBA)

function TBTFASAEHBNS(me, others, storage) {
    this.getLevel = function (type) {
        return (typeof me.levels[type] === 'undefined' ? 0 : me.levels[type]);
    };

    this.getPower = function (type, level) {
        if (typeof level === 'undefined') level = this.getLevel(type);
        if (type === 'heal') return level + 5;
        if (type === 'attack') return (level * 1.25) + 5;
        if (type === 'shield') return (level * 1.5) + 5;
        if (type === 'farm') return (level * 2) + 5;
    };

    this.canUpgrade = function (type) {
        return myGold >= cost(this.getLevel(type));
    };

    this.farmOrUpgradeFarm = function () {
        if (this.canUpgrade('farm')) return upgrade('farm');
        if (myHp < 3) return heal();
        return farm();
    };

    let currentTurn = turn(),
        myGold = me.gold,
        myHp = me.hp,
        myShield = me.shield,
        myTotalHp = myHp + myShield,
        myHealPower = this.getPower('heal'),
        myShieldPower = this.getPower('shield'),
        myAttackPower = this.getPower('attack'),
        myFarmPower = this.getPower('farm'),
        topAttackPower = 0,
        attackOptions1 = [],
        attackOptions3 = [],
        attackOptions2 = [],
        finalTurns = 980;

    if (currentTurn === 1) {
        storage.othersInfo = {};
    }

    others.sort((a, b) => b.attack - a.attack);
    for (let i = 0; i < others.length; i++) {
        let other = others[i];

        if (i < 3) topAttackPower += this.getPower('attack', other.attack);

        if (other.worth > myFarmPower) {
            if (other.hp <= myAttackPower) {
                attackOptions1.push(other);
            } else {
                if (typeof storage.othersInfo[other.uid] !== 'undefined') {
                    let otherHpChange = storage.othersInfo[other.uid].hp - other.hp;

                    if (other.hp - otherHpChange <= 0) {
                        attackOptions2.push(other);
                    } else if (other.hp - (otherHpChange * 3) <= 0) {
                        attackOptions3.push(other);
                    }
                }
            }
        }

        storage.othersInfo[other.uid] = {hp: other.hp};
    }

    if (myTotalHp < (topAttackPower * 7) + 5) return shield();
    if (currentTurn <= 10) return this.farmOrUpgradeFarm();

    if (attackOptions1.length > 0) {
        attackOptions1.sort((a, b) => b.worth - a.worth);
        return attack(attackOptions1[0].uid);
    } else if (attackOptions2.length > 0) {
        attackOptions2.sort((a, b) => b.worth - a.worth);
        return attack(attackOptions2[0].uid);
    } else if (attackOptions3.length > 0) {
        attackOptions3.sort((a, b) => b.worth - a.worth);
        return attack(attackOptions3[0].uid);
    }

    if (currentTurn <= 20) return this.farmOrUpgradeFarm();
    if (currentTurn < finalTurns && myShieldPower < topAttackPower / 2 && Math.random() * 15 < 1 && this.canUpgrade('shield')) return upgrade('shield');
    if (currentTurn < finalTurns && this.canUpgrade('farm')) return upgrade('farm');
    if (currentTurn < finalTurns && myHealPower < 10 && this.canUpgrade('heal')) return upgrade('heal');
    if (myHp < 3) return heal();
    return farm();
}

This bot basically:

  • Builds up on farming at start
  • Defends itself when needed
  • Attacks when it can kill or when it thinks there is a chance to kill someone
  • Upgrades here and then
  • Farms the rest of the time
  • Never stuns

Edit 1: Fixed an issue and improved some tiny things in the bot based on tests with lots of games.

Edit 2: Reduced shield upgrades.

Night2

Posted 2019-08-01T15:40:21.240

Reputation: 5 484

2As soon as I saw the name I knew it would be your bot (: – Redwolf Programs – 2019-08-07T16:58:06.283

I'm sorry about the long names, but I'm addicted to it! – Night2 – 2019-08-07T16:59:17.713

1Maybe it's a sign of a good bot...my tests show it's in 5th place – Redwolf Programs – 2019-08-07T17:00:12.963

5

JustFarm

I thought I'd start simple.

function justFarm(me, others){
    return farm();
}

Anonymous

Posted 2019-08-01T15:40:21.240

Reputation: 89

13This bot will suicide due to the 2HP cost of farming. – Draco18s no longer trusts SE – 2019-08-01T17:28:27.000

@Draco18s Although the round may end before that, depending on the number of bots – Redwolf Programs – 2019-08-01T17:52:21.130

1While technically true, a 50 round timer is very very short when the default end time is 1000. – Draco18s no longer trusts SE – 2019-08-01T17:57:57.810

It beat the two example bots, but now there are a few more submissions I might try to come up with something better. – Anonymous – 2019-08-01T18:27:25.140

@Anonymous Maybe including healing and farming upgrades will be enough. Since getting the most gold is the ultimate goal, keeping that as the bot's main job could work. There haven't been any bots so far which have "modes" like heal mode and farm mode, might be an interesting approach – Redwolf Programs – 2019-08-01T20:23:49.167

5

bullyBot

function bullyBot(me, others, storage){
    if(turn()==1){return farm();}
    if(storage.bullyTarget==null){storage.bullyTarget=others[0].uid;}

    var targetlives = false;
    for(var i = 0; i < others.length; i++) {
        if (others[i].uid == storage.bullyTarget) {
            targetlives = true;
            break;
        }
    }
    if(!targetlives){storage.bullyTarget = others[0].uid;}

    return stun(storage.bullyTarget);
}

Try it online!

May not win but will certainly try his damndest to make sure his target doesn't either. bullyBot also farms on the first turn so that if there's no outside influence, he will beat his target 5-0 or tie them 5-5.

Veskah

Posted 2019-08-01T15:40:21.240

Reputation: 3 580

5

SniperBot

This bot will only be effective if someone starts adding bots that actually attack on a regular basis. SmartFarmer is my current optimized solution

  1. heals if can get one-shot
  2. heals if under 30
  3. attacks bot if can pick it off and earn more money than farming
  4. upgrades farming if can afford
  5. upgrades healing if is under 80 health and can afford
  6. farms

vultures don't need an attack

function sniperBot(me, others){
    if(me.hp < 30) return heal();
    for(var i = 0; i < others.length; i++)if(others[i].attack > me.hp)return heal();
    var target = -1;
    var targetWorth = me.levels.farm * 2 + 5;
    for(var i = 0; i < others.length; i++) {
        if (others[i].hp <= 1.25 * me.levels.attack + 5 && others[i].worth / 2 > targetWorth) {
            target= others[i].uid;
            targetWorth = others[i].worth / 2;
        }
    }
    if(target!=-1) return attack(target);
    if(me.gold >= cost(me.levels.farm)) return upgrade("farm");
    if(me.hp < 50 && me.gold >= cost(me.levels.heal)) return upgrade("heal");
    return farm();
}

B0RDERS

Posted 2019-08-01T15:40:21.240

Reputation: 311

Unexpected identifier (int) on line 2. ReferenceError: health is not defined. – Draco18s no longer trusts SE – 2019-08-01T20:06:56.210

Should be me.hp? – mbomb007 – 2019-08-01T20:19:48.600

sorry. new to javascript. thanks for the help – B0RDERS – 2019-08-01T20:26:29.073

Your if(me.hp <30 && ...) could be simplified to just the first clause on account of needing an absurd level of heal for it to matter (lvl 65) – Veskah – 2019-08-02T14:43:22.380

@Veskah Thanks for pointing that out. That was a remnant from when the min hp was higher – B0RDERS – 2019-08-02T14:44:50.413

Oh, I guess one more thing, you can get some free run speed performance (not that it matters) by moving that check above the "See if others can kill me" check. Saves flipping through all the bots – Veskah – 2019-08-02T14:48:31.377

5

BullyDozerBot

function BullyDozerBot(me, others, storage){
    if(me.gold >= cost(me.levels.attack) && (storage.bullyTarget && storage.bullyTarget.hp < 500)) {
        return upgrade("attack");
    }
    if(storage.bullyTarget==null){
        storage.bullyTarget=others.sort((a,b) => a.hp - b.hp)[0];
    }
    potential_victim = others.find( bot => bot.hp <= me.levels.attack * 1.25 + 5 );
    if (potential_victim) {
        return attack(potential_victim.uid);
    }
    var targetlives = false;
    for(var i = 0; i < others.length; i++) {
        if (others[i] == storage.bullyTarget) {
            targetlives = true;
            break;
        }
    }
    if(!targetlives){
        storage.bullyTarget=others.sort((a,b) => a.hp - b.hp)[0];
    }
    if(storage.bullyTarget.hp >= 500) {
        if(me.gold >= cost(me.levels.farm)) {
            return upgrade("farm");
        }
        for(var i = 0; i < others.length; i++){
          if(others[i].attack*1.25+10 > me.hp){
            return heal();
          }
        }
        return farm();
    }
    return attack(storage.bullyTarget.uid);
}

Mashup of BullyBot and some other bits. Optimist had a short and sweet opportunistic attack chunk that I yoinked (though other bots do similar calculations).

Instead of bullying the target by stunning them, it murders them for their sweet, sweet loot. It also targets the weakest in the herd for bullying, but it will give up and just go farming if the weakest target's HP is too high.

Draco18s no longer trusts SE

Posted 2019-08-01T15:40:21.240

Reputation: 3 053

you are farming yourself to death. Accept my edit :) – B0RDERS – 2019-08-02T15:18:58.087

1@AndrewBorders Ha, didn't even think about it. Thanks. – Draco18s no longer trusts SE – 2019-08-02T16:50:27.917

This bot was great until those protector bots came around. – B0RDERS – 2019-08-02T19:04:02.503

@B0RDERS Shield is very strong, even if it wastes time. – Draco18s no longer trusts SE – 2019-08-02T19:21:45.657

5

FizzBuzz

function FizzBuzz(me, others, storage) {
    if (!storage.target) storage.target = others[0].uid;
    const uids = others.map(x=>x.uid);
    if(!uids.includes(storage.target) || (turn() % 30 === 0 
        && others[uids.indexOf(storage.target)].hp>30))
        storage.target = others[0].uid;

    if (cost(me.levels.farm) < me.gold) return upgrade("farm");
    if (turn() % 15 === 0) return heal();
    if (turn() % 3 === 0) return farm();
    if (turn() % 5 === 0) return heal();

    if (cost(me.levels.attack) < me.gold) return upgrade("attack");
    return attack(storage.target);
}

Mostly offensive bot. Extremely upset by the fact that it can't truly FizzBuzz so it just Buzzes angrily instead. When it's not Fizzing or Buzzing, it chips away at another bot for 30 turns and gives up and chooses another bot to target if it isn't making progress.

Performs extraordinarily inconsistently. Never mind, updated the controller, now seems to always be around the middle of the pack.

Rin's Fourier transform

Posted 2019-08-01T15:40:21.240

Reputation: 827

I like this concept. Regardless of the current situation, it keeps chugging along at its own pace. – Ness – 2019-08-02T19:05:44.207

4

ScavengerBot (V2)

Realized it wasn't much of a scavenger before. New strategy is wait until it can kill another bot. If no one can be killed, it sits and builds up shield.

function scavengerBot(me, others) {
    if (me.shield < (me.levels.shield * 1.5 + 5)) {
        return shield();
    }
    var currentAttack = 1.25 * me.levels.attack + 5;
    var hasVictim = false;
    var victimUid = 0;
    var maxWorth = 0;
    for (var i = 0; i < others.length; i++) {
        var hp = others[i].hp;
        var worth = others[i].worth;
        if (hp <= currentAttack && worth > maxWorth) {
            hasVictim = true;
            victimUid = others[i].uid;
            maxWorth = worth;
        }
    }

    if (hasVictim) {
        return attack(victimUid);
    }

    if (me.gold >= cost(me.levels.attack)) {
        return upgrade("attack");
    }

    if (me.gold >= cost(me.levels.shield)) {
        return upgrade("shield");
    }
    return shield();
}

reffu

Posted 2019-08-01T15:40:21.240

Reputation: 1 361

1me.levels.attacl? – Draco18s no longer trusts SE – 2019-08-01T18:52:54.463

Good catch, fixed – reffu – 2019-08-01T18:59:16.173

4

Moody

function Moody(me, others, storage) {
    health = me.hp + me.shield;
    damage = storage.previous_health - health;
    storage.previous_health = health;
    if( damage > 2 ) {
        storage.fear = 2;
    }
    if( storage.fear ) {
        storage.fear -= 1;
        if( me.gold >= cost(me.levels.heal) )
            return upgrade("heal");
        return heal();
    }
    if ( me.hp <= 50 ) {
        return heal();
    }
    if (cost(me.levels.farm) < 0.15 * (1000 - turn())) {
        if( me.gold >= cost(me.levels.farm) )
            return upgrade("farm");
        if( me.gold >= cost(me.levels.heal) )
            return upgrade("heal");
        return farm();
    }
    rich_bots = others.sort( (x,y) => y.worth - x.worth );
    richest_enemy = rich_bots[0];
    if (richest_enemy.hp >= storage.target_hp) {
        storage.anger = true;
    }
    storage.target_hp = NaN;
    if (storage.anger) {
        if( me.gold >= cost(me.levels.attack) ) {
            storage.anger = 0;
            return upgrade("attack");
        }
        return farm();
    }
    storage.target_hp = richest_enemy.hp;   
    return attack(richest_enemy.uid);   
}

Moody's default strategy is to upgrade farming and healing for a while, then take out the other bots in descending order of worth. However, if it's attacked, it'll get scared and refocus on healing for a bit. If it attacks and "fails", because the victim was healing or shielding more effectively than the attack, it'll get angry and go off to upgrade its attacking abilities.

histocrat

Posted 2019-08-01T15:40:21.240

Reputation: 20 600

4

The Opportunist

This one borrows a bit from a few others (notably ScavengerBot (V2) and Unkillable) since they had the same ideas I had in mind, but I generally like well-roundedness and jack-of-all-trades styles more than focusing on only one or two things. This will likely mean that I will not win, but it should be in the middle somewhere (which happens to me most of the time in many things).

So it steals juicy kills; heals if necesssary; upgrades farm, attack, and heal in that order; and farms otherwise.

function Opportunist(me, others, storage) {

    // Initializing and keeping track of selfWorth
    if (turn() == 1) {
        storage.selfWorth = 0;
    }
    else if (storage.previousGold < me.gold) {
        storage.selfWorth += (me.gold - storage.previousGold);
    }
    storage.previousGold = me.gold;

    // Me stats
    var me_attack = 1.25 * me.levels.attack + 5;
    var me_heal = me.levels.heal + 5;

    // Look for the juiciest hunk of loot
    // If there are multiple of the highest worth, the last is chosen
    var choice = others[0].uid;
    var mostWorthy = -1;
    for (var i = 0; i < others.length; i++) {
        worth = others[i].worth
        if (others[i].hp <= me_attack && worth >= mostWorthy) {
            choice = others[i].uid;
            mostWorthy = worth;
        }
    }

    // Actions in order of priority
    // The juicy targets must be worth the action
    if (mostWorthy > (storage.selfWorth * 0.25) ) {
        return attack(choice);
    }
    else if (me.hp <= 100 - me_heal) {
        return heal()
    }
    else if (me.gold >= cost(me.levels.farm)) {
        return upgrade("farm");
    }
    else if (me.gold >= cost(me.levels.attack)) {
        return upgrade("attack");
    }
    else if (me.gold >= cost(me.levels.heal)) {
        return upgrade("heal");
    }
    else {
        return farm();
    }
}

Ness

Posted 2019-08-01T15:40:21.240

Reputation: 161

12nd argument should be others – SuperStormer – 2019-08-02T19:10:57.807

4

Bandit

function Bandit(me, others, storage) {
    // stuff we need
    const epsilon = 0.3; // really high epsilon
    function argmax(xs) {
        var max = 0;
        var argmax = 0;
        for (var i=0; i<xs.length; i++) {
            if (xs[i]>max) {
                max = xs[i];
                argmax = i;
            }
        }
        return argmax;
    }
    function base3ToActionSeries(strategy) {
        const actions = [shield(), farm(), heal()];
        var idxs = []
        var strategy_cut = strategy;
        for (var i = 81; i >= 1; i /= 3) {
            if (strategy_cut >= 2 * i) {idxs.push(2); strategy_cut -= 2*i}
            else if (strategy_cut >= i) {idxs.push(1); strategy_cut -= i}
            else idxs.push(0);
        }
        return idxs.map(idx => actions[idx]);
    }

    // actual logic starts here
    // current strategy and info to calculate reward
    if (!storage.prior)
        storage.prior = [0,0.03325,0,0.0361,0.0361,0.2372,0,0.2372,0,0.00035,0.0361,0.23555,0.01305,0.0361,0.5798,0.23555,0.62065,0.23555,0,0.2372,0,0.20965,0.5841,0.2372,0,0.21905,0,0.0361,0.0361,0.2081,0.0361,0.0361,0.01455,0.000350,0.62065,0.205,0.000350,0.0361,0.3708,0.0361,0.0323,1.018050,0.5798,0.04495,0.5798,0.23555,0.62065,0.23555,0.62065,1.06395,0.62065,0.23555,0.62065,0.23555,0,0.2372,0,0.2372,0.5841,0.2372,0,0.2372,0,0.23555,0.62065,0.13775,0.5798,1.0257,0.5798,0.23555,0.62065,0.23555,0,0.2339,0,0.2372,0.5841,0.2339,0,0.2372,0,0.0342,0.0361,0.2372,0.03515,0.03325,0.6228,0.2372,0.5841,0.2372,0.0361,0.0130599,0.62065,0.03515,0.0361,1.0665,0.62065,0.24050,0.62065,0.23555,0.51465,0.2372,0.6228,1.0257,0.6228,0.2372,0.5841,0.2372,0.0361,0.0361,0.58195,0.0361,0.0313596,1.0614,0.58195,1.02315,0.58195,0.0342,0.0361,1.0206,0.02255,0.0183,0.02595,1.0206,1.5526,1.0206,0.58195,1.02315,0.58195,0.02765,0.0251,1.0614,0.0007,0.02085,0.3088,0.2372,0.5841,0.2273,0.6185,0.02255,0.6228,0.2372,0.5841,0.2372,0.62065,1.06395,0.62065,1.0665,0.0917,1.0665,0.62065,0,0.62065,0.2372,0.5841,0.2372,0.6228,1.0257,0.6228,0.2372,0.5841,0.2372,0,0.2372,0,0.23225,0.5841,0.2372,0,0.2372,0,0.23555,0.62065,0.23555,0.5798,1.0257,0.5798,0.23555,0.6142,0.23555,0,0.22235,0,0.2372,0.5841,0.2372,0,0.2372,0,0.23555,0,0.21905,0.62065,0.02255,0.62065,0.23555,0.61205,0.23555,0.5798,1.05885,0.5798,1.018050,0.03895,1.018050,0.5798,1.05885,0.5798,0.23555,0.62065,0.23555,0.62065,0.0361,0.62065,0.23555,0.62065,0.23555,0,0.2372,0,0.2372,0.3745,0.2372,0,0.2372,0,0.23555,0.62065,0.23555,0.5798,0.9452,0.5798,0.23555,0.5626,0.23555,0,0.2372,0,0.18175,0.5841,0.0138,0,0.2372,0]
    if (storage.lastScore == null)
        storage.lastScore = 0;
    if (storage.bestStrategy == null)
        storage.bestStrategy = argmax(storage.prior);

    if (cost(me.levels.heal) < me.gold) return upgrade("heal");
    if (cost(me.levels.farm) < me.gold) return upgrade("farm");

    // This barely explores and mostly exploits.
    if (turn() % 5 === 0) {
        // update
        const reward = me.gold/2 - storage.lastScore;
        // biased a bit towards later learned rewards
        storage.prior[storage.bestStrategy] += reward*0.01
        storage.prior[storage.bestStrategy] *= 100/101

        // explore
        if (Math.random() < epsilon) {
            storage.bestStrategy = Math.floor(Math.random()*243);
        }
        else { // exploit
            storage.bestStrategy = argmax(storage.prior);
        } 
        storage.lastScore = me.gold/2;
    }

    var action = base3ToActionSeries(storage.bestStrategy)[turn() % 5];
    return action;
}

First attempt at a reinforcement learning bot. Purely defensive for now to narrow down the search space. Sort of a smarter spinoff of FizzBuzz -- it repeats a specific series of five actions over and over; the five actions are what are chosen by RL.

But it's mostly based off of enumeration for now -- I just generated all 3^5=243 permutations of series of five defensive actions that repeated itself over and over, and stored their average scores (divided by 200, to get the average gain over five turns) over 100 iterations in the storage.prior array. Then, during the game, it implements an epsilon-greedy approach to updating those score lists so it's more future-proof. (Also because using epsilon=0.3 did way better than epsilon=0.1 so I just kept it.)

It does okay, consistently placing between scavengerBot and Optimist. I'm currently doing some more training on real games, and looking for better ways to frame the strategy, to see if I can improve it.

Rin's Fourier transform

Posted 2019-08-01T15:40:21.240

Reputation: 827

4

ScaredBot

  1. It finds other bots:
    • with the highest attack
    • with most wealth and HP lower than own attack
  2. If its HP+shield is lower than the found highest attack * (25% of bots), or it gets near the lower end of HP + shield, then it shields
  3. If it found a bot with lower shield than its own attack, it attacks it.
  4. If its health is < 50, it heals.
  5. If it can upgrade any of shield, heal and farm, it upgrades the one with the lowest level
  6. It farms
function ScaredBot(me, others) {
    const my_attack = me.levels.attack * 1.25 + 5;
    const my_defense = me.hp + me.shield;

    var max_attack_val = 0;
    var min_hp_worth = 0;
    var min_hp_id = null;
    var hp_under_me = 0;
    for (var i=0; i<others.length; i++){
        if (others[i].hp < my_attack && others[i].worth > min_hp_worth){
            min_hp_id = others[i].uid;
            min_hp_worth = others[i].worth;
        }
        if (others[i].attack*1.25+5 > max_attack_val){
            max_attack_val = others[i].attack*1.25+5;
        }
        if (others[i].hp < my_defense && others[i].hp > 0){
            hp_under_me++;
        }
    }
    if (max_attack_val*0.25*others.length > my_defense || hp_under_me < 0.25*others.length){
        return shield();
    }
    else if (min_hp_id != null){
        return attack(min_hp_id);
    }
    else if (me.hp < 50){
        return heal();
    }
    else {
        var min_lvl = NaN;
        var min_name = null;
        const vals = [me.levels.heal, me.levels.shield, me.levels.farm];
        const names = ["heal", "shield", "farm"];
        for (var i=0; i<vals.length; i++){
            if (!(min_lvl < vals[i])){
                min_lvl = vals[i];
                min_name = names[i];
            }
        }
        if (me.gold > cost(min_lvl)){
            return upgrade(min_name);
        }
        return farm();
    }
}

The idea is to stay alive the longest possible and otherwise try getting gold in a safe and cheap way to be able to upgrade.

Upgrade priorities should probably be tweaked, as well as the condition when determining on whether to shield.

user24343

Posted 2019-08-01T15:40:21.240

Reputation: 261

3

Friendly bot

function menShengFaDaCai(me, others) {
  // heal if needed
  const maxAttack = Math.max(...others.map(bot => bot.attack));
  const maxAttackCost = maxAttack * maxAttack + 5;
  const othersHp = others.map(bot => bot.hp).sort();
  const targetHp = othersHp[Math.ceil(othersHp.length / 2)];
  if (me.hp < 95 && me.hp < Math.max(maxAttackCost * 2, targetHp, 50)) return heal();

  // upgrade heal and farm if possible
  const { heal: healLevel, farm: farmLevel } = me.levels;
  const gain = (heal, farm) => ((5 + heal) / 2) * (2 * farm + 5) / ((5 + heal) / 2 + 1);
  const gain0 = gain(healLevel, farmLevel);
  const gainUpgradeHeal = gain(healLevel + 1, farmLevel);
  const gainUpgradeFarm = gain(healLevel, farmLevel + 1);
  const gainUpgradeHealPerGold = (gainUpgradeHeal - gain0) / cost(healLevel);
  const gainUpgradeFarmPerGold = (gainUpgradeFarm - gain0) / cost(farmLevel);
  const preferUpgradeHeal = gainUpgradeHealPerGold > gainUpgradeFarmPerGold;
  const mayOffer = type => me.gold >= cost(me.levels[type]);
  if (preferUpgradeHeal && mayOffer('heal')) return upgrade('heal');
  if (!preferUpgradeHeal && mayOffer('farm')) return upgrade('farm');

  // keep farming
  return farm();
}

others[0].hp is hp + shield instead of hp...

tsh

Posted 2019-08-01T15:40:21.240

Reputation: 13 072

4Can anyone help me translating the function name to English? ^_^ – tsh – 2019-08-02T03:59:57.537

4According to Google Translate, "闷声发大财" means "Muffled". Pretty sure that's not what you want and is in fact yet another Google Translate epic fail... I searched further and all results seem to mention that there's no single English word that can be used here, so it might be better to keep it as is, actually, as it seems to be a Chinese preposition that generally means that one should work silently and let the results speak for themselves and attains to a traditional philosophy. Unfortunately, I don't know Chinese at all to translate it directly. :D – Erik the Outgolfer – 2019-08-02T12:25:33.993

1as a native Chinese speaker, it means something like "silently make a huge fortune" :v 闷声 also connotes intentionally being quiet, literally "covering up sound" – Rin's Fourier transform – 2019-08-02T15:39:04.393

1Sneaky? UnderTheRadar? DontMindMe? AttentionDeflector? – Peter Taylor – 2019-08-03T08:57:07.557

3

SmartFarmer

Farms, upgrades farming, heals if low health. Farming seemed overpowered until the truly offensive bots arrived. Now my bot gets killed :-(

function smartFarmer(me, others){
    if(me.hp < 13) return heal();
    for(var i = 0; i < others.length; i++)if(others[i].attack * 1.25 + 5 > me.hp)return heal();
    if(me.gold >= cost(me.levels.farm)) return upgrade("farm");
    if(me.levels.heal < 9 && me.levels.farm > me.levels.heal + 7 && me.gold >= cost(me.levels.heal)) return upgrade("heal");
    return farm();
}

B0RDERS

Posted 2019-08-01T15:40:21.240

Reputation: 311

1I was (manually) testing essentially the same strategy to see what the maximum obtainable worth is and the best numbers I could get, was by slightly delaying when heal is upgraded (I used gold >= cost*2) and going up to lvl10 heal. – Nicolai – 2019-08-02T14:26:42.360

That price multiplier is a good idea. I added something similar. I would be interested to see what numbers you got – B0RDERS – 2019-08-02T14:37:59.163

3

Mort

function Mort(me, others, storage) {
    if (me.hp <= 100 - (me.levels.heal + 5))
        return heal();
    actions = ["farm", "heal", "attack"].filter(action => cost(me.levels[action]) <= me.gold).map( action => [upgrade(action), 1000 - turn() - cost(me.levels[action]) ] )
    my_damage = me.levels.attack * 1.25 + 5;
    actions = actions.concat(others.map( bot => [ attack(bot.uid), (bot.worth/2)/Math.max(bot.hp/(my_damage-(bot.hp > my_damage ? 5 : 0)),1) ] ));
    actions.push( [farm(), (2 * me.levels.farm + 5)*(1-2/(me.levels.heal+5))] );
    return actions.sort( (x,y) => y[1] - x[1] )[0][0];
}

Each turn, compares the amortized profit of killing each bot to that of farming and healing and picks the best option. Really it should use state to figure out how long it'll take to kill a bot, but for now it just assumes every bot heals or shields an average of 5 points a turn net of damage other bots do.

histocrat

Posted 2019-08-01T15:40:21.240

Reputation: 20 600

3

The Accountant

This practical bot calculates the most economically beneficial move, but he likes to keep his attack profile low to avoid trouble from all of the vigilante bots. He doesn't try to pick help the defenseless or prey on them. Rather, he does what helps him the most.

function accountant(me, others, storage) {
    if (turn() == 1) {
        storage.lastHP = me.hp + me.shield;
        storage.hisAttack = 5;
        storage.timesAttacked = 0;
        storage.lastAttack = -1;
        storage.healths = [], storage.uids = [], storage.heals = [];
        for (var i = 0; i < others.length; i++) {
            storage.healths.push(others[i].hp);
            storage.uids.push(others[i].uid);
            storage.heals.push(5);
        }
    }
    storage.timesAttacked++;
    if (storage.lastHP == me.hp + me.shield) storage.timesAttacked = 0;
    else storage.hisAttack = storage.lastHP - me.hp - me.shield;
    storage.lastHP = me.hp + me.shield;
    var attacks = [];
    for (var i = 0; i < others.length; i++) if (others[i].uid != me.uid) attacks[i] = 1.25 * others[i].attack + 5;
    attacks.sort();
    for (var i = 0; i < others.length; i++) {
        storageIndex = storage.uids.indexOf(others[i].uid);
        if (storage.heals[storageIndex] < others[i].hp - storage.healths[storageIndex] + (others[i].uid == storage.lastAttack ? 1.25 * me.levels.attack + 5 : 0)) others[i].hp - storage.healths[storageIndex] + (others[i].uid == storage.lastAttack ? 1.25 * me.levels.attack + 5 : 0);
    }
    var maxProfitTurn = 2 * me.levels.farm + 5, victimID = -1, tempProfit;
    for (var i = 0; i < others.length; i++) {
        storageIndex = storage.uids.indexOf(others[i].uid);
        tempProfit = others[i].worth / 2 * (1.25 * me.levels.attack + 5 - storage.heals[storageIndex]) / others[i].hp;
        if (tempProfit > maxProfitTurn) {
            victimID = others[i].uid;
            maxProfitTurn = tempProfit;
        }
    }
    maxUrgentProfit = 0;
    for (var i = 0; i < others.length; i++) if (maxUrgentProfit < others[i].worth / 2 && others[i].hp <= attacks.slice(0, 4).reduce((a, b) => a + b) + 1.25 * me.levels.attack + 5) {
        maxUrgentProfit = others[i].worth / 2;
        victimID = others[i].uid;
    }
    if (maxUrgentProfit > 0) {
        storage.lastAttack = victimID;
        return attack(victimID);
    }
    storage.lastAttack = -1;
    if (storage.timesAttacked == 0) {
        if (me.levels.shield < 20 && me.gold >= cost(me.levels.shield)) return upgrade("shield");
        if (me.levels.heal < 5 && me.levels.shield >= me.levels.heal + 5 && me.gold >= cost(me.levels.heal)) return upgrade("heal");
        if (Math.random() < Math.pow((me.hp + me.shield) / 100, -2)) {
            storage.lastHP += 1.5 * me.levels.shield + 5;
            return shield();
        }
    }
    else {
        if (Math.random() < .5 || me.hp + me.shield - storage.hisAttack - attacks[0] <= 10) {
            storage.lastHP += 1.5 * me.levels.shield + 5;
            return shield();
        }
        if (me.levels.shield < 20 && me.gold >= cost(me.levels.shield)) return upgrade("shield");
        if (me.hp <= 2) {
            storage.lastHP += me.levels.shield + 5;
            return heal();
        }
        storage.lastHP -= 2;
        return farm();
    }
    if (me.gold >= cost(me.levels.farm)) return upgrade("farm");
    storage.lastAttack = victimID;
    if (victimID != -1) return attack(victimID);
    if (me.hp <= 2) {
        storage.lastHP += me.levels.shield + 5;
        return heal();
    }
    storage.lastHP -= 2;
    return farm();
}

B0RDERS

Posted 2019-08-01T15:40:21.240

Reputation: 311

3

reallyCommittedTurtle

function reallyCommittedTurtle(me, others, storage) {
    if( storage.previousHP ) {
        others.forEach ( o => {storage.deltaHP[o.uid] = o.hp - storage.previousHP[o.uid]; storage.previousHP[o.uid] = o.hp } );
    }
    else {
        storage.previousHP = {};
        storage.deltaHP = {};
        others.forEach ( o => storage.previousHP[o.uid] = o.hp );
    }
    if (turn() < 3)
        return upgrade("shield");
    if ( me.shield < 400 || others.find( o=> o.deltaHP < -2 ) )
        return shield();
    if (me.hp <= 95 - me.levels.heal) {
        if (me.gold >= cost(me.levels.heal))
            return upgrade("heal");
        return heal();
    }
    rich_bots = others.sort( (x,y) => y.worth - x.worth );
        potential_victim = rich_bots.find( bot => bot.hp + storage.deltaHP[bot.uid] <= me.levels.attack * 1.25 + 5 );
        if (potential_victim && potential_victim.worth/2 > me.levels.farm*2 + 5)
            return attack(potential_victim.uid);
    if (me.gold >= cost(me.levels.farm))
        return upgrade("farm");
    return farm();
}

Here's the thing. It's gotten really dangerous out there. Farming at all increases your worth, making you a target. So it's really not safe to farm until you've built up a huge shield and all the violence has died down. Then you can poke your head out of your shell and start farming. Or kill-assisting. Whatever pays better.

histocrat

Posted 2019-08-01T15:40:21.240

Reputation: 20 600

2

Indestructible

A modification of Draco18's bot, using shields (more effective against other bots)

function indestructible(me){
    if (me.hp < 100) {
        return heal();
    } else if (me.shield < 15) {
        return shield();
    } else {
        if (me.gold >= cost(me.levels.shield)) {
            return upgrade("shield");
        } else if (me.gold >= cost(me.levels.farm)) {
            return upgrade("farm");
        } else {
            return farm();
        }
    }
}

Redwolf Programs

Posted 2019-08-01T15:40:21.240

Reputation: 2 561

2

Guardian

I can have more than one submission, right?

A fork of CampBot. Doesn't shield, instead focusing on attacking. Shows a preference for attacking players with higher attack stats, rather than striking at random like CampBot. Focuses on upgrading its farming rather than healing.

function guardian(self,others,storage){
    if(!storage.victimBlacklist){
        storage.victimBlacklist=[]
    }
    let turnsLeft=999-turn()
    function findVictim(){
        let potentialVictims=others.filter(bot=>!storage.victimBlacklist.includes(bot.uid))
        if(potentialVictims.length>0){
            let victim=potentialVictims.reduce((el, em) => el.attack > em.attack ? el : em);
            storage.victimUid=victim.uid
            storage.victimPrevHp=victim.hp
            storage.prevMove="attack"
            return attack(victim.uid)
        }else{
            storage.prevMove="farm"
            return farm()
        }   
    }
    if(self.hp<=(95-self.levels.heal)){
        storage.prevMove="heal"
        return heal()
    } else if(self.gold>=cost(self.levels.attack)){
        storage.prevMove="upgrade"
        return upgrade("attack")
    } else if(self.gold>=cost(self.levels.farm)&&turnsLeft>100&&self.levels.heal<=1){
        storage.prevMove="upgrade"
        return upgrade("farm")
    } else if(!storage.victimUid){
        return findVictim()
    }else if(Object.values(others).map(bot=>bot.uid).includes(storage.victimUid)){
        let victimCurrHp=Object.values(others).filter(bot=>bot.uid==storage.victimUid)[0].hp
        if(storage.victimPrevHp<victimCurrHp&&storage.prevMove==="attack"){
            storage.victimBlacklist.push(storage.victimUid)
            storage.victimUid=undefined
            return findVictim()
        }else{  
            storage.victimPrevHp=victimCurrHp
            storage.prevMove="attack"
            return attack(storage.victimUid)
        }
    }else{
        storage.victimUid=undefined
        return findVictim()
    }
}

Anonymous

Posted 2019-08-01T15:40:21.240

Reputation: 89

my bot doesnt randomly attack... – SuperStormer – 2019-08-02T17:02:33.063

You can post as many times as you want, the more the merrier I suppose – Redwolf Programs – 2019-08-02T17:04:29.487

@SuperStormer I realise yours isn't entirely random, but: let victim=potentialVictims[Math.floor(Math.random()*potentialVictims.length)] – Anonymous – 2019-08-02T17:24:59.560

but it first filters out the ones that aren't worth attacking – SuperStormer – 2019-08-02T17:28:39.137

I was working on a similar bot named the equalizer when you posted this. I am still fine-tuning it, but I like some of your ideas. – B0RDERS – 2019-08-02T18:21:28.980

2

Kill Bot

function killBot(me, others, storage) {
    // If I lost health since my last check, shield.
    if (me.hp < storage.hp){
        storage.hp = me.hp;
        return shield();
    }

    storage.hp = me.hp;

    health = Math.min(...others.map(o => o.hp));
    // If I have the least health or can be one-shot, shield.
    if (others.some(o => o.attack * 1.25 + 5 >= me.hp + me.shield) || (health > me.hp + me.shield && health < 500)) return shield();

    // If I can kill someone, kill them!
    targets = others.filter(o => o.hp < me.attack);
    if (targets.length > 0){
        wealth = Math.max(...targets.map(o => o.worth));
        targets = targets.filter(o => o.worth == wealth);
        target = targets[Math.floor(Math.random()*targets.length)];
        return attack(targets[0].uid);
    }

    // If I have the money, upgrade shielding or attack
    if (me.levels.shield <= me.levels.attack){
        if (cost(me.levels.shield) < me.gold) return upgrade("shield");
    } else {
        if (cost(me.levels.attack) < me.gold) return upgrade("attack");
    }

    // Otherwise, attack the weakest!
    targets = others.filter(o => o.hp == health);
    // And if there's a tie, attack the wealthiest.
    wealth = Math.max(...targets.map(o => o.worth));
    targets = targets.filter(o => o.worth == wealth);
    target = targets[Math.floor(Math.random()*targets.length)];
    return attack(targets[0].uid);
}

A simple bot, Kill Bot just wants to kill its enemies. Since shielding is much more efficient than healing (especially when levelled), Kill Bot just tries to always be an unappealing target by shielding itself whenever attacked. Kill Bot does quite well among the weak, pacifistic bots around here (you can feel its scorn for them).

Spitemaster

Posted 2019-08-01T15:40:21.240

Reputation: 695

3Note that o.attack is the attack level, not its damage – Redwolf Programs – 2019-08-02T19:37:13.347

2

Rando

This silly guy will choose actions based on uniform randomness with some biases. If a randomly chosen action won't work, then it drops down to the next choice.

So on average, he should attack almost 2/9 of the time and farm almost 3/9 of the time. The rest are about 1/9 chance if he is able to upgrade, or if the healing/shielding is worth it, etc.

He will not likely perform well, but at least there's a small chance that he reigns supreme. And that's the whole purpose of Rando. He just needs to believe in himself! All the options are laid in front of him. He needs only choose what is needed for any given situation.

function Rando(me, others, storage) {

    var rnum = Math.floor(Math.random() * 9);
    switch (rnum) {
        case 0:
            if (me.gold >= cost(me.levels.shield)) {
                return upgrade("shield");
            }
        case 1:
            if (me.hp >= 100 - (me.levels.heal + 5) && me.levels.shield >= me.levels.heal) {
                return shield();
            }
        case 2:
            if (me.hp < 100 - (me.levels.heal + 5)) {
                return heal();
            }
        case 3:
            if (me.gold >= cost(me.levels.farm)) {
                return upgrade("farm");
            }
        case 4:
            if (me.gold >= cost(me.levels.heal)) {
                return upgrade("heal");
            }
        case 5:
            if (me.hp > 2) {
                return farm();
            }
        case 6:
            // Beat down the leader!
            var currentLeader = others[0].uid;
            var leaderWorth = -1;
            for (var i = 0; i < others.length; i++) {
                worth = others[i].worth;
                if (worth > leaderWorth) {
                    currentLeader = others[i].uid;
                    leaderWorth = worth;
                }
            }
            return stun(currentLeader);
        case 7:
            if (me.gold >= cost(me.levels.attack)) {
                return upgrade("attack");
            }
        case 8:
            // Find the juiciest kill (if any), or attack the strongest
            var choice = others[0].uid;
            var choiceWorth = -1;
            var currentLeader = others[0].uid;
            var leaderWorth = -1;
            for (var i = 0; i < others.length; i++) {
                worth = others[i].worth
                if (worth > leaderWorth) {
                    currentLeader = others[i].uid;
                    leaderWorth = worth;
                }
                if (others[i].hp <= (1.25 * me.levels.attack + 5) && worth >= choiceWorth) {
                    choice = others[i].uid;
                    choiceWorth = worth;
                }
            }
            if (choice > -1) {
                return attack(choice);
            }
            else {

                return attack(currentLeader);
            }
        default:
            return false
    }
}

(I know "default" is unnecessary, but I think it is good coding practice for robust code.)

Ness

Posted 2019-08-01T15:40:21.240

Reputation: 161

2"He just needs to believe in himself"...I'm laughing so much right now – Redwolf Programs – 2019-08-02T20:42:31.137

2

FarmHeal Bot

Forked from @Anonymous' JustFarm bot

function farmhealBot(me, others, storage) {
  if (me.hp <= 95)
    return heal();
  else return farm();
}

maninthecomputer

Posted 2019-08-01T15:40:21.240

Reputation: 121

1

PacifistBot

He keeps track of every single bot in the game via storage.others. It permits him to know of the dead guys' worth to know if he can win, even when they're dead.

Decision tree:

  • If he's 1v1 and winning, stuns the opponent until turn 1000.
  • If he's really low life, heal. Has to be rethinked because it calcs on max atk over all the others, and now others are more aggressive.
  • If he's not safe, shield. It's the main source of its defense since it will upgrade shield the most. Has to be rethinked because I forgot to take shield attribute in account, I was using me like I use others (hp being hp + shield).
  • Upgrades heal once, so to level 1, never more. it permits 3 farms per heal for a ridiculously low cost, rather than 5 farms per 2 heals.
  • Upgrades shield if someone becomes aggressive. Could be rethinked to look out for aggressiveness towards him.
  • Upgrades farm if possible, keeping a solid active gold basis to be able to upgrade shield in case of imminent danger.
  • Farms.
function pacifistWinnerBot(me, others, storage) {
	if(!storage.start){
		storage.start=true;
		storage.others = others;
	}
	others.map(bot => 
		storage.others.map(
			bot2 => {if(bot2.uid==bot.uid)bot=bot2}//update worth, hp, attack level
		)
	);
	const maxAtk = 5+1.25*Math.max(storage.others.map(bot => bot.attack));
	if(storage.win || ((me.gold > Math.max(storage.others.map(bot => bot.worth))+25) && storage.others.reduce((bot,x) => x+(bot.hp<=0),0)==1)){
		storage.win=true;
		return stun(storage.others.filter(bot => bot.hp>0)[0].uid);
	}
	if(me.hp < 2+maxAtk) return heal();
	if(me.hp < 2+maxAtk*2) return shield();
	if(me.levels.healing<1 && me.gold>cost(me.levels.healing)) return upgrade("heal");
	if((me.gold > cost(me.levels.shield)) && (1.5*me.levels.shield+5 < 2*maxAtk)) return upgrade("shield");
	if(me.gold > 50+cost(me.levels.farm)) return upgrade("farm");
	return farm();
}

Try it online! with for timestamp 4.57pm Aug 2nd UTC+2.

BullyDozer is making it hard, but still happy with what I've done.

V. Courtois

Posted 2019-08-01T15:40:21.240

Reputation: 868

1

CampBot

function campBot(self,others,storage){
    if(!storage.victimBlacklist){
        storage.victimBlacklist=[]
    }
    let turnsLeft=999-turn()
    function findVictim(){
        let potentialVictims=others.filter(bot=>bot.worth>70&&((bot.attack>1&&bot.hp<200)||bot.hp<80)&&!storage.victimBlacklist.includes(bot.uid))
        if(potentialVictims.length>0){
            let victim=potentialVictims[Math.floor(Math.random()*potentialVictims.length)]
            storage.victimUid=victim.uid
            storage.victimPrevHp=victim.hp
            storage.prevMove="attack"
            return attack(victim.uid)
        }else{
            storage.prevMove="farm"
            return farm()
        }   
    }
    if(self.hp<=(95-self.levels.heal)){
        storage.prevMove="heal"
        return heal()
    } else if(self.shield<=50){
        return shield()
    } else if(self.gold>=cost(self.levels.attack)&&turnsLeft>100&&self.levels.attack<=3){
        storage.prevMove="upgrade"
        return upgrade("attack")
    } else if(self.gold>=cost(self.levels.farm)&&cost(self.levels.farm)<turnsLeft*(2*(self.levels.farm+1)+5)){
        storage.prevMove="upgrade"
        return upgrade("farm")
    }else if(self.gold>=cost(self.levels.heal)&&cost(self.levels.heal)<turnsLeft/(self.levels.heal+6)*(2*self.levels.farm+5)&&self.levels.heal<=2){
        storage.prevMove="upgrade"
        return upgrade("heal")
    }else if(self.gold>=cost(self.levels.shield)&&self.levels.shield<=3){
        return upgrade("shield")
    }else if(!storage.victimUid){
        return findVictim()
    }else if(Object.values(others).map(bot=>bot.uid).includes(storage.victimUid)){
        let victimCurrHp=Object.values(others).filter(bot=>bot.uid==storage.victimUid)[0].hp
        if(storage.victimPrevHp<victimCurrHp&&storage.prevMove==="attack"){
            storage.victimBlacklist.push(storage.victimUid)
            storage.victimUid=undefined
            return findVictim()
        }else{  
            storage.victimPrevHp=victimCurrHp
            storage.prevMove="attack"
            return attack(storage.victimUid)
        }
    }else{
        storage.victimUid=undefined
        return findVictim()
    }
}

If there is an easy target with a decent amount of gold, then it attacks it. If it discovers that the other bot heals faster than it can attack, then it blacklists the other bot from being attacked and finds another victim. If there are no good targets, it just camps by farming while waiting for a good target(hence the name).

SuperStormer

Posted 2019-08-01T15:40:21.240

Reputation: 927

1

Serial Killer

function serialKiller(self,others,storage){
    let turnsLeft=999-turn()
    let lowestHpBots=others.sort((a,b)=>a.hp-b.hp)
    if(self.hp<=(95-self.levels.heal)){
        return heal()
    } else if(self.shield<=150||self.shield<=6000/others.length&&self.shield<=1200&&turn()>=20||lowestHpBots[1]&&lowestHpBots[1].hp>self.hp+self.shield){
        return shield()
    } else if(self.gold>=cost(self.levels.attack)&&turnsLeft>20){
        return upgrade("attack")
    }else if(self.gold>=cost(self.levels.shield)&&self.levels.shield<=4&&turnsLeft>20){
        return upgrade("shield")
    }else{
        return attack(others.sort((a,b)=>a.hp-b.hp)[0].uid)
    }
}

This bot's one goal is to kill everything, starting from the bot with the lowest health.

SuperStormer

Posted 2019-08-01T15:40:21.240

Reputation: 927

1

Captain Farmer

It's hard being everyone's favorite superhero, so he just kills the villains that are easy to get. When he's not busy letting other people do his job, he farms gold and heals once he reaches a certain threshold. When his health gets too low, he'll put up some shields to keep himself safe from the villains (other bots), so he can get back to farming. If he knows he's going to die, he'll farm for the rest of his short life to maximize his gold output.

function captFarmer(me, others, storage) {
    if (!storage.mode)
        storage.mode = "prep";
    var weak = others.find(el => el.hp < me.levels.attack * 1.25 + Math.max(...others.map(el => el.attack)) * 1.75 + 10 && Math.ceil(el.worth / 2) > me.levels.farm * 2 + 5);
    if (weak && storage.weak >= 0) {
        storage.weak += 1;
        return attack(weak.uid);
    }
    if (storage.weak < 0)
        storage.weak += 1;
    else if (storage.weak > 3)
        storage.weak = -3;
    else if (!storage.weak)
        storage.weak = 0;
    if (me.hp + me.shield < 90)
        storage.mode = "lowhp";
    if (storage.mode == "prep") {
        if (turn() < 5)
            return shield();
        if (turn() == 5)
            return upgrade("attack");
        if (turn() == 6)
            return upgrade("attack");
        storage.modeTimer = 0;
        storage.mode = "farm";
    } else if (storage.mode == "lowhp") {
        let last = storage.lastHp;
        let hp = me.hp + me.shield;
        storage.lastHp = hp;
        if (!last)
            return shield();
        if (me.hp == 100) {
            if (me.shield >= 25) {
                storage.mode = "farm";
                storage.modeTimer = 0;
            }
            return shield();
        }
        if (last > hp) {
            if (last - hp + me.levels.heal + 5 <= me.levels.shield * 1.5 + 5)
                return shield();
            else
                return farm();
        } else {
            return shield();
        }
    } else if (storage.mode == "farm") {
        if (me.gold >= cost(me.levels.shield))
            return upgrade("shield");
        if (me.gold >= cost(me.levels.farm))
            return upgrade("farm");
        if (me.gold >= cost(me.levels.attack))
            return upgrade("attack");
        if (me.hp > 80 && !storage.healMode)
            return farm();
        storage.healMode = true;
        if (me.hp < 97)
            return heal();
        storage.healMode = false;
        return shield();
    }
    return shield();
}

Redwolf Programs

Posted 2019-08-01T15:40:21.240

Reputation: 2 561

1

Persistent Blue Shell

function BlueShell(me, others, storage) {
    var victimUid = 0;
    var maxWorth = 0;
    var needNewVictim = true;
    var victimHp = 100;
    for (var i = 0; i < others.length; i++) {
        var hp = others[i].hp;
        var worth = others[i].worth;
        var uid = others[i].uid;
        if (worth > maxWorth) {
            victimUid = uid;
            maxWorth = worth;
            victimHp = hp;
        }
        if (uid == storage.victim) {
            needNewVictim = false;
        }
    }
    var attacked = false;
    if (turn() > 1) {
        attacked = (me.hp + me.shield) < (storage.hp + storage.shield);
    }
    storage.hp = me.hp;
    storage.shield = me.shield;
    if (needNewVictim || !storage.victim || attacked) {
        storage.victim = victimUid;
        storage.victimHp = victimHp;
        if (cost(me.levels.shield) <= me.gold) {
            return upgrade("shield");
        } else {
            return shield();
        }
    }
    if (storage.victimHp <= 5) {
        return attack(storage.victim)
    }
    return stun(storage.victim);
}
  • Select the bot with highest worth
  • Repeatedly attempt to stun (attack if victim has < 5 health)
  • If the chosen bot dies or BlueShell gets deflected (i.e. attacked) re-evaluate who is in the lead and target them instead.
    • But first shield once to avoid dying (hopefully) mitigate damage from attacks

Will never win, and almost never ends with a non-zero worth, but when testing it looks like it will affect the order of the bots in the top few places

reffu

Posted 2019-08-01T15:40:21.240

Reputation: 1 361

Shielding once just adds the equivalent of 5 health to your bot. How does that prevent dying? – Redwolf Programs – 2019-08-07T19:42:02.857

It doesn't prevent it entirely, but it helps to mitigate the damage from the bots that attack "randomly", and shield is both more efficient to upgrade than healing, and doesn't have a cap. Updated the wording slightly to make the intent a little clearer – reffu – 2019-08-07T19:45:05.993

1Another use is to defend against serial killer. If you bot has the lowest HP, serial killer will attack you, then others will "jump on the bandwagon" and kill you – Redwolf Programs – 2019-08-07T19:56:48.530

1

AntiWar

function antiWar(self,others,storage){
    let turnsLeft=999-turn()
    let lowestHpBots=others.sort((a,b)=>a.hp-b.hp)
    if(self.shield<=50||self.shield<=5500/others.length&&self.shield<=1200&&turn()>=20||lowestHpBots[1]&&lowestHpBots[1].hp>self.hp+self.shield){
        return shield()
    } else if(self.gold>=cost(self.levels.attack)&&turnsLeft>20){
        return upgrade("attack")
    }else if(self.gold>=cost(self.levels.shield)&&self.levels.shield<=4&&turnsLeft>20){
        return upgrade("shield")
    }else{
        return stun(others.sort((a,b)=>b.attack-a.attack)[0].uid)
    }
}

This bot hates all others that fight and tries to prevent war. Its method of doing this is stunning the bot with the most attack, as that bot is most likely to attack others.

SuperStormer

Posted 2019-08-01T15:40:21.240

Reputation: 927

1

LittleAnt

Didn't really have time. Wrote it in last moment. Had only time to test if it works. No idea how it will do.

function LittleAnt(me, others) {
    if (turn() == 1) {
        return upgrade('farm');
    }
    if (turn() ==2) {
        return upgrade('shield');
    }
    let avgHp = 0;
    let minHp = 100;
    let maxHp = 0;
    let avgGold = 0;
    let maxGold = 0;
    let avgAttack = 0;
    let maxAttack = 0;
    let i = 0, calculatedValue;
    let countOthers = others.length;
    let currEnemy = null;
    let estimatedAttackers = 0;
    let maxAttackEnemyId = null;
    let maxUpgradeName = null;
    let maxAttackValue = 0;
    let maxHealValue = 0;
    let maxUpgradeValue = 0;
    let maxFarmValue = 0;
    let bestTacticValue = 0;
    let maxShieldValue = 0;
    let levels = [
        {
            name: "attack",
            level: me.levels.attack,
            averageValue: 2,
            estimatedValue: 0
        },
        {
            name: "farm",
            level: me.levels.farm,
            averageValue: 6,
            estimatedValue: 0
        },
        {
            name: "heal",
            level: me.levels.heal,
            averageValue: 3,
            estimatedValue: 0
        },
        {
            name: "shield",
            level: me.levels.shield,
            averageValue: 8,
            estimatedValue: 0
        }
    ];

    for (i = 0; i < countOthers; i++) {
        currEnemy = others[i];
        avgHp += currEnemy.hp / countOthers;
        avgGold += currEnemy.worth / countOthers;
        avgAttack += currEnemy.attack / countOthers;
        maxHp = ((maxHp > currEnemy.hp) ? maxHp : currEnemy.hp);
        minHp = ((minHp < currEnemy.hp) ? minHp : currEnemy.hp);
        maxGold = ((maxGold > currEnemy.worth) ? maxGold : currEnemy.worth);
        maxAttack = ((maxAttack > currEnemy.hp) ? maxAttack : currEnemy.hp);
        estimatedAttackers = Math.floor(Math.sqrt(countOthers - 1));
        calculatedValue = (-currEnemy.hp + estimatedAttackers * (me.levels.attack + 5)) * (currEnemy.worth / 2);
        maxAttackValue = maxAttackValue > calculatedValue ? maxAttackValue : calculatedValue;
        maxAttackEnemyId = maxAttackValue > calculatedValue ? maxAttackEnemyId : currEnemy.uid;
    }

    maxHealValue = 100 - me.hp + Math.sqrt(turn()) + 5 + Math.sqrt(countOthers);

    for (i = 0; i < 4; i++) {
        if (me.gold - cost(levels[i].level) >= 0) {

        } else {
            levels[i].estimatedValue = -100;
        }
        levels[i].estimatedValue = Math.sqrt(me.hp) * (me.gold / cost(levels[i].level) * levels[i].averageValue) - Math.sqrt(turn());
        maxUpgradeValue = maxUpgradeValue > levels[i].estimatedValue ? maxUpgradeValue : levels[i].estimatedValue;
        maxUpgradeName = maxUpgradeValue > levels[i].estimatedValue ? maxUpgradeName : levels[i].name;
    }

    maxFarmValue = 5 + Math.sqrt(turn()) + me.levels.farm - 2 - Math.sqrt(maxHealValue);
    maxShieldValue = Math.sqrt(turn()) * me.shield + 50;
    maxShieldValue = maxShieldValue > 80 ? maxShieldValue : 80;

    bestTacticValue = Math.max(maxHealValue, maxAttackValue, maxFarmValue, maxUpgradeValue, maxShieldValue);

    if (me.hp < 3) {
        return heal();
    } else {
        if (Math.abs(maxAttackValue - bestTacticValue) < 1) {
            return attack(maxAttackEnemyId);
        }
        if (Math.abs(maxFarmValue - bestTacticValue) < 1) {
            return farm();
        }
        if (Math.abs(maxUpgradeValue - bestTacticValue) < 1) {
            return upgrade(maxUpgradeName);
        }
        if (Math.abs(maxHealValue - bestTacticValue) < 1) {
            return heal();
        }
    }
    return farm();
 }

NoOorZ24

Posted 2019-08-01T15:40:21.240

Reputation: 131

you should probably add a heading – SuperStormer – 2019-08-09T14:08:20.963

and fix the indentation. The trailing } not being inside the code block will mess up the automated code scraper's attempts to load your bot. – Draco18s no longer trusts SE – 2019-08-09T14:09:08.707

Average gold/round of 412.755, 29th place out of 39 – Redwolf Programs – 2019-08-09T14:26:09.397

1

Reformed TeamBot

function reformedAttackBot(self,others,storage){
    let turnsLeft=999-turn()
    let lowestHpBots=others.sort((a,b)=>a.hp-b.hp)
    let maxDamage=others.map(c=>c.attack).reduce((a, b) => Math.max(a, b))*1.25+5
    if(storage.attackFailures===undefined){
        storage.attackFailures=0
    }
    if(!storage.worth){
        storage.worth=5 
        storage.prevGold=self.gold
    }else if(self.gold>storage.prevGold){
        storage.worth+=self.gold-storage.prevGold
    }
    if(others.length===1&&storage.worth>others[0].worth){
        //stun lock the other bot if there are only 2 left and I can win
        return stun(others[0].uid)
    } else if(self.hp<=(95-self.levels.heal)){
        return heal()
    } else if(lowestHpBots[0]&&lowestHpBots[0].hp<=25&&lowestHpBots[0].worth/2>2*self.levels.farm+5&&self.hp+self.shield>=110){
        //kill assist
        return attack(lowestHpBots[0].uid)
    } else if(self.shield<=200||self.shield<=6000/others.length&&self.shield<=1200&&turn()>=20||lowestHpBots[1]&&lowestHpBots[1].hp>self.hp+self.shield){
        return shield()
    }if(storage.attackFailures>3){
        if(self.gold>=cost(self.levels.shield)&&self.levels.shield<=6){
            return upgrade("shield")
        } else if(self.gold>=cost(self.levels.farm)&&(turnsLeft+1)*(2*(self.levels.farm)+5)<turnsLeft*(2*(self.levels.farm+1)+5)){
            return upgrade("farm")
        } else if(self.gold>=cost(self.levels.heal)&&(turnsLeft+1)/(self.levels.heal+5)*(2*self.levels.farm+5)<turnsLeft/(self.levels.heal+6)*(2*self.levels.farm+5)&&self.levels.heal<=2){
            return upgrade("heal")
        }else{
            return farm()
        }
    } else if(self.gold>=cost(self.levels.attack)&&turnsLeft>20&&self.levels.attack+1<Math.max(others.map(bot=>bot.attack))){
        return upgrade("attack")
    } else if(self.gold>=cost(self.levels.shield)&&self.levels.shield<=4&&turnsLeft>20){
        return upgrade("shield")
    } else if(others.length>0){
        if(others.find(bot=>bot.uid===storage.target)===undefined){
            let target=others.sort((a,b)=>b.worth-a.worth)[0]
            storage.target=target.uid
            if(storage.targetPrevHP!==undefined&&storage.targetPrevHp<target.hp){
                storage.attackFailures++
                return farm()
            }else{
                storage.targetPrevHp=target.hp
                return attack(target.uid)
            }
        }
    } else if(self.gold>=cost(self.levels.farm)&&turnsLeft>100){
        return upgrade("farm")
    }else{
        return farm()   
    }
}

After the ban on teaming, TeamBot - Attack reformed and stopped teaming. This hasn't prevented it from exploiting PersistentBlueShell's stunning to attack, though. If PersistentBlueShell dies, it switches to farming.

Old Teambots

SuperStormer

Posted 2019-08-01T15:40:21.240

Reputation: 927

1

LettuceBot

Nobody really likes, nobody really hates it. Bots eat it, but usually not on its own.

function lettuceBot(me, others, storage) {
    var weak = others.find(el => el.hp < me.levels.attack * 1.25 + Math.max(...others.map(el => el.attack)) * 2.5 + 10 && Math.ceil(el.worth / 2) >= 5);
    if (weak)
        return attack(weak.uid);
    if (me.levels.heal < 1)
        return upgrade("heal");
    if (me.gold >= cost(me.levels.farm))
        return upgrade("farm");
    if (me.shield < 100)
        return shield();
    if (me.hp <= 94)
        return heal();
    return farm();
}

Redwolf Programs

Posted 2019-08-01T15:40:21.240

Reputation: 2 561

1

Bernard Szumborski

function BernardSzumborski(me, others) {
    this.getLevel = function (type) {
        return (typeof me.levels[type] === 'undefined' ? 0 : me.levels[type]);
    };

    this.getAttackPower = function (level) {
        if (typeof level === 'undefined') level = this.getLevel('attack');
        return (level * 1.25) + 5;
    };

    this.shieldOrUpgrade = function () {
        if (myGold >= cost(this.getLevel('shield'))) return upgrade('shield');
        return shield();
    };

    let currentTurn = turn(),
        myGold = me.gold,
        myTotalHp = me.hp + me.shield,
        myAttackPower = this.getAttackPower(),
        topAttackPower = 0,
        attackOptions = [];

    others.sort((a, b) => b.attack - a.attack);
    for (let i = 0; i < others.length; i++) {
        let other = others[i];
        if (i < 5) topAttackPower += this.getAttackPower(other.attack);
        if (other.hp <= myAttackPower + 10) attackOptions.push(other);
    }

    if (attackOptions.length > 0) {
        attackOptions.sort((a, b) => b.worth - a.worth);
        return attack(attackOptions[0].uid);
    }

    if (myTotalHp < (topAttackPower * 3) + 5) return shield();

    if (currentTurn > 50) {
        let topWorth = others.filter(other => (other.hp > (currentTurn / 2) + 100)).sort((a, b) => b.worth - a.worth).slice(0, 3);
        let topHp = others.sort((a, b) => b.hp - a.hp).slice(0, 2);

        for (let topWorthBot of topWorth) {
            for (let topHpBot of topHp) {
                if (topWorthBot.uid === topHpBot.uid) {
                    return stun(topWorthBot.uid);
                }
            }
        }

        if (topWorth.length > 0 && Math.random() * 20 < 19) {
            let target = topWorth[Math.floor(Math.random() * topWorth.length)];
            return stun(target.uid);
        }
    }

    return this.shieldOrUpgrade();
}

Bernard Szumborski is a mercenary, he has been sent by a group of people to stop the powerful wealthy class.

He makes sure that he is strong enough that no one can kill him. Then looks for wealthiest with lots of defense (HP) and targets them because they are the wealthiest of wealthiest! If the ones with top wealth don't have lots of defense, then just targets one of them randomly each turn.

Although it is not in his contract to kill, he gets tempted when he sees someone very weak and kills them.

Night2

Posted 2019-08-01T15:40:21.240

Reputation: 5 484

0

Barbarian Man

As everyone's least favorite superhero, he's a bit desperate. Rather than farm in his free time, he likes to use complex math to choose which villain to repeatedly attack. Once the world is empty of easy-to-kill villains, he retires to a peaceful life of farming. Like his cousin Captain Farmer, he has a special emergency low-health mode.

function barbarianMan(me, others, storage) {
    if (!storage.mode) {
        storage.mode = "prep";
        storage.prep = 0;
    }
    if (me.hp + me.shield < 100)
        storage.mode = "lowhp";
    var weak = others.find(el => el.hp < me.levels.attack * 1.25 + Math.max(...others.map(el => el.attack)) * 2.5 + 10 && Math.ceil(el.worth / 2) >= 5);
    if (weak && storage.weak >= 0) {
        storage.weak += 1;
        return attack(weak.uid);
    }
    if (storage.weak < 0)
        storage.weak += 1;
    else if (storage.weak > 3)
        storage.weak = -3;
    else if (!storage.weak)
        storage.weak = 0;
    if (storage.mode == "prep") {
        storage.prep += 1;
        if (storage.prep < 3)
            return upgrade("shield");
        if (storage.prep < 7)
            return shield();
        if (storage.prep < 10)
            return farm();
        if (storage.prep < 11)
            return heal();
        if (storage.prep < 13)
            return farm();
        if (storage.prep < 14)
            return heal();
        if (storage.prep < 16)
            return upgrade("attack");
        storage.mode = "kill";
    } else if (storage.mode == "lowhp") {
        if ((me.hp + me.shield) > 150) {
            if (me.hp >= 95)
                storage.mode = "kill";
            return heal();
        }
        return shield();
    } else if (storage.mode == "kill") {
        if (!storage.badTargs) {
            storage.badTargs = [];
            storage.testHp = [];
        }
        storage.lastHp = null;
        if (me.gold >= cost(me.levels.attack) && me.levels.attack < 5)
            return upgrade("attack");
        if (me.gold >= cost(me.levels.shield))
            return upgrade("shield");
        if (me.gold >= cost(me.levels.farm))
            return upgrade("farm");
        if (storage.gTarg && attack(storage.gTarg)[0])
            return attack(storage.gTarg);
        if (storage.test == "attack1" && attack(storage.tTarg)[0]) {
            storage.test = "attack2";
            storage.testHp[0] = others.find(el => el.uid == storage.tTarg).hp;
            return attack(storage.tTarg);
        }
        if (storage.test == "attack2" && attack(storage.tTarg)[0]) {
            storage.test = "exam";
            storage.testHp[1] = others.find(el => el.uid == storage.tTarg).hp;
            return attack(storage.tTarg);
        }
        if (storage.test == "exam" && attack(storage.tTarg)[0]) {
            if (storage.testHp[1] - storage.testHp[0] > 3) {
                storage.gTarg = storage.tTarg;
                storage.test = null;
            } else {
                storage.badTargs.push(storage.tTarg);
                storage.test = null;
            }
            return heal();
        }
        let targs = [];
        let scores = [];
        for (let o, i = 0; i < others.length; i++) {
            o = others[i];
            if (storage.badTargs.includes(o.uid))
                continue;
            targs.push(o.uid);
            scores.push((-(o.worth ** 2) / 5000 + 4 * (o.worth) / 15) / o.hp);
        }
        if (!targs.length)
            storage.mode = "farm";
        storage.tTarg = targs[scores.indexOf(Math.max(...scores))];
        storage.test = "attack1";
        return heal();
    } else if (storage.mode == "farm") {
        if (me.gold >= cost(me.levels.shield))
            return upgrade("shield");
        if (me.gold >= cost(me.levels.farm))
            return upgrade("farm");
        if (me.hp > 80 && !storage.healMode)
            return farm();
        storage.healMode = true;
        if (me.hp < 97)
            return heal();
        storage.healMode = false;
        return shield();
    }
    return shield();
}

Redwolf Programs

Posted 2019-08-01T15:40:21.240

Reputation: 2 561

He will happily farm himself to death with over 800 shields left. – Draco18s no longer trusts SE – 2019-08-07T02:29:06.823

@Draco18s Maybe my new update will fix that – Redwolf Programs – 2019-08-07T03:00:50.643

0

TheHalfHalf

Looks every second move for heal or farm. Every other move he shields or randomly attacks other bots. Decides everything on ~50% of its own HP.

function TheHalfHalf(me, others, storage){

    if (turn() == 0) {
        return upgrade("heal");
    }

    if(turn() % 2 == 0) {
        if(me.hp <= 50 - (me.levels.heal + 5)){
            return heal()
        } else {
            if(me.gold >= cost(me.levels.farm)){
                return upgrade("farm")
            } else {
                return farm();
            }
        }
    } else {
        if(me.hp <= 50){            
            return shield();
        } else {
            return attack(others[Math.floor(Math.random() * Math.floor(others.length))].uid);       
        }
    }
}

pixma140

Posted 2019-08-01T15:40:21.240

Reputation: 135

2I recommend upgrading heal once at the beginning of the game, so you can farm three times before healing rather than two – Redwolf Programs – 2019-08-07T12:57:43.480

1@RedwolfPrograms - Tested it out and increased the average score by +200 - thanks! I did not took a look on many of the other bots here, so I hope this was no trick on improving your or other bots in any way :p blind trust here now :D – pixma140 – 2019-08-07T13:20:32.720

1Don't worry, we're all just here to have a good time. I've never heard of anyone trying to cheat or sabotage a bot in these challenges – Redwolf Programs – 2019-08-07T13:28:24.553

1No worries here. Just joking around a little ;) – pixma140 – 2019-08-07T13:40:17.823

0

TeacherBot

function TeacherBot(self, others, storage){
    var maxAtk = others.map(c=>c.attack).reduce((a, b) => Math.max(a, b));
    var maxDmg = maxAtk * 1.25 + 5;
    var myDmg = self.levels.attack * 1.25 + 5;
    var potential_victim = others.find( bot => bot.hp <= Math.max(maxDmg, myDmg) );
    if (potential_victim) {
        return attack(potential_victim.uid);
    }
    let turnsLeft=999-turn()
    if(self.shield<50 && turn() > 5){
        return shield()
    }else if(self.gold>=cost(self.levels.attack)&&turnsLeft>20){
        return upgrade("attack")
    }else if(self.gold>=cost(self.levels.shield)&&self.levels.shield<=4&&turnsLeft>50){
        return upgrade("shield")
    }else{
        return attack(others.sort((a,b)=>b.attack-a.attack)[0].uid)
    }
}

The teacher is not above getting in on a kill (who doesn't like money? BullyBot, that's who), but its primary focus for attacks is the bot with the highest attack value, aiming to punish them for attacking other bots.

Performs at about rank #8 (~12k average gold).

Draco18s no longer trusts SE

Posted 2019-08-01T15:40:21.240

Reputation: 3 053