2048 Bot Challenge


19

Estamos clonando 2048, analisando 2048, mas por que ainda não o reproduzimos? Escreva um trecho javascript de 555 bytes para reproduzir 2048 automaticamente; a melhor pontuação após uma hora será contada (veja a pontuação abaixo).

Configuração:

Vá para 2048 e execute:

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

a é o objeto para controlar o jogo.

Regras:

Após a instalação, você pode executar 555 bytes de javascript no console para controlar o jogo. O código fonte do jogo pode ser encontrado aqui (incluindo comentários).

  • Só pode fazer coisas possíveis para o usuário:
    • a.move(n) para acionar uma ação principal em qualquer uma das quatro direções.
      • 0: cima, 1: direita, 2: baixo, 3: esquerda
    • a.restart() para reiniciar o jogo. É permitido reiniciar no meio do jogo.
  • Informações sobre o estado do jogo podem ser encontradas em a.grid.cells. Esta informação é somente leitura
  • É permitido conectar-se a qualquer uma das funções, alterar seu comportamento de qualquer maneira não é (ou alterar outros dados)
  • A mudança é permitida apenas uma vez a cada 250ms

Exemplo

Apenas um exemplo muito simples para começar. Sem comentários e inserindo 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);

Pontuação e resultados

Estarei executando os trechos por uma hora seguida e a melhor pontuação contará. De fato, há uma chance de que randomboto vencedor ganhe dessa maneira, mas 1 hora deve ser suficiente para vencê-lo:

  • Rei Bottomstacker VII : 9912
  • Rainha Bottomstacker V : 9216
  • Prince Bottomstacker II : 7520
  • Senhor Bottom and Right : 6308
  • Camponês Randombot : 1413
  • Bottomstacker IV: 12320 Desqualificado por fazer dois movimentos em um intervalo (dentro de 250ms)

Perguntas frequentes

  • Por que esse desafio não é independente de idioma no terminal?
    • Pela simples razão de que é mais divertido assim. Assistir a um jogo se jogar graficamente é simplesmente muito mais fascinante do que ver um console cuspir números. Mesmo sem conhecer javascript, você poderá participar desse desafio, pois não se trata principalmente de recursos de idioma (basta usar esta ferramenta para minimizar o código)

3
Parece que acabará sendo um monte de implementações em JavaScript do melhor algoritmo daqui , não?
Jason C

2
-1 para ...best score after an hour will count... Por que apenas uma hora?
user80551

3
Em qualquer caso, sugiro, em nome da justiça, semear o gerador de números aleatórios da mesma forma para cada execução de teste de resposta e também executar 14.400 movimentos por hora (1 hora / 250 ms =) para eliminar variações dessa contagem devido imprecisões de tempo. Pelo menos os resultados poderiam ser um pouco mais determinísticos e dignos de um KotH.
Jason C

11
750 bytes ou 550 bytes?
Peter Taylor

2
Muito restritivo. 750 bytes, 1 hora, JavaScript.
precisa saber é o seguinte

Respostas:


4

Como não consigo codificar o javascript, roubei sua resposta.

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

Ele usa a estratégia que eu também uso.

Edição: Bom, apenas bateu sua pontuação após cerca de 5 minutos na minha máquina: D

EDIT: Esqueceu de descer duas vezes em vez de apenas uma vez, este é o código que você deve usar:

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

Além disso, há um bug que é reiniciado quando não é necessário, mas não tenho certeza de como corrigir isso. EDIT: Atualmente, possui um recorde de 3116 (após 3 minutos). Eu acho que é seguro dizer que esse algoritmo é melhor do que apenas fazer movimentos aleatórios.

EDIT Versão mais 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: Outra nova versão, esta desce diretamente após subir.

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

Edição: Atualização: apenas quebrou meu recorde pessoal com uma pontuação louca de 12596.

EDIT: Ei, eu sou o bottomstacker: D Também:

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

(Na verdade, não é uma alteração, apenas compactado.)

Quinta vez é um charme? Não tenho certeza. Enfim:

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

Outra nova versão:

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

(Espero que não seja um problema demais que isso continue por trás da tela do gameover? Acho que você pode adicionar um a.over=0lugar que seja executado com frequência. Vou descobrir isso algum dia.)

EDIT (novamente): deixei de lado a maneira padrão de jogo e voltei à velha maneira de fazer as coisas. Agora estou testando uma adição que sempre será mesclada se houver 2 peças de 16 ou mais juntas:

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

