2048 Bot Challenge

19

7

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)

David Mulder

Posted 2014-04-02T15:53:33.483

Reputation: 437

3

This seems like it will just end up being a bunch of JavaScript implementations of the best algorithm from here, no?

– Jason C – 2014-04-02T16:14:06.690

2-1 for ...best score after an hour will count... Why just an hour? – user80551 – 2014-04-02T16:18:34.240

And further, if all the most competitive answers are just ports of the same algorithms from the page, the "objective" winner is just determined by random chance. So this questions seems to translate to: Port one or all of the algorithms from the SO post to JavaScript, then after an hour, a random winner will be chosen. – Jason C – 2014-04-02T16:22:13.327

@JasonC Perhaps. If I do submit, though, I won't be pulling from those answers. Part of the fun of these challenges for me isn't winning, it's exploring the algorithms and approaches available to come up with a good solution, with an eye towards invention/new learning. I doubt that applies for everyone, but that's my philosophy for games in code. – ProgrammerDan – 2014-04-02T16:25:21.133

+1 for pointing out how easy this is to interface! Looking forward to watching the answers here – Claudiu – 2014-04-02T16:33:49.070

3In 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 – 2014-04-02T16:34:34.677

1750 bytes or 550 bytes? – Peter Taylor – 2014-04-02T16:51:31.547

2Too restrictive. 750 bytes, 1 hour, JavaScript. – TheDoctor – 2014-04-02T17:11:15.653

My internet stopped working the past hour, and I have to accede that I wasn't aware that question existed. None the less I believe the fairly low character limit should prevent the more complex algorithms from being doable and a fair amount of creativity should still be required. – David Mulder – 2014-04-02T17:42:07.813

@user80551 and @ Jason C: I considered building a seeded fork of 2048 to do exactly that, but the beauty of the simplicity of the interface and challenge would be lost that way, so I decided (after running a couple of simulations) that an hour should be able to differentiate between different algorithms even if the differences are quite minimal. – David Mulder – 2014-04-02T17:47:24.757

@PeterTaylor: Shame on me, discussed this on chat couple of days ago and changed it from 750 to 555 back then, but forgot changing both. – David Mulder – 2014-04-02T17:48:54.293

1@DavidMulder I think a sacrifice of some simplicity is warranted to make this a proper, deterministic challenge. 14,400 moves (and again: actual move count, don't rely on 250ms intervals being consistent) is not enough to ensure that every user gets equally serendipitous sets of moves (and equally avoids unlucky sets of moves). With your "maximum" rule, it only takes one extremely lucky set of moves to have an otherwise poor algorithm dominate the results. This is simply not fair or balanced that way. Note also that the winner should always be the winner no matter how many retests are run. – Jason C – 2014-04-02T18:08:42.890

1I wonder if the [ai-player] tag is a better fit for this since the entries will not be playing directly against each other? – Gareth – 2014-04-02T18:11:57.253

@Gareth [tag:ai-player] does not currently seem to signify a challenge style, but I'm sure nobody would have a problem if you examined questions that currently have that tag, updated the tag wiki, and edited this if it is appropriate and beneficial. You may wish to start a discussion of possible good uses for [tag:ai-player] on meta. The current [tag:king-of-the-hill] description doesn't preclude contests where the programs don't directly interact (I think I saw some related discussion in chat recently; ask Chris Jester-Young, I believe he was involved). – Jason C – 2014-04-02T18:14:54.223

@JasonC: Already made clear in my answer that 1) I am aware that randombot could have won in theory (with which I am fine) and 2) the fun factor of being able to watch each-others bots counts a lot for me (the reason why I set the rule that 250ms should be between turns, rather than a limit on the moves). If you dislike it that much downvote and don't join, as far as I am concerned the only valid point you raised was that question on SO which I (nor the people on chat) were aware of. – David Mulder – 2014-04-02T18:31:09.160

1@DavidMulder At minimum at least use a hard count of 14,400 moves; that shouldn't affect your interface. You are not guaranteed to get 1 hour / 250 ms moves in an interval-based test. As for the randomness, the fact that a different winner can result from multiple retests of all the answers is a valid point and pretty clearly puts a major hole in this challenge (which otherwise isn't a particularly bad challenge). In theory, challenges here should have distinct deterministic criteria, and results should be repeatable. – Jason C – 2014-04-02T19:49:32.337

@DavidMulder It's worth noting, by the way, that 14,400 moves and 250 ms between moves are not mutually exclusive. – Jason C – 2014-04-05T15:52:00.793

@JasonC: Wasn't planning on replying, but let me point out something which it seems I have not made clear regarding the randomness: by seeding the random function the difference will be that a certain bot would win deterministic, it would however in no way make that bot better or anything like that at all. The result is in that sense just as random. Additionally challenges here should have an objective criteria, not necessarily a deterministic one as far as I can tell. Either way, I agree that a fixed amount of moves wouldn't have been a bad idea, but it would open an entire set of – David Mulder – 2014-04-05T16:08:08.440

