# Make a Bubble-wrap simulator

Bubble-wraps are maximum-level entertainment. Everyone can agree to that.

Now, you will make even computers enjoy bubble-wraps.

# Specs

You will be given two integers, w, and h.(each are responsively width and height)

Your program should output all w*h phases waiting 1 second between each one and terminate.

Every bubble-wrap starts with all cells full.

For example, a 4*6 bubble-wrap starts like:

O_O_
_O_O
O_O_
_O_O
O_O_
_O_O

And each phase, a random non-popped cell is popped.For example,

O_O_
_O_O
O_X_
_O_O
O_O_
_O_O

The program should terminate when all the cells are popped. aka.

X_X_
_X_X
X_X_
_X_X
X_X_
_X_X

# Examples

(4,6)
(5,5)
(6,2)
(10,10)
(7,9)
• Can we use 1 and 0 instead of O and X? – Pavel Mar 12 '17 at 0:40
• NEEDZ BUBBLEZ pls send help – Christopher Mar 12 '17 at 0:52
• Is it acceptable for a (1,1) to have no bubbles (e.g. top-left "cell" is always an underscore)? – Jonathan Allan Mar 12 '17 at 1:14
• @JonathanAllan Yes. – Matthew Roh Mar 12 '17 at 1:17
• @KevinCruijssen It doesn't have to be a full program. – Matthew Roh Mar 13 '17 at 11:48

# C (Windows), 260 248 bytes

#import<windows.h>
i,j,l,r;b(w,h){char*s=malloc(l=w*h+h);for(i=h;i--;*s++=10)for(j=w;j--;*s++=i%2^j%2?79:45);*(s-1)=0;s-=l;for(srand(time(0));j>system("cls")+puts(s)-2;j>-1?s[j]=88:0)for(Sleep(1000),r=rand(),j=-2,i=r+l*2;--i-r;j=s[i%l]==79?i%l:j);}

• Note that there's a sleep function in the thread library, which is included in C++11. – Matthew Roh Mar 12 '17 at 8:35
• @MatthewRoh Yep, but this is shorter, and the system("cls") is also Windows-specific, so the code wouldn't be more portable with the thread library either. And with C++ I would also need to include iostream or cstdio. – Steadybox Mar 12 '17 at 11:07
• btw you don't need to reset the screen. that will make it shorter. – Matthew Roh Mar 12 '17 at 11:26

# Python 3, 222 220 bytes

This is my first time answering, so please be gentle (and point out mistakes that I have made).

