Falling ASCII balls

Input

You are given a 2D map with balls and ground in it. It looks like this:

  1         5          2
3
4

__________________________


Each number is a ball, and the _ is ground level. The underscore _ character is not allowed in any other line than ground level line. There are only spaces, newlines and digits 0-9 allowed above ground level. You cannot assume that last line is the ground level - empty lines below ground level are allowed. You can also add spaces, to fill empty lines, if that does help you.

Balls can have numbers from 0 to 9, can be placed above each other, but not under ground. The ball's numbers will be unique.

Assume that each character is one meter.

Get map from pastebin!
Test case 1 - should output something like this
Test case 2 - should produce same results as first map

Challenge

Your challenge is to read a map like that from a file or from stdin — you are allowed to use cat balls.txt | ./yourexecutable — and output velocity of each ball when it hits the ground.

Here's the formula for velocity: Assume that h is the line number difference between the ground's line number, and the ball's line number, and that g equals 10m/s^2.

Output

You should output each balls number and velocity in m/s at ground level. For example N - Vm/s, where N is ball number and V is its velocity. You can also output an array if you want.

Happy coding! :)

• Test cases with no expected result are not test cases – edc65 Jun 25 '16 at 15:26
• @edc65 I added expected results to the question – Jacajack Jun 25 '16 at 15:33
• Is it okay if I take the directory as input from the user as part of the program? – Daniel Jun 25 '16 at 15:49
• @Dopapp What do you mean exactly? – Jacajack Jun 25 '16 at 15:52
• See my answer. – Daniel Jun 25 '16 at 16:07

MATL, 313027 25 bytes

95\16\5B#fG&X>1)b- 20*X^h


Input is a 2D char array with ; as row separator:

['  1         5          2  ';'                 3        ';'     4                    ';'                          ';'                          ';'__________________________']


Try it online! Or include an initial t in the code to display the map for greater clarity.

Here are the other test cases: first, second.

Explanation

95\      % Take input implicitly. Modulo 95: convert to numbers and map '_' into 0
16\      % Modulo 16: map space into 0 and digit chars into corresponding numbers
5B#f     % Find row indices and values of nonzero entries
G        % Push input again
&X>      % Index of maximum of each column. This finds character '_'
1)       % Get first value (they are all equal)
b        % Bubble row indices of numbers up in the stack
-        % Subtract to get distance from each number to the ground
20*X^    % Multiply by 20, take sqrt. This gives the velocity values
h        % Horizontally concat numbers and velocities. Display implicitly


C, 125122 121 bytes

b={};main(l,c){for(;(c=getchar())<95u;)b[c]=(l+=c==10);for(c=47;++c<58;)b[c]&&printf("%c,%f\n",c,sqrt((l-b[c])*20));}


Compile & run with gcc -w golf.c -lm && cat balls.txt | ./a.out.

• That's really great, sir! I didn't say that in my question, but I'd like you to know, that your example does not output anything, when character other than 0 ... 9 occurs in text file. Anyway, +1, because not pointing this out is my fault – Jacajack Jun 25 '16 at 11:45
• @Jacajack No, any character is fine as long as it does not contain a character with ASCII code bigger than _. However, this could be fixed with one extra byte (!= instead of <). – orlp Jun 25 '16 at 12:32
• Well, I used 'x' for testing. Nevermind. Your code's great :) – Jacajack Jun 25 '16 at 12:56
• @Jacajack In the new version it's not a one character fix anymore, but I saved 3 more bytes :) – orlp Jun 25 '16 at 16:21
• Nice! :) I'll take a look at what I can do with my code, when I come back home. I know it can be shortened a lot, but I don't want it to be a copy of yours :p – Jacajack Jun 25 '16 at 16:29

C -194(-5)150 137 bytes

With a little bit more time and thinking, I golfed off 44 bytes
Thanks to orlp for helping me to save 13 bytes

b={},n,i=47;main(l,c){for(;~(c=getchar());n=c==95?l:n)b[c]=(l+=c==10);for(;++i<58;)b[i]&&printf("%d %f\n",i-48,sqrt((n-b[i])*20));}


//Throws many warnings, but lack of libraries is tolerated

/*
c - current character
l - line number (starts at 1)
n - ground level
i - iterator
b - balls array
*/

b = {}, n, i = 47; //That actually works, as long as you are using ASCII

main( l, c )
{
for ( ;~( c = getchar( ) ); n = c == 95 ? l : n ) //Read stdin and search for ground
b[c] = ( l += c == 10 ); //Increment lines counter on newlines, and save line numbers

for ( ; ++i < 58; ) //Iterate through balls
b[i] && printf( "%d %f\n", i - 48, sqrt( ( n - b[i] ) * 20 ) ); //Print out data
}


Compile and run like that: gcc -o balls ballsgolf.c -lm && cat 1.txt | ./balls

Output

1 10.000000
2 10.000000
3 8.944272
4 7.745967
5 10.000000

• Save 4 bytes: ~(c=getchar()) instead of (c=getchar())!=EOF. – marinus Jun 25 '16 at 14:45
• @marinus That's what I had. – orlp Jun 25 '16 at 16:16
• if (x != -1) is the same as if (~x) (on two's complement machines) because ~-1 is (uniquely) 0. In C golf never use while(cond), as for(;cond;) is just as long and provides more opportunities for golfing. In your example this can become for(;~(c=getchar());n=c==95?l:n)b[c]=(l+=c==10);. – orlp Jun 25 '16 at 22:05
• @orlp I understand, thanks for advice :) – Jacajack Jun 25 '16 at 22:08
• l=1 can be circumvented by making l the first argument for main, as the C runtime passes the number of arguments to main as its first argument (argc), and when you call a program without any command line arguments (./a.out), then argc = l = 1. n=0; is unnecessary, as global integers are automatically initialized to 0. So just n; will suffice. – orlp Jun 25 '16 at 22:09

