Subasta de billetes de dólar

32

Este es un KOTH desafío para el juego de subasta de billetes de un dólar en teoría de juegos. En él, se vende un dólar al mejor postor. Las ofertas aumentan en incrementos de 5 ¢, y el perdedor también paga su oferta. La idea es que ambos jugadores intensifiquen la guerra de ofertas mucho más allá del valor de un dólar para reducir sus pérdidas.

Esperemos que tus bots sean más inteligentes que eso.

Creará un bot para jugar este juego ampliando la net.ramenchef.dollarauction.DollarBidderclase. Debe implementar el nextBidmétodo que devuelve la próxima oferta de su bot dada la oferta anterior del otro bot. Si es necesario, también puede usar el newAuctionmétodo para reiniciar para cada subasta con la clase del bot del 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);
}

La licitación continúa hasta que ocurre uno de los siguientes:

  • nextBidlanza una excepción. Si esto sucede, el bot que lanzó la excepción paga su oferta anterior, y el otro bot obtiene el dólar gratis.
  • Cualquiera de los bot no paga lo suficiente para superar la oferta anterior. Si esto sucede, ambos robots pagan sus ofertas (el perdedor paga su oferta anterior) y el ganador recibe un dólar.
  • Ambos bots ofertaron más de $ 100. Si esto sucede, ambos bots pagan $ 100 y ninguno de ellos recibe el dólar.

Se realizan 2 subastas por cada combinación de bots. Los bots se puntúan según el beneficio total que obtuvieron en esas subastas. El puntaje más alto gana.

Ejemplos

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

No use esto como plantilla para bots con mentalidad analítica; utilizar ImprovedAnalystBoten su lugar.

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

Reglas Adicionales

  • Las lagunas estándar están prohibidas.
  • Se permite sabotear otros bots, pero intentar alterar la visibilidad del campo / método dará como resultado misteriosos SecurityExceptions. Una excepción está causando que otro bot rompa el límite de 500 ms.
  • Los bots no pueden acceder al paquete del corredor excepto para extender la DollarBidderclase.
  • Todos los métodos deben regresar en 500 ms o menos.
  • Los bots no necesitan ser deterministas.
  • Su oferta no necesita ser un múltiplo de 5 ¢.
  • $ 1 = 100 ¢
  • Los resultados se publicarán el 24 de abril de 2018.

Runner en GitHub

Resultados

Ver las rondas individuales aquí.

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

¡Felicitaciones a MTargetedBotcon una ganancia de $ 14.30!

RamenChef
fuente
11
Este desafío es fundamentalmente vulnerable a One-Upping. Como conozco la clase de mi oponente, es fácil elegir la mejor estrategia contra ella. (Luego aparece alguien y puede cambiar mi bot , etc.)
Nathan Merrill
2
" Las ofertas aumentan en incrementos de 5 ¢ ". Sin embargo, no tiene nada en su código para validar esto ... LuckyDiceBotpor ejemplo, ofertas en incrementos 2-12aleatorios ...
Kevin Cruijssen
44
Además: ¿qué pasa si mi bot hace que otros bots excedan la restricción de 500 ms?
Nathan Merrill
44
@RamenChef Estamos hablando de código malicioso aquí. ¿Qué sucede si detecto cuando otro bot me está llamando y llamo Thread.sleep (1000)?
Nathan Merrill
3
Soy VTC, ya que no está claro qué sabotaje está permitido y qué no. El OP ha rechazado las presentaciones que "atacan al corredor" (que es vago), y no hay una línea clara entre el código malicioso que está permitido y el código malicioso que no lo está (¿Cómo se determina qué bot causó que un bot demore demasiado? ?)
Nathan Merrill

Respuestas:

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);
    }
}
  • Basado en MBot actualizado
  • Utiliza un método similar como CounterBot, pero con algunos métodos refinados para golpear más fuerte a algunos de sus oponentes, también debería ser más legible
  • En oponente desconocido, el valor predeterminado es MBot strat
