Scala , 764 octets
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
}
}
}
Comment ça marche
L'algorithme remplit d'abord une séquence 2D avec de fausses valeurs. Il détermine combien d'itérations (boîtes ouvertes) existent en fonction des arguments de ligne de commande insérés. Il crée un pli avec cette valeur comme limite supérieure. La valeur entière du repli n'est utilisée qu'implicitement comme moyen de compter le nombre d'itérations pour lesquelles l'algorithme doit s'exécuter. La séquence remplie que nous avons créée précédemment est la séquence de départ du pli. Ceci est utilisé pour générer une nouvelle séquence 2D de fausses valeurs avec leurs indécies co-répondantes.
Par exemple,
[[false, true],
[true, false],
[true, true]]
Sera transformé en
[[(false, 0)], [(false, 1)]]
Notez que toutes les listes qui sont complètement vraies (ont une longueur de 0) sont omises de la liste des résultats. L'algorithme prend ensuite cette liste et sélectionne une liste aléatoire dans la liste la plus externe. La liste aléatoire est choisie pour être la ligne aléatoire que nous choisissons. À partir de cette ligne aléatoire, nous trouvons à nouveau un nombre aléatoire, un index de colonne. Une fois que nous avons trouvé ces deux indices aléatoires, nous dormons le thread sur lequel nous sommes pendant 1000 millisecondes.
Après avoir dormi, nous effaçons l'écran et créons un nouveau tableau avec une true
valeur mise à jour dans les indices aléatoires que nous avons créés.
Pour l'imprimer correctement, nous l'utilisons map
et le compressons avec l'index de la carte, nous l'avons donc dans notre contexte. Nous utilisons la valeur de vérité de la séquence pour savoir si nous devons imprimer un X
ou un O
ou ou _
. Pour choisir ce dernier, nous utilisons la valeur de l'indice comme guide.
Choses intéressantes à noter
Pour déterminer s'il doit imprimer un O
ou un _
, le conditionnel ((r._2 % 2) + c._2) % 2 == 0
est utilisé. r._2
fait référence à l'index de ligne actuel tandis que c._2
fait référence à la colonne actuelle. Si l'un est sur une ligne impaire, r._2 % 2
sera 1, donc compensé c._2
par un au conditionnel. Cela garantit que sur les lignes impaires, les colonnes sont déplacées de 1 comme prévu.
L'impression de la chaîne "\033[H\033[2J\n"
, selon une réponse de Stackoverflow que j'ai lue, efface l'écran. Il écrit des octets sur le terminal et fait des trucs géniaux que je ne comprends pas vraiment. Mais j'ai trouvé que c'était la manière la plus simple de s'y prendre. Cependant, cela ne fonctionne pas sur l'émulateur de console d'Intellij IDEA. Vous devrez l'exécuter à l'aide d'un terminal standard.
Une autre équation que l'on pourrait trouver étrange lors de la première lecture de ce code est (l * k) / 2 - (l * k + 1) % 2
. Tout d'abord, démystifions les noms de variables. l
fait référence aux premiers arguments passés dans le programme tandis que k
fait référence au second. Pour le traduire, (first * second) / 2 - (first * second + 1) % 2
. Le but de cette équation est de trouver le nombre exact d'itérations nécessaires pour obtenir une séquence de tous les X. La première fois que j'ai fait ça, j'ai fait (first * second) / 2
comme ça avait du sens. Pour chaque n
élément de chaque sous-liste, il y a des n / 2
bulles que nous pouvons faire éclater. Cependant, cela casse lorsqu'il s'agit d'entrées telles que(11 13)
. Nous devons calculer le produit des deux nombres, le rendre impair s'il est pair et même s'il est impair, puis prendre le mod de cela par 2. Cela fonctionne parce que les lignes et les colonnes qui sont impaires nécessiteront une itération de moins pour arriver au résultat final.
map
est utilisé à la place d'un forEach
car il contient moins de caractères.
Des choses qui peuvent probablement être améliorées
Une chose qui me dérange vraiment dans cette solution est l'utilisation fréquente de zipWithIndex
. Cela prend tellement de personnages. J'ai essayé de faire en sorte que je puisse définir ma propre fonction à un caractère qui fonctionnerait simplement zipWithIndex
avec la valeur transmise. Mais il s'avère que Scala ne permet pas à une fonction anonyme d'avoir des paramètres de type. Il y a probablement une autre façon de faire ce que je fais sans utiliser, zipWithIndex
mais je n'ai pas trop réfléchi à une façon intelligente de le faire.
Actuellement, le code s'exécute en deux passes. La première génère une nouvelle carte tandis que la deuxième passe l'imprime. Je pense que si l'on devait combiner ces deux passes en une seule, cela économiserait quelques octets.
C'est le premier golf de code que j'ai fait, donc je suis sûr qu'il y a beaucoup de place pour l'amélioration. Si vous souhaitez voir le code avant d'optimiser autant que possible les octets, le voici.
1
et0
au lieu deO
etX
?