Pyth, 272625 24 bytes

smf-hT" _".e,b@*20-xd\_k2dC
smf@hTM;.e,b@*20-xd\_k2dC
smf@TM;.e,b@*20-xd\_k2dC
sm@#M;.e,b@*20-xd\_k2dC


Try it online!

Matlab, 1009689 90 bytes

s=input('');X=find(s==95);for i=0:9
[x y]=find(s==48+i);if(x)[i sqrt(20*(X(1)-x))]
end
end


Many bytes saved thanks to Luis Mendo

Input format:

['  1         9          2  ';'                 3        ';'     4                    ';'                          ';'                          ';'__________________________']


Explanation:

X=find(s==95)         -- finds '_', we'll need X(1) to determine max height
for i=0:9             -- loops through balls' numbers
[x y]=find(s==48+i)   -- finds the ball
if(x)                 -- if it is present
[i sqrt(20*(X(1)-x))] -- output its number and velocity


Python 3, 84 bytes

Version 6, 84 bytes: (Thanks to Leaky Nun!)

lambda a:[(c,(~-(len(a)-i)*20)**.5)for i,s in enumerate(a)for c in s if c.isdigit()]


Version 5, 91 bytes:

lambda a:[c+":"+str((~-(len(a)-i)*20)**.5)for i,s in enumerate(a)for c in s if c.isdigit()]


Version 4, 92 bytes:

lambda i:[c+":"+str((~-(len(i)-n)*20)**.5)for n in range(len(i))for c in i[n]if c.isdigit()]


Version 3, 99 bytes:

def r(i):x=len(i);print([c+":"+str((~-(x-n)*20)**.5)for n in range(x)for c in i[n] if c.isdigit()])


Version 2, 102 bytes:

def r(i):
n=len(i)
for l in i:
for c in l:
if c.isdigit():print(c+":"+str((~-n*20)**.5))
n-=1


The above versions take an array of strings as input.

Version 1, 140 bytes:

with open(input(),"r")as i:
n=sum(1for l in i);i.seek(0)
for l in i:
for c in l:
if c.isdigit():print(c+":"+str((~-n*20)**.5))
n-=1


This takes the directory of the file as input from the user.

• 1 for l in i -> 1for l in i – Leaky Nun Jun 25 '16 at 16:09
• @LeakyNun, does that trick work with all keywords and numbers? – Daniel Jun 25 '16 at 16:10
• I believe so. Also, (n-1)*20 -> ~-n*20 – Leaky Nun Jun 25 '16 at 16:10
• Hold on. Doesn't Python3 require parentheses with the print call? – Yytsi Jun 25 '16 at 16:15
• @LeakyNun No it doesn't work for all keywords and numbers in Python 2. It specifically does not work for keywords starting with an e, because then the Python tokenizer will attempt to parse it as floating point scientific notation (e.g. 1e5). Example that fails: f = lambda n:-1if n<0else 1. An example that fails in both Python version is 0or 1, because the tokenizer thinks 0o starts an octal number. – orlp Jun 25 '16 at 22:14

Python 3, 84 bytes

lambda x:[[i,(20*x[x.find(i):x.find('_')].count('\n'))**.5]for i in x if i.isdigit()]


An anonymous function that accepts input by argument as a multi-line string with all empty lines filled with spaces, and returns an array where each element is of the form [ball number, speed].

How it works

lambda x                      Function with input x
...for i in x if i.isdigit()  Loop through all characters i in x for which i is a digit,
and hence one of the balls
x[x.find(i):x.find('_')]      Slice x to give the substring between the ball and the ground
....count('\n')               Count the number of newlines in the substring to give the
height of the ball
(20*...)**.5                  Calculate the speed of the ball as it hits the ground
[i,...]                       Package the ball number and speed into a list
:[...]                        Return all ball-speed pairs as a list with elements [ball
number, speed]


Try it on Ideone

• In this case, I think, it's code snippet rather than full standalone Python script, isn't it? – Jacajack Jun 25 '16 at 19:56
• @Jacajack This is in fact a function, not a snippet, which is allowed by default. In Python, lambda functions are functions without a name which can assigned to a variable and then called when needed; you could write f = MyAnswer, and then call using f(x). There is a consensus that there is no need to name lambdas. Nice challenge, by the way! – TheBikingViking Jun 25 '16 at 20:04
• Sure, I just thought lambdas were assumed to be code snippets here (meta.codegolf.stackexchange.com/a/1146/55729). I guess everything is fine, then. Thank you for your opinion :) – Jacajack Jun 25 '16 at 20:11

JavaScript (ES6) 93

Edit 2 bytes saved thx @Jacajack

A function with a multiline string as input parameter. Output is not sorted (as this in not requested)

a=>[...a].reverse().map(c=>c>'Z'?b=i:c<' '?++i:c>' '&&console.log(c,Math.sqrt((i-b)*20)),i=0)


Test

F=
a=>[...a].reverse().map(c=>c>'Z'?b=i:c<' '?++i:c>' '&&console.log(c,Math.sqrt((i-b)*20)),i=0)

function test()
{
F(I.value);
}

test()
#I { height: 12em; width: 30em}
<textarea id=I>

1         5          2
3
4

__________________________

</textarea>
<button onclick="test()"></button>

• Wouldn't sqrt(x) be shorter than pow(x,.5)`? – Jacajack Jun 25 '16 at 23:32
• @Jacajack yes thanks I don't know how that slipped off my mind – edc65 Jun 25 '16 at 23:37