NAV 網格導航尋路

<html><head><title>nav_mesh_path_finding</title>

</head><body><div>
    <canvas id="canvas" width="1300" height="550" style="border: solid black 1px; cursor: default;"></canvas>
</div>
<div>
    <input type="button" value="clear" onclick="demo.clear();">
    <input type="button" value="load from string" onclick="demo.load();">
    <input type="button" value="generate" onclick="demo.generate(true);">
    <input type="button" value="generate obstacles" onclick="demo.generateObstacles();">
    <!-- <input type="button" value="generate maze" onclick="demo.clear();"> -->
    <input type="button" value="save path" onclick="demo.savePath();">
</div>
<br/>
<textarea id="txtArea" style="margin: 0px; width: 1200px; height: 33px;"></textarea>
<br/>

<script type="text/javascript" src="MathUtil.js"></script>
<script type="text/javascript">
Triangle.prototype.drawHere = function(ctx) {
    var v1 = this.v1, v2 = this.v2, v3 = this.v3;
    if (this.isValid) {
        if (this.isObstacle) {
            ctx.beginPath();
            ctx.moveTo(v1.x, v1.y);
            ctx.lineTo(v2.x, v2.y);
            ctx.lineTo(v3.x, v3.y);
            ctx.lineTo(v1.x, v1.y);
            ctx.closePath();
            ctx.fillStyle = "#636363";
            ctx.fill();
        } else {
            ctx.beginPath();
            ctx.moveTo(v1.x, v1.y);
            ctx.lineTo(v2.x, v2.y);
            ctx.lineTo(v3.x, v3.y);
            ctx.lineTo(v1.x, v1.y);
            ctx.closePath();
            ctx.strokeStyle = "#ff0000";
            ctx.stroke();
            // ctx.fillStyle = "#ffffff";
            // ctx.fill();
        }
    } else {
        ctx.beginPath();
        ctx.moveTo(v1.x, v1.y);
        ctx.lineTo(v2.x, v2.y);
        ctx.lineTo(v3.x, v3.y);
        ctx.lineTo(v1.x, v1.y);
        ctx.closePath();
        ctx.fillStyle = "#a3a3a3";
        ctx.fill();
    }
    // this.drawIndex(ctx);
}

var demo = new Demo();
var config = new Config();
function Config() {
    this.MIN_DISTANCE = 30;
    this.TOTAL_VERTICES = 200;
    this.OBSTACLE_PERCENT = 25;
}

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

