Аукцион долларовой банкноты


32

Это вызов КОТ для игры на аукционе долларовой банкноты в теории игр. В нем доллар продается по высокой цене. Ставки повышаются с шагом 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;
    }
}

Дополнительные правила

  • Стандартные лазейки запрещены.
  • Разрешается саботировать других ботов, но попытка изменить видимость поля / метода приведет к таинственному SecurityException s. Исключение заставляет другого бота нарушать ограничение в 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
Эта проблема принципиально уязвима для One-Upping. Так как я знаю класс своего оппонента, легко выбрать лучшую стратегию против него. (Затем кто-то приходит и может сразиться с моим ботом и т. Д.)
Натан Меррилл,

2
« Ставки повышаются с шагом 5 ¢ ». У вас нет ничего в вашем коде, чтобы проверить это, хотя .. LuckyDiceBotнапример, ставки с приращениями в 2-12случайном порядке ..
Кевин Круйссен

4
Кроме того: что если мой бот заставит других ботов превысить ограничение в 500 мс?
Натан Меррилл

4
@RamenChef Мы говорим о вредоносном коде здесь. Что если я обнаружу, когда мне звонит другой бот, и вызову Thread.sleep (1000)?
Натан Меррилл

3
Я это VTC, так как неясно, какие диверсии разрешены, а какие нет. ОП запретил представления, которые «нападают на бегуна» (что является расплывчатым), и нет четкой границы между разрешенным вредоносным кодом и недопустимым вредоносным кодом (Как определить, какой бот заставил бота занять слишком много времени? ?)
Натан Меррилл,

Ответы:


2

MTargetedBot

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 Что, на ваш взгляд, не совсем справедливо в этом решении?
Млеко

Зная имена ваших противников.
Иисус Навин

@ Джошуа половина решений использует эту информацию. Мы даже написали автору, что это должно быть изменено или произойдет One-Upping, он отказался изменить вызов - так что вот оно
mleko

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 долларов.
Nissa

Слишком рано, чтобы оптимизировать против других ответов. Кроме того, это AnalystBotне так Analyst.
RamenChef

8
Вероятно, должно быть правило «имена классов будут рандомизированы».
user202729

1
@ user202729 Как насчет "нет прямых ссылок на классы"?
RamenChef

1
Я хотел бы видеть эту ручку MimicBot.
Nissa

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 Как?
дкудрявцев

Mirror пытается эмулировать игру Analyst против себя, что приводит к переполнению стека.
Сильвио Майоло

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 .)
Арно

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
Как это сократить свои потери или остановить потери? Если он всегда добавляет бросок костей к ставке оппонента, вы всегда будете предлагать больше. Случайность может запутать достаточно аналитического бота, мне нравится концепция.
Freiheit

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

Две вещи: 1. @Freiheit прав, и этот бот будет продолжать делать ставки до тех пор, пока не выиграет, независимо от того, как высоко. opponentsBidв nextBid(int opponentsBid)удерживает общую ставку вашего оппонента, а не следующую ставку. Лучшим термином для этого метода будет raise(как термин «покер») imho. 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

InflationBot

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, так что это не будет тратить на них больше 5c.

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, то ваш оппонент делает ставку 105, вы теряете 100, а они проигрывают только 5.

@Mnemonic А, конечно .. Я не думал об этой части ... Хм ... это делает вещи интереснее, но и сложнее. Отредактирую описание на данный момент, но оставлю бот как есть.
Кевин Круйссен

1
Я думаю, что вы имеете в виду «потерять», а не «потерять». Проиграть - это противоположность победы. Loose - это противоположность плотному.
Kat

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!
Alion

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 убийца аналитика просто выдает исключение, если видит, что анализируется. Я могу это поймать
дкудрявцев

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, чтобы они были безубыточными, и пусть они проигрывают 9 901.
  • 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 - хотя это сломает внутренняя петля времени). Основываясь на врагах, которых он имеет в своем 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 будет в 100 раз больше значения 100. В переключателе case 0цикл снова будет в диапазоне [0,100), и, поскольку он i + 5будет не более 104, значение if 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()метод вернет false, поэтому, если он может начаться или если я сделаю ставку 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, не прошли даром.
Nissa

@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
@ StefhenLeppik, ну, я украл его у MBot ...
Уинстон Эверт,

1
Что ж, MBot, вероятно, украл его у меня ...
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 it's probably a typo...
Stan Strum

@Neil the rules specifically state: "Your bid does not need to be a multiple of 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.


Note that I've since updated my stack trace check to handle this.
Nissa

1

Darth Vader

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

This one tries to force the opponent's bot to overpay by setting the integer cache to the value over the $100 limit.


2
The security manager would stop this.
Nissa

2
And this wouldn't work anyway since nowhere in the runner does it box its integers.
Nissa

Even if this wouldn't be stopped, this is a jerk move, although valid. "Sabotaging other bots is allowed, but attempting to alter field/method visibility will result in mysterious SecurityExceptions."
NoOneIsHere

1
@StephenLeppik The point of this is to break things like return opponentsBid <= 195 ? opponentsBid + 5 : 0 and make it return opponentsBid <= 100001 ? opponentsBid + 100001 : 100001.
NoOneIsHere

1
Fails to compile because of unchecked exceptions.
Nissa

1

ImprovedAnalystBot (non-competing)

A lot of people seem to be using the AnalystBot code as a template, even though it's deliberately bad code. So I'm making a better template.

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

Why not just edit your challenge?
Nathan Merrill

@NathanMerrill How would I edit it?
RamenChef

By clicking the edit button, and replacing AnalystBot with this code?
Nathan Merrill

@NathanMerrill AnalystBot is deliberately bad code so that it can demonstrate the AnalystKiller sabotaging it.
RamenChef

1
The AnalystKiller still works with the improved one :) The issue with making it a post is that the challenge is far more visible than an answer.
Nathan Merrill

1

MBot

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

Slightly refined MarginalerBot

  • be unkind to those who wan't to check you
  • allow paying 100 to get 100 and break even case, just to deny others easy money

You can't declare nextBid to throw ClassCastException.
RamenChef

@RamenChef ok, swapped it to RuntimeException which don't require declaration :)
mleko

Your code for stack trace checking looks suspiciously similar to mine.
Nissa

@StephenLeppik probably it's copy of it
mleko

@mleko why though? The class it's copied from is an abstract superclass that's free to use.
Nissa

1

Non-competing: 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(":(");
            }
        }
    }
}

Same logic as MBot, just use timeout instead of Exception when fighting against enemy. So far no one is defending agains timeout so should be effective


The stated rules forbid deliberately causing another bot to timeout.
Winston Ewert

@WinstonEwert can you quote? I can't find rule disallowing this
mleko

"Sabotaging other bots is allowed, but attempting to alter field/method visibility will result in mysterious SecurityExceptions. An exception is causing another bot to break the 500ms limit." Also, I'm defending against timeout.
RamenChef

@RamenChef but this don't alter visibility of other elements. I'm not sure if I understand you correctly. Is provoking timeout allowed?
mleko

"An exception is causing another bot to break the 500ms limit." Specifically, this is an exception to the rule about sabotage.
RamenChef
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.