Leilão de notas de dólar

32

Este é um desafio KOTH para o jogo de leilão de notas de dólar na teoria dos jogos. Nele, um dólar está sendo vendido pelo maior lance. Os lances aumentam em incrementos de 5 ¢ e o perdedor também paga o lance. A idéia é que ambos os jogadores escalem a guerra de lances muito além do valor de um dólar para reduzir suas perdas.

Vamos torcer para que seus bots sejam mais inteligentes que isso.

Você criará um bot para jogar este jogo, estendendo a net.ramenchef.dollarauction.DollarBidderclasse. Você deve implementar o nextBidmétodo que retorna o próximo lance do seu bot, considerando o lance anterior do outro bot. Se necessário, você também pode usar o newAuctionmétodo para redefinir para cada leilão a classe do bot do oponente.

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

Os lances vão até que aconteça um dos seguintes:

  • nextBidlança uma exceção. Se isso acontecer, o bot que lançou a exceção paga o lance anterior e o outro bot recebe o dólar de graça.
  • Qualquer bot não paga o suficiente para cobrir o lance anterior. Se isso acontecer, ambos os bots pagam seus lances (o perdedor paga seu lance anterior) e o vencedor recebe um dólar.
  • Ambos os bots oferecem lances acima de US $ 100. Se isso acontecer, ambos os bots pagam US $ 100 e nenhum bot recebe o dólar.

São realizados 2 leilões para cada combinação de bots. Os robôs são pontuados pelo lucro total que obtiveram nesses leilões. A pontuação mais alta vence.

Exemplos

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

Não use isso como modelo para bots com análise analítica; use em ImprovedAnalystBotvez disso.

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

Regras adicionais

  • As brechas padrão são proibidas.
  • É permitido sabotar outros bots, mas tentar alterar a visibilidade do campo / método resultará em SecurityExceptions misteriosos . Uma exceção está fazendo com que outro bot ultrapasse o limite de 500ms.
  • Os robôs não podem acessar o pacote do corredor, exceto para estender a DollarBidderclasse.
  • Todos os métodos devem retornar em 500 ms ou menos.
  • Bots não precisam ser determinísticos.
  • Seu lance não precisa ser múltiplo de 5 ¢.
  • $ 1 = 100 ¢
  • Os resultados serão publicados em 24 de abril de 2018.

Corredor no GitHub

Resultados

Veja as rondas individuais aqui.

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

Parabéns MTargetedBotcom um lucro de $ 14,30!

RamenChef
fonte
11
Esse desafio é fundamentalmente vulnerável ao One-Upping. Como conheço a classe do meu oponente, é fácil escolher a melhor estratégia contra ela. (Então alguém aparece e pode criar meu bot , etc.)
Nathan Merrill
2
"Os lances aumentam em incrementos de 5 ¢ ". No entanto, você não tem nada no código para validar isso. LuckyDiceBotPor exemplo, lances em incrementos 2-12aleatórios.
Kevin Cruijssen
4
Além disso: e se meu bot fizer com que outros bots excedam a restrição de 500ms?
Nathan Merrill
4
@RamenChef Estamos falando de código malicioso aqui. E se eu detectar quando outro bot está me chamando e ligar para Thread.sleep (1000)?
19418 Nathan Merrill
3
Eu sou VTC, pois não está claro o que é sabotada e o que não é permitida. O OP não permitiu envios que "atacassem o corredor" (o que é vago), e não há uma linha clara entre código malicioso permitido e código malicioso que não é (como você determina qual bot causou a demora de um bot ?)
Nathan Merrill

Respostas:

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);
    }
}
  • Baseado no MBot atualizado
  • Usa um método semelhante ao CounterBot, mas com alguns métodos refinados para atingir com mais força alguns dos seus oponentes, também deve ser mais legível
  • No oponente desconhecido, padrão para MBot strat
