2048 Bot Challenge


19

Мы клонировали 2048, анализировали 2048, но почему мы еще не сыграли? Напишите 555-байтовый фрагмент javascript для автоматического воспроизведения 2048, при этом будет учитываться лучший результат через час (см. Оценку ниже).

Настроить:

Перейти к 2048 и запустить:

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

a это объект для управления игрой.

Правила:

После настройки вы можете запустить 555 байт javascript из консоли для управления игрой. Исходный код игры можно найти здесь (включая комментарии).

  • Он может делать только то, что возможно для пользователя:
    • a.move(n) вызвать ключевое действие в любом из 4 направлений.
      • 0: вверх, 1: вправо, 2: вниз, 3: слева
    • a.restart() перезапустить игру. Перезапуск разрешен в середине игры.
  • Информация о состоянии игры может быть найдена в a.grid.cells. Эта информация только для чтения
  • Разрешено использование любой из функций, изменение их поведения любым способом (или изменение любых других данных)
  • Перемещение разрешено только один раз каждые 250 мс

пример

Просто очень простой пример для начала. Без комментариев и вводит 181 байт .

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

Оценка и результаты

Я буду бегать отрывки в течение одного часа подряд, и лучший счет будет засчитан. В самом деле, есть шанс, что randombotвыше, победит таким образом, но 1 часа должно быть достаточно, чтобы победить:

  • Король Bottomstacker VII : 9912
  • Королева Bottomstacker V : 9216
  • Принц Bottomstacker II : 7520
  • Господь Bottom and Right : 6308
  • Крестьянин Randombot : 1413
  • Bottomstacker IV: 12320 Дисквалифицировано за два хода за один интервал (в течение 250 мс)

Вопросы-Ответы

  • Почему этот вызов не зависит от языка через терминал?
    • По той простой причине, что так веселее. Наблюдение за игрой в графическом виде просто намного более завораживающе, чем наблюдение, когда консоль выплевывает цифры. Даже не зная javascript, вы сможете присоединиться к этой задаче, поскольку речь идет не только о языковых возможностях (просто используйте этот инструмент для минимизации кода)

3
Похоже, что это просто будет реализация JavaScript лучшего алгоритма отсюда , не так ли?
Джейсон С

2
-1 для ...best score after an hour will count... чего всего час?
user80551

3
В любом случае, во имя справедливости, я предлагаю, чтобы генератор случайных чисел одинаково заполнял каждый тестовый прогон, а также выполнял жесткие (1 час / 250 мс =) 14 400 ходов за прогон, чтобы исключить изменения этого количества из-за неточности. По крайней мере, результаты могут быть несколько более детерминированными и достойными KotH.
Джейсон С

1
750 байт или 550 байт?
Питер Тейлор,

2
Слишком ограничительно. 750 байт, 1 час, JavaScript.
TheDoctor

Ответы:


4

Я не могу кодировать JavaScript, поэтому я украл ваш ответ.

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

Он использует стратегию, которую я также использую.

РЕДАКТИРОВАТЬ: Хорошо, это просто побить ваш счет примерно через 5 минут на моей машине: D

РЕДАКТИРОВАТЬ: Забыл перейти вниз два раза, а не один раз, это код, который вы должны использовать:

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

Кроме того, в нем есть ошибка, которая перезапускается, когда она не нужна, но я не уверен, как это исправить. РЕДАКТИРОВАТЬ: он в настоящее время имеет рекорд 3116 (через 3 минуты). Я думаю, можно с уверенностью сказать, что этот алгоритм лучше, чем просто делать случайные шаги.

РЕДАКТИРОВАТЬ Более новая версия:

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

РЕДАКТИРОВАТЬ: еще одна новая версия, эта движется вниз сразу после движения вверх.

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

РЕДАКТИРОВАТЬ: Обновление: это просто побило мой личный рекорд с довольно сумасшедшим счетом 12596.

РЕДАКТИРОВАТЬ: Эй, я нижний укладчик: D Также:

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

(На самом деле не изменение, просто сжато.)

5-й раз это очарование? Точно сказать не могу. В любом случае:

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

и:

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

Еще одна новая версия:

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

и:

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

(Я надеюсь, что это не слишком большая проблема, что это продолжается за экраном игры? Я думаю, что вы могли бы добавить место, a.over=0которое часто выполняется. Я когда-нибудь это выясню.)

РЕДАКТИРОВАТЬ (снова): я отказался от стандартного режима игры и вернулся к старому способу ведения дел. Сейчас я тестирую дополнение, которое будет всегда объединяться, если есть 2 плитки по 16 или более:

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