mleko
fuente
1
Esto no es justo.
Joshua
@Joshua ¿Qué es lo que particularmente no es justo acerca de esta solución en su opinión?
mleko
Conocer los nombres de tus oponentes.
Joshua
@Joshua la mitad de las soluciones usan esa información. Incluso le escribimos al autor que esto debería cambiarse o que ocurrirá One-Upping, se negó a cambiar el desafío, así que aquí está
mleko el
1
Ya lo hice ...
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;
        }
    }
}

Santa vaca Esperaba que esto fuera simple de escribir, luego pasé 3 horas en ello.

En esencia, MimicBotmantiene una lista actualizada de los bots disponibles. Cuando va a una nueva subasta, recorre la lista en busca de la más efectiva contra el oponente actual. Luego usa ese bot como "referencia" en la subasta.

Para fines de prueba, sería mejor usar un subconjunto aleatorio de los envíos o el conjunto completo. Comienza con GreedyBot, MimicBoty un bot más que solo ofrece 5 ¢.

Nissa
fuente
11

InsiderTradingBot

En el espíritu de la respuesta de @ StephenLeppik, InsiderTradingBot conoce a todos sus oponentes y comprende sus estrategias. Tu movimiento, 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
fuente
1
No, el uso de información privilegiada sería si el RichJerkbot hiciera una excepción específica para su bot y oferte $ 0 por él.
Nissa
Es demasiado temprano para optimizar con otras respuestas. Además, es AnalystBot, no Analyst.
RamenChef
8
Probablemente debe haber una regla "los nombres de las clases serán aleatorizados".
user202729
1
@ user202729 ¿Qué tal "no hay referencias directas a clases"?
RamenChef
1
Me gustaría ver esto manejar el MimicBot.
Nissa
8

MirrorBot

Hace que el enemigo juegue contra sí mismo.

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
fuente
66
Te bombardeaste Analystespectacularmente.
Silvio Mayolo
@SilvioMayolo ¿Cómo?
dkudriavtsev
Mirror intenta emular a Analyst jugando contra sí mismo, lo que resulta en un desbordamiento de pila.
Silvio Mayolo
8

Editar : los cambios dirigidos en la clase DollarBidder han roto 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;
  }
}

Después de 1 subasta, su puntaje será -2147483645, pero la próxima vez perderá 5 ¢ o 105 ¢, lo que hará que el puntaje sea positivo y muy grande. Todas las demás pérdidas serían entonces insignificantes.

En la primera subasta, también haría que GreedyBot apueste -2147483646, que no es divisible por 5.

Invierno
fuente
scoreestá protegido por paquete. Tus bots no pueden acceder a él.
RamenChef
@RamenChef Oops, eliminó el CheatingBot
Invierno
No hay una regla en contra de "atacar al corredor", solo "acceder" a él, lo que no hace. Recomiendo corregir el error, que resuelve el 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;
        }
    }
}

No puedo probar esto en este momento, así que avíseme si está roto.

Básicamente, elige un valor para el dólar y supera al oponente hasta que superemos ese valor.

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

Muy simple, trata de determinar si un oponente disputaría una oferta mínima y, si no, la coloca.

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

Una nueva versión más inteligente de MarginalBot que comprueba si puede hacer algún movimiento para ganar dinero sin competencia, en lugar de solo esperar ganar con el mínimo.

Como está en la misma familia que mi bot anterior, pero esquiva las estrategias que intentan superarlo, pensé que una nueva entrada en la misma publicación era la forma más razonable de presentarlo.

Edición 1: Se realizó un pequeño cambio en el nuevo método de Subasta para optimizar contra otros bots de tipo analizador.

Edición 2: realizó un cambio en MarginalerBot para minimizar las pérdidas contra estrategias furtivas o no deterministas.

Qaghan
fuente
Bienvenido a PPCG!
Martin Ender
1
¡Es simple, pero supera a todos los otros bots por un margen bastante grande!
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);
  }
}

Se rinde si no puede alcanzar el equilibrio.

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