function Demo() {
    var vertices = [];
    var obstacleFlags = [];
    var allTriangles;
    function initRandomVertices() {
        if (vertices.length == 0) {
            var first = createRandomPoint();
            first.id = 0;
            vertices.push(first);
        }
        for (var i = 1; i < config.TOTAL_VERTICES; i++) {
            var p = createProperRandomPoint();
            if (p != null) {
                p.id = i;
                vertices.push(p);
            } else {
                break;
            }
        }
        console.log("expected: " + config.TOTAL_VERTICES + ", actually: " + vertices.length);
    }

    function createProperRandomPoint() {
        for (var i = 0; i < 255; i++) {
            var v = createRandomPoint();
            var closestDistance = getClosestPoint(v, vertices, []).distance;
            if (closestDistance >= config.MIN_DISTANCE) {
                return v;
            }
        }
        return null;
    }

    function createRandomPoint() {
        return new Vertex(getRandom(50, canvas.width-50), getRandom(50, canvas.height-50));
    }

    function getTriangleByPoint(p) {
        if (allTriangles != null && allTriangles.length > 0) {
            for (var i = 0; i < allTriangles.length; i++) {
                if (allTriangles[i].contains(p)) {
                    return allTriangles[i];
                }
            }
        }
        return null;
    }

    var srcTriangle = null;
    var dstTriangle = null;
    var p1, p2;
    this.addAt = function(x, y) {
        if (srcTriangle != null && dstTriangle != null) {
            srcTriangle = dstTriangle = null;
            p1 = p2 = null;
            //clear path
            context.clearRect(0, 0, Number(canvas.width), Number(canvas.height));
            this.render();
        }

        var p = new Vertex(x, y);
        p.draw(context);
        if (srcTriangle == null) {
            srcTriangle = getTriangleByPoint(p);
            p1 = p;
        } else {
            dstTriangle = getTriangleByPoint(p);
            p2 = p;
            var neighbor = getPathOfTriangles(srcTriangle, dstTriangle);
            drawPathEdgeNodes(neighbor, context);
            drawSimplifyPath(context, neighbor, p1, p2);
        }
    }

    var isLoading = false;
    this.load = function() {
        this.clearVertices();
        isLoading = true;
        var txt = document.getElementById("txtArea").value;
        if (txt == null || txt.length < 12) {
            return;
        }
        var arr = txt.split("<>");
        var points = arr[0].split(",");
        for (var i=0; i<points.length; i++) {
            var p = new Vertex(0, 0);
            p.init(points[i]);
            p.id = i;
            vertices.push(p);
        }
        if (arr.length > 1) {
            var obstaclesStr = arr[1];
            obstacleFlags = obstaclesStr.split(",");
        }
        if (obstacleFlags.length > 0) {
            this.generateObstacles();
        } else if (vertices.length > 0) {
            this.generate(true);
        }
        if (arr.length > 2) {
            var twoPoints = arr[2].split(",");
            p1 = new Vertex(0, 0);
            p2 = new Vertex(0, 0);
            p1.init(twoPoints[0]);
            p2.init(twoPoints[1]);
            p1.draw(context);
            p2.draw(context);
            srcTriangle = getTriangleByPoint(p1);
            dstTriangle = getTriangleByPoint(p2);
            var neighbor = getPathOfTriangles(srcTriangle, dstTriangle);
            drawPathEdgeNodes(neighbor, context);
            drawSimplifyPath(context, neighbor, p1, p2);
        }
        isLoading = false;
    }

    this.generate = function(needDraw) {
        if (!isLoading) {
            this.clear();
            initRandomVertices();
            document.getElementById("txtArea").value = vertices.toString();
        }
        allTriangles = getAllDelaunayTriangles(vertices);
        initNeighborhood(allTriangles);
        if (needDraw) {
            this.render();
        }
    }

    this.generateObstacles = function() {
        context.clearRect(0, 0, Number(canvas.width), Number(canvas.height));
        if (!isLoading) {
            if (vertices.length == 0) {
                this.generate(false);
            }
            obstacleFlags=[];
            for (var i = 0; i < allTriangles.length; i++) {
                if (!allTriangles[i].isValid) {
                    obstacleFlags.push(2);
                    continue;
                }
                var factor = parseInt(getRandom(0, 101));
                if (factor <= config.OBSTACLE_PERCENT) {
                    allTriangles[i].isObstacle = true;
                    obstacleFlags.push(1);
                } else {
                    allTriangles[i].isObstacle = false;
                    obstacleFlags.push(0);
                }
            }
            document.getElementById("txtArea").value = vertices.toString() + "<>" + obstacleFlags.toString();
        } else {
            allTriangles = getAllDelaunayTriangles(vertices);
            initNeighborhood(allTriangles);
            for (var i = 0; i < allTriangles.length; i++) {
                if (!allTriangles[i].isValid) {
                    continue;
                }
                allTriangles[i].isObstacle = obstacleFlags[i] == 1 ? true : false;
            }
        }

        this.render();
    }

    this.savePath = function() {
        var startEnd = [p1.toString(), p2.toString()];
        document.getElementById("txtArea").value = vertices.toString() + "<>" + obstacleFlags.toString() + "<>" + startEnd.toString();
    }

    this.clear = function() {
        this.clearVertices();
        document.getElementById("txtArea").value = "";
    }

    this.clearVertices = function() {
        clearNeighborhood();
        vertices = [];
        obstacleFlags = [];
        context.clearRect(0, 0, Number(canvas.width), Number(canvas.height));
    }

// function render() {
    this.render = function() {
        if (!canvas.getContext)
            return;
        if (vertices.length <= 0) {
            return;
        }

        var validCount = 0;
        for (var i = 0; i < allTriangles.length; i++) {
            allTriangles[i].drawHere(context);
            if (allTriangles[i].isValid) {
                validCount++;
            }
        }
        console.log("delaunay triangle total: " + allTriangles.length + ", validCount:" + validCount);

        vertices.forEach(function(vertex) {
            vertex.draw(context);
            // context.font = "23px Courier New";
            // context.fillStyle = "orange";
            // context.fillText(vertex.id, vertex.x, vertex.y);
        });


    }
  };

