KoTH de Batalha de Ouro

43

Este desafio terminou. Para ver a pontuação final dos competidores, clique aqui

Nesse desafio, cada envio é um bot. Cada bot deve ser uma função Javascript. Bots lutarão para obter o maior valor total em ouro. O ouro pode ser cultivado ou ganho com a morte de outros bots e é usado para melhorar a cura, o ataque, a proteção e a agricultura.

Objetivo:

Em várias rodadas que contêm até 1000 turnos (termina quando resta apenas um bot), o bot com o valor total mais alto (a soma de todo o ouro obtido) é o vencedor.

Voltas:

Em cada turno, todo bot que estiver vivo (> 0 HP) será executado uma vez. Pode retornar uma movimentação, que pode ser uma das seguintes:

  • Cura: Recupera HP
  • Ataque: Remove HP de outro bot
  • Escudo: Defende-se contra ataques posteriores
  • Atordoar: Ignora o próximo turno de outro bot
  • Fazenda: ganha ouro ao custo da HP
  • Atualização: faça certas jogadas melhor

Todos os bots retornarão sua jogada antes que qualquer seja executado, portanto, atordoamento, cura, ataque, escudo etc. não afetarão nenhum bote em movimento mais tarde nesse turno. Por exemplo, se o Bot A atordoa o Bot B, e o Bot B está atrás do Bot A na ordem do turno, o Bot B ainda se moverá mais tarde nesse mesmo turno e o atordoamento ocorrerá no próximo turno.

Combate, agricultura e atualização:

Cada bot tem um HP máximo de 100 e um UID atribuído entre 0 e 99. Esse UID muda após cada rodada e é assim que os bots se controlam.

A cura é um dos movimentos mais simples, adicionando uma quantidade de HP determinada pelo seu nível (começa em 5 HP). Um bot não pode curar mais de 100 HP.

Atacar um bot por seu UID é outro movimento possível, com um dano base de 5 HP no nível 0. Os bots também podem ser atordoados, pulando o próximo turno, que também usa UIDs.

Bots possuem escudo adicional HP, que não tem limite. Este escudo HP absorve dano de ataques diretos de outros bots e é adicionado por blindagem. No nível 0, a blindagem adiciona 5 de HP de escudo.

A agricultura ganhará 5 de ouro no nível 0, ao custo de 2 PV. Este 2 HP não pode ser protegido. O único uso do ouro (além da vitória) é atualizar os movimentos. Cura, ataque e proteção têm um valor base de 5 HP, e a agricultura começa com 5 de ouro. Cada uma dessas jogadas possui níveis individuais, que começam em 0. Essas fórmulas determinarão o valor em HP ou ouro de uma jogada, onde L é o nível:

  • Cura: L + 5
  • Atacante: 1.25L + 5
  • Blindagem: 1.5L + 5
  • Agricultura: 2L + 5

O custo da atualização de qualquer movimento é o mesmo para um determinado nível e é determinado por 2.5L² + 2.5L + 10onde L é o nível atual. Um bot pode usar a função cost(currentLevel)como um atalho para determinar isso.

Os bots começam com 25 de ouro, permitindo que eles atualizem rapidamente dois movimentos para o nível 1 ou um para o nível 2. Esse ouro inicial não conta para um valor total de bots. Matar um bot dá a você metade do seu valor total em ouro, arredondado para cima, e se dois bots matam outro no mesmo turno, ambos recebem a recompensa.

Entrada / Saída:

Para se comunicar com o controlador, o valor de retorno da função é usado para enviar informações de movimentação. Um destes deve ser retornado:

  • Curar: heal()
  • Ataque: attack(uid)
  • Escudo: shield()
  • Atordoar: stun(uid)
  • Fazenda: farm()
  • Melhorar: upgrade("heal" / "attack" / "shield" / "farm")

Para pular um turno (não fazer nada), não retornar nada ou retornar um valor falso.

Para obter o número da curva atual (começa em 1), use turn().

Os argumentos de sua função incluirão informações sobre seu bot, UIDs de outros bots e armazenamento entre turnos. O primeiro argumento é um objecto com as seguintes propriedades: uid, hp, gold, e shield. Estas são cópias das informações atuais do seu bot. Há também um objecto aninhada levels, com os números de nível de heal, attack, shield, e farm.

O segundo argumento é um conjunto aleatório de todas as outras do que a sua bots vivos, formatado como um objecto contendo propriedades uid, hp(mais blindagem), worthe attack(nível de ataque). O terceiro argumento é um objeto vazio que pode ser usado para armazenamento entre turnos.