Que se tenía que hacer.

Neil
fuente
" Las ofertas aumentan en incrementos de 5 ¢ ". Su bot actualmente no lo está haciendo.
Kevin Cruijssen
1
@KevinCruijssen Bastante justo. También cambié el límite superior para que pudiera ofertar el total de $ 1, por si acaso,
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;
    }
}

Intenta persuadir a los bots de mentalidad analítica de que el único movimiento ganador es no jugar.

histocrat
fuente
1
Me di cuenta de que mi comentario algo críptico "¿Joshua? ¿Eres tú?" ha sido eliminado. Entonces, para aclarar, fue una referencia a una famosa cita de la película WarGames: "el único movimiento ganador es no jugar" . (Joshua es el apodo de la WOPR .)
Arnauld
5

LuckyDiceBot

LuckyDiceBot solo confía en sus dados. Lanza dos dados, agrega la suma al valor del postor actual y ofrece esa cantidad. Si no es suficiente para superar la oferta del oponente, corta sus pérdidas y sigue su camino.

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
fuente
2
¿Cómo reduce esto sus pérdidas o detiene las pérdidas? Si siempre agrega su tirada de dados a la oferta de los oponentes, siempre ofertarás más. La aleatoriedad podría confundir a un bot suficientemente analítico, me gusta el concepto.
Freiheit
Si el resultado es 4 o menos (estadísticamente improbable, pero eventualmente sucederá), entonces la oferta es insuficiente para vencer al oponente y la subasta termina.
Silvio Mayolo
Dos cosas: 1. @Freiheit tiene razón y este bot seguirá haciendo ofertas hasta que haya ganado, no importa cuán alto sea. opponentsBidin nextBid(int opponentsBid)contiene la oferta total que su oponente ha ofertado hasta ahora, no su próxima oferta. Un mejor término para el método sería raise(como el término de Poker) en mi humilde opinión. 2. Su bot no muerde en incrementos de 5, por lo que está validando una de las reglas. Si se solucionan estos problemas, todavía me gusta el concepto, porque los bots analíticos no podrán contrarrestar y, por lo tanto, lo más probable es que ganes con bastante frecuencia.
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 hace una fortuna con su juego ilegal con LuckyDiceBot. Entonces, por supuesto, cuando llega la policía (DeterrentBot), tiene que deshacerse rápidamente de sus ganancias de alguna manera, como ofertar en la próxima subasta.

Nissa
fuente
4

InflaciónBot

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

No puedo probar esto en este momento, así que avíseme si está roto.

Cada ronda, el valor del dólar sube.


fuente
Esto sería excelente contra MirrorBot, MarginalerBot y probablemente también MimicBot.
Nissa
@StephenLeppik Eso es lo que estaba pensando cuando lo hice. Sin embargo, todavía hay muchas debilidades.
+1, me gusta la idea. Hmm, ¿se pretende que tu bot haga una oferta de 0 y se rompa incluso si comienza una ronda (cuando opponentsBidtodavía es 0)?
Kevin Cruijssen
@KevinCruijssen Sí. Eso solo puede suceder contra el primer oponente. Cualquiera de los bots que lo copien comenzará en 0, por lo que esto no desperdiciará más de 5c en ellos.
4

No competidor: 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;
        }
    }
}

Esto no pretende ser una presentación verdadera, sino más bien como una placa repetitiva para que otros la utilicen para disuadir a los robots de mascotas como MirrorBoty MimicBot.

Como es el constructor predeterminado, no hay necesidad de llamarlo en su subclase. Implementa un isPeekingmétodo para determinar si otro bot está husmeando.

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

Escenarios

  • Si el oponente puede comenzar y puja <= 0, pierde.
  • Si el oponente puede comenzar y [5,95]pujar: oferte 100 usted mismo. O bien tu oponente se detiene ahora, o pujará por encima de 100 en total, en cuyo caso dejas de pujar para dejar que ganen y te saldes bien.
  • Si el oponente puede comenzar y >= 100puja: apuesta 0 a ti mismo para perder pero llega a un punto de equilibrio.
  • Si puede comenzar: oferte 100 de inmediato. O bien tu oponente se detiene ahora, o pujará por encima de 100, en cuyo caso dejas de pujar para dejar que ganen y te saldes bien.
