Золотая Битва KoTH

44

Эта проблема закончилась. Чтобы увидеть итоговые оценки участников, нажмите здесь

В этом задании каждая заявка представляет собой одного бота. Каждый бот должен быть функцией Javascript. Боты будут сражаться, чтобы получить наивысшую общую стоимость золота. Золото можно добывать на ферме или зарабатывать на убийстве других ботов, и оно используется для улучшения лечения, атаки, защиты и ведения сельского хозяйства.

Задача:

В течение нескольких раундов, содержащих до 1000 ходов (заканчивается, когда остается только один бот), бот с наибольшей общей стоимостью (сумма всего полученного золота) становится победителем.

Повороты:

В каждом ходу каждый живой бот (> 0 HP) запускается один раз. Он может вернуть ход, который может быть одним из следующих:

  • Лечение: восстанавливает HP
  • Атака: удаляет HP из другого бота
  • Щит: защищает от последующих атак
  • Оглушение: пропускает следующий ход другого бота
  • Ферма: зарабатывает золото за счет HP
  • Обновление: сделать определенные шаги лучше

Все боты вернут свой ход до того, как они будут выполнены, поэтому оглушение, лечение, атака, щит и т. Д. Не повлияют на ботов, движущихся позже в этом ходу. Например, если бот A оглушает бот B, а бот B следует за ботом A в порядке хода, бот B все равно будет двигаться позже в том же ходу, и оглушение произойдет в следующем ходу.

Бой, фермерство и модернизация:

Каждый бот имеет максимальный HP 100 и назначенный UID от 0 до 99. Этот UID меняется после каждого раунда, и как боты отслеживают друг друга.

Лечение - один из самых простых ходов, добавляющий количество HP, определяемое его уровнем (начинается с 5 HP). Бот не может лечить более 100 HP.

Атака бота по его UID - это еще один возможный ход с базовым уроном 5 HP на уровне 0. Боты также могут быть оглушены, пропуская свой следующий ход, который также использует UID.

Боты имеют дополнительный щит HP, который не имеет ограничений. Этот щит HP поглощает урон от прямых атак других ботов и добавляется защитой. На уровне 0 экранирование добавляет 5 щитовых HP.

Фермерство заработает 5 золотых на уровне 0, за 2 HP. Эти 2 HP не могут быть защищены. Единственное использование для золота (кроме выигрыша) - улучшать ходы. Исцеление, атака и защита имеют базовую стоимость 5 HP, а фарм начинается с 5 золота. Каждый из этих ходов имеет индивидуальные уровни, которые начинаются с 0. Эти формулы будут определять значение в HP или золоте хода, где L - уровень:

  • Лечение: L + 5
  • Атакующий: 1.25L + 5
  • Экранирование: 1.5L + 5
  • Сельское хозяйство: 2L + 5

Стоимость улучшения любого хода одинакова для определенного уровня и определяется тем 2.5L² + 2.5L + 10, где L - текущий уровень. Бот может использовать функцию cost(currentLevel)в качестве ярлыка, чтобы определить это.

Боты начинают с 25 золотых, что позволяет им быстро улучшить либо два хода до уровня 1, либо один ход до уровня 2. Это начальное золото не засчитывается в общую стоимость ботов. Убив бота, вы получаете половину его общей стоимости в золоте, округленную в большую сторону, и если два бота убивают другого в одном ходу, они оба получают вознаграждение.

Ввод, вывод:

Для связи с контроллером возвращаемое значение функции используется для отправки информации о перемещении. Один из них должен быть возвращен:

  • Лечение: heal()
  • Атака: attack(uid)
  • Щит: shield()
  • Оглушение: stun(uid)
  • Ферма: farm()
  • Обновить: upgrade("heal" / "attack" / "shield" / "farm")

Чтобы пропустить поворот (ничего не делать), ничего не возвращать или возвращать ложное значение.

