2048 Bot Challenge


19

Abbiamo clonato 2048, analizzando 2048, ma perché non l'abbiamo ancora giocato? Scrivi uno snippet javascript da 555 byte per giocare automaticamente al 2048, conterà il miglior punteggio dopo un'ora (vedi punteggio sotto).

Impostare:

Vai a 2048 ed esegui:

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

a è l'oggetto per controllare il gioco.

Regole:

Dopo l'installazione puoi eseguire 555 byte di javascript dalla console per controllare il gioco. Il codice sorgente del gioco può essere trovato qui (inclusi i commenti).

  • Può fare solo cose che sono possibili per l'utente:
    • a.move(n) per attivare un'azione chiave in una delle 4 direzioni.
      • 0: su, 1: destra, 2: giù, 3: sinistra
    • a.restart() per riavviare il gioco. Il riavvio è consentito durante il gioco.
  • Informazioni sullo stato del gioco sono disponibili in a.grid.cells . Questa informazione è di sola lettura
  • È consentito collegarsi a una qualsiasi delle funzioni, non alterare il loro comportamento in alcun modo (o cambiare altri dati)
  • Lo spostamento è consentito solo una volta ogni 250 ms

Esempio

Solo un esempio molto semplice da cui partire. Senza commenti e inserisce 181 byte .

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

Punteggio e risultati

Farò correre i frammenti per un'ora di fila e il punteggio migliore conterà. In effetti c'è una possibilità che randombotsopra vincerà in quel modo, ma 1 ora dovrebbe essere sufficiente per batterlo:

  • Re Bottomstacker VII : 9912
  • Regina Bottomstacker V : 9216
  • Principe Bottomstacker II : 7520
  • Signore Bottom and Right : 6308
  • Contadino Randombot : 1413
  • Bottomstacker IV: 12320 Squalificato per aver fatto due mosse in un intervallo (entro 250 ms)

FAQ

  • Perché questa sfida non è agnostica del linguaggio attraverso il terminale?
    • Per la semplice ragione che è più divertente come questo. Guardare una partita stessa graficamente è semplicemente molto più affascinante che vedere una console sputare numeri. Pur non conoscendo JavaScript, dovresti essere in grado di partecipare a questa sfida in quanto non riguarda principalmente le funzionalità del linguaggio (basta usare questo strumento per ridurre al minimo il codice)

3
Sembra che finirà per essere un mucchio di implementazioni JavaScript del miglior algoritmo da qui , no?
Jason C

2
-1 per ...best score after an hour will count... Perché solo un'ora?
user80551

3
In ogni caso, suggerisco, in nome dell'equità, di seminare lo stesso generatore di numeri casuali per ogni test di risposta e di eseguire 14.400 mosse (1 ora / 250 ms =) per ogni variazione per eliminare le variazioni di quel conteggio dovute a tempi imprecisi. Almeno i risultati potrebbero essere in qualche modo più deterministici e degni di un KotH.
Jason C

1
750 byte o 550 byte?
Peter Taylor,

2
Troppo restrittivo 750 byte, 1 ora, JavaScript.
TheDoctor

Risposte:


4

Non riesco a programmare javascript, quindi ho rubato la tua risposta.

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

Usa anche la strategia che uso.

EDIT: Bello, ha appena battuto il tuo punteggio dopo circa 5 minuti sulla mia macchina: D

EDIT: Hai dimenticato di spostarti verso il basso due volte anziché solo una volta, questo è il codice che dovresti usare:

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

Inoltre, c'è un bug che si riavvia quando non è necessario, ma non sono sicuro di come risolvere questo problema. EDIT: attualmente ha un punteggio più alto di 3116 (dopo 3 minuti). Penso che sia sicuro affermare che questo algoritmo è meglio che fare semplicemente mosse casuali.

EDIT Versione più recente:

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: Un'altra nuova versione, questa si sposta direttamente verso il basso dopo aver spostato verso l'alto.

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: Aggiornamento: ha appena rotto il mio record personale con un punteggio piuttosto folle di 12596.

EDIT: Hey, io sono bottomstacker: D anche:

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

(In realtà non è un cambiamento, solo compresso.)

La quinta volta è un incanto? Non sono sicuro. In ogni modo:

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

e:

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

Un'altra nuova versione:

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

e:

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

(Spero non sia un grosso problema che questo continui dietro la schermata di gameover? Penso che potresti aggiungerne uno a.over=0 posto che viene eseguito spesso. Lo un giorno.)

EDIT (di nuovo): ho abbandonato il modo standard di gameover e sono tornato al vecchio modo di fare le cose. Ora sto testando un'aggiunta che si fonderà sempre se ci sono 2 tessere di 16 o più insieme:

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

