Morra, el noble juego de los reyes


28

Fondo

El juego de Morra es un juego simple. En la versión "original", varios jugadores tiran simultáneamente un número 0-5 con sus manos mientras adivinan la suma total de las manos de todos. La versión que usaré aquí se ha modificado para aumentar el potencial de una estrategia no trivial, y se describe a continuación:

  • Hay dos jugadores
  • Como en piedra, papel o tijera, los jugadores se mueven simultáneamente.
  • Cada turno, cada jugador elige un número 0-5 y también adivina la elección de sus oponentes de 0-5. Esto significa que se emiten dos números cada turno. Para aclarar, la salida de ambos números debe estar en el rango 0-5, inclusive.
  • Si adivina correctamente la elección de su oponente pero su oponente no adivinó correctamente, gana un cierto número de puntos igual a la suma de los dos números jugados. Por ejemplo, si los números jugados fueron 3 y 5, una suposición correcta valdría 8 puntos.
  • Si ambos o ninguno de los jugadores adivinan correctamente, no se otorgan puntos.
  • La persona con más puntos después de 1000 rondas gana ese juego.

El torneo

El torneo se realizará en un estilo de todos contra todos y se realizará creando cada posible pareja de concursantes. Por cada victoria, el concursante gana 2 puntos de victoria. Cada empate resulta en 1 punto de victoria. No se ganan puntos de victoria por una pérdida.

Intuitivamente, el ganador del torneo será el concursante con más puntos de victoria contra otros.


Cómo entrar

Habrá dos métodos para enviar bots para competir. El primer método, y muy preferido, es implementar una interfaz Java provista por el controlador. El segundo método es escribir un programa independiente.

Cubramos primero el método Java. La interfaz que tendrá que aplicar es Playery define dos métodos: public String getName()identifica su bot, y public int[] getMove(String[] args)toma argscomo una matriz de seis cuerdas, mychoices myguesses myscore opponentchoices opponentguesses opponentscore. Un ejemplo es el siguiente:

042 045 0 324 432 6

Esto significa que elegí 0 en la primera ronda y supuse que mi oponente iba a lanzar un 0. Mi oponente lanzó un 3 y supuso que lanzaría un 4. En la tercera ronda, mi oponente hizo la suposición correcta de que lanzaría un 2, lo que significa que gana 2 + 4 = 6 puntos.

Su método devolverá una matriz de dos enteros, que son su elección y suposición, respectivamente. Un ejemplo es {4,2}para una elección de 4 y una suposición de 2.

Aquí hay un ejemplo de un bot Java completo escrito como método. Si lo desea, su envío solo debe incluir lo que está pasando en el getMovemétodo.

import java.util.Random;
/**
 * A simple example Morra bot to get you started.
 */
public class ExampleBot implements Player
{
    public String getName()
    {
        return "ExampleBot";
    }

    public int[] getMove(String [] args)
    {
        //easiest way I know to break down to create a move history
        //(just contains their throw history)
        char[] theirThrowsC = args[3].toCharArray();
        int[] theirThrows = new int[theirThrowsC.length];
        for(int i = 0; i < theirThrowsC.length; i++)
        {
            theirThrows[i] = Integer.parseInt(Character.toString(theirThrowsC[i]));
        }

        //get my score
        int myScore = Integer.parseInt(args[2]);

        Random r = new Random();
        int guess = r.nextInt(6);
        if(theirThrows.length > 0)
        {
            guess = theirThrows[theirThrows.length-1];
        }

        //throws a random number, guesses what they threw last
        return new int[] {r.nextInt(6),guess}; 
    }

    public static int otherMethod(int example) //you can write additional static methods
    {
        return 0;
    }
}

Como un programa independiente

Actualmente estoy limitado en mi soporte de idiomas adicionales. Además de Java, puedo aceptar programas escritos en Python 3.4, Perl 5 o Ruby 2.1.5. Si hay un idioma que varias personas parecen desear, haré todo lo posible para agregarlo.

La entrada a su programa serán argumentos en la línea de comando. Podría verse así:

perl awesomebot.plx 042 045 0 324 432 6

La salida de su programa debe ser su elección seguida de su conjetura, cada uno seguido de espacios en blanco.

Incluya en su respuesta el comando exacto necesario para ejecutarlo. Tenga en cuenta que estoy ejecutando Windows 8.1.


Reglas extra

Ahorro de estado y tiempos de espera

Su programa podrá crear un archivo de texto en el directorio local, donde puede almacenar información. Esta información se mantendrá durante todo el torneo pero se eliminará después. Dale al archivo un nombre que pueda identificar.

Hay un límite de tiempo de 500 milisegundos para que su código responda. No responder en el límite de tiempo (o dar un movimiento no válido) resultará en la pérdida de ese partido en particular. Los envíos de Java actualmente tienen un tiempo de espera pasivo (que puedo actualizar a activo), mientras que los envíos que no son de Java tienen un tiempo de espera activo donde su proceso finaliza después de 500 milisegundos.

Más reglas de presentación

  • Se le permiten múltiples envíos, siempre que cumplan con las reglas y no etiqueten al equipo.
  • Cada entrada debe ser única. No puedes hacer una copia exacta de la lógica de otro bot en un idioma diferente.
  • Los bots no pueden interactuar entre sí (para formar un equipo de cualquier tipo).
  • No puede usar la lógica de los otros bots dentro de su bot para, por ejemplo, identificar a su competidor y predecir sus acciones. Por supuesto, puedes tratar de determinar la estrategia de tu oponente.
  • No intente meterse con el controlador, otros concursantes o mi computadora. No se conecte a fuentes de información externas.

El controlador

La versión actual del controlador se encuentra aquí . Está escrito en Java 8. El archivo "Torneo" es el controlador principal, que también contiene la lista de competidores (si desea organizar sus propias competiciones).


Tabla de clasificación

Realmente no he podido actualizar la tabla de clasificación con mucha frecuencia. Estoy bastante ocupado este fin de semana. Por "bastante ocupado" quiero decir que no hay acceso a una computadora de 6:30 a.m. a 9:30 p.m. Aquí están los puntajes después de 5 carreras. El bot "Echo" siguió perdiendo por alguna razón (podría ser mi culpa, aún no lo he investigado).

  170 - Quinn and Valor                         
  158 - Historian                               
  142 - DeltaMax                                
  140 - MorraCowbell                            
  132 - Extrapolator                            
  115 - Rainbolt                                
  102 - Popularity                              
  100 - Interpolator                            
   83 - CounterBot                              
   80 - Basilisk                                
   76 - Erratica                                
   65 - Trendy                                  
   63 - Scholar                                 
   62 - RandomGuesser                           
   60 - KingFisher                              
   59 - NullifierBot                            
   55 - EvolvedBot                              
   48 - Confused          

Crédito

Muchas gracias a Rainbolt y Peter Taylor por su ayuda con el controlador.


1
@ MartinBüttner Ruby 2.1.5 ha sido agregado.
PhiNotPi

¿Cómo funciona el round robin? Player1 vs Player2 1000 veces, Player1 vs Player3 1000 veces, etc ... O es Player1 vs Player2 una vez y luego player1 vs player 3 una vez, etc ...
Vajura

@Vajura Un torneo individual se compone de 1 batalla entre cada par. Una batalla tiene 1000 rondas, con el puntaje total más alto al final de la batalla que determina quién obtiene los dos puntos de victoria. El marcador actual muestra el total de puntos de victoria después de 40 torneos.
PhiNotPi