var EPSILON = Number.EPSILON || Math.pow(2,-52);
function getAllDelaunayTriangles(vertices) {
        var n, i, j, indices, buffer, open, closed, current, dx, dy;

        if ((n = vertices.length) < 3) return [];

        vertices = vertices.concat();

        //create a array base vertices's key
        for (i = n, indices = new Array(n); i--;) indices[i] = i;
        //sort the indices base on the x coordinate of vertices
        //from bigger to smaller
        indices.sort(function (a, b) {
            return vertices[b].x - vertices[a].x;
        });


        //create a supertriangle to buffer and push the vertex to vertices Array
        buffer = supertriangle(vertices);
        vertices.push(buffer[0], buffer[1], buffer[2]);


        //attach supertriangle to open for a start triangle
        open = [circumcircle(vertices, n + 0, n + 1, n + 2)];
        closed = [];
        buffer = [];

        //starting loop from smallest x of vertex
        for (i = indices.length; i--; buffer.length = 0) {
            current = indices[i];

            for (j = open.length; j--;) {

                dx = vertices[current].x - open[j].x;
                if (dx > 0 && dx * dx > open[j].r) {
                    closed.push(open[j]);
                    open.splice(j, 1);
                    continue;
                }

                //skip this point if it is outside this circumcircle
                dy = vertices[current].y - open[j].y;
                if (dx * dx + dy * dy - open[j].r > EPSILON) continue;


                //add edges of this triangle to buffer
                buffer.push(open[j].i, open[j].j, open[j].j, open[j].k, open[j].k, open[j].i);
                open.splice(j, 1);
            }

            dedup(buffer);

            for (j = buffer.length; j;) {
                var obj = circumcircle(vertices, buffer[--j], buffer[--j], current);
                if (obj.r != Infinity) {
                    open.push(obj);
                }
            }
        }

        for (i = open.length; i--;)
            closed.push(open[i]);

        var ret = [];
        for (open.length = 0, i = closed.length; i--;) {
            if (closed[i].i < n && closed[i].j < n && closed[i].k < n) {//deleting triangles that are relative to supertriangle
                var point = closed[i];
                open.push(closed[i].i, closed[i].j, closed[i].k);
                var t = new Triangle(vertices[point.i], vertices[point.j], vertices[point.k]);
                t.index = ret.length;
                ret.push(t);
                saveToPointAndEdgeMap(t);
            }
        }
        // return open;
        return ret;
    }

    function supertriangle(v) {
        var xmin = Number.POSITIVE_INFINITY, ymin = xmin, ymax = 0, xmax = 0, i, xl, yl, xlh;
        for (i = v.length; i--;) v[i].x < xmin && (xmin = v[i].x), v[i].x > xmax && (xmax = v[i].x), v[i].y < ymin && (ymin = v[i].y), v[i].y > ymax && (ymax = v[i].y);
        xl = xmax - xmin;
        yl = ymax - ymin;
        xlh = xl / 2;

        return [
            new Vertex(xmin - xlh - 2, ymax + 1),     //the left vertex's coordinate
            new Vertex(xmin + xlh, ymin - yl),        //the top vertex's coordinate
            new Vertex(xmax + xlh + 2, ymax + 1)      //teh right vertex's coordinate
        ];
    }

    //multiplex the circumcircle algorithm
    function circumcircle(v, i, j, k) {
        var x1 = v[i].x,
            y1 = v[i].y,
            x2 = v[j].x,
            y2 = v[j].y,
            x3 = v[k].x,
            y3 = v[k].y,
            fabsy1y2 = Math.abs(y1 - y2),
            fabsy2y3 = Math.abs(y2 - y3),
            xc, yc, m1, m2, mx1, mx2, my1, my2, dx, dy;

        if (fabsy1y2 < EPSILON) {
            m2 = -((x3 - x2) / (y3 - y2));
            mx2 = (x2 + x3) / 2.0;
            my2 = (y2 + y3) / 2.0;
            xc = (x2 + x1) / 2.0;
            yc = m2 * (xc - mx2) + my2;
        }

        else if (fabsy2y3 < EPSILON) {
            m1 = -((x2 - x1) / (y2 - y1));
            mx1 = (x1 + x2) / 2.0;
            my1 = (y1 + y2) / 2.0;
            xc = (x3 + x2) / 2.0;
            yc = m1 * (xc - mx1) + my1;
        }

        else {
            m1 = -((x2 - x1) / (y2 - y1));
            m2 = -((x3 - x2) / (y3 - y2));
            mx1 = (x1 + x2) / 2.0;
            mx2 = (x2 + x3) / 2.0;
            my1 = (y1 + y2) / 2.0;
            my2 = (y2 + y3) / 2.0;
            xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
            yc = (fabsy1y2 > fabsy2y3) ?
            m1 * (xc - mx1) + my1 :
            m2 * (xc - mx2) + my2;
        }

        dx = x2 - xc;
        dy = y2 - yc;
        return {i: i, j: j, k: k, x: xc, y: yc, r: dx * dx + dy * dy};
    }

    function dedup(edges) {
        var i, j, a, b, m, n;

        for (j = edges.length; j;) {
            b = edges[--j];
            a = edges[--j];

            for (i = j; i;) {
                n = edges[--i];
                m = edges[--i];

                if ((a === m && b === n) || (a === n && b === m)) {
                    edges.splice(j, 2);
                    edges.splice(i, 2);
                    break;
                }
            }
        }
    }

