Аукціон доларових купюр


32

Це KOTH виклик для гри на аукціоні в доларовій грі на аукціоні . У ньому долар продається найвищому торгу. Ставки збільшуються з кроком у 5 ¢, а програв також сплачує свою ставку. Ідея полягає в тому, що обидва гравці посилюють торги, що значно перевищують вартість долара, щоб зменшити свої втрати.

Будемо сподіватися, що ваші боти розумніші від цього.

Ви створите бота для гри в цю гру, розширивши net.ramenchef.dollarauction.DollarBidderклас. Ви повинні реалізувати nextBidметод, який повертає наступну ставку вашого бота, враховуючи попередню ставку іншого бота. При необхідності ви також можете скористатися newAuctionметодом скидання для кожного аукціону з класом бота противника.

public abstract class DollarBidder {
    /**
     * Used by the runner to keep track of scores.
     */
    long score = 0;

    /**
     * (Optional) Prepare for the next auction.
     *
     * @param opponent The class of the opponent's bot.
     */
    public void newAuction(Class<? extends DollarBidder> opponent) {}

    /**
     * Bid on the dollar. Bidding ends if the bid is
     * not enough to top the previous bid or both bids
     * exceed $100.
     *
     * @param opponentsBid How much money, in cents,
     *  that the opponent bid in the previous round. If
     *  this is the first round in the auction, it will
     *  be 0.
     * @return How much money to bid in this round, in
     *  cents.
     */
    public abstract int nextBid(int opponentsBid);
}

Проведення ставок відбувається до тих пір, поки не відбудеться одне з наступних дій:

  • nextBidкидає виняток. Якщо це станеться, бот, який кинув виняток, сплачує попередню ставку, а інший бот отримує долар безкоштовно.
  • Будь-який бот не заплатив достатньо, щоб перевищити попередню ставку. Якщо це станеться, обидва боти платять свої ставки (програв платить попередню ставку), і переможець отримує долар.
  • Обидва бота ставлять понад 100 доларів. Якщо це станеться, обидва боти платять 100 доларів, і жоден бот не отримує долар.

Проводяться 2 аукціони для кожної комбінації ботів. Боти оцінюються за загальним прибутком, отриманим на цих аукціонах. Виграє найвищий бал.

Приклади

GreedyBot

import net.ramenchef.dollarauction.DollarBidder;

public class GreedyBot extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        return opponentsBid + 5;
    }
}

OnlyWinningMove

import net.ramenchef.dollarauction.DollarBidder;

public class OnlyWinningMove extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        return 0;
    }
}

AnalystBot

Не використовуйте це як шаблон для аналітично налаштованих ботів; використовувати ImprovedAnalystBotзамість цього.

import net.ramenchef.dollarauction.DollarBidder;

// yes, this is a poor implementation, but I'm not
// going to waste my time perfecting it
public class AnalystBot extends DollarBidder {
    private DollarBidder enemy;

    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        try {
            enemy = opponent.newInstance();
            enemy.newAuction(this.getClass());
        } catch (ReflectiveOperationException e) {
            enemy = null;
        }
    }

    @Override
    public int nextBid(int opponentsBid) {
        if (enemy == null)
            return 0;

        return enemy.nextBid(95) >= 100 ? 0 : 95;
    }
}

AnalystKiller

import net.ramenchef.dollarauction.DollarBidder;

public class AnalystKiller extends DollarBidder {
    private static int instances = 0;
    private final boolean tainted;

    public AnalystKiller() {
        this.tainted = instances++ != 0;
    }

    @Override
    public int nextBid(int opponentsBid) {
        if (tainted)
            throw new RuntimeException("A mysterious error occurred! >:)");

        return 0;
    }
}

Додаткові правила

  • Стандартні лазівки заборонені.
  • Саботаж інших ботів дозволений, але спроба змінити видимість поля / методу призведе до загадкових SecurityExceptions. Виняток викликає інший бот, щоб пробити межу в 500 мс.
  • Боти не можуть отримати доступ до пакету бігуна, крім розширення DollarBidderкласу.
  • Усі методи повинні повернутися за 500 мс або менше.
  • Ботам не потрібно бути детермінованими.
  • Ваша ставка не повинна бути кратною 5 ¢.
  • $ 1 = 100 ¢
  • Результати будуть розміщені 24 квітня 2018 року.

Бігун на GitHub

Результати

Подивіться окремі тури тут.

MTargetedBot: $14.30
BuzzardBot: $9.83
BluffBot: $9.40
RiskRewardBot: $9.35
SecretBot: $8.50
LuckyDiceBot: $7.28
CounterBot: $6.05
MBot: $5.40
StackTraceObfuscaterBot: $5.20
EvilBot: $4.80
MarginalBot: $4.60
TargetValueBot: $4.59
InflationBot: $4.27
UpTo200: $4.20
InsiderTradingBot: $1.90
MimicBot: $1.50
BorkBorkBot: $1.22
DeterrentBot: $0.95
MarginalerBot: $0.00
RandBot: $-4.45
BreakEvenAsap: $-7.00
AnalystOptimizer: $-13.95
DeterredBot: $-1997.06
ScoreOverflowBot: $-21474844.15
MirrorBot: $-21475836.25

Вітаємо MTargetedBotз прибутком 14,30 доларів!


11
Цей виклик є принципово вразливим для «Однорядних». Оскільки я знаю клас свого опонента, легко вибрати найкращу стратегію проти нього. (Тоді хтось підходить і може підняти мого бота тощо)
Натан Меррілл

2
" Ставки збільшуються з кроком у 5 ¢ ". У вас немає нічого в коді, щоб підтвердити це, LuckyDiceBotнаприклад ... наприклад, ставки з кроком 2-12випадковим чином ..
Кевін Кройсейсен

4
Також: що робити, якщо мій бот змушує інших ботів перевищувати обмеження 500ms?
Натан Меррілл

4
@RamenChef Тут йдеться про шкідливий код. Що робити, якщо я виявляю, коли інший бот дзвонить мені, і дзвоню Thread.sleep (1000)?
Натан Меррілл

3
Я VTC це, оскільки незрозуміло, що допускається диверсія, а що ні. ОП забороняє подання про те, що "нападає на бігуна" (що є невиразним), і немає чіткої межі між шкідливим кодом, який дозволений, і шкідливим кодом, який немає (Як визначити, який бот спричинив бота зайняти занадто довго ?)
Натан Меррілл

Відповіді:


2

MTarratedBot

public class MTargetedBot extends MBot {