Perdón por los retrasos en la actualización del tablero. Estoy extremadamente ocupado este fin de semana. Esperar y actualizar esta noche y mañana por la mañana.
PhiNotPi

¡Guau, no esperaba que mi bot lo hiciera tan bien! Además, ¿qué significan los números por el primer conjunto de resultados ... número de victorias?
mbomb007

Respuestas:


17

Morra Cowbell

Para cualquiera que busque importancia en el nombre de este bot, el nombre Morra me hace pensar en el espacio italiano , por lo que pensé que necesitaba un nombre que jugara en eso. Otros candidatos incluidos Morra te engañaron a ti y a Morra por mí .

Esta es una clase completa que implementa la Playerinterfaz. Explicación a continuación.

import java.util.Random;

public class MorraCowbell implements Player {
    private final Random rnd = new Random();

    public String getName() {
        return "MorraCowbell";
    }

    public int[] getMove(String[] args) {
        int[] prior = new int[36];
        for (int i = 0; i < 36; i++) prior[i] = 1;
        // args: myChoices myGuesses myScore opponentChoices opponentGuesses opponentScore
        if (args.length == 6 && args[3].length() == args[4].length()) {
            for (int i = 0; i < args[3].length(); i++) prior[6*(args[3].charAt(i) - '0') + (args[4].charAt(i) - '0')]++;
        }

        int[] weights = new int[6];
        for (int r = 0; r < 6; r++) {
            for (int s = 0; s < 6; s++) {
                for (int t = 0; t < 6; t++) {
                    weights[r] += (r + s) * ((r + s == 5 ? 1 : 0) + (r == t ? -1 : 0)) * prior[s * 6 + t];
                }
            }
        }

        // Find the best window.
        int[][] magic = new int[][] {
            { 7776, 6480, 5400, 4500, 3750, 3125 }, { 3125, 2500, 2000, 1600, 1280, 1024 }, { 1875, 1500, 1200, 960,
            768, 640 }, { 1125, 900, 720, 576, 480, 400 }, { 1620, 1296, 1080, 900, 750, 625 }, { 1296, 1080, 900, 750,
            625, 500 }, { 750, 625, 500, 400, 320, 256 }, { 675, 540, 432, 360, 300, 250 }, { 648, 540, 450, 375, 300,
            250 }, { 375, 300, 250, 200, 160, 128 }, { 375, 300, 240, 200, 160, 128 }, { 450, 375, 300, 240, 192, 160,
            128 }, { 324, 270, 225, 180, 150, 125 }, { 270, 225, 180, 144, 120, 100, 80 }, { 225, 180, 150, 120, 96,
            80 }, { 225, 180, 144, 120, 96, 80 }, { 324, 270, 216, 180, 150, 125, 100, 80, 64 }, { 135, 108, 90, 72, 60,
            50 }, { 135, 108, 90, 75, 60, 50, 40, 32 }, { 108, 90, 75, 60, 48, 40, 32 }, { 54, 45, 36, 30, 25, 20, 16 },
            { 54, 45, 36, 30, 24, 20, 16 }
        };
        long bestN = 0;
        int bestD = 1, bestIdx = -1, bestA[] = null;
        for (int[] A : magic) {
            for (int i = 0; i < A.length - 5; i++) {
                long n = 0;
                int d = 0;
                for (int j = 0; j < 6; j++) {
                    n += weights[j] * A[i + j];
                    d += A[i + j];
                }
                if (n * bestD > bestN * d) {
                    bestN = n;
                    bestD = d;
                    bestIdx = i;
                    bestA = A;
                }
            }
        }

        int r = rnd.nextInt(bestD);
        for (int i = 0; i < 6; i++) {
            r -= bestA[bestIdx + i];
            if (r < 0) return new int[] { i, 5 - i };
        }

        // Just a precaution: this should be unreachable.
        return new int[] { 0, 5 };
    }
}

Explicación

Comencé analizando juegos con menos dedos. El más simple no trivial permite llamadas de 0o 1y tiene la siguiente tabla de pagos (los valores son pagos para el jugador de la fila):

       (0,0) (0,1) (1,0) (1,1)
      +-----------------------
(0,0) |  0     0    -1     0
(0,1) |  0     0     0     1
(1,0) |  1     0     0    -1
(1,1) |  0    -1     1     0

La (0,0)estrategia está dominada por (0,1), por lo que podemos reducir la tabla a

       (0,1) (1,0) (1,1)
      +-----------------
(0,1) |  0     0     1
(1,0) |  0     0    -1
(1,1) | -1     1     0

Ahora la (1,0)estrategia está dominada por (0,1), por lo que podemos reducir aún más la tabla a

       (0,1) (1,1)
      +-----------
(0,1) |  0     1
(1,1) | -1     0

Y ahora (1,1)está dominado por (0,1), así que terminamos con

       (0,1)
      +-----
(0,1) |  0  

Por lo tanto, jugar siempre (0,1)es un equilibrio de Nash. Pero lo curioso es que no es el único. Este es un juego simétrico de suma cero, por lo que el resultado esperado es 0, y cualquier combinación de estrategia mixta (0,1)y (1,0)dónde(0,1) se elija al menos el 50% del tiempo logra esa recompensa. Entonces tenemos un espacio unidimensional de equilibrios de Nash.

Parece ser el caso, aunque no lo he probado, que el ndedo Morra tiene un npolitopo tridimensional de equilibrios de Nash que son estrategias mixtas entre los n+1 (pick, guess)pares para los cuales pick + guess = n.

Los números mágicos en el código anterior codifican los 32 vértices del politopo 5-dimensional de equilibrios de Nash. Los encontré configurando una instancia de programación lineal que representaba el politopo y luego usando funciones objetivas aleatorias. La razón para codificar los 32 en lugar de elegir uno es simple: el resultado esperado es 0, por lo que necesito hacerlo mejor de lo esperado para obtener una victoria. Básicamente, supongo que el otro jugador está usando una estrategia mixta y calculo la distribución en función de su historial de selección. Luego selecciono el vértice del politopo que maximiza mi ganancia esperada contra esa distribución estimada.

QuinnAndValor demuestra la vulnerabilidad de la suposición de que el otro jugador está usando una estrategia mixta. Al detectar a un jugador que usa las estrategias de los equilibrios de Nash, puede cambiar a un modo de caminata aleatoria donde, jugando una estrategia de no equilibrio, es probable que pierda en promedio, pero solo necesita ganar una ventaja de vez en cuando puede volver a jugar pares para los cuales pick + guess = n. Por lo tanto, los equilibrios de Nash para un solo juego no se generalizan trivialmente a los equilibrios de Nash para el juego repetido, lo que permite estrategias más complejas.


44
¿Es posible que tu magia contenga una parte de los números Hamming ? Ciertamente no los contiene a todos, pero muchos (¿ o todos? ) Están en la lista de ese sitio web.
GiantTree

@GiantTree, todos son números de Hamming. Interesante observación.
Peter Taylor

No es de extrañar que tu bot vaya a ser jamón. : D
mbomb007

11

Quinn y Valor (actualizado)

Quinn y Valor son un equipo de guardabosques de élite. Con ballesta y garra, destrozan a todos los oponentes que se atreven a desafiarlos.

import java.util.ArrayList;
import java.util.List;