function saveTriangleToPointMap(triangle, key) {
    if (point_triangles[key] == null) {
        point_triangles[key] = [triangle];
    } else {
        point_triangles[key].push(triangle);
    }
}

function saveTriangleToEdgeMap(triangle, str1, str2) {
    if (edge_triangles[str1 + str2] != null) {
        edge_triangles[str1 + str2].push(triangle);
    } else if (edge_triangles[str2 + str1] != null) {
        edge_triangles[str2 + str1].push(triangle);
    } else {
        edge_triangles[str1 + str2] = [triangle];
    }
}

function contains(p, arr) {
    for (var i=0; i<arr.length; i++) {
        if (arr[i].x == p.x && arr[i].y == p.y) {
            return true;
        }
    }
    return false;
}

function getClosestPoint(p, array, uncheckArr) {
    var arr = subtract(array, uncheckArr);
    var min = -1;
    var v;
    for (var i=0; i<arr.length; i++) {
        var distance = getDistanceSquare(p, arr[i]);
        if (min < 0) {
            min = distance;
            v = arr[i];
        } else if (min > distance) {
            min = distance;
            v = arr[i];
        }
    }
    v.distance = Math.sqrt(min);
    return v;
}

var point_triangles = [];
var edge_triangles = [];
var triangle_neighbor = [];

function saveToPointAndEdgeMap(triangle) {
    var str1 = triangle.v1.toString();
    var str2 = triangle.v2.toString();
    var str3 = triangle.v3.toString();
    saveTriangleToPointMap(triangle, str1);
    saveTriangleToPointMap(triangle, str2);
    saveTriangleToPointMap(triangle, str3);
    saveTriangleToEdgeMap(triangle, str1, str2);
    saveTriangleToEdgeMap(triangle, str2, str3);
    saveTriangleToEdgeMap(triangle, str3, str1);
}

function initNeighborhood(allTriangles) {
    for (var i = 0; i < allTriangles.length; i++) {
        var triangle = allTriangles[i];
        var neighbor = new Neighbor(triangle);
        triangle_neighbor[triangle.toString()] = neighbor;
        var edge12_triangles = subtract(getTrianglesByEdge(triangle.v1, triangle.v2), [triangle]);
        var edge23_triangles = subtract(getTrianglesByEdge(triangle.v2, triangle.v3), [triangle]);
        var edge31_triangles = subtract(getTrianglesByEdge(triangle.v3, triangle.v1), [triangle]);
        var v1_triangles = subtract(point_triangles[triangle.v1.toString()], [triangle].concat(edge12_triangles).concat(edge31_triangles));
        var v2_triangles = subtract(point_triangles[triangle.v2.toString()], [triangle].concat(edge12_triangles).concat(edge23_triangles));
        var v3_triangles = subtract(point_triangles[triangle.v3.toString()], [triangle].concat(edge23_triangles).concat(edge31_triangles));
        neighbor.edgeNeighbors = edge12_triangles.concat(edge23_triangles).concat(edge31_triangles);
        neighbor.pointNeighbors = v1_triangles.concat(v2_triangles).concat(v3_triangles);
        if (edge12_triangles.length > 0) neighbor.triangle_edge[edge12_triangles[0].toString()] = [triangle.v1, triangle.v2];
        if (edge23_triangles.length > 0) neighbor.triangle_edge[edge23_triangles[0].toString()] = [triangle.v2, triangle.v3];
        if (edge31_triangles.length > 0) neighbor.triangle_edge[edge31_triangles[0].toString()] = [triangle.v3, triangle.v1];
    }
}

