# List where pinballs would exit a grid of bumpers from different entrance points

Inspired by this lumosity mini game, Pinball Recall

We start with a rectangular grid (viewed from above) with several bumpers as follows. Entrances/exits are numbered counter-clockwise starting from the leftmost of the bottom of the grid, as number 0.

   24 23 22 21 20 19 18 17 16 15
25  .  .  .  .  .  /  .  .  .  . 14
26  .  .  .  .  .  .  .  .  .  . 13
27  .  /  \  .  .  \  .  .  \  . 12
28  .  .  .  .  .  .  .  .  .  . 11
29  .  .  .  .  .  .  /  .  .  . 10
0  1  2  3  4  5  6  7  8  9


Pinballs can be fired from each entrance into the grid at a direction perpendicular to the grid wall, e.g. in the above example:

• 0 to 9: fired upwards
• 10 to 14: fired to the left
• 15 to 24: fired downwards
• 24 to 29: fired to the right

The pinballs will move in a straight line until they reach an exit, or a bumper, in which case the ball will bounce off the bumper and change direction. So in above diagram,

• from 0, ball goes straight to 24
• from 1, ball goes up to / and bounces off to reach \, then bounces off again and ends up at 2
• from 10, ball goes left to / and bounces off to 6

Inputs

Program shall receive the grid width and height. Then program receives the bumper orientation (represented as / or \) followed by coordinates, which also start from 0 from bottom left, e.g.

 4  .  .  .  .  .  .  .  .  .  .
3  .  .  .  .  .  .  .  .  .  .
2  .  .  .  .  .  .  .  .  .  .
1  .  .  .  .  .  .  .  .  .  .
0  .  .  .  .  .  .  .  .  .  .
0  1  2  3  4  5  6  7  8  9


"done" keyword used to indicate end of input

edit: as suggested by ngn, program may receive list of bumper coordinates instead so the "done" keyword is not needed

Output

Program should print out the exit locations of a pinball if they are fired from 0 to the last entrance of grid, in above case, 0 to 29

Sample input and output

Input

10 5
/ 1 2 / 5 4 / 6 0 \ 2 2 \ 5 2 \ 8 2
done


Output

24 2 1 21 20 22 10 17 14 15 6 28 16 26 8 9 12 7 29 25 4 3 5 27 0 19 13 23 11 18


1. Neil, Charcoal, 133 bytes
2. Arnauld, JavaScript (ES6), 212 bytes
3. Embodiment of Ignorance, C# (Visual C# Interactive Compiler), 277 bytes
4. Chas Brown, Python 2, 290 bytes

edit: FryAmTheEggman points out a similar question to try

• nice first challenge! welcome to codegolf.stackexchange.com. how much flexibility would you allow in the input? for instance would it be ok to write a function that accepts a list like [["/",x0,y0],["\\",x1,y1],...] instead of input terminated with done? – ngn Oct 12 at 10:17
• @ngn thanks! I think that's ok, as long as numbering follows the description – kuantumleap123 Oct 12 at 10:25
• I'm definitely going to try and answer this in Scratch as soon as I can. Should be fun! (nice question btw) – Jono 2906 Oct 12 at 10:54
• There are several similar questions, the closest of which seems to be this one. – FryAmTheEggman Oct 12 at 17:05
• Just to be clear, I wasn't pointing that out to in any way say your challenge was bad. Other challenges are often linked on this site so that people can look at other approaches for golfing/etc inspiration. Also, while this went very well for a first challenge, please consider using our sandbox in the future! :) – FryAmTheEggman Oct 12 at 17:45

# JavaScript (ES6),  218 215 213  212 bytes

Takes input as (w,h,o) where $$\o\$$ is an object whose keys are the coordinates of the bumpers in 'x,y' format and whose values are either '/' or '\'.

(w,h,o)=>(A=[...Array(w+h<<1)].map((_,n)=>(p=n-w)<0?[n,-1,3]:p<h?[w,p]:(p-=w)<h?[h+~p,h,1]:[-1,2*h+~p,2])).map(g=([x,y,d])=>A.every(([X,Y])=>X-x|Y-y&&++i,(c=o[[x+=~-d%2,y+=~-~-d%2]])?d^=c<{}||3:i=0)?g([x,y,d]):i)


Try it online!

### Commented

We first create an array $$\A[\:]\$$ of tuples $$\[x, y, d]\$$ where $$\(x, y)\$$ is the starting position along the borders of the playfield and $$\d\$$ is the initial direction.

( A = [...Array(w + h << 1)]  // create an array A[] of (w + h) * 2 items
.map((_, n) =>              // for each item at position n in A[]:
(p = n - w) < 0 ?         //   set p = n - w; if this is the bottom side:
[n, -1, 3]              //     start at (n, -1) in direction 3
:                         //   else:
p < h ?                 //     if this is the right side:
[w, p]                //       start at (w, p) in direction 0 (implicit)
:                       //     else:
(p -= w) < h ?        //       subtract w from p; if this is the top side:
[h + ~p, h, 1]      //         start at (h + ~p, h) in direction 1
:                     //       else (left side):
[-1, 2 * h + ~p, 2] //         start at (-1, 2 * h + ~p) in direction 2
)                           // end of map()
)                             //


We then simulate the trajectory of the ball, starting at each position defined above, until it reaches another starting position.