    @Override
    protected int calcBid(int opponentsBid, boolean isPeeking, boolean isSubPeeking) {
        Class c = this.rivalClass;

        switch (c.getSimpleName()) {
            case "AnalystBot":
                if (isPeeking && !isSubPeeking) {
                    throw new RuntimeException();
                } else if (isPeeking) {
                    return 66666;
                }
                break;
            case "MirrorBot":
                if (isPeeking && !isSubPeeking) {
                    throw new RuntimeException();
                } else if (isPeeking) {
                    return 0;
                }
                break;
            case "GreedyBot":
            case "LuckyDiceBot":
            case "InflationBot":
            case "TargetValueBot":
                // not playing with ya
                return 0;
            case "MimicBot":
            case "BuzzardBot":
            case "MarginalBot":
            case "MarginalerBot":
            case "BluffBot":
            case "MBot":
                // go away, gimme easy money
                return isPeeking ? 66666 : 5;
            case "RandBot":
                // me or noone
                return 100;
            case "SecretBot":
                return 10;
            case "AnalystKiller":
            case "OnlyWinningMove":
            case "EvilBot":
            case "StackTraceObfuscaterBot":
                // easy
                return opponentsBid + 5;
        }

        return super.calcBid(opponentsBid, isPeeking, isSubPeeking);
    }
}
  • На основі оновленого MBot
  • Використовується аналогічний метод, як CounterBot, але з деякими методами, які вдосконалюються, щоб важче вразити деяких опонентів, також повинен бути більш читабельним
  • На невідомому противнику за замовчуванням MBot strat

1
Це не справедливо.
Джошуа

@Joshua Що на ваш погляд особливо не чесно?
молоко

Знаючи імена своїх опонентів.
Джошуа

@Joshua половина рішень використовує цю інформацію. Ми навіть писали авторові, що це має бути змінено, або відбудеться Одноповерховий, він відмовився змінити виклик - так ось це
молоко

1
Уже зробив ....
Джошуа

15

MimicBot

import net.ramenchef.dollarauction.DollarBidder;

import java.util.Set;
import java.util.HashSet;

public class MimicBot extends AbstractAnalystCounterBot {

    private final Set<Class<? extends DollarBidder>> bidders = new HashSet<>();
    private DollarBidder reference = null;

    // A benchmark class. Not MarginalBot because of proposed rule changes.
    public static class BidFive extends DollarBidder {
        public int nextBid(int o) {
            return 5;
        }
    }


    public MimicBot() {
        bidders.add(OnlyWinningMove.class);
        bidders.add(GreedyBot.class);
        bidders.add(BidFive.class);
    }


    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        DollarBidder enemy;
        reference = null;
        try {
            enemy = opponent.newInstance();
        } catch (Throwable t) {
            return;
        }

        if (!bidders.contains(opponent))
            bidders.add(opponent);

        Class<? extends DollarBidder> leader = OnlyWinningMove.class;
        int best = 0;

        for (Class<? extends DollarBidder> audition : bidders) {
            try {
                enemy.newAuction(MimicBot.class);
            } catch (Throwable t) {
                reference = new GreedyBot(); // Deterrence.
                break;
            }

            DollarBidder tryout;
            try {
                tryout = audition.newInstance();
                tryout.newAuction(opponent);
            } catch (Throwable t) {
                continue;
            }

            int tryoutScore = -100000;
            /* This code was copy-pasted from the *
             * runner, with significant changes. */
            int bid1 = 0, bid2 = 0;
            while (true) {
                int next;
                try {
                    next = enemy.nextBid(bid2);
                } catch (Throwable t) {
                    tryoutScore = 100;
                    break;
                }
                if (next < bid2 + 5) {
                    if (bid2 > 0) {
                        tryoutScore = 100 - bid1;
                    }
                    break;
                }
                if (next > 10000 && bid2 > 10000) {
                    tryoutScore = -10000;
                    break;
                }
                bid1 = next;

                try {
                    next = tryout.nextBid(bid1);
                } catch (Throwable t) {
                    tryoutScore = -bid2;
                    break;
                }
                if (next < bid1 + 5) {
                    tryoutScore = -bid2;
                    break;
                }
                if (next > 10000 && bid1 > 10000) {
                    tryoutScore = -10000;
                    break;
                }
                bid2 = next;
            }
            /* End of copy-pasted code. */

            if (tryoutScore > best) {
                best = tryoutScore;
                leader = audition;
            }
        }

        try {
            reference = leader.newInstance();
        } catch (Throwable t) {
            reference = new OnlyWinningMove();
        }
        reference.newAuction(opponent);
    }


    @Override
    public int nextBid(int opponentsBid) {
        try {
            return reference.nextBid(opponentsBid);
        } catch (Throwable t) {
            return 5;
        }
    }
}

Свята корова. Я очікував, що це буде просто написати, потім згодом витратив на це 3 години.

По суті, MimicBotзберігає поточний список доступних ботів. Переходячи до нового аукціону, він проходить список у пошуках найефективнішого проти поточного опонента. Потім він використовує цей бот як "посилання" на аукціоні.

Для цілей тестування найкраще використовувати або рандомізований підмножина поданих матеріалів, або повний набір. Він починається з GreedyBot, MimicBotі ще одного бота, який пропонує лише 5 ¢.


11

InsiderTradingBot

У дусі відповіді @ StephenLeppik InsiderTradingBot знає всіх своїх опонентів і розуміє їхні стратегії. Ваш хід, Стівен.

import net.ramenchef.dollarauction.DollarBidder;

public class InsiderTradingBot extends DollarBidder {
  private static boolean analystNutcracker = false;
  private int bid;

  @Override
  public void newAuction(Class<? extends DollarBidder> opponent) {
    if (opponent.equals(DeterredBot.class) ||
        opponent.equals(OnlyWinningMove.class) ||
        opponent.equals(MirrorBot.class)) {
      // I can do this ^.^
      bid = 5;
    } else if (opponent.equals(AnalystKiller.class)) {
      // Outbid 'em >:D
      bid = 10;
    } else if (opponent.equals(BreakEvenAsap.class) ||
               opponent.equals(BorkBorkBot.class) ||
               opponent.equals(DeterrentBot.class)) {
      // Break even quicker!
      bid = 100;
    } else if (opponent.equals(InsiderTradingBot.class)) {
      // I'm probably a simulation inside MirrorBot
      bid = 0;
    } else if (opponent.equals(Analyst.class)) {
      // Let's fight the Analyst with the power of global variables
      bid = 100;
      analystNutcracker = true;
    } else {
      // Welp
      bid = 0;
    }
  }

  @Override
  public int nextBid(int opponentsBid) {
    if ((opponentsBid == 95) && analystNutcracker) {
      analystNutcracker = false;
      return 0;
    }
    return bid;
  }

};

1
Так, інсайдерська торгівля була б, якщо RichJerkбот зробив конкретний виняток для вашого бота і запропонував $ 0 за це.
Нісса

Занадто рано бути оптимізацією щодо інших відповідей. Також, це AnalystBot, ні Analyst.
RamenChef

8
Напевно, має бути правило "назви класів будуть рандомізовані".
користувач202729

1
@ user202729 Як щодо "немає прямих посилань на класи"?
RamenChef

1
Я хотів би бачити цю ручку MimicBot.
Нісса

8

MirrorBot

Змушує ворога грати проти себе.

import net.ramenchef.dollarauction.DollarBidder;

public class MirrorBot extends DollarBidder{