function clearNeighborhood() {
    point_triangles = [];
    edge_triangles = [];
    triangle_neighbor = [];
}

function getPathOfTriangles(srcTriangle, dstTriangle) {
    var start = triangle_neighbor[srcTriangle.toString()];
    start.parentNeighbor = "none";
    if (start.me.equals(dstTriangle)) {
        return start;
    }
    var openedItems = start.getPassableNeighbors();
    var closedNeighbors = [start];
    for (var i = 0; i < openedItems.length; i++) {
        openedItems[i].parentNeighbor = start;
    }
    while (openedItems.length > 0) {
        var neighbor = openedItems.shift();
        if (neighbor.me.equals(dstTriangle)) {
            return neighbor;
        }
        var newComingItems = neighbor.getPassableNeighbors();
        for (var i = 0; i < newComingItems.length; i++) {
            // if (newComingItems[i].parentNeighbor == null) {
            //  newComingItems[i].parentNeighbor = neighbor;
            //  openedItems.push(newComingItems[i]);
            // }
            var newItem = newComingItems[i];
            if (!hasNeighbor(openedItems, newItem) && !hasNeighbor(closedNeighbors, newItem)) {
                newItem.parentNeighbor = neighbor;
                openedItems.push(newItem);
            }
        }
        closedNeighbors.push(neighbor);
    }
    return null;
}

function hasNeighbor(arr, neighbor) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i].equals(neighbor)) {
            return true;
        }
    }
    return false;
}

function drawPathEdgeNodes(neighbor, ctx) {
    if (neighbor == null) {
        alert("cannot reach.");
        return;
    }
    var desc = [];
    var child = neighbor;
    while (child.parentNeighbor != null) {
        desc.push(child.index);
        var node = child.parentNeighbor;
        if (node == "none") {
            child.me.v1.emphasizeDraw(ctx);
            child.me.v2.emphasizeDraw(ctx);
            child.me.v3.emphasizeDraw(ctx);
            console.log("path: " + desc.toString());
            return;
        }
        var edgeNodes = child.triangle_edge[node.me.toString()];
        if (edgeNodes != null) {
            edgeNodes[0].emphasizeDraw(ctx);
            edgeNodes[1].emphasizeDraw(ctx);
        }
        child = child.parentNeighbor;
    }
}

function getEdgesFromNeighbor(neighbor) {
    if (neighbor == null) {
        return [];
    }
    var child = neighbor;
    var neighbors = [];
    while (child.parentNeighbor != null) {
        neighbors.push(child);
        if (child.parentNeighbor == "none") {
            break;
        }
        child = child.parentNeighbor;
    }
    var edges = [];
    for (var i= 0; i < neighbors.length - 1; i++) {
        edges.push(neighbors[i].triangle_edge[neighbors[i+1].me.toString()]);
    }
    return edges.reverse();
}

function drawSimplifyPath(ctx, neighbor, start, end) {
    ctx.moveTo(start.x, start.y);
    var edges = getEdgesFromNeighbor(neighbor);
    var points = findSimplifyPath(start, end, edges);
    // ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);
    for (var i = 1; i < points.length; i++) {
        ctx.lineTo(points[i].x, points[i].y);
    }
    // ctx.closePath();
    ctx.strokeStyle = "#00ffaa";
    ctx.lineWidth = 3;
    ctx.stroke();
    ctx.lineWidth = 1; 
}

function Neighbor(triangle) {
    this.index = triangle.index;
    this.me = triangle;
    this.triangle_edge = [];
    this.pointNeighbors = [];
    this.edgeNeighbors = [];
    this.getAllNeighbors = function() {
        return this.edgeNeighbors.concat(this.pointNeighbors);
    }
    this.getPassableNeighbors = function () {
        var ret = [];
        for (var i = 0; i < this.edgeNeighbors.length; i++) {
            if (this.edgeNeighbors[i].isValid && !this.edgeNeighbors[i].isObstacle) {
                ret.push(triangle_neighbor[this.edgeNeighbors[i].toString()]);
            }
        }
        return ret;
    }

    this.equals = function(other) {
        if (other == null)
            return false;
        if (other.index == this.index) {
            return true;
        } else {
            return false;
        }
    }
}