mleko
fonte
1
Isso não é justo.
Joshua
@ Josué O que particularmente não é justo sobre esta solução na sua opinião?
mleko
Conhecendo os nomes dos seus oponentes.
Joshua
@ Josué metade das soluções usa essas informações. Nós até escreveu a autora que este deve ser mudado ou One-Upping irá ocorrer, ele se recusou a mudar desafio - por isso aqui está
mleko
1
Já fez ....
Joshua
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;
        }
    }
}

Vaca sagrada. Eu esperava que isso fosse simples de escrever e depois passei 3 horas nele.

Em essência, MimicBot mantém uma lista contínua dos bots disponíveis. Quando se trata de um novo leilão, ele percorre a lista em busca do mais eficaz contra o atual oponente. Ele então usa esse bot como uma "referência" no leilão.

Para fins de teste, seria melhor usar um subconjunto aleatório dos envios ou o conjunto completo. Ela começa com GreedyBot, MimicBote mais um bot que só ordena 5 ¢.

Nissa
fonte
11

InsiderTradingBot

No espírito da resposta de @ StephenLeppik, o InsiderTradingBot conhece todos os seus oponentes e entende suas estratégias. Sua vez, Stephen.

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

};
Silvio Mayolo
fonte
1
Não, o uso de informações privilegiadas seria se o RichJerkbot fizesse uma exceção específica para ele e oferecesse US $ 0 por ele.
Nissa
É muito cedo para otimizar contra outras respostas. Além disso, é AnalystBot, não Analyst.
RamenChef 17/04/19
8
Provavelmente, deve haver uma regra "os nomes das classes serão randomizados".
usar o seguinte comando
1
@ user202729 Que tal "sem referências diretas a classes"?
RamenChef 18/04/19
1
Eu gostaria de ver isso lidar com o MimicBot.
Nissa
8

MirrorBot

Faz o inimigo jogar contra si mesmo.

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);
        }
    }
}
dkudriavtsev
fonte
6
Você disparou Analystespetacularmente.
Silvio Mayolo 17/04
@SilvioMayolo Como?
Dkudriavtsev 17/04
O Mirror tenta emular o Analyst jogando contra si mesmo, resultando em um estouro de pilha.
Silvio Mayolo
8

Edit : Alterações direcionadas na classe DollarBidder quebraram este bot.

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

Após um leilão, sua pontuação será -2147483645, mas na próxima vez perderá 5 ¢ ou 105 ¢, tornando a pontuação positiva e muito grande. Todas as outras perdas seriam insignificantes.

No primeiro leilão, também faria a GreedyBot apostar -2147483646, que não é divisível por 5.

Inverno
fonte
scoreestá protegido por pacotes. Seus bots não podem acessá-lo.
RamenChef
@RamenChef Ops, removemos o CheatingBot
Inverno
Não existe uma regra contra "atacar o corredor", apenas "acessá-lo", o que não funciona. Eu recomendo corrigir o bug, o que resolve o problema :)
Nathan Merrill
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;
        }
    }
}

Não é possível testar isso no momento, então, deixe-me saber se está quebrado.

Basicamente, escolha um valor para o dólar e supere o oponente até excedermos esse valor.

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

Muito simples, tenta determinar se um oponente contestaria uma oferta mínima e, se não, colocá-la.

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

Uma versão nova e mais inteligente do MarginalBot que verifica se pode fazer algum movimento lucrativo sem contestação, em vez de apenas esperar ganhar com o mínimo.

Como está na mesma família do meu bot anterior, mas evita estratégias que tentam vencê-lo, achei que uma nova entrada no mesmo post era a maneira mais razoável de apresentá-la.

Editar 1: fez uma pequena alteração no método newAuction para otimizar contra outros bots do tipo analisador.

Edit 2: Alterou o MarginalerBot para minimizar as perdas contra estratégias sorrateiras ou não determinísticas.

Qaghan
fonte
Bem-vindo ao PPCG!
Martin Ender
1
É simples, mas supera todos os outros bots por uma margem bastante grande!
RamenChef 18/04/19
6

BorkBorkBot

import net.ramenchef.dollarauction.DollarBidder;

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

Desiste se não pode empatar.

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

Tinha que ser feito.