interface Champion extends Player {
}

/*
 * Quinn and Valor are an elite ranger team. With crossbow and claw, they ...
 */
public class QuinnAndValor implements Champion {

    private final Champion quinn = new Quinn();
    private final Champion valor = new Valor();

    private int checker;
    private int myScore, opScore;
    private boolean ulted;
    private boolean teemoDetected;
    private boolean quinnNeverLose, valorNeverLose;
    private int quinnScore, valorScore;
    private int quinnRound, valorRound;

    public QuinnAndValor() {
        checker = check() ? 0 : 1;
    }

    // Check if is a fine use
    private static boolean check() {
        return Thread.currentThread().getStackTrace()[3].getClassName().equals(
                "Tournament");
    }

    @Override
    public String getName() {
        return quinn + " and " + valor;
    }

    @Override
    public int[] getMove(String[] args) {
        // Punish for bad usage
        switch (checker) {
        case 1:
            checker++;
            return new int[] { -1, -1 };
        case 2:
            checker++;
            return null;
        case 3:
            throw new Error("Mua he he heh!");
        default:
            if (checker > 0)
                throw new Error("Mua he he heh!");
            break;
        }

        int round = args[0].length();
        if (round == 0) {
            // Buy starting items
            myScore = opScore = 0;
            teemoDetected = false;
            quinnNeverLose = valorNeverLose = true;
            quinnScore = valorScore = quinnRound = valorRound = 0;
            ((Valor) valor).reset();
        }

        if (ulted = useUltimate(args)) {
            valorRound++;
            return valor.getMove(args);
        } else {
            quinnRound++;
            return quinn.getMove(args);
        }
    }

    /*
     * Quinn's ultimate has a lengthy cool-down, especially at lower ranks, so
     * we have to use it only when needed.
     */
    private boolean useUltimate(String[] args) {
        int round = args[0].length();
        int lastMyScore = myScore;
        int lastOpScore = opScore;
        myScore = Integer.parseInt(args[2]);
        opScore = Integer.parseInt(args[5]);
        int score = (myScore - opScore) - (lastMyScore - lastOpScore);
        if (ulted) {
            valorScore += score;
            valorNeverLose &= score >= 0;
        } else {
            quinnScore += score;
            quinnNeverLose &= score >= 0;
        }

        if (round < 100) {
            // Haven't hit level 6 yet
            return false;
        }

        if (myScore > opScore) {
            // We're already winning. Press on with strategy impossible to lose
            if (quinnNeverLose && quinnRound >= 50)
                return false;
            if (valorNeverLose && valorRound >= 50)
                return true;
        } else if (myScore < opScore) {
            // Although Quinn can blind others to counter them, she can be
            // counter be Teemo who also has blind! Don't fall for this!
            if (!teemoDetected) {
                teemoDetected = true;
                for (int i = round - 20; i < round; i++)
                    if (args[3].charAt(i) + args[4].charAt(i) != 'e')
                        teemoDetected = false;
            }
            if (teemoDetected)
                return true;
        }

        if (valorRound < 100) {
            // If we never use our ultimate, how can we know how strong it is?
            return true;
        }

        if (quinnScore < 0 && valorScore < 0)
            return valorRound < quinnRound;
        else
            return quinnScore * valorRound < valorScore * quinnRound;
    }

    @Override
    public String toString() {
        return getName();
    }

    /*
     * Quinn is a female Demacian elite ranger.
     * 
     * @see Valor
     */
    public static class Quinn implements Champion {
        @Override
        public String getName() {
            return "Quinn";
        }

        /*
         * Magic!
         */
        @Override
        public int[] getMove(String[] args) {
            int t = (int) ((Math.sqrt(Math.random() * 168 + 1) - 1) / 2);
            return new int[] { 5 - t, t };
        }

        @Override
        public String toString() {
            return getName();
        }
    }

    /*
     * Valor is Quinn's Demacian eagle.
     * 
     * @see Quinn
     */
    public static class Valor implements Champion {
        @Override
        public String getName() {
            return "Valor";
        }

        private int lastRound;
        private double[][] c;

        public void reset() {
            lastRound = 0;
            c = new double[6][6];
        }

        /*
         * Magic!
         */
        @Override
        public int[] getMove(String[] args) {
            int round = args[0].length();
            int[] b = new int[6];
            for (int i = round - 12; i < round; i++)
                b[args[0].charAt(i) - '0']++;
            {
                double deWeight = Math.pow(0.95, round - lastRound);
                for (int i = 0; i < 6; i++)
                    for (int j = 0; j < 6; j++)
                        c[i][j] *= deWeight;
                double weight = 1;
                for (int i = round - 1; i >= lastRound; i--) {
                    c[args[3].charAt(i) - '0'][args[4].charAt(i) - '0'] += weight;
                    weight *= 0.95;
                }
            }
            lastRound = round;

            List<int[]> pq = new ArrayList<>(1);
            double e = Integer.MIN_VALUE;
            for (int i = 0; i < 6; i++)
                for (int j = 0; j < 6; j++) {
                    double f = 0;
                    for (int k = 0; k < 6; k++)
                        f += (i + j) * c[j][k];
                    for (int k = 0; k < 6; k++)
                        f -= (i + k) * c[k][i];
                    // recently played moves are dangerous
                    f -= b[i] * b[i] * ((round + 11) / 12);
                    if (f >= e) {
                        if (f > e) {
                            pq.clear();
                            e = f;
                        }
                        pq.add(new int[] { i, j });
                    }
                }
            return pq.get((int) (Math.random() * pq.size()));
        }

        @Override
        public String toString() {
            return getName();
        }
    }
}

Casi siempre ganan contra todas las soluciones Java en mi máquina.

Editar:

Admito que Quinn y Valor no pudieron pelear contra el Historiador, pero todavía tengo buena fe en ellos para ganar el torneo.

Mi principio es, para cualquier solución con choice + guess == 5, también jugar con los choice + guess == 5beneficiarios manteniendo su ventaja.

Actualizar:

Bueno ... todo se volvió complicado.


1
Me gusta la referencia de League of Legends. Realmente quiero hacer un Teemo bot ahora. :)
mbomb007

6

Erudito

El erudito intenta aprender de los movimientos de su oponente, eligiendo el que su oponente menos adivinó y adivinando el que más usó su oponente. Pero la teoría no lo es todo, por lo que Scholar no lo hace muy bien ...

import java.util.HashMap;

public class Scholar implements Player
{
    public static int[] pm = new int[6];
    public static int[] pg = new int[6];
    public static HashMap<Integer, Integer> lm = new HashMap<>();
    public static HashMap<Integer, Integer> lg = new HashMap<>();

    public String getName()
    {
        return "Scholar";
    }

    public int[] getMove(String[] a)
    {
        int r = a[0].length();
        for (int i = 0; i < 6; i++) { pm[i]=0; pg[i]=0; }
        for (int i = 0; i < a[3].length(); i++) {
            int m = Integer.parseInt(String.valueOf(a[4].charAt(i)));
            int g = Integer.parseInt(String.valueOf(a[3].charAt(i)));
            pm[m]++; pg[g]++;
        }
        for (int i = 0; i < pm.length; i++) { lm.put(i, pm[i]); lg.put(i, pg[i]); }

        if (r < 1) {
            return new int[] { 3, 3 };
        } else {

            int mm = lm.entrySet().stream().min((x, y) -> x.getValue() > y.getValue() ? 1 : -1).get().getKey();
            int mg = lg.entrySet().stream().max((x, y) -> x.getValue() > y.getValue() ? 1 : -1).get().getKey();
            return new int[] { mm, mg };
        }   
    }
}

