require("./../rur.js");
require("./../world_api/is_fatal.js");
require("./../world_api/walls.js");
require("./../programming_api/commands.js");
var random = require("./../utils/random.js");
var shuffle = random.shuffle;
var randint = random.randint;
// The ideas used here are inspired from
// http://www.redblobgames.com/pathfinding/a-star/introduction.html
default_palette = {
'current': 'rgba(0, 255, 127, 0.5)',
'on_frontier': 'rgba(135, 206, 235, 0.5)',
'done': 'rgba(211, 211, 211, 0.5)'
};
function set_custom_palette(user_palette, container){
"use strict";
var i, key, keys;
keys = Object.keys(user_palette);
for(i=0; i < keys.length; i++) {
key = keys[i];
container.palette[key] = user_palette[key];
}
}
/** @constructor Deque
* @memberof RUR
*
* @desc Description to be added
*/
RUR.Deque = function (no_colors) {
if (!no_colors) {
this.use_colors = true;
this.palette = {};
set_custom_palette(default_palette, this);
} else {
this.use_colors = false;
}
this.array = [];
this.previous_item = undefined;
this.append = function(item) {
this.array.push(item);
if (this.use_colors) {
this.set_color(this.palette["on_frontier"], item);
}
};
this.set_palette = function (palette) {
set_custom_palette(palette, this);
};
this.get_last = function() {
var item = this.array.pop();
if (this.use_colors) {
this.set_color(this.palette["current"], item);
}
this.previous_item = item;
return item;
};
this.get_first = function() {
var item = this.array.shift();
if (this.use_colors) {
this.set_color(this.palette["current"], item);
}
this.previous_item = item;
return item;
};
this.is_empty = function () {
return this.array.length === 0;
};
this.mark_done = function (item) {
if (this.use_colors) {
this.set_color(this.palette["done"], item);
}
};
this.set_color = function(color, item) {
var x=item[0], y=item[1], recording_state;
recording_state = RUR._recording_(false);
if (RUR.is_decorative_object(this.palette["current"], x, y)) {
RUR.remove_decorative_object(this.palette["current"], x, y);
}
if (RUR.is_decorative_object(this.palette["on_frontier"], x, y)) {
RUR.remove_decorative_object(this.palette["on_frontier"], x, y);
}
if (RUR.is_decorative_object(this.palette["done"], x, y)) {
RUR.remove_decorative_object(this.palette["done"], x, y);
}
RUR.add_decorative_object(color, x, y);
RUR._recording_(recording_state);
RUR.record_frame();
};
};
// To be called from Python
RUR.create_deque = function(no_colors) {
return new RUR.Deque(no_colors);
};
/**---------------------------- */
/** @constructor PriorityQueue
* @memberof RUR
*
* @desc Description to be added
*/
RUR.PriorityQueue = function (no_colors) {
if (!no_colors) {
this.use_colors = true;
this.palette = {};
set_custom_palette(default_palette, this);
} else {
this.use_colors = false;
}
this.array = [];
this.add = function(node, cost) {
var i, idx = 0, n = this.array.length;
if (n===0) {
this.array[0] = [node, cost];
} else {
for (i=n-1; i >= 0; i--){
if (cost < this.array[i][1]) {
idx = i + 1;
break;
} else {
this.array[i+1] = this.array[i];
}
}
this.array[idx] = [node, cost];
}
if (this.use_colors) {
this.set_color(this.palette["on_frontier"], node);
}
};
this.set_palette = function (palette) {
set_custom_palette(palette, this);
};
this.get_best = function() {
var item = this.array.pop();
if (this.use_colors) {
this.set_color(this.palette["current"], item[0]);
}
return item[0];
};
this.is_empty = function () {
return this.array.length === 0;
};
this.mark_done = function (node) {
if (this.use_colors) {
this.set_color(this.palette["done"], node);
}
};
this.set_color = function(color, node) {
var x=node[0], y=node[1], recording_state;
recording_state = RUR._recording_(false);
if (RUR.is_decorative_object(this.palette["current"], x, y)) {
RUR.remove_decorative_object(this.palette["current"], x, y);
}
if (RUR.is_decorative_object(this.palette["on_frontier"], x, y)) {
RUR.remove_decorative_object(this.palette["on_frontier"], x, y);
}
if (RUR.is_decorative_object(this.palette["done"], x, y)) {
RUR.remove_decorative_object(this.palette["done"], x, y);
}
RUR.add_decorative_object(color, x, y);
RUR._recording_(recording_state);
RUR.record_frame();
};
};
// To be called from Python
RUR.create_priority_queue = function(no_colors) {
return new RUR.PriorityQueue(no_colors);
};
// get neighbours when direction is unimportant
function get_neighbours_around (current, ignore_walls, ordered, robot_body) {
"use strict";
var x, y, neighbours, world, max_x, max_y;
world = RUR.get_current_world();
neighbours = [];
x = current[0];
y = current[1];
max_x = world.cols;
max_y = world.rows;
if (x < max_x && !RUR.is_fatal_position(x+1, y, robot_body)) {
if (ignore_walls || !RUR.is_wall(RUR.translate("east"), x, y)) {
neighbours.push([x+1, y]);
}
}
if (y < max_y && !RUR.is_fatal_position(x, y+1, robot_body)) {
if (ignore_walls || !RUR.is_wall(RUR.translate("north"), x, y)) {
neighbours.push([x, y+1]);
}
}
if (x > 1 && !RUR.is_fatal_position(x-1, y, robot_body)) {
if (ignore_walls || !RUR.is_wall(RUR.translate("west"), x, y)) {
neighbours.push([x-1, y]);
}
}
if (y > 1 && !RUR.is_fatal_position(x, y-1, robot_body)) {
if (ignore_walls || !RUR.is_wall(RUR.translate("south"), x, y)) {
neighbours.push([x, y-1]);
}
}
if (!ordered) {
shuffle(neighbours);
}
return neighbours;
}
/* for get_neighbours_turn_left, we define a neighbour as either the node
immediately in front **or** the same node but turning left.
*/
function get_neighbours_turn_left (current, ignore_walls, ordered, robot_body) {
"use strict";
var direction, x, y, neighbours, world, max_x, max_y;
world = RUR.get_current_world();
neighbours = [];
x = current[0];
y = current[1];
direction = current[2];
max_x = world.cols;
max_y = world.rows;
switch (direction) {
case "east":
neighbours.push([x, y, "north"]);
if (x < max_x && !RUR.is_fatal_position(x+1, y, robot_body)) {
if (ignore_walls || !RUR.is_wall(RUR.translate("east"), x, y)) {
neighbours.push([x+1, y, "east"]);
}
}
break;
case "north":
neighbours.push([x, y, "west"]);
if (y < max_y && !RUR.is_fatal_position(x, y+1, robot_body)) {
if (ignore_walls || !RUR.is_wall(RUR.translate("north"), x, y)) {
neighbours.push([x, y+1, "north"]);
}
}
break;
case "west":
neighbours.push([x, y, "south"]);
if (x > 1 && !RUR.is_fatal_position(x-1, y, robot_body)) {
if (ignore_walls || !RUR.is_wall(RUR.translate("west"), x, y)) {
neighbours.push([x-1, y, "west"]);
}
}
break;
case "south":
neighbours.push([x, y, "east"]);
if (y > 1 && !RUR.is_fatal_position(x, y-1, robot_body)) {
if (ignore_walls || !RUR.is_wall(RUR.translate("south"), x, y)) {
neighbours.push([x, y-1, "south"]);
}
}
break;
}
if (!ordered) {
shuffle(neighbours);
}
return neighbours;
}
/** @constructor Graph
* @memberof RUR
*
* @desc Description to be added
*/
RUR.Graph = function (options) {
"use strict";
this.robot_body = RUR._default_robot_body_();
this.ordered = false;
this.ignore_walls = false;
this.turn_left = false;
this.cost_info = {};
if (options) {
if (options.robot_body) {
this.robot_body = options.robot_body;
}
if (options.ignore_walls){
this.ignore_walls = options.ignore_walls;
}
if (options.ordered) {
this.ordered = options.ordered;
}
if (options.turn_left) {
this.turn_left = options.turn_left;
}
}
this.neighbours = function(current) {
if (this.turn_left) {
return get_neighbours_turn_left(current, this.ignore_walls, this.ordered, this.robot_body);
} else {
return get_neighbours_around(current, this.ignore_walls, this.ordered, this.robot_body);
}
};
this.cost = function(current, neighbour) {
var action, x, y, to_x, to_y;
x = current[0];
y = current[1];
to_x = neighbour[0];
to_y = neighbour[1];
if (x != to_x || y != to_y) {
action = "move";
} else {
action = get_turn(current, neighbour);
}
if (action=="move") {
return 1;
} else if (action == "turn_left") {
return 1;
} else if (action == "turn_around") {
return 2;
} else if (action == "turn_right") {
return 3;
} else {
throw new RUR.ReeborgError("Unknown action in RUR.Graph.cost");
}
};
};
function get_turn (current, neighbour) {
"use strict";
var action, direction, to_direction;
direction = current[2];
to_direction = neighbour[2];
switch (direction) {
case "east":
if (to_direction == "north"){
return "turn_left";
} else if (to_direction == "west"){
return "turn_around";
} else if(to_direction == "south") {
return "turn_right";
}
break;
case "north":
if (to_direction == "west"){
return "turn_left";
} else if (to_direction == "south"){
return "turn_around";
} else if(to_direction == "east") {
return "turn_right";
}
break;
case "west":
if (to_direction == "south"){
return "turn_left";
} else if (to_direction == "east"){
return "turn_around";
} else if(to_direction == "north") {
return "turn_right";
}
break;
case "south":
if (to_direction == "east"){
return "turn_left";
} else if (to_direction == "north"){
return "turn_around";
} else if(to_direction == "west") {
return "turn_right";
}
break;
default:
throw new RUR.ReeborgError("Unknown direction in get_turn() [search.js]");
}
}
// To be called from Python
RUR.create_graph = function(options) {
return new RUR.Graph(options);
};