Skip to content

Commit

Permalink
Initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
rosado committed Mar 14, 2010
0 parents commit bbce5e3
Show file tree
Hide file tree
Showing 6 changed files with 590 additions and 0 deletions.
348 changes: 348 additions & 0 deletions astar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,348 @@
/**
* @author rs
* http://www.haltingproblem.net/
*/

var MapCtr = null;

createDelegate =function(instance, method){
return function(){
method.apply(instance,arguments);
}
}

Array.indexOfElement = function(list, element){
if(list.length == 0){
return -1;
}

for(var i= 0; i < list.length; i++){
if(list[i] == element) { return i; }
}
return -1;
};

Array.contains = function(list, element){
if(Array.indexOfElement(list, element) == -1){
return false;
}
return true;
};
Array.remove = function(list, element){
var l = list.lentgth;
var newList = [];
for(var i = 0; i < l; i++){
if(list[i] == element){
continue;
}
newList.push(list[i]);
}
return newList;
};


// init stuff
$(document).ready(function(){
createMap(20, 10, 40);

$("#hiro").Draggable(
{
ghosting: false,
zIndex: 1000
}
);

$("#enemy").Draggable(
{
ghosting: false,
zIndex: 1000
}
);

$("#findBtn").click(function(){
// clear old path
clearPathOnMap();

var result = mapCtr.findPath();
if(!result) {
alert("I can't find a path.");
return;
}

$(mapCtr.get_pathElements()).addClass("pth");
});

$("#resetBtn").click(function(){
clearPathOnMap();
});

});

function clearPathOnMap(){
$("#container > div").removeClass("pth");
mapCtr.clear_all();
}

function createMap(width, height, tileSize){
if(tileSize == null) { tileSize = 60; }

mapCtr = new MapContainer(width, height, tileSize);
mapCtr.set_element($("#container")[0]);
mapCtr.initMap();
}

function resetPosition(elem){
$(elem).css("top", 0).css("left", 0);
}

function Tile(){
this._element = document.createElement("div");
this.prevStep = null;
this._obstacle = false;
this.g = null;
this.h = null;
this._coords = null;
}

Tile.prototype = {
// properties
get_element: function() { return this._element; },
set_map: function(m) {
this.Map = m;
var elem = this.get_element();
var cb = createDelegate(this, this.dropHandler)
$("#" + elem.id).Droppable(
{
accept: 'agent',
hoverclass: 'movePossible',
tolerance: 'intersect',
onDrop: cb
}
);
},
is_obstacle: function(){ return this._obstacle; },
set_obstacle: function(val) {
this._obstacle = val;
var elem = this.get_element();
if(val){
elem.style.backgroundColor = "black";
//$("#" + elem.id).DroppableDestroy();
} else {
elem.style.backgroundColor = "";
}
},
get_coords: function(){
return this._coords;
},
set_coords: function(coords){
this._coords = coords;
},

//methods
toggleObstacle: function(){
var newVal = !(this.is_obstacle());
this.set_obstacle(newVal);
},
dropHandler: function(drag) {
if(this.is_obstacle()){
resetPosition(drag);
return;
}
var coords = this.get_coords();
this.Map.injectObject(coords, drag);
return coords;
},
valueOf: function(){
return (this.g + this.h);
},
//priv
Map: null
};

function MapContainer(width, height, tileSize){
this._width = width;
this._height = height;
this._tileSize = tileSize;
this._element = null;
this._tiles = null;
this._pathStart = null;
this._pathEnd = null;
this._path = null;
}