6

DeltaMax

(Se actualizó para no usar archivos y se agregó una nueva sección. También se modificó para no quedarse atascado en la primera sección).

Consiste en un par de estrategias que comienzan de manera simple y luego se vuelven más complejas; si borra una, se pasa a la siguiente sección.

  • Sección 1: Adivina {0, 5}constantemente
  • Sección 2: Verifique si sus últimas 4 conjeturas forman un patrón constante, lineal o cuadrático y siga adivinando el patrón hasta que se rompa
  • Sección 3: Verifique si adivina una cantidad anormalmente baja de algún número (menos de 1/13) y elija ese número
  • Sección 4: Analice bigrams en sus elecciones y vea qué es más probable que salga a continuación
  • Sección 5: Mire las últimas 100 rondas y elija el (choice, guess)par que tenga la mejor expectativa, ponderado para que las rondas recientes sean más importantes
  • Sección final: Adivina al azar, con mayor probabilidad de tener pocas opciones y altas conjeturas. Si llegas aquí, DeltaMax se ha dado por vencido y quisiera decir "buen juego".

Para averiguar qué estrategia se utilizó al final, descomente el

if (myChoices.length == 999) { System.out.println(strat); }

línea.

Disculpas por el horrible Java, pasé la tarde juntando partes y volviendo a aprender el idioma :)

import java.io.*;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;

public class DeltaMax implements Player
{
    private int strat = 100;

    public String getName() { return "DeltaMax"; }

    public int[] toInts(String s) {
        char [] chars = s.toCharArray();
        int[] ints = new int[chars.length];

        for (int i = 0; i < chars.length; i++){
            ints[i] = Integer.parseInt(Character.toString(chars[i]));
        }

        return ints;
    }

    public int mod6(int n) {
        n = n % 6;
        if (n < 0) { n += 6; }
        return n;
    }

    public int[] getMove(String [] args)
    {
       int[] myChoices = toInts(args[0]);
       int[] myGuesses = toInts(args[1]);
       int myScore = Integer.parseInt(args[2]);
       int[] opponentChoices = toInts(args[3]);
       int[] opponentGuesses = toInts(args[4]);
       int opponentScore = Integer.parseInt(args[5]);

       int rounds = myChoices.length;

       if (rounds == 0) { strat = 100; }
       Random r = new Random();

       // if (myChoices.length == 999) { System.out.println(strat); }

       if (strat == 100) { // Section 1 - {0, 5}
           if (opponentScore - myScore > 21 || (opponentScore >= myScore && rounds > 100)) {
               strat = 200;
           } else {
               return new int[] {0, 5};
           }
       }

       if (strat == 200) { // Section 2 - Mini interpolator
           int w = opponentChoices[opponentChoices.length - 4];
           int x = opponentChoices[opponentChoices.length - 3];
           int y = opponentChoices[opponentChoices.length - 2];
           int z = opponentChoices[opponentChoices.length - 1];

           if (w == x && x == y && y == z) { // Constant
               return new int[] { r.nextInt(4) + 2, w };
           }

           if (mod6(x-w) == mod6(y-x) && mod6(y-x) == mod6(z-y)) { // Linear
               return new int[] { r.nextInt(4) + 2, mod6(z + (z-y)) };
           }

           if (mod6((y-x) - (x-w)) == mod6((z-y) - (y-x))) { // Quadratic
               return new int[] { r.nextInt(4) + 2, mod6((z-y) + mod6((y-x) - (x-w))) };
           }

           strat = 300;
       }

       if (strat == 300) { // Section 3 - exploit least guessed
           int [] counts = new int[6];

           for (int i = 0; i < rounds; i++) {
               counts[opponentGuesses[i]] += 1;
           }

           int minCount = rounds;

           for (int i = 0; i < 6; i++) {
               if ((counts[i] <= 1 || counts[i] * 13 < rounds) && counts[i] < minCount) {
                   minCount = counts[i];
               }
           }

           if (minCount == rounds) {
               strat = 400;
           } else {
               ArrayList<Integer> choices = new ArrayList<Integer>();

               for (int i = 0; i < 6; i++) {
                   if (counts[i] == minCount) {
                       choices.add((Integer) i);
                   }
               }

               int choice = choices.get(r.nextInt(choices.size()));

               // {0, 0} is about the worst thing you can do, so DeltaMax tries to avoid that
               if (choice == 0) {
                   return new int[] { 0, r.nextInt(4) + 2 };
               } else {
                   return new int[] { choice, r.nextInt(6) };
               }
           }
       }

       if (strat == 400) { // Section 4 - bigrams
           if (opponentScore - myScore > 42 || (opponentScore >= myScore && rounds > 300)){
               strat = 500;
           } else {
               int[] opponentScores = new int[6];
               int opponentLast = opponentChoices[opponentChoices.length - 1];

               int[] myScores = new int[6];
               int myLast = myChoices[myChoices.length - 1];

               for (int i = 0; i < opponentChoices.length - 1; i++) {
                   if (opponentChoices[i] == opponentLast) {
                       opponentScores[opponentChoices[i+1]] += 1;
                   }

                   if (myChoices[i] == myLast) {
                       myScores[myChoices[i+1]] += 1;
                   }
               }

               int maxIndex = -1;
               int maxScore = 0;

               int minIndex = -1;
               int minScore = rounds;

               for (int i = 0; i < 6; i++) {
                   if (opponentScores[i] >= maxScore) {
                       maxScore = opponentScores[i];
                       maxIndex = i;
                   }

                   if (myScores[i] <= minScore) {
                       minScore = myScores[i];
                       minIndex = i;
                   }
               }

               if (minIndex == 0 && maxIndex == 0) {
                   return new int[] { 0, r.nextInt(4) + 2 };
               } else {
                   return new int[] { minIndex, maxIndex };
               }
           }
       }

       if (strat == 500) { // Section 5 - best expectation
           if (opponentScore - myScore > 84 || (opponentScore >= myScore && rounds > 800)){
               strat = 573;
           } else {
               int minLen = Math.min(rounds, 100);

               double bestScore = 0;
               int bestGuess = 0;
               int bestChoice = 5;

               for (int guess = 0; guess < 6; guess++) {
                   for (int choice = 0; choice < 6; choice++) {
                       double score = 0;
                       int start = rounds - minLen;

                       for (int i = start; i < rounds; i++) {
                           if (opponentGuesses[i] == choice && opponentChoices[i] != guess) {
                               score -= (choice + opponentChoices[i]) * ((double) i - start) / minLen;
                           } else if (opponentGuesses[i] != choice && opponentChoices[i] == guess) {
                               score += (choice + opponentChoices[i]) * ((double) i - start) / minLen;
                           }
                       }

                       if (score > bestScore) {
                           bestScore = score;
                           bestGuess = guess;
                           bestChoice = choice;
                       }
                   }
               }

               if (bestChoice == 0 && bestGuess == 0) {
                   return new int[] { r.nextInt(4) + 2, bestGuess };
               } else {
                   return new int[] {bestChoice, bestGuess};
               }
           }
       }

       // Section final - hope for the best
       int num = (int) Math.floor(Math.sqrt(r.nextInt(35)));
       return new int[] {5 - num, num};
    }
}

