Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

We have been cloning 2048, analyzing 2048, but why haven't we played it yet? Write a 555 byte javascript snippet to play 2048 automatically, the best score after an hour will count (see scoring below).

Setup:

Goto 2048 and run:

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

a is the object to control the game.

Rules:

After setup you may run 555 bytes of javascript from the console to control the game. The source code of the game can be found here (including comments).

  • It may only do things which are possible for the user:
    • a.move(n) to trigger a key action in any of the 4 directions.
      • 0: up, 1: right, 2: down, 3: left
    • a.restart() to restart the game. Restarting is allowed in the middle of the game.
  • Information about the state of the game can be found in a.grid.cells. This information is read-only
  • Hooking into any of the functions is allowed, altering their behaviour in any way is not (or changing any other data)
  • Moving is allowed only once every 250ms

Example

Just a very simple example to start from. Without comments and enters 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);

Scoring and results

I will be running the snippets for one hour straight and the best score will count. Indeed there is a chance that randombot above will win that way, but 1 hour should be enough to beat it:

  • King Bottomstacker VII: 9912
  • Queen Bottomstacker V: 9216
  • Prince Bottomstacker II: 7520
  • Lord Bottom and Right: 6308
  • Peasant Randombot: 1413
  • Bottomstacker IV: 12320 Disqualified for making two moves in one interval (within 250ms)

FAQ

  • Why is this challenge not language agnostic through the terminal?
    • For the simple reason that it is more fun like this. Watching a game play itself graphically is simply a lot more mesmerizing than seeing a console spit out numbers. Even knowing no javascript you should be able to join into this challenge as it isn't about language features primarily (just use this tool to minimize the code)
  • This is not one-on-one, but yet tagged king-of-the-hill?
    • True, my bad, best tag to describe the type of the competition.
share|improve this question
1  
This seems like it will just end up being a bunch of JavaScript implementations of the best algorithm from here, no? –  Jason C Apr 2 at 16:14
2  
-1 for ...best score after an hour will count... Why just an hour? –  user80551 Apr 2 at 16:18
3  
In any case, I suggest, in the name of fairness, seeding the random number generator the same for every answer's test run, and also running a hard (1 hour / 250 ms =) 14,400 moves per run to eliminate variations of that count due to timing inaccuracies. At least the results could be somewhat more deterministic and worthy of a KotH. –  Jason C Apr 2 at 16:34
1  
750 bytes or 550 bytes? –  Peter Taylor Apr 2 at 16:51
2  
Too restrictive. 750 bytes, 1 hour, JavaScript. –  TheDoctor Apr 2 at 17:11

4 Answers 4

I can't code javascript, so I stole your answer.

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

It uses the strategy I also use.

EDIT: Nice, it just beat your score after about 5 minutes on my machine :D

EDIT: Forgot to move down twice instead of just once, this is the code you should use:

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

Also, there's a bug in it that it restarts when it's not needed, but I'm not sure how to fix this. EDIT: It currently has a highscore of 3116 (after 3 minutes). I think it's safe to say this algoritm is better than just doing random moves.

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

EDIT: Another new version, this one moves down directly after moving up.

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: it just broke my personal record with a pretty crazy score of 12596.

EDIT: Hey, I'm bottomstacker :D Also:

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

(Not actually a change, just compressed.)

5th time's a charm? Not sure. Anyways:

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

and:

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

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

and:

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

(I hope it's not too much of a problem that this continues behind the gameover screen? I think you could add an a.over=0 somewhere that gets executed often. I'll figure it out someday.)

EDIT (again): I dropped the standard gameover way and reverted to the old way of doing things. I'm now testing out an addition that'll always merge if there are 2 tiles of 16 or more together:

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);
share|improve this answer
    
Add mfs=0 inside addRandomTile, that way it will restart counting after a successful move. –  David Mulder Apr 2 at 18:26
    
And watching it play now, got to say, it's doing better than I expected O:) :D –  David Mulder Apr 2 at 18:34
    
Just noticed that you are making two moves in one interval in the last 2 versions, so had to disqualify the 12320 score I had recorded. And yeah, I needed some kind of name :P –  David Mulder Apr 2 at 19:34
    
@DavidMulder Nooooooo! (Do you have any idea where this happens, so that I can fix it?) –  ɐɔıʇǝɥʇuʎs Apr 2 at 20:18
9  
Your answer is full of new version, please remove the outdated code. Or explain the differences between the versions. Old code will still be accessible in the "edit" logs if anyone is interested in. –  A.L Apr 3 at 2:26

Right and Down bot: 345 bytes

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

Long 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 words

Move down, then right, if you can't move, move up (or if you can't, move left), if both the top right and bottom right corner is filled, move right otherwise start over.

Current highscore

My own best score was 7668, but that was run at far greater speed than t=250 (and thus indirectly longer than an hour).

share|improve this answer
    
This script has a tendency to perform multiple moves per turn. –  jdstankosky 2 days ago

Windshield Wipers: 454 bytes

Simply goes Right, Up, Left, Up... repeating (just like the wipers on a car) unless it gets jammed. If it gets jammed, it will attempt to turn off the wipers and turn them back on. Highest score I got in an hour was 12,156 -- However, most of the scores are somewhere between 3k - 7k.

It will output the score into the console after every attempt.

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);
share|improve this answer

UpAndLeftBot

As title suggests, moves up & left by stealing David Mulder's work & swapping some numbers (I don't know jack about Javascript, so best I can do is this).

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();
share|improve this answer
    
Just like David Mulder's script, this also performs multiple moves per turn every once in a while. –  jdstankosky 2 days ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.