2048 Bot Challenge


19

Nous avons cloné 2048, analysé 2048, mais pourquoi ne l'avons-nous pas encore joué? Écrivez un extrait javascript de 555 octets pour lire automatiquement 2048, le meilleur score après une heure comptera (voir le score ci-dessous).

Installer:

Allez à 2048 et lancez:

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

a est l'objet pour contrôler le jeu.

Règles:

Après la configuration, vous pouvez exécuter 555 octets de javascript à partir de la console pour contrôler le jeu. Le code source du jeu peut être trouvé ici (y compris les commentaires).

  • Il ne peut faire que des choses qui sont possibles pour l'utilisateur:
    • a.move(n) pour déclencher une action clé dans l'une des 4 directions.
      • 0: haut, 1: droite, 2: bas, 3: gauche
    • a.restart() pour redémarrer le jeu. Le redémarrage est autorisé en milieu de partie.
  • Vous trouverez des informations sur l'état du jeu dans a.grid.cells. Ces informations sont en lecture seule
  • Il est permis de se connecter à l'une des fonctions, de modifier leur comportement de quelque manière que ce soit (ou de modifier toute autre donnée)
  • Le déplacement n'est autorisé qu'une fois toutes les 250 ms

Exemple

Juste un exemple très simple pour commencer. Sans commentaires et entre 181 octets .

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

Scoring et résultats

Je vais exécuter les extraits pendant une heure d'affilée et le meilleur score comptera. En effet, il y a une chance que ce qui randombotprécède gagne de cette façon, mais 1 heure devrait suffire pour le battre:

  • Roi Bottomstacker VII : 9912
  • Reine Bottomstacker V : 9216
  • Prince Bottomstacker II : 7520
  • Seigneur Bottom and Right : 6308
  • Paysan Randombot : 1413
  • Bottomstacker IV: 12320 Disqualifié pour avoir effectué deux coups dans un intervalle (dans les 250 ms)

FAQ

  • Pourquoi ce défi n'est-il pas indépendant de la langue via le terminal?
    • Pour la simple raison que c'est plus amusant comme ça. Regarder un jeu se jouer graphiquement est tout simplement beaucoup plus fascinant que de voir une console cracher des chiffres. Même en ne connaissant pas le javascript, vous devriez pouvoir participer à ce défi car il ne s'agit pas principalement de fonctionnalités linguistiques (utilisez simplement cet outil pour minimiser le code)

3
Il semble que cela finira par être un tas d'implémentations JavaScript du meilleur algorithme d' ici , non?
Jason C

2
-1 pour ...best score after an hour will count... Pourquoi juste une heure?
user80551

3
Dans tous les cas, je suggère, au nom de l'équité, d'ensemencer le générateur de nombres aléatoires de la même manière pour chaque test de réponse, et également de faire un dur (1 heure / 250 ms =) 14 400 coups par run pour éliminer les variations de ce nombre dues aux imprécisions de synchronisation. Au moins, les résultats pourraient être un peu plus déterministes et dignes d'un KotH.
Jason C

1
750 octets ou 550 octets?
Peter Taylor

2
Trop restrictif. 750 octets, 1 heure, JavaScript.
TheDoctor

Réponses:


4

Je ne peux pas coder javascript, j'ai donc volé votre réponse.

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

Il utilise la stratégie que j'utilise également.

EDIT: Nice, il vient de battre votre score après environ 5 minutes sur ma machine: D

EDIT: J'ai oublié de descendre deux fois au lieu d'une seule, c'est le code que vous devez utiliser:

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

En outre, il contient un bogue qui redémarre lorsqu'il n'est pas nécessaire, mais je ne sais pas comment résoudre ce problème. EDIT: Il a actuellement un meilleur score de 3116 (après 3 minutes). Je pense qu'il est prudent de dire que cet algorithme est meilleur que de simplement faire des mouvements aléatoires.

EDIT Version plus récente:

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

EDIT: Une autre nouvelle version, celle-ci descend directement après avoir monté.

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

EDIT: Mise à jour: il vient de battre mon record personnel avec un score assez fou de 12596.

EDIT: Hey, je suis empileur: D Aussi:

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

(Pas vraiment un changement, juste compressé.)

La 5ème fois est un charme? Pas certain. Quoi qu'il en soit:

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

et:

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

Une autre nouvelle version:

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

et:

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

(J'espère que cela ne pose pas trop de problème que cela continue derrière l'écran de gameover? Je pense que vous pourriez ajouter un a.over=0endroit qui est souvent exécuté. Je le découvrirai un jour.)

EDIT (encore une fois): J'ai abandonné la méthode de reprise standard et suis revenu à l'ancienne façon de faire les choses. Je teste maintenant un ajout qui fusionnera toujours s'il y a 2 tuiles de 16 ou plus ensemble:

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

Ajoutez à l' mfs=0intérieur addRandomTile, de cette façon, il recommencera à compter après un coup réussi.
David Mulder