Aggiungi mfs=0all'interno addRandomTile, in questo modo riavvierà il conteggio dopo una mossa riuscita.
David Mulder,

E guardarlo suonare ora, devo dire, sta andando meglio di quanto mi aspettassi O :): D
David Mulder

Ho appena notato che stai facendo due mosse in un intervallo nelle ultime 2 versioni, quindi ho dovuto squalificare il punteggio 12320 che avevo registrato. E sì, avevo bisogno di una specie di nome: P
David Mulder,

@DavidMulder Nooooooo! (Hai idea di dove accada, così posso ripararlo?)
2ıʇǝɥʇuʎs

13
La tua risposta è piena di nuova versione , rimuovi il codice obsoleto. Oppure spiega le differenze tra le versioni. Il vecchio codice sarà comunque accessibile nei registri "modifica" se qualcuno è interessato.
AL

3

Bot destro e giù: 345 byte

Versione breve

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

Versione lunga

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

In parole

Sposta in basso, poi a destra, se non riesci a muoverti, sposta in alto (o se non puoi, sposta a sinistra), se sia l'angolo in alto a destra che quello in basso a destra sono pieni, sposta a destra altrimenti ricomincia.

Punteggio attuale

Il mio miglior punteggio è stato 7668, ma è stato eseguito a una velocità molto maggiore di t=250(e quindi indirettamente più di un'ora).


Questo script ha la tendenza a eseguire più mosse per turno.
jdstankosky,

3

In qualche modo mi sono imbattuto in questo vecchio concorso stamattina e, poiché amo il 2048, amo l'IA e JS è una delle poche lingue che conosco bene al momento, ho pensato di provarci.

GreedyBot ( 607 536 byte)

Versione breve:

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

Versione lunga (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 versione più lunga non è stata affatto giocata a golf (a parte la riduzione dei nomi delle variabili), quindi potrebbe essere abbreviata un po 'mentre era ancora leggibile. La versione più breve è stata creata usando Closure Compiler (grazie per il link!), Che è finito a 650. Con alcune modifiche personalizzate da parte mia, sono stato in grado di radere via un altro 43 114 bit.

Fondamentalmente, cerca attraverso la griglia le possibili mosse e ogni volta che ne trova una, aggiunge il suo valore al totale orizzontale o verticale. Dopo aver cercato tutte le mosse possibili, determina la direzione in cui deve spostarsi, in base al fatto se il totale H o V è più alto e le direzioni che ha già provato. Destra e Giù sono le prime scelte.

Ripensandoci, mi rendo conto ora che se uno dei due è diverso da zero, il primo tentativo di far scorrere le tessere in quella direzione avrà sicuramente successo. Forse potrei semplificare la sezione decisionale verso la fine basata su questo.

Ho lasciato questo programma attivo per un'ora e ho ottenuto un punteggio elevato di 6080. Tuttavia, in una delle prove (pre-minificazione), ha ottenuto un punteggio elevato di6492 solo 128 dietro il mio meglio di 6620. La sua logica potrebbe essere notevolmente migliorata spostandola a sinistra di tanto in tanto, poiché i numeri tendono ad accumularsi in questo modo:

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

( EDIT: l' ho lasciato in esecuzione per un po 'di più, e ha gestito alcuni 7532punti. Accidenti, il mio programma è più intelligente di me ....)

Un altro bocconcino interessante: in uno dei miei tentativi glitch di creare qualcosa di utilizzabile, in qualche modo è finito in modo che in qualsiasi momento qualsiasi due tessere fossero nella stessa riga o colonna, fossero combinate. Ciò ha portato a sviluppi interessanti in quanto i 2 o 4 casuali si combinano ripetutamente con la tessera più alta, raddoppiandola ogni volta. Una volta, in qualche modo è riuscito a segnare oltre 11.000 in 15 secondi prima di spegnerlo .... XD

Eventuali suggerimenti per il miglioramento sono i benvenuti!


1

Tergicristalli: 454 byte

Va semplicemente a destra, su, sinistra, su ... ripetendo (proprio come i tergicristalli di un'auto) a meno che non si blocchi. Se si inceppa, tenterà di spegnere i tergicristalli e riaccenderli. Il punteggio più alto che ho ottenuto in un'ora è stato di 12.156 - Tuttavia, la maggior parte dei punteggi sono compresi tra 3k e 7k.

Emetterà il punteggio nella console dopo ogni tentativo.

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

Come suggerisce il titolo, si sposta verso l'alto e a sinistra rubando il lavoro di David Mulder e scambiando alcuni numeri (non conosco jack su Javascript, quindi il meglio che posso fare è questo).

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

Proprio come la sceneggiatura di David Mulder, anche questa volta esegue più mosse per turno una volta ogni tanto.
jdstankosky,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.