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 послідовність помилковими значеннями. Він визначає, скільки ітерацій (відкритих полів) існує на підставі аргументів командного рядка, що містяться. Це створює складку з цим значенням як верхню межу. Ціле значення згину використовується лише неявно як спосіб підрахунку кількості ітерацій, за якими повинен працювати алгоритм. Заповнена нами послідовність заповнена послідовністю є початковою послідовністю для складання. Це використовується для генерування нової 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 за призначенням.
"\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
використовується замість forEach
символу a, оскільки він містить менше символів.
Речі, які, ймовірно, можуть бути покращені
Одне, що насправді клопоче мене про це рішення, - це часте використання zipWithIndex
. Це займає стільки символів. Я намагався зробити це так, щоб я міг визначити свою власну функцію з одним символом, яка просто виконувала бzipWithIndex
зі значенням, яке передається. Але виявляється, що Scala не дозволяє анонімній функції мати параметри типу. Можливо, є ще один спосіб робити те, що я роблю, не використовуючи, zipWithIndex
але я не надто замислювався над розумним способом зробити це.
В даний час код працює в два проходи. Перша створює нову плату, а друга пропускає її. Я думаю, що якби поєднати ці два проходи в один прохід, це дозволить заощадити пару байтів.
Це перший гольф з кодом, який я зробив, тому впевнений, що є багато можливостей для вдосконалення. Якщо ви хочете побачити код, перш ніж я максимально оптимізував для байтів, ось він.
1
і0
замістьO
іX
?