Neil
fonte
"Os lances aumentam em incrementos de 5 ¢ ". Seu bot atualmente não está fazendo isso.
Kevin Cruijssen
1
@KevinCruijssen Fair suficiente. Também mudei o limite superior para que ele pudesse oferecer toda a US $ 1, apenas no caso,
Neil
6

DeterrentBot

import net.ramenchef.dollarauction.DollarBidder;

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

Tentativas de persuadir quaisquer bots analiticamente pensados ​​de que o único movimento vencedor é não jogar.

histocrata
fonte
1
Percebi que meu comentário um tanto enigmático "Josué? É você?" foi deletado. Então, só para esclarecer, era uma referência a uma famosa citação do filme WarGames: "a única jogada vencedora é não jogar" . (Josué é o apelido da WOPR .)
Arnauld
5

LuckyDiceBot

LuckyDiceBot confia apenas em seus dados. Ele joga dois dados, adiciona a soma ao valor do licitante atual e oferece muito. Se não for suficiente para superar a oferta do oponente, ele reduz suas perdas e segue seu caminho.

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

};
Silvio Mayolo
fonte
2
Como isso reduz suas perdas ou impede perdas? Se ele sempre adiciona seu lançamento de dados à oferta do oponente, você sempre oferece mais. A aleatoriedade pode confundir um bot suficientemente analítico, eu gosto do conceito.
21418 Freiheit
Se o lançamento for 4 ou menos (estatisticamente improvável, mas eventualmente ocorrerá), o lance é insuficiente para vencer o oponente e o leilão termina.
Silvio Mayolo
Duas coisas: 1. @ Freiheit está certo e esse bot continuará fazendo lances até vencer, não importa quão alto seja. opponentsBidin nextBid(int opponentsBid)mantém o lance total que seu oponente fez até o momento, não o próximo lance. Um termo melhor para o método seria raise(como o termo do Poker) imho. 2. Seu bot não morde incrementos de 5, portanto, está validando uma das regras. Se esses problemas forem resolvidos, ainda gosto do conceito, porque os bots analíticos não serão capazes de combater e, portanto, você provavelmente vencerá com bastante frequência.
Kevin Cruijssen
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 ganha uma fortuna com seus jogos ilegais com o LuckyDiceBot. Então, é claro que quando a polícia (DeterrentBot) chega, ele precisa rapidamente dispor de seus ganhos de alguma forma, como licitar no próximo leilão.

Nissa
fonte
4

InfllationBot

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

Não é possível testar isso no momento, então, deixe-me saber se está quebrado.

A cada rodada, o valor do dólar aumenta.


fonte
Isso seria excelente contra o MirrorBot, o MarginalerBot e, provavelmente, também o MimicBot.
Nissa19
@StephenLeppik Era o que eu estava pensando quando o fiz. Ainda há muitas fraquezas.
+1, eu gosto da ideia. Hmm, pretende-se que o seu bot lance 0 e quebre mesmo que inicie uma rodada (quando opponentsBidainda é 0)?
Kevin Cruijssen
@KevinCruijssen Yes. Isso só pode acontecer contra o primeiro oponente. Qualquer um dos bots que o copiarem começará em 0, portanto, isso não desperdiçará mais do que 5c neles.
4

Não concorrente: 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;
        }
    }
}

Isso não pretende ser uma submissão verdadeira, mas sim um modelo para outros usarem para impedir bots de criação de animais de estimação como MirrorBoteMimicBot .

Como é o construtor padrão, não há necessidade de chamá-lo em sua subclasse. Ele implementa um isPeekingmétodo para determinar se outro bot está bisbilhotando.

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

Cenários

  • Se o oponente pode começar e fazer lances, <= 0ele perde.
  • Se o oponente puder começar e fazer lances [5,95]: ofereça 100 você mesmo. Seu oponente para agora, ou faz lances acima de 100 no total. Nesse caso, você para de oferecer para que eles tenham a vitória e se igualam.
  • Se o oponente puder começar e fazer lances >= 100: ofereça 0 a si próprio para perder, mas empate.
  • Se você pode começar: ofereça 100 imediatamente. O seu oponente para agora, ou faz lances acima de 100. Nesse caso, você para de oferecer para que eles tenham a vitória e se igualem.