    private DollarBidder enemy;

    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        try {
            enemy = opponent.newInstance();
            enemy.newAuction(this.getClass());
        } catch (ReflectiveOperationException e) {
            enemy = null;
        }
    }

    @Override
    public int nextBid(int opponentsBid){
        if (enemy == null)
            return (opponentsBid >= 95) ? 0 : (opponentsBid + 5);
        try {
            return enemy.nextBid(opponentsBid);
        } catch (Throwable e) {
            System.out.println("haha no");
            return (opponentsBid >= 95) ? 0 : (opponentsBid + 5);
        }
    }
}

6
Ви Analystзахоплююче виграли .
Сільвіо Майоло

@SilvioMayolo Як?
dkudriavtsev

Дзеркало намагається наслідувати аналітику, що грає проти себе, що призводить до переповнення стека.
Сільвіо Майоло

8

Редагувати : цільові зміни в класі DollarBidder зламали цього бота.

ScoreOverflowBot

import net.ramenchef.dollarauction.DollarBidder;

public class ScoreOverflowBot extends DollarBidder {
  boolean betBig = true;

  @Override
  public int nextBid(int opponentsBid) {
    if(betBig)
    {
      betBig = false;
      return 2147483645;
    }
    else
      return 105;
  }
}

Після 1 аукціону його бал становитиме -2147483645, але наступного разу він втратить 5 ¢ або 105 ¢, зробивши рахунок позитивним і дуже великим. Усі інші втрати були б тоді незначними.

На першому аукціоні це також зробить ставку GreedyBot -2147483646, яка не ділиться на 5.


scoreзахищений пакетом. Ваші боти не можуть отримати доступ до нього.
RamenChef

@RamenChef На жаль, зняв CheatingBot
зима

Не існує правила проти "нападу на бігуна", лише "доступу до нього", що це не робить. Рекомендую виправити помилку, яка вирішує проблему :)
Натан Меррілл

7

TargetValueBot

import java.util.Random;
import net.ramenchef.dollarauction.DollarBidder;

public class TargetValueBot extends DollarBidder {
    private int target;

    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        Random rand = new Random();
        target = 100;
        for (int i = 0; i < 20; i++) {
            target += rand.nextInt(2) * 10 - 5;
        }
    }

    @Override
    public int nextBid(int opponentsBid) {
        if (opponentsBid >= target) {
            return 0;
        } else {
            return opponentsBid + 5;
        }
    }
}

Наразі це неможливо перевірити, тому, будь ласка, повідомте мене, якщо воно порушено.

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


7

MarginalBot

import net.ramenchef.dollarauction.DollarBidder;

public class MarginalBot extends DollarBidder {
    private DollarBidder rival;

    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        try {
            rival = opponent.newInstance();
            rival.newAuction(this.getClass());
        } catch (Throwable t) {
            try {
                rival = opponent.newInstance();
                rival.newAuction(null);
            } catch (Throwable h) {
                rival = null;
            }
        }
    }

    @Override
    public int nextBid(int opponentsBid) {
        if (opponentsBid == 0) {
            try {
                if (rival.nextBid(5) < 10) {
                    return 5;
                }
            } catch (Throwable t) {
                //do nothing.
            }
        }
        return 0;
    }
}

Дуже просто, він намагається визначити, чи би опонент оспорював мінімальну ставку, а якщо ні, то розміщує її.

MarginalerBot

import net.ramenchef.dollarauction.DollarBidder;

public class MarginalerBot extends DollarBidder {
    private DollarBidder rival;
    private int bidCount;

    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        bidCount = 0;

        try {
            rival = opponent.newInstance();
            rival.newAuction(this.getClass());
        } catch (Throwable t) {
            try {
                rival = opponent.newInstance();
                rival.newAuction(null);
            } catch (Throwable h) {
                rival = null;
            }
        }
    }

    @Override
    public int nextBid(int opponentsBid) {
        bidCount += 1;

        for (int iBid = opponentsBid + 5; iBid < 100; iBid = iBid + 5) {
            if (bidCount > 0) {
                break;
            }

            try {
                if (rival.nextBid(iBid) < iBid + 5) {
                    return iBid;
                }
            } catch (Throwable t) {
                //do nothing.
            }
        }
        return 0;
    }
}

Нова, розумніша версія MarginalBot, яка перевіряє, чи може вона зробити будь-який заробіток без конкуренції, а не просто сподіватися на перемогу з мінімумом.

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

Редагувати 1: Внесено невелику зміну до методу newAuction, щоб оптимізувати його відносно інших ботів типу аналізатора.

Редагування 2: Внесено зміни до MarginalerBot, щоб мінімізувати втрати від підступних або недетермінованих стратегій.


Ласкаво просимо до PPCG!
Мартін Ендер

1
Це просто, але він перемагає всіх інших ботів досить великим запасом!
RamenChef

6

BorkBorkBot

import net.ramenchef.dollarauction.DollarBidder;

public class BorkBorkBot extends DollarBidder{
  @Override
  public int nextBid(int opponentsBid){
    return (opponentsBid >= 95) ? 0 : (opponentsBid + 5);
  }
}

Поступається, якщо не може зламатись.


6

RandBot

import net.ramenchef.dollarauction.DollarBidder;
import java.util.concurrent.ThreadLocalRandom;

public class RandBot extends DollarBidder {

    @Override
    public int nextBid(int opponentsBid) {
        return ThreadLocalRandom.current().nextInt(21) * 5;
    }
}

Це треба було зробити.


" Ставки збільшуються з кроком у 5 ¢ ". Зараз ваш бот не робить цього.
Кевін Кройсейсен

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

6

DeterrentBot

import net.ramenchef.dollarauction.DollarBidder;

public class DeterrentBot extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        return opponentsBid > 5 ? 100 : opponentsBid + 5;
    }
}

Спроби переконати будь-яких аналітично налаштованих ботів, що єдиний виграшний хід - це не грати.


1
Я помітив, що мій дещо криптований коментар "Джошуа? Це ти?" було видалено. Тож для уточнення це була посилання на відому цитату з фільму WarGames: "єдиний виграшний хід - це не грати" . (Ісуса Навина прізвисько WOPR .)
Arnauld

5

LuckyDiceBot

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

import net.ramenchef.dollarauction.DollarBidder;
import java.util.Random;

public class LuckyDiceBot extends DollarBidder {
  private Random random;

  public LuckyDiceBot() {
    random = new Random();
  }

  @Override
  public int nextBid(int opponentsBid) {
    int d1 = random.nextInt(6) + 1;
    int d2 = random.nextInt(6) + 1;
    return opponentsBid + d1 + d2;
  }

};

2
Як це зменшує свої втрати чи зупиняє втрати? Якщо він завжди додає свою кістку на кістки до ставки опонентів, то ви завжди будете ставити більше. Випадковість може заплутати досить аналітичного бота, мені подобається концепція.
Фрайхейт

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

Дві речі: 1. @Freiheit має рацію, і цей бот продовжуватиме торги до тих пір, поки не виграє незалежно від висоти. opponentsBidв nextBid(int opponentsBid)має загальну ставку вашого противник ставка до сих пір, не чергова пропозиція. Кращим терміном для методу буде raise(як термін покеру) імхо. 2. Ваш бот не кусається з кроком 5, тому підтверджує одне з правил. Якщо ці проблеми будуть вирішені, мені все одно подобається концепція, оскільки аналітичні боти не зможуть протистояти, і тому ви, швидше за все, виграєте досить часто.
Кевін Кройсейсен

