JS-BFA演算法及ui實現

順炸天發表於2020-11-20

今天貼個程式碼Breadth-First-Algorithm

目錄

今天貼個程式碼Breadth-First-Algorithm

1. BFA實現演算法流程

2. Breadth-First-Algorithm.html - 演算法實現

3. generateMap.html - 生成地圖陣列

4. 截圖及相關功能說明


1. BFA實現演算法流程

 

 

2. 程式碼裡有什麼

  • html grid使用
  • canvas drawline
  • JS寫innerHtml
  • mouse down and up position - get

3. Breadth-First-Algorithm.html - 演算法實現

map由generateMap.html[見下]生成;

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>Bredth-First-Algorithm</title>
</head>

<body>
    <button type="button" onclick="run()">start</button>
    <button type="button" onclick="location.reload()">refresh</button>
    <a id="iteration"></a>
    <a id="panel" style="display: grid;"></a>

    <script type="text/javascript">
        const colors = ["black", "blue", "green", "red"];
        const pxSize = 20, lineWidth = 3;
        const timeStep = 100;
        // 0 - obstacle; 1 - blank; 2 - start; 3 - goal
        const start = 2, goal = 3, obstacle = 0, blank = 1;
        const map = [[2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1], [1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1], [1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1], [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1], [1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1], [1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1], [1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1], [1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1], [1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1], [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1], [1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1], [1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1], [1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0], [1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1], [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 3, 1, 1, 1, 0, 1, 1, 1, 1, 1]];
        const row = map.length;
        const col = map[0].length;
        document.getElementById("panel").style.width = pxSize * row + "px";
        document.getElementById("panel").style.height = pxSize * col + "px";
        document.getElementById("panel").style.gridTemplateColumns = (pxSize + "px ").repeat(row);
        document.getElementById("panel").style.gridTemplateRows = (pxSize + "px ").repeat(col);

        //add grid - num at row * col
        var addGridStringText = "<canvas id='myCanvas' width=" + pxSize * row + " height=" + pxSize * row + " style='position:absolute; solid #000000;'></canvas>" + "<canvas id='myCanvasForFinalPath' width=" + pxSize * row + " height=" + pxSize * row + " style='position:absolute; solid #000000;'></canvas>";
        for (i = 0; i < row; i++) {
            for (j = 0; j < col; j++)
                addGridStringText += "<div row='" + i + "' col='" + j + "' id='" + i + "_" + j + "' style=\"border:1px solid white; background-color:" + colors[map[i][j]] + "\"></div>";
        }
        document.getElementById("panel").innerHTML = addGridStringText;

        function drawLine(from, to) {
            var c = document.getElementById("myCanvas");
            var ctx = c.getContext("2d");
            ctx.lineWidth = lineWidth;
            ctx.moveTo(pxSize * (from.colAt + 0.5), pxSize * (from.rowAt + 0.5));
            ctx.lineTo(pxSize * (to.colAt + 0.5), pxSize * (to.rowAt + 0.5));
            ctx.stroke();
        }

        function drawFinalPathLine(from, to) {
            var c = document.getElementById("myCanvasForFinalPath");
            var ctx = c.getContext("2d");
            ctx.strokeStyle = '#cc0000';
            ctx.lineWidth = lineWidth;
            ctx.moveTo(pxSize * (from.colAt + 0.5), pxSize * (from.rowAt + 0.5));
            ctx.lineTo(pxSize * (to.colAt + 0.5), pxSize * (to.rowAt + 0.5));
            ctx.stroke();
        }

        function Node(parent, rowat, colat) {
            this.parent = parent;
            this.rowAt = rowat;
            this.colAt = colat;
            if (parent != null) drawLine(parent, this);

            this.getMapValue = function () {
                return map[rowAt][colAt];
            }

            //from left top and normal clock order
            this.addNeightBor = function () {
                rowAt = this.rowAt;
                colAt = this.colAt;
                rightTop = [rowAt - 1, colAt + 1];
                if (rightTop[0] >= 0 && rightTop[1] < col && map[rightTop[0]][rightTop[1]] != obstacle && notContain(rightTop)) { node = new Node(this, rightTop[0], rightTop[1]); openList.push(node); createdList.push(node); }
                right = [rowAt, colAt + 1];
                if (right[1] < col && map[right[0]][right[1]] != obstacle && notContain(right)) { node = new Node(this, right[0], right[1]); openList.push(node); createdList.push(node); }
                rightBott = [rowAt + 1, colAt + 1];
                if (rightBott[0] < row && rightBott[1] < col && map[rightBott[0]][rightBott[1]] != obstacle && notContain(rightBott)) { node = new Node(this, rightBott[0], rightBott[1]); openList.push(node); createdList.push(node); }
                bott = [rowAt + 1, colAt];
                if (bott[0] < row && map[bott[0]][bott[1]] != obstacle && notContain(bott)) { node = new Node(this, bott[0], bott[1]); openList.push(node); createdList.push(node); }
                leftBott = [rowAt + 1, colAt - 1];
                if (leftBott[0] < row && leftBott[1] >= 0 && map[leftBott[0]][leftBott[1]] != obstacle && notContain(leftBott)) { node = new Node(this, leftBott[0], leftBott[1]); openList.push(node); createdList.push(node); }
                left = [rowAt, colAt - 1];
                if (left[1] >= 0 && map[left[0]][left[1]] != obstacle && notContain(left)) { node = new Node(this, left[0], left[1]); openList.push(node); createdList.push(node); }
                leftTop = [rowAt - 1, colAt - 1];
                if (leftTop[0] >= 0 && leftTop[1] >= 0 && map[leftTop[0]][leftTop[1]] != obstacle && notContain(leftTop)) { node = new Node(this, leftTop[0], leftTop[1]); openList.push(node); createdList.push(node); }
                //can not use top, top is already defined as window class in JS
                topNode = [rowAt - 1, colAt];
                if (topNode[0] >= 0 && map[topNode[0]][topNode[1]] != obstacle && notContain(topNode)) { node = new Node(this, topNode[0], topNode[1]); openList.push(node); createdList.push(node); }
            }
        }

        function notContain(testNode) {
            for (i = 0; i < createdList.length; i++) {
                if (createdList[i].rowAt == testNode[0] && createdList[i].colAt == testNode[1]) return false;
            }
            return true;
        }

        var openList;
        var closedList;
        var createdList;
        var startNode;
        var currentNode;

        function run() {
            startNode = getStartNode();
            openList = [];
            openList.push(startNode);
            createdList = [];
            createdList.push(startNode);
            closedList = [];
            iterationTime = 0;
            //start iteration procedure
            if (openList.length != 0) {
                iteration();
            }
        }

        function iteration() {
            //after 2s, remove highlight of currentNode
            if (currentNode != null) document.getElementById(currentNode.rowAt + "_" + currentNode.colAt).style.border = "1px solid white";
            currentNode = openList.shift();
            //highlight currentNode
            document.getElementById(currentNode.rowAt + "_" + currentNode.colAt).style.border = "1px solid red";
            console.log("currentNode is(" + currentNode.rowAt + "," + currentNode.colAt + ")")
            currentNode.addNeightBor();
            closedList.push(currentNode);
            document.getElementById("iteration").innerText = "iteration:" + ++iterationTime;
            if (currentNode.getMapValue() == goal) {
                //find path through closedList
                var node = closedList[closedList.length - 1]; //goal node
                var path = new Array(node);
                while (node.getMapValue != startNode.getMapValue) {
                    node = node.parent;
                    path.unshift(node);
                }

                for (i = path.length - 1; i > 0; i--) {
                    drawFinalPathLine(path[i], path[i].parent);
                }


                return;
            } else setTimeout(() => {
                iteration();
            }, timeStep);

        }


        function getStartNode() {
            for (i = 0; i < row; i++) {
                for (j = 0; j < col; j++) {
                    if (map[i][j] == start)
                        return new Node(null, i, j);
                }
            }
        }

    </script>