Чтобы получить номер текущего хода (начинается с 1), используйте turn().

Аргументы вашей функции будут включать информацию о вашем боте, UID других ботов и хранилище между поворотами. Первый аргумент является объектом со следующими свойствами: uid, hp, gold, и shield. Это копии текущей информации вашего бота. Существует также несомый объект levels, с номерами уровня heal, attack, shieldи farm.

Второй аргумент перетасованного массив всех других , чем ваша живых ботов, отформатированные как объект , содержащий свойства uid, hp(плюс щит), worthи attack(уровень атаки). Третий аргумент - пустой объект, который можно использовать для хранения между поворотами.

Пример ботов:

Этот бот будет фармить до тех пор, пока не сможет повысить свою атаку до уровня 5, а затем атаковать случайного бота каждый ход, пока он не умрет (или не победит). Не очень эффективно из-за отсутствия лечения / защиты.

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

Этот бот имеет два режима: атакующий и оборонительный. Он будет либо оглушать случайного бота, либо лечить в режиме защиты, а также атаковать или защищать в режиме атаки. Он будет пытаться обновить свои атаки, когда это возможно.

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

Правила:

  • Стандартные лазейки запрещены
  • Боты не могут читать, изменять или добавлять какие-либо переменные вне своей области, не могут пытаться обманывать и не могут вызывать какие-либо определенные контроллером или функции DOM
  • Возвращаемое значение должно быть ложным, или один из вышеуказанных выходов функции
  • Боты не должны быть предназначены для конкретного бота, но могут быть разработаны, чтобы использовать преимущества общих стратегий
  • Боты не могут атаковать самих себя (обнаружено из-за комментария @Ness)
  • Боты должны достаточно отличаться от любых других ботов, чтобы их можно было разумно считать отдельными записями
  • Объединение в команду теперь не разрешено
  • Контроллер можно найти здесь
  • Chatroom

Отладка нового контроллера:

Используя файл gold-battle-log.js, вы можете установить значение debugсвойства бота в botData0 (без регистрации), 1 (перемещение журналов) или 2 (перемещение журналов, hp, золото, уровни и т. Д.)

Вызов заканчивается в 17:00 UTC в пятницу, 9 августа.

Redwolf Программы
источник
4
Создан суть со всеми ботами. gist.github.com/Draco18s/2efbf95edcf98d6b1f264e26bbb669d1 Я буду стараться обновлять его (но если нет, то это неплохое начало).
Draco18s
4
Контроллер автообновления
Программы
4
Я голосую, чтобы закрыть этот вопрос, потому что он де-факто уже закрыт для новых ответов («Этот вызов закончился. Чтобы увидеть окончательные результаты ...»)
pppery
3
@pppery Не могли бы вы? Я был бы в порядке с неконкурентными ответами, и [closed]в конце, вероятно, заставит случайных зрителей пропустить чтение моего вызова, так как они предполагают, что это низкое качество или не по теме.
Программы
5
@pppery Я никогда не слышал о том, что вызов был закрыт из-за того, что он был завершен до сегодняшнего дня, и я бы сказал, что социальных ограничений, которые вы хотите применить, даже не существует. Нет необходимости закрывать его, и я не хочу, чтобы он закрывался. Мне кажется, что это закрытие ради закрытия, а не ради блага сайта. Если кто-то хочет опубликовать ответ на старый вопрос, он должен это сделать. После правила о серьезном сопернике нет примечания о том, что он должен быть серьезным, когда публикуется; ответ может быть серьезным претендентом на вызов, даже если он не претендует на победу
Программы

Ответы:

16

незавершаемых

раздвоенный от неуязвимого .

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

Учитывая экспоненциальную стоимость улучшений, мы можем также улучшить фермерство, если мы не сможем улучшить лечение, что позволит боту более эффективно собирать золото.

