2048 Bot Challenge

19

Hemos estado clonando 2048, analizando 2048, pero ¿por qué no lo hemos jugado todavía? Escriba un fragmento de JavaScript de 555 bytes para jugar 2048 automáticamente, el mejor puntaje después de una hora contará (vea la puntuación a continuación).

Preparar:

Ir a 2048 y ejecutar:

 a = new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager);

a es el objeto para controlar el juego.

Reglas:

Después de la configuración, puede ejecutar 555 bytes de javascript desde la consola para controlar el juego. El código fuente del juego se puede encontrar aquí (incluidos los comentarios).

  • Solo puede hacer cosas que sean posibles para el usuario:
    • a.move(n) para activar una acción clave en cualquiera de las 4 direcciones.
      • 0: arriba, 1: derecha, 2: abajo, 3: izquierda
    • a.restart() para reiniciar el juego. Se permite reiniciar en el medio del juego.
  • Puede encontrar información sobre el estado del juego en a.grid.cells . Esta información es de solo lectura
  • Está permitido engancharse a cualquiera de las funciones, alterar su comportamiento de alguna manera no lo está (o cambiar cualquier otro dato)
  • Solo se permite mover una vez cada 250 ms

Ejemplo

Solo un ejemplo muy simple para comenzar. Sin comentarios y entra 181 bytes .

//bind into new tile function and change m(ove) variable when a tile was moved
b = a.addRandomTile.bind(a);
m = !1;
a.addRandomTile = function() { m = !0; b(); };
//number of move fails
mfs = 0;
setInterval(function() {
  //set global moved tracking variable to false
  m = !1;
  a.move(Math.floor(4 * Math.random()));
  m || mfs++;
  //restart after 10 moves failed
  10 < mfs && (mfs = 0, a.restart());
}, 250);

Puntuación y resultados

Ejecutaré los fragmentos durante una hora consecutiva y contaré la mejor puntuación. De hecho, existe la posibilidad de que lo randombotanterior gane de esa manera, pero 1 hora debería ser suficiente para superarlo:

  • Rey Bottomstacker VII : 9912
  • Reina Bottomstacker V : 9216
  • Príncipe Bottomstacker II : 7520
  • Señor Bottom and Right : 6308
  • Campesino Randombot : 1413
  • Bottomstacker IV: 12320 Descalificado por hacer dos movimientos en un intervalo (dentro de 250 ms)

Preguntas más frecuentes

  • ¿Por qué este desafío no es independiente del lenguaje a través del terminal?
    • Por la simple razón de que es más divertido así. Ver un juego en sí mismo gráficamente es simplemente mucho más fascinante que ver una consola escupir números. Incluso sin saber javascript, debería poder unirse a este desafío, ya que no se trata principalmente de las características del lenguaje (solo use esta herramienta para minimizar el código)
David Mulder
fuente
3
Parece que terminará siendo un montón de implementaciones de JavaScript del mejor algoritmo desde aquí , ¿no?
Jason C
2
-1 para ...best score after an hour will count... ¿Por qué solo una hora?
user80551
3
En cualquier caso, sugiero, en nombre de la equidad, sembrar el generador de números aleatorios de la misma manera para cada prueba de respuesta, y también ejecutar un duro (1 hora / 250 ms =) 14,400 movimientos por carrera para eliminar las variaciones de ese conteo debido a inexactitudes de tiempo. Al menos los resultados podrían ser algo más deterministas y dignos de una KotH.
Jason C
1
750 bytes o 550 bytes?
Peter Taylor
2
Demasiado restrictivo. 750 bytes, 1 hora, JavaScript.
TheDoctor

Respuestas:

4

No puedo codificar javascript, así que robé tu respuesta.

//bind into new tile function and change m(ove) variable when a tile was moved
b = a.addRandomTile.bind(a);
m = !1;
a.addRandomTile = function() { m = !0; b(); };
//number of move fails
mfs = 0;
c=1;
setInterval(function() {
  //set global moved tracking variable to false
  m = !1;
  a.move(c)
  c++
  if (c>3) {c=1}
  m || mfs++;
  //restart after 10 moves failed
  10 < mfs && (mfs = 0, a.restart());
}, 250);

Utiliza la estrategia que yo también uso.