Kevin Cruijssen
fonte
Uau, isso é um bug. Disse que estava comentando sobre a pergunta, mas acabou aqui. Tenho que encontrar uma maneira de reproduzi-lo
Stan Strum
@ RamenChef Typo .. Mas eu modifiquei o bot inteiro. Ele tinha alguns erros de qualquer maneira ..
Kevin Cruijssen
4
Isso pode absolutamente perder dinheiro. Se você lance 100, em seguida, seus lances oponente 105, você acaba perdendo 100, e eles só perdem 5.
@ Ahmemonic Ah é claro .. Não tinha pensado nessa parte .. Hmm .. isso torna as coisas mais interessantes, mas também mais difíceis. Editará a descrição por enquanto, mas deixe o bot como está.
Kevin Cruijssen
1
Eu acho que você quer dizer "perder" e não "perder". Perder é o oposto de vencer. Loose é o oposto de tight.
19418 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;
    }

}

Lança um erro em vez de uma exceção para confundir os analistas.

Winston Ewert
fonte
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;
    }
}

Tenta avaliar o oponente com o qual se defronta e certifique-se de não morder mais do que pode mastigar.

ripkoops
fonte
1
Bem-vindo ao 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;
        }
    }
}

paralelepípedos de partes de outros bots. este é reproduzido ao tentar ser AnalystBot e, se malsucedido, torna-se BorkBorkBot.

Eu não acho que este fará isso bem.

dkudriavtsev
fonte
Cuidado com o AnalystKiller.
RamenChef 17/04/19
@RamenChef AFAIK, o analista assassino apenas lança uma exceção se se vê sendo analisado. Eu posso pegar isso
dkudriavtsev 17/04/19
1
Você provavelmente deveria pegá-lo.
RamenChef 17/04/19
@RamenChef Não faço ideia se isso vai funcionar, eu não posso 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;
  }
}