Et le regarder jouer maintenant, je dois dire que ça va mieux que ce à quoi je m'attendais O :): D
David Mulder

Je viens de remarquer que vous effectuez deux coups dans un intervalle dans les 2 dernières versions, j'ai donc dû disqualifier le score 12320 que j'avais enregistré. Et oui, j'avais besoin d'une sorte de nom: P
David Mulder

@DavidMulder Nooooooo! (Avez-vous une idée de l'endroit où cela se produit, afin que je puisse y remédier?)
ɐɔıʇǝɥʇuʎs

13
Votre réponse est pleine de nouvelle version , veuillez supprimer le code obsolète. Ou expliquez les différences entre les versions. L'ancien code sera toujours accessible dans les journaux "edit" si quelqu'un est intéressé.
AL

3

Bot droit et bas: 345 octets

Version courte

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

Version longue

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

Dans les mots

Déplacez-vous vers le bas, puis vers la droite, si vous ne pouvez pas vous déplacer, vers le haut (ou si vous ne le pouvez pas, déplacez-vous vers la gauche), si le coin supérieur droit et le coin inférieur droit sont remplis, déplacez-vous à droite sinon recommencez.

Meilleur score actuel

Mon meilleur score était de 7668, mais cela a été exécuté à une vitesse bien supérieure à t=250(et donc indirectement supérieure à une heure).


Ce script a tendance à effectuer plusieurs mouvements par tour.
jdstankosky

3

D'une manière ou d'une autre, j'ai rencontré ce concours plus ancien ce matin, et comme j'adore 2048, j'adore l'IA et que JS est l'une des rares langues que je connais bien actuellement, j'ai pensé que je pourrais essayer.

GreedyBot ( 607 536 octets)

Version courte:

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

Version longue (obsolète):

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 version plus longue n'a pas été jouée du tout (à part le rétrécissement des noms de variables), elle pourrait donc être raccourcie un peu tout en restant lisible. La version plus courte a été créée à l'aide de Closure Compiler (merci pour le lien!), Qui a fini à 650. Avec quelques modifications personnalisées de ma part, j'ai pu raser 43 autres 114 bits supplémentaires.

Fondamentalement, il recherche dans la grille les mouvements possibles et, chaque fois qu'il en trouve un, ajoute sa valeur au total horizontal ou vertical. Après avoir parcouru tous les mouvements possibles, il détermine dans quelle direction il doit se déplacer, selon que le total H ou V est plus élevé et les directions qu'il a déjà essayées. Droite et Bas sont les premiers choix.

En y repensant, je me rends compte maintenant que si l'un ou l'autre total est différent de zéro, la première tentative de faire glisser les carreaux dans cette direction est garantie de réussir. Je pourrais peut-être simplifier la section de décision de déplacement vers la fin sur la base de cela.

J'ai laissé ce programme fonctionner pendant une heure et je me suis retrouvé avec un score élevé de 6080. Cependant, dans l'un des essais (pré-minification), il a réussi un score élevé de 6492seulement 128 derrière mon record personnel 6620. Sa logique pourrait être grandement améliorée en le faisant se déplacer de gauche en bas de temps en temps, car les chiffres ont tendance à s'accumuler comme ceci:

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

( EDIT: Je l'ai laissé fonctionner un peu plus longtemps, et il a réussi certains 7532points. Darn, mon programme est plus intelligent que moi ....)

Encore une petite friandise intéressante: dans l'une de mes tentatives de créer quelque chose utilisable, il s'est finalement retrouvé de sorte que chaque fois que deux tuiles se trouvaient dans la même ligne ou colonne, elles ont été combinées. Cela a conduit à des développements intéressants car les 2 ou 4 aléatoires se combinaient à plusieurs reprises avec la tuile la plus élevée, la doublant à chaque fois. Une fois, il a réussi à marquer plus de 11 000 en 15 secondes avant de l'éteindre ... XD

Toute suggestion d'amélioration est la bienvenue!


1

Essuie-glaces: 454 octets

Va simplement à droite, en haut, à gauche, en haut ... répétition (tout comme les essuie-glaces sur une voiture) à moins qu'elle ne se coince. S'il se coince, il essaiera d'éteindre les essuie-glaces et de les rallumer. Le score le plus élevé que j'ai obtenu en une heure était de 12 156 - Cependant, la plupart des scores se situent entre 3 000 et 7 000.

Il affichera le score dans la console après chaque tentative.

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

0

UpAndLeftBot

Comme le titre le suggère, se déplace vers le haut et la gauche en volant le travail de David Mulder et en échangeant certains chiffres (je ne connais pas Jack au sujet de Javascript, donc le mieux que je puisse faire est le suivant).

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

Tout comme le script de David Mulder, cela effectue également plusieurs mouvements par tour de temps en temps.
jdstankosky
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.