</body>

</html>

4. generateMap.html - 生成地圖陣列

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>GenerateMapForBFS</title>
</head>

<body>
    <label>row_num:</label>
    <input type="text" id="num_row"><br>
    <label>col_num:</label>
    <input type="text" id="num_col"><br>
    <button type="button" onclick="setRowAndCol()">set</button>
    <button type="button" onclick="setColor(this)" style="background:black">curr_color</button>
    <button type="button" onclick="generate()">generate</button><br>
    <a id="panel" onmousedown="mousedown(event)" onmouseup="mouseup(event)" style="display:grid;"></a>
    <div id="resultField"></div>
    <script type="text/javascript">
        //default rowNum and colNum
        var rowNum = 10, colNum = 10;
        //showed grid size
        const pxSize = 20;
        var color = "black";

        function setRowAndCol() {
            var addGridStringText = "";
            if (document.getElementById("num_row").value != "")
                rowNum = parseInt(document.getElementById("num_row").value);
            if (document.getElementById("num_col").value != "")
                colNum = parseInt(document.getElementById("num_col").value);
            document.getElementById("panel").style.width = pxSize + "px";
            document.getElementById("panel").style.gridTemplateColumns = (pxSize + "px ").repeat(colNum);
            document.getElementById("panel").style.gridTemplateRows = (pxSize + "px ").repeat(rowNum);
            document.getElementById("panel").style.width = pxSize + "px";
            for (i = 0; i < rowNum; i++) {
                for (j = 0; j < colNum; j++)
                    addGridStringText += "<div  row='" + i + "' col='" + j + "' id='" + i + "_" + j + "' style=\"border:1px solid white; background-color: black; \"></div>";
            }
            document.getElementById("panel").innerHTML = addGridStringText;
        }

        function changeColor(div) {
            if (div.style.backgroundColor == "black") div.style.backgroundColor = "blue";
            else if (div.style.backgroundColor == "blue") div.style.backgroundColor = "green";
            else if (div.style.backgroundColor == "green") div.style.backgroundColor = "red";
            else if(div.style.backgroundColor == "red") div.style.backgroundColor = "black";
        }

        function generate() {
            var map = [];
            for (i = 0; i < rowNum; i++) {
                var tempMap = [];
                for (j = 0; j < colNum; j++) {
                    var element = document.getElementById(i + "_" + j);
                    if (element.style.backgroundColor == "black") tempMap.push(0);
                    else if(element.style.backgroundColor == "blue") tempMap.push(1);
                    else if(element.style.backgroundColor == "green") tempMap.push(2);
                    else if(element.style.backgroundColor == "red") tempMap.push(3);
                }
                map.push(tempMap);
            }
            result = "[";
            for (i = 0; i < rowNum; i++) {
                innerResult = "";
                for (j = 0; j < colNum; j++)
                    if (j != colNum - 1) innerResult += map[i][j] + ","; else innerResult += map[i][j];
                if (i != rowNum - 1) result += "[" + innerResult + "],";
                else result += "[" + innerResult + "]]"
            }
            document.getElementById("resultField").innerText = result;
        }

        //get mouse drag position and resopned draw color
        var mouseDownAt = [0, 0], mouseUpAt = [0, 0];
        function mousedown(e) {
            mouseDownAt[0] = Math.floor((e.pageY - document.getElementById("panel").offsetTop) / pxSize);
            mouseDownAt[1] = Math.floor((e.pageX - document.getElementById("panel").offsetLeft) / pxSize);
        }
        function mouseup(e) {
            mouseUpAt[0] = Math.floor((e.pageY - document.getElementById("panel").offsetTop) / pxSize);
            mouseUpAt[1] = Math.floor((e.pageX - document.getElementById("panel").offsetLeft) / pxSize);

            leftTopRow = mouseDownAt[0] < mouseUpAt[0] ? mouseDownAt[0] : mouseUpAt[0];
            leftTopCol = mouseDownAt[1] < mouseUpAt[1] ? mouseDownAt[1] : mouseUpAt[1];

            rightBottRow = mouseDownAt[0] > mouseUpAt[0] ? mouseDownAt[0] : mouseUpAt[0];
            rightBottCol = mouseDownAt[1] > mouseUpAt[1] ? mouseDownAt[1] : mouseUpAt[1];

            for (i = leftTopRow; i <= rightBottRow; i++) {
                for (j = leftTopCol; j <= rightBottCol; j++) {
                    document.getElementById(i + "_" + j).style.backgroundColor = color;
                }
            }
        }

        function setColor(button) {
            if (button.style.backgroundColor == "black") {
                button.style.backgroundColor = "blue";
                color = "blue";
            } else if(button.style.backgroundColor == "blue") {
                button.style.backgroundColor = "green";
                color = "green";
            }else if(button.style.backgroundColor == "green") {
                button.style.backgroundColor = "red";
                color = "red";
            } else if(button.style.backgroundColor == "red") {
                button.style.backgroundColor = "black";
                color = "black";
            }
        }
    </script>
</body>

</html>

5. 截圖及相關功能說明

  • 兩個canvas控制路徑連線和最終路徑連線。
  • 改變設定的pxSize等常量可控制相關引數如迭代時間,畫筆粗細,grid大小
  • 生成地圖可以拖動,用onmousedown和onmouseup獲得座標點來實現
  • 顏色對應點型別:start-green,red-goal,bule-path,black-obstacle
  • B站紀念視訊

相關文章