# Gravity simulator

### Statement

You are given a still of a series of balls falling in a 2D grid. This grid is surrounded by immutable and unbreakable walls so all the action in contained within them. Your task is to determine what will the state of the scenario be after gravity does all it's work.

### Elements inside grid

• - Floor, doesn't alter the direction of falling balls.
• \ Right slide, alters the path of the ball one (1) position right.
• / Left slide, alters the path of the ball one (1) position left.
• o A ball.

### Rules

• Balls fall.
• Floors, and slides don't fall.
• If the ball hits a slide that would make it go through a wall (\#or#/), or cross through a floor the slide will act as a floor.
• When a ball hits another ball the will become one ball, but increase their power to the sum of both balls.
• New balls (joined) will continue to behave as usual.
• When a ball can't move anymore, it's replaced by its power.
• The power of a ball will always be at most 9.

### Input

The grid will be given in a string variable with whatever name is shortest in your language of choice. By default we will use a as the input. A sample of an input, exactly as received:

##########\n# \      #\n#o       #\n#  - -\o #\n#/-    \ #\n#  \oo-/\#\n#-/ \   /#\n#   \ \  #\n#       /#\n##########


For random grids generated use https://repl.it/B1j3/2. Use my generated page instead (no ads, no crap, just the input and the output)

Note line breaks are \n. Printing the input to screen (not required for the challenge) would show things like this. Although I've put four puzzles alongside to safe space.

##########  ##########  ##########  ##########
# \      #  # o    -/#  #       o#  #-o /    #
#o       #  #    \   #  # o     -#  #-- \ /\ #
#  - -\o #  #-  \    #  #    - \o#  # - -- o-#
#/-    \ #  #        #  #o /\    #  #/ \     #
#  \oo-/\#  #o  -o-  #  # /    -o#  #/ /o oo/#
#-/ \   /#  #   -/-  #  # -  o  -#  #o/      #
#   \ \  #  #    \\  #  #   \o  /#  #o-o    o#
#       /#  # \o\  /\#  #     \o #  # -\o o /#
##########  ##########  ##########  ##########


### Output

The same grid, printed to screen with the final result of ball power. A valid answer would be one (1) of the following puzzles, each one corresponds to the input at the same position, of course if the input is different you should adjust the output. Not limit it to those four!

##########  ##########  ##########  ##########
# \      #  #      -/#  #       1#  #-1 /    #
#        #  #    \   #  #       -#  #-- \ /\ #
#1 - -\  #  #-  \    #  #    - \ #  # - --  -#
#/-    \1#  #        #  #  /\    #  #/ \     #
#  \  -/\#  #   -1-  #  # /    -2#  #/ /    /#
#-/ \   /#  #   -/-  #  # -     -#  # /      #
#   \ \  #  #    \\  #  #   \   /#  # -      #
#    2  /#  #1\2\  /\#  #2   2\1 #  #2-\3 23/#
##########  ##########  ##########  ##########


### Score

Languages will compete against themselves so feel free to use nongolf languages. To validate a solution I must be able to test it somewhere to see it works!.

Score is number of bytes. In the event of a tie, the first answer to reach the tied score wins.

### Warnings

• If unsure of how a ball should react, ask me and I'll clarify, I've been as clear as I could but I'm sure there are cases that are confusing.
• Slides are only ridden if you can exit them, think about it like a real slide. There's a guy at the top that doesn't let you through the ball unless it will exit through the other side.

### Clarifying examples of ball movement

######                       ######
#-o- #    BALL WOULD GO RD   #- - #
# \  #                       # \o #
######                       ######

######                       ######
#-o- #     BALL WOULD STAY   #-o- #
# \\ #                       # \\ #
######                       ######

######                       ######
#  -o#     BALL WOULD STAY   #  -o#
#   \#                       #   \#
######                       ######

######                       ######
#  o #     BALL WOULD STAY   #  o #
#  \/#                       #  \/#
######                       ######

######                       ######
#-o- #    BALL WOULD GO LD   #- - #
# /\ #                       #o/\ #
######                       ######


### How can I test if my answer is valid?

I've set up a simple page in one of my sites that will give you a random puzzle, and its answer. Take the input and check it against the output. My solution, without worrying too much about golfing is 389b 355b in python (generator, and page also python)

var QUESTION_ID=64359,OVERRIDE_USER=8478;function answersUrl(e){return"https://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"https://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a,language:a,link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

• I'm reminded of Marbelous. – Arcturus Nov 20 '15 at 2:08
• Bonus points if anybody answers in Marbelous. – Mego Nov 20 '15 at 2:09
• Borderline dupe of A Mere Bagatelle and Not Your Routine Bean Machine – Peter Taylor Nov 20 '15 at 8:20
• sounds like potentially an ascii-art pinball game – Khaled.K Dec 9 '15 at 22:28
• @JuanCortés why don't you use the fancy leader board code so you don't have to update the rankings yourself? – usandfriends Jan 4 '16 at 16:56

# JavaScript (ES6), 157 196

Edit char by char instead of row by row, much better result

g=>(s=~g.search
,g=[...g]).map((c,i)=>c<' '?0:g[[0,1,-1].map(d=>!d|'\\ /'[d+1]==g[d+=i]&&+g[d+=s]?g[v+=+g[d],d+v-v]=' ':0,v=c>'a'?1:+c),i]=v?v:c)&&g.join


Note: does not handle ball values > 9. But it could, with a 18 bytes cost. See basic code below.

TEST snippet (better full page)

F=g=>(s=~g.search
,g=[...g]).map((c,i)=>c<' '?0:g[[0,1,-1].map(d=>!d|'\\ /'[d+1]==g[d+=i]&&+g[d+=s]?g[v+=+g[d],d+v-v]=' ':0,v=c=='o'?1:+c),i]=v?v:c)&&g.join

// Basic code, starting point before golfing
B=g=>{
s = ~g.search('\n');
(g=[...g]).map((c,i)=>{
v = c == 'o' ? 1 : +c
if (c>=' ' // skip newlines
&& !isNaN(v)) // digit or space
{
if (w=+g[i+s]) v += w, g[i+s]=' '
if (g[i-1]=='\\' && (w=+g[i+s-1])) v += w, g[i+s-1]=' '
if (g[i+1]=='/' && (w=+g[i+s+1])) v += w, g[i+s+1]=' '
if (v) g[i] = v
}
})
// return g.join
// To handle values > 9 ...
return g.map(v=>+v?v%10:v).join
}

function test() {
O.textContent = F(I.value)
}

test()
textarea,pre { width: 15em; height: 15em; display: block; margin: 0; }
iframe { height: 25em; width: 15em}
td { vertical-align: top }
<table>
<tr>
<th>Test cases</th>
<th>Input</th>
<td></td>
<th>Output</th>
</tr><tr>
<td>
Copy/paste test cases from here <br>(courtesy of OP)
</td>
<td><textarea id=I>##########
#  o  o o#
# o\o o  #
#oo o/   #
#       o#
#     /o #
#\o   o  #
# o /-   #
#   o  - #
##########</textarea></td>
<td><button onclick='test()'>Test</button></td>
<td><pre id=O></pre></td>
</tr>
</table>

• Nice! I have a lot to learn in the ways of golf – usandfriends Jan 4 '16 at 23:48
• shouldn´t you map values >9 with v>9?9:v? – Titus Nov 8 '17 at 2:29
• @Titus I could, but in fact I could do whatever I want, as a value > 9 is not expected, see the comment by OP answering my question. – edc65 Nov 8 '17 at 10:00

# Javascript (ES6), 453426409306290 286 bytes

The first and most obvious solution that came to my mind is one that looks around the slides and then merges or replaces.

a=>{a=a.split
.map(b=>[...b.replace(/o/g,'1')]);for(r=1;r<a.length-1;r++){d=a[r];for(c=1;c<d.length-1;c++){e=a[r+1];f=e[c]=='\\'?c+1:e[c]=='/'?c-1:!isNaN(+e[c])?c:null;(''+d[c]).match(/[0-9]/g)&&f!=null&&!isNaN(+e[f])?(e[f]=+e[f]+ +d[c],d[c]=' '):0}}return a.map(b=>b.join).join
}


Ungolfed:

func = state => {
state = state.split 
.map(line => [...line.replace(/o/g, '1')]);

for (r = 1; r < state.length - 1; r++) {
thisState = state[r];
for (c = 1; c < thisState.length - 1; c++) {
nextState = state[r + 1];
nc = nextState[c] == '\\' ? c + 1 : nextState[c] == '/' ? c - 1 : !isNaN(+nextState[c]) ? c : null;

('' + thisState[c]).match(/[0-9]/g) && nc != null && !isNaN(+nextState[nc]) ? (
nextState[nc] = +nextState[nc] + +thisState[c],
thisState[c] = ' '
) : 0;
}
}

return state.map(line => line.join ).join 
;
}


Test like:

func(##########
# -- o - #
# \\\\\\ -  #
#-       #
# o  o   #
#o \\\\ /-\\#
#      \\ #
#/-  //  #
#   /- o #
##########)


Thanks to: @edc65

• I'll post my python when I'm sure I can't golf it anymore, but so far it's the python code that's generating the answer. Any way I can test your golfed code somewhere so I can pop you on the leaderboard? (jsfiddle,jsbin,ideone,whatever) – Juan Cortés Jan 1 '16 at 21:01
• got it down to 355, your move! – Juan Cortés Jan 4 '16 at 2:54
• @JuanCortés Done! – usandfriends Jan 4 '16 at 16:44
• b.replace(/o/g,'1').split  can be shortened to [...b.replace(/o/g,1)] – edc65 Jan 4 '16 at 19:44
• @edc65 I think I fixed it. Basically it always keeps the power under 10 by mod'ing 10. – usandfriends Jan 4 '16 at 20:22

## Java, Too Many1102 987 bytes

Because, Java.

\o/ It's under 1000!

class G{class T{char s;int p=0;T(char c){s=c;}}T A=new T(' ');T[][]o;boolean i(){for(int i=1;i<o.length;i++)for(int j=1;j<o[i].length;j++)if(o[i][j].p>0){if(m(i,j,i+1,j)||o[i+1][j].s=='/'&&m(i,j,i+1,j-1)||o[i+1][j].s=='\\'&&m(i,j,i+1,j+1))return 1>0;int w=o[i][j].p;o[i][j]=new T(Integer.toString(w).charAt(0)){{p=w;}};}return 1<0;}boolean m(int a,int b,int c,int d){if(o[c][d]==A||o[c][d].p>0){o[a][b].p+=o[c][d].p;o[c][d]=o[a][b];o[a][b]=A;return 1>0;}return 1<0;}String s(){String s="";for(T[]r:o){for(T t:r)s+=t.s;s+="\n";}return s;}void f(String s){String[]r=s.split("\\\\n");o=new T[r.length][r.length()];for(int i=0;i<o.length;i++)for(int j=0;j<o[i].length;j++)switch(r[i].charAt(j)){case'-':o[i][j]=new T('-');break;case'\\':o[i][j]=new T('\\');break;case'/':o[i][j]=new T('/');break;case'o':o[i][j]=new T('o'){{p=1;}};break;case'#':o[i][j]=new T('#');break;default:o[i][j]=A;}}public static void main(String[]a){G g=new G();g.f(a);while(g.i());System.out.println(g.s());}}


A side goal was being able to print every iteration of the board: just remove the middle ; in while(g.i()) ; System.out.print(g.s()); (Though this does disable the last print which has the 0->power conversion). Unfortunately, in this version, gravity works in strangely. Each pass I take the first non-stuck ball and move it. Short circuiting iterate() there is less bytes than going over the whole board then returning if anything changed.

This is a complete main class, compile and run on the command line with argument:

java -jar G.jar "##########\n# o-/    #\n#-  / -/ #\n# oo   o #\n# /   \o #\n# o   o \#\n#    o   #\n#   -\o  #\n#\  \\ o/#\n##########"


class GravitySimulator {
class Token {
char symbol;
int power = 0;

Token(char c) {
symbol = c;
}
}

Token A = new Token(' ');

Token[][] board;

boolean iterate() {
for (int i=1; i<board.length; i++)
for (int j=1; j<board[i].length; j++)
if (board[i][j].power>0) {
if (move(i,j,i+1,j) || board[i+1][j].symbol=='/' && move(i,j,i+1,j-1) || board[i+1][j].symbol=='\\' && move(i,j,i+1,j+1)) return true;
int pow = board[i][j].power;
board[i][j] = new Token(Integer.toString(pow).charAt(0)){{power=pow;}};
}
return false;
}

boolean move(int x1, int y1, int x2, int y2) {
if (board[x2][y2] == A || board[x2][y2].power>0) {
board[x1][y1].power += board[x2][y2].power;
board[x2][y2] = board[x1][y1];
board[x1][y1] = A;
return true;
} return false;
}

String string() {
String s = "";
for (Token[] row : board) {
for (Token token : row) s+=token.symbol;
s+="\n";
}
return s;
}

void fromString(String s) {
String[] rows = s.split("\\\\n");
board = new Token[rows.length][rows.length()];
for (int i=0; i<board.length; i++)
for (int j=0; j<board[i].length; j++)
switch(rows[i].charAt(j)) {
case '-': board[i][j]=new Token('-');break;
case '\\':board[i][j]=new Token('\\');break;
case '/': board[i][j]=new Token('/');break;
case 'o': board[i][j]=new Token('o'){{power=1;}};break;
case '#': board[i][j]=new Token('#');break;
default:  board[i][j]=A;
}
}

public static void main(String[] args) {
GravitySimulator g = new GravitySimulator();
g.fromString(args);
while(g.iterate());
System.out.println(g.string());
}
}

• Such java much verbose. +1 – Rohan Jhunjhunwala Jul 9 '16 at 11:01

## Python3, 355b

g=g.replace("o","1").split("\n")
r=1
while r:
r=0
for y in range(len(g)):
for x in range(len(g[y])):
if g[y][x].isdigit():
h=g[y+1]
m={"/":-1,"\\":1}
j=x+m[h[x]]if h[x]in m else x
if("0"+h[j].strip()).isdigit():
r=1
g[y+1]=h[:j]+str(int(g[y][x])+int("0"+h[j]))+h[j+1:]
g[y]=g[y][:x]+' '+g[y][x+1:]
print("\n".join(g))


Test here

# PHP, 228 204 197 194 bytes

for($a=strtr($a,o,1);$c=$a[$i];$i++)$c>0&&(($d=$a[$t=$i+strpos($a,"
")+1])>" "?$d!="/"?$d!="\\"?$d>0:$a[++$t]<"!"||$a[$t]>0:$a[--$t]<"!"||$a[$t]>0:1)&&$a[$t]=min($a[$t]+$c,9).!$a[$i]=" ";echo$a;  yields warnings in PHP 7.1. Insert (int) before $a[$t]+$c to fix.

Run with php -nr '$a="<string>";<code>' or try it online. breakdown for($a=strtr($a,o,1); # replace "o" with "1"$c=$a[$i];$i++) # loop through string$c>0                    # if character is numeric
&&(($d=$a[                  # and ...
$t=$i+                  # 3: target position = current position + 1 line
strpos($a,"\n")+1 # 2: width = (0-indexed) position of first newline +1 ])>" " # if target char is not space ?$d!="/"                # and not left slide
?$d!="\\" # and not right slide ?$d>0                   # but numeric: go
:$a[++$t]<"!"||$a[$t]>0     # right slide: if target+1 is space or ball, then go
:$a[--$t]<"!"||$a[$t]>0     # left slide: if target-1 is space or ball, then go
:1                              # space: go
)&&                     # if go:
$a[$t]=min($a[$t]+$c,9) # move ball/merge balls .!$a[$i]=" " # clear source position ; echo$a;                 # print string