Con la implementación actual del controlador, no hay necesidad de guardar cosas en un archivo si los datos solo se usan para un solo juego. private int strat;es decir, es lo suficientemente bueno.
johnchen902

@ johnchen902 Gracias, no me di cuenta de que podía hacer eso. Eso hace las cosas mucho más fáciles.
Sp3000

6

Historiador

(Actualizado: la misma lógica, código más corto y 100 veces más rápido, pero solo puedes usar un bot Historian en un torneo).

Utiliza ponderación aleatoria para elegir un par de tiro y conjetura basado en la efectividad de usar solo ese par contra la historia previa de los oponentes. Los pesos son los cuadrados de las puntuaciones alcanzables.

public class Historian implements Player {
    private static java.util.Random r = new java.util.Random();
    private static int[] sc=new int[36]; //reseted between games, use only one Historian bot
    public String getName() {return "Historian";}
    public int[] getMove(String [] a) {
        if (a[3].length()==0)  {sc=new int[36]; for(int i=0;i<6;i++) sc[i*6+(5-i)]=5-i;}
        else {int t=a[3].charAt(a[3].length()-1)-'0'; int g=a[4].charAt(a[3].length()-1)-'0';
            for(int i=0; i<6; i++) {sc[i*6+t]+=i+t; sc[g*6+i]-=t+g;}}
        int sum=0; for(int i=0; i<36; i++) {sum+=(sc[i]<1)?1:sc[i]*sc[i];}
        int seed=r.nextInt(sum);int mt=-1;
        while (seed>=0) {seed-=(sc[++mt]<1)?1:sc[mt]*sc[mt];}  
        return new int[] {(int)(mt/6),mt%6};} }

Golpea Quinn and Valor (ya no) y pierde Morra Cowbell. En el torneo con la mayoría de los bots Historianocupa el segundo lugar Quinn and Valor.


Bueno, es bueno ver que gané en la máquina de alguien. Estoy perdiendo la tabla de líderes oficial actual . Me preguntaba si es por mala suerte o por algún error sutil imprevisto.
johnchen902

@ johnchen902 Debo haber golpeado alucinado Morra Cowbell. Editado el post. Sin embargo, puede eliminar comentarios si se vuelven obsoletos.
randomra

¡Creo que puedo ganar el 75% de nuestro duelo ahora después de mi actualización!
johnchen902

5

Extrapolador (v1.1)

Extrapolación extrema de uno de los equilibrios de Nash de un juego más simple.

¡Apoyo el formato de respuesta breve! (En estilo pitón).

public class Extrapolator implements Player { 
    private static java.util.Random r = new java.util.Random();
    public String getName() { return "Extrapolator"; }
    public int[] getMove(String [] args) {
        int t=-1;
        for(int c=15,s=r.nextInt(60);s>=0;s-=c,c-=2,t++);
        return new int[] {t,5-t}; } }

Parece vincularse con la Vaca Mágica (Morra Cowbell) y supera otras entradas que verifiqué.


1
Mueva Random r a un campo estático, para que no lo inicialice cada vez, ¡esto ayudará al rendimiento general!
Falco

¿Por qué el cambio en la distribución?
Peter Taylor

4

De moda

Trendy echa un vistazo a los movimientos pasados ​​del oponente, ponderándolos por lo reciente. Adivina el más pesado, y escoge uno cambiado ligeramente de eso. Aquí está, en todo su esplendor:

public class Trendy implements Player{public String getName(){return "Trendy";}public int[]getMove(String[]a){float h=0,c[]=new float[6];int i=0,l=a[3].length(),p=0;for(;i<l;)c[a[3].charAt(i++)-48]+=(float)i/l;for(i=0;i<6;i++)if(c[i]>h)h=c[p=i];return new int[]{(p+2)%6,p};}}    

Lo único con lo que puedo compararlo ahora es Cowbell. Pierde por un pequeño margen la mayoría de las veces, pero aparece en la parte superior con la frecuencia suficiente para mi gusto. Veremos cómo le va con más competidores.


77
¿Podrías formatear el código en varias líneas? Esto no es código golf ...
mbomb007

77
@ mbomb007 Ocupa menos espacio de esta manera. Uno de los dolores de KotHs en general es todo el desplazamiento para mirar las entradas. Describí lo que hace, y es muy sencillo para las partes interesadas formatearlo.
Geobits

4

Adivinador aleatorio

Esto es realmente sencillo. Efectivamente tira un d6, y agrega otro rollo al rollo anterior para su suposición. No ganará, pero proporcionará un buen punto de referencia.

import java.util.Random;

public class RandomGuesser implements Player {
    private final Random rnd = new Random();
    public String getName() { return "RandomGuesser"; }

    public int[] getMove(String[] args) {
        return new int[] { rnd.nextInt(6), rnd.nextInt(6) };
    }
}

4

Confundido, Python 3

Una entrada innecesariamente complicada. Incluso yo no sé lo que hace.

import sys
from random import *

