Search code examples
javascriptarrayshtmlpath-finding

Detecting movement range with array


I have one dimension array like this:

    0, 0, 0, 0, 0, 0, 0,
    0, 1, 1, 1, 1, 1, 0,
    0, 0, 0, 0, 0, 1, 1,
    0, 0, 0, 0, 0, 0, 0,
    1, 0, 0, 0, 0, 1, 1,
    0, 1, 0, 1, 1, 1, 0,
    0, 1, 0, 1, 0, 0, 0,

Legend: 0 = empty floor, 1 = wall that blocks the way, 2 = starting point, 3 = floor that is possible to reach from starting point. Movement is possible horizontally, vertically, and diagonally, if value is 0. Example map array is 7x7 and example movement range is 3, but these parameters might be even 15x9 with 6. What I am trying to do is getting one dimension array that shows possible movement range from chosen point like this (example range is 3 steps, and diagonal can pass between walls if position has 0 as you can see in bottom left corner):

    0, 0, 0, 0, 0, 0, 0,
    3, 1, 1, 1, 1, 1, 0,
    3, 3, 3, 3, 3, 1, 1,
    3, 3, 3, 2, 3, 3, 3,
    1, 3, 3, 3, 3, 1, 1,
    3, 1, 3, 1, 1, 1, 0,
    0, 1, 3, 1, 0, 0, 0,

That was easier version, because it would be good if range can be limited to specified shape that can be different in one dimension mask array like this example (0 = outside range):

    0, 0, 0, 1, 0, 0, 0,
    0, 0, 1, 1, 1, 0, 0,
    0, 1, 1, 1, 1, 1, 0,
    1, 1, 1, 1, 1, 1, 1,
    0, 1, 1, 1, 1, 1, 0,
    0, 0, 1, 1, 1, 0, 0,
    0, 0, 0, 1, 0, 0, 0,

In this case, result would be like this:

    0, 0, 0, 0, 0, 0, 0,
    0, 1, 1, 1, 1, 1, 0,
    0, 3, 3, 3, 3, 1, 1,
    3, 3, 3, 2, 3, 3, 3,
    1, 3, 3, 3, 3, 1, 1,
    0, 1, 3, 1, 1, 1, 0,
    0, 1, 0, 1, 0, 0, 0,

Code:

<div id="results" style="font-family: monospace; font-weight: bold; font-size: 24pt; background-color: #000000; color: #FFFFFF;">
</div>
<script>
var map=[0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,0,1,1,1,0,0,1,0,1,0,0,0,];
var mask=[0,0,0,1,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,1,0,0,0,];

function path_create(map,width,height,point,range,mask)
{
// map = pure map with 0 as floor and 1 as wall
// width, height = size of map
// point = starting point to calculate movement range
// range = number of moves from starting point to each direction of horizontal, vertical, and diagonal
// mask = (optional) if possible to do, array mask (0 is not in range) can change range for diagonal moves with special shapes like circle or rhombus
var matrix=[];
return matrix;
// one dimension array where 0 is no range, and 1 is ok
}

function path_show(matrix,width,height)
{
var v="";
for(var i=0; i<matrix.length; i++)
{
if(i!=0 && i%7==0){v=v+"<br>";}
v=v+matrix[i]+" ";
}
document.getElementById('results').innerHTML=v;
}

path_show(path_create(map,7,7,25,3,mask));
//path_show(path_create(map,7,7,16,3,mask));
</script>

Solution

  • Basically you could integrate the mask array and exit early if a cell is found which should not be used.

    This proposal uses matrices instead of linear arrays, because the check for adjacent cells is easier to address.

    Then it takes an array for the possible directions and a check for early exit with

    if (!mask[i][j] || array[i][j] === 1 || !steps || reach[i][j] >= steps) {
        return;
    }
    

    where

    • !mask[i][j] checks the mask,
    • array[i][j] === 1 checks a wall,
    • !steps checks the leftover steps to go or
    • reach[i][j] >= steps where a cell's steps is greater than the leftover steps, this prevents to check already checked steps and prevents to take shorter leftover steps as posssible.

    In all above cases the further processing stops.

    function check(i, j, steps) {
        var directions = [{ x: 0, y: -1 }, { x: -1, y: -1 }, { x: -1, y: 0 }, { x: -1, y: 1 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 1, y: 0 }, { x: 1, y: -1 }];
    
        if (!mask[i][j] || array[i][j] === 1 || !steps || reach[i][j] >= steps) {
            return;
        }
    
        reach[i][j] = steps;
        directions.forEach(({ x, y }) => {
            x += i;
            y += j;
            if (x < 0 || x >= width || y < 0 || y >= height) {
                return;
            }
            check(x, y, steps - 1);
        });
    }
    
    function out(array) {
        document.getElementById('out').innerHTML += array.map(a => a.join('  ')).join('\n') + '\n\n';
    }
    
    function getMatrix(raw, width) {
        var array = [],
            i = 0;
    
        while (i < raw.length) {
            array.push(raw.slice(i, i += width));
        }
        return array;
    }
    
    var width = 7,
        height = 7,
        rawData = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0],
        rawMask = [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0],
        array = getMatrix(rawData, width),
        mask = getMatrix(rawMask, width),
        reach = Array.from({ length: height }, _ => Array.from({ length: width }).fill(0)),
        max = 3,
        result;
    
    array[3][3] = 2;
    check(3, 3, max + 1);
    result = reach.map((a, i) => a.map((b, j) => b === max + 1 ? 2 : b ? 3 : array[i][j]));
    
    out(array);
    out(reach);
    out(result);
    <pre id="out"></pre>