Добавьте mfs=0внутрь addRandomTile, таким образом он возобновит счет после успешного хода.
Дэвид Малдер

И, наблюдая, как он играет сейчас, должен сказать, что все идет лучше, чем я ожидал, O :): D
Дэвид Малдер,

Только что заметил, что вы делаете два хода за один интервал в последних 2 версиях, поэтому пришлось дисквалифицировать результат 12320, который я записал. И да, мне нужно было какое-то имя: P
Дэвид Малдер,

@DavidMulder Нееееееееет! (У вас есть идея, где это происходит, чтобы я мог это исправить?)
Aprıʇǝɥʇuʎs

13
Ваш ответ полон новой версии , пожалуйста, удалите устаревший код. Или объясните разницу между версиями. Старый код по-прежнему будет доступен в журналах «редактирования», если кому-то будет интересно.
AL

3

Правый и нижний бот: 345 байт

Укороченная версия

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

Длинная версия

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

В словах

Двигайтесь вниз, затем вправо, если вы не можете двигаться, двигайтесь вверх (или, если вы не можете, двигайтесь влево), если верхний правый и нижний правый угол заполнены, двигайтесь вправо, иначе начните сначала.

Текущий рекорд

Мой лучший результат был 7668, но он был запущен на гораздо большей скорости, чем t=250(и, следовательно, косвенно дольше часа).


Этот сценарий имеет тенденцию выполнять несколько ходов за ход.
jdstankosky

3

Каким-то образом я натолкнулся на это старое соревнование этим утром, и так как я люблю 2048, я люблю ИИ, и JS - один из немногих языков, которые я в настоящее время хорошо знаю, я решил попробовать.

GreedyBot ( 607 536 байт)

Укороченная версия:

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

Длинная версия (устаревшая):

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

Более длинная версия вообще не игралась в гольф (кроме сокращения имен переменных), поэтому ее можно было немного сократить, оставаясь читаемой. Более короткая версия была создана с использованием Closure Compiler (спасибо за ссылку!), Которая в итоге оказалась на уровне 650. С некоторыми пользовательскими изменениями с моей стороны я смог сбрить еще 43 114 бит.

По сути, он ищет в таблице возможные ходы и всякий раз, когда находит, добавляет свое значение к горизонтальной или вертикальной сумме. После поиска каждого возможного движения он определяет, в каком направлении он должен двигаться, основываясь на том, больше ли сумма H или V, и направлениях, которые он уже пробовал. Вправо и Вниз - первый выбор.

Оглядываясь назад на это, я теперь понимаю, что, если любое из значений не равно нулю, первая попытка сдвинуть плитки в этом направлении гарантированно будет успешной. Возможно, я мог бы упростить раздел решения о переходе к концу, основываясь на этом.

Я оставил эту программу запущенной в течение часа и получил высокий балл 6080. Тем не менее, в одном из пробных заездов (предминификация) он набрал высокий балл 6492, всего 128 из моих личных лучших 6620. Его логика может быть значительно улучшена, если время от времени перемещать его влево, так как числа накапливаются следующим образом:

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

( РЕДАКТИРОВАТЬ: я оставил его работать немного дольше, и он справился с некоторыми 7532пунктами. Черт, моя программа умнее меня ....)

Еще один интересный момент: в одной из моих неудачных попыток создать что-то полезное, каким-то образом это закончилось так, что в любое время любые две плитки были в одной строке или столбце, они были объединены. Это привело к интересным событиям, так как случайные 2 или 4 неоднократно объединялись с самой высокой плиткой, удваивая ее каждый раз. Однажды он каким-то образом сумел набрать более 11 000 за 15 секунд, прежде чем я его отключил .... XD

Любые предложения по улучшению приветствуются!


1

Стеклоочистители: 454 байта

Просто идет вправо, вверх, влево, вверх ... повторяя (точно так же, как дворники на автомобиле), если только он не заклинило. Если он застрянет, он попытается выключить дворники и снова включить их. Наивысший результат, который я получил за час, составил 12 156 - однако, большинство из них находятся где-то между 3k и 7k.

Он будет выводить счет в консоль после каждой попытки.

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

Как следует из названия, поднимается и уходит, крадя работу Дэвида Малдера и меняя некоторые цифры (я не знаю, Джек, о Javascript, поэтому лучше всего я могу это сделать).

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

Как и сценарий Дэвида Малдера, он также выполняет несколько ходов за ход, время от времени.
jdstankosky
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.