Золота битва KoTH


43

Цей виклик закінчився. Щоб побачити остаточні результати змагань, натисніть тут

У цьому виклику кожне подання - один бот. Кожен бот повинен мати функцію Javascript. Боти боротимуться за отримання найвищої загальної вартості золота. Золото можна вирощувати на фермах або заробляти від вбивства інших ботів, і воно використовується для покращення зцілення, нападу, охорони та ведення господарства.

Мета:

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

Виходить:

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

  • Зцілення: відновлює HP
  • Attack: видаляє HP з іншого бота
  • Щит: захищає від пізніших атак
  • Приголомшення: пропускає черговий черговий бот
  • Ферма: Заробляє золото вартістю HP
  • Оновлення: зробіть певні кроки краще

Усі боти повернуть свій хід до того, як будь-який буде виконаний, тому оглушення, загоєння, атака, щит тощо не впливатимуть на жодних ботів, що рухаються пізніше на цьому кроці. Наприклад, якщо Bot A приголомшує Bot B, а Bot B після Bot A у черговому порядку, Bot B все одно буде рухатися пізніше в тому ж повороті, і оглушення відбудеться на наступному кроці.

Боротьба, землеробство та оновлення:

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

Зцілення - це один із найпростіших кроків, додаючи кількість HP, що визначається її рівнем (починається з 5 HP). Бот не може вилікувати останні 100 к.с.

Атака бота за допомогою його 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)
  • Боти повинні бути достатньо відмінними від будь-яких інших ботів, що їх можна обгрунтовано вважати окремими записами
  • Зараз командування заборонено
  • Контролер можна знайти тут
  • Чат-кімната

Нова налагодження контролера:

Використовуючи файл gold-battle-log.js, ви можете встановити значення debugвластивості бота в botData0 (без реєстрації), 1 (переміщення журналу) або 2 (ходи журналу, hp, золото, рівні тощо)

Виклик закінчується о 17:00 UTC у п’ятницю, 9 серпня


4
Створив суть із усіма ботами. gist.github.com/Draco18s/2efbf95edcf98d6b1f264e26bbb669d1 Я намагатимусь його оновлювати (але якщо це не гідний початок).
Draco18s

4
Автоматичне оновлення контролера з включеними ботами: redwolfprograms.com/koth
Programs

4
Я голосую, щоб закрити це питання, оскільки воно вже фактично закрите для нових відповідей ("Цей виклик закінчився. Щоб побачити остаточні результати ...")
pppery

3
@pppery Не могли б ви? Мені б добре було відповідати неконкурентоспроможні відповіді, і, [closed]в кінці кінців, швидше за все, випадкові глядачі можуть пропустити читання мого виклику, оскільки вони будуть вважати, що це низька якість або поза темою.
Програми Redwolf

5
@pppery Я ніколи не чув, щоб закриття виклику було закінчено до сьогодні, і я б стверджував, що соціальне обмеження, яке ви хочете застосувати, навіть не існує. Не потрібно його закривати, і я не хочу, щоб він був закритим. Мені це здається як закриття заради закриття, а не заради сайту. Якщо хтось хоче опублікувати відповідь на старе запитання, він повинен мати можливість. Немає жодної записки після того, як правило серйозного претендента говорить, що він повинен бути серйозним претендентом, коли він розміщений; відповідь все ще може бути серйозним претендентом на виклик, навіть якщо це не претендент на перемогу
Redwolf Programs

Відповіді:


16

Неможливий

роздвоєний від Undayable .

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

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


Абсолютно розбиває конкуренцію в моїх тестах
програми Redwolf

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

Тепер не тільки краде вбиває, але й краде вбивства!

Цей бот не робить багато, крім ферми, і коли помічає можливість, приєднується до виконання останнього удару помираючому ворогу, і якось вдається бути дуже хорошим.


Це працює тому, що всі боти, які беруть участь у вбивстві, отримують повну винагороду.
Draco18s

@ Draco18s Я розумію, чому це може бути добре, я просто не очікував такої простої ідеї отримати в середньому 2 рази бал наступного найкращого бота (на момент його створення).
dzaima

Хе-х, це справедливо. Мені доведеться завантажити всі боти, коли зможу і побачити, чи зможу я знайти інше рішення.
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();
}

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% свого часу в мирному господарстві, тому він починається з максимізації сільського господарства, і лише після цього починає приділяти будь-яку увагу своїм бойовим навичкам. Напевно нічого не піде!


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

Зачекайте, у якому світі Капітан Фермер отримує золото 14 тис?
Програми Redwolf

Цей:runGame(1) results: [...] captFarmer: 13768
Draco18s

Це досить несподівано високо ... у моїх тестах це зазвичай близько 10
тис.

* shrugh * Ніякої ідеї. Я зроблю автоматичне оновлення суті просто для того, щоб забезпечити все чисто.
Draco18s