.map(g = ([x, y, d]) =>       // for each tuple [x, y, d] in A[]:
A.every(([X, Y]) =>         //   for each tuple [X, Y] in A[]:
X - x | Y - y             //     break if (x, y) = (X, Y)
&& ++i,                   //     otherwise, increment i
( c =                     //     define c as
o[[                   //       the bumper character at
x += ~-d % 2,       //         (x, y), once updated according to d
y += ~-~-d % 2      //         (0 = West, 1 = South, 2 = East, 3 = North)
]]                    //
) ?                       //     if c is set:
d ^= c < {} || 3        //       XOR d with 1 (for '/') or 3 (for '\')
:                         //     else:
i = 0                   //       initialize i to 0
) ?                         //   end of every(); if truthy:
g([x, y, d])              //     do a recursive call to move the ball further
:                           //   else:
i                         //     we've reached a starting position: yield i
)                             // end of map()

• Amazing! I got one question, I cannot understand why A=[...Array((w+h)*2)] works, but A=Array((w+h)*2) doesn't. Isn't [...Array((w+h)*2)] actually creating same array as Array((w+h)*2) itself? – Night2 Oct 12 at 14:40
• @Night2 Array(n) basically just sets the length of the array to $n$ but doesn't create any item -- even undefined ones. [...Array(n)] forces the array to be filled with $n$ undefined values that can be iterated with .map(). – Arnauld Oct 12 at 14:47
• Thanks, for some reason FireFox's console was showing they are the same and I was confused: i.imgur.com/JOeSkxn.png It probably is a bug in how FireFox visualizes arrays in the console. – Night2 Oct 12 at 14:54
• @Night2 Yes, the output of the console in FF is misleading. BTW, I should have said forces the creation of another array filled with $n$ undefined values (this is definitely not the same array). Here is some code illustrating that. – Arnauld Oct 12 at 16:26

# Python 2, 317300298 290 bytes

def f(w,h,B):
A=[w*[-1]for c in' '*h];p=w+h
for x,y,c in B:A[y][x]=c<'0'
for i in range(2*p):
x,y,d=[[[(0,2*p+~i,0),(w+p+~i,h-1,1)][i<w+p],(w-1,i-w,3)][i<p],(i,0,2)][i<w]
while w>x>=0<=y<h:d^=1+A[y][x];x+=[1,0,0,-1][d];y+=[0,-1,1,0][d]
print[[y+w,2*p+~y][x<0],[w+p+~x,x][y<h]][0<d<3],


Try it online!

Takes the bumpers as a list of tuples (x,y,c)where x and y are the coordinates of the bumper and c is either \ or /.

# C# (Visual C# Interactive Compiler), 277 bytes

(w,h,d)=>{var z=new int[w+h<<1].Select((_,i)=>i<w?(i,-1,3):i<w+h?(w,i-w,0):(i-=w+w)<h?(h+~i,h,1):(-1,h+h+~i,2)).ToList();z.ForEach(x=>{var(a,b,c)=x;do{try{c^=d[(a,b)]<48?1:3;}catch{}a+=~-c%2;b+=(c-2)%2;}while(a>=0&a<w&b>=0&b<h);Print(z.FindIndex(l=>l.Item1==a&l.Item2==b));});}


Try it online!

# Charcoal, 133 bytes

ＮθＮηＵＯθη.ＷＳ«ＪＮＮι»≔⟦⟧ζＦθ⊞ζ⟦²ι±¹⟧Ｆη⊞ζ⟦⁰θι⟧Ｆθ⊞ζ⟦⁶⁻θ⊕ιη⟧Ｆη⊞ζ⟦⁴±¹⁻η⊕ι⟧Ｆζ«Ｊ§ι¹§ι²≔﹪⁺⁴§ι⁰¦⁸δＭ✳δＷ¬№ζ⟦δⅈⅉ⟧«≡ＫＫ/≦⁻⁶δ\≔﹪⁻χδ⁸δＰωＭ✳δ»⊞υ⌕ζ⟦δⅈⅉ⟧»⎚Ｉυ


Try it online! Link is to verbose version of code. Takes input on separate lines with a blank trailing line but for convenience the test case uses spaces instead of newlines. Explanation:

ＮθＮηＵＯθη.


Input the width and height and draw a box of .s of that size. I'm drawing with the origin at the top left but I'm flipping the vertical co-ordinate in all of my calculations so the results will be correct.

ＷＳ«ＪＮＮι»


Loop over the input list and print the \ or / characters at the relevant positions.

≔⟦⟧ζＦθ⊞ζ⟦²ι±¹⟧Ｆη⊞ζ⟦⁰θι⟧Ｆθ⊞ζ⟦⁶⁻θ⊕ιη⟧Ｆη⊞ζ⟦⁴±¹⁻η⊕ι⟧Ｆζ«


Build up a list for all of the entry/exit points giving the direction of exit and the coordinates and loop over the list.

Ｊ§ι¹§ι²≔﹪⁺⁴§ι⁰¦⁸δＭ✳δ


Jump to the exit point, calculate the direction of entry and take a step in that direction.

Ｗ¬№ζ⟦δⅈⅉ⟧«


Until an exit point is found...

≡ＫＫ/≦⁻⁶δ\≔﹪⁻χδ⁸δＰω


... adjust the direction if the cursor is over a / or \...

Ｍ✳δ»


... and take a step in the current direction.

⊞υ⌕ζ⟦δⅈⅉ⟧


Remember the found exit point.

»⎚Ｉυ


Clear the grid and output the list of exit points.

• It appears the output doesn't display on tio.run? – kuantumleap123 Oct 21 at 13:00
• @kuantumleap123 My bad, I typo'd a character, sorry about that. Fixed now. – Neil Oct 21 at 13:04