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 true
wartością zaktualizowaną w losowych indeksach, które stworzyliśmy.
Aby wydrukować to poprawnie, używamy map
i 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ć X
albo czy O
albo _
. Aby wybrać ten drugi, używamy wartości indeksu jako naszego przewodnika.
Ciekawe rzeczy do odnotowania
Aby dowiedzieć się, czy należy wydrukować an, O
czy an _
, ((r._2 % 2) + c._2) % 2 == 0
używa się warunkowej . r._2
odnosi się do bieżącego indeksu wierszy, a c._2
odnosi się do bieżącej kolumny. Jeśli jeden jest w nieparzystym rzędzie, r._2 % 2
będzie wynosił 1, a zatem zostanie przesunięty c._2
o 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. l
odnosi się do pierwszych argumentów przekazanych do programu, podczas gdy k
odnosi 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) / 2
to sens. Dla każdego n
elementu w każdej podlistie są n / 2
bą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.
map
jest używany zamiast a, forEach
ponieważ 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 zipWithIndex
z 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, zipWithIndex
ale 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.
1
i0
zamiastO
iX
?