Adicione por mfs=0dentro addRandomTile, assim ele reiniciará a contagem após uma jogada bem-sucedida.
David Mulder

E vê-lo jogar agora, tenho que dizer, ele está fazendo melhor do que eu esperava O :): D
David Mulder

Só notei que você está fazendo dois movimentos em um intervalo nas duas últimas versões, então teve que desqualificar a pontuação 12320 que eu havia registrado. E sim, eu precisava de algum tipo de nome: P
David Mulder

@DavidMulder Nooooooo! (Você tem alguma idéia de onde isso acontece, para que eu possa corrigi-lo?)
ɐɔıʇǝɥʇuʎs

13
Sua resposta está cheia de nova versão . Remova o código desatualizado. Ou explique as diferenças entre as versões. O código antigo ainda estará acessível nos logs de "edição" se alguém estiver interessado.
AL

3

Bot direito e baixo: 345 bytes

Versão curta

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

Versão longa

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

Em palavras

Mova para baixo e para a direita, se não puder se mover, para cima (ou se não puder, para a esquerda), se o canto superior direito e o canto inferior direito estiverem preenchidos, mova para a direita, caso contrário, comece novamente.

Pontuação atual

Minha melhor pontuação foi 7668, mas foi executada a uma velocidade muito maior do que t=250(e, portanto, indiretamente, mais de uma hora).


Esse script tem tendência a executar vários movimentos por turno.
Jdstankosky

3

De alguma forma, deparei-me com este concurso mais antigo esta manhã e, como amo 2048, amo a IA e o JS é um dos poucos idiomas que conheço atualmente, imaginei que tentaria.

GreedyBot ( 607 536 bytes)

Versão curta:

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

Versão longa (desatualizada):

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

A versão mais longa não possuía nenhum tipo de golfe (exceto os nomes de variáveis ​​reduzidos); portanto, ela poderia ser reduzida bastante enquanto ainda estivesse legível. A versão mais curta foi criada usando o Closure Compiler (obrigado pelo link!), Que terminou em 650. Com algumas modificações personalizadas da minha parte, pude cortar outros 43 114 bits.

Basicamente, ele pesquisa na grade possíveis movimentos e, sempre que encontra um, agrega seu valor ao total horizontal ou vertical. Depois de pesquisar todos os movimentos possíveis, ele determina em qual direção ele deve se mover, com base em se o total de H ou V for maior e nas direções que ele já tentou. Direita e Baixo são as primeiras escolhas.

Olhando para trás, percebo agora que, se qualquer total for diferente de zero, a primeira tentativa de deslizar os ladrilhos nessa direção certamente será bem-sucedida. Talvez eu possa simplificar a seção de decisão de mudança no final com base nisso.

Eu deixei este programa em execução por uma hora e acabei com uma pontuação alta de 6080. No entanto, em uma das execuções de teste (pré-minificação), ele conseguiu uma pontuação alta de 6492apenas 128 atrás do meu melhor 6620. Sua lógica pode ser muito melhorada, movendo-a para a esquerda ocasionalmente, pois os números tendem a se acumular assim:

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

( EDIT: Eu deixei isso funcionando por mais algum tempo, e ele conseguiu alguns 7532pontos. Droga, meu programa é mais inteligente que eu ....)

Um petisco mais interessante: em uma de minhas tentativas Glitched em criar algo útil, de alguma forma acabou assim que a qualquer momento quaisquer duas peças estavam na mesma linha ou coluna, eles foram combinados. Isso levou a desenvolvimentos interessantes, já que os 2s ou 4s aleatórios se combinavam repetidamente com o ladrilho mais alto, dobrando-o sempre. Uma vez, ele conseguiu marcar mais de 11.000 em 15 segundos antes de eu desligá-lo .... XD

Todas as sugestões de melhoria são bem-vindas!


1

Limpadores de pára-brisa: 454 bytes

Simplesmente vai Direita, Cima, Esquerda, Cima ... repetindo (assim como os limpadores de carro), a menos que fique preso. Se ficar congestionado, tentará desligar os limpadores e ligá-los novamente. A pontuação mais alta que obtive em uma hora foi 12.156 - No entanto, a maioria das pontuações está entre 3 e 7k.

Ele produzirá a pontuação no console após cada tentativa.

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

Como o título sugere, move-se para cima e para a esquerda roubando o trabalho de David Mulder e trocando alguns números (não sei nada sobre Javascript, o melhor que posso fazer é isso).

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

Assim como o script de David Mulder, isso também realiza vários movimentos por turno de vez em quando.
Jdstankosky
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.