Scala , 764 bytes
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
}
}
}
Cómo funciona
El algoritmo primero llena una secuencia 2D con valores falsos. Determina cuántas iteraciones (cuadros abiertos) existen en función de los argumentos de la línea de comando introducidos. Crea un pliegue con este valor como límite superior. El valor entero del pliegue solo se usa implícitamente como una forma de contar cuántas iteraciones debe ejecutar el algoritmo. La secuencia rellena que creamos anteriormente es la secuencia de inicio para el pliegue. Esto se utiliza en la generación de una nueva secuencia 2D de valores falsos con sus indecisiones de co-respuesta.
Por ejemplo,
[[false, true],
[true, false],
[true, true]]
Se convertirá en
[[(false, 0)], [(false, 1)]]
Tenga en cuenta que todas las listas que son completamente verdaderas (tienen una longitud de 0) se omiten de la lista de resultados. El algoritmo luego toma esta lista y elige una lista aleatoria en la lista más externa. La lista aleatoria se elige como la fila aleatoria que elegimos. Desde esa fila aleatoria, nuevamente encontramos un número aleatorio, un índice de columna. Una vez que encontramos estos dos índices aleatorios, dormimos el hilo en el que estamos durante 1000 milisegundos.
Una vez que terminamos de dormir, limpiamos la pantalla y creamos un nuevo tablero con un true
valor actualizado en los índices aleatorios que hemos creado.
Para imprimir esto correctamente, lo usamos map
y lo comprimimos con el índice del mapa para tenerlo en nuestro contexto. Usamos el valor de verdad de la secuencia en cuanto a si debemos imprimir una X
o una O
o_
. Para elegir este último, utilizamos el valor del índice como nuestra guía.
Cosas interesantes a tener en cuenta
Para determinar si debe imprimir una O
o una _
, ((r._2 % 2) + c._2) % 2 == 0
se usa el condicional . r._2
se refiere al índice de fila actual mientras que se c._2
refiere a la columna actual. Si uno está en una fila impar, r._2 % 2
será 1, por lo tanto, compensado c._2
por uno en el condicional. Esto asegura que en filas impares, las columnas se muevan en 1 según lo previsto.
Imprimir la cadena "\033[H\033[2J\n"
, de acuerdo con alguna respuesta de Stackoverflow que leí, borra la pantalla. Está escribiendo bytes en la terminal y haciendo algunas cosas funky que realmente no entiendo. Pero he descubierto que es la forma más fácil de hacerlo. Sin embargo, no funciona en el emulador de consola de Intellij IDEA. Tendrás que ejecutarlo usando una terminal regular.
Otra ecuación que uno puede encontrar extraño al ver este código por primera vez es (l * k) / 2 - (l * k + 1) % 2
. Primero, desmitifiquemos los nombres de las variables. l
se refiere a los primeros argumentos pasados al programa mientras que se k
refiere al segundo. Para traducirlo, (first * second) / 2 - (first * second + 1) % 2
. El objetivo de esta ecuación es llegar a la cantidad exacta de iteraciones necesarias para obtener una secuencia de todas las X. La primera vez que hice esto, hice lo (first * second) / 2
que tenía sentido. Para cada n
elemento en cada sublista, hayn / 2
burbujas que podemos reventar. Sin embargo, esto se rompe cuando se trata de entradas como(11 13)
. Necesitamos calcular el producto de los dos números, hacerlo impar si es par, e incluso si es impar, y luego tomar el mod de eso por 2. Esto funciona porque las filas y columnas que son impares requerirán una iteración menos para llegar al resultado final.
map
se usa en lugar de a forEach
porque tiene menos caracteres.
Cosas que probablemente puedan mejorarse
Una cosa que realmente me molesta de esta solución es el uso frecuente de zipWithIndex
. Está tomando tantos personajes. Traté de hacerlo para poder definir mi propia función de un solo carácter que solo funcionaría zipWithIndex
con el valor pasado. Pero resulta que Scala no permite que una función anónima tenga parámetros de tipo. Probablemente haya otra forma de hacer lo que estoy haciendo sin usar, zipWithIndex
pero no he pensado demasiado en una forma inteligente de hacerlo.
Actualmente, el código se ejecuta en dos pases. El primero genera un nuevo tablero mientras que el segundo pase lo imprime. Creo que si uno combinara estos dos pases en uno solo, ahorraría un par de bytes.
Este es el primer código de golf que he hecho, así que estoy seguro de que hay mucho margen de mejora. Si desea ver el código antes de que optimice los bytes tanto como sea posible, aquí está.
1
y en0
lugar deO
yX
?