Kevin Cruijssen
fuente
Wow eso es un error. Decía que estaba comentando sobre la pregunta, pero terminó aquí. Tengo que encontrar una manera de reproducirlo
Stan Strum
@RamenChef Typo .. Pero he modificado todo el bot. Tenía algunos errores de todos modos ..
Kevin Cruijssen
44
Esto puede perder dinero absolutamente. Si pujas 100, tu oponente apuesta 105, terminas perdiendo 100 y solo pierden 5.
@Mnemonic Ah, por supuesto ... No había pensado en esa parte ... Hmm ... eso hace las cosas más interesantes pero también más difíciles. Editará la descripción por ahora, pero deja el bot como está.
Kevin Cruijssen
1
Creo que te refieres a "perder", no "perder". Perder es lo contrario de ganar. Loose es lo opuesto a tight.
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;
    }

}

Lanza un error en lugar de una excepción para confundir a los analistas.

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

Intenta evaluar al oponente con el que se enfrenta y asegúrate de no morder más de lo que puede masticar.

ripkoops
fuente
1
Bienvenido a PPCG!
Alion
3

Analista Optimizador

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

empedrado de partes de otros bots. este se juega tratando de ser AnalystBot, y si no tiene éxito, se convierte en BorkBorkBot.

No creo que este lo haga bien.