MapContainer.prototype = {
// properties
get_dimensions: function() {
var dims = {};
dims.x = this._width;
dims.y = this._height;
return dims;
},
get_element: function() { return this._element; },
set_element: function(elem) {this._element = elem; },
set_pathStart: function(coords){ this._pathStart = coords; },
get_pathStart: function(){ return this._pathStart; },
set_pathEnd: function(coords) { this._pathEnd = coords; },
get_pathEnd: function() { return this._pathEnd; },

// methods
initMap: function(){
var elem = this.get_element();
elem.style.width = this._width * this._tileSize + "px";
elem.style.height = this._height * this._tileSize + "px";
this._buildMap();
},
_buildMap: function() {
var tiles = [];
var elem = this.get_element();
for(var row = 0; row < this._height; row++){
var rowArray = [];
for(var col = 0; col < this._width; col++){
var tile = new Tile();
var coords = {x: col, y: row};
tile.set_coords(coords);
var tileElem = tile.get_element();
tileElem.style.left = col * this._tileSize + "px";
tileElem.style.top = row * this._tileSize + "px";
//tileElem.innerHTML = "[" + col + "," + row + "]";
tileElem.id = "t_" + col + "_" + row;

var cb = createDelegate(tile, tile.toggleObstacle);
$(tileElem).click(cb);

elem.appendChild(tileElem);
tile.set_obstacle(false);
tile.set_map(this);
rowArray.push(tile);
}
tiles.push(rowArray);
}
this._tiles = tiles;
this._setTileStyles();
},
_setTileStyles: function() {
//$("#container > div").addClass("tile");
$("#container > div").css("width", this._tileSize);
$("#container > div").css("height", this._tileSize);
},
injectObject: function(coords, obj){
if(obj.id == "hiro"){
this.set_pathStart(coords);
} else if (obj.id == 'enemy') {
this.set_pathEnd(coords);
}

},
get_successors: function(coords){
var tiles = this._tiles;
var row = coords.y;
var col = coords.x;
var succ = [];

//left
if(col - 1 >= 0 ) {
succ.push(tiles[row][col - 1]);
}
//right
if(col + 1 < this._width){
succ.push(tiles[row][col + 1]);
}
//top
if(row - 1 >= 0){
succ.push(tiles[row - 1][col]);
}
//bottom
if(row + 1 < this._height){
succ.push(tiles[row + 1][col]);
}
return succ;
},
findPath: function(){
var cA = this.get_pathStart();
var cB = this.get_pathEnd();
if(cA == null || cB == null)
{
alert("endpoints not set");
return false;
}
// min-priority queue, hence less than
var openQ = new PriorityQueue();
openQ.cmp = function(a,b) {
return a < b;
};
var closedList = [];


var startNode = this._tiles[cA.y][cA.x];
startNode.prevStep = null;
startNode.g = 0;
startNode.h = this.d(cA, cB);
var endNode = this._tiles[cB.y][cB.x];
openQ.insert(startNode);

while(openQ.get_size() > 0){
var n = openQ.extract_extreme();
if( n == endNode){
this._path = this.constructPath(n);
return true;
}
var su = this.get_successors(n.get_coords());
for(var s = 0; s < su.length; s++){
var newg = n.g + this.dist(su[s], n);
if((Array.contains(closedList, su[s]) || Array.contains(openQ.nodes.slice(1,-1), su[s]) || su[s].is_obstacle())
&& su[s].g <= newg){
continue;
}
su[s].prevNode = n;
su[s].g = newg;
su[s].h = this.dist(su[s], endNode);
if(Array.contains(closedList, su[s])){
Array.remove(closedList, su[s]);
}
if(!Array.contains(openQ.nodes.slice(1,-1), su[s])){
openQ.insert(su[s]);
}
}
closedList.push(n);
}
return false;
},
dist: function(tileA, tileB) {
var a = tileA.get_coords();
var b = tileB.get_coords();
return this.d(a, b);
},
d: function(a, b){
var x = Math.abs(b.x - a.x);
var y = Math.abs(b.y - a.y);
return x + y;
},
constructPath: function(endNode){
var current = endNode;
var path = [];
while(current.prevNode != null){
path.push(current);
current = current.prevNode;
}
return path;
},
get_pathElements: function(){
var path = this._path;
var elems = [];
for(var e = 0; e < path.length; e++){
elems.push(path[e].get_element());
}
return elems;
},
clear_all: function() {
var tiles = this._tiles;
for(var row = 0; row < tiles.length; row++){
for(var col = 0; col < tiles[row].length; col++){
var tile = tiles[row][col];
tile.prevNode = null;
tile.g = null;
tile.h = null;
}
}
}
};
47 changes: 47 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>A* Algorithm in Javascript</title>
<link rel="stylesheet" type="text/css" href="style.css" media="screen" />
<script type="text/javascript" src="lib/jquery/jquery-compressed.js"></script>
<script type="text/javascript" src="lib/interface/interface.js"></script>
<script type="text/javascript" src="pqueue.js"></script>
<script type="text/javascript" src="astar.js"></script>
</head>
<body>
<h1>A* <span class="dn">(A Star)</span></h1>

<p>Drag the green and yellow squares somewhere on the blue area. Click on an empty space to toggle a wall on/off.
Then click "Find path". </p>

<p><em>Note for IE users</em>: The green and yellow square disappear after you drag them onto the board.
I tried to make it work with IE, but life's to short. Blame Microsoft.</p>

<div class="menu">
<div class="button" id="findBtn">Find path</div>
<div class="button" id="resetBtn">Clear path</div>
</div>

<div id="agentContainer">
<div id="hiro" class="agent"></div>
<div id="enemy" class="agent"></div>
</div>

<div id="container"></div>

<div id="footer">
Go to the <a href="http://www.haltingproblem.net">Front Page</a>.
</div>

<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4070081-1");
pageTracker._initData();
pageTracker._trackPageview();
</script>
</body>
</html>
Loading

0 comments on commit bbce5e3

Please sign in to comment.