Bots de exemplo:

Este bot irá cultivar até que possa atualizar seu ataque para o nível 5, depois atacará um bot aleatório a cada turno até morrer (ou vencer). Não é muito eficaz devido à falta de cura / blindagem.

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);
}

Este bot tem dois modos: ofensivo e defensivo. Atordoará um bot aleatório ou curará quando estiver no modo defensivo, e atacará ou protegerá quando estiver no modo ofensivo. Ele tentará atualizar seus ataques sempre que possível.

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();
}

Regras:

  • Lacunas padrão proibidas
  • Os bots não podem ler, modificar ou adicionar variáveis ​​fora do seu escopo, não podem tentar trapacear e não podem chamar nenhuma função DOM ou definida pelo controlador
  • O valor de retorno deve ser falso, ou uma das funções da função acima
  • Os bots não devem ser projetados para atingir um bot específico, mas podem ser projetados para tirar proveito de estratégias comuns
  • Os bots podem não se atacar (descobertos devido a um comentário de @Ness)
  • Os bots devem ser suficientemente diferentes dos outros bots para que possam ser considerados razoavelmente entradas separadas
  • Agora, o agrupamento não é permitido
  • O controlador pode ser encontrado aqui
  • Sala de bate-papo

Nova depuração de controlador:

Usando o arquivo gold-battle-log.js, você pode definir o valor da debugpropriedade de um bot botDatacomo 0 (sem registro), 1 (movimentos do log) ou 2 (movimentos do log, hp, ouro, níveis, etc.)

O desafio termina às 1700 UTC na sexta-feira, 9 de agosto

Programas Redwolf
fonte
4
Criou uma essência com todos os bots. gist.github.com/Draco18s/2efbf95edcf98d6b1f264e26bbb669d1 Vou me esforçar para mantê-lo atualizado (mas se não for um começo decente).
Draco18s
4
Controlador de atualização automática com bots incluído: redwolfprograms.com/koth
Programas Redwolf
4
Estou votando para encerrar esta pergunta porque ela já está fechada para novas respostas ("Este desafio terminou. Para ver a pontuação final ...")
pppery em
3
@pppery Você não poderia? Eu ficaria bem com respostas não competitivas e, [closed]no final, é provável que os espectadores casuais deixem de ler o meu desafio, pois eles assumem que é de baixa qualidade ou fora de tópico.
Redwolf Programs
5
@pppery Eu nunca ouvi falar de um desafio encerrado por terminar até hoje, e eu argumentaria que a restrição social que você deseja impor nem existe. Não há necessidade de fechá-lo e não o quero fechado. Para mim, isso parece fechar por uma questão de fechamento, e não pelo bem do site. Se alguém quiser postar uma resposta para uma pergunta antiga, deve poder. Após a regra do candidato sério, não há nenhuma nota dizendo que ele deve ser um candidato sério quando publicado; uma resposta ainda pode ser um candidato sério para o desafio, mesmo que não seja um candidato à vitória
Redwolf Programs

Respostas:

16

Inábil

bifurcada de 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()
        }
    }
}

Dados os custos exponenciais das atualizações, também podemos melhorar a agricultura se não pudermos melhorar a cura, permitindo que o bot colete ouro com mais eficiência.

Draco18s
fonte
Absolutamente esmagando a competição nos meus testes
Redwolf Programs
1
Eu sinto que esse bot pode ser um pouco mais forte se a primeira ifdeclaração for usada <=- atualmente ele nunca se recuperará completamente.
Scoots
@Scoots Não tenho certeza do quanto isso importa, mas vou mudar isso.
Draco18s
2
@ Draco18s Tenho certeza de que importa muito pouco - mas este site não trata de pequenas melhorias praticamente insignificantes? :)
Scoots
@Scoots A cura para a saúde máxima não importa muito neste desafio, porque não há ameaças reais de ataque. O único bot verdadeiramente ofensivo é o bullybot, e você realmente não pode fazer nada sobre ele. Na verdade, pode diminuir o desempenho para manter a saúde total.
B0RDERS
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();
            }
        }
    }
}

Existem muitos bots, não há ouro suficiente para circular. Este bot propõe uma solução.

Genocídio, sim, mas aleatório, desapaixonado, justo para ricos e pobres.

Eles o chamavam de louco.