dkudriavtsev
fuente
Cuidado con el AnalystKiller.
RamenChef
@RamenChef AFAIK, el asesino analista solo lanza una excepción si se ve a sí mismo siendo analizado. Puedo
entender
1
Probablemente deberías atraparlo.
RamenChef
@RamenChef No tengo idea de si eso funcionará, no puedo 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:

  • DarthVaderse contrarresta causando un SecurityExceptionantes de que comience la licitación, pero ofertaré 5 por si acaso.
  • AnalystBoty AnalystOptimizerambos verán mi respuesta cuando oferte 95, en cuyo caso mostraré que oferto 100 para que oferte 95. Sin embargo, ofertaré 5 si empiezo (o 100 si han comenzado), por lo que pierden 95 centavos y gano el billete de 1 USD al ofertar solo 5 centavos o al llegar al punto de equilibrio.
  • MirrorBotofertaré lo que yo ofertaría contra él. Entonces apostaré 5, y quien comience gana 95 centavos, y el otro pierde 5 centavos.
  • MarginalBot ofertaré 5 si oferte menos de 10 (o lo que comienza), de lo contrario, ofertará 0. Entonces, si solo oferto 5 cuando comienzo, o 10 cuando comienza, gano 95 o 90 centavos, y pierden 5 centavos
  • GreedyBot siempre ofrece 5 más que yo, así que solo apuesta 0 para alcanzar el punto de equilibrio y déjalos ganar
  • OnlyWinningMovey AnalystKillerambos siempre ofertan 0, así que solo apuesta 5 para ganar
  • TargetValueBotofertará en el rango [100,200], así que oferte 5 más cada vez hasta que estén en 190, en cuyo caso aumentaremos a 200 para alcanzar el punto de equilibrio al ganar el dólar (y dejarlos perder 190 o 195 dependiendo de quién comenzó)
  • BorkBorkBotofertará en el rango [5,95], así que oferte 5 más cada vez también. Tan pronto como oferten 85 o 90 (dependiendo de quién comenzó), oferte 95 usted mismo. Perderán 85 o 90 centavos, y usted gana la factura de 1 USD por una ganancia de 5 centavos.
  • DeterrentBot ofertará 5 si comienzan o 100 si comenzamos, así que solo oferte 105 para que contrarresten con 100, haciendo que pierdan 100 y que nosotros perdamos solo 5 centavos al ganar la factura de 1 USD.
  • BreakEvenAsapofertará 100 de inmediato. Entonces, si comenzaron con su oferta de 100, contrarresten con 105 para ganar 95 centavos y dejen que pierdan 100. Si podemos comenzar, solo oferte 100 para que ambos lleguemos a un punto de equilibrio.
  • RichJerk ofertará 10,001 de inmediato, así que solo haga una oferta de 0 para alcanzar el punto de equilibrio y déjelos perder 9,901.
  • DeterredBot no me conoce y por lo tanto ofertará 0, así que solo apuesta 5 para ganar.
  • LuckyDiceBotsigue pujando hasta que gana. Entonces, si comenzamos, oferte 5 con la esperanza de que oferten lo más alto posible para ganar el dólar. Si han comenzado, solo haga una oferta de 0 para permitirles ganar y alcanzar el punto de equilibrio.
  • RandBotofertará al azar en el rango [5,100], así que solo apuesta 5 más hasta que se detenga, en cuyo caso has ganado 95 centavos y han perdido 0-100.
  • UpTo200(como su nombre lo indica) ofertará hasta 200. Así que solo oferte 5 más hasta que se detengan. Ganaremos el billete de 1 USD y tomaremos una pérdida total de 105 centavos, sin embargo, pierden 200 centavos.
  • InsiderTradingBot no me conoce, así que solo apuesta 5 centavos para ganar
  • MimicBotFue lo más difícil. Simplemente ofrezca 10 para comenzar o contrarrestar su primera oferta de 5. Si intentan acceder a mí, lanzaré una RuntimeException (que atraparán, en cuyo caso actuaría como si hubiera ofrecido 100 en su lugar, aunque romperá el bucle while interno). Según los enemigos que tiene en su HashSet, sucede algo diferente. Tendré que volver a visitar y mirar más de cerca para ver si hay un contador real.
  • RiskRewardBot no me conoce, así que solo pujaré 5, en cuyo caso apostaré 5 para ganar.
  • MarginalerBotmordirá hasta 100 dependiendo de lo que ofertaría. Si puedo comenzar, ofertaré 90, luego ofertaré 95, luego ofertaré 100 para que oferte 0 y pierda 95 centavos, mientras gano el billete de 1 USD y alcanzo el punto de equilibrio. Si puede comenzar en su lugar, ve que ofertaría 90 contra él, por lo que se oferta 90 por sí mismo, luego ofertaré 95 para que oferte 0 y pierda 90 centavos, mientras gano el billete de 1 USD con una ganancia de 5 centavos.
  • BuzzardBotanalizará todos mis contadores en el rango [0,100). Si hago una oferta de 100inmediato, usará oppFlag = 0y la matriz completa de tamaño 100 contendrá 100 veces el valor 100. En el interruptor case 0, el bucle volverá a estar en el rango [0,100), y como i + 5máximo será 104, el if bids[i] < i + 5nunca será cierto , por lo que la puja que sigue siendo 0
  • ImprovedAnalystBotsiempre lo tendrá this.enemy = nullporque su oponente es CounterBot, no él mismo. Por lo tanto, siempre ofertará 0, que acabo de contrarrestar con una oferta de 5.
  • InflationBot ofertará 0 para alcanzar el punto de equilibrio cuando comience, de lo contrario seguirá haciendo una oferta 5. Así que solo hagamos una oferta 0 para alcanzar el punto de equilibrio de inmediato y dejar que ganen.
  • ScoreOverflowBotofertarán cerca Integer.MAX_VALUEsi pueden comenzar, de lo contrario, ofertarán 105. Entonces, si han ofertado 105, solo oferte 110 nosotros mismos (perderán 105, nosotros perderemos 10), de lo contrario, solo haga una oferta 0 para que puedan ganar.
  • MBotes lo mismo que MarginalerBot, pero con una protección adicional contra oponentes "asomados". Como no me asomo, es básicamente lo mismo que MarginalerBot.
  • SecretBottendrá su isPeeking()método devuelto falso, por lo que si puede comenzar o si oferto 5, ofertará 5 o 10 respectivamente. De lo contrario, ofertará 0. Entonces, ya sea que empiece o no, opponentsBid + 5me haría ganar de cualquier manera, ya sea con mi oferta de 10 centavos o 15 centavos, haciendo que pierdan 5 o 10 centavos.
  • BluffBotanalizará lo que ofertaría cuando su oferta sea 95, y si es mayor o igual a 100, ofrecerá 0 para alcanzar el punto de equilibrio, de lo contrario, ofertará opponentsBid + 5. Así que solo pujaré opponentsBid + 5. Se igualará independientemente de quién comience, y gano 100 o 95 centavos dependiendo de si he comenzado o no.
  • StackTraceObfuscaterBotactuará igual que MarginalerBot.
  • EvilBotsiempre ofertará 5, así que solo oferte opponentsBid + 5. De cualquier manera, perderán esos 5 centavos, y ganaremos la oferta de 1 USD (ya sea con una oferta de 5 centavos si hemos comenzado, o una oferta de 10 centavos si han comenzado).
  • MSlowBotes lo mismo MBoty por lo tanto también MarginalerBot.

