简单路径查询

应用场景

          棋盘类的路径选择。     

作用效果  

        从棋盘上随机或指定位置中选择一个起点,按照规定好的数量和方向(例如:上、下、左、右)去随机的选择相邻的模块,每个模块只会选择一次,如果数量未达到指定要求时返回找到的最大的路线。

实现原理

        主要用栈的方法实现

        选择好第一个节点后,节点进栈,

        按照规定好的方向去搜索下一个节点,

                找到后进栈,删除来时方向,标记为不可选,加入到备选返回队列中

                未找到,栈内元素出栈,检查栈是否为空,

                        不为空,栈顶元素搜索下一节点

                        空,将此次遍历的所有节点置为不可选,从新选择开始节点,如无开始节点结束查找。


 代码实现

var Utils = require('geoUtils');

/**

* createMap函数将返回这样的数据类型的二维数组

* @param {*} item

* @param {*} canSelect

* @param {*} directions //此数据将在选择路径时再赋值    createMap时不进行赋值

*/

function MapInformation(item, canSelect, position, directions) {

    this.item = item;

    this.canSelect = canSelect;

    this.position = position;

    this.directions = directions;

}

var MapUtils = {

    DirectionEnum: {

        UP: { x: -1, y: 0 },

        DOWN: { x: 1, y: 0 },

        LEFT: { x: 0, y: -1 },

        RIGHT: { x: 0, y: 1 },

        UPPER_LEFT: { x: -1, y: -1 },

        UPPER_RIGHT: { x: -1, y: 1 },

        LOWER_LEFT: { x: 1, y: -1 },

        LOWER_RIGHT: { x: 1, y: 1 },

    },

    /**

    * 根据给定的道具集合,创建二维数据集

    * @param {*} items 道具集合(1维)

    * @param {*} numCols 行数

    * @param {*} numVols 列数

    * @returns 返回包装了items的二维数据集,用于搜索功能。

    */

    createMap(items, numCols, numVols) {

        //将要返回的对象

        var ret = {

            //用于探索的二维数组

            array: [],

            //用于记录步数不足maxSteps的item的数组

            notEnough: [],

        };

        //记录数组items当前位置的变量

        var index = 0;

        for (var i = 0; i < numCols; i++) {

            ret.array.push([]);

            for (var j = 0; j < numVols; j++) {

                //获得item  同时index更新

                var item = items[index++];

                var mapObj = new MapInformation(item, true, { x: i, y: j });

                ret.array[i].push(mapObj);

            }

        }

        return ret;

    },

    /**

    *

    * @param {*} map createMap函数返回的对象

    * @param {*} directions 可以选择的方向

    * @param {*} maxSteps 想要走几步

    * @param {*} useStrict 当步数不足maxSteps时是否返回选到的其它的最大步数

    * @param {*} start 起点对象数组[{col:0,vol:0},{col:1,vol:1}]

    * @example

    * var result = MapUtils.serachMap(map,

        *  [MapUtils.DirectionEnum.up, MapUtils.DirectionEnum.down],

        *  2, true);

        */

    searchMap(map, directions, maxSteps, useStrict, start) {

        var mapObjArray = this._localSearchMap(map, directions, maxSteps, useStrict, start);

        var itemArray = this._mapObjArrayToItemArray(mapObjArray);

        return itemArray;

    },

    /**

    *

    * @param {*} map

    * @param {*} PreinstallObjs [{directions:dir,maxSteps:num,useStrict,start:{}},{directions:dir,maxSteps:num,start:{}}]

    */

    searchMapByPreinstall(map, PreinstallObjs) {

        var pObj = Utils.array.random(PreinstallObjs, 1, true)[0];

        return this.searchMap(map, pObj.directions, pObj.maxSteps, pObj.useStrict, pObj.start);

    },

    /**

    *

    * @param {*} map createMap函数返回的对象

    * @param {*} directions 可以选择的方向

    * @param {*} maxSteps 想要走几步

    * @param {*} useStrict 当步数不足maxSteps时是否返回选到的其它的最大步数

    * @example

    * var result = MapUtils.serachMap(map,

    *  [MapUtils.DirectionEnum.up, MapUtils.DirectionEnum.down],

    *  2, true);

    */

    _localSearchMap(map, directions, maxSteps, useStrict, start) {

        var ret = [];

        //记录本次便利未达到指定步数的路线

        var notEnough = [];

        //用于判断item的栈

        var storehouse = new Array(maxSteps);

        //栈指针 指向栈顶元素

        var s = -1;

        //用于记录当前已经选择的mapObj的数组

        var selectMapObj = [];

        //添加可选择的方向

        this._addDirections(map.array, directions);

        var canSelectMapObjs = this._getCanSelectObj(map.array, start);

        do {

            //每次循环前清空数组

            selectMapObj = [];

            //回复数据记录

            this._recoverNotEnough(notEnough);

            this._addDirections(notEnough, directions);

            // notEnough = [];

            //选择第一个节点

            var firstMapObj = this._selectFirstObj(canSelectMapObjs);

            if (firstMapObj == false)

                break;

            //入栈

            storehouse[++s] = firstMapObj;

            //加入选择队列

            selectMapObj.push(firstMapObj);

            var lastMapObj = firstMapObj;

            for (var i = 0; i < maxSteps - 1; i++) {

                lastMapObj = this._selectNextObj(map.array, lastMapObj);

                if (lastMapObj != null) {

                    //入栈

                    storehouse[++s] = lastMapObj;

                    //加入选择队列

                    selectMapObj.push(lastMapObj);

                }

                else {

                    //栈内元素为空本次寻找结束  将当前记录的item数组添加到notEnough中

                    if (--s < 0) {

                        notEnough.push(selectMapObj);

                        break;

                    }

                    //栈不为空 栈顶元素出栈 新的栈顶元素继续寻找

                    lastMapObj = storehouse[s];

                    //本次未选到mapObj  i-- 保证总量达到maxSteps

                    i--;

                }

            }

            if (selectMapObj.length == maxSteps) {

                ret = selectMapObj;

                break;

            }

        } while (canSelectMapObjs.length > 0);

        if (ret.length == 0 && useStrict == true) {

            ret = this._findLongestArrayInNotEnough(notEnough);

        }

        this._recoverNotEnough(notEnough);

        return ret;

    },

    /**

    * 撤销本次查找的不足路线的canSelect

    * @param {*} notEnough

    */

    _recoverNotEnough(notEnough) {

        notEnough.forEach(notEnoughArray => {

            notEnoughArray.forEach(element => {

                element.canSelect = true;

            });

        });

    },

    /**

    * 添加方向

    * @param {*} mapArray

    * @param {*} directions

    */

    _addDirections(mapArray, directions) {

        mapArray.forEach(mapObjArray => {

            mapObjArray.forEach(mapIns => {

                mapIns.directions = directions.concat();

            });

        });

    },

    /**

    * 获得map中可以选择的所有mapObj

    * @param {*} mapArray

    */

    _getCanSelectObj(mapArray, start) {

        //获得可选择的obj

        var ret = [];

        if (start == null) {

            var numCols = mapArray.length;

            var numVols = mapArray[0].length;

            for (var i = 0; i < numCols; i++) {

                for (var j = 0; j < numVols; j++) {

                    var mapObj = mapArray[i][j];

                    if (mapObj.canSelect == true) {

                        ret.push(mapObj);

                    }

                }

            }

        }

        else {

            var mapObj = mapArray[start.col][start.vol];

            if (mapObj.canSelect == true) {

                ret.push(mapObj);

            }

        }

        return ret;

    },

    /**

    * 随机选择一个item为初始节点

    * @param {*} canSelectMapObjs

    */

    _selectFirstObj(canSelectMapObjs) {

        if (canSelectMapObjs.length == 0)

            return false;

        var firstMapObj = Utils.array.random(canSelectMapObjs, 1, true)[0];

        firstMapObj.canSelect = false;

        return firstMapObj;

    },

    /**

    * 根据lastMapObj中记录的directions选择下一个mapObj选择失败返回null

    * @param {*} mapArray

    * @param {*} lastMapObj

    */

    _selectNextObj(mapArray, lastMapObj) {

        for (var i = 0; i < lastMapObj.directions.length;) {

            //随机选择一个方向并且删除使用过的方向

            var d = Utils.array.random(lastMapObj.directions, 1, true)[0];

            var nextX = lastMapObj.position.x + d.x;

            var nextY = lastMapObj.position.y + d.y;

            //判断是否出界

            if (nextX >= 0 && nextY >= 0 && nextX < mapArray.length && nextY < mapArray[0].length) {

                var mapObj = mapArray[nextX][nextY];

                //判断是否可选

                if (mapObj.canSelect == true) {

                    mapObj.canSelect = false;

                    //删除来时的方向

                    this._removeDirection(mapObj.directions, { x: -d.x, y: -d.y });

                    return mapObj;

                }

            }

        }

        return null;

    },

    /**

    * 第一个参数是对象数组objArray

    * 第二个参数是自己定义的对象obj

    * 删除对象数组中属性值等于obj中的属性值的对象

    * 例如    objArray[{a:1,b:1,c:1},{a:1,b:2,c:1}]

    *        obj{b:1}

    * 函数运行完毕后objArray[{a:1,b:2,c:1}]

    * @param {*} directions

    * @param {*} dir

    */

    _removeDirection(directions, dir) {

        for (var i = 0; i < directions.length; i++) {

            var element = directions[i];

            var isRemove = true;

            for (var attr in dir) {

                if (element[attr] != dir[attr]) {

                    isRemove = false;

                    break;

                }

            }

            if (isRemove) {

                directions.splice(i, 1);

                return true;

            }

        }

    },

    /**

    * 返回notEnough数组中的最长的一项

    * @param {*} notEnough

    */

    _findLongestArrayInNotEnough(notEnough) {

        var ret = [];

        for (var i = 0; i < notEnough.length; i++) {

            if (notEnough[i].length > ret.length) {

                ret = notEnough[i];

            }

        }

        Utils.array.remove(notEnough, ret);

        return ret;

    },

    _mapObjArrayToItemArray(mapObjArray) {

        var ret = [];

        mapObjArray.forEach(element => {

            ret.push(element.item);

        });

        return ret;

    },

};

module.exports = MapUtils;

            

    

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容