ThanosBot quer o melhor para a comunidade de bot e está disposto a ir todo o caminho. No começo, ele atualizará seu ataque, agricultura e cura, para reunir recursos com mais eficiência e vencer batalhas. Prograssivamente, ele começará a atacar pessoas aleatoriamente enquanto ainda reúne recursos, para as próximas batalhas. Ele continuará melhorando seu exército, suas armas e ele próprio.

Assim que 50% da população for eliminada, os bots nascidos conhecerão apenas a barriga e o céu limpo, ele se retirará para uma vida agrícola e assistirá o sol nascer em um universo agradecido. Ele se tornará completamente pacifista, apenas se curando com sopas de legumes e agricultura.

Kaito Kid
fonte
6
Estou tentado a mudar o nome de "ataque" para "encaixar"
Programas Redwolf
11

Ladrão da Matança

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();
}

Agora, não apenas rouba mata, mas rouba também mata!

Esse bot não faz muito, exceto a fazenda, e quando percebe a possibilidade, se une para dar o golpe final a um inimigo moribundo e, de alguma forma, consegue ser muito bom.

dzaima
fonte
Funciona porque todos os bots envolvidos em um golpe fatal recebem a recompensa total.
Draco18s
@ Draco18s Eu entendo por que poderia ser bom, eu simplesmente não esperava uma idéia tão simples de obter, em média, 2x a pontuação do próximo melhor bot (na hora de fazê-lo).
dzaima
Hehe, isso é justo. Vou ter que baixar todos os bots quando puder e ver se consigo encontrar outra solução.
Draco18s
9

O Equalizador

Este bot procura restaurar a paz na comunidade de bot. Ele implacavelmente mira nos bots com o ataque mais alto, desistindo apenas se a cura do bot for melhor que o seu próprio ataque. Uma vez que nenhum robô com cura pior do que seu ataque é deixado, ele se retirará para uma vida de agricultura pacífica.

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
fonte
8

Otimista

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();
}

Supõe que ele será capaz de gastar 80% de seu tempo cultivando pacificamente, então começa maximizando a agricultura e só então começa a prestar atenção em suas habilidades de combate. Certamente nada vai dar errado!

histocrata
fonte
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();
}

Por que atualizar o valor do ataque quando você pode causar dano de plink e ainda obter crédito total?

Mais uma vez voltando às costas do Kill Stealer. Consegui simplificar vários blocos de código em que as declarações sempre eram verdadeiras e mexiam em alguns números que resultavam em ganhos maciços em relação ao original.

Eu tenho que entregá-lo a @dzaima por perceber que atordoar um oponente rico que provavelmente estará envolvido em uma assistência no turn antes que uma matança aconteça é bastante inteligente. Uma das (muito) poucas vezes Stun()tem um resultado positivo de soma. Mais uma vez, fui capaz de aprimorar a ideia, pois, sabendo que o Kill Stealer estará executando uma lógica semelhante, o Kill Assist procura um "segundo melhor" alvo (com alguma discrição) e atordoa-os.

Atualização menor para evitar atordoar o bot-prestes-a-morrer e evitar atordoar o bot mais provável de matar.

Resultados de amostra (top 5 truncado após 1000 jogos)

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
fonte
Espere, em que mundo o capitão Farmer ganha ouro de 14k?
Programas Redwolf
Este:runGame(1) results: [...] captFarmer: 13768
Draco18s
Isso é inesperadamente alto ... geralmente chega em torno de 10k nos meus testes
Redwolf Programs
* shrugh * Não faço ideia. Farei uma atualização automática de essência apenas para garantir que tudo esteja limpo.
Draco18s
Meu bot favorito até o final do prazo.
Night2
7

Bot Indyable (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
fonte
Não se importe comigo ... vou emprestar isso.
Draco18s
6

PatientStrategistBot

Eu tentei escrever um bot que começa a enquadrar e defender conforme necessário e depois muda para matar outros bots de alto valor posteriormente no jogo.

Atualmente, isso parece não funcionar corretamente, pois é morto por uma gangue de assassinos no início do jogo ou fica preso em algum lugar no seu modo ofensivo.

Ainda muito feliz por esse ser meu primeiro código JS, então ... (roubei trechos de código daqui e de lá, porque isso foi mais rápido do que pesquisar todas as sintaxes básicas do JS)

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
fonte
6

Suíça

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()
    }
}

Como o nome sugere, este bot é neutro, principalmente neutro (agora ajuda a matar bots que vão morrer) e apenas fazendas e cura, lentamente construindo seu ouro ( como a Suíça )

SuperStormer
fonte
6

O bot que cultiva, ataca, protege e até cura, mas nunca atordoa