Avísame si ves algún error o error tipográfico en mis contadores.

Kevin Cruijssen
fuente
1
MirrorBotllama a newAuction con tu propia clase, así que eso es un problema. Además, me alegra saber que las 3 horas que pasé en MimicBot no fueron en vano.
Nissa
@StephenLeppik Eliminó el código en el newAuctionporque fallaría la mayoría de las veces ... No puedo contrarrestarme MirrorBotni puede contrarrestarme. Quien comience de los dos gana 95 centavos y el otro pierde 5 centavos.
Kevin Cruijssen
3
¡Santo encadenamiento ternario, Batman!
Skyler
1
Además, cuando juegas BorkBorkBot, ¿no deberías subir a 95 cuando llegan a 85? De lo contrario, ambos están ofertando 95 si comienzan.
Skyler
1
@Freiheit lo sé. Solo utilicé un caso adicional para devolver 0 en caso de que quisiera cambiar el valor predeterminado por cualquier razón. Pero ahora los puse por defecto (al comentarlos). Y sé que puedo jugar golf todo un poco, pero no se trata de hacer el código más corto. Acabo de hacer un ternario para hacerlo un poco más compacto, pero eso es todo. Lo dejaré así por ahora.
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;
        }
    }
}

No puedo probar esto en este momento, así que avíseme si está roto.

El objetivo es obtener el puntaje total más alto, así que no te preocupes por vencer a nadie. Simplemente tome las victorias fáciles y no desperdicie dinero en posibles pérdidas.


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


}

Un espía que conoces es más valioso que ningún espía ...

Si alguien más intenta llamar al método getBid, BluffBot responde con $ 100 para engañarlos para que abandonen o apuesten realmente alto.

De lo contrario, vea si es posible ganar por menos de $ 1, y no haga una oferta si no es así.

Caín
fuente
2

Más de 200

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

}

Este bot hace intentos mínimos de ganar al hacer una oferta de 5 o 10. También comprueba el seguimiento de la pila para ver si otro Bot lo llamó y luego les miente sobre las ofertas que hará.

Winston Ewert
fuente
¿Le importa si puerto de E isPeekingen AbstractAnalystCounterBot?
Nissa
1
@StephenLeppik, bueno, lo robé de MBot ...
Winston Ewert
1
Bueno, MBot probablemente me lo robó ...
Nissa
2

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

Ofrece 6 más que la última oferta, solo porque puede.

