Make a Bubble-wrap simulator

23

4

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)

Matthew Roh

Posted 2017-03-12T00:26:27.700

Reputation: 5 043

Can we use 1 and 0 instead of O and X? – 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.757

1@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 and h 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.033

1@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

Answers

7

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);}

enter image description here

Steadybox

Posted 2017-03-12T00:26:27.700

Reputation: 15 798

Note that there's a sleep function in the thread library, which is included in C++11. – Matthew Roh – 2017-03-12T08:35:29.950

@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 – 2017-03-12T11:07:40.880

btw you don't need to reset the screen. that will make it shorter. – Matthew Roh – 2017-03-12T11:26:32.283

5

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.

nile

Posted 2017-03-12T00:26:27.700

Reputation: 61

1Welcome to PPCG! – AdmBorkBork – 2017-03-13T14:44:52.683

1This 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 – 2017-03-13T15:44:05.353

@rassar Woops, thank you! – nile – 2017-03-14T00:53:41.523

4

MATL, 37 bytes

:!i:+o`T&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).

Luis Mendo

Posted 2017-03-12T00:26:27.700

Reputation: 87 464

4

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:

GIF of Mathematica popping bubble-wrap

Not a tree

Posted 2017-03-12T00:26:27.700

Reputation: 3 106

3

Jelly, 30 29 bytes

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

Example run

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
+                       -     addition                 e.g. [[2,3,4],[3,4,5]]
                        - left argument (implicit), w  e.g. 3
   +                    - add                          e.g. [[5,6,7],[6,7,8]]
                        - right argument (implicit), h e.g. 2
    +                   - add                          e.g. [[7,8,9],[8,9,10]]
     µ                  - monadic chain separation
       ⁾_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)

Jonathan Allan

Posted 2017-03-12T00:26:27.700

Reputation: 67 804

@ГригорийПерельман written it up. – Jonathan Allan – 2017-03-12T03:19:11.680

2

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.

Stefan Aleksić

Posted 2017-03-12T00:26:27.700

Reputation: 119

1

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>

Neil

Posted 2017-03-12T00:26:27.700

Reputation: 95 035

The width was not in terms of cells- but including blank(underlines) spaces. – Matthew Roh – 2017-03-12T22:55:25.250

@MatthewRoh Sorry, I remembered to fix it for the height but I forgot to check the width. – Neil – 2017-03-13T00:44:09.520

Hmm.. can't this part: \${`O`.repeat(w).slice(w)} ${`O`.repeat(w).slice(w)}be somehow combined? Perhaps a boolean-flag to first determineOorO, and then do the.repeat(w).slice(w)`? – Kevin Cruijssen – 2017-03-13T09:26:48.937

1@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 – 2017-03-13T09:55:56.970

1

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)

Arya

Posted 2017-03-12T00:26:27.700

Reputation: 131

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 – 2017-03-13T09:17:21.680

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 – 2017-03-13T09:22:22.647

@KevinCruijssen the Python3 one above me just has it as a function of w,h, is that allowed? – Arya – 2017-03-13T14:24:04.630

1

Yes, both functions with the expected parameters as well as full programs are allowed by default, unless the question states otherwise.

– Kevin Cruijssen – 2017-03-13T14:30:04.120

1Ok - I've made it a function of H and W now. – Arya – 2017-03-13T15:05:52.800

1

Charcoal, 49 46 39 bytes (noncompeting)

UONNO_¶_OAKAαA№αOβHWψβ«A§α§⌕AαO‽βXA№α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)
}

ASCII-only

Posted 2017-03-12T00:26:27.700

Reputation: 4 687

1

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|-/¨⍳⎕]

Adám

Posted 2017-03-12T00:26:27.700

Reputation: 37 779

1

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()

RootTwo

Posted 2017-03-12T00:26:27.700

Reputation: 1 749

0

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'
      Thread.sleep(1000);                                  //   Wait 1 second
      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)

enter image description here

Kevin Cruijssen

Posted 2017-03-12T00:26:27.700

Reputation: 67 575

0

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"

Dada

Posted 2017-03-12T00:26:27.700

Reputation: 8 279

0

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:

enter image description here

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

Grant Miller

Posted 2017-03-12T00:26:27.700

Reputation: 706