5

DeterredBot

import net.ramenchef.dollarauction.DollarBidder;

public class DeterredBot extends DollarBidder {
    private int deterrence;
    public void newAuction(Class<? extends DollarBidder> opponent) {
        if (opponent.equals(DeterrentBot.class)) {
            deterrence = 1;
        } else if (opponent.equals(LuckyDiceBot.class)) {
            deterrence = -1;
        } else {
            deterrence = 0;
        }
    }
    @Override
    public int nextBid(int opponentsBid) {
        switch (deterrence) {
        case 0:
            return 0;
        case -1:
            return opponentsBid + 5;
        case 1:
            // Holy shit, the fuzz! Hide the money!
            return 100001;
        }
        throw new RuntimeException("Darn hackers!");
    }
}

DeterredBot заробляє удачу від своїх незаконних азартних ігор з LuckyDiceBot. Тож звичайно, коли приїжджає поліція (DeterrentBot), йому доводиться швидко розпоряджатися своїм заробітком якимось чином, таким як торги на наступному аукціоні.


4

ІнфляціяБот

import net.ramenchef.dollarauction.DollarBidder;

public class InflationBot extends DollarBidder {
    private int target = -5;

    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        target += 5;
    }

    @Override
    public int nextBid(int opponentsBid) {
        if (opponentsBid >= target) {
            return 0;
        } else {
            return opponentsBid + 5;
        }
    }
}

Наразі це неможливо перевірити, тому, будь ласка, повідомте мене, якщо воно порушено.

З кожним раундом вартість долара піднімається вгору.


Це було б чудово проти MirrorBot, MarginalerBot, і, ймовірно, також MimicBot.
Нісса

@StephenLeppik Про це я думав, коли це робив. Однак все-таки безліч слабких місць.

+1, ідея мені подобається. Хм, чи призначено ваш бот встановити ставки 0 і зламати, навіть якщо він розпочне раунд (коли opponentsBidще 0)?
Кевін Кройсейсен

@KevinCruijssen Так. Це може статися лише проти першого суперника. Будь-який з ботів, які його копіюють, починається з 0, тому на них не витрачається більше 5с.

4

Без конкуренції: AbstractAnalystCounterBot

import net.ramenchef.dollarauction.DollarBidder;

import java.util.Set;
import java.util.HashSet;

public abstract class AbstractAnalystCounterBot extends DollarBidder {

public AbstractAnalystCounterBot() {
    if (isPeeking())
        throw new RuntimeException();
}

    protected boolean isPeeking() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement ste : stackTrace) {
            Class<?> clazz;
            try {
                clazz = Class.forName(ste.getClassName());
            } catch (ClassNotFoundException | SecurityException e) {
                continue;
            }
            if (DollarBidder.class.isAssignableFrom(clazz) && !clazz.isAssignableFrom(this.getClass()))
                return true;
        }
        try {
            return Class.forName(stackTrace[0].getClassName()).getPackage().getName().equals("net.ramenchef.dollarauction");
        } catch (Exception e) {
            return true;
        }
    }
}

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

Оскільки це конструктор за замовчуванням, не потрібно його називати у своєму підкласі. Він реалізує isPeekingметод, щоб визначити, чи співає інший бот.


4

BreakEvenAsap

import net.ramenchef.dollarauction.DollarBidder;

public class BreakEvenAsap extends DollarBidder{
  @Override
  public int nextBid(int opponentsBid){
    // If the opponent has bid 100 or more: bid 0 to break even and let them win
    return opponentsBid >= 100 ? 0
    // Else: bid 100 to break even (and possibly win)
     : 100;
  }
}

Сценарії

  • Якщо опонент може стартувати і подавати ставки, <= 0вони програють.
  • Якщо опонент може почати і подає ставки [5,95] : ставте 100 самі. Або ваш опонент зупиняється зараз, або будете виставляти ставки понад 100, і в такому випадку ви припините торги, щоб дати їм виграти і зламати навіть себе.
  • Якщо опонент може почати і подає ставки >= 100 подати заявку: запропонуйте 0 себе програти, але вирівняйте.
  • Якщо ви можете почати: ставте 100 відразу. Або ваш опонент зупиняється зараз, або подає ставки вище 100, і в цьому випадку ви припините торги, щоб дати їм виграти і зламати навіть себе.

Нічого собі, це помилка. Там сказано, що я коментую це питання, але воно закінчилося тут. Треба знайти спосіб її відтворити
Стен Струм

@RamenChef Typo .. Але я змінив цілого бота. У будь-якому випадку в ньому були деякі помилки ..
Кевін Круїйсен

4
Це може абсолютно втратити гроші. Якщо ви ставите 100, то ваш противник

@ Mnemonic Ах, звичайно. Не думав про цю частину .. Хм .. це робить речі більш цікавими, але й складнішими. Відредагуйте опис наразі, але залиште бота таким, яким він є.
Кевін Крейссен

1
Я думаю, ти маєш на увазі «програти», а не «втратити». Втрата - це протилежність виграшу. Розпущений - це протилежність тугому.
Кет

3

EvilBot

import java.util.Arrays;

import net.ramenchef.dollarauction.DollarBidder;

public class EvilBot extends DollarBidder {

    @Override
    public int nextBid(int opponentsBid) {
        if (isPeeking()) {
            throw new Error("HaHa!");
        } else {
            return 5;
        }

    }

    private static boolean isPeeking() {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement ste : Arrays.copyOfRange(stackTrace, 3, stackTrace.length)) {
            Class<?> clazz;
            try {
                clazz = Class.forName(ste.getClassName());
            } catch (ClassNotFoundException e) {
                return true;
            }
            if (DollarBidder.class.isAssignableFrom(clazz))
                return true;
        }
        return false;
    }

}

Кидає помилку замість винятку, щоб заплутати аналітиків.


3

BuzzardBot

import java.util.Random;

import net.ramenchef.dollarauction.DollarBidder;

public class BuzzardBot extends DollarBidder {

    private int[] bids = new int[100];
    private int oppFlag = 0;

    public void newAuction(Class<? extends DollarBidder> opponent) {
        oppFlag = 0;
        if(isPeeking()) {
            oppFlag = 3;
            return;
        }
        try {
            DollarBidder enemy = opponent.newInstance();
            enemy.newAuction(this.getClass());
            // a simple (and fallible) determinism check
            int sample = new Random().nextInt(100);
            int a = enemy.nextBid(sample);
            int b = enemy.nextBid(sample);
            int c = enemy.nextBid(sample);
            if ((a - b) * (b - c) != 0) {
                oppFlag = 2;
                return;
            }
            for (int i = 0; i < 100; i++) {
                bids[i] = enemy.nextBid(i);
            }
        } catch (Throwable t) {
            oppFlag = 1;
        }
    }

