Paths & Wasting Time

22

1

Premise

So recently I was about half an hour early to an appointment, and decided to wait outside. I also determined that it would look strange if I just stood motionlessly in front of the house. Therefore, I decided to go on a quick walk, within a limited area. I also concluded that if I started walking in circles that would make it obvious that I was loitering. So I was inspired to create my first Code Golf challenge.

Specification

You will be given a list, a map of the area, which will contain either " " or "#", which represent free spaces and obstacles of some sort. Free spaces can only be crossed once, and it takes 1 minute to cross it. Your initial position will be signified with a "@" per roguelike tradition, and the target will be represented with a "$" because that's what you're going to lose there. You will also be given an integer which will represent how many minutes you have to waste before not seeming as if you were intruding. When you land on the "$", it will have to have been the exact amount minutes (so if you were counting down, it will have to be 1 on an adjacent tile, and be 0 on the tile). It will always be possible to reach the destination. Your program or function will have to return a list showing the shortest path with <, >, ^, and v to represent the four possible directions.

Examples

Input:

[[" ", " ", " ", " "],
 ["@", " ", " ", "$"],
 [" ", " ", " ", " "],
 [" ", " ", " ", " "]]

and

5

Ouput:

[[">", ">", ">", "v"],
 ["^", " ", " ", "$"],
 [" ", " ", " ", " "],
 [" ", " ", " ", " "]]

Input:

[[" ", "#", " ", " ", " "],
 [" ", "#", " ", " ", " "],
 ["@", "#", " ", "$", " "],
 [" ", " ", " ", " ", " "],
 [" ", "#", " ", " ", " "],
 [" ", "#", " ", " ", " "]]

and

7

Output:

[[" ", "#", " ", " ", " "],
 [" ", "#", ">", "v", " "],
 ["v", "#", "^", "$", " "],
 [">", ">", "^", " ", " "],
 [" ", "#", " ", " ", " "],
 [" ", "#", " ", " ", " "]]

Input:

[[" ", "#", " ", " ", " "],
 [" ", "#", " ", " ", " "],
 ["@", "#", " ", "$", " "],
 [" ", " ", " ", " ", " "],
 [" ", "#", " ", " ", " "],
 [" ", "#", " ", " ", " "]]

and

17

Output:

[[" ", "#", " ", "v", "<"],
 [" ", "#", " ", "v", "^"],
 ["v", "#", " ", "$", "^"],
 [">", ">", "v", ">", "^"],
 [" ", "#", "v", "^", "<"],
 [" ", "#", ">", ">", "^"]]

Rules

  • Standard loopholes apply
  • Each tile must only be moved over once
  • The exact amount of time must be spent on the board
  • Only one path needs to be displayed in the case of multiple paths
  • This is a code golfing question so shortest answer wins
  • As per user202729's question in the comments, you may assume valid input.

Add a comment if any further clarification is required

LForchini

Posted 2018-04-10T11:11:37.800

Reputation: 311

1Is it guaranteed that "It will always be possible to reach the destination in the time specified"? – user202729 – 2018-04-10T11:19:13.417

Yes, it will, there will always be a way, even if convoluted – LForchini – 2018-04-10T11:23:00.100

5Welcome to PPCG! :) Nice first challenge. – Giuseppe – 2018-04-10T11:56:50.283