different discussions regarding whether a move that doesn't move anything is a move, and lots of similar things whereas in my humble opinion a time limit works quite nicely as well. Especially as execution times comes into play a bit as well (no matter how insignificant for the feels it's quite nice). I built myself a piece of code that counts the execution time and then can run at 25ms per second still fairly simulating the hour limit taking into account the execution time. – David Mulder – 2014-04-05T16:14:53.700

Oh well, whatever. I have had a terrible amount of fun with this challenge though I would have loved seeing more submissions, so I am happy the way it ran and can't really bother making a deal out of the determinism and time constraint rules. If they would cause loopholes (like the seeded system could do) you would have me up in arms, but like this I can't really be bothered. – David Mulder – 2014-04-05T16:16:17.223

Many games which are not deterministic admit measuring the average performance of entries by running each many times. – dmckee --- ex-moderator kitten – 2014-04-13T23:57:04.293

Re KotH: "True, my bad, best tag to describe the type of the competition." I think a simple [code-challenge] would be the appropriate tag here. – Martin Ender – 2014-05-16T17:36:51.117

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

ɐɔıʇǝɥʇuʎs

Posted 2014-04-02T15:53:33.483

Reputation: 4 449

Add mfs=0 inside addRandomTile, that way it will restart counting after a successful move. – David Mulder – 2014-04-02T18:26:15.480

And watching it play now, got to say, it's doing better than I expected O:) :D – David Mulder – 2014-04-02T18:34:01.627

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 – 2014-04-02T19:34:40.657

@DavidMulder Nooooooo! (Do you have any idea where this happens, so that I can fix it?) – ɐɔıʇǝɥʇuʎs – 2014-04-02T20:18:29.240

Seems you figured it out after all :D – David Mulder – 2014-04-02T20:29:34.903

Running your final at 5ms interval rather than 250ms got me a 13k score after like 5 minutes, cool :P (not within the bounds of the competition, but still cool to see :D ). Still running it now just for fun :P – David Mulder – 2014-04-02T20:44:12.820

@DavidMulder 13k? Nice! – ɐɔıʇǝɥʇuʎs – 2014-04-02T20:52:31.833

13Your 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 – 2014-04-03T02:26:42.657

Not only is this confusing and lengthy, the bottom-most script is WELL over the 555 byte limit. – jdstankosky – 2014-07-30T20:54:10.647

@jdstankosky Sorry, forgot to add the minimized script (that actually is under 555 bytes, even including the initializing line a = .... – ɐɔıʇǝɥʇuʎs – 2014-08-01T08:14:45.390

3

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

David Mulder

Posted 2014-04-02T15:53:33.483

Reputation: 437

This script has a tendency to perform multiple moves per turn. – jdstankosky – 2014-07-30T20:48:15.827

3

Somehow I ran across this older contest this morning, and since I love 2048, I love AI, and JS is one of the few languages I currently know well, I figured I'd give it a shot.

GreedyBot (607 536 bytes)

Short version:

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

Long version (outdated):

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

The longer version was not golfed at all (other than shrinking variable names), so it could be shortened quite a bit while still being readable. The shorter version was created using Closure Compiler (thanks for the link!), which ended up at 650. With some custom modifications on my part, I was able to shave off another 43 114 bits.

Basically, it searches for through the grid for possible moves, and whenever it finds one, adds its value to either the horizontal or vertical total. After searching through every possible move, it determines which direction it should move, based on if the H or V total is higher, and the directions it has already tried. Right and Down are the first choices.

Looking back at this, I realize now that if either total is non-zero, the first attempt at sliding the tiles in that direction is guaranteed to succeed. Perhaps I could simplify the move-deciding section towards the end based on this.

I left this program running for an hour and ended up with a high score of 6080. However, in one of the trial runs (pre-minification), it managed a high score of 6492, only 128 behind my personal best of 6620. Its logic could be greatly improved by having it move left-down occasionally, as the numbers tend to pile up like this:

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

(EDIT: I left it running a while longer, and it managed some 7532 points. Darn, my program is smarter than me....)

One more interesting tidbit: in one of my glitched attempts at creating something useable, somehow it ended up so that anytime any two tiles were in the same row or column, they were combined. This led to interesting developments as the random 2s or 4s would repeatedly combine with the highest tile, doubling it every time. One time, it somehow managed to score over 11,000 in 15 seconds before I shut it off.... XD

Any suggestions for improvement are very welcome!

ETHproductions

Posted 2014-04-02T15:53:33.483

Reputation: 47 880

1

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

jdstankosky

Posted 2014-04-02T15:53:33.483

Reputation: 1 474

0

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

Kyle Kanos

Posted 2014-04-02T15:53:33.483

Reputation: 4 270

Just like David Mulder's script, this also performs multiple moves per turn every once in a while. – jdstankosky – 2014-07-30T20:52:31.547