EDITAR: Bien, solo superó tu puntaje después de unos 5 minutos en mi máquina: D

EDITAR: Olvidé bajar dos veces en lugar de solo una, este es el código que debe usar:

a = new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager);
//bind into new tile function and change m(ove) variable when a tile was moved
b = a.addRandomTile.bind(a);
m = !1;
a.addRandomTile = function() { m = !0; b(); };
//number of move fails
mfs = 0;
c=1;
setInterval(function() {
  //set global moved tracking variable to false
  m = !1;
  if (c<=3) {n=c}
  else {n=2}
  a.move(n)
  c++

  if (c>4) {c=1} 
  m || mfs++;
  //restart after 10 moves failed
  10 < mfs && (mfs = 0, a.restart());
}, 250);

Además, hay un error que se reinicia cuando no es necesario, pero no estoy seguro de cómo solucionarlo. EDITAR: actualmente tiene una puntuación más alta de 3116 (después de 3 minutos). Creo que es seguro decir que este algoritmo es mejor que solo hacer movimientos aleatorios.

EDITAR versión más nueva:

a = new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager);
//bind into new tile function and change m(ove) variable when a tile was moved
b = a.addRandomTile.bind(a);
m = !1;
a.addRandomTile = function() { m = !0; mfs=0; b(); };
//number of move fails
mfs = 0;
c=1;
setInterval(function() {
  //set global moved tracking variable to false
  m = !1;
  if (c<=3) {n=c}
  else {n=2}
  a.move(n)
  c++
  if (c>4) {c=1} 
  m || mfs++;
  //up after 5 moves
  5 < mfs && (a.move(0));
  //restart after 10 moves failed
  10 < mfs && (mfs = 0, a.restart());
}, 250);

EDITAR: Otra nueva versión, esta se mueve hacia abajo directamente después de subir.

a = new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager);
//bind into new tile function and change m(ove) variable when a tile was moved
b = a.addRandomTile.bind(a);
m = !1;
a.addRandomTile = function() { m = !0; mfs=0; b(); };
//number of move fails
mfs = 0;
c=1;
setInterval(function() {
  //set global moved tracking variable to false
  m = !1;
  if (c<=3) {n=c}
  else {n=2}
  a.move(n)
  c++
  if (c>4) {c=1} 
  m || mfs++;
  //up after 5 moves
  5 < mfs && (a.move(0), c=4);
  //restart after 10 moves failed
  10 < mfs && (mfs = 0, a.restart());
}, 250);

EDITAR: Actualización: acaba de romper mi récord personal con una puntuación bastante loca de 12596.

EDITAR: Hola, soy apostador: D También:

b=a.addRandomTile.bind(a);m=!1;a.addRandomTile=function(){m=!0;mfs=0;b()};mfs=0;c=1;setInterval(function(){m=!1;n=3>=c?c:2;a.move(n);c++;4<c&&(c=1);m||mfs++;5<mfs&&(a.move(0),c=4);10<mfs&&(mfs=0,a.restart())},250);

(En realidad no es un cambio, solo comprimido).

¿La quinta vez es un encanto? No estoy seguro. De todos modos:

//bind into new tile function and change m(ove) variable when a tile was moved
b = a.addRandomTile.bind(a);
m = !1;
a.addRandomTile = function() { m = !0; mfs=0; b(); };
//number of move fails
mfs = 0;
c=1;
setInterval(function() {
  //set global moved tracking variable to false
  m = !1;
  if (c<=3) {n=c}
  else {n=2}
  a.move(n)
  c++
  if (c>4) {c=1} 
  if (c==0) {c=4}
  m || mfs++;
  //up after 5 moves
  5 < mfs && (c=0);
  //restart after 10 moves failed
  10 < mfs && (mfs = 0, a.restart());
}, 250);

y:

b=a.addRandomTile.bind(a);m=!1;a.addRandomTile=function(){m=!0;mfs=0;b()};mfs=0;c=1;setInterval(function(){m=!1;n=3>=c?c:2;a.move(n);c++;4<c&&(c=1);0==c&&(c=4);m||mfs++;5<mfs&&(c=0);10<mfs&&(mfs=0,a.restart())},250);

Otra nueva versión:

a = new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager);
//bind into new tile function and change m(ove) variable when a tile was moved
b = a.addRandomTile.bind(a);
m = !1;
a.addRandomTile = function() { m = !0; mfs=0; b(); };
//number of move fails
mfs = 0;
c=1;
setInterval(function() {
  //set global moved tracking variable to false
  m = !1;
  if (c<=3) {n=c}
  else {n=2}
  a.move(n)
  c++
  if (c>4) {c=1} 
  if (c==0) {c=4}
  m || mfs++;
  //up after 5 moves
  5 < mfs && (c=0);
  //Found this in the source, as the criteria for a gameover. Might as well reset then ;)
  if (!a.movesAvailable()) {
      a.restart()
  }

}, 250);

y:

a=new GameManager(4,KeyboardInputManager,HTMLActuator,LocalStorageManager);b=a.addRandomTile.bind(a);m=!1;a.addRandomTile=function(){m=!0;mfs=0;b()};mfs=0;c=1;setInterval(function(){m=!1;n=3>=c?c:2;a.move(n);c++;4<c&&(c=1);0==c&&(c=4);m||mfs++;5<mfs&&(c=0);a.movesAvailable()||a.restart()},250);

(Espero que no sea un gran problema que esto continúe detrás de la pantalla de gameover. Creo que podrías agregar a.over=0 lugar que se ejecute a menudo. Lo resolveré algún día).

EDITAR (nuevamente): abandoné la forma estándar de gameover y volví a la antigua forma de hacer las cosas. Ahora estoy probando una adición que siempre se fusionará si hay 2 fichas de 16 o más juntas:

a = new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager);
b = a.addRandomTile.bind(a);
m = !1;
a.addRandomTile = function() {
  m = !0;
  mfs = 0;
  b();
};
mfs = 0;
c = 1;
setInterval(function() {
  m = !1;
  l = 8;
  for (x = 0;x < 4;x++) {
    for (y = 0;y < 4;y++) {
      t1 = a.grid.cellContent({x:x, y:y});
      t2 = a.grid.cellContent({x:x, y:y + 1});
      t3 = a.grid.cellContent({x:x + 1, y:y + 1});
      if (t1 & t2) {
        if (t1.value == t2.value) {
          if (t1.value > l) {
            l = t1.value;
            c = 2;
          }
        }
        if (t1 & t3) {
          if (t1.value == t2.value) {
            if (t1.value > l) {
              l = t1.value;
            }
          }
        }
      }
    }
  }
  if (c <= 3) {
    n = c;
  } else {
    n = 2;
  }
  a.move(n);
  c++;
  if (c > 4) {
    c = 1;
  }
  if (c == 0) {
    c = 4;
  }
  m || mfs++;
  5 < mfs && (c = 0);
  10 < mfs && (mfs = 0, a.restart());
}, 250);
ɐɔıʇǝɥʇuʎs
fuente
Agregue mfs=0adentro addRandomTile, de esa manera reiniciará el conteo después de un movimiento exitoso.
David Mulder
Y al verlo jugar ahora, tengo que decir que está mejor de lo que esperaba O :): D
David Mulder
Acabo de notar que estás haciendo dos movimientos en un intervalo en las últimas 2 versiones, por lo que tuve que descalificar la puntuación de 12320 que había registrado. Y sí, necesitaba algún tipo de nombre: P
David Mulder
@DavidMulder ¡Nooooooo! (¿Tienes alguna idea de dónde sucede esto, para que pueda solucionarlo?)
ɐɔıʇǝɥʇuʎs
13
Su respuesta está llena de una nueva versión , elimine el código obsoleto. O explica las diferencias entre las versiones. El código antiguo seguirá siendo accesible en los registros de "edición" si alguien está interesado.
AL
3

Bot derecho y abajo: 345 bytes

Version corta

b=a.addRandomTile.bind(a);m=!1;t=250;d=!0;a.addRandomTile=function(){m=!0;b();d&&setTimeout(c,t)};c=function(){d=!1;a.move(2);setTimeout(function(){m=!1;d=!0;a.move(1);m||setTimeout(function(){a.move(0);m?a.grid.cells[3][0]&&a.grid.cells[3][3]&&setTimeout(function(){a.move(1)},t):setTimeout(function(){a.move(3);m||a.restart()},t)},t)},t)};c();