(O nome abreviado é TBTFASAEHBNS , não deve ser confundido com 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();
}

Este bot basicamente:

  • Começa a cultivar no início
  • Defende-se quando necessário
  • Ataca quando pode matar ou quando pensa que há uma chance de matar alguém
  • Atualizações aqui e depois
  • Fazendas o resto do tempo
  • Nunca atordoa

Editar 1: Corrigido um problema e aprimorado algumas pequenas coisas no bot com base em testes com muitos jogos.

Edit 2: Atualizações de escudo reduzidas.

Night2
fonte
2
Assim que vi o nome, sabia que seria o seu bot (:
Programas Redwolf
Sinto muito pelos nomes longos, mas sou viciado nisso!
Night2
1
Talvez seja um sinal de um bom bot ... meus testes mostram que está em 5º lugar
Programas Redwolf
5

SniperBot

Esse bot só será eficaz se alguém começar a adicionar bots que realmente atacam regularmente. SmartFarmer é minha atual solução otimizada

  1. cura se pode obter um tiro
  2. cura se menos de 30
  3. ataca bot se pode pegá-lo e ganhar mais dinheiro do que a agricultura
  4. atualizações agricultura se pode pagar
  5. melhora a cura se tiver menos de 80 pontos de vida e pode pagar
  6. fazendas

abutres não precisam de um ataque

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
fonte
Identificador inesperado ( int) na linha 2. ReferenceError: a saúde não está definida.
Draco18s
Deveria ser me.hp?
mbomb007
desculpa. novo no javascript. obrigado pela ajuda
B0RDERS
Você if(me.hp <30 && ...)pode ser simplificado para apenas a primeira cláusula por precisar de um nível absurdo de cura para que isso importe (lvl 65)
Veskah
@ Veskah Obrigado por apontar isso. Isso foi um remanescente de quando o hp mínimo foi maior
B0RDERS
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 do BullyBot e alguns outros bits. O Optimist tinha um pequeno e doce pedaço de ataque oportunista que eu vinculei (embora outros bots façam cálculos semelhantes).

Em vez de intimidar o alvo atordoando-o, ele o mata por seu doce, doce saque. Ele também tem como alvo os mais fracos no rebanho para o bullying, mas desistirá e continuará a cultivar se o HP do alvo mais fraco for muito alto.

Draco18s
fonte
você está se matando até a morte. Accept my edit :)
B0RDERS
1
@AndrewBorders Ha, nem sequer pensou nisso. Obrigado.
Draco18s
Esse bot foi ótimo até os robôs protetores aparecerem.
B0RDERS
@ B0RDERS O escudo é muito forte, mesmo que perca tempo.
Draco18s
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);
}

Bot principalmente ofensivo. Extremamente chateado pelo fato de que ele não pode realmente usar o FizzBuzz, apenas vibra com raiva. Quando não é Fizzing ou Zumbido, ele corta outro bot por 30 turnos e desiste e escolhe outro bot para mirar se não estiver progredindo.

Executa extraordinariamente inconsistentemente. Não importa, atualizou o controlador, agora parece estar sempre no meio do pacote.

Transformada de Fourier de Rin
fonte
Eu gosto deste conceito. Independentemente da situação atual, ela continua acompanhando seu próprio ritmo.
Ness
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);
}

Experimente online!

Pode não ganhar, mas certamente tentará o máximo possível para garantir que seu alvo também não. O bullyBot também faz agricultura no primeiro turno, para que, se não houver influência externa, ele derrote o alvo por 5-0 ou empate em 5-5.

Veskah
fonte
5

JustFarm

Eu pensei que começaria simples.

function justFarm(me, others){
    return farm();
}
Anônimo
fonte
13
Este bot vai se suicidar devido ao custo de 2HP da agricultura.
Draco18s
@ Draco18s Embora a rodada possa terminar antes disso, dependendo do número de bots
Programas Redwolf
1
Embora tecnicamente verdadeiro, um cronômetro de 50 rodadas é muito muito curto quando o horário de término padrão é 1000.
Draco18s
Ele superou os dois exemplos de bots, mas agora há mais algumas submissões que eu poderia tentar apresentar algo melhor.
Anónimo
@ Anônimo Talvez seja suficiente incluir atualizações de cura e agricultura. Como obter o máximo de ouro é o objetivo final, mantendo-o como o principal trabalho do bot. Não houve quaisquer bots até agora que tem "modos" como o modo eo modo fazenda curar, pode ser uma abordagem interessante
RedWolf Programas
4

