2048 Bot Challenge

19

Zostaliśmy klonowania 2048, analizując 2048, ale dlaczego nie graliśmy go jeszcze? Napisz fragment kodu javascript o długości 555 bajtów, aby automatycznie grać w 2048, liczy się najlepszy wynik po godzinie (patrz punktacja poniżej).

Ustawiać:

Przejdź do 2048 i uruchom:

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

a jest przedmiotem do kontrolowania gry.

Zasady:

Po instalacji możesz uruchomić 555 bajtów javascript z konsoli, aby kontrolować grę. Kod źródłowy gry można znaleźć tutaj (w tym komentarze).

  • Może robić tylko rzeczy, które są możliwe dla użytkownika:
    • a.move(n) aby uruchomić kluczową akcję w jednym z 4 kierunków.
      • 0: góra, 1: prawo, 2: dół, 3: lewo
    • a.restart() aby ponownie uruchomić grę. Ponowne uruchomienie jest dozwolone w środku gry.
  • Informacje o stanie gry można znaleźć w a.grid.cells. Ta informacja jest tylko do odczytu
  • Podłączenie do dowolnej funkcji jest dozwolone, zmiana ich zachowania w jakikolwiek sposób nie jest (ani zmiana jakichkolwiek innych danych)
  • Przenoszenie jest dozwolone tylko raz na 250ms

Przykład

To bardzo prosty przykład na początek. Bez komentarzy i wprowadza 181 bajtów .

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

Punktacja i wyniki

Będę działał przez jedną godzinę z rzędu, a najlepszy wynik będzie się liczył. Rzeczywiście istnieje szansa, że randombotpowyżej wygra w ten sposób, ale 1 godzina powinna wystarczyć:

  • Król Bottomstacker VII : 9912
  • Królowa Bottomstacker V : 9216
  • Książę Bottomstacker II : 7520
  • Pan Bottom and Right : 6308
  • Chłop Randombot : 1413
  • Bottomstacker IV: 12320 Zdyskwalifikowany za wykonanie dwóch ruchów w jednym przedziale (w ciągu 250 ms)

FAQ

  • Dlaczego to wyzwanie nie jest agnostyczne pod względem językowym przez terminal?
    • Z tego prostego powodu, że jest to tak zabawne. Graficzne oglądanie samej gry jest po prostu o wiele bardziej fascynujące niż konsola wyrzucająca liczby. Nawet nie znając javascript, powinieneś być w stanie dołączyć do tego wyzwania, ponieważ nie chodzi przede wszystkim o funkcje językowe (użyj tego narzędzia, aby zminimalizować kod)
David Mulder
źródło
3
Wygląda na to, że będzie to po prostu pakiet implementacji JavaScript najlepszego algorytmu stąd , nie?
Jason C
2
-1 dla ...best score after an hour will count... Dlaczego tylko godzina?
user80551
3
W każdym razie sugeruję, w imię uczciwości, zaszczepienie generatora liczb losowych tak samo dla przebiegu testowego każdej odpowiedzi, a także wykonanie twardego (1 godzina / 250 ms =) 14,400 ruchów na bieg, aby wyeliminować zmiany tej liczby do niedokładności pomiaru czasu. Przynajmniej wyniki mogą być nieco bardziej deterministyczne i godne KotH.
Jason C
1
750 bajtów czy 550 bajtów?
Peter Taylor
2
Zbyt restrykcyjne. 750 bajtów, 1 godzina, JavaScript.
TheDoctor

Odpowiedzi:

4

Nie mogę kodować javascript, więc ukradłem twoją odpowiedź.

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

Wykorzystuje strategię, której również używam.

EDYCJA: Fajnie, właśnie pobiłem Twój wynik po około 5 minutach na mojej maszynie: D

EDYCJA: Zapomniałeś zejść dwa razy w dół zamiast tylko raz, to jest kod, którego powinieneś użyć:

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

Jest też błąd, który uruchamia się ponownie, gdy nie jest potrzebny, ale nie jestem pewien, jak to naprawić. EDYCJA: Obecnie ma najwyższy wynik 3116 (po 3 minutach). Myślę, że można bezpiecznie powiedzieć, że ten algorytm jest lepszy niż wykonywanie przypadkowych ruchów.

EDYCJA Nowsza wersja:

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

EDYCJA: Kolejna nowa wersja, ta przechodzi w dół bezpośrednio po przejściu w górę.

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

EDYCJA: Aktualizacja: właśnie pobił mój osobisty rekord z dość szalonym wynikiem 12596.

EDYCJA: Hej, jestem na dole: D Takż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);m||mfs++;5<mfs&&(a.move(0),c=4);10<mfs&&(mfs=0,a.restart())},250);

(W rzeczywistości nie jest to zmiana, tylko skompresowane).

Piąty raz to urok? Niepewny. W każdym razie:

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

i:

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

Kolejna nowa wersja:

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

i:

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

(Mam nadzieję, że nie jest to zbyt wielkim problemem, że problem ten utrzymuje się za ekranem Gameover? Myślę, że możesz dodać coś a.over=0, co będzie często wykonywane. Pewnego dnia to rozwiążę ).

EDYCJA (ponownie): zrezygnowałem ze standardowego sposobu zmiany gry i powróciłem do starego sposobu robienia rzeczy. Testuję teraz dodatek, który zawsze będzie się łączyć, jeśli będą razem 2 płytki po 16 lub więcej:

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
źródło
Dodaj do mfs=0środka addRandomTile, w ten sposób zacznie się odliczanie po udanym ruchu.
David Mulder
I oglądając, jak gra teraz, muszę powiedzieć, że idzie lepiej niż się spodziewałem O :): D
David Mulder
Właśnie zauważyłem, że wykonujesz dwa ruchy w jednym przedziale w ostatnich 2 wersjach, więc musiałem zdyskwalifikować zapisany przeze mnie wynik 12320. I tak, potrzebowałem jakiegoś imienia: P
David Mulder
@DavidMulder Nooooooo! (Czy masz pojęcie, gdzie to się dzieje, abym mógł to naprawić?)
2014
13
Twoja odpowiedź jest pełna nowej wersji , usuń nieaktualny kod. Lub wyjaśnij różnice między wersjami. Stary kod będzie nadal dostępny w dziennikach „edycji”, jeśli ktoś jest zainteresowany.
AL
3

Prawy i dolny bot: 345 bajtów

Krótka wersja

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

Długa wersja

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

W słowach

Przesuń w dół, a następnie w prawo, jeśli nie możesz się poruszać, przejdź w górę (lub jeśli nie możesz, przejdź w lewo), jeśli zarówno prawy górny, jak i prawy dolny róg są wypełnione, przesuń w prawo, w przeciwnym razie zacznij od nowa.

Aktualny wynik

Mój najlepszy wynik to 7668, ale był on prowadzony z dużo większą prędkością niż t=250(a zatem pośrednio dłużej niż godzinę).

David Mulder
źródło
Ten skrypt ma tendencję do wykonywania wielu ruchów na turę.
jdstankosky
3

Jakoś natknąłem się na ten starszy konkurs dziś rano, a ponieważ kocham 2048, uwielbiam sztuczną inteligencję, a JS jest jednym z niewielu języków, które obecnie znam dobrze, pomyślałem, że dam temu szansę.

GreedyBot ( 607 536 bajtów)

Krótka wersja:

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

Wersja długa (nieaktualna):

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

Dłuższa wersja w ogóle nie była golfa (oprócz kurczących się nazw zmiennych), więc można ją było nieco skrócić, jednocześnie zachowując czytelność. Krótsza wersja została stworzona przy użyciu kompilatora Closure (dzięki za link!), Który skończył się na 650. Z pewnymi modyfikacjami z mojej strony udało mi się zgolić kolejne 43 114 bitów.

Zasadniczo wyszukuje w siatce możliwe ruchy i za każdym razem, gdy je znajdzie, dodaje swoją wartość do sumy poziomej lub pionowej. Po przeszukaniu każdego możliwego ruchu określa, w którym kierunku powinien się poruszać, na podstawie tego, czy suma H lub V jest wyższa, i kierunków, które już próbował. Prawo i dół to pierwsze opcje.

Patrząc wstecz na to, zdaję sobie teraz sprawę, że jeśli suma jest różna od zera, pierwsza próba przesunięcia płytek w tym kierunku z pewnością się powiedzie. Być może mógłbym na tej podstawie uprościć sekcję dotyczącą decydowania o ruchu.

Zostawiłem ten program uruchomiony na godzinę i skończyłem z wysokim wynikiem 6080. Jednak w jednym z testów (przedminifikacja) udało się osiągnąć wysoki wynik 6492, zaledwie 128 za moim osobistym wynikiem 6620. Jego logikę można znacznie poprawić, od czasu do czasu przesuwając ją w lewo, ponieważ liczby zwykle się piętrzą:

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

( EDYCJA: Zostawiłem go trochę dłużej i udało mu się zdobyć kilka 7532punktów. Cholera, mój program jest mądrzejszy ode mnie ....)

Jeszcze jedna ciekawa ciekawostka: w jednej z moich niedbałych prób stworzenia czegoś użytecznego, jakoś skończyło się to tak, że za każdym razem, gdy dwa kafelki były w tym samym rzędzie lub kolumnie, były łączone. Doprowadziło to do interesujących zmian, ponieważ losowe 2 lub 4 były wielokrotnie łączone z najwyższą płytką, podwajając ją za każdym razem. Pewnego razu udało mu się zdobyć ponad 11 000 w ciągu 15 sekund, zanim go wyłączyłem ... XD

Wszelkie sugestie dotyczące ulepszeń są bardzo mile widziane!

ETHprodukcje
źródło
1

Wycieraczki przedniej szyby: 454 bajty

Po prostu idzie w prawo, w górę, w lewo, w górę ... powtarzając (podobnie jak wycieraczki w samochodzie), chyba że się zablokuje. Jeśli się zablokuje, spróbuje wyłączyć wycieraczki i włączyć je ponownie. Najwyższy wynik, jaki uzyskałem w ciągu godziny, wynosił 12 156 - Jednak większość wyników mieści się w przedziale 3–7 tys.

Po każdej próbie wyświetli wynik w konsoli.

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
źródło
0

UpAndLeftBot

Jak sugeruje tytuł, przesuwa się w górę i w lewo poprzez kradzież pracy Davida Muldera i zamianę niektórych liczb (nie znam Jacka o JavaScript, więc najlepiej mogę to zrobić).

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
źródło
Podobnie jak scenariusz Davida Muldera, wykonuje on także wiele ruchów na turę co jakiś czas.
jdstankosky