Versión larga

a = new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager);
b = a.addRandomTile.bind(a);
m = !1;
t = 250;
d = !0;
a.addRandomTile = function() {
  m = !0;
  b();
  d && setTimeout(c, t);
};
c = function() {
  d = !1;
  a.move(2);
  setTimeout(function() {
    m = !1;
    d = !0;
    a.move(1);
    m || setTimeout(function() {
      a.move(0);
      m ? a.grid.cells[3][0] && a.grid.cells[3][3] && setTimeout(function() {
        a.move(1);
      }, t) : setTimeout(function() {
        a.move(3);
        m || a.restart();
      }, t);
    }, t);
  }, t);
};
c();

En palabras

Mueva hacia abajo, luego hacia la derecha, si no puede moverse, mueva hacia arriba (o si no puede, mueva hacia la izquierda), si tanto la esquina superior derecha como la inferior derecha están llenas, mueva hacia la derecha; de lo contrario, comience de nuevo.

Puntaje actual

Mi mejor puntaje fue 7668, pero eso se ejecutó a una velocidad mucho mayor que t=250(y por lo tanto indirectamente más de una hora).

David Mulder
fuente
Este script tiene una tendencia a realizar múltiples movimientos por turno.
jdstankosky
3

De alguna manera me encontré con este concurso más antiguo esta mañana, y como amo 2048, amo la IA, y JS es uno de los pocos idiomas que conozco bien, pensé que lo probaría.

GreedyBot ( 607 536 bytes)

Version corta:

C=function(x,y){return a.grid.cellContent({x:x,y:y})},h=[[1,3,2,0],[2,1,3,0]],V='value',A='addRandomTile';a=new GameManager(4,KeyboardInputManager,HTMLActuator,LocalStorageManager);b=a[A].bind(a);m=!1;f=d=X=Y=0;a[A]=function(){m=!0;f=0;b()};setInterval(function(){m=!1;for(var c=X=Y=0;4>c;c++)for(var e=0;4>e;e++)if(u=C(c,e),!!u){for(q=e+1;4>q;){v=C(c,q);if(!!v){u[V]==v[V]&&(Y+=u[V]);break}q++}for(q=c+1;4>q;){v=C(q,e);if(!!v){u[V]==v[V]&&(X+=u[V]);break}q++}}f<4&&a.move(h[X>Y+4?0:1][f]);m&&(f=0);m||f++;15<f&&(f=0,a.restart())},250);

Versión larga (obsoleta):

a = new GameManager(4, KeyboardInputManager, HTMLActuator,    LocalStorageManager);
b = a.addRandomTile.bind(a);
m = !1;
f = d = X = Y = 0;
a.addRandomTile = function() { m = !0; f = 0; b(); };
setInterval(function() {
    m = !1;
    X = Y = 0;

    for(var x=0;x<4;x++) {
        for(var y=0;y<4;y++) {
            u = a.grid.cellContent({x:x, y:y});
            if(u==null){continue;}
            q = y+1;
            while(q < 4) {
                v = a.grid.cellContent({x:x,y:q});
                if(v!=null){
                    if(u.value==v.value){
                        Y+=u.value;
                    }
                    break;
                }
                q++;
            }
            q = x+1;
            while(q < 4) {
                v = a.grid.cellContent({x:q,y:y});
                if(v!=null){
                    if(u.value==v.value){
                        X+=u.value;
                    }
                    break;
                }
                q++;
            }
        }
    }

    if(X>=Y){
        if(f==0)
            a.move(1);
        else if(f==1)
            a.move(3);
        else if(f==2)
            a.move(2);
        else if(f==3)
            a.move(0);
    } else {
        if(f==0)
            a.move(2);
        else if(f==1)
            a.move(0);
        else if(f==2)
            a.move(1);
        else if(f==3)
            a.move(3);
    }
    if(m)f=0;
    m || f++;
    if(15 < f) f=0,a.restart();
}, 250);