    @Override
    public int nextBid(int opponentsBid) {
        switch (oppFlag) {
        case 0:
            // assume the opponent's nextBid function depends only on the bid provided, and
            // make the bid that yields the biggest profit possible accordingly
            int best = 0;
            int bid = 0;
            for (int i = 0; i < 100; i++) {
                if (bids[i] < i + 5) {
                    int gain = (i >= opponentsBid + 5) ? 100 - i : -i;
                    if (gain > best) {
                        best = gain;
                        bid = i;
                    }
                }
            }
            return bid;
        case 1:
            // act like BorkBorkBot against anything that tries to foil analysis with an
            // Exception
            return (opponentsBid >= 95) ? 0 : (opponentsBid + 5);
        case 3:
            // bid aggressively against opposing analysts
            return Math.min(opponentsBid + 5, 100);
        case 2:
        default:
            // place an opening bid against something unpredictable, as it might yield 95c
            // profit, and failure has a low cost.
            return (opponentsBid == 0) ? 5 : 0;
        }
    }

    private static boolean isPeeking() {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement ste : Arrays.copyOfRange(stackTrace, 3, stackTrace.length)) {
            Class<?> clazz;
            try {
                clazz = Class.forName(ste.getClassName());
            } catch (ClassNotFoundException e) {
                return true;
            }
            if (DollarBidder.class.isAssignableFrom(clazz))
                return true;
        }
        return false;
    }
}

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


1
Ласкаво просимо до PPCG!
Аліон

3

AnalystOptimizer

import net.ramenchef.dollarauction.DollarBidder;

public class AnalystOptimizer extends DollarBidder{

    private DollarBidder enemy;

    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        try {
            enemy = opponent.newInstance();
            enemy.newAuction(this.getClass());
        } catch (ReflectiveOperationException e) {
            enemy = null;
        }
    }

    @Override
    public int nextBid(int opponentsBid){
        if (enemy == null)
            return (opponentsBid >= 95) ? 0 : (opponentsBid + 5);
        int nb = 0;
        try {
            return enemy.nextBid(95) >= 100 ? 95 : 0;
        } catch (Throwable e) {
            System.out.println("haha no");
            return 95;
        }
    }
}

звалилися з частин інших ботів. цей грає, намагаючись бути AnalystBot, і якщо це не вдалося, стає BorkBorkBot.

Я не думаю, що це зробить це добре.


Слідкуйте за AnalystKiller.
RamenChef

@RamenChef AFAIK вбивця аналітика просто кидає виняток, якщо він бачить, що він аналізується. Я можу це спіймати
dkudriavcevv

1
Ви, мабуть, повинні його зловити.
RamenChef

@RamenChef Не маю уявлення, чи це буде працювати, я не можу Java
dkudriavtsev

3

CounterBot

import net.ramenchef.dollarauction.DollarBidder;

public class CounterBot extends DollarBidder {
  private Class<? extends DollarBidder> enemy;

  @Override
  public void newAuction(Class<? extends DollarBidder> opponent){
    this.enemy = opponent;
  }

  @Override
  public int nextBid(int opponentsBid) {
    if(this.enemy.equals(CounterBot.class))
      throw new RuntimeException("Here boy, catch!");

    return this.enemy.equals(DarthVader.class) || 
           this.enemy.equals(MirrorBot.class) || 
           this.enemy.equals(OnlyWinningMove.class) ||
           this.enemy.equals(AnalystKiller.class) || 
           this.enemy.equals(DeterredBot.class) ||
           this.enemy.equals(InsiderTradingBot.class) ||
           this.enemy.equals(RiskRewardBot.class) ||
           this.enemy.equals(ImprovedAnalystBot.class) ?
            5
         : this.enemy.equals(MarginalBot.class) ?
           opponentsBid == 0 ? 5 : 10
         : this.enemy.equals(AnalystBot.class) || 
           this.enemy.equals(AnalystOptimizer.class) ?
            opponentsBid == 95 ? 100 : 5
         : this.enemy.equals(TargetValueBot.class) ?
            opponentsBid < 190 ? opponentsBid + 5 : 200
         : this.enemy.equals(BorkBorkBot.class) ?
            opponentsBid < 90 ? opponentsBid + 5 : 95
         : this.enemy.equals(DeterrentBot.class) ?
            105
         : this.enemy.equals(BreakEvenAsap.class) ?
            opponentsBid == 100 ? 105 : 100
         : this.enemy.equals(LuckyDiceBot.class) ?
            opponentsBid == 0 ? 5 : 0
         : this.enemy.equals(RandBot.class) || 
           this.enemy.equals(UpTo200.class) ||
           this.enemy.equals(SecretBot.class) ||
           this.enemy.equals(BluffBot.class) ||
           this.enemy.equals(EvilBot.class) ?
            opponentsBid + 5
         : this.enemy.equals(MimicBot.class) ? // TODO: Find actual counter
            10
         : this.enemy.equals(MarginalerBot.class) ||
           this.enemy.equals(MBot.class) ||
           this.enemy.equals(StackTraceObfuscaterBot.class) ||
           this.enemy.equals(MSlowBot.class) ?
            opponentsBid < 95 ? 90 : opponentsBid == 95 ? 100 : 95;
         : this.enemy.equals(BuzzardBot.class) ?
            100
         : this.enemy.equals(ScoreOverflowBot.class) ?
            opponentsBid == 105 ? 110 : 0
         : //this.enemy.equals(GreedyBot.class) || 
           //this.enemy.equals(RichJerk.class) ||
           //this.enemy.equals(InflationBot.class) ?
           // TODO: More bots?
            0;
  }
}

