2048 Bot Challenge

19

Wir haben 2048 geklont und 2048 analysiert , aber warum haben wir es noch nicht gespielt? Schreiben Sie ein 555-Byte-Javascript-Snippet, um 2048 automatisch zu spielen. Das beste Ergebnis nach einer Stunde zählt (siehe unten).

Installieren:

Gehe zu 2048 und führe aus:

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

a ist das Objekt, um das Spiel zu steuern.

Regeln:

Nach dem Setup können Sie 555 Bytes Javascript auf der Konsole ausführen, um das Spiel zu steuern. Den Quellcode des Spiels finden Sie hier (inklusive Kommentare).

  • Es dürfen nur Dinge getan werden, die für den Benutzer möglich sind:
    • a.move(n) um eine Tastenaktion in eine der 4 Richtungen auszulösen.
      • 0: hoch, 1: rechts, 2: runter, 3: links
    • a.restart() um das Spiel neu zu starten. Ein Neustart ist in der Spielmitte erlaubt.
  • Informationen zum Stand des Spiels finden Sie in a.grid.cells. Diese Informationen sind schreibgeschützt
  • Das Einhängen in eine der Funktionen ist erlaubt, das Ändern ihres Verhaltens in keiner Weise (oder das Ändern anderer Daten).
  • Das Bewegen ist nur einmal alle 250ms erlaubt

Beispiel

Nur ein sehr einfaches Beispiel, um damit zu beginnen. Ohne Kommentare und gibt 181 Bytes ein .

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

Wertung und Ergebnisse

Ich werde die Schnipsel eine Stunde lang durchgehen lassen und die beste Punktzahl wird zählen. In der Tat gibt es eine Chance, dass das randombotObige auf diese Weise gewinnt, aber 1 Stunde sollte ausreichen, um es zu schlagen:

  • König Bottomstacker VII : 9912
  • Königin Bottomstacker V : 9216
  • Prince Bottomstacker II : 7520
  • Lord Bottom and Right : 6308
  • Bauer Randombot : 1413
  • Bottomstacker IV: 12320 Disqualifiziert für zwei Züge in einem Intervall (innerhalb von 250 ms)

FAQ

  • Warum ist diese Herausforderung nicht sprachunabhängig?
    • Aus dem einfachen Grund, dass es so mehr Spaß macht. Es ist einfach viel faszinierender, ein Spiel selbst grafisch zu sehen, als wenn eine Konsole Zahlen ausspuckt. Selbst wenn Sie kein Javascript kennen, sollten Sie in der Lage sein, sich dieser Herausforderung anzuschließen, da es nicht in erster Linie um Sprachfunktionen geht (verwenden Sie einfach dieses Tool, um den Code zu minimieren).
David Mulder
quelle
3
Dies scheint so, als ob es nur ein Haufen JavaScript-Implementierungen des besten Algorithmus von hier sein wird , nicht wahr?
Jason C
2
-1 für ...best score after an hour will count... Warum nur eine Stunde?
User80551
3
In jedem Fall schlage ich vor, im Namen der Fairness den Zufallszahlengenerator für jeden Testlauf der Antwort gleich zu setzen und außerdem harte (1 Stunde / 250 ms =) 14.400 Züge pro Lauf auszuführen, um Abweichungen von dieser Zählung zu eliminieren zu zeitlichen Ungenauigkeiten. Zumindest könnten die Ergebnisse etwas deterministischer und einer KOTH würdiger sein.
Jason C
1
750 Bytes oder 550 Bytes?
Peter Taylor
2
Zu restriktiv. 750 Bytes, 1 Stunde, JavaScript.
TheDoctor

Antworten:

4

Ich kann kein Javascript codieren, daher habe ich deine Antwort gestohlen.

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

Es benutzt die Strategie, die ich auch benutze.

EDIT: Schön, es hat deine Punktzahl nach ungefähr 5 Minuten auf meiner Maschine übertroffen: D

BEARBEITEN: Vergessen, zweimal anstatt nur einmal nach unten zu gehen. Dies ist der Code, den Sie verwenden sollten:

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

Es gibt auch einen Fehler, der neu gestartet wird, wenn er nicht benötigt wird, aber ich bin nicht sicher, wie ich das beheben soll. BEARBEITEN: Es hat derzeit einen Highscore von 3116 (nach 3 Minuten). Ich denke, es ist sicher zu sagen, dass dieser Algorithmus besser ist, als nur zufällige Züge zu machen.

EDIT Neuere 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} 
  m || mfs++;
  //up after 5 moves
  5 < mfs && (a.move(0));
  //restart after 10 moves failed
  10 < mfs && (mfs = 0, a.restart());
}, 250);

BEARBEITEN: Eine weitere neue Version, die sich direkt nach dem Hochfahren nach unten bewegt.

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: Update: Es hat meinen persönlichen Rekord mit einer verrückten Punktzahl von 12596 gebrochen.

EDIT: Hey, ich bin Bottomstacker: D Auch:

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

(Eigentlich keine Änderung, nur komprimiert.)

5. Mal ist ein Charme? Nicht sicher. Sowieso:

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

und:

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

Eine weitere neue 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);

und:

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

(Ich hoffe, es ist kein allzu großes Problem, dass dies hinter dem Gameover-Bildschirm weitergeht. Ich denke, Sie könnten einen Ort hinzufügen a.over=0, der oft ausgeführt wird. Ich werde es eines Tages herausfinden.)