Contadores:

  • DarthVader contadores, causando um SecurityException antes do início da licitação, mas lancei 5 apenas por precaução.
  • AnalystBot e AnalystOptimizer ambos olharão para a minha resposta quando oferecer um lance de 95. Nesse caso, mostrarei que lancei 100 para que ele faça um lance de 95. No entanto, lancei 5, se eu começar (ou 100, se eles começaram), para que eles percam 95 centavos e eu ganhe a nota de 1 USD oferecendo apenas 5 centavos ou empatando.
  • MirrorBotvai oferecer o que eu iria oferecer contra ele. Então, lancei apenas 5, e quem começar ganha 95 centavos e o outro perde 5 centavos.
  • MarginalBot oferecerá 5 se eu oferecer menos que 10 (ou o que começa), caso contrário, oferecerá 0. Portanto, se eu oferecer apenas 5 quando eu começar ou 10 quando começar, eu ganho 95 ou 90 centavos e eles perdem 5 centavos.
  • GreedyBot sempre oferece mais 5 do que eu, então lance 0 para empatar e deixe que eles tenham a vitória
  • OnlyWinningMovee AnalystKillerambos sempre dão lances 0, então lance 5 para ganhar
  • TargetValueBot fará lances no intervalo [100,200] , então lance mais 5 a cada vez até chegar aos 190. Nesse caso, aumentamos para 200 para atingir o ponto de equilíbrio ao ganhar o dólar (e deixá-los perder 190 ou 195, dependendo de quem começou)
  • BorkBorkBotfará lances no intervalo [5,95], então lance 5 a mais também. Assim que eles oferecerem 85 ou 90 (dependendo de quem começou), ofereça 95 você mesmo. Eles perderão 85 ou 90 centavos e você ganha a nota de 1 USD por um lucro de 5 centavos.
  • DeterrentBot oferecerá 5 se eles começarem ou 100 se começarmos, então apenas ofereça 105 para que contrariem com 100, fazendo com que eles percam 100 e nós percam apenas 5 centavos ao ganhar a nota de 1 dólar.
  • BreakEvenAsapoferecerá 100 imediatamente. Portanto, se eles começaram com seu lance de 100, contra-105 para ganhar 95 centavos e deixe-os perder 100. Se podemos começar apenas lance 100, então ambos empatamos.
  • RichJerk ofereça 10.001 imediatamente, portanto, ofereça 0 para empatar e deixe que eles percam 9.901.
  • DeterredBot não me conhece e, portanto, dará lance 0, então lance 5 para ganhar.
  • LuckyDiceBotcontinua a licitar até vencer. Portanto, se começássemos, ofereça 5 na esperança de que eles ofereçam o mais alto possível para ganhar o dólar. Se eles começaram, lance apenas 0 para permitir que eles tenham a vitória e se igualem.
  • RandBotoferecerá lances aleatórios no intervalo [5,100], portanto, ofereça mais 5 até que pare. Nesse caso, você ganhou 95 centavos e eles perderam 0-100.
  • UpTo200fará (como o nome indica) oferecer até 200. Portanto, faça um lance 5 mais alto até que eles parem. Ganharemos a nota de 1 dólar e teremos uma perda total de 105 centavos, mas eles perdem 200 centavos.
  • InsiderTradingBot não me conhece, então lance 5 centavos para ganhar
  • MimicBotfoi o mais difícil. Apenas faça um lance de 10 para começar ou contrariar o primeiro lance de 5. Se eles tentarem me acessar, lançarei uma RuntimeException (que eles capturarão, nesse caso, ela atuaria como se eu tivesse feito um lance de 100) - embora isso interrompa o loop while interno). Com base nos inimigos que ele possui, o HashSet acontece de maneira diferente. Vou ter que revisitar e olhar mais de perto para ver se há um contador real.
  • RiskRewardBot não me conhece, lance apenas 5, caso em que lancei 5 para ganhar.
  • MarginalerBotvai bit até 100, dependendo do que eu ofereceria. Se eu puder começar, lance 90, lance 95, lance 100, para que lance 0 e perca 95 centavos, enquanto eu ganho a nota de 1 USD e o mesmo empate. Se, em vez disso, começar, ele verá que eu ofereceria 90 contra ele, então fará um lance de 90, então lancei 95 para que faça um lance de 0 e perca 90 centavos, enquanto eu ganho a nota de 1 USD com um lucro de 5 centavos.
  • BuzzardBotanalisará todos os meus contadores no intervalo [0,100). Se eu fizer um lance 100imediatamente, ele usará oppFlag = 0e a matriz completa de 100 tamanhos conterá 100x o valor 100. No switch case 0, o loop estará no intervalo [0,100)novamente e, como i + 5no máximo será 104, o if bids[i] < i + 5nunca será verdadeiro , portanto, o lance permanece 0.
  • ImprovedAnalystBotsempre terá this.enemy = nullporque seu oponente é CounterBot, não ele próprio. Por isso, ele sempre oferecerá 0, o que eu contra-atento com um lance de 5.
  • InflationBot fará um lance de 0 para empatar quando começar, caso contrário, continuará fazendo um lance de 5. Então, faça um lance de 0 para empatar imediatamente e deixe que eles tenham a vitória.
  • ScoreOverflowBotfará um lance próximo Integer.MAX_VALUEse eles começarem, caso contrário, fará um lance 105. Portanto, se eles fizeram um lance de 105, apenas lance 110 (eles perderão 105, perderemos 10); caso contrário, lance 0 para permitir que eles tenham a vitória.
  • MBoté o mesmo que MarginalerBot, mas com proteção adicional contra 'espreitar' os oponentes. Desde que eu não 'espio', é basicamente o mesmo que MarginalerBot.
  • SecretBotfará com que seu isPeeking()método retorne false; portanto, se ele iniciar ou se eu oferecer 5, oferecerá 5 ou 10, respectivamente. Caso contrário, ele oferecerá 0. Portanto, se eu começar ou não, opponentsBid + 5me levará a ganhar de qualquer maneira, com meus 10 centavos ou 15 centavos, fazendo com que eles percam 5 ou 10 centavos.
  • BluffBotanalisará o que eu ofereceria quando o lance dele for 95 e, se for maior ou igual a 100, o lance será 0 para empate, caso contrário, o lance será opponentsBid + 5. Então, eu apenas lancei opponentsBid + 5. Ficará empatado, independentemente de quem começar, e eu ganho 100 ou 95 centavos, dependendo de ter começado ou não.
  • StackTraceObfuscaterBotagirá da mesma forma que MarginalerBot.
  • EvilBotlance sempre 5, então lance opponentsBid + 5. De qualquer forma, eles perderão esses 5 centavos e ganharemos o lance de 1 USD (com um lance de 5 centavos se começarmos ou 10 lances de centavo se eles começarem).
  • MSlowBoté o mesmo que MBote, portanto, também MarginalerBot.