Мій улюблений бот до кінця терміну.
Ніч2

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

}

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

Як випливає з назви, цей бот є нейтральним, здебільшого нейтральним (тепер він допомагає вбивати ботів, які помруть) і просто ферми і лікує, повільно нарощуючи своє золото ( як Швейцарія )


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: Зменшення оновлення екрана.


2
Як тільки я побачив ім'я, я зрозумів, що це буде ваш бот (:
Redwolf Programs

Мені шкода довгих імен, але я захоплююся цим!
Ніч2

1
Можливо, це знак хорошого бота ... мої тести показують, що він знаходиться на 5 місці
програми Redwolf

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

Несподіваний ідентифікатор ( int) у рядку 2. ReferenceError: здоров'я не визначено.
Draco18s

Повинно бути me.hp?
mbomb007

вибачте. новий для JavaScript. дякую за допомогу
B0RDERS

Вас if(me.hp <30 && ...)можна спростити лише до першого пункту, оскільки для того, щоб мати значення, необхідний абсурдний рівень зцілення,
65

@Veskah Дякую, що вказали на це. Це був залишок від того, коли
мінусовий

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 найслабшої цілі занадто високий.


ти землеробствуєш до смерті. Прийміть мою
редакцію

1
@AndrewBorders Ha, навіть не думав про це. Дякую.
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

хуліган

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

Спробуйте в Інтернеті!

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


5

JustFarm

Я думав, що почну з простого.

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

13
Цей бот покінчить життя самогубством через 2 х.с. господарства.
Draco18s

@ Draco18s Хоча раунд може закінчитися до цього, залежно від кількості ботів
Redwolf Programs

1
Хоча технічно це правда, 50-ти
крутний

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

@ Anonymous Можливо, включаючи оздоровлення та оновлення сільського господарства буде достатньо. Оскільки отримати найбільше золота - це кінцева мета, зберегти це як головна робота бота може працювати. До цих пір не було жодних ботів, які мали б такі режими, як режим зцілення та режим ферми, може бути цікавим підходом
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();
}

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


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 - він повторює певну серію з п’яти дій знову і знову; п'ять дій - це те, що обирає РЛ.

Але це здебільшого базується на перерахунку - я просто генерував усі 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();
    }
}

1
2-й аргумент повинен бути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();
    }
}

Ідея полягає в тому, щоб якомога довше залишатися в живих і в іншому випадку спробувати отримати золото безпечним і дешевим способом, щоб мати можливість оновити.

Мабуть, слід змінити пріоритети щодо модернізації, а також умова при визначенні того, чи слід захищати.


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

1
Я (вручну) випробовував по суті ту саму стратегію, щоб побачити, яка максимально можлива ціна та найкращі цифри, які я міг отримати, - трохи затримавшись при оновленні (я використав золото> = вартість * 2) і підійшов до lvl10 зцілити .
Миколай

Цей мультиплікатор цін є хорошою ідеєю. Я додав щось подібне. Мені було б цікаво подивитися, які цифри ви отримали
B0RDERS

3

Морт

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 балів, за вирахуванням шкоди, яку інші боти роблять.


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
Ерік Аутгольфер

1
як носія китайської мови, це означає щось на кшталт "мовчки зробити величезний стан": v 闷声 також кономентує навмисне мовчати, буквально "приховуючи звук"
перетворення Фур'є Ріна

1
Підлий? UnderTheRadar? DontMindMe? УвагаДефлектор?
Пітер Тейлор

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

3

дійсноЗапущена черепашка

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

Ось річ. Це стає дійсно небезпечним там. Сільське господарство взагалі збільшує вашу цінність, роблячи вас мішенню. Тож справді не безпечно вести господарство, поки ви не збудуєте величезний щит, і все насильство не вщухло. Тоді ви можете висунути голову зі своєї шкаралупи і розпочати господарство. Або вбивство. Що б краще платити.


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

@SuperStormer Я розумію, що твій не зовсім випадковий, але:let victim=potentialVictims[Math.floor(Math.random()*potentialVictims.length)]
Анонім

але спочатку фільтруються ті, на кого не варто нападати
SuperStormer

Я працював над аналогічним ботом на ім'я еквалайзера, коли ви публікували це. Я все ще добре його налаштовую, але мені подобаються деякі ваші ідеї.
B0RDERS

2

Рандо

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

Тому в середньому він повинен атакувати майже 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
    }
}

(Я знаю, що "за замовчуванням" зайвий, але я думаю, що це хороша практика кодування для надійного коду.)


2
"Йому просто потрібно вірити в себе" ... Я зараз так
сміюся

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 досить добре опиняється серед слабких, пацифістичних ботів тут (ви можете відчути їх презирство).


3
Зауважте, що o.attackце рівень атаки, а не його збиток
Redwolf Programs


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();
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.