Лічильники:

  • DarthVaderвраховує себе, викликаючи SecurityExceptionдо початку торгів, але я ставлю 5 на всякий випадок.
  • AnalystBot і AnalystOptimizer обидва будуть дивитись на мою відповідь, коли я ставлю 95, і в такому випадку я покажу, що я ставлю 100, тож вона сама подасть ставки 95. Проте я ставлю 5, якщо я почну (або 100, якщо вони почали), тож вони втратять 95 центів, і я або виграю банкноту в розмірі 1 долара, запропонувавши лише 5 центів, або розбивши рівну суму.
  • MirrorBotвиставить ставку на те, що я став би проти. Тому я лише ставлю 5, і хто починає, виграє 95 центів, а інший втратить 5 центів.
  • MarginalBot виставить ставку 5, якщо я запропонував би менше 10 (або те, що воно починається), інакше він подасть ставку 0. Отже, якщо я лише ставлю 5, коли я починаю, або 10, коли він починається, я виграю 95 або 90 центів, і вони програють 5 центів.
  • GreedyBot завжди ставить на 5 більше, ніж я, тому просто запропонуйте 0, щоб пробити рівний рівень, і хай вони виграють
  • OnlyWinningMoveі AnalystKillerобидва завжди ставлять 0, тому просто ставте 5, щоб виграти
  • TargetValueBotподаватимуть ставки в діапазоні [100,200], тому пропонуйте ще 5 разів кожного разу, поки вони не на 190, і тоді ми піднімаємо до 200, щоб вибити долар (і нехай вони втратять 190 або 195 залежно від того, хто почав)
  • BorkBorkBotподаватиме ставки в діапазоні [5,95], тому кожен раз пропонуйте ще 5. Як тільки вони запропонують 85 або 90 (залежно від того, хто почав), ставте 95 самі. Вони втратять 85 або 90 центів, а ви виграєте банкноту в розмірі 1 долара США за 5 центів прибутку.
  • DeterrentBot виставить ставку 5, якщо вони стартують, або 100, якщо ми почнемо, тож просто виставляйте ставку 105, щоб вони протистояли 100, в результаті чого вони втрачали 100, а ми втрачали лише 5 центів, вигравши банкноту в 1 долар.
  • BreakEvenAsapбуде одразу ставити 100. Тож якщо вони розпочали свою заявку від 100, протидійте 105, щоб виграти 95 центів, і нехай вони програють 100. Якщо ми можемо почати просто ставку 100, то ми обидва прориваємося.
  • RichJerk подаватиме ставки 10,001 одразу, тому просто попросіть 0, щоб вирівняти, і нехай вони втратять 9901.
  • DeterredBot не знає мене, і тому запропонував би ставку 0, тому просто ставте 5, щоб виграти.
  • LuckyDiceBotпродовжує торги, поки не виграє. Тож якщо ми розпочали, запропонуйте 5, сподіваючись, що вони запропонують якомога вище, щоб виграти долар. Якщо вони розпочали лише ставки 0, нехай вони виграють і зламають навіть себе.
  • RandBotбуде виставляти ставку випадково в діапазоні [5,100], тому просто ставте ще 5, поки вона не зупиниться; тоді ви виграли 95 центів, і вони програли 0-100.
  • UpTo200буде (як зазначено в назві) ставка до 200. Тож просто ставте на 5 вище, поки вони не зупиняться. Ми виграємо банкноту в розмірі 1 долара США і візьмемо загальну втрату 105 центів, проте вони втратять 200 центів.
  • InsiderTradingBot не знає мене, тому просто виграйте 5 центів, щоб виграти
  • MimicBotбуло найважче. Просто ставте 10, щоб або почати з першої заявки на 5, або спростувати їх. Якщо вони спробують отримати доступ до мене, я викину RuntimeException (який вони впіймають; у такому випадку це буде діяти так, як ніби я ставлю 100 замість цього - хоча це порушить внутрішній while-петля). Виходячи з ворогів, які в ньому є HashSet, відбувається інше. Мені доведеться переглянути і придивитись уважніше, щоб побачити, чи є справжній лічильник.
  • RiskRewardBot не знає мене, тому я просто ставлю 5, і в такому випадку я ставлю 5, щоб виграти.
  • MarginalerBotбуде біти до 100 залежно від того, що я б запропонував. Якщо я можу почати, я ставлю 90, тоді він буде ставити 95, тоді я ставлю 100, то він зробить ставку 0 і втратить 95 центів, в той час як я виграю банкноту в 1 долар і беззбитковості. Якщо він може початись замість цього, він побачить, що я ставлю 90 проти цього, тож він подає ставки 90, тоді я ставлю 95, то він зробить ставку 0 і втратить 90 центів, тоді як я виграю банкноту в розмірі 1 долара з 5-відсотковим прибутку.
  • BuzzardBotпроаналізую всі мої лічильники в діапазоні [0,100). Якщо я 100одразу подаю заявку , її використовувати, oppFlag = 0і повний масив розміром 100 буде містити 100x значення 100. У комутаторі case 0цикл знову буде в діапазоні [0,100), і оскільки i + 5буде максимум 104, якщо bids[i] < i + 5ніколи не буде правдою , тому ставка, яку вона робить, залишається 0.
  • ImprovedAnalystBotзавжди буде, this.enemy = nullбо його опонент - це CounterBotне він сам. Тож він завжди буде ставити 0, що я просто протиставляю ставці 5.
  • InflationBot буде ставити 0, щоб пробити навіть тоді, коли він почнеться, інакше він продовжуватиме торги 5. Тож просто попросіть 0 себе, щоб пробити ще раз, і нехай вони отримають виграш.
  • ScoreOverflowBotабо зробить ставку поруч, Integer.MAX_VALUEякщо вони можуть почати, інакше вони подадуть ставки 105. Тож якщо вони запропонували 105, то самі зробіть ставку 110 (вони втратять 105, ми втратимо 10), інакше просто ставте 0, щоб вони отримали виграш.
  • MBotце те саме MarginalerBot, але з додатковим захистом від "підглядання" опонентів. Оскільки я не "заглядаю", це в основному те саме, що MarginalerBot.
  • SecretBotйого isPeeking()метод поверне помилковим, тому якщо він може початися або якщо я ставлю 5, він буде ставити 5 або 10 відповідно. Інакше ставка 0. Тож, починаю чи ні, opponentsBid + 5я спричинив би перемогу в будь-якому випадку, з моєю ціною 10 копійок або 15 центів, що призведе до втрати 5 або 10 центів.
  • BluffBotпогляне на те, що я запропонував би, коли його ставка становить 95, і якщо ця величина більша або дорівнює 100, вона зробить ставку 0, щоб пробити рівну суму, інакше вона подасть заявку opponentsBid + 5. Тож я просто ставлю ставку opponentsBid + 5. Це зламається навіть незалежно від того, хто стартує, і я виграю 100 чи 95 копійок залежно від того, почав я чи ні.
  • StackTraceObfuscaterBotбуде діяти так само, як MarginalerBot.
  • EvilBotзавжди буде ставити 5, тому просто ставку opponentsBid + 5. У будь-якому випадку вони втратять ці 5 центів, і ми виграємо ставку на 1 долар США (або зі ставкою на 5 центів, якщо ми починаємо, або з 10 центів, якщо вони почали).
  • MSlowBotте саме, що MBotі, отже, також MarginalerBot.

Повідомте мене, якщо ви бачите помилки або помилки на моїх прилавках.


1
MirrorBotвикликає newAuction з власним класом, тому це проблема. Крім того, рада знати, що 3 години, проведені на MimicBot, не були марними.
Нісса

@StephenLeppik Видалено код у newAuctionтому, що він буде виходити з ладу частіше, ніж ні. Я не можу протидіяти, MirrorBotі він не може протидіяти мені. Хто починається з двох, виграє 95 центів, а інший втрачає 5 центів.
Кевін Кройсейсен

3
Святе потрійне прикування, Бетмен!
Скайлер

1
Крім того, під час гри BorkBorkBotне слід піднімати до 95, коли вони потрапляють у 85? Інакше ви обидва призначаєте ставки 95, якщо вони почнеться.
Скайлер

1
@Freiheit Я знаю. Я просто використав додатковий випадок, щоб повернути 0, якщо я хотів би змінити типовий за будь-якої причини. Але я поставив їх зараз за замовчуванням (коментуючи їх). І я знаю, що можу гольфувати все зовсім небагато, але справа не в тому, щоб зробити найкоротший код. Я щойно зробив це потрійним, щоб зробити його трохи більш компактним, але це про це. Залишимо поки що так.
Кевін Крейссен

3

RiskRewardBot

import net.ramenchef.dollarauction.DollarBidder;