Deixe-me saber se você encontrar algum erro de digitação ou falha em meus contadores.

Kevin Cruijssen
fonte
1
MirrorBotchama newAuction com sua própria classe, então isso é um problema. Além disso, fico feliz em saber que as três horas que passei no MimicBot não foram em vão.
Nissa
@StephenLeppik Removido o código no newAuctionporque falharia com mais frequência do que não .. Não posso combater MirrorBotnem pode me contrariar. Quem começa dos dois ganha 95 centavos e o outro perde 5 centavos.
Kevin Cruijssen 18/04/19
3
Santo encadeamento ternário, Batman!
Skyler #
1
Além disso, ao jogar BorkBorkBot, você não deve aumentar para 95 quando atingir 85? Caso contrário, você estará oferecendo 95 se eles começarem.
Skyler #
1
@ Freiheit eu sei. Eu apenas usei um caso adicional para retornar 0, caso desejasse alterar o padrão por qualquer motivo. Mas eu os coloquei no padrão agora (comentando-os). E sei que posso jogar tudo um pouco, mas não se trata de criar o código mais curto. Eu apenas tornei um ternário para torná-lo um pouco mais compacto, mas é isso. Só vou deixar assim por enquanto.
Kevin Cruijssen
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;
        }
    }
}

Não é possível testar isso no momento, então, deixe-me saber se está quebrado.

O objetivo é obter a maior pontuação total, portanto, não se preocupe em bater em ninguém. Basta ter as vitórias fáceis e não desperdiçar dinheiro com possíveis perdas.


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


}

Um espião que você conhece é mais valioso do que nenhum espião ...

Se alguém tentar chamar o método getBid, o BluffBot responderá com US $ 100 para induzi-lo a desistir ou apostar muito alto.

Caso contrário, veja se é possível ganhar com menos de US $ 1 e simplesmente não lance se não for.

Caim
fonte
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;
  }
}
Kevin Cruijssen
fonte
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;
    }

}

Esse bot faz tentativas mínimas de vitória, oferecendo 5 ou 10. Ele também verifica o rastreamento da pilha para ver se ele foi chamado por outro Bot e depois mente para eles sobre quais lances ele fará.

Winston Ewert
fonte
Se importa se eu isPeekingentrar AbstractAnalystCounterBot?
N20
1
@StephenLeppik, bem, eu roubei de MBot ...
Winston Ewert
1
Bem, MBot provavelmente roubou de mim ...
Nissa
2

One Extra

import net.ramenchef.dollarauction.DollarBidder;

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

Oferece 6 a mais do que o último lance, apenas porque ele pode.

MegaTom
fonte
Ele não pode oferecer 6 uma vez que todas as propostas devem ser múltiplos de 5 ...
Neil
@Neil é provavelmente um erro de digitação ...
Stan Strum
@Neil, as regras especificam especificamente: "Seu lance não precisa ser múltiplo de 5 ¢"
MegaTom 25/04/19
@MegaTom Huh, bem que foi adicionado desde a última vez ler as regras ...
Neil
@ Neil Fazia parte das regras originais, mas eu o adicionei porque não era muito óbvio.
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;
    }
}

