Terrain Reachability

Turn-based tactics games like Advance Wars, Wargroove, and Fire Emblem are made up of a square grid of varying terrain with units of differing movement classes requiring different costs for each terrain type. We'll be investigating a subset of that problem.

Challenge

Your task is to determine if one location is reachable from another given a grid of terrain costs and a movement speed.

Units can only move orthogonally where the cost of moving onto a square is the value of the corresponding cell on the grid (moving off is free). For instance, moving from a cell valued 3 onto a cell valued 1 costs 1 movement, but going the other way requires 3. Some squares may be inaccessible.

Example

1 [1] 1  1  1
1  2  2  3  1
2  3  3  3  4
1  3 <1> 3  4


Moving from [1] to <1> requires a minimum of 7 movement points by moving right one square and then down three. Thus, if given 6 or less as the movement speed, you should output a falsy answer.

Example Test Cases

These will use top-left-origin zero-indexed (row, column) coordinates rather than bracketed cells for start and end to make parsing easier. Unreachable cells will be represented with X

Case 1a

1 1 2 1 X
1 2 2 1 1
2 1 1 2 1
X X X 1 2
Speed: 5
From (2, 3) to (0, 1)

Output: True


Case 1b

1 1 2 1 X
1 2 2 1 1
2 1 1 2 1
X X X 1 2
Speed: 4
From (2, 3) to (0, 1)

Output: False


Case 1c

1 1 2 1 X
1 2 2 1 1
2 1 1 2 1
X X X 1 2
Speed: 5
From (0, 1) to (2, 3)

Output: False


Case 2a

3 6 1 1 X 4 1 2 1 X
5 1 2 2 1 1 1 X 1 5
2 1 1 1 2 1 1 1 X 1
2 1 1 3 1 2 3 4 1 2
1 1 2 1 1 4 1 1 1 2
3 2 3 5 6 1 1 X 1 4
Speed: 7
From (3, 4) to (2, 1)

Output: True


Case 2b

3 6 1 1 X 4 1 2 1 X
5 1 2 2 1 1 1 X 1 5
2 1 1 1 2 1 1 1 X 1
2 1 1 3 1 2 3 4 1 2
1 1 2 1 1 4 1 1 1 2
3 2 3 5 6 1 1 X 1 4
Speed: 4
From (3, 4) to (2, 1)

Output: False


Case 2c

3 6 1 1 X 4 1 2 1 X
5 1 2 2 1 1 1 X 1 5
2 1 1 1 2 1 1 1 X 1
2 1 1 3 1 2 3 4 1 2
1 1 2 1 1 4 1 1 1 2
3 2 3 5 6 1 1 X 1 4
Speed: 7
From (1, 8) to (2, 7)

Output: True


Case 3a

2 1 1 2
2 3 3 1
Speed: 3
From (0, 0) to (1, 1)

Output: False


Case 3b

2 1 1 2
2 3 3 1
Speed: 3
From (1, 1) to (0, 0)

Output: True


Rules, Assumptions, and Notes

• Standard loopholes are banned, I/O can be in any convenient format
• You may assume coordinates are all on the grid
• Movement speed will never be over 100
• Inaccessible cells may be represented with very large numbers (e.g. 420, 9001, 1 million) or with 0 or null, whichever is most convenient for you.
• All inputs will consist of positive integers (unless using null or 0 to represent unreachable cells)
• @LuisfelipeDejesusMunoz "These will use top-left-origin zero-indexed (row, column) coordinates" – Beefster Mar 27 '19 at 18:03
• You say I/O can be in any convenient format. Does that include, for example, a list/array with dimensions? I believe that's typically permitted, but it definitely saves a lot of bytes over parsing a string. – dfeuer Mar 27 '19 at 18:57
• @dfeuer, yes of course – Beefster Mar 27 '19 at 18:58
• I downloaded advanced wars on my phone emulator... I am so sad that it forces you to do the 13 tutorial levels... I wanted to replay it very badly but my patience is paper thin for tutorial pandering on old systems. – Magic Octopus Urn Mar 27 '19 at 19:20

TSQL query, 205 191 bytes

Input is a table variable @t

@x=start xpos, @y=start ypos @i=end xpos , @j=end ypos @ =speed

DECLARE @t table(x int,y int,v int)
INSERT @t
values
(0,0,1),(0,1,1),(0,2,2),(0,3,1),(0,4,null),
(1,0,1),(1,1,2),(1,2,2),(1,3,1),(1,4,1),
(2,0,2),(2,1,1),(2,2,1),(2,3,2),(2,4,1),
(3,0,null),(2,1,null),(2,2,null),(2,3,1),(2,4,2)

DECLARE @x INT=2,@y INT=3,@i INT=0,@j INT=1,@ INT=5;

WITH C as(SELECT @y f,@x r,@ s
UNION ALL
SELECT f+a,r+b,s-v FROM C
JOIN(values(1,0),(0,1),(-1,0),(0,-1))x(a,b)ON
s>0JOIN @t
ON f+a=x and r+b=y)SELECT
max(iif(S>=0and f=@j and r=@i,1,0))FROM c


Try it online ungolfed version

Python 2, 220 bytes

def f(m,a,w,h,r,c,R,C):
T=[w*[999]for _ in' '*h];T[r][c]=0;P=[(r,c)];j,k=1,0
for u,v in P:exec"U,V=u+j,v+k;j,k=-k,j\nif h>U>-1<V<w:q=T[U][V];T[U][V]=min(T[u][v]+m[U][V],q);P+=[(U,V)]*(q>T[U][V])\n"*4
return a>=T[R][C]