La versión más larga no se jugó en absoluto (aparte de reducir los nombres de las variables), por lo que podría acortarse un poco sin dejar de ser legible. La versión más corta se creó con el Compilador de cierre (¡gracias por el enlace!), Que terminó en 650. Con algunas modificaciones personalizadas de mi parte, pude eliminar otro 43 114 bits.

Básicamente, busca a través de la cuadrícula posibles movimientos, y cada vez que encuentra uno, agrega su valor al total horizontal o vertical. Después de buscar a través de cada movimiento posible, determina en qué dirección debe moverse, en función de si el total de H o V es más alto y las direcciones que ya ha intentado. Derecha y Abajo son las primeras opciones.

Mirando hacia atrás, ahora me doy cuenta de que si cualquiera de los dos totales no es cero, el primer intento de deslizar las fichas en esa dirección tiene éxito. Quizás podría simplificar la sección de decisión de movimiento hacia el final en base a esto.

Dejé este programa funcionando durante una hora y terminé con un puntaje alto de 6080. Sin embargo, en una de las ejecuciones de prueba (preminificación), logró un puntaje alto de 6492, solo 128 por detrás de mi mejor marca personal 6620. Su lógica podría mejorarse enormemente haciendo que se mueva hacia la izquierda de vez en cuando, ya que los números tienden a acumularse así:

 2  4  8 16
 4  8 16 32
 8 16 32 64
16 32 64 128

( EDITAR: lo dejé funcionando un poco más y logró algunos 7532puntos. Maldición, mi programa es más inteligente que yo ...)

Otro dato interesante: en uno de mis intentos fallidos de crear algo utilizable, de alguna manera terminó de manera tal que en cualquier momento dos baldosas estaban en la misma fila o columna, que se combinaron. Esto condujo a desarrollos interesantes, ya que los 2 o 4 aleatorios se combinarían repetidamente con el mosaico más alto, duplicándolo cada vez. Una vez, de alguna manera logró anotar más de 11,000 en 15 segundos antes de apagarlo ... XD

¡Cualquier sugerencia para mejorar es bienvenida!

ETHproducciones
fuente
1

Limpiaparabrisas: 454 bytes

Simplemente va a la derecha, arriba, izquierda, arriba ... repitiendo (al igual que los limpiaparabrisas de un automóvil) a menos que se atasque. Si se atasca, intentará apagar los limpiaparabrisas y volver a encenderlos. El puntaje más alto que obtuve en una hora fue 12,156. Sin embargo, la mayoría de los puntajes están entre 3k - 7k.

Producirá la puntuación en la consola después de cada intento.

var move = !1;
var bad = 0;
var c = 0;
var b = a.addRandomTile.bind(a);
a.addRandomTile = function() {
    b();
    move=!0;
    bad=0;
}
setInterval(function() {
    if (!move) bad++;
    if (c>3) c=0;
    move = !1;
    if (c==3) {a.move(0);c++;}
    if (c==2) {a.move(3);c++;}
    if (c==1) {a.move(0);c++;}
    if (c==0) {a.move(1);c++;}
    if (bad>10) {a.move(2);}
    if (!a.movesAvailable()) {console.log("Score: "+a.score);a.restart();}
}, 250);
jdstankosky
fuente
0

UpAndLeftBot

Como sugiere el título, se mueve hacia arriba y hacia la izquierda robando el trabajo de David Mulder e intercambiando algunos números (no sé nada sobre Javascript, así que lo mejor que puedo hacer es esto).

a = new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager);
b = a.addRandomTile.bind(a);
m = !1;
t = 250;
d = !0;
a.addRandomTile = function() {
  m = !0;
  b();
  d && setTimeout(c, t);
};
c = function() {
  d = !1;
  a.move(0); // a.move(2)
  setTimeout(function() {
    m = !1;
    d = !0;
    a.move(3); // a.move(1)
    m || setTimeout(function() {
      a.move(2);  //a.move(0)
      m ? a.grid.cells[3][0] && a.grid.cells[3][3] && setTimeout(function() {
        a.move(3); // a.move(1)
      }, t) : setTimeout(function() {
        a.move(1);  // a.move(3)
        m || a.restart();
      }, t);
    }, t);
  }, t);
};
c();
Kyle Kanos
fuente
Al igual que el guión de David Mulder, esto también realiza múltiples movimientos por turno de vez en cuando.
jdstankosky