public class RiskRewardBot extends DollarBidder {
    private int target;

    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        if (opponent.equals(OnlyWinningMove.class) ||
            opponent.equals(DeterredBot.class) ||
            opponent.equals(MirrorBot.class) ||
            opponent.equals(AnalystKiller.class) ||
            opponent.equals(RiskRewardBot.class)) {
            target = 5;
        } else if (opponent.equals(MarginalBot.class) ||
            opponent.equals(EvilBot.class)) {
            target = 10;
        } else if (opponent.equals(SecretBot.class)) {
            target = 15;
        } else if (opponent.equals(BorkBorkBot.class)) {
            target = 95;
        } else if (opponent.equals(MarginalerBot.class) ||
             opponent.equals(BluffBot.class) ||
             opponent.equals(BuzzardBot.class)) {
            target = 100;
        }
        } else {
            target = 0;
        }
    }

    @Override
    public int nextBid(int opponentsBid) {
        if (opponentsBid >= target) {
            return 0;
        } else if (target > 10 && opponentsBid == target - 10) {
            return target;
        } else {
            return opponentsBid + 5;
        }
    }
}

Наразі це неможливо перевірити, тому, будь ласка, повідомте мене, якщо воно порушено.

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


3

BluffBot

import net.ramenchef.dollarauction.DollarBidder;

public class BluffBot extends DollarBidder {

private DollarBidder enemy;

@Override
public void newAuction(Class<? extends DollarBidder> opponent){
  try {
    this.enemy = opponent.newInstance();
    enemy.newAuction(this.getClass());
} catch (Throwable e) {
    enemy = null;
}
}

@Override
public int nextBid(int opponentsBid) {
    //Is this a legit call?
    for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
        Class<?> clazz;
        try {
            clazz = Class.forName(ste.getClassName());
            if (DollarBidder.class.isAssignableFrom(clazz) && !clazz.isAssignableFrom(this.getClass())) {
                return 100000;
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //Play it safe against strangers
    int enemyMaxBid;
    try{
        enemyMaxBid = enemy.nextBid(95);
    }
    catch (Throwable t){
        enemyMaxBid = 0;
        enemy = null;
    }
    if(enemy == null) return opponentsBid <= 5 ? opponentsBid + 5 : 0; //Hazard a 5c guess because of how many bots fold instantly.

    //If there's profit to be had, get there as cheaply as possible. Otherwise, best outcome is zero.
    return enemyMaxBid >= 100 ? 0 : opponentsBid + 5;
}


}

Шпигун, якого ви знаєте, цінніший, ніж зовсім не шпигун ...

Якщо хтось інший намагається зателефонувати за методом getBid, BluffBot відповідає 100 доларів США, щоб підманути їх на те, щоб відмовитись чи зробити ставку дуже високою.

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


2

UpTo200

import net.ramenchef.dollarauction.DollarBidder;

public class UpTo200 extends DollarBidder{
  @Override
  public int nextBid(int opponentsBid){
    // If the current bid of the opponent is in the range [0,195]: raise the bid by 5
    return opponentsBid <= 195 ? opponentsBid + 5
    // Else: Give up
     : 0;
  }
}

2

SecretBot

import java.util.Arrays;

import net.ramenchef.dollarauction.DollarBidder;

public class SecretBot extends DollarBidder {

    @Override
    public int nextBid(int opponentsBid) {
        if (isPeeking()) {
            return opponentsBid;
        } else if (opponentsBid < 10) {
            return opponentsBid + 5;
        } else {
            return 0;
        }

    }

    private static boolean isPeeking() {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement ste : Arrays.copyOfRange(stackTrace, 3, stackTrace.length)) {
            Class<?> clazz;
            try {
                clazz = Class.forName(ste.getClassName());
            } catch (ClassNotFoundException e) {
                return true;
            }
            if (DollarBidder.class.isAssignableFrom(clazz))
                return true;
        }
        return false;
    }

}

Цей бот робить мінімальні спроби виграти, ставлячи 5 або 10. Він також перевіряє стежку стека, щоб побачити, чи викликав його інший Бот, а потім бреше їм про те, які ставки він зробить.


Майте в виду , якщо я порт isPeekingв AbstractAnalystCounterBot?
Нісса

1
@StephenLeppik, ну, я вкрав його з MBot ...
Вінстон Еверт

1
Що ж, МБот, ймовірно, вкрав це у мене…
Nissa

2

Один зайвий

import net.ramenchef.dollarauction.DollarBidder;

public class OneExtra extends DollarBidder {
    @Override
    public int nextBid(int opponentsBid) {
        if(opponentsBid < 110)
          return opponentsBid + 6;
        return opponentsBid;
    }
}

Ставки на 6 більше, ніж остання пропозиція, тільки тому, що він може.


Він не може ставити 6, оскільки всі ставки повинні бути кратними 5 ...
Ніл

@Neil - це, мабуть, помилка друку ...
Stan Strum

@Neil правила спеціально зазначають: "Ваша ставка не повинна бути кратною 5 ¢"
MegaTom

@MegaTom Хм, добре, що було додано з моменту останнього читання правил ...
Ніл,

@Neil Це було частиною оригінальних правил, але я додав його туди, оскільки це було не дуже очевидно.
RamenChef

2

StackTraceObfuscaterBot

import net.ramenchef.dollarauction.DollarBidder;

import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

public class StackTraceObfuscaterBot extends DollarBidder {
    private volatile static boolean created = false;
    private volatile DollarBidder pet;
    private boolean firstBid = false;

    public StackTraceObfuscaterBot() {
        if (created)
            throw new IllegalStateException("THERE CAN ONLY BE ONE!");
        created = true;
    }

    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        firstBid = true;
        RunnableFuture<DollarBidder> task = new FutureTask<>(() -> {
            try {
                return opponent.newInstance();
            } catch (Throwable t) {
                return null;
            }
        });
        Thread thread = new Thread(task);
        thread.start();
        try {
            pet = task.get(450, TimeUnit.MILLISECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            task.cancel(true);
            pet = null;
        }
    }