What happened to the half minute at each end?! (no need to change anything if it's not obvious) – Jonathan Allan – 2018-04-10T21:04:25.763

Answers

6

JavaScript (ES6), 171 bytes

Takes input in currying syntax (a)(n). Outputs by modifying the input matrix.

a=>g=(n,y=a[F='findIndex'](r=>~(i=r[F](v=>v>'?'))),x=i,R=a[y])=>!n--|[-1,0,1,2].every(d=>(R[x]='<^>v'[d+1],(c=(a[Y=y+~-d%2]||0)[X=x+d%2])<1?g(n,Y,X):n|c!='$')&&(R[x]=' '))

Try it online!

Commented

a =>                           // a[] = input matrix
g = (                          // g = recursive function taking:
  n,                           //   n = number of remaining moves
                               //   (x, y) = current coordinates, initialized as follows:
  y = a[F = 'findIndex'](r =>  //     y = index of the row containing the starting point,
    ~(i = r[F](v => v > '?'))  //         found by iterating over all rows r until we
  ),                           //         find some i such that r[i] > '?'
  x = i,                       //     x = index of the column of the starting point
  R = a[y]                     //   R[] = current row
) =>                           //
  !n-- |                       // decrement n; force failure if we're out of moves
  [-1, 0, 1, 2].every(d =>     // for each direction d, where -1 = left, 0 = up,
    (                          // 1 = right and 2 = down:
      R[x] = '<^>v'[d + 1], (  //   update the current cell with the direction symbol
        c = (                  //   c = content of the new cell at (X, Y) with:
          a[Y = y + ~-d % 2]   //     Y = y + dy
          || 0                 //     (use a dummy value if this row does not exist)
        )[X = x + d % 2]       //     X = x + dx
      ) < 1 ?                  //   if c is a space:
        g(n, Y, X)             //     we can go on with a recursive call
      :                        //   else:
        n | c != '$'           //     return false if n = 0 and we've reached the target
    ) &&                       //   unless the above result is falsy,
    (R[x] = ' ')               //   restore the current cell to a space
  )                            // end of every()

Arnauld

Posted 2018-04-10T11:11:37.800

Reputation: 111 334

5

Python 2, 310 256 bytes

Thanks @cairdcoinheringaahing for except:0 -3 bytes
Thanks @Mnemonic for -8 bytes
Thanks @JonathanAllan for -3 bytes
Thanks @ovs for -5 bytes

G,L=input()
R=[]
def S(x,y,G,c,R):
 try:
	if x>-1<y<G[y][x]in' @':i=0;exec"T=[r[:]for r in G];T[y][x]='<v^>'[i];S(x+~-i/2,y+~-(i^2)/2,T,c-1,R);i+=1;"*4
	R+=[G]*(0==c<'$'==G[y][x])
 except:0
for i,y in enumerate(G):"@"in y>S(y.index("@"),i,G,L,R)
print R[0]

Try it online!

Some explanation:

try-except is used to ensure, that both x and y coordinates are in boundaries. Exception will be raised upon access to G[y][x]. Python is too good and negative indices are acceptable, so check x>-1<y is added.

T=[r[:]for r in G] used to create copy of G by values

~-i/2 and ~-(i^2)/2 are used to generate pairs (-1, 0), (0, 1), (0, -1), (1, 0), that used to move in grid (there still should be shorter way!)

R+=[G]*(0==c<'$'==G[y][x]) check, that '$' is reached in required number of steps. R is used to get this result from recursive function calls.

for i,y in enumerate(G):"@"in y>S(y.index("@"),i,G,L,R) Found x and y of '@' in input and call function S.

print R[0] R might contain more that one solution, so output just first

Dead Possum

Posted 2018-04-10T11:11:37.800

Reputation: 3 256

-4 bytes – caird coinheringaahing – 2018-04-10T13:32:44.943

1You can save a byte by replacing if G[y][x]=='$': with if'$'==G[y][x]:. – None – 2018-04-10T14:36:31.980

1Actually, that whole condition can be replaced with R+=(G[y][x]=='$')*(c==0)*[G] for another byte. – None – 2018-04-10T14:44:11.773

@Mnemonic Second fix does not save bytes, but still cool, thanks ;D – Dead Possum – 2018-04-10T14:55:17.433

1Huh, not sure what I was seeing. You can save a couple bytes in the first condition with if(x>-1<y)*(G[y][x]in' @'): – None – 2018-04-10T15:01:37.667

Also, you can save a few on the loop at the end. 268 bytes

– None – 2018-04-10T15:04:32.657

@Mnemonic Thanks! Even 2 more with cheeky list comprehension :D – Dead Possum – 2018-04-10T15:11:20.907

1A shorter way for your y+cmp(i%2,i/2) would be y+~-(i^2)/2; there may well be shorter still. – Jonathan Allan – 2018-04-10T21:28:39.170

@JonathanAllan Thanks! Search continues.. – Dead Possum – 2018-04-11T14:36:31.907

@ovs Clever fixes, made me scrach my head for some time, thanks! – Dead Possum – 2018-04-11T14:37:21.743

[S(y.index("@"),i,G,L,R)for i,y in enumerate(G)if"@"in y]-> for i,y in enumerate(G):"@"in y>S(y.index("@"),i,G,L,R) for -2 bytes – ovs – 2018-04-11T15:23:56.200

2

Python 2, 264 261 251 249 bytes

def f(a,n,r=-1,s=0):
 j=len(a[0]);x=1;z=y=0
 if r<0:s,r=divmod(sum(a,[]).index('@'),j)
 for c in'>v<^':
	u=r+x;v=s+y;x,y=-y,x
	if j>u>-1<v<len(a):b=[e[:]for e in a];b[s][r]=c;w=a[v][u];z=n*(w<'!')and f(b,n-1,u,v)or n==1and w=='$'and b
	if z:return z

Try it online!

Chas Brown

Posted 2018-04-10T11:11:37.800

Reputation: 8 959

1@Arnauld: Oops! Got a bit too fancy. Fixed. – Chas Brown – 2018-04-11T17:21:03.957