Draco18s
источник
Абсолютно подавляющее соревнование в моих тестах
программы
1
Я чувствую, что этот бот мог бы быть немного сильнее, если бы использовалось это первое ifутверждение <=- в настоящее время он никогда не излечится полностью.
Scoots
@ Scoots Не уверен, насколько это будет иметь значение, но я изменю это.
Draco18s
2
@ Draco18s Я уверен, что это очень мало, но разве этот сайт не посвящен крошечным, практически незначительным улучшениям? :)
Scoots
@ Scoots Исцеление до максимального здоровья не имеет большого значения в этой задаче, потому что нет реальных атакующих угроз. Единственный действительно оскорбительный бот - это Bullybot, и вы ничего не можете с ним поделать. Это может фактически снизить производительность, чтобы оставаться на полном здоровье.
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();
            }
        }
    }
}

Слишком много ботов, недостаточно золота, чтобы ходить. Этот бот предлагает решение.

Геноцид, да, но случайный, беспристрастный, справедливый по отношению к богатым и бедным.

Они называли его сумасшедшим.

ThanosBot хочет лучшего для сообщества ботов и готов идти до конца. В начале он улучшит свою атаку, фарм и лечение, чтобы более эффективно собирать ресурсы и побеждать в битвах. Постепенно он начнет нападать на людей случайным образом, собирая ресурсы для предстоящих сражений. Он будет продолжать улучшать свою армию, свое оружие и себя.

Как только 50% населения будет уничтожено, рожденные боты будут знать только полные животы и ясное небо, он уйдет на фермерскую жизнь и будет наблюдать за восходом солнца в благодарной вселенной. Он станет полностью пацифистом, только исцеляя себя овощными супами и занимаясь земледелием.

Кайто Кид
источник
6
Я испытываю желание переименовать «атаку» в «оснастку»
Redwolf Programs
11

Убить Стилера

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

Теперь не только ворует, но и ворует!

Этот бот не делает ничего, кроме фермы, и когда он замечает возможность, присоединяется к нанесению последнего удара по умирающему врагу и каким-то образом удается быть очень хорошим.

dzaima
источник
Это работает, потому что все боты, вовлеченные в смертельный удар, получают полную награду.
Draco18s
@ Draco18s Я понимаю, почему это может быть хорошо, я просто не ожидал, что такая простая идея получит в среднем в 2 раза больше очков следующего лучшего бота (на момент его создания).
Дзайма
Хе-хе, это справедливо. Мне нужно будет загрузить всех ботов, когда я смогу, и посмотреть, смогу ли я найти другое решение.
Draco18s
9

Эквалайзер

Этот бот стремится восстановить мир в сообществе ботов. Он неустанно целится в ботов с самой высокой атакой, сдаваясь, только если лечение бота лучше, чем его собственная атака. Как только не останутся боты с лечением хуже, чем его атака, он уйдет в мирное сельское хозяйство.

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
источник
8

оптимист

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

Предполагается, что он сможет тратить 80% своего времени на мирное сельское хозяйство, поэтому он начинает с максимального развития сельского хозяйства и только после этого начинает обращать внимание на свои боевые навыки. Конечно, ничего не пойдет не так!

histocrat
источник
8

Помощь в убийстве

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

Зачем обновлять значение атаки, если вы можете наносить урон и все еще получать полный кредит?

Еще раз вернемся к спекуляции с Kill Stealer. Мне удалось упростить несколько блоков кода, где операторы всегда были верны и возились с некоторыми числами, что приводило к огромным выигрышам по сравнению с оригиналом.

Я должен передать это @dzaima, чтобы понять, что ошеломляющий богатый противник, который, вероятно, будет вовлечен в помощь на терне до того, как произойдет убийство, довольно умен. Один из (очень) нескольких раз Stun()имеет положительный результат. Я снова смог улучшить эту идею: зная, что Kill Stealer будет использовать аналогичную логику, Kill Assist ищет «вторую лучшую» цель (с некоторым усмотрением) и вместо этого оглушает ее.