function getTrianglesByEdge(v1, v2) {
    var str1 = v1.toString();
    var str2 = v2.toString();
    if (edge_triangles[str1 + str2] != null) {
        return edge_triangles[str1 + str2];
    } else if (edge_triangles[str2 + str1] != null) {
        return edge_triangles[str2 + str1];
    }
    return [];
}

window.onload = function() {
    document.getElementById("canvas").onclick = function(e) {
        e = e ? e : window.event;
        var rect = this.getBoundingClientRect();
        demo.addAt(e.clientX - rect.left, e.clientY - rect.top);
    }
};

</script>

</body></html>

(406 | 340),(1144 | 105),(1165 | 353),(361 | 244),(927 | 185),(331 | 326),(906 | 475),(240 | 497),(115 | 213),(173 | 333),(373 | 476),(960 | 231),(1060 | 392),(577 | 61),(1115 | 73),(1125 | 422),(722 | 472),(783 | 491),(798 | 153),(698 | 373),(1141 | 201),(509 | 50),(695 | 112),(359 | 174),(99 | 392),(1176 | 70),(243 | 437),(432 | 291),(1207 | 50),(373 | 286),(1190 | 486),(1170 | 168),(452 | 485),(232 | 387),(1082 | 159),(1061 | 455),(104 | 331),(69 | 57),(845 | 69),(55 | 307),(624 | 285),(425 | 216),(414 | 166),(592 | 312),(1114 | 218),(763 | 250),(359 | 52),(971 | 314),(418 | 94),(1194 | 263),(722 | 281),(1034 | 62),(945 | 469),(485 | 145),(849 | 160),(233 | 311),(1081 | 363),(679 | 74),(865 | 332),(1196 | 301),(842 | 239),(273 | 198),(659 | 51),(750 | 72),(1049 | 160),(587 | 454),(993 | 183),(169 | 488),(923 | 284),(1244 | 186),(719 | 138),(616 | 339),(1042 | 248),(265 | 234),(1156 | 319),(349 | 144),(1208 | 442),(583 | 384),(139 | 172),(416 | 485),(909 | 211),(905 | 347),(606 | 132),(242 | 273),(1160 | 271),(73 | 177),(574 | 343),(1198 | 332),(570 | 283),(504 | 483),(896 | 153),(832 | 460),(681 | 477),(364 | 96),(94 | 462),(651 | 472),(973 | 403),(948 | 424),(907 | 425),(634 | 445),(874 | 483),(115 | 73),(1123 | 157),(1178 | 436),(311 | 261),(199 | 309),(719 | 324),(523 | 328),(754 | 165),(329 | 488),(208 | 120),(889 | 280),(1209 | 144),(507 | 409),(1195 | 363),(124 | 427),(1038 | 191),(891 | 59),(1118 | 473),(437 | 139),(766 | 381),(1241 | 328),(148 | 294),(1101 | 288),(890 | 383),(528 | 254),(751 | 335),(279 | 51),(166 | 158),(779 | 410),(767 | 292),(991 | 268),(885 | 242),(555 | 464),(656 | 368),(668 | 156),(336 | 219),(714 | 435),(237 | 105),(903 | 119),(575 | 124),(744 | 439),(300 | 165),(894 | 311),(554 | 172),(868 | 188),(1112 | 124),(647 | 263),(975 | 81),(1223 | 398),(955 | 123),(286 | 463),(263 | 412),(606 | 240),(1231 | 286),(155 | 63),(154 | 407),(1078 | 235),(279 | 330),(1000 | 153),(245 | 152),(78 | 249),(91 | 143),(182 | 374),(297 | 117),(1047 | 326),(1017 | 415),(66 | 424),(269 | 372),(970 | 492),(307 | 61),(1169 | 406),(966 | 362),(478 | 323),(575 | 205),(475 | 252),(333 | 396),(1063 | 424),(333 | 84),(378 | 377),(812 | 322),(632 | 208),(1175 | 202),(787 | 348),(429 | 362),(270 | 84),(1029 | 498),(419 | 395),(85 | 301),(769 | 106),(393 | 446),(689 | 293),(1055 | 287),(1142 | 451),(665 | 118),(329 | 175),(517 | 190),(191 | 273),(1071 | 90),(828 | 212)<>2,2,2,2,2,2,2,2,1,1,0,0,0,1,1,1,0,0,1,2,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1,0,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
1,0,2,1,0,0,0,0,1,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,0,0,0,1,0,1,1,0,1,1,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,1,
0,0,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,1,1,1,1,0,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,
0,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,1,0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章