BEARBEITEN (nochmal): Ich habe den Standard-Gameover-Weg verlassen und bin zu der alten Art zurückgekehrt, Dinge zu tun. Ich teste jetzt einen Zusatz, der immer zusammengeführt wird, wenn 2 Kacheln von 16 oder mehr zusammenkommen:

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
quelle
Addiere es mfs=0hinein addRandomTile, auf diese Weise wird die Zählung nach einem erfolgreichen Zug neu gestartet.
David Mulder
Und wenn ich es mir jetzt ansehe, muss ich sagen, es läuft besser als ich erwartet hatte O :): D
David Mulder
Ich habe gerade bemerkt, dass Sie in den letzten beiden Versionen zwei Züge in einem Intervall ausführen und daher den von mir aufgezeichneten 12320-Score disqualifizieren mussten. Und ja, ich brauchte einen Namen: P
David Mulder
@ DavidMulder Nooooooo! (Haben Sie eine Idee, wo dies passiert, damit ich es beheben kann?)
ɐɔıɐɔuʇǝɥʇs
13
Ihre Antwort ist voll mit neuer Version , bitte entfernen Sie den veralteten Code. Oder erklären Sie die Unterschiede zwischen den Versionen. Auf alten Code kann weiterhin in den "Bearbeiten" -Protokollen zugegriffen werden, wenn sich jemand dafür interessiert.
AL
3

Rechter und unterer Bot: 345 Bytes

Kurze Version

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

Lange Version

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 Worten

Nach unten und dann nach rechts bewegen, wenn Sie sich nicht bewegen können, nach oben bewegen (oder wenn Sie sich nicht nach links bewegen können), wenn sowohl die obere rechte als auch die untere rechte Ecke gefüllt sind, nach rechts bewegen, andernfalls von vorne beginnen.

Aktueller Highscore

Meine beste Punktzahl war 7668, aber das war viel schneller als t=250(und damit indirekt länger als eine Stunde).

David Mulder
quelle
Dieses Skript tendiert dazu, mehrere Züge pro Spielzug auszuführen.
Jdstankosky
3

Irgendwie bin ich heute Morgen auf diesen älteren Wettbewerb gestoßen, und da ich 2048 liebe, liebe ich KI, und JS ist eine der wenigen Sprachen, die ich derzeit gut kenne, ich dachte, ich würde es versuchen.

GreedyBot ( 607 536 Bytes)

Kurzfassung:

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

Lange Version (veraltet):

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

Die längere Version wurde (abgesehen vom Verkleinern der Variablennamen) überhaupt nicht gespielt, so dass sie einiges gekürzt werden konnte, während sie noch lesbar war. Die kürzere Version wurde mit dem Closure Compiler erstellt (danke für den Link!), Der bei 650 endete. Mit einigen benutzerdefinierten Modifikationen meinerseits konnte ich weitere 43 114 Bits abschneiden .

Grundsätzlich sucht es im Raster nach möglichen Zügen und addiert seinen Wert, wann immer es einen findet, entweder zur horizontalen oder zur vertikalen Summe. Nach dem Durchsuchen jeder möglichen Bewegung wird bestimmt, in welche Richtung sie sich bewegen soll, basierend darauf, ob die H- oder V-Summe höher ist und in welche Richtungen sie bereits versucht hat. Rechts und Runter sind die erste Wahl.

Wenn ich zurückblicke, wird mir klar, dass der erste Versuch, die Kacheln in diese Richtung zu schieben, garantiert erfolgreich ist, wenn eine der beiden Summen ungleich Null ist. Vielleicht könnte ich den bewegungsentscheidenden Abschnitt zum Ende hin auf dieser Grundlage vereinfachen.

Ich habe dieses Programm für eine Stunde laufen lassen und am Ende einen Highscore von 6080. In einem der Testläufe (Pre-Minification) erreichte es jedoch eine hohe Punktzahl von 6492nur 128 Punkten hinter meiner persönlichen Bestleistung 6620. Ihre Logik könnte erheblich verbessert werden, wenn sie gelegentlich nach links verschoben wird, da sich die Zahlen in der Regel wie folgt häufen:

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

( BEARBEITEN: Ich habe es eine Weile laufen lassen und es hat einige 7532Punkte geschafft. Verdammt, mein Programm ist schlauer als ich ....)

Noch ein interessanter Leckerbissen: Bei einem meiner fehlerhaften Versuche, etwas Nutzbares zu erschaffen, kam es irgendwie dazu, dass jedes Mal , wenn sich zwei Kacheln in derselben Zeile oder Spalte befanden, sie kombiniert wurden. Dies führte zu interessanten Entwicklungen, da die zufälligen 2er oder 4er immer wieder mit dem höchsten Plättchen kombiniert wurden und es jedes Mal verdoppelten. Einmal gelang es mir irgendwie, in 15 Sekunden über 11.000 Punkte zu erzielen, bevor ich es abschaltete ... XD

Verbesserungsvorschläge sind herzlich willkommen!

ETHproductions
quelle
1

Scheibenwischer: 454 Bytes

Einfach nach rechts, oben, links, oben ... wiederholen (genau wie die Scheibenwischer an einem Auto), es sei denn, es klemmt. Wenn es eingeklemmt ist, versucht es, die Scheibenwischer auszuschalten und wieder einzuschalten. Die höchste Punktzahl, die ich in einer Stunde erreicht habe, war 12.156 - Die meisten Punkte liegen jedoch irgendwo zwischen 3.000 und 7.000.

Nach jedem Versuch wird die Punktzahl in die Konsole ausgegeben.

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
quelle
0

UpAndLeftBot

Wie aus dem Titel hervorgeht, geht es nach oben und links, indem man David Mulders Arbeit stiehlt und einige Zahlen austauscht (ich weiß nicht, wie es mit Javascript aussieht, also ist das das Beste, was ich tun kann).

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
quelle
Genau wie in David Mulders Drehbuch werden auch hier ab und zu mehrere Züge pro Spielzug ausgeführt.
Jdstankosky