Try it online!

Takes an array m of integers, with 'X' as a larger-than-100 value;, a speed a, m having width w and height h; and returns whteher we can start at the zero-indexed row/column cell (r,c) and get to the final cell (R,C).

The algorithm is a modified flood-fill. Slightly ungolfed code:

def f(m,a,w,h,r,c,R,C):
T = [w*[999]for _ in ' '*h] # make an array same size as m, with all
#   values 999, whose values will represent
#   the cost of getting to each cell.
T[r][c] = 0                 # set the starting location to a cost of 0
P = [(r,c)]                 # initialize a set of cells whose neighbors'
#   cost might need to be be updated
j,k = 1,0                   # and also j,k which will take on values:
#  (1,0), (0,1), (-1,0), (0,1), used to
#  probe orthogonal neighbors
for u,v in P:               # scan the cells in P
for _ in '1234':         # look at each of 4 orthogonal positions
U,V = u+j,v+k        # U and V get the indexes of a neighbor
#   of the current cell.
j,k = -k,j           # this 'rotates' the j,k pairing.
if h>U>-1<V<w:       # if the coordinates are in bounds...
q = T[U][V]      # save the current 'cost' of getting to cell (U,V)
# see if we can reduce that cost, which is calculated
#   by taking the cost of the currently scanned cell
#   + the value from m for the neighbor cell.
T[U][V] = min(T[u][v]+m[U][V] ,q)
# if we can reduce the cost, add the neighbor
#   to P because **it's** neighbors might,
#   in turn, need updating.
P += [(U,V)]*(q>T[U][V])
return a>=T[R][C]           # return if speed is enough for the cost.


JavaScript (ES7),  116  113 bytes

Takes input as (matrix)([endRow, endCol])(speed, startRow, startCol). Expects large values for unreachable squares. Returns $$\0\$$ or $$\1\$$.

m=>e=>o=g=(s,y,x)=>m.map((r,Y)=>r.map((v,X)=>r[s<v|(x-X)**2+(y-Y)**2-1||g(s-v,Y,X,r[X]=1/0),X]=v),o|=y+[,x]==e)|o


Try it online!

Commented

m =>                        // m[] = matrix
e =>                        // e[] = target coordinates
o =                       // o   = success flag, initialized to a non-numeric value
g = (                     // g   = recursive depth-first search function taking:
s,                      //   s    = speed
y, x                    //   y, x = starting coordinates
) =>                      //
m.map((r, Y) =>         // for each row r[] at position Y in m[]:
r.map((v, X) =>       //   for each value v at position X in r[]:
r[                  //     this statement ultimately updates r[X]:
s < v |           //       abort if s is less than v
(x - X) ** 2 +    //       or the quadrance between (x, y)
(y - Y) ** 2 - 1  //       and (X, Y) is not equal to 1
|| g(             //       otherwise, do a recursive call to g:
s - v,       //         subtract v from s
Y, X,        //         pass (Y, X) as the new coordinates
r[X] = 1 / 0 //         temporarily make this cell unreachable
),             //       end of recursive call
X                 //       restore r[X] ...
] = v               //     ... to its original value
),                    //   end of inner map()
o |= y + [, x] == e   //   set the flag o if (y + ',' + x) matches (e + '')
) | o                   // end of outer map(); return o


Jelly, 59 bytes

+2¦µ_2ịæ.Ø½œị$Ʋ+5ịƲ$4¦01Ñḣ3Ḋ⁼/Ɗ?ḣ2=/ẸƊoF<0ẸƊƊ?
çⱮØ.,U$;N$¤Ẹ


Try it online!

Not terribly fast; tries all paths until the speed units are exhausted, even retracing its steps. However, this avoids the need to check whether spaces are visited. Input is provided as [nrows, ncols],[start_row, start_col],[end_row, end_col],speed,flattened matrix column-major

Explanation

+2¦                                       | add the right argument to the second item in the left argument (current location)
µ                                      | start a new monadic chain with the modified left argument
4¦                    | for the fourth item (speed)...
_                                     |   subtract...
ịƲ$| the item located at... 2ịæ.Ø½œị$Ʋ                           |       the dot product of the current position and (number of columns,
+5                         |       plus five
? | Now, if...
Ɗ  |   next three as a monad
ḣ2=/ẸƊ         |   either current row or current column are equal to nrows/ncolumns respectively
o        | or
F<0ẸƊ   |   any value is negative
0                        | return zero
?               | else if...
ḣ3Ḋ⁼/Ɗ                 | the second and third items (current and end pos) are equal
1                       | return 1
Ñ                      | else pass the current list back to the main link


ç             | call the helper link with the current list...
Ɱ            |   and each of
Ø.,U$;N$¤   |   [0,1],[1,0],[0,-1],[-1,0]
Ẹ  | Check if any are true


Jelly, 38 bytes

ạƝṢ€Ḅ’¬Ạ
ŒṪ’ḟŒPŒ!€ẎW€j¥@€ÇƇḊ€‘œị⁸§Ṃ’<⁵


An extremely inefficient full program accepting the terrain (with unvisitables as 101) then the start and end co-ordinates then the speed.

Try it online! (not much point trying most of the test cases!)

How?

Creates a list of all permutations of each of the power-set of "all terrain locations except the start & end", surrounds each of these with the start & end, filters to those which make only orthogonal moves of distance one, drops the start from each, indexes back into the terrain, sums each, takes the minimum, subtracts one and tests that this is less than the speed.