ScavengerBot (V2)

Percebeu que não era muito um limpador antes. A nova estratégia é esperar até que ele possa matar outro bot. Se ninguém pode ser morto, ele se senta e constrói um escudo.

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
fonte
1
me.levels.attacl?
Draco18s
Boa captura, corrigida
reffu 01/08
4

temperamental

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);   
}

A estratégia padrão da Moody's é atualizar a agricultura e a cura por um tempo e remover os outros bots em ordem decrescente de valor. No entanto, se for atacado, ficará com medo e se concentrará na cura por um tempo. Se atacar e "falhar", porque a vítima estava curando ou protegendo com mais eficácia do que o ataque, ela fica com raiva e sai para melhorar suas habilidades de ataque.

histocrata
fonte
4

Bandido

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;
}

Primeira tentativa de um robô de aprendizado por reforço. Puramente defensivo, por enquanto, para diminuir o espaço de pesquisa. Uma espécie de spinoff mais inteligente do FizzBuzz - ele repete uma série específica de cinco ações repetidas vezes; as cinco ações são as escolhidas pela RL.

Mas, na maioria das vezes, é baseado na enumeração - acabei de gerar todas as permutações de 3 ^ 5 = 243 de séries de cinco ações defensivas que se repetiam repetidamente e armazenava suas pontuações médias (divididas por 200, para obter o ganho médio cinco turnos) mais de 100 iterações na storage.priormatriz. Então, durante o jogo, ele implementa uma abordagem gananciosa para atualizar essas listas de pontuação, para que fique mais à prova de futuro. (Também porque o uso do epsilon = 0.3 foi muito melhor que o epsilon = 0.1, eu apenas o mantive.)

Está tudo bem, consistentemente colocando entre scavengerBot e Optimist. Atualmente, estou treinando um pouco mais em jogos reais e procurando maneiras melhores de estruturar a estratégia, para ver se posso melhorá-la.

Transformada de Fourier de Rin
fonte
4

O Oportunista

Este empresta um pouco de alguns outros (principalmente o ScavengerBot (V2) e o Unkillable), pois eles tinham as mesmas idéias que eu tinha em mente, mas geralmente gosto mais de estilo arredondado e de pau para fora, do que focar apenas em uma ou duas coisas. Isso provavelmente significa que eu não vou ganhar, mas deve estar no meio do caminho (o que acontece comigo na maioria das vezes em muitas coisas).

Por isso, rouba mortes suculentas; cura, se necessário; atualiza fazenda, ataque e cura nessa ordem; e fazendas de outra forma.

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
fonte
1
2º argumento deve serothers
SuperStormer
4

ScaredBot

  1. Encontra outros bots:
    • com o maior ataque
    • com mais riqueza e HP menor que o próprio ataque
  2. Se o seu escudo HP + for menor que o encontrado highest attack * (25% of bots), ou se chegar perto da extremidade inferior HP + shield, ele fará o escudo
  3. Se ele encontrou um bot com escudo mais baixo que seu próprio ataque, ele o ataca.
  4. Se sua saúde é < 50, ela cura.
  5. Se ele pode atualizar qualquer escudo, curar e cultivar, atualiza aquele com o nível mais baixo
  6. Fazendas
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();
    }
}

A idéia é manter-se vivo o maior tempo possível e, caso contrário, tente conseguir ouro de uma maneira segura e barata para poder atualizar.

As prioridades de atualização provavelmente devem ser ajustadas, bem como a condição ao determinar se deve ser blindado.

user24343
fonte
3

SmartFarmer

Fazendas, melhora a agricultura, cura se a saúde é baixa. A agricultura parecia dominada até que os robôs verdadeiramente ofensivos chegassem. Agora meu bot é morto :-(

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
fonte
1
Eu estava (manualmente) testando essencialmente a mesma estratégia para ver qual é o valor máximo possível de obtenção e os melhores números que consegui, atrasando levemente a atualização do heal (usei gold> = cost * 2) e subindo para o lvl10 heal .
Nicolai
Esse multiplicador de preços é uma boa ideia. Eu adicionei algo semelhante. Eu estaria interessado em ver quais números você obteve
B0RDERS
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];
}

A cada turno, compara o lucro amortizado de matar cada bot ao da agricultura e da cura e escolhe a melhor opção. Realmente, ele deve usar state para descobrir quanto tempo levará para matar um bot, mas, por enquanto, ele assume que todo bot cura ou protege uma média de 5 pontos por turno, líquido dos danos que outros bots causam.

