Scala , 764 байта
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
}
}
}
Как это работает
Алгоритм сначала заполняет 2D-последовательность ложными значениями. Он определяет, сколько итераций (открытых блоков) существует на основе введенных аргументов командной строки. Он создает сгиб с этим значением в качестве верхней границы. Целочисленное значение сгиба неявно используется только как способ подсчета количества итераций, которые должен выполнить алгоритм. Заполненная последовательность, которую мы создали ранее, является начальной последовательностью для сгиба. Это используется при генерации новой двумерной последовательности ложных значений с соответствующими им единицами.
Например,
[[false, true],
[true, false],
[true, true]]
Будет превращен в
[[(false, 0)], [(false, 1)]]
Обратите внимание, что все списки, которые являются полностью истинными (имеют длину 0), исключаются из списка результатов. Затем алгоритм берет этот список и выбирает случайный список во внешнем списке. Случайный список выбирается как случайная строка, которую мы выбираем. Из этой случайной строки мы снова находим случайное число, индекс столбца. Как только мы находим эти два случайных индекса, мы спим в потоке, в котором мы находимся, в течение 1000 миллисекунд.
После того, как мы закончили спать, мы очищаем экран и создаем новую доску со true
значением, обновленным в случайных индексах, которые мы создали.
Чтобы правильно распечатать это, мы используем map
и заархивируем его индексом карты, чтобы мы имели это в нашем контексте. Мы используем значение истинности последовательности относительно того, должны ли мы печатать X
или или O
или или _
. Чтобы выбрать последнее, мы используем значение индекса в качестве руководства.
Интересные вещи на заметку
Чтобы выяснить, следует ли печатать O
или _
, используется условное выражение ((r._2 % 2) + c._2) % 2 == 0
. r._2
ссылается на текущий индекс строки, а c._2
ссылается на текущий столбец. Если один находится в нечетной строке, r._2 % 2
будет 1, поэтому смещение c._2
на 1 в условном выражении. Это гарантирует, что в нечетных строках столбцы перемещаются на 1, как и предполагалось.
Распечатка строки в "\033[H\033[2J\n"
соответствии с прочитанным мною ответом Stackoverflow очищает экран. Он пишет байты в терминал и делает что-то напуганное, чего я не очень понимаю. Но я нашел, что это самый простой способ сделать это. Однако он не работает на консольном эмуляторе Intellij IDEA. Вам придется запустить его с помощью обычного терминала.
Другое уравнение можно было бы найти странно видеть , когда первый глядя на этот код (l * k) / 2 - (l * k + 1) % 2
. Во-первых, давайте демистифицируем имена переменных. l
ссылается на первые аргументы, переданные в программу, а k
ссылается на второй. Для того, чтобы перевести его, (first * second) / 2 - (first * second + 1) % 2
. Цель этого уравнения - найти точное количество итераций, необходимое для получения последовательности всех X. В первый раз, когда я сделал это, я просто сделал (first * second) / 2
так, чтобы это имело смысл. Для каждого n
элемента в каждом подсписке есть n / 2
всплывающие подсказки. Тем не менее, это нарушается при работе с такими ресурсами, как(11 13)
, Нам нужно вычислить произведение двух чисел, сделать его нечетным, если оно четным, и даже если оно нечетным, и затем принять его значение на 2. Это работает, потому что строки и столбцы, которые являются нечетными, потребуют на одну итерацию меньше чтобы добраться до конечного результата.
map
используется вместо a, forEach
потому что в нем меньше символов.
Вещи, которые можно улучшить
Одна вещь, которая действительно беспокоит меня об этом решении - это частое использование zipWithIndex
. Это занимает так много персонажей. Я попытался сделать так, чтобы я мог определить свою собственную односимвольную функцию, которая будет просто выполнять zipWithIndex
с переданным значением. Но оказывается, что Scala не позволяет анонимной функции иметь параметры типа. Возможно, есть другой способ сделать то, что я делаю, без использования, zipWithIndex
но я не слишком много думал о том, как сделать это умным способом.
В настоящее время код выполняется в два этапа. Первый генерирует новую доску, а второй проход печатает ее. Я думаю, что если объединить эти два прохода в один, это сэкономит пару байтов.
Это первый кодовый гольф, который я сделал, поэтому я уверен, что есть много возможностей для совершенствования. Если вы хотите увидеть код до того, как я максимально оптимизирую его для байтов, вот оно.
1
и0
вместоO
иX
?