import time,random as t
def f(c,r):
p=print;a='0_'*c;d=((a[:c]+'\n'+a[1:c+1]+'\n')*r)[:-~c*r]
for i in[1]*((r*c+r%2*c%2)//2):
p(d);k=1
while d[k]!='0':k=t.randrange(len(d))
d=d[:k]+'X'+d[k+1:];time.sleep(1)
p(d)

Try it online!

How it works:

1. Chain a lot of '0_''s together
2. Chop into '0_0_...\n' and '_0_0...\n' parts and concatenate
3. Generate random indices until the char at the index is a '0'
4. Create new string with the char at the generated index is replaced with a 'X' (Damn you python for non-mutable strings!)
5. Repeat r*c+r%2*c%2 times: There are r*c bubbles in the pattern, unless r and c are both odd, in which case there are r*c+1.
• Welcome to PPCG! – AdmBorkBork Mar 13 '17 at 14:44
• This is rather minor, but your width and height are reversed. Great answer though! (Just change it to f(c,r) and you'll be fine). – rassar Mar 13 '17 at 15:44
• @rassar Woops, thank you! – nile Mar 14 '17 at 0:53

# MATL, 37 bytes

:!i:+oT&Xxt3:q'_OX'XEcD2y1=ft&v1Zr(T

The upper-left corner is always an underscore (allowed by the challenge).

The screen is cleared between phases. I could save a byte by not clearing the screen, but it looks better this way.

The program exits with an error (allowed by default) after displaying all phases.

Try it at MATL Online! (If it doesn't work after a couple of seconds, please refresh the page and try again).

## Mathematica (145 bytes)

Anonymous function, takes height and width as input (in that order — if that's a problem, replace {##} with {#2,#} in the middle of the code for an extra 2 bytes).

Code:

Monitor[Do[Pause@1,{i,NestList[RandomChoice@StringReplaceList[#,"O"->"X"]&,""<>Riffle["_"["O"][[Mod[#+#2,2]]]&~Array~{##},"
"],Floor[##/2]]}],i]&

Explanation:

• ""<>Riffle[Array["_"["O"][[Mod[#+#2,2]]]&,{##}],"\n"] creates the initial, unpopped bubble-wrap, by making an array of "_"s and "O"s and then StringJoining them between newlines.
• NestList[RandomChoice@StringReplaceList[#,"O"->"X"]&,..., Floor[##/2]] repeatedly chooses one of the "O"s to replace with an "X", as many times as there are "O"s (which is Floor[width * height / 2] — thanks to @JonathanAllan for the idea of putting "_" instead of "O" in the top left corner, otherwise this would be Ceiling instead and thus 2 bytes more).
• Monitor[Do[Pause@1,{i,...}],i] makes i take the values in the list we just calculated, for 1 second each, and dynamically prints i.

Example output:

# Jelly, 30 29 bytes

=”OTX
+þ⁹++µị⁾_OYµṄœS1”XÇ¦µÐL

Calls the link as a dyad with the program arguments and then quits with a message (the code for which is çṛ“\'=ṙMḋḌẓ(ėo»)

Nuance: the bottom-right "cell" will always be a bubble (rather than the top-left like the example in the question), this is to ensure that when all bubbles are popped the random choice returns 0 which will be the "X" at the end of the list - replacing that then makes no change to the value and breaks the loop.

Note: does not clear the screen (was not specified, and I'm not sure how to do so).

### How?

=”OTX - Link 1, choose a random O index or 0: string   e.g. "_O_\nX_O"
”O   - "O"
=     - equals (vectorises over the string)            e.g. [0,1,0,0,0,0,1]
T  - truthy indexes                                 e.g. [2,7]
X - random choice (if empty this returns 0)

+þ⁹++µị⁾_OYµṄœS1”XÇ¦µÐL - Main link: w, h              e.g. 3, 2
- left argument (implicit), w  e.g. 3
⁹                     - right argument, h            e.g. 2
þ                      - outer product with
- left argument (implicit), w  e.g. 3
- right argument (implicit), h e.g. 2
⁾_O              - "_O"
ị                 - index into (1-based & mod)   e.g. [['_','O','_'],['O','_','O']]
-     note: the additions above assure the last entry is an 'O'.
Y             - join with line feeds         e.g. ['_','O','_','\n','O','_','O']
µ        µ   - monadic chain separations
ÐL - loop until the results are no longer unique:
Ṅ           -     print with a trailing line feed and yield
œS1        -     sleep for 1 second and yield
¦    -     apply to index
Ç     -         given by calling the last link (1) as a monad
-                 (a random O index or 0 if none exists)
”X      -         an "X"  (      ... which will be an X already)
• @ГригорийПерельман written it up. – Jonathan Allan Mar 12 '17 at 3:19

# 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)
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.

## JavaScript (ES6), 246 229 bytes

document.write(<pre id=o></pre>)
setInterval(_=>{(a=o.innerHTML.split(/(O)/))[1]?a[Math.random()*~-a.length|1]=X:0;o.innerHTML=a.join},1e3)
f=(w,h)=>o.innerHTML=[...Array(h)].map((_,i)=>O_.repeat(w+h).substr(i,w)).join

<div oninput=f(+w.value,+h.value)><input id=w type=number min=1><input id=h type=number min=1>

• The width was not in terms of cells- but including blank(underlines) spaces. – Matthew Roh Mar 12 '17 at 22:55
• @MatthewRoh Sorry, I remembered to fix it for the height but I forgot to check the width. – Neil Mar 13 '17 at 0:44
• Hmm.. can't this part: ${_O.repeat(w).slice(w)}${O_.repeat(w).slice(w)} be somehow combined? Perhaps a boolean-flag to first determine _O or O_, and then do the .repeat(w).slice(w)? – Kevin Cruijssen Mar 13 '17 at 9:26
• @KevinCruijssen I'd lost 16 bytes because of a quick bugfix which I hadn't had time to golf at the time. I've since taken another look and come up with a 17 byte saving, – Neil Mar 13 '17 at 9:55

# Python - 290 bytes

I've never done one of these before - so any constructive criticism would be appreciated :)

Main trick here is just annoyingly nested list comprehensions. I could save a few characters by not having a newline between the pops but that just looks ugly.

r=range
q=print
import random as n,time
def f(H,W):
def p(b):
q("\n".join(["".join(["O"if(i,j)in b else"X"if(i,j)in X else"_"for j in r(H)])for i in r(W)]));time.sleep(1);q()
b=[(i,h)for h in r(H)for i in r(h%2,W,2)];n.shuffle(b);X=[]
while len(b)>0:
p(b);X+=[b.pop()]
p(b)
• Hi, welcome to PPCG! The challenge was to take the w and h as input (through STDIN, as a function input, or something similar), instead of having a hardcoded H=4 W=6. Also, although I never programmed in Python, I think you can golf some spaces in your current code. Tips for Golfing in Python might also be interesting to read through to give you ideas on how to golf it down further. Enjoy your stay! :) – Kevin Cruijssen Mar 13 '17 at 9:17
• Also, regarding your comment: "I could save a few characters by not having a newline between the pops but that just looks ugly." No matter how ugly or how not-done it is in real-life programming, codegolf is about saving as much bytes as possible. The shorter and uglier, the better. ;) – Kevin Cruijssen Mar 13 '17 at 9:22
• @KevinCruijssen the Python3 one above me just has it as a function of w,h, is that allowed? – Arya Mar 13 '17 at 14:24
• Yes, both functions with the expected parameters as well as full programs are allowed by default, unless the question states otherwise. – Kevin Cruijssen Mar 13 '17 at 14:30
• Ok - I've made it a function of H and W now. – Arya Mar 13 '17 at 15:05

# Charcoal, 4946 39 bytes (noncompeting)

ＵＯＮＮO_¶_OＡＫＡαＡ№αOβＨＷψβ«Ａ§α§⌕ＡαO‽βXＡ№αOβ

## Verbose

Oblong(InputNumber(), InputNumber(), "O_\n_O")
Assign(PeekAll(), a)
Assign(Count(a, "O"), b)
RefreshWhile (k, b) {
AssignAtIndex(a, AtIndex(FindAll(a, "O"), Random(b)), "X")
Assign(Count(a, "O"), b)
}

# APL (Dyalog), 61 59 bytes

⎕←m←'O_'[2|-/¨⍳⎕]
(b/,m)[?+/b←'O'=,m]←'X'
⎕DL 1
→2/⍨'O'∊⎕←m

⎕← output
m←m, where m is
'O_'[] these characters indexed by…
2| the division-remainder-when-divided-by-two of
-/¨ the difference between each of
all the coordinates (indices) in an array of shape
numeric input (the number of rows and columns)

()[]←'X' assign the character X to one of the…
b/ filtered-by-b (to be defined)
,m raveled elements of m, specifically…
? a random element (lit. number) in the range one to
+/ the sum of
b←b, where b is
'O'= Boolean for where the letter equals
,mm raveled

⎕DL 1Delay one second

→2 Go to line 2
/⍨ if (lit. filtered by)
'O'∊ whether the letter is a member of
⎕←m the outputted value, where the outputted value is m

Try it online!

From version 16.0 it will be shorter:

{0::→⋄'X'@(⊂(?∘≢⊃⊢)⍸'O'=⍵⊣⎕DL 1)⊢⎕←⍵}⍣≡'O_'[2|-/¨⍳⎕]

# Python 3, 195 188 bytes

import time,random
def f(w,h):
a=bytearray(b'0-'*w*h);b=[*range(0,w*h,2)];random.shuffle(b);
while 1:print(*(a.decode()[w*i:w*i+w]for i in range(h)),sep='\n');a[b.pop()]=88;time.sleep(1)

Using bytearray and decode seems to be shorter than slicing and reassembling a string a la a[:i]+'X'+a[i+1:].

import time,random
def f(w,h):
x=[*range(1,h*w,2)];random.shuffle(x)
while 1:
for i in range(w*h):
print('-X0'[(i%w%2!=i//w%2)+(i in x)],end='\n'[i%w<w-1:])
print();time.sleep(1);x.pop()

# Java 7, 317 bytes

void c(int w,int h)throws Exception{String r="";int x=0,j=0,i;for(;j++<h;x^=1,r+="\n")for(i=0;i<w;r+=(i+++x)%2<1?"_":"O");for(System.out.println(r);r.contains("O");System.out.println(r=r.substring(0,x)+'X'+r.substring(x+1))){Thread.sleep(1000);for(x=0;r.charAt(x)!='O';x=new java.util.Random().nextInt(r.length()));}}

Explanation:

void c(int w, int h) throws Exception{                     // Method with two integer parameters (throws Exception is required for the Thread.sleep)
String r = "";                                           //  String we build to print
int x=0, j=0, i;                                         //  Some temp values and indexes we use
for(; j++<h;                                             //  Loop over the height
x^=1,                                                //    After every iteration change the flag x every iteration from 0 to 1, or vice-versa
r += "\n")                                           //    And append the String with a new-line
for(i=0; i<w;                                          //   Inner loop over the width
r += (i++ + x)%2 < 1 ? "_" : "O")                  //    Append the String with either '_' or 'O' based on the row and flag-integer
;                                                      //   End inner width-loop (implicit / no body)
//  End height-loop (implicit / single-line body)
for(                                                     //  Loop
System.out.println(r);                                 //   Start by printing the starting wrap
r.contains("O");                                       //   Continue loop as long as the String contains an 'O'
System.out.println(r=                                  //   Print the changed String after every iteration
r.substring(0,x)+'X'+r.substring(x+1))){           //    After we've replaced a random 'O' with 'X'
for(x=0; r.charAt(x) != 'O';                         //   Loop until we've found a random index containing still containing an 'O'
x = new java.util.Random().nextInt(r.length()))  //    Select a random index in the String
;                                                    //   End loop that determines random index containing 'O' (implicit / no body)
}                                                        //  End loop
}                                                          // End method

Test gif (4,6)

## Perl, 148 bytes

146 bytes of code + -pl flags.

$_=O x($x=$_+1);s/O\K./_/g;for$i(1..($y=<>)){$b.=($i%2?$_:_.s/.$//r).$/}}print$_="\e[H$b";while(/O/){$r=0|rand$y*$x+3;s/.{$r}\KO/X/s||redo;sleep 1

To run it:

perl -ple '$_=O x($x=$_+1);s/O\K./_/g;for$i(1..($y=<>)){$b.=($i%2?$_:_.s/.$//r).$/}}print$_="\e[H$b";while(/O/){$r=0|rand$y*$x+3;s/.{$r}\KO/X/s||redo;sleep 1' <<< "6
4"

# MATLAB (R2016b), 172 bytes

Code:

x=input('');m=[eye(x(2),x(1)) ''];m(:)='O';m(1:2:end,2:2:end)='_';m(2:2:end,1:2:end)='_';o=find(m=='O');r=o(randperm(nnz(o)));disp(m);for i=r';pause(1);m(i)='X';disp(m);end

Recommendations are always welcome! Try it online!

Program Output:

Explanation:

x = input( '' );                    % Input
m = [ eye( x( 2 ), x( 1 ) ) '' ];   % Character Matrix
m( : ) = 'O';                       % Fill Matrix with "Bubbles"

m( 1:2:end, 2:2:end ) = '_';        % Alternate Spaces Between Bubbles (Part 1)
m( 2:2:end, 1:2:end ) = '_';        % Alternate Spaces Between Bubbles (Part 2)

o = find( m == 'O' );               % Index Bubble Locations
r = o( randperm( nnz( o ) ) );      % Randomize Bubble Locations

disp( m );                          % Display Initial Bubble Wrap Phase

for i = r'
pause( 1 );                     % Pause for 1 Second
m( i ) = 'X';                   % Pop Bubble
disp( m );                      % Display Subsequent Bubble Wrap Phase
end