Незначительное обновление, чтобы предотвратить ошеломление бот-о-умирать и предотвратить ошеломляющий бот-скорее всего, чтобы сделать убийство.

Пример результатов (усеченные топ-5 после 1000 игр)

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
источник
Подождите, в каком мире капитан Фармер получает 14 тысяч золота?
Программы
Вот этот:runGame(1) results: [...] captFarmer: 13768
Draco18s
Это довольно неожиданно высоко ... обычно в моих тестах оно составляет около 10 тыс.
Redwolf Programs
* shrugh * Понятия не имею. Я сделаю автоматическое обновление гист, чтобы убедиться, что все чисто.
Draco18s
Мой любимый бот к концу срока.
Night2
7

Неуязвимый бот (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()
            }
        }        
    }   
}

Луис Фелипе Де Иисус Муньос
источник
Не против меня ... Я собираюсь позаимствовать это.
Draco18s
6

PatientStrategistBot

Я попытался написать бота, который начинает создавать и защищать по мере необходимости, а затем переключается на убийство других ценных ботов позже в игре.

В настоящее время это, кажется, не работает должным образом, так как либо убит бандой роботов-убийц в начале игры, либо застрял где-то в его наступательном режиме.

Все еще очень доволен тем, что это мой первый JS-код, так что ... (я украл фрагменты кода отсюда и там, потому что это было быстрее, чем поиск всего основного синтаксиса 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
источник
6

Швейцария

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

Как следует из названия, этот бот является нейтральным, в основном нейтральным (теперь он помогает убивать ботов, которые умрут), и только фермы и лечит, медленно наращивая свое золото ( как Швейцария )

SuperStormer
источник
6

Бот, который обрабатывает, атакует, защищает и даже лечит, но никогда не оглушает

(Краткое название TBTFASAEHBNS , не путать с 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();
}

Этот бот в основном:

  • Создает на ферме в начале
  • Защищает себя при необходимости
  • Атакует, когда может убить или когда думает, что есть шанс убить кого-то
  • Обновления здесь и тогда
  • Фермы остальное время
  • Никогда не оглушает

Редактировать 1: Исправлена ​​проблема и улучшены некоторые мелочи в боте, основанные на тестах с большим количеством игр.

Редактировать 2: Уменьшены обновления щита.

night2
источник
2
Как только я увидел имя, я понял, что это будет ваш бот (:
Redwolf Programs
Я сожалею о длинных именах, но я зависим от этого!
Night2
1
Может быть, это признак хорошего бота ... мои тесты показывают, что он на 5-м месте
Redwolf Programs
5

SniperBot

Этот бот будет эффективен, только если кто-то начнет добавлять ботов, которые на самом деле регулярно атакуют. SmartFarmer - мое текущее оптимизированное решение

  1. лечит, если может получить один выстрел
  2. лечит, если до 30
  3. атакует бота, если может отхватить его и заработать больше денег, чем фермерство
  4. модернизирует фермерство, если может себе позволить
  5. улучшает лечение, если у него меньше 80 единиц здоровья и он может себе позволить
  6. фермы

стервятники не нуждаются в нападении

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
источник
Неожиданный идентификатор ( int) в строке 2. ReferenceError: здоровье не определено.
Draco18s
Должно быть me.hp?
mbomb007
извиняюсь. плохо знакомый с javascript. спасибо за помощь
B0RDERS
Вы if(me.hp <30 && ...)могли бы быть упрощены до первого пункта из-за необходимости абсурдного уровня исцеления, чтобы это имело значение (65 уровень)
Веска
@Veskah Спасибо за указание на это. Это был остаток от того, когда min hp был выше
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 BullyBot и некоторые другие биты. У Оптимиста был короткий и приятный оппортунистический кусок атаки, который я обнаружил (хотя другие боты делают аналогичные вычисления).

Вместо того, чтобы запугивать цель, оглушая их, она убивает их за их сладкую, сладкую добычу. Он также предназначается для самых слабых в стаде для запугивания, но он сдастся и просто пойдет на ферму, если HP самой слабой цели слишком высок.

Draco18s
источник
Вы забиваете себя до смерти. Примите мои правки :)
B0RDERS
1
@ AndrewBorders Ха, даже не думал об этом. Спасибо.
Draco18s
Этот бот был великолепен, пока не появились те защитные боты.
B0RDERS
@ B0RDERS Щит очень сильный, даже если он тратит время.
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);
}