MegaTom
fuente
No puede ofertar 6 ya que todas las ofertas deben ser múltiplos de 5 ...
Neil
@Neil es probablemente un error tipográfico ...
Stan Strum
@Neil las reglas establecen específicamente: "No es necesario que su oferta sea un múltiplo de 5 ¢"
MegaTom
@MegaTom Huh, bueno, eso se agregó desde la última vez que leí las reglas ...
Neil
@Neil Era parte de las reglas originales, pero lo agregué allí porque no era muy obvio.
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 se ríe de los intentos de detectar el reflejo a través del seguimiento de la pila. Lo más parecido que ven a una DollarBidderclase lambda que creó. Claramente, no otro robot tratando de reflejarlos. Poco saben que esa clase lambda en realidad está funcionando para a DollarBidder. Más allá de eso, él actúa como MarginalerBot.

RamenChef
fuente
Tenga en cuenta que desde entonces he actualizado mi comprobación de seguimiento de pila para manejar esto.
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;
}
}

Este intenta forzar al bot del oponente a pagar de más al establecer la memoria caché entera al valor por encima del límite de $ 100.

Locura
fuente
2
El gerente de seguridad detendría esto.
Nissa
2
Y esto no funcionaría de todos modos ya que en ninguna parte del corredor encajona sus enteros.
Nissa
Incluso si esto no se detendría, este es un movimiento brusco, aunque válido. "Está permitido sabotear otros bots, pero intentar alterar la visibilidad del campo / método dará como resultado misteriosas excepciones de seguridad".
NoOneIsHere
1
@StephenLeppik El objetivo de esto es romper cosas como return opponentsBid <= 195 ? opponentsBid + 5 : 0y hacerlo return opponentsBid <= 100001 ? opponentsBid + 100001 : 100001.
NoOneIsHere
1
No se puede compilar debido a excepciones no marcadas.
Nissa
1

ImprovedAnalystBot (no competidor)

Muchas personas parecen estar usando el AnalystBotcódigo como plantilla, a pesar de que es un código deliberadamente malo. Entonces estoy haciendo una mejor plantilla.

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
fuente
¿Por qué no solo editas tu desafío?
Nathan Merrill
@NathanMerrill ¿Cómo lo editaría?
RamenChef
Al hacer clic en el botón editar y reemplazar AnalystBot con este código?
Nathan Merrill
@NathanMerrill AnalystBotes un código deliberadamente malo para que pueda demostrar el AnalystKillersabotaje.
RamenChef
1
El AnalystKiller todavía funciona con el mejorado :) El problema al publicarlo es que el desafío es mucho más visible que una respuesta.
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 ligeramente refinado

  • Sé cruel con aquellos que no quieren controlarte
  • permitir pagar 100 para obtener 100 y llegar a un punto de equilibrio, solo para negarle dinero fácil a otros
mleko
fuente
No puedes declarar nextBidtirar ClassCastException.
RamenChef
@RamenChef ok, lo cambié a RuntimeException que no requiere declaración :)
mleko
Su código para la comprobación del seguimiento de la pila se parece sospechosamente similar al mío.
Nissa
@StephenLeppik probablemente es una copia de él
mleko
@mleko, ¿por qué? La clase de la que se copia es una superclase abstracta que es de uso gratuito.
Nissa
1

No competidor: 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(":(");
            }
        }
    }
}

La misma lógica que MBot, solo usa el tiempo de espera en lugar de la excepción al luchar contra el enemigo. Hasta ahora nadie está defendiendo contra el tiempo de espera, por lo que debería ser efectivo

mleko
fuente
Las reglas establecidas prohíben causar deliberadamente que otro bot se agote.
Winston Ewert
@ WinstonEwert, ¿puedes citar? No puedo encontrar una regla que no
permita
"Está permitido sabotear otros bots, pero intentar alterar la visibilidad del campo / método dará como resultado misteriosas excepciones de seguridad. Una excepción está causando que otro bot rompa el límite de 500 ms". Además, me defiendo contra el tiempo de espera.
RamenChef
@RamenChef pero esto no altera la visibilidad de otros elementos. No estoy seguro si te entiendo correctamente. ¿Está permitido provocar un tiempo de espera?
mleko
"Una excepción está causando que otro bot rompa el límite de 500 ms". Específicamente, esta es una excepción a la regla sobre sabotaje.
RamenChef