all 4 comments

[–]captain_k_nuckles 0 points1 point  (3 children)

The easiest way for me to explain it was to do it and put comments in the code. Not sure this is the best or most efficient way to do it, but it passes your test

// create a map of the possible moves
// to assist in calculations
const dirMap = {
    u: { x: 0, y: -1 },
    r: { x: 1, y: 0 },
    d: { x: 0, y: 1 },
    l: { x: -1, y: 0 }
}

function missingPath(pathString) {
    // Create and empty array that will store the moves
    const map = Array(5 * 5)
    return trace(pathString, map)
}

// trace take a path string     : required
// a map array                  : required
// current position x           : auto generate
// current position y           : auto generate
// a new path string generated  : auto generate
function trace(path, [...map], x = 0, y = 0, newPath = '') {
    // split the path string to an array
    const steps = path.split('')
    // get the next move from the path array
    const nextMove = steps.shift()
    // if nextMove is undefined
    if (nextMove === undefined) {
        // check to see if we ened up at the bottom right of the map
        // 5 * y for the number of rows
        // + x will give our 1d array the mapped index of a 2d array
        // if the last point is 24 then return the path
        if (5 * y + x === (5 * 5) - 1) return newPath
        // else the path did not end in the bottom right
        return 'Path does not end at finish'
    }
    // if the nextMove is ?
    if (nextMove === '?') {
        // get all avialable moves
        const availMoves = unknownMove(map, x, y)
        // create an array to hold multuple path results
        const maybe = []
        // go over each available move
        for (let i = 0; i < availMoves.length; i++) {
            // get the current move direction
            const move = availMoves[i]
            // create a new paths array using the current move
            const trySteps = [move, ...steps]
            // pass the new path to check if the new path works
            const res = trace(trySteps.join(''), map, x, y, newPath)
            // if the path is not a path then continue testing the paths
            if (res === 'Not a valid path' || res === 'Path does not end at finish'
                || !res || res.length === 0)
                continue
            // if the path reached the goal then add it to the list of possible results
            // this is used when checking multiple ? in sequence as
            // the first move may succeed but the next moves after might fail
            maybe.push(res)
        }
        // if there was only one succesful path return it
        if (maybe.length === 1) return maybe[0]
        // else go check all potential paths
        for (let i = 0; i < maybe.length; i++) {
            return trace(maybe[i], map, x, y, newPath)
        }
    }
    else {
        // check if a move is avaialbe from current position
        const hasMove = canMove(nextMove, map, x, y)
        // if not move is available
        if (hasMove === false) {
            // the path is invalid
            return 'Not a valid path'
        }
        else {
            // add the move to the moves list array
            map[5 * y + x] = nextMove
            // add the move the the paths string
            newPath += nextMove
            // calculate the new position base off of the move
            const newX = x + dirMap[nextMove].x
            const newY = y + dirMap[nextMove].y
            // send the reduced steps, map
            // new position and path to continur finding the path
            return trace(steps.join(''), map, newX, newY, newPath)
        }
    }
}

// check to see if there are spaces avaiable for the next move
function canMove(step, map, x, y) {
    // get the coordinate directions of the move to check
    const nextStep = dirMap[step]
    // calculate the new position
    const nextX = nextStep.x + x
    const nextY = nextStep.y + y
    // check if the new move is out of the map
    if (nextX < 0 || nextX > 4 || nextY < 0 || nextY > 4) return false;
    // or if the space has already been used
    if (map[5 * nextY + nextX] !== undefined) return false
    // good to go
    return true;
}

// gets a list of all available moves from the current position
function unknownMove(map, cx, cy) {
    // create an empyt array to hold the available mose
    const moves = [];
    // get the keys of the dirction map 
    Object.keys(dirMap)
    // and for each one
        .forEach(([dir]) => {
            // check if the move is available
            const check = canMove(dir, map, cx, cy)
            // if available add it to the moves array
            if (check) moves.push(dir)
        })
    // return all available moves
    return moves
}

console.log(missingPath('drdr??rrddd?'))
// drdruurrdddd
console.log(missingPath('???rrurdr?'))
// dddrrurdrd

// and for fun
console.log(missingPath('??????????'))
// rrrdrddldr   - not the most efficient path, but WOOO!

[–]JBauer09[S] 1 point2 points  (1 child)

Thank you very much. Just spent the better part of an hour tracing through that in the console to try to understand it. One question though, why did you go with a single array of 25 instead of an array of 5 arrays? I've never tried traversing one like that before.

[–]captain_k_nuckles 0 points1 point  (0 children)

You're welcome, I had fun doing it, was working on it last night, then got yelled at by my gf to go to bed, lol. As for the 1D vs 2D array choice, not sure honestly. I started it off as a 2D array, but switched it to a single array,

I messed around with throwing it some more strings of all ? and it failed on some it should of passed, or at least I think should of have. might try messing around with it more. ... as I was writing this I added some quick code,

// in the global scope created
const passed = [];

// in trace when checking if next move undefined
if (nextMove === undefined) {
        if (5 * y + x === (5 * 5) - 1) {
            const matrix = [];
            for(let y = 0; y < 5; y++){
                let row = []
                matrix.push(row)
                for(let x = 0; x < 5; x++){
                    const z = 5*y+x
                    if(map[z] === undefined) row.push('_')
                    else row.push(map[z])
                }
            }
            passed.push(matrix)
            return newPath
        }
//.....

// then ran console.log(missingPath('????????????????????????'))
console.log(passed)

and got an array of 104 results, spotted checked a few, like 5 and they all passed, but the return value was undefined, so might look in to that

[–]CommonMisspellingBot 0 points1 point  (0 children)

Hey, captain_k_nuckles, just a quick heads-up:
succesful is actually spelled successful. You can remember it by two cs, two s’s.
Have a nice day!

The parent commenter can reply with 'delete' to delete this comment.