Scala , 764 bajty
object B{
def main(a: Array[String]):Unit={
val v=false
val (m,l,k,r,n)=(()=>print("\033[H\033[2J\n"),a(0)toInt,a(1)toInt,scala.util.Random,print _)
val e=Seq.fill(k, l)(v)
m()
(0 to (l*k)/2-(l*k+1)%2).foldLeft(e){(q,_)=>
val a=q.zipWithIndex.map(r => r._1.zipWithIndex.filter(c=>
if(((r._2 % 2) + c._2)%2==0)!c._1 else v)).zipWithIndex.filter(_._1.length > 0)
val f=r.nextInt(a.length)
val s=r.nextInt(a(f)._1.length)
val i=(a(f)._2,a(f)._1(s)._2)
Thread.sleep(1000)
m()
val b=q.updated(i._1, q(i._1).updated(i._2, !v))
b.zipWithIndex.map{r=>
r._1.zipWithIndex.map(c=>if(c._1)n("X")else if(((r._2 % 2)+c._2)%2==0)n("O")else n("_"))
n("\n")
}
b
}
}
}
Jak to działa
Algorytm najpierw wypełnia sekwencję 2D fałszywymi wartościami. Określa liczbę iteracji (otwartych pól) na podstawie wprowadzonych argumentów wiersza poleceń. Tworzy fałd z tą wartością jako górną granicą. Wartość całkowita zwijania jest używana domyślnie tylko jako sposób na zliczenie liczby iteracji, dla których algorytm powinien działać. Wypełniona sekwencja, którą wcześniej utworzyliśmy, jest sekwencją początkową składania. Służy to do generowania nowej sekwencji 2D fałszywych wartości z ich współodpowiedzialnymi indecies.
Na przykład,
[[false, true],
[true, false],
[true, true]]
Zostanie zamieniony w
[[(false, 0)], [(false, 1)]]
Zauważ, że wszystkie listy, które są całkowicie prawdziwe (mają długość 0) są pomijane na liście wyników. Algorytm następnie pobiera tę listę i wybiera losową listę z listy najbardziej oddalonych. Losowa lista jest wybrana jako losowy rząd, który wybieramy. Z tego losowego wiersza ponownie znajdujemy losową liczbę, indeks kolumny. Kiedy znajdziemy te dwa losowe wskaźniki, uśpimy wątek, w którym jesteśmy, przez 1000 milisekund.
Po snu wyczyścimy ekran i utworzymy nową tablicę z truewartością zaktualizowaną w losowych indeksach, które stworzyliśmy.
Aby wydrukować to poprawnie, używamy mapi kompresujemy ją wraz z indeksem mapy, abyśmy mieli to w swoim kontekście. Używamy prawdziwej wartości sekwencji, aby sprawdzić, czy powinniśmy wydrukować Xalbo czy Oalbo _. Aby wybrać ten drugi, używamy wartości indeksu jako naszego przewodnika.
Ciekawe rzeczy do odnotowania
Aby dowiedzieć się, czy należy wydrukować an, Oczy an _, ((r._2 % 2) + c._2) % 2 == 0używa się warunkowej . r._2odnosi się do bieżącego indeksu wierszy, a c._2odnosi się do bieżącej kolumny. Jeśli jeden jest w nieparzystym rzędzie, r._2 % 2będzie wynosił 1, a zatem zostanie przesunięty c._2o jeden w warunkowym. Zapewnia to, że w nieparzystych wierszach kolumny są przesuwane o 1 zgodnie z przeznaczeniem.
Wydrukowanie łańcucha "\033[H\033[2J\n", zgodnie z przeczytaną odpowiedzią Stackoverflow, czyści ekran. Pisze bajty do terminala i robi fajne rzeczy, których tak naprawdę nie rozumiem. Ale uważam, że jest to najłatwiejszy sposób, aby to zrobić. Jednak nie działa na emulatorze konsoli Intellij IDEA. Będziesz musiał uruchomić go za pomocą zwykłego terminala.
Innym równaniem, które może wydawać się dziwne, gdy patrzymy na ten kod po raz pierwszy, jest (l * k) / 2 - (l * k + 1) % 2. Najpierw odmytajmy nazwy zmiennych. lodnosi się do pierwszych argumentów przekazanych do programu, podczas gdy kodnosi się do drugiego. Aby przetłumaczyć go (first * second) / 2 - (first * second + 1) % 2. Celem tego równania jest wymyślenie dokładnej liczby iteracji potrzebnych do uzyskania sekwencji wszystkich X-ów. Za pierwszym razem, gdy to zrobiłem, zrobiłem to, co miało (first * second) / 2to sens. Dla każdego nelementu w każdej podlistie są n / 2bąbelki, które możemy otworzyć. Jednak to się psuje, gdy mamy do czynienia z takimi danymi wejściowymi jak(11 13). Musimy obliczyć iloczyn dwóch liczb, uczynić go nieparzystym, jeśli jest parzysty, a nawet jeśli jest nieparzysty, a następnie przyjąć mod tego o 2. Działa to, ponieważ nieparzyste wiersze i kolumny będą wymagały jednej mniej iteracji aby dojść do końcowego wyniku.
mapjest używany zamiast a, forEachponieważ ma mniej znaków.
Rzeczy, które prawdopodobnie można poprawić
Jedną z rzeczy, które naprawdę mnie denerwują w tym rozwiązaniu, jest częste używanie zipWithIndex. Zajmuje tak wiele postaci. Próbowałem to zrobić, aby móc zdefiniować własną funkcję jednoznakową, która po prostu działałaby zipWithIndexz przekazaną wartością. Okazuje się jednak, że Scala nie pozwala, aby funkcja anonimowa miała parametry typu. Jest prawdopodobnie inny sposób robienia tego, co robię bez użycia, zipWithIndexale nie myślałem zbyt wiele o sprytnym sposobie zrobienia tego.
Obecnie kod działa w dwóch przebiegach. Pierwszy generuje nową planszę, podczas gdy drugie przejście drukuje ją. Myślę, że gdyby połączyć te dwa przejścia w jedno przejście, zaoszczędziłoby to kilka bajtów.
To pierwszy kod golfowy, jaki zrobiłem, więc jestem pewien, że jest wiele do zrobienia. Jeśli chcesz zobaczyć kod, zanim zoptymalizuję jak najwięcej bajtów, oto jest.
1i0zamiastOiX?