histocrata
fonte
3

Bot amigável

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é em hp + shieldvez de hp...

tsh
fonte
4
Alguém pode me ajudar a traduzir o nome da função para o inglês? ^ _ ^
tsh
4
Segundo o Google Translate, "闷声 发大财" significa "Abafado". Tenho certeza de que não é isso que você deseja e é, de fato, mais uma falha épica do Google Tradutor ... Pesquisei mais e todos os resultados parecem mencionar que não há uma única palavra em inglês que possa ser usada aqui; portanto, é melhor mantê-la como é, na verdade, como parece ser uma preposição chinesa que geralmente significa que se deve trabalhar silenciosamente e deixar que os resultados falem por si mesmos e atinjam uma filosofia tradicional. Infelizmente, não sei chinês para traduzi-lo diretamente. : D
Erik o Outgolfer
1
como um falante nativo de chinês, significa algo como "silenciosamente faça uma enorme fortuna": v também denota intencionalmente silêncio, literalmente "encobrindo o som"
Transformação de Fourier de Rin em
1
Sorrateiro? UnderTheRadar? DontMindMe? AttentionDeflector?
Peter Taylor
3

O contador

Esse bot prático calcula a jogada economicamente mais benéfica, mas ele gosta de manter o perfil de ataque baixo para evitar problemas de todos os bots vigilantes. Ele não tenta ajudar os indefesos ou atacá-los. Em vez disso, ele faz o que mais o ajuda.

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
fonte
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();
}

Aqui está a coisa. Ficou realmente perigoso por aí. Cultivar aumenta o seu valor, fazendo de você um alvo. Portanto, não é realmente seguro cultivar até que você construa um escudo enorme e toda a violência diminua. Então você pode tirar a cabeça da casca e começar a cultivar. Ou ajudar na matança. O que paga melhor.

histocrata
fonte
2

Guardião

Eu posso ter mais de uma submissão, certo?

Um garfo do CampBot. Não protege, concentrando-se em atacar. Mostra uma preferência por atacar jogadores com estatísticas de ataque mais altas, em vez de atacar aleatoriamente como o CampBot. Concentra-se em melhorar sua agricultura, em vez de curar.

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()
    }
}
Anônimo
fonte
meu bot não ataca aleatoriamente ...
SuperStormer
Você pode postar quantas vezes quiser, mais
divertido
@SuperStormer Sei que o seu não é inteiramente aleatório, mas:let victim=potentialVictims[Math.floor(Math.random()*potentialVictims.length)]
Anônimo
mas primeiro filtra aqueles que não valem a pena atacar
SuperStormer
Eu estava trabalhando em um bot semelhante chamado equalizador quando você postou isso. Ainda estou ajustando, mas gosto de algumas de suas idéias.
B0RDERS
2

Rando

Esse cara tolo escolherá ações com base na aleatoriedade uniforme com alguns vieses. Se uma ação escolhida aleatoriamente não funcionar, ela será direcionada para a próxima opção.

Então, em média, ele deve atacar quase 2/9 das vezes e cultivar quase 3/9 das vezes. O restante tem cerca de 1/9 de chance se ele puder atualizar, ou se a cura / proteção vale a pena, etc.

Ele provavelmente não terá um bom desempenho, mas pelo menos há uma pequena chance de que ele reine supremo. E esse é o objetivo de Rando. Ele só precisa acreditar em si mesmo! Todas as opções são colocadas na frente dele. Ele precisa apenas escolher o que é necessário para qualquer situação.

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
    }
}

(Eu sei que "padrão" é desnecessário, mas acho que é uma boa prática de codificação para código robusto.)

Ness
fonte
2
"Ele só precisa acreditar em si mesmo" ... Estou rindo muito agora
Programas Redwolf
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);
}

Um bot simples, o Kill Bot só quer matar seus inimigos. Como proteger é muito mais eficiente que curar (especialmente quando nivelado), Kill Bot apenas tenta ser sempre um alvo desagradável, protegendo-se sempre que atacado. Kill Bot se sai muito bem entre os robôs fracos e pacifistas por aqui (você pode sentir seu desprezo por eles).

Spitemaster
fonte
3
Observe que esse o.attacké o nível de ataque, não o dano
Programas Redwolf
2

FarmHeal Bot

Bifurcada de @Anonymous 'JustFarm bot

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

Indestrutível

Uma modificação do bot de Draco18, usando escudos (mais eficaz contra outros 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();
        }
    }
}
Programas Redwolf
fonte