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
}
}
}
How it works
The algorithm first fills a 2D Sequence with false values. It determines how many iterations (open boxes) exist based on the command line arguments put in. It creates a fold with this value as the upper bound. The integer value of the fold is only used implicitly as a way to count how many iterations the algorithm should run for. The filled sequence we created previously is the starting sequence for the fold. This is used in generating a new 2D sequence of false values with their cooresponding indecies.
For example,
[[false, true],
[true, false],
[true, true]]
Will be turned into
[[(false, 0)], [(false, 1)]]
Note that all lists that are completely true (have a length of 0) are omitted from the result list. The algorithm then takes this list and picks a random list in the outermost list. The random list is chosen to be the random row we pick. From that random row, we again find a random number, a column index. Once we find these two random indices, we sleep the thread we are on for 1000 miliseconds.
After we're done sleeping, we clear the screen and create a new board with a true
value updated in the random indices we have created.
To print this out properly, we use map
and zip it with the index of the map so we have that in our context. We use the truth value of the sequence as to whether we should print an X
or either an O
or _
. To chose the latter, we use the index value as our guide.
Interesting things to note
To figure out if it should print an O
or an _
, the conditional ((r._2 % 2) + c._2) % 2 == 0
is used. r._2
refers to the current row index while c._2
refers to the current column. If one is on an odd row, r._2 % 2
will be 1, therefore offsetting c._2
by one in the conditional. This ensures that on odd rows, columns are moved over by 1 as intended.
Printing out the string "\033[H\033[2J\n"
, according to some Stackoverflow answer I read, clears the screen. It's writing bytes to the terminal and doing some funky stuff I don't really understand. But I've found it to be the easiest way to go about it. It doesn't work on Intellij IDEA's console emulator, though. You'll have to run it using a regular terminal.
Another equation one might find strange to see when first looking at this code is (l * k) / 2 - (l * k + 1) % 2
. First, let's demystify the variable names. l
refers to the first arguments passed into the program while k
refers to the second one. To translate it, (first * second) / 2 - (first * second + 1) % 2
. The goal of this equation is to come up with the exact amount of iterations needed to get a sequence of all X's. The first time I did this, I just did (first * second) / 2
as that made sense. For every n
elements in each sublist, there are n / 2
bubbles we can pop. However, this breaks when dealing with inputs such as (11 13)
. We need to compute the product of the two numbers, make it odd if it's even, and even if it's odd, and then take the mod of that by 2. This works because rows and columns that are odd are going to require one less iteration to get to the final result.
map
is used instead of a forEach
because it has less characters.
Things that can probably be improved
One thing that really bugs me about this solution is the frequent use of zipWithIndex
. It's taking up so many characters. I tried to make it so that I could define my own one character function that would just perform zipWithIndex
with the value passed in. But it turns out that Scala does not allow an anonymous function to have type parameters. There is probably another way to do what I'm doing without using zipWithIndex
but I haven't thought too much about a clever way to do it.
Currently, the code runs in two passes. The first generates a new board while the second pass prints it out. I think that if one were to combine these two passes into one pass, that would save a couple of bytes.
This is the first code golf I've done so I'm sure there is a lot of room for improvement. If you'd like to see the code before I optimized for bytes as much as possible, here it is.
Can we use
1
and0
instead ofO
andX
? – Pavel – 2017-03-12T00:40:54.180@ГригорийПерельман Yes, but make sure each cell represents a truthy/falsy value. I just used that because it looks like a legitmate bubble-wrap cell. – Matthew Roh – 2017-03-12T00:42:18.960
1NEEDZ BUBBLEZ pls send help – Christopher – 2017-03-12T00:52:32.793
The examples are not there, did they get popped already :(? – Jonathan Allan – 2017-03-12T00:57:57.800
I popped them guilty grin – Christopher – 2017-03-12T01:03:03.997
@JonathanAllan It was too long then the codeblock decided to nuke them – Matthew Roh – 2017-03-12T01:03:04.263
3Is it acceptable for a
(1,1)
to have no bubbles (e.g. top-left "cell" is always an underscore)? – Jonathan Allan – 2017-03-12T01:14:59.7571@JonathanAllan Yes. – Matthew Roh – 2017-03-12T01:17:00.023
@Sanchises umm.. what do you mean? – Matthew Roh – 2017-03-12T10:29:58.677
I like this question, it is entertaining to watch the output and also has more meat on the bone in terms of work product. – KalleMP – 2017-03-12T17:55:27.720
Is a function with
w
andh
as parameters acceptable, instead of a full program? I'm asking because you (explicitly¿) mention 'program' a couple of times. – Kevin Cruijssen – 2017-03-13T10:15:17.0331@KevinCruijssen It doesn't have to be a full program. – Matthew Roh – 2017-03-13T11:48:54.347
Your program should output all w*h phases. A 4×6 wrap has only 13 phases, no? – Adám – 2017-03-17T00:16:03.293
@Adám Well, right, but people seem to understand what I mean so I'll edit when I have some time. – Matthew Roh – 2017-03-17T06:30:22.023