15
\$\begingroup\$

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

Leaderboard (as of 20Oct2019)

  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

\$\endgroup\$
14
  • 5
    \$\begingroup\$ 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? \$\endgroup\$
    – ngn
    Oct 12, 2019 at 10:17
  • 1
    \$\begingroup\$ @ngn thanks! I think that's ok, as long as numbering follows the description \$\endgroup\$ Oct 12, 2019 at 10:25
  • \$\begingroup\$ I'm definitely going to try and answer this in Scratch as soon as I can. Should be fun! (nice question btw) \$\endgroup\$
    – lyxal
    Oct 12, 2019 at 10:54
  • 1
    \$\begingroup\$ There are several similar questions, the closest of which seems to be this one. \$\endgroup\$ Oct 12, 2019 at 17:05
  • 2
    \$\begingroup\$ 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! :) \$\endgroup\$ Oct 12, 2019 at 17:45

4 Answers 4

7
\$\begingroup\$

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()
\$\endgroup\$
4
  • \$\begingroup\$ 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? \$\endgroup\$
    – Night2
    Oct 12, 2019 at 14:40
  • 3
    \$\begingroup\$ @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(). \$\endgroup\$
    – Arnauld
    Oct 12, 2019 at 14:47
  • \$\begingroup\$ 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. \$\endgroup\$
    – Night2
    Oct 12, 2019 at 14:54
  • 3
    \$\begingroup\$ @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. \$\endgroup\$
    – Arnauld
    Oct 12, 2019 at 16:26
2
\$\begingroup\$

Python 2, 317 300 298 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 /.

\$\endgroup\$
2
\$\begingroup\$

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!

\$\endgroup\$
1
\$\begingroup\$

Charcoal, 133 bytes

NθNηUOθη.WS«JNNι»≔⟦⟧ζFθ⊞ζ⟦²ι±¹⟧Fη⊞ζ⟦⁰θι⟧Fθ⊞ζ⟦⁶⁻θ⊕ιη⟧Fη⊞ζ⟦⁴±¹⁻η⊕ι⟧Fζ«J§ι¹§ι²≔﹪⁺⁴§ι⁰¦⁸δM✳δW¬№ζ⟦δⅈⅉ⟧«≡KK/≦⁻⁶δ\≔﹪⁻χδ⁸δPωM✳δ»⊞υ⌕ζ⟦δⅈⅉ⟧»⎚Iυ

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:

NθNηUOθη.

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.

WS«JNNι»

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

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

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

J§ι¹§ι²≔﹪⁺⁴§ι⁰¦⁸δM✳δ

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

W¬№ζ⟦δⅈⅉ⟧«

Until an exit point is found...

≡KK/≦⁻⁶δ\≔﹪⁻χδ⁸δPω

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

M✳δ»

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

⊞υ⌕ζ⟦δⅈⅉ⟧

Remember the found exit point.

»⎚Iυ

Clear the grid and output the list of exit points.

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.