В основном оскорбительный бот. Чрезвычайно расстроен фактом, что он не может по-настоящему FizzBuzz, поэтому он просто гудит вместо этого. Когда это не Fizzing или Buzzing, он теряет силу у другого бота на 30 ходов, сдается и выбирает другого бота, если он не прогрессирует.

Выполняет чрезвычайно непоследовательно. Не берите в голову, обновленный контроллер, теперь, кажется, всегда в середине пакета.

Преобразование Фурье Рина
источник
Мне нравится эта концепция. Независимо от текущей ситуации, он продолжает двигаться вперед в своем собственном темпе.
Несс
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);
}

Попробуйте онлайн!

Может не победить, но, безусловно, постарается сделать все возможное, чтобы убедиться, что его цель тоже не победит. BullyBot также работает на первом ходу, так что, если нет внешнего влияния, он побьет свою цель 5-0 или свяжет их 5-5.

Veskah
источник
5

JustFarm

Я думал, что начну с простого.

function justFarm(me, others){
    return farm();
}
анонимное
источник
13
Этот бот совершит самоубийство из-за 2HP стоимости фермерства.
Draco18s
@ Draco18s Хотя раунд может закончиться до этого, в зависимости от количества ботов
Redwolf Programs
1
Хотя это технически верно, таймер 50 раундов очень короток, когда время окончания по умолчанию равно 1000.
Draco18s
Он побил два примера ботов, но теперь есть еще несколько представлений, которые я мог бы попытаться придумать что-нибудь получше.
Аноним
@ Аноним Может быть, в том числе улучшения лечения и сельского хозяйства будет достаточно. Поскольку получение наибольшего количества золота является конечной целью, сохранить его в качестве основной работы бота может сработать. До сих пор не было ботов, у которых есть «режимы», такие как режим лечения и режим фермы, может быть интересным подходом
Redwolf Programs
4

ScavengerBot (V2)

Понял, что это не было большой мусорщик раньше. Новая стратегия - ждать, пока он не сможет убить другого бота. Если никто не может быть убит, он сидит и строит щит.

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
источник
1
me.levels.attacl?
Draco18s
Хороший улов, исправлено
reffu
4

капризный

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 по умолчанию состоит в том, чтобы на некоторое время улучшить фермерство и лечение, а затем убить других ботов в порядке убывания их стоимости. Однако, если он подвергнется нападению, он испугается и немного сосредоточится на лечении. Если он атакует и «терпит неудачу», потому что жертва лечила или защищала более эффективно, чем атака, она разозлится и уйдет, чтобы улучшить свои атакующие способности.

histocrat
источник
4

разбойник

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

Первая попытка подкрепления учебного бота. На данный момент чисто оборонительный, чтобы сузить пространство поиска. Что-то вроде более умного побочного продукта FizzBuzz - он повторяет определенную серию из пяти действий снова и снова; пять действий - это то, что выбирает RL.

Но пока это в основном основано на перечислении - я просто сгенерировал все 3 ^ 5 = 243 перестановок серии из пяти защитных действий, которые повторялись снова и снова, и сохранял их средние оценки (разделенные на 200, чтобы получить средний выигрыш за пять ходов) более 100 итераций в storage.priorмассиве. Затем, во время игры, он реализует эпсилон-жадный подход к обновлению этих списков очков, чтобы он был более ориентирован на будущее. (Также потому, что использование epsilon = 0.3 работало лучше, чем epsilon = 0.1, поэтому я просто сохранил его.)