    @Override
    public int nextBid(int opponentsBid) {
        if (!firstBid)
            return 0;
        firstBid = false;

        for (int bid = opponentsBid + 5; i < 100; i += 5) {
            final int bidt = bid;
            RunnableFuture<Boolean> task = new FutureTask<>(() -> {
                pet.newAuction(this.getClass());
                return pet.nextBid(bidt) < bidt + 5;
            });
            Thread thread = new Thread(task);
            thread.start();
            try {
                if (task.get(23, TimeUnit.MILLISECONDS))
                    return bid;
            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                task.cancel(true);
                return 0;
            }
        }
        return 0;
    }
}

Цей бот сміється з спроб виявити відображення за допомогою сліду стека. Найближче, що вони бачать, - DollarBidderце якийсь створений ним клас лямбда. Очевидно, що не інший бот намагається відобразити їх. Мало хто знає, що цей клас лямбда насправді працює для а DollarBidder. Крім того, він діє так MarginalerBot.


Зауважте, що з тих пір я оновлював перевірку слідів стека, щоб впоратися з цим.
Нісса

1

Дарт Вейдер

import java.lang.reflect.Field;
import net.ramenchef.dollarauction.DollarBidder;

public class DarthVader extends DollarBidder
{
@Override
public void newAuction(Class<? extends DollarBidder> opponent) {
    //set all values in the integer cache to over the $100 limit except 0
    Class icache = Integer.class.getDeclaredClasses()[0];
    Field c = icache.getDeclaredField("cache");
    c.setAccessible(true);
    Integer[] cache = (Integer[]) c.get(cache);
    for(sbyte b=0;b<128;b++)
    {
     cache[b]=100001;
    }
}

@Override
public int nextBid(int opponentsBid) 
{
    return 0;
}
}

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


2
Менеджер із безпеки зупинить це.
Нісса

2
І це б не спрацювало, так як ніде в бігунці не встановлено коробку своїх цілих чисел.
Нісса

Навіть якщо це не було б зупинено, це ривок, хоча дійсний. "Саботаж інших ботів дозволений, але спроба змінити видимість поля / методу призведе до загадкових SecurityExceptions."
NoOneIsHere

1
@StephenLeppik Сенс у тому, щоб зламати такі речі, як return opponentsBid <= 195 ? opponentsBid + 5 : 0і зробити це return opponentsBid <= 100001 ? opponentsBid + 100001 : 100001.
NoOneIsHere

1
Не вдалося компілювати через неперевірені винятки.
Нісса

1

ImprovedAnalystBot (неконкуруючий)

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

import net.ramenchef.dollarauction.DollarBidder;

public class ImprovedAnalystBot extends DollarBidder {
    private DollarBidder enemy;

    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        if (!opponent.equals(this.getClass()))
            try {
                this.enemy = opponent.newInstance();
                enemy.newAuction(this.getClass());
            } catch (Throwable t) {
                this.enemy = null;
            }
        else
            this.enemy = null;
    }

    @Override
    public int nextBid(int opponentsBid) {
        try {
            return enemy != null && enemy.nextBid(95) < 100 ? 95 : 0;
        } catch (Throwable t) {
            return 0;
        }
    }
}

Чому б просто не відредагувати свій виклик?
Натан Меррілл

@NathanMerrill Як я можу це відредагувати?
RamenChef

Натиснувши кнопку редагування та замінивши AnalystBot цим кодом?
Натан Мерріл

@NathanMerrill AnalystBotнавмисно поганий код, щоб він міг демонструвати його AnalystKillerсаботаж.
RamenChef

1
AnalystKiller все ще працює з вдосконаленим :) Проблема з тим, щоб зробити його публікацією, полягає в тому, що виклик набагато помітніше, ніж відповідь.
Натан Мерріл

1

МБот

import net.ramenchef.dollarauction.DollarBidder;

import java.util.Arrays;

public class MBot extends DollarBidder {
    protected DollarBidder rival = null;
    protected boolean rivalPrepared = false;
    protected Class<? extends DollarBidder> rivalClass;


    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        this.rivalClass = opponent;
        this.rivalPrepared = false;
    }

    protected DollarBidder getRival() {
        if (!rivalPrepared) {
            rivalPrepared = true;
            try {
                rival = rivalClass.newInstance();
                rival.newAuction(this.getClass());
            } catch (Throwable t) {
                rival = null;
            }
        }
        return rival;
    }

    @Override
    public int nextBid(int opponentsBid) {
        return calcBid(opponentsBid, isPeeking(3), isPeeking(4));
    }

    protected int calcBid(int opponentsBid, boolean isPeeking, boolean isSubPeeking) {
        if (isPeeking) {
            throw new RuntimeException();
        }

        for (int iBid = opponentsBid + 5; iBid <= 100; iBid = iBid + 5) {
            try {
                if (getRival().nextBid(iBid) < iBid + 5) {
                    return iBid;
                }
            } catch (Throwable t) {
                // noop
            }
        }
        return 0;
    }

    protected boolean isPeeking(int level) {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        final StackTraceElement[] stackTraceElements = Arrays.copyOfRange(stackTrace, level, stackTrace.length);
        for (StackTraceElement ste : stackTraceElements) {
            try {
                Class<?> clazz = Class.forName(ste.getClassName());
                if (DollarBidder.class.isAssignableFrom(clazz))
                    return true;
            } catch (ClassNotFoundException e) {
                return true;
            }
        }
        return false;
    }
}

Трохи вишуканий MarginalerBot

  • будьте недобрі до тих, хто не хоче вас перевірити
  • дозволити платити 100, щоб отримати 100, і розбити рівний випадок, просто відмовити іншим у легких грошах

Ви не можете оголосити nextBidкидок ClassCastException.
RamenChef

@RamenChef добре, поміняв його на RuntimeException, декларація не потребує :)
mleko

Ваш код для перевірки сліду стека виглядає підозріло схожим на мій.
Нісса

@StephenLeppik, ймовірно, це його копія
mleko

@mleko чому хоч? Клас, з якого скопійовано, - це абстрактний суперклас, який можна безкоштовно використовувати.
Нісса

1

Без конкуренції: MSlowBot

import net.ramenchef.dollarauction.DollarBidder;

import java.util.Arrays;

public class MSlowBot extends DollarBidder {
    private DollarBidder rival;

    @Override
    public void newAuction(Class<? extends DollarBidder> opponent) {
        try {
            rival = opponent.newInstance();
            rival.newAuction(this.getClass());
        } catch (Throwable t) {
            rival = null;
        }
    }

    @Override
    public int nextBid(int opponentsBid) {
        noPeeking();

        for (int iBid = opponentsBid + 5; iBid <= 100; iBid = iBid + 5) {
            try {
                if (rival.nextBid(iBid) < iBid + 5) {
                    return iBid;
                }
            } catch (Throwable t) {
                //do nothing.
            }
        }
        return 0;
    }

    private void noPeeking() {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement ste : Arrays.copyOfRange(stackTrace, 3, stackTrace.length)) {
            try {
                Class<?> clazz = Class.forName(ste.getClassName());
                if (DollarBidder.class.isAssignableFrom(clazz))
                    Thread.sleep(1000);
            } catch (ClassNotFoundException | InterruptedException e) {
                throw new RuntimeException(":(");
            }
        }
    }
}

Така ж логіка, що і MBot, просто використовуйте тайм-аут замість винятку при боротьбі з ворогом. Поки ніхто не захищає тайм-аут, тому він повинен бути ефективним


Зазначені правила забороняють навмисно викликати тайм-аут іншого бота.
Вінстон Еверт

@WinstonEwert ви можете процитувати? Я не можу знайти правило, яке забороняє це
mleko

"Саботаж інших ботів дозволений, але спроба змінити видимість поля / методу призведе до таємничих SecurityExceptions. Виняток спричиняє, що інший бот пробив межу в 500 мс." Також я захищаю від тайм-ауту.
RamenChef

@RamenChef, але це не змінює видимість інших елементів. Я не впевнений, чи правильно я вас зрозумів. Чи дозволено провокувати тайм-аут?
млеко

"Виняток змушує іншого бота пробити межу 500мс." Зокрема, це виняток із правила про диверсії.
RamenChef
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.