var agentSpeed = 10;
var WALL = 0;
var started = false;
var gridSize = 20;
var x = 0;
var y = 0;
var runsSameLine = false;
class Agent {
constructor(x, y, charge, cap, distance) {
this.x = x;
this.y = y;
this.charge = charge;
this.cap = cap;
this.distance = distance;
}
}
$(function() {
var $grid = $("#search_grid");
var opts = {
gridSize: gridSize
};
var grid = new GraphSearch($grid, opts, astar.search);
$("#btnInit").click(function() {
if (!started) {
var agent = new Agent(0, 0, 100, 50, 0);
agent.initialize();
started = true;
}
});
});
function GraphSearch($graph, options, implementation) {
this.$graph = $graph;
this.search = implementation;
this.opts = options;
this.initialize();
}
var grid;
GraphSearch.prototype.move = function($start, $end) {
var end = this.nodeFromElement($end);
if ($end.hasClass("wall")) {
return;
}
var start = this.nodeFromElement($start);
var path = this.search(this.graph.nodes, start, end, true);
if (!path || path.length == 0) {
} else {
return path;
}
};
GraphSearch.prototype.nodeFromElement = function($cell) {
return this.graph.nodes[parseInt($cell.attr("x"))][parseInt($cell.attr("y"))];
};
GraphSearch.prototype.initialize = function() {
this.grid = [];
var self = this,
nodes = [],
$graph = this.$graph;
$graph.empty();
var cellWidth = ($graph.width() / this.opts.gridSize) - 2,
cellHeight = ($graph.height() / this.opts.gridSize) - 2,
lineHeight = (this.opts.gridSize >= 30 ? "9.px" : ($graph.height() / this.opts.gridSize) - 10 + "px"),
fontSize = (this.opts.gridSize >= 30 ? "10px" : "20px");
$cellTemplate = $("<span />").addClass("grid_item").width(cellWidth).height(cellHeight).css("line-height", lineHeight).css("font-size", fontSize);
for (var x = 0; x < this.opts.gridSize; x++) {
var $row = $("<div class='row' />");
nodeRow = [],
gridRow = [];
for (var y = 0; y < this.opts.gridSize; y++) {
var id = "cell_" + x + "_" + y,
$cell = $cellTemplate.clone();
$cell.attr("id", id).attr("x", x).attr("y", y);
$row.append($cell);
gridRow.push($cell);
var isWall = addWall(x, y, this.opts.gridSize);
if (isWall === 1) {
$cell.addClass("wall");
nodeRow.push(1);
} else {
$cell.addClass('weight1');
nodeRow.push(0);
}
}
$graph.append($row);
this.grid.push(gridRow);
nodes.push(nodeRow);
if ($(window).width() < 700) {
$("#search_grid").css("width", "320px");
$("#main").css("width", "38%");
} else {
$("#search_grid").css("width", "300px");
$("#main").css("width", "20%");
}
}
this.graph = new Graph(nodes);
this.$cells = $graph.find(".grid_item");
grid = this;
};
addWall = function(x, y, size) {
var limitPointLeftUp = [2, 3];
var limitPointRightUp = [2, size - 4];
var limitPointLeftDown = [size - 4, 2];
var limitPointRightDown = [size - 4, size - 4];
if ((x == 2 && y == 2) || (x == 2 && y == size - 3)) {
return 1;
}
if ((x == size - 3 && y == 2) || (x == size - 3 && y == size - 3)) {
return 1;
}
if (x >= 2 && (y == 3 && x >= limitPointLeftUp[0] && x <= limitPointLeftDown[0] + 1)) {
return 1;
}
if (x >= 2 && (y == size - 4 && x >= limitPointRightUp[0] && x <= limitPointRightDown[0] + 1)) {
return 1;
}
if ((x == 1 && y == 5) || (x == 9 && y == 17) || (x == 6 && y == 0) || (x == 9 && y == 7) || (x == 15 && y == 0) || (x == 15 && y == 2) || (x == 18 && y == 15)) {
return 1;
}
}
Agent.prototype.initialize = function() {
var agent = this;
var goToLeft = false;
var goToRight = true;
var rightLimit = gridSize - 1;
var leftLimit = 0;
var lastPos = 0;
var path = [];
var completedPath = true;
timerId = setInterval(function() {
agent.x = x;
agent.y = y;
currentCell = $("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]");
currentCell.css("background-color", "#e2e2e2");
if (agent.x == gridSize - 1 && agent.y == 0) {
stopInterval(timerId);
return false;
}
if (goToRight && y == rightLimit) {
if (runsSameLine) {
goToLeft = true;
goToRight = false;
runsSameLine = false;
} else {
if (FreeCell((x + 1), y)) {
endCell = $("#search_grid .row .grid_item[x=" + (x + 1) + "][y=" + y + "]");
x++;
goToLeft = true;
goToRight = false;
} else {
endCell = FindNextFreeCell(x, y, "limDir");
goToLeft = true;
goToRight = false;
}
}
} else if (goToLeft && y == leftLimit) {
if (runsSameLine) {
goToLeft = false;
goToRight = true;
runsSameLine = false;
} else {
if (FreeCell((x + 1), y)) {
endCell = $("#search_grid .row .grid_item[x=" + (x + 1) + "][y=" + y + "]");
x++;
goToLeft = false;
goToRight = true;
} else {
endCell = FindNextFreeCell(x, y, "limEsq");
goToLeft = false;
goToRight = true;
}
}
} else if (goToRight) {
if (FreeCell(x, (y + 1))) {
endCell = $("#search_grid .row .grid_item[x=" + x + "][y=" + (y + 1) + "]");
y++;
} else {
endCell = FindNextFreeCell(x, y, "dir");
}
} else if (goToLeft) {
if (FreeCell(x, (y - 1))) {
endCell = $("#search_grid .row .grid_item[x=" + x + "][y=" + (y - 1) + "]");
y--;
} else {
endCell = FindNextFreeCell(x, y, "esq");
}
}
if (completedPath) {
path = grid.move(currentCell, endCell);
}
if (path) {
if (lastPos == path.length - 1) {
completedPath = true;
}
if (path.length > 1 && lastPos < path.length && lastPos != path.length - 1) {
x = path[lastPos].x;
y = path[lastPos].y;
lastPos++;
completedPath = false;
} else if (completedPath) {
x = path[lastPos].x;
y = path[lastPos].y;
lastPos = 0;
}
}
grid.$cells.removeClass("agent");
$("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]").addClass("agent");
}, agentSpeed);
var stopInterval = function() {
clearInterval(timerId);
};
};
FindNextFreeCell = function(x, y, dir) {
if (dir == "limDir") {
if (x != gridSize) {
for (var y = y; y >= 0; y--) {
if (FreeCell((x + 1), y)) {
return getCell((x + 1), y);
}
}
}
} else if (dir == "limEsq") {
if (x != gridSize) {
for (var y = y; y <= gridSize; y++) {
if (FreeCell((x + 1), y)) {
return getCell((x + 1), y);
}
}
}
} else if (dir == "dir") {
for (var y = y; y < gridSize - 1; y++) {
if (FreeCell(x, (y + 1))) {
return getCell(x, (y + 1));
}
}
for (var x = x; x <= gridSize - 1; x++) {
if (FreeCell((x + 1), y)) {
runsSameLine = true;
return getCell((x + 1), y);
}
}
} else if (dir == "esq") {
for (var y = y; y > 0; y--) {
if (FreeCell(x, (y - 1))) {
return getCell(x, (y - 1));
}
}
for (var x = x; x <= gridSize - 1; x++) {
if (FreeCell((x + 1), y)) {
runsSameLine = true;
return getCell((x + 1), y);
}
}
}
}
EmptySqm = function(x, y) {
var bNotWall = !$("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]").hasClass("wall");
return bNotWall;
}
getCell = function(x, y) {
return $("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]");
}
FreeCell = function(x, y) {
var bNaoTemParede = !$("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]").hasClass("wall");
var bNaoTemLixeira = !$("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]").hasClass("lixeira");
var bNaoTemRecarga = !$("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]").hasClass("pontoRecarga");
return bNaoTemParede && bNaoTemLixeira && bNaoTemRecarga;
}
ValidSqm = function(x, y) {
return ((x >= 0 && x < gridSize) && (y >= 0 && y < gridSize));
}
var astar = {
init: function(grid) {
for (var x = 0, xl = grid.length; x < xl; x++) {
for (var y = 0, yl = grid[x].length; y < yl; y++) {
var node = grid[x][y];
node.f = 0;
node.g = 0;
node.h = 0;
node.cost = node.type;
node.visited = false;
node.closed = false;
node.parent = null;
}
}
},
heap: function() {
return new BinaryHeap(function(node) {
return node.f;
});
},
search: function(grid, start, end, diagonal, heuristic) {
astar.init(grid);
heuristic = heuristic || astar.manhattan;
diagonal = !!diagonal;
var openHeap = astar.heap();
openHeap.push(start);
while (openHeap.size() > 0) {
var currentNode = openHeap.pop();
if (currentNode === end) {
var curr = currentNode;
var ret = [];
while (curr.parent) {
ret.push(curr);
curr = curr.parent;
}
return ret.reverse();
}
currentNode.closed = true;
var neighbors = astar.neighbors(grid, currentNode, diagonal);
for (var i = 0, il = neighbors.length; i < il; i++) {
var neighbor = neighbors[i];
if (neighbor.closed || neighbor.isWall() || $("#search_grid .row .grid_item[x=" + neighbor.x + "][y=" + neighbor.y + "]").hasClass("pontoRecarga") || $("#search_grid .row .grid_item[x=" + neighbor.x + "][y=" + neighbor.y + "]").hasClass("lixeira")) {
continue;
}
var gScore = currentNode.g + neighbor.cost;
var beenVisited = neighbor.visited;
if (!beenVisited || gScore < neighbor.g) {
neighbor.visited = true;
neighbor.parent = currentNode;
neighbor.h = neighbor.h || heuristic(neighbor.pos, end.pos);
neighbor.g = gScore;
neighbor.f = neighbor.g + neighbor.h;
if (!beenVisited) {
openHeap.push(neighbor);
} else {
openHeap.rescoreElement(neighbor);
}
}
}
}
return [];
},
manhattan: function(pos0, pos1) {
var d1 = Math.abs(pos1.x - pos0.x);
var d2 = Math.abs(pos1.y - pos0.y);
return d1 + d2;
},
neighbors: function(grid, node, diagonals) {
var ret = [];
var x = node.x;
var y = node.y;
if (grid[x - 1] && grid[x - 1][y]) {
ret.push(grid[x - 1][y]);
}
if (grid[x + 1] && grid[x + 1][y]) {
ret.push(grid[x + 1][y]);
}
if (grid[x] && grid[x][y - 1]) {
ret.push(grid[x][y - 1]);
}
if (grid[x] && grid[x][y + 1]) {
ret.push(grid[x][y + 1]);
}
if (diagonals) {
if (grid[x - 1] && grid[x - 1][y - 1]) {
ret.push(grid[x - 1][y - 1]);
}
if (grid[x + 1] && grid[x + 1][y - 1]) {
ret.push(grid[x + 1][y - 1]);
}
if (grid[x - 1] && grid[x - 1][y + 1]) {
ret.push(grid[x - 1][y + 1]);
}
if (grid[x + 1] && grid[x + 1][y + 1]) {
ret.push(grid[x + 1][y + 1]);
}
}
return ret;
}
};
var GraphNodeType = {
OPEN: 0,
WALL: 1
};
function Graph(grid) {
var nodes = [];
for (var x = 0; x < grid.length; x++) {
nodes[x] = [];
for (var y = 0, row = grid[x]; y < row.length; y++) {
nodes[x][y] = new GraphNode(x, y, row[y]);
}
}
this.input = grid;
this.nodes = nodes;
}
Graph.prototype.toString = function() {
var graphString = "\n";
var nodes = this.nodes;
var rowDebug, row, y, l;
for (var x = 0, len = nodes.length; x < len; x++) {
rowDebug = "";
row = nodes[x];
for (y = 0, l = row.length; y < l; y++) {
rowDebug += row[y].type + " ";
}
graphString = graphString + rowDebug + "\n";
}
return graphString;
};
function GraphNode(x, y, type) {
this.data = {};
this.x = x;
this.y = y;
this.pos = {
x: x,
y: y
};
this.type = type;
}
GraphNode.prototype.toString = function() {
return "[" + this.x + " " + this.y + "]";
};
GraphNode.prototype.isWall = function() {
return this.type === GraphNodeType.WALL;
};
function BinaryHeap(scoreFunction) {
this.content = [];
this.scoreFunction = scoreFunction;
}
BinaryHeap.prototype = {
push: function(element) {
this.content.push(element);
this.sinkDown(this.content.length - 1);
},
pop: function() {
var result = this.content[0];
var end = this.content.pop();
if (this.content.length > 0) {
this.content[0] = end;
this.bubbleUp(0);
}
return result;
},
remove: function(node) {
var i = this.content.indexOf(node);
var end = this.content.pop();
if (i !== this.content.length - 1) {
this.content[i] = end;
if (this.scoreFunction(end) < this.scoreFunction(node)) {
this.sinkDown(i);
} else {
this.bubbleUp(i);
}
}
},
size: function() {
return this.content.length;
},
rescoreElement: function(node) {
this.sinkDown(this.content.indexOf(node));
},
sinkDown: function(n) {
var element = this.content[n];
while (n > 0) {
var parentN = ((n + 1) >> 1) - 1,
parent = this.content[parentN];
if (this.scoreFunction(element) < this.scoreFunction(parent)) {
this.content[parentN] = element;
this.content[n] = parent;
n = parentN;
}
else {
break;
}
}
},
bubbleUp: function(n) {
var length = this.content.length,
element = this.content[n],
elemScore = this.scoreFunction(element);
while (true) {
var child2N = (n + 1) << 1,
child1N = child2N - 1;
var swap = null;
var child1Score;
if (child1N < length) {
var child1 = this.content[child1N];
child1Score = this.scoreFunction(child1);
if (child1Score < elemScore) {
swap = child1N;
}
}
if (child2N < length) {
var child2 = this.content[child2N],
child2Score = this.scoreFunction(child2);
if (child2Score < (swap === null ? elemScore : child1Score)) {
swap = child2N;
}
}
if (swap !== null) {
this.content[n] = this.content[swap];
this.content[swap] = element;
n = swap;
}
else {
break;
}
}
}
};
html,
body {
height: 100%;
margin: 0;
}
.buttons {
float: right;
position: relative;
right: 10px;
top: 10px;
}
.buttons a {
text-decoration: none;
}
#content {
margin: 0 auto;
width: 98%;
text-align: center;
}
#controls {
text-align: center;
margin-bottom: 25px;
padding: 5px;
}
#search_grid {
width: 300px;
height: 300px;
position: relative;
}
#main {
margin: auto;
width: 20%;
}
.grid_item {
display: block;
border: 1px solid #bbb;
float: left;
line-height: 12px;
font-size: 10px;
}
.grid_item.wall {
background-color: #000000;
}
.grid_item.weight1 {
background-color: #ffffff;
}
.agent {
text-align: center;
color: grey;
font-size: 20px;
background-color: red !important;
color: blue;
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="content">
<input type="button" id="btnInit" value="Start" /><br><br>
<div id="main">
<div id="search_grid">Loading...</div>
</div>
</div>
<div id="footer"></div>
</body>