Это нормально, последовательно размещая между scavengerBot и Optimist. В настоящее время я провожу еще несколько занятий по реальным играм и ищу лучшие способы сформулировать стратегию, чтобы посмотреть, смогу ли я ее улучшить.

Преобразование Фурье Рина
источник
4

Оппортунист

Этот заимствует немного из нескольких других (особенно ScavengerBot (V2) и Unkillable), так как у них были те же идеи, которые я имел в виду, но я обычно люблю стили округлости и мастерства на все руки больше, чем фокусируюсь только на одна или две вещи. Это, вероятно, будет означать, что я не выиграю, но это должно быть где-то посередине (что случается со мной в большинстве случаев во многих случаях).

Так он крадет сочные убийства; лечит, если необходимо; модернизирует ферму, атакует и лечит в этом порядке; и фермы в противном случае.

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
источник
1
Второй аргумент должен бытьothers
SuperStormer
4

ScaredBot

  1. Находит других ботов:
    • с самой высокой атакой
    • с наибольшим богатством и HP ниже, чем собственная атака
  2. Если его HP + щит ниже, чем найденный highest attack * (25% of bots), или он приближается к нижнему краю HP + shield, то он экранирует
  3. Если он нашел бота с более низким щитом, чем его собственная атака, он атакует его.
  4. Если его здоровье < 50, оно лечит.
  5. Если он может улучшить любой из щита, исцеления и фермы, он улучшит тот, у кого самый низкий уровень
  6. Это фермы
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();
    }
}

Идея состоит в том, чтобы остаться в живых как можно дольше и в противном случае попытаться получить золото безопасным и дешевым способом, чтобы иметь возможность апгрейда.

Приоритеты обновления, вероятно, должны быть изменены, а также условия при определении того, стоит ли защищать.

user24343
источник
3

SmartFarmer

Фермы, апгрейды, лечит, если здоровье слабое. Сельское хозяйство казалось подавленным, пока не прибыли действительно оскорбительные боты. Теперь мой бот погибает :-(

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
источник
1
Я (вручную) тестировал, по сути, ту же стратегию, чтобы увидеть, какова максимальная доступная стоимость и лучшие цифры, которые я мог получить, - это с небольшой задержкой при улучшении лечения (я использовал золото> = стоимость * 2) и поднялся до лечения на 10 лвл. ,
Николай
Этот множитель цены - хорошая идея. Я добавил что-то подобное. Мне было бы интересно посмотреть, какие цифры вы получили
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];
}

Каждый ход сравнивает амортизированную прибыль от убийства каждого бота с доходом от фермерства и лечения и выбирает лучший вариант. На самом деле он должен использовать состояние, чтобы выяснить, сколько времени потребуется, чтобы убить бота, но сейчас он просто предполагает, что каждый бот лечит или защищает в среднем 5 пунктов за ход, за вычетом урона, наносимого другими ботами.

histocrat
источник
3

Дружелюбный бот

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это hp + shieldвместо hp...

ТТГ
источник
4
Может ли кто-нибудь помочь мне перевести название функции на английский? ^ _ ^
TSH
4
Согласно Google Translate, «闷声 发大财» означает «Приглушенный». Я уверен, что это не то, что вам нужно, и на самом деле это очередная эпическая ошибка Google Translate ... Я искал дальше, и во всех результатах, похоже, упоминается, что здесь нет единственного английского слова, которое можно использовать здесь, так что, возможно, было бы лучше сохранить его как на самом деле это, как кажется, предлог Китая, который обычно означает, что нужно работать тихо, и пусть результаты говорят сами за себя и достигают традиционной философии. К сожалению, я совсем не знаю китайский язык, чтобы перевести его напрямую. : D
Эрик Outgolfer
1
как носитель китайского языка, это означает что-то вроде «тихо заработать огромное состояние»: v 闷声 также означает намеренно молчать, буквально «скрывая звук»
преобразование Фурье Рина
1
Sneaky? В зоне покрытия радара? DontMindMe? AttentionDeflector?
Питер Тейлор
3