Este bot ri de tentativas de detectar reflexão através do rastreamento de pilha. A coisa mais próxima que eles vêem DollarBidderé uma classe lambda que ele criou. Claramente, nenhum outro bot tentando refleti-los. Mal sabem eles que a classe lambda está realmente trabalhando para a DollarBidder. Além disso, ele age assim MarginalerBot.

RamenChef
fonte
Observe que, desde então, atualizei minha verificação de rastreamento de pilha para lidar com isso.
Nissa20
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;
}
}

Este tenta forçar o bot do oponente a pagar em excesso, definindo o cache inteiro como um valor acima do limite de US $ 100.

Insanidade
fonte
2
O gerente de segurança pararia isso.
Nissa
2
E isso não funcionaria de qualquer maneira, já que em nenhum lugar do corredor encaixava seus números inteiros.
Nissa
Mesmo que isso não seja interrompido, é uma jogada idiota, embora válida. "Sabotar outros bots é permitido, mas tentar alterar a visibilidade do campo / método resultará em misteriosas exceções de segurança."
NoOneIsHere 18/04/19
1
@StephenLeppik O objetivo disso é quebrar as coisas return opponentsBid <= 195 ? opponentsBid + 5 : 0e torná-las return opponentsBid <= 100001 ? opponentsBid + 100001 : 100001.
NoOneIsHere 18/04/19
1
Falha ao compilar devido a exceções não verificadas.
N20 de
1

ImprovedAnalystBot (não concorrente)

Muitas pessoas parecem estar usando o AnalystBotcódigo como modelo, mesmo que seja um código deliberadamente ruim. Então, eu estou fazendo um modelo melhor.

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;
        }
    }
}
RamenChef
fonte
Por que não apenas editar seu desafio?
181118 Nathan Merrill
@NathanMerrill Como eu o edito?
RamenChef 18/04/19
Clicando no botão editar e substituindo o AnalystBot por esse código?
Nathan Merrill
O @NathanMerrill AnalystBoté um código deliberadamente incorreto, para que possa demonstrar a AnalystKillersabotagem.
RamenChef 18/04/19
1
O AnalystKiller ainda trabalha com o aprimorado :) O problema de publicar um post é que o desafio é muito mais visível do que uma resposta.
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;
    }
}

MarginalerBot ligeiramente refinado

  • seja cruel com aqueles que não querem ver você
  • permita pagar 100 para obter 100 e empate, apenas para negar aos outros dinheiro fácil
mleko
fonte
Você não pode declarar nextBidjogar ClassCastException.
RamenChef 19/04/19
@RamenChef ok, trocou-o para RuntimeException que não requerem declaração :)
mleko
Seu código para verificação de rastreamento de pilha é suspeito e semelhante ao meu.
Nissa
@StephenLeppik provavelmente é cópia dele
mleko
@mleko por que embora? A classe da qual é copiada é uma superclasse abstrata que é livre para usar.
Nissa
1

Não concorrente: 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(":(");
            }
        }
    }
}

Mesma lógica do MBot, basta usar timeout em vez de Exception ao lutar contra o inimigo. Até agora, ninguém está defendendo novamente o tempo limite, portanto deve ser eficaz

mleko
fonte
As regras declaradas proíbem deliberadamente o tempo limite de outro bot.
Winston Ewert
@WinstonEwert você pode citar? Não consigo encontrar uma regra que não
permita
"Sabotar outros bots é permitido, mas tentar alterar a visibilidade do campo / método resultará em SecurityExceptions misteriosas. Uma exceção está fazendo com que outro bot ultrapasse o limite de 500 ms." Além disso, estou me defendendo contra o tempo limite.
RamenChef 20/04/19
@RamenChef, mas isso não altera a visibilidade de outros elementos. Não sei se entendi corretamente. É permitido provocar um tempo limite?
mleko
"Uma exceção está causando outro bot ultrapassar o limite de 500ms." Especificamente, isso é uma exceção à regra sobre sabotagem.
RamenChef