if len(sys.argv) == 7:
    mn,mg,ms,on,og,os = [list(map(int, v)) for v in sys.argv[1:]]
    s,t = sum(mn+on)%5, sum(mg+og)%5
    n = [0]*3+list(range(6))*5+[5,0,5]
    m = [1,0,5,4]+n[:-2:s//9+1]
    numoptions = [n.extend(n[i+s::5+t]+[i]*i*(6+t)) for i in n[:]] and n
    guessoptions = [m.extend(m[i+t//2::8]+[i]*i*(5+s)) for i in m[:]] and m
    num = choice(numoptions)
    guess = choice(guessoptions)
else:
    num, guess = randint(0, 5), randint(0, 5)

sys.stdout.write('%u %u\n' % (num, guess))

Aunque este algoritmo avanzado parece funcionar peor que al azar en este torneo, y utiliza una memoria y un tiempo de ejecución significativos, tiene resultados sorprendentes para ciertos valores de 5 ;-)


4

Perno de lluvia

Toma la diferencia entre los dos últimos números que adivinó nuestro oponente, agrega eso a la última suposición de nuestro oponente, encuentra el módulo y evita elegir ese número a toda costa. Por ejemplo, si adivina {5,4,3} (disminuyendo en uno), entonces evitaríamos elegir 2 a toda costa.

Toma la diferencia entre los dos últimos números que eligió nuestro oponente, agrega eso a la última opción de nuestro oponente y adivina ese número. Por ejemplo, si adivina {1,4,5,2} (aumentando en tres), entonces adivinaríamos 5.

Evita rollos sin sentido o muy cerca de rollos sin sentido.

public class Rainbolt implements Player {

    public String getName() { 
        return "Rainbolt"; 
    }

    public int[] getMove(String[] args) {
        int[] yourChoices = toIntArray(args[3]);
        int[] yourGuesses = toIntArray(args[4]);

        int myChoice;
        if (yourGuesses.length > 1) {
            int latest = yourGuesses[yourGuesses.length - 1];
            int secondLatest = yourGuesses[yourGuesses.length - 2];
            int numberToAvoid = (2 * latest - secondLatest + 6) % 6;
            do {
                myChoice = rollRandom();
            } while (myChoice == numberToAvoid);
        } else { 
            myChoice = rollRandom();
        }

        int myGuess;
        if (yourChoices.length > 1) {
            int latest = yourChoices[yourChoices.length - 1];
            int secondLatest = yourChoices[yourChoices.length - 2];
            myGuess = (2 * latest - secondLatest + 6) % 6;
        } else { 
            myGuess = rollRandom();
        }

        if ((myChoice + myGuess) < 3) {
            do {
                myGuess = rollRandom();
            } while ((myChoice + myGuess) < 3);
        }

        return new int[] { myChoice, myGuess };
    }

    private static int[] toIntArray(String arg) {
        int[] result = new int[arg.length()];
        for (int i = 0; i < arg.length(); i++)
            result[i] = Character.getNumericValue(arg.charAt(i));
        return result;
    }

    private static int rollRandom() {
        return (int) (Math.random() * 6);
    }
}

No hagas que tu getMove()método sea estático. No puede implementar un método no estático como ese (al menos no en Java 8).
GiantTree

@GiantTree Gracias por atrapar eso.
Rainbolt

3

Bot evolucionado

Desarrollé este bot para que sea el mejor bot aleatorio.

import java.util.Arrays;

public class EvolvedBot implements Player {

    private static final double MUTATION_RATE = .2;
    private static final double CROSS_OVER_RATE = .5;

    private final double[] pickProbabilities;
    private final double pickSum;
    private final double[] guessProbabilities;
    private final double guessSum;

    public EvolvedBot(){
        this(new double[]{1.0069058661897903, 0.8949716031797937, 0.5249198534098369, 0.437811964976626, 0.2630925750209125, 0.4862172884617061},
                new double[]{0.6336558074769376, 0.13700756148363913, 0.9586621925124863, 0.11223159366330251, 0.8931390659502754, 0.662974949440039});
    }

    public EvolvedBot(double[] pickProbabilities, double[] guessProbabilities) {
        this.pickProbabilities = pickProbabilities;
        this.guessProbabilities = guessProbabilities;
        pickSum = Arrays.stream(pickProbabilities).sum();
        guessSum = Arrays.stream(guessProbabilities).sum();
    }

    @Override
    public String getName() {
        return "EvolvedBot"/* + ": " + Arrays.toString(pickProbabilities) + Arrays.toString(guessProbabilities)*/;
    }

    @Override
    public int[] getMove(String[] args) {
        int[] move = new int[]{5, 5};
        double pick = Math.random() * pickSum;
        double guess = Math.random() * guessSum;
        for (int i = 0; i < 6; i++){
            if (pick >= 0) {
                pick -= pickProbabilities[i];
                if (pick < 0) {
                    move[0] = i;
                }
            }
            if (guess >= 0){
                guess -= guessProbabilities[i];
                if (guess < 0){
                    move[1] = i;
                }
            }
        }
        return move;
    }

    public EvolvedBot mutate(double mutationRate){
        double[] pickProbabilities = Arrays.copyOf(this.pickProbabilities, 6);
        double[] guessProbabilities = Arrays.copyOf(this.guessProbabilities, 6);

        for (int i = 0; i < 6; i++){
            pickProbabilities[i] = Math.max(pickProbabilities[i] + (Math.random() * 2 - 1) * mutationRate, 0);
        }

        for (int i = 0; i < 6; i++){
            guessProbabilities[i] = Math.max(guessProbabilities[i] + (Math.random() * 2 - 1) * mutationRate, 0);
        }

        return new EvolvedBot(pickProbabilities, guessProbabilities);
    }

}

3

Popularidad, Python 3

Calcule las suposiciones basadas en números populares usados ​​en el pasado por el oponente. Los números utilizados recientemente tienen más peso. La elección del número es a menudo la misma que la suposición.

import sys
from random import *

if len(sys.argv) == 7:
    mn,mg,ms,on,og,os = [list(map(int, v)) for v in sys.argv[1:]]
    n = list(range(6))
    guess = choice(n + on[-100:] + on[-20:]*8)
    num = choice(n + [guess]*6)
else:
    num, guess = randint(0, 5), randint(0, 5)

sys.stdout.write('%u %u\n' % (num, guess))

3

Interpolador

(Cambió a Java ya que Python estaba causando problemas)

Utiliza la interpolación polinómica en las últimas 10 elecciones del oponente para calcular el siguiente número del oponente, luego hace lo mismo con sus propias elecciones y evita elegir ese número. Además, Interpolator tiene un ligero sesgo en contra de elegir 0 o 5, y su elección a veces se ve afectada por su suposición:

  • Si adivina 0, nunca elegirá 0
  • Si adivina 5, siempre elegirá 0 o 1
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

public class Interpolator implements Player
{
    private final int TAIL_LENGTH = 10;

    public String getName()
    {
        return "Interpolator";
    }

    public int[] toInts(String s) {
        char [] chars = s.toCharArray();
        int[] ints = new int[chars.length];

        for (int i = 0; i < chars.length; i++){
            ints[i] = Integer.parseInt(Character.toString(chars[i]));
        }

        return ints;
    }

    public int mod6(int n) {
        n = n % 6;
        if (n < 0) { n += 6; }
        return n;
    }

    public int interpolate(int[] nums){
        boolean allEqual = true;

        for (int i = 0; i < nums.length; i++){
            if (nums[i] != nums[0]){
                allEqual = false;
            }
        }

        if (allEqual) {
            return nums[0];

        } else {
            int [] newNums = new int[nums.length - 1];

            for (int i = 0; i < nums.length - 1; i++){
                newNums[i] = nums[i+1] - nums[i];
            }

            return nums[nums.length - 1] + interpolate(newNums);
        }
    }

    public int[] tail(int[] nums) {
        int minLength = Math.min(TAIL_LENGTH, nums.length);
        int[] tailArray = new int[minLength];

        for (int i = 0; i < minLength; i++){
            tailArray[i] = nums[nums.length - minLength + i];
        }

        return tailArray;
    }

    public int[] getMove(String [] args)
    {
        Random r = new Random();

        if (args[0].length() == 0){
            return new int[] {r.nextInt(5), r.nextInt(5)};
        }

        int[] myChoices = toInts(args[0]);
        int[] opponentChoices = toInts(args[3]);
        int[] opponentGuesses = toInts(args[4]);

        int guess = mod6(interpolate(tail(opponentChoices)));
        int avoid = mod6(interpolate(tail(myChoices)));

        if (guess == 5){ return new int[] {r.nextInt(2), 5}; }

        int[] choiceArray = {0, 1, 1, 2, 2, 3, 3, 4, 4, 5};
        ArrayList<Integer> choices = new ArrayList<Integer>();
        for (int i = 0; i < choiceArray.length; i++) { choices.add(choiceArray[i]); }

        choices.removeAll(Collections.singleton((Integer) avoid));
        if (guess <= 0) { choices.removeAll(Collections.singleton((Integer) 0)); }
        int choice = choices.get(r.nextInt(choices.size())); 

        return new int[] {choice, guess};
    }
}

3

CounterBot

No contrarresta a nadie, sino que cuenta hasta 0-5 en un círculo ( 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4 ...)

import java.util.Random;

public class Counter implements Player {

    int lastChoice = new Random().nextInt(6); //Chooses a random starting number

    public String getName() {
        return "CounterBot";
    }

    public int[] getMove(String[] args) {
        int[] oChoices = new int[6]; //Array to store the amount of individual choices of the opponent

        for (int i = 0; i < args[3].length(); i++) {
            int index = Integer.parseInt(String.valueOf(args[3].charAt(i))); //get that choice
            oChoices[index]++; //Increment the number corresponding the choice
        }
        int guess = 0, last = 0;
        for (int i = 0; i < oChoices.length; i++) { //Increment over the choices' array
            if (oChoices[i] > last) { //If the number has been chosen more often than the one before
                last = oChoices[i]; //Set the new maximum value (later the last maximum value)
                guess = i; //Set it as the next guess
            }
        }
        lastChoice++; //Increment our choice
        lastChoice %= 6; //Make sure it's within the bounds of 0-5 ie. modulo 6 (6 modulo 6 = 0)
        return new int[]{lastChoice, guess}; //return our choice and guess
    }
}

2

Basilisco, Python

Según la leyenda, el basilisco es el rey de las serpientes. ( fuente ) Supuse que ese es un nombre apropiado para un bot que juega "The Noble Game Of Kings" y está escrito en python. = D Este bot infunde miedo en el corazón de los otros bots y causa la muerte con una sola mirada.

import sys
import random

args = sys.argv
argc = len(args)
if argc < 6:
    sys.exit()

myChoices = args[1]
myGuesses = args[2]
myScore = args[3]
opponentChoices = args[4]
opponentGuesses = args[5]
opponentScore = args[6]

if len(myChoices) == 0:
    print (random.randint(0, 5))
    print (random.randint(0, 5))
    sys.exit()

guesses = [0, 0, 0, 0, 0, 0]
for char in opponentGuesses:
    i = int(char)
    guesses[i] += 1

#Will default towards smaller guesses to minimize opponent winnings
#For example, if the guess list is
#[5, 3, 7, 3, 4, 8]
#This will return 1. (index of the first 3)
myNextMove = guesses.index(min(guesses))

list = [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]]
i = 0

while i < len(myGuesses) - 1:
    myGuess = int(myGuesses[i])
    opponentResponse = int(opponentChoices[i+1])
    list[myGuess][opponentResponse] += 1
    i += 1

myPreviousGuess = int(myGuesses[-1])
relevantList = list[myPreviousGuess]

#Defaults towards higher moves.
#For example, if the list is
#[3, 8, 6, 8, 0, 7]
#This will return 3 (index of the last 8)
highestValue = -1
highestIndex = -1
for i in range(len(relevantList)):
    if relevantList[i] >= highestValue:
        highestValue = relevantList[i]
        highestIndex = i


myNextGuess = highestIndex

print (myNextMove)
print (myNextGuess)

Esto se ejecuta en una estrategia bastante simple. No espero que gane, pero fue divertido escribirlo. Este es también mi primer desafío de KoTH, así que estoy emocionado de ver qué tan bien funciona.

Cómo elige su próximo movimiento.

El basilisco siempre hace el movimiento que su oponente ha adivinado la menor cantidad de veces. En caso de empate, elegirá el número más pequeño. (para minimizar el número de puntos del oponente).

Cómo elige su próxima suposición.

El basilisco elegirá la respuesta más probable a su suposición anterior. Por ejemplo, si la última vez, adivinó un 3, volverá a través de todas las veces anteriores que adivinó un 3, y luego devolverá el movimiento más común del oponente que viene después de una suposición de 3. En caso de empate , elegirá el número más grande (para maximizar el número de puntos que podría hacer).

En una nota técnica, ¿funcionará esto correctamente? ¿Es suficiente print (), o debería usar algo como sys.stdout.write () como lo han hecho los otros Pythonistas?


sys.stdout.write () funciona en Python. print () funciona solo en Python 3. Sin embargo, debería estar bien.
TheNumberOne

No, print () funciona tampoco, estoy seguro. Los paréntesis son opcionales en 2.x
DJMcMayhem

Según esto , funcionan de manera diferente. Sin embargo, la forma en que lo usas, no importa.
TheNumberOne

¿Pero eso hace alguna diferencia?
DJMcMayhem

Aparentemente no.
TheNumberOne

2

Ídem

Esto se convierte en el oponente, pero detrás por una conjetura / elección.

import java.util.Random;

public class Ditto implements Player {
    private final Random rnd = new Random();
    public String getName() { return "Ditto"; }

    // myChoices myGuesses myScore oppChoices oppGuesses oppScore
    public int[] getMove(String[] args) {
        if(args[0] == null || args[0].isEmpty()) {
            return new int[] { rnd.nextInt(6), rnd.nextInt(6) };
        }
        int[] myChoices = toIntArray(args[0]);
        int[] myGuesses = toIntArray(args[1]);
        //int myScore = Integer.parseInt(args[2]);
        int[] oppChoices = toIntArray(args[3]);
        int[] oppGuesses = toIntArray(args[4]);
        //int oppScore = Integer.parseInt(args[5]);

        return new int[] { oppChoices[oppChoices.length-1], oppGuesses[oppGuesses.length-1] };
    }

    private static int[] toIntArray(String arg) {
        int[] result = new int[arg.length()];
        for (int i = 0; i < arg.length(); i++)
            result[i] = Character.getNumericValue(arg.charAt(i));
        return result;
    }
}

1

NullifierBot, Java

Siempre lanza 0 para minimizar las ganancias de cualquier oponente. Si el oponente alguna vez adivina mi número, solo gana lo que arrojó.

Siempre adivina 5 para maximizar mis ganancias. Como no puedo obtener ningún punto de mi lanzamiento, quiero obtener tantos puntos del oponente. Podría adivinar al azar, pero ¿dónde está la diversión en eso?

public class NullifierBot implements Player
{
    public String getName()
    {
        return "NullifierBot";
    }

    public int[] getMove(String [] args)
    {
        // always throws 0 to minimize opponents score
        // always guesses 5 to maximize my score
        return new int[] {0, 5}; 
    }
}

Supongo que este bot lo hará terriblemente. Cualquier bot que use cuotas tal vez incluso obtenga cada conjetura justo después de la primera.
mbomb007

@ mbomb007 ¡Sin embargo, no es lo peor! Aunque funciona peor que tu RandomBot.
Brian J

1

Erratica, Java

No es genial, pero originalmente fue diseñado para ser mayormente aleatorio, hasta que el valor de la compensación se me ocurrió. Se las arregla para perder constantemente vs. Counter Bot> _ <

import java.util.Random;
class Erratica implements Player
{
    private final Random rnd = new Random();

    public String getName() {
        return "Erratica";
    }

    public int[] getMove(String[] args) {
        if(args[0] == null || args[0].isEmpty())
        {
            return new int[]{rnd.nextInt(4)/3+4,rnd.nextInt(4)/3};
        }
        int[] myChoices = toIntArray(args[0]);
        int[] myGuesses = toIntArray(args[1]);
        int myScore = Integer.parseInt(args[2]);
        int[] opponentChoices = toIntArray(args[3]);
        int[] opponentGuesses = toIntArray(args[4]);
        int opponentScore = Integer.parseInt(args[5]);
        int round = opponentChoices.length + 1;
        int choice=0;
        int guess=0;
        if(round<7)
        {
            if(rnd.nextFloat()<(0.1f*(float)round-0.1f))
            {
                choice=(opponentChoices[round-2]+opponentGuesses[round-2])%6;
            }else
            {
                choice=rnd.nextInt(6);
            }
            if(rnd.nextFloat()<(0.1f*(float)round-0.1f))
            {
                guess=opponentChoices[round-2];
            }else
            {
                guess=rnd.nextInt(6);
            }
            return new int[]{choice, rnd.nextInt(6)/5*(5-choice-guess)+guess};
        }else
        {
            int lastError=Math.abs(opponentGuesses[round-2]-myChoices[round-2]);
            for(int i=round-2; i>round-8;i--)
            {
                if(lastError<rnd.nextInt(6))
                {
                    lastError++;
                }else
                {
                    lastError--;
                }
                if(lastError<0)
                    lastError+=6;

            }
            lastError = lastError%6; //shouldn't change
            switch(rnd.nextInt(4))
            {
                case 0:
                    choice=(myChoices[round-2-lastError-round/10])%6;
                    break;
                case 1:
                    choice=(myChoices[lastError+round/10])%6;
                    break;
                default:
                    choice = rnd.nextInt(6);
                    break;
            }

            lastError=Math.abs(myGuesses[round-2]-opponentChoices[round-2]);
            for(int i=round-2; i>round-8;i--)
            {
                if(lastError<rnd.nextInt(6))
                {
                    lastError++;
                }else
                {
                    lastError--;
                }
                if(lastError<0)
                    lastError+=6;
            }
            lastError = lastError%6; //shouldn't change
            switch(rnd.nextInt(4))
            {
                case 0:
                    guess=(opponentChoices[round-2-lastError-round/10])%6;
                    break;
                case 1:
                    guess=(opponentChoices[lastError+round/10])%6;
                    break;
                default:
                    guess = rnd.nextInt(4);
                    break;
            }
        }

        if(myScore>opponentScore)
            switch(rnd.nextInt(2)){
                case 0:
                    choice=5-guess;
                    break;
                case 1:
                    guess=5-choice;
                    break;
                default:
                    break;
            }
        return new int[]{choice, guess};
    }

    private static int[] toIntArray(String arg) {
        int[] result = new int[arg.length()];
        for (int i = 0; i < arg.length(); i++)
            result[i] = Character.getNumericValue(arg.charAt(i));
        return result;
    }
}

1

Echo, Ruby

mychoices, myguesses, myscore, opponentchoices, opponentguesses, opponentscore = $*

unless mychoices
 puts "0 5"
 exit
end

if mychoices.size > 990 && myscore == '0'
  nextchoice = rand(1..5)
else
  nextchoice = opponentchoices[-1].to_i
end

recentchoices = opponentchoices[/.{0,100}$/]

nextguess = (0..5).max_by do |choice|
  (recentchoices.count(choice.to_s)+1) * (nextchoice + choice)
end

puts "%s %s"%[nextchoice,nextguess]

Juega la última jugada que hizo el oponente, según la teoría de que cualquiera puede hacer un bot que no puede predecir. Suposiciones basadas en el valor esperado utilizando una muestra de cien movimientos.


Recibo este error: echo.rb:3:in <principal> ': método indefinido size' for nil:NilClass (NoMethodError). Parece ocurrir solo en la primera ronda, cuando no hay historial de movimientos.
PhiNotPi

Extraño, no sucedió cuando lo probé. Lo editaré
histocrat

¿Cuál es la relevancia de la if (mychoices.size > 990 && myscore == '0') nextchoice = rand(1..5)parte?
randomra

Si está a punto de terminar en un empate sin puntaje (como sucedería, por ejemplo, contra sí mismo), comienza a jugar al azar, ya que ~ 50% de posibilidades de ganar es mejor que nada.
histocrat

1

REY PESCADOR

    import java.util.Random;
public class KingFisher {

    private Random rnd = new Random();
    private int wins = 0;
    private int loses = 0;
    private int median = 0;
    private int medianMoved = 0;
    private int[] weightedLow = {40,65,80,90,95};
    private int[] weightedHigh = {5,15,30,55,95};
    private boolean highWeightMethod = true;

    public String getName() {
        return "KingFisher";
    }

    public int[] getMove(String [] args)
    {
        char[] mc  = args[0].toCharArray();
        char[] mg  = args[1].toCharArray();
        char[] oc  = args[3].toCharArray();
        char[] og  = args[4].toCharArray();
        int len = mc.length;
        int currentGuess = 0;
        int currentChoice = 0;
        if(len < 10)
            return new int[] {rnd.nextInt(6),rnd.nextInt(6)}; 
        int[] guessWeight = {0,0,0,0,0,0};
        int[] guessWeightTotal = {0,0,0,0,0,0};
        for(int a = 0; a< len;a++)
            guessWeight[oc[a]-48]++;
        if(!highWeightMethod){

            int[] whiteList = {1,1,1,1,1,1};
            for(int b = 0;b<3;b++){

                int min = 0;
                int max = 0;
                int minIndex = 0;
                int maxIndex = 0;
                for(int a = 0;a<6;a++){

                    if(whiteList[a] == 1){

                        min = guessWeight[a];
                        max = guessWeight[a];
                        minIndex = a;
                        maxIndex = a;
                        break;
                    }
                }

                for(int a = 0; a<6;a++){

                    if(whiteList[a] == 1){

                        if(guessWeight[a]<min){

                            min = guessWeight[a];
                            minIndex = a;
                        }
                        if(guessWeight[a]>max){

                            max = guessWeight[a];
                            maxIndex = a;
                        }
                    }
                }
                guessWeight[maxIndex] = min;
                guessWeight[minIndex] = max;
                whiteList[maxIndex] = 0;
                whiteList[minIndex] = 0;
            }
        }

        for(int a = 0; a< 6;a++)
            for(int b = 0; b<=a;b++)
                guessWeightTotal[a]+=guessWeight[b];
        int randInt = rnd.nextInt(guessWeightTotal[5]);
        for(int a = 0; a<6;a++){

            if(randInt < guessWeightTotal[a]){
                currentGuess = a;
                break;
            }
        }

        if(mg[len-1] == oc[len-1]){
            wins++;
            median++;
        }
        if(og[len-1] == mc[len-1]){
            loses++;
            median--;
        }
        if(median > 2){

            medianMoved++;
            median = 0;
        }
        if(median < -2){

            medianMoved--;
            median = 0;
        }

        randInt = rnd.nextInt(95);
        if((wins-medianMoved)>(loses+medianMoved)){

            for(int a = 0; a<6;a++){

                if(randInt < weightedLow[a]){
                    currentChoice = a;
                    break;
                }
            }
        }
        else{

            for(int a = 0; a<6;a++){

                if(randInt < weightedHigh[a]){
                    currentChoice = a;
                    break;
                }
            }
        }
        if(medianMoved < -5){

            highWeightMethod = !highWeightMethod;
            medianMoved = 0;
        }
        return new int[] {currentChoice,currentGuess}; 

    }
}

Este tipo consiste en malos algoritmos de adivinanzas que utilizan principalmente matrices ponderadas.


Estará en la próxima actualización.
PhiNotPi

1

Uh uh Sé lo que estás pensando. "¿Va a elegir cinco o algo más?" Bueno, para decirte la verdad en toda esta emoción, no estoy seguro de mí mismo, pero siendo este un método .44, el método más poderoso del mundo y sobrecargaría tu stack de inmediato, debes hacerte una pregunta : "¿Me siento afortunado?"

¿Bien, punk?

public class DirtyHarry implements Player {

    @Override
    public String getName() {
        return "DirtyHarry";
    }

    @Override
    public int[] getMove(String[] args) {
        return new int[]{5, 5};
    }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.