Бухгалтер

Этот практичный бот рассчитывает наиболее экономически выгодный ход, но ему нравится держать свой профиль атаки на низком уровне, чтобы избежать проблем со всеми ботами бдительности. Он не пытается помочь беззащитным или охотиться на них. Скорее он делает то, что ему помогает больше всего.

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
источник
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();
}

Вот вещь Там стало действительно опасно. Сельское хозяйство вообще увеличивает вашу ценность, делая вас целью. Так что на самом деле не безопасно работать на ферме, пока вы не построите огромный щит и все насилие не утихнет. Тогда вы можете высунуть голову из раковины и начать заниматься сельским хозяйством. Или помощь в убийстве. Все, что платит лучше.

histocrat
источник
2

блюститель

У меня может быть более одного представления, верно?

Вилка CampBot. Не защищает, вместо этого сосредотачиваясь на нападении. Показывает предпочтение атаковать игроков с более высокими характеристиками атаки, а не наносить удары случайным образом, как CampBot. Ориентирован на улучшение своего сельского хозяйства, а не лечения.

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()
    }
}
анонимное
источник
мой бот случайно не атакует ...
SuperStormer
Вы можете отправлять столько раз, сколько захотите, тем больше я полагаю, тем лучше
Redwolf Programs
@SuperStormer Я понимаю, что ваш не совсем случайно, но:let victim=potentialVictims[Math.floor(Math.random()*potentialVictims.length)]
Аноним
но сначала он отфильтровывает тех, на которых не стоит нападать
SuperStormer
Я работал над похожим ботом под названием эквалайзер, когда вы это опубликовали. Я все еще настраиваю его, но мне нравятся некоторые ваши идеи.
B0RDERS
2

Rando

Этот глупый парень выберет действия, основанные на равномерной случайности с некоторыми уклонами. Если случайно выбранное действие не сработает, оно перейдет к следующему выбору.

Поэтому в среднем он должен атаковать почти 2/9 времени и фармить почти 3/9 времени. Остальные имеют шанс 1/9, если он может улучшиться, или если лечение или защита того стоят и т. Д.

Он вряд ли будет хорошо выступать, но, по крайней мере, есть небольшой шанс, что он будет верховным. И в этом вся цель Рэндо. Ему просто нужно верить в себя! Все варианты выложены перед ним. Ему нужно только выбрать то, что нужно для той или иной ситуации.

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

(Я знаю, что по умолчанию нет необходимости, но я думаю, что это хорошая практика кодирования для надежного кода.)

Ness
источник
2
«Ему просто нужно верить в себя» ... Я сейчас так много смеюсь
Redwolf Programs
2

Убить бот

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

Простой бот, Kill Bot просто хочет убить своих врагов. Поскольку защита намного эффективнее лечения (особенно при выравнивании), Kill Bot просто пытается всегда быть непривлекательной целью, экранируя себя всякий раз, когда на него нападают. Kill Bot отлично справляется с ситуацией среди слабых, пацифистских ботов (вы можете почувствовать его презрение к ним).

Spitemaster
источник
3
Обратите внимание, что o.attackэто уровень атаки, а не ее урон
Redwolf Programs
2

FarmHeal Bot

Создана из анонимного бота JustFarm

function farmhealBot(me, others, storage) {
  if (me.hp <= 95)
    return heal();
  else return farm();
}
maninthecomputer
источник
2

нерушимый

Модификация бота Draco18 с использованием щитов (более эффективна против других ботов)

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 Программы
источник