Tic-Tac-Latin!
To prawdziwa historia, więc imiona zostały zmienione.
Mój nauczyciel łaciny, pan Latin, stworzył swój własny wariant (bez żartów) kółko i krzyżyk. Nazwijmy to Tic-Tac-Latin. Gra jest prosta, to w zasadzie kółko i krzyżyk rozgrywane na siatce cztery na cztery.
Formalna deklaracja reguły
Linia to albo rząd, kolumna lub przekątna. Istnieją dwa symbole, „X” i „O”, ale jeden lub oba mogą być zastąpione innym symbolem.
Zdobywasz jeden punkt, gdy masz trzy symbole i jedną z pozostałych postaci.
Wynik tych ustaleń:
--- O -O-- XXXO XOOX O -XX - O - - X - --- O
Te nie oceniają:
---- XXXX ---- OOOO ---- XXX ---- OOO-
Gra jest wygrywana, gdy jeden gracz ma więcej punktów niż inny. Gra polega na losowaniu tylko wtedy, gdy plansza się zapełni.
Wyzwanie
Rozwiąż tę grę. Twoim zadaniem jest zapewnienie sposobu na wygraną lub remis, w zależności od tego, który wynik jest optymalny.
Twoje rozwiązanie może rozpocząć od pierwszego lub drugiego (i dlatego może wybrać jego symbol). Nie jest obowiązkowe wdrożenie interaktywnej gry, w której użytkownik wprowadza ruchy i zmienia się odpowiedni ekran. Może to być również funkcja lub program, który przyjmuje dane wejściowe jako stan gry i generuje nową planszę lub opis ich ruchu . Każda opcja musi działać w ciągu około dziesięciu sekund na każdy wykonany ruch.
Rozegranie gracza z dowolną sekwencją ruchów musi dać optymalny wynik. Oznacza to, że możesz założyć, że pozycja wejściowa jest możliwa do osiągnięcia w grze z twoim odtwarzaczem. Zgłoszenia muszą być deterministyczne i niekoniecznie muszą dostarczać dowód optymalności, ale jeśli zostaną złamane (przez bicie), zostaną uznane za nieważne (możesz je pominąć, ale dodać (pęknięty) w nagłówku).
Jest to nietrywialne zadanie, więc każde prawidłowe zgłoszenie jest imponujące i godne zaakceptowanego tiksa, ale ustawię golfa jako główne kryterium wygranej.
Zwycięzca jest wybierany przez przewijanie tej listy do momentu wybrania jednego zwycięzcy.
- Najkrótsza rozwiązana implementacja, która zawsze wygrywa
- Najkrótsza realizacja
Odpowiedzi:
Perl, 147 bajtów (niekonkurencyjny, zajmuje więcej niż 10 sekund na ruch)
Obejmuje +4 za
-0p
Program jest odtwarzany
X
. Będzie grać w idealną grę.Wprowadź tablicę na STDIN, np .:
Ouptut będzie tą samą deską, a wszystkie zostaną
X
zastąpioneO
i odwrotnie. Puste miejsca zostaną wypełnione liczbą wskazującą wynik, jeśli X tam zagra,1
co oznacza, że wynikiem będzie zwycięstwo,2
remis i3
przegrana. Ukończona gra po prostu zwraca tę samą pozycję z odwróconymi kolorami.W tym przykładzie dane wyjściowe to:
Tak więc pozycja jest wygrana,
X
jeśli gra w 3 miejscach u góry i po lewej stronie. Wszystkie pozostałe ruchy tracą.Ten mylący wynik jest w rzeczywistości wygodny, jeśli chcesz wiedzieć, jak gra się rozwija po ruchu. Ponieważ program zawsze gra
X
, musisz zamienićX
iO
zobaczyć ruchyO
. Na przykład tutaj jest całkiem jasne, żeX
wygrywa, grając w lewym górnym rogu, ale co jeśliX
gra w trzeciej pozycji u góry? Po prostu skopiuj dane wyjściowe, umieśćO
miejsce w miejscu wybranego ruchu i zamień ponownie wszystkie inne liczby-
, więc tutaj:Wynikające z:
Oczywiście każdy ruch
O
powinien przegrać, więc jak on przegrywa, jeśli gra w lewym górnym rogu? Ponownie zrób to, umieszczającO
w lewym górnym rogu i zastępując cyfry-
:Dający:
Więc X ma tylko jedną drogę do zwycięstwa:
Dający
Sytuacja
O
pozostaje beznadziejna. Teraz widać, że każdy ruch pozwalaX
od razu wygrać. Spróbujmy przynajmniej wybrać 3 O z rzędu:Dający:
X
gra jedyny zwycięski ruch (zauważ, że robi toXXXO
wzdłuż trzeciej kolumny:Oto wynik:
ponieważ gra była już ukończona. Wygraną możesz zobaczyć w trzeciej kolumnie.
Rzeczywisty program
tictaclatin.pl
:Zastosowane do pustej planszy, ocenia 9506699 pozycji, co zajmuje 30 Gb i 41 minut na moim komputerze. Wynik to:
Tak więc każdy ruch początkowy dobiera. Ta gra to remis.
Ekstremalne użycie pamięci jest spowodowane głównie przez użycie rekurencji
do$0
. Korzystanie z tej 154-bajtowej wersji przy użyciu zwykłej funkcji wymaga 3Gb i 11 minut:co jest bardziej znośne (ale wciąż za dużo, coś wciąż musi przeciekać pamięć).
Połączenie kilku przyspieszeń prowadzi do tej 160-bajtowej wersji (5028168 pozycji, 4 minuty i 800 M dla pustej planszy):
Ten ostatni wykorzystuje
0
do wygranej (nie mylić zO
),1
do remisu i2
do przegranej. Wydajność tego jest również bardziej myląca. Wypełnia wygrywający ruch dla X w przypadku wygranej bez zamiany kolorów, ale jeśli gra wejściowa została już wygrana, nadal zamienia kolor i nie wypełnia żadnego ruchu.Wszystkie wersje oczywiście przyspieszają i zużywają mniej pamięci w miarę zapełniania się płyty. Szybsze wersje powinny wygenerować ruch w niecałe 10 sekund, jak tylko zostaną wykonane 2 lub 3 ruchy.
Zasadniczo ta 146-bajtowa wersja powinna również działać:
ale na mojej maszynie uruchamia błąd perla i zrzuca rdzeń.
Wszystkie wersje będą w zasadzie nadal działać, jeśli
$$_||=
usunięte zostanie buforowanie 6-bajtowej pozycji, ale zajmuje to tyle czasu i pamięci, że działa tylko w przypadku prawie wypełnionych kart. Ale teoretycznie mam przynajmniej 140 bajtowe rozwiązanie.Jeśli umieścisz
$\=
(koszt: 3 bajty) tuż przed$@<=>0
czym każda płyta wyjście nastąpi statusu całego Zarządu:1
doX
zwycięstw,0
za remis i-1
za utratę.Oto interaktywny sterownik oparty na najszybszej wersji wspomnianej powyżej. Kierowca nie ma logiki po zakończeniu gry, więc musisz się zatrzymać. Kod w golfa jednak wie. Jeśli sugerowany ruch powróci bez
-
zastąpienia go przez cokolwiek, gra się kończy.źródło
$move
zadeklarowano w linii 11. Nie mam pojęcia, czy istnieje ludzka heurystyka. Ten program po prostu robi minimax na drzewie gry, nie ma żadnej wiedzy strategicznej.JavaScript (ES6) 392 bajty
Stosowanie
„Bot” zagra na drugim miejscu.
Narysuj siatkę 4x4, które są ponumerowane w następujący sposób:
Uruchommy to w konsoli przeglądarki: po prostu umieść
f=
przed kodemTak więc, jeśli chcę zacząć od
1
, ucieknęf([1])([])
i da mi to14
.Niezły ruch ... Co jeśli zagram
2
później?f([2,1])([14])
. Wróci13
.Pozwól mi się poddać. Grać
3
.f([3,2,1])([14,13])
. Och0
! Masz mnie!Grać
0
?f([0,2,1])([14,13])
.15
Ok, grajmy dalej ...Uwaga
Graj interaktywnie. Zacznij od
f([your-step])([])
.Przygotuj następny krok. (Zobacz demo powyżej)
Pomóż „botowi” wprowadzić swoje kroki. Nie da ci dobrych wyników, jeśli nadasz mu losowe ustawienie. (Jak
f([1,2,4])([14,12])
da14
- Hej bot chciał zagrać13
w swoim drugim ruchu!Krótkie podsumowanie
Tak długo, jak się nie poddajesz, bot będzie wykonywać ruch lustra.
Dzięki @EHTproductions za poinformowanie mnie, że źle przeczytałem zasady gry i wskazówki dotyczące gry w golfa: P
Teraz wykryje również, czy dostał mat. Jeśli tak, zablokuj to!
Jego priorytety: Blok> Lustro> (awaryjne) szukają sposobów na odtworzenie lustra
źródło
3,2,1
dla ciebie i0
dla bota wygrana?[0,1,2,3].map(i=>{k(n=>n%4==i);k(n=>Math.floor(n/4)==i);})
można grać w golfa[0,1,2,3].map(i=>k(n=>n%4==i)+k(n=>(n/4|0)==i))
.