Path finding with theta* when the triangle inequality is not fulfilled - algorithm

I try to use the theta* algorithm (aigamedev.com/open/tutorial/lazy-theta-star) to find the fastest path on a rectangular grid. Distances are euqlidian, but speed between nodes is time dependent and varies between directions. So the triangle inequality, which is a prerequisite for the algorithm, is violated. Still it works excellently in most cases. How could I modify the code to work nicely also in turbulent areas? I suspect I may have to reevaluate some closed nodes and put them back into the open list. If so, under what conditions? An extensive web search hasn't helped.
vertex_t *thetastar(vertex_t *startnode, vertex_t *finishnode, double starttime) {
vertex_t *s, *s1;
double gold, gnew;
int dir; //8 directions to search for node neighbours
//Initialize
vertex[0].row = startnode->row;
vertex[0].col = startnode->col;
vertex[0].g = starttime;
vertex[0].h = h(&vertex[0], finishnode);
vertex[0].open = true;
vertex[0].closed = false;
vertex[0].parent = &vertex[0];
openlist[0] = &vertex[0];
openlist[1] = NULL;
//Find path
while ((s = pop(openlist)) != NULL) {
if (s->row == finishnode->row && s->col == finishnode->col) {
return s;
}
s->closed = true;
for (dir = 0; dir < 8; dir++) {
if ((s1 = nghbrvis(s, dir)) != NULL) {
if (!s1->closed) {
if (!s1->open) {
s1->g = inftime;
s1->parent = NULL;
}
gold = s1->g;
//Path 2
if (lineofsight(s->parent, s1)) {
gnew = (s->parent)->g + c(s->parent, s1);
if (gnew < s1->g) {
s1->parent = s->parent;
s1->g = gnew;
} }
//Path 1
gnew = s->g + c(s, s1);
if (gnew < s1->g) {
s1->parent = s;
s1->g = gnew;
}
if (s1->g < gold) {
s1->h = h(s1, finishnode);
if (s1->open)
remove(s1, openlist);
insert(s1, openlist);
} } } } }
return NULL;
}

Related

Algorithm that discovers all the fields on a map with as least turns as possible

Let's say I have such map:
#####
..###
W.###
. is a discovered cell.
# is an undiscovered cell.
W is a worker. There can be many workers. Each of them can move once per turn. In one turn he can move by one cell in 4 directions (up, right, down or left). He discovers all 8 cells around him - turns # into .. In one turn, there can be maximum one worker on the same cell.
Maps are not always rectangular. In the beginning all cells are undiscovered, except the neighbours of W.
The goal is to make all the cells discovered, in as least turns as possible.
First approach
Find the nearest # and go towards it. Repeat.
To find the nearest # I start BFS from W and finish it when first # is found.
On exemplary map it can give such solution:
##### ##### ##### ##### ##... #.... .....
..### ...## ....# ..... ...W. ..W.. .W...
W.### .W.## ..W.# ...W. ..... ..... .....
6 turns. Pretty far from optimal:
##### ..### ...## ....# .....
..### W.### .W.## ..W.# ...W.
W.### ..### ...## ....# .....
4 turns.
Question
What is the algorithm that discovers all the cells with as least turns as possible?
Here is a basic idea that uses A*. It is probably quite time- and memory-consuming, but it is guaranteed to return an optimal solution and is definitely better than brute force.
The nodes for A* will be the various states, i.e. where the workers are positioned and the discovery state of all cells. Each unique state represents a different node.
Edges will be all possible transitions. One worker has four possible transitions. For more workers, you will need every possible combination (about 4^n edges). This is the part where you can constrain the workers to remain within the grid and not to overlap.
The cost will be the number of turns. The heuristic to approximate the distance to the goal (all cells discovered) can be developed as follows:
A single worker can discover at most three cells per turn. Thus, n workers can discover at most 3*n cells. The minimum number of remaining turns is therefore "number of undiscovered cells / (3 * worker count)". This is the heuristic to use. This could even be improved by determining the maximum number of cells that each worker can discover in the next turn (will be max. 3 per worker). So overall heuristic would be "(undiscorvered cells - discoverable cells) / (3 * workers) + 1".
In each step you examine the node with the least overall cost (turns so far + heuristic). For the examined node, you calculate the costs for each surrounding node (possible movements of all workers) and go on.
Strictly speaking, the main part of this answer may be considered as "Not An Answer". So to first cover the actual question:
What is the algorithm that discovers all the cells with as least turns as possible?
Answer: In each step, you can compute all possible successors of the current state. Then the successors of these successors. This can be repeated recursively, until one of the successors contains no more #-fields. The sequence of states through which this successor was reached is optimal regarding the number of moves that have been necessary to reach this state.
So far, this is trivial. But of course, this is not feasible for a "large" map and/or a "large" number of workers.
As mentioned in the comments: I think that finding the optimal solution may be an NP-complete problem. In any case, it's most likely at least a tremendously complicated optimization problem where you may employ some rather sophisticated techniques to find the optimal solution in optimal time.
So, IMHO, the only feasible approach for tackling this are heuristics.
Several approaches can be imagined here. However, I wanted to give it a try, with a very simple approach. The following MCVE accepts the definition of the map as a rectangular string (empty spaces represent "invalid" regions, so it's possible to represent non-rectangular maps with that). The workers are simply enumerated, from 0 to 9 (limited to this number, at the moment). The string is converted into a MapState that consists of the actual map, as well as the paths that the workers have gone through until then.
The actual search here is a "greedy" version of the exhaustive search that I described in the first paragraph: Given an initial state, it computes all successor states. These are the states where each worker has moved in either direction (e.g. 64 states for 3 workers - of course these are "filtered" to make sure that workers don't leave the map or move to the same field).
These successor states are stored in a list. Then it searches the list for the "best" state, and again computes all successors of this "best" state and stores them in the list. Sooner or later, the list contains a state where no fields are missing.
The definition of the "best" state is where the heuristics come into play: A state is "better" than another when there are fewer fields missing (unvisited). When two states have an equal number of missing fields, then the average distance of the workers to the next unvisited fields serves as the criterion to decide which one is "better".
This finds and a solution for the example that is contained in the code below rather quickly, and prints it as the lists of positions that each worker has to visit in each turn.
Of course, this will also not be applicable to "really large" maps or "many" workers, because the list of states will grow rather quickly (one could consider dropping the "worst" solutions to speed this up a little, but this may have caveats, like being stuck in local optima). Additionally, one can easily think of cases where the "greedy" strategy does not give optimal results. But until someone posts an MVCE that always computes the optimal solution in polynomial time, maybe someone finds this interesting or helpful.
import java.awt.Point;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class MapExplorerTest
{
public static void main(String[] args)
{
String mapString =
" ### ######"+"\n"+
" ### ###1##"+"\n"+
"###############"+"\n"+
"#0#############"+"\n"+
"###############"+"\n"+
"###############"+"\n"+
"###############"+"\n"+
"###############"+"\n"+
"###############"+"\n"+
"###############"+"\n"+
"##### #######"+"\n"+
"##### #######"+"\n"+
"##### #######"+"\n"+
"###############"+"\n"+
"###############"+"\n"+
"###############"+"\n"+
"### ######2##"+"\n"+
"### #########"+"\n";
MapExplorer m = new MapExplorer(mapString);
MapState solution = m.computeSolutionGreedy();
System.out.println(solution.createString());
}
}
class MapState
{
private int rows;
private int cols;
private char map[][];
List<List<Point>> workerPaths;
private int missingFields = -1;
MapState(String mapString)
{
workerPaths = new ArrayList<List<Point>>();
rows = countLines(mapString);
cols = mapString.indexOf("\n");
map = new char[rows][cols];
String s = mapString.replaceAll("\\n", "");
for (int r=0; r<rows; r++)
{
for (int c=0; c<cols; c++)
{
int i = c+r*cols;
char ch = s.charAt(i);
map[r][c] = ch;
if (Character.isDigit(ch))
{
int workerIndex = ch - '0';
while (workerPaths.size() <= workerIndex)
{
workerPaths.add(new ArrayList<Point>());
}
Point p = new Point(r, c);
workerPaths.get(workerIndex).add(p);
}
}
}
}
MapState(MapState other)
{
this.rows = other.rows;
this.cols = other.cols;
this.map = new char[other.map.length][];
for (int i=0; i<other.map.length; i++)
{
this.map[i] = other.map[i].clone();
}
this.workerPaths = new ArrayList<List<Point>>();
for (List<Point> otherWorkerPath : other.workerPaths)
{
this.workerPaths.add(MapExplorer.copy(otherWorkerPath));
}
}
int distanceToMissing(Point p0)
{
if (getMissingFields() == 0)
{
return -1;
}
List<Point> points = new ArrayList<Point>();
Map<Point, Integer> distances = new HashMap<Point, Integer>();
distances.put(p0, 0);
points.add(p0);
while (!points.isEmpty())
{
Point p = points.remove(0);
List<Point> successors = MapExplorer.computeSuccessors(p);
for (Point s : successors)
{
if (!isValid(p))
{
continue;
}
if (map[p.x][p.y] == '#')
{
return distances.get(p)+1;
}
if (!distances.containsKey(s))
{
distances.put(s, distances.get(p)+1);
points.add(s);
}
}
}
return -1;
}
double averageDistanceToMissing()
{
double d = 0;
for (List<Point> workerPath : workerPaths)
{
Point p = workerPath.get(workerPath.size()-1);
d += distanceToMissing(p);
}
return d / workerPaths.size();
}
int getMissingFields()
{
if (missingFields == -1)
{
missingFields = countMissingFields();
}
return missingFields;
}
private int countMissingFields()
{
int count = 0;
for (int r=0; r<rows; r++)
{
for (int c=0; c<cols; c++)
{
if (map[r][c] == '#')
{
count++;
}
}
}
return count;
}
void update()
{
for (List<Point> workerPath : workerPaths)
{
Point p = workerPath.get(workerPath.size()-1);
for (int dr=-1; dr<=1; dr++)
{
for (int dc=-1; dc<=1; dc++)
{
if (dr == 0 && dc == 0)
{
continue;
}
int nr = p.x + dr;
int nc = p.y + dc;
if (!isValid(nr, nc))
{
continue;
}
if (map[nr][nc] != '#')
{
continue;
}
map[nr][nc] = '.';
}
}
}
}
public void updateWorkerPosition(int w, Point p)
{
List<Point> workerPath = workerPaths.get(w);
Point old = workerPath.get(workerPath.size()-1);
char oc = map[old.x][old.y];
char nc = map[p.x][p.y];
map[old.x][old.y] = nc;
map[p.x][p.y] = oc;
}
boolean isValid(int r, int c)
{
if (r < 0) return false;
if (r >= rows) return false;
if (c < 0) return false;
if (c >= cols) return false;
if (map[r][c] == ' ')
{
return false;
}
return true;
}
boolean isValid(Point p)
{
return isValid(p.x, p.y);
}
private static int countLines(String s)
{
int count = 0;
while (s.contains("\n"))
{
s = s.replaceFirst("\\\n", "");
count++;
}
return count;
}
public String createMapString()
{
StringBuilder sb = new StringBuilder();
for (int r=0; r<rows; r++)
{
for (int c=0; c<cols; c++)
{
sb.append(map[r][c]);
}
sb.append("\n");
}
return sb.toString();
}
public String createString()
{
StringBuilder sb = new StringBuilder();
for (List<Point> workerPath : workerPaths)
{
Point p = workerPath.get(workerPath.size()-1);
int d = distanceToMissing(p);
sb.append(workerPath).append(", distance: "+d+"\n");
}
sb.append(createMapString());
sb.append("Missing "+getMissingFields());
return sb.toString();
}
}
class MapExplorer
{
MapState mapState;
public MapExplorer(String mapString)
{
mapState = new MapState(mapString);
mapState.update();
computeSuccessors(mapState);
}
static List<Point> copy(List<Point> list)
{
List<Point> result = new ArrayList<Point>();
for (Point p : list)
{
result.add(new Point(p));
}
return result;
}
public MapState computeSolutionGreedy()
{
Comparator<MapState> comparator = new Comparator<MapState>()
{
#Override
public int compare(MapState ms0, MapState ms1)
{
int m0 = ms0.getMissingFields();
int m1 = ms1.getMissingFields();
if (m0 != m1)
{
return m0-m1;
}
double d0 = ms0.averageDistanceToMissing();
double d1 = ms1.averageDistanceToMissing();
return Double.compare(d0, d1);
}
};
Set<MapState> handled = new HashSet<MapState>();
List<MapState> list = new ArrayList<MapState>();
list.add(mapState);
while (true)
{
MapState best = list.get(0);
for (MapState mapState : list)
{
if (!handled.contains(mapState))
{
if (comparator.compare(mapState, best) < 0)
{
best = mapState;
}
}
}
if (best.getMissingFields() == 0)
{
return best;
}
handled.add(best);
list.addAll(computeSuccessors(best));
System.out.println("List size "+list.size()+", handled "+handled.size()+", best\n"+best.createString());
}
}
List<MapState> computeSuccessors(MapState mapState)
{
int numWorkers = mapState.workerPaths.size();
List<Point> oldWorkerPositions = new ArrayList<Point>();
for (int i=0; i<numWorkers; i++)
{
List<Point> workerPath = mapState.workerPaths.get(i);
Point p = workerPath.get(workerPath.size()-1);
oldWorkerPositions.add(p);
}
List<List<Point>> successorPositionsForWorkers = new ArrayList<List<Point>>();
for (int w=0; w<oldWorkerPositions.size(); w++)
{
Point p = oldWorkerPositions.get(w);
List<Point> ps = computeSuccessors(p);
successorPositionsForWorkers.add(ps);
}
List<List<Point>> newWorkerPositionsList = new ArrayList<List<Point>>();
int numSuccessors = (int)Math.pow(4, numWorkers);
for (int i=0; i<numSuccessors; i++)
{
String s = Integer.toString(i, 4);
while (s.length() < numWorkers)
{
s = "0"+s;
}
List<Point> newWorkerPositions = copy(oldWorkerPositions);
for (int w=0; w<numWorkers; w++)
{
int index = s.charAt(w) - '0';
Point newPosition = successorPositionsForWorkers.get(w).get(index);
newWorkerPositions.set(w, newPosition);
}
newWorkerPositionsList.add(newWorkerPositions);
}
List<MapState> successors = new ArrayList<MapState>();
for (int i=0; i<newWorkerPositionsList.size(); i++)
{
List<Point> newWorkerPositions = newWorkerPositionsList.get(i);
if (workerPositionsValid(newWorkerPositions))
{
MapState successor = new MapState(mapState);
for (int w=0; w<numWorkers; w++)
{
Point p = newWorkerPositions.get(w);
successor.updateWorkerPosition(w, p);
successor.workerPaths.get(w).add(p);
}
successor.update();
successors.add(successor);
}
}
return successors;
}
private boolean workerPositionsValid(List<Point> workerPositions)
{
Set<Point> set = new HashSet<Point>();
for (Point p : workerPositions)
{
if (!mapState.isValid(p.x, p.y))
{
return false;
}
set.add(p);
}
return set.size() == workerPositions.size();
}
static List<Point> computeSuccessors(Point p)
{
List<Point> result = new ArrayList<Point>();
result.add(new Point(p.x+0, p.y+1));
result.add(new Point(p.x+0, p.y-1));
result.add(new Point(p.x+1, p.y+0));
result.add(new Point(p.x-1, p.y+0));
return result;
}
}

Algorithms find shortest path to all cells on grid

I have a grid [40 x 15] with 2 to 16 units on it, and unknown amount of obstacles.
How to find the shortest path to all the units from my unit location.
I have two helper methods that we can consider as O(1)
getMyLocation() - return the (x, y) coordinates of my location on the grid
investigateCell(x, y) - return information about cell at (x,y) coordinates
I implemented A* search algorithm, that search simultaneously to all the directions. At the end it output a grid where each cell have a number representing the distance from my location, and collection of all the units on the grid. It performs with O(N) where N is the number of cells - 600 in my case.
I implement this using AS3, unfortunately it takes my machine 30 - 50 milliseconds to calculate.
Here is my source code. Can you suggest me a better way?
package com.gazman.strategy_of_battle_package.map
{
import flash.geom.Point;
/**
* Implementing a path finding algorithm(Similar to A* search only there is no known target) to calculate the shortest path to each cell on the map.
* Once calculation is complete the information will be available at cellsMap. Each cell is a number representing the
* number of steps required to get to that location. Enemies and Allies will be represented with negative distance. Also the enemy and Allys
* coordinations collections are provided. Blocked cells will have the value 0.<br><br>
* Worth case and best case efficiency is O(N) where N is the number of cells.
*/
public class MapFilter
{
private static const PULL:Vector.<MapFilter> = new Vector.<MapFilter>();
public var cellsMap:Vector.<Vector.<int>>;
public var allys:Vector.<Point>;
public var enemies:Vector.<Point>;
private var stack:Vector.<MapFilter>;
private var map:Map;
private var x:int;
private var y:int;
private var count:int;
private var commander:String;
private var hash:Object;
private var filtered:Boolean;
public function filter(map:Map, myLocation:Point, commander:String):void{
filtered = true;
this.commander = commander;
this.map = map;
this.x = myLocation.x;
this.y = myLocation.y;
init();
cellsMap[x][y] = 1;
excecute();
while(stack.length > 0){
var length:int = stack.length;
for(var i:int = 0; i < length; i++){
var mapFilter:MapFilter = stack.shift();
mapFilter.excecute();
PULL.push(mapFilter);
}
}
}
public function navigateTo(location:Point):Point{
if(!filtered){
throw new Error("Must filter before navigating");
}
var position:int = Math.abs(cellsMap[location.x][location.y]);
if(position == 0){
throw new Error("Target unreachable");
}
while(position > 2){
if(canNavigateTo(position, location.x + 1, location.y)){
location.x++;
}
else if(canNavigateTo(position, location.x - 1, location.y)){
location.x--;
}
else if(canNavigateTo(position, location.x, location.y + 1)){
location.y++;
}
else if(canNavigateTo(position, location.x, location.y - 1)){
location.y--;
}
position = cellsMap[location.x][location.y];
}
return location;
throw new Error("Unexpected filtering error");
}
private function canNavigateTo(position:int, targetX:int, targetY:int):Boolean
{
return isInMapRange(targetX, targetY) && cellsMap[targetX][targetY] < position && cellsMap[targetX][targetY] > 0;
}
private function excecute():void
{
papulate(x + 1, y);
papulate(x - 1, y);
papulate(x, y + 1);
papulate(x, y - 1);
}
private function isInMapRange(x:int, y:int):Boolean{
return x < cellsMap.length &&
x >= 0 &&
y < cellsMap[0].length &&
y >= 0;
}
private function papulate(x:int, y:int):void
{
if(!isInMapRange(x,y) ||
cellsMap[x][y] != 0 ||
hash[x + "," + y] != null ||
map.isBlocked(x,y)){
return;
}
// we already checked that is not block
// checking if there units
if(map.isEmpty(x,y)){
cellsMap[x][y] = count;
addTask(x,y);
}
else{
cellsMap[x][y] = -count;
if(map.isAlly(x,y, commander)){
hash[x + "," + y] = true;
allys.push(new Point(x,y));
}
else {
hash[x + "," + y] = true;
enemies.push(new Point(x,y));
}
}
}
private function addTask(x:int, y:int):void
{
var mapFilter:MapFilter = PULL.pop();
if(mapFilter == null){
mapFilter = new MapFilter();
}
mapFilter.commander = commander;
mapFilter.hash = hash;
mapFilter.map = map;
mapFilter.cellsMap = cellsMap;
mapFilter.allys = allys;
mapFilter..enemies = enemies;
mapFilter.stack = stack;
mapFilter.count = count + 1;
mapFilter.x = x;
mapFilter.y = y;
stack.push(mapFilter);
}
private function init():void
{
hash = new Object();
cellsMap = new Vector.<Vector.<int>>();
for(var i:int = 0; i < map.width;i++){
cellsMap.push(new Vector.<int>);
for(var j:int = 0; j < map.height;j++){
cellsMap[i].push(0);
}
}
allys = new Vector.<Point>();
enemies = new Vector.<Point>();
stack = new Vector.<MapFilter>();
count = 2;
}
}
}
You can use Floyd Warshall to find the shortest path between every pair of points. This would be O(|V|^3) and you would not have to run it for each unit, just once on each turn. It's such a simple algorithm I suspect it might be faster in practice than running something like BFS / Bellman Ford for each unit.

How to correct loop counters for maze algorithm?

I have figured out how to move my character around the maze using the algorithm I have written, but the count is not figuring correctly. At the end of each row my character moves up and down several times until the count reaches the specified number to exit the loop, then the character moves along the next row down until it reaches the other side and repeats the moving up and down until the count reaches the specified number again. Can anyone help me find why my count keeps getting off? The algorithm and the maze class I am calling from is listed below.
public class P4 {
public static void main(String[] args) {
// Create maze
String fileName = args[3];
Maze maze = new Maze(fileName);
System.out.println("Maze name: " + fileName);
// Get dimensions
int mazeWidth = maze.getWidth();
int mazeHeight = maze.getHeight();
// Print maze size
System.out.println("Maze width: " + mazeWidth);
System.out.println("Maze height: " + mazeHeight);
int r = 0;
int c = 0;
// Move commands
while (true){
for (c = 0; c <= mazeWidth; c++){
if (maze.moveRight()){
maze.isDone();
c++;
}
if (maze.isDone() == true){
System.exit(1);
}
if (maze.moveRight() == false && c != mazeWidth){
maze.moveDown();
maze.moveRight();
maze.moveRight();
maze.moveUp();
c++;
}
}
for (r = 0; r % 2 == 0; r++){
maze.moveDown();
maze.isDone();
if (maze.isDone() == true){
System.exit(1);
}
}
for (c = mazeWidth; c >= 0; c--){
if (maze.moveLeft()){
c--;
maze.isDone();
System.out.println(c);
}
if (maze.isDone() == true){
System.exit(1);
}
if (maze.moveLeft() == false && c != 0){
maze.moveDown();
maze.moveLeft();
maze.moveLeft();
maze.moveUp();
c--;
}
}
for (r = 1; r % 2 != 0; r++){
maze.moveDown();
maze.isDone();
if (maze.isDone() == true){
System.exit(1);
}
}
}
}
}
public class Maze {
// Maze variables
private char mazeData[][];
private int mazeHeight, mazeWidth;
private int finalRow, finalCol;
int currRow;
private int currCol;
private int prevRow = -1;
private int prevCol = -1;
// User interface
private JFrame frame;
private JPanel panel;
private Image java, student, success, donotpass;
private ArrayList<JButton> buttons;
// Maze constructor
public Maze(String fileName) {
// Read maze
readMaze(fileName);
// Graphics setup
setupGraphics();
}
// Get height
public int getHeight() {
return mazeHeight;
}
// Get width
public int getWidth() {
return mazeWidth;
}
// Move right
public boolean moveRight() {
// Legal move?
if (currCol + 1 < mazeWidth) {
// Do not pass?
if (mazeData[currRow][currCol + 1] != 'D')
{
currCol++;
redraw(true);
return true;
}
}
return false;
}
// Move left
public boolean moveLeft() {
// Legal move?
if (currCol - 1 >= 0) {
// Do not pass?
if (mazeData[currRow][currCol - 1] != 'D')
{
currCol--;
redraw(true);
return true;
}
}
return false;
}
// Move up
public boolean moveUp() {
// Legal move?
if (currRow - 1 >= 0) {
// Do not pass?
if (mazeData[currRow - 1][currCol] != 'D')
{
currRow--;
redraw(true);
return true;
}
}
return false;
}
// Move down
public boolean moveDown() {
// Legal move?
if (currRow + 1 < mazeHeight) {
// Do not pass?
if (mazeData[currRow + 1][currCol] != 'D')
{
currRow++;
redraw(true);
return true;
}
}
return false;
}
public boolean isDone() {
// Maze solved?
if ((currRow == finalRow) && (currCol == finalCol))
return true;
else
return false;
}
private void redraw(boolean print) {
// Wait for awhile
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
if (print)
System.out.println("Moved to row " + currRow + ", column " + currCol);
// Compute index and remove icon
int index = (prevRow * mazeWidth) + prevCol;
if ((prevRow >= 0) && (prevCol >= 0)) {
buttons.get(index).setIcon(null);
}
// Compute index and add icon
index = (currRow * mazeWidth) + currCol;
if ((currRow == finalRow) && (currCol == finalCol))
buttons.get(index).setIcon(new ImageIcon(success));
else
buttons.get(index).setIcon(new ImageIcon(student));
// Store previous location
prevRow = currRow;
prevCol = currCol;
}
// Set button
private void setButton(JButton button, int row, int col) {
if (mazeData[row][col] == 'S') {
button.setIcon(new ImageIcon(student));
currRow = row;
currCol = col;
} else if (mazeData[row][col] == 'J') {
button.setIcon(new ImageIcon(java));
finalRow = row;
finalCol = col;
} else if (mazeData[row][col] == 'D') {
button.setIcon(new ImageIcon(donotpass));
}
}
// Read maze
private void readMaze(String filename) {
try {
// Open file
Scanner scan = new Scanner(new File(filename));
// Read numbers
mazeHeight = scan.nextInt();
mazeWidth = scan.nextInt();
// Allocate maze
mazeData = new char[mazeHeight][mazeWidth];
// Read maze
for (int row = 0; row < mazeHeight; row++) {
// Read line
String line = scan.next();
for (int col = 0; col < mazeWidth; col++) {
mazeData[row][col] = line.charAt(col);
}
}
// Close file
scan.close();
} catch (IOException e) {
System.out.println("Cannot read maze: " + filename);
System.exit(0);
}
}
// Setup graphics
private void setupGraphics() {
// Create grid
frame = new JFrame();
panel = new JPanel();
panel.setLayout(new GridLayout(mazeHeight, mazeWidth, 0, 0));
frame.add(Box.createRigidArea(new Dimension(0, 5)), BorderLayout.NORTH);
frame.add(panel, BorderLayout.CENTER);
// Look and feel
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
// Configure window
frame.setSize(mazeWidth * 100, mazeHeight * 100);
frame.setTitle("Maze");
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setAlwaysOnTop(true);
// Load and scale images
ImageIcon icon0 = new ImageIcon("Java.jpg");
Image image0 = icon0.getImage();
java = image0.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
ImageIcon icon1 = new ImageIcon("Student.jpg");
Image image1 = icon1.getImage();
student = image1.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
ImageIcon icon2 = new ImageIcon("Success.jpg");
Image image2 = icon2.getImage();
success = image2.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
ImageIcon icon3 = new ImageIcon("DoNotPass.jpg");
Image image3 = icon3.getImage();
donotpass = image3.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
// Build panel of buttons
buttons = new ArrayList<JButton>();
for (int row = 0; row < mazeHeight; row++) {
for (int col = 0; col < mazeWidth; col++) {
// Initialize and add button
JButton button = new JButton();
Border border = new LineBorder(Color.darkGray, 4);
button.setOpaque(true);
button.setBackground(Color.gray);
button.setBorder(border);
setButton(button, row, col);
panel.add(button);
buttons.add(button);
}
}
// Show window
redraw(false);
frame.setVisible(true);
}
}
One error I can see in your code is that you're incrementing your c counter more often than you should. You start with it managed by your for loop, which means that it will be incremented (or decremented, for the leftward moving version) at the end of each pass through the loop. However, you also increment it an additional time in two of your if statements. That means that c might increase by two or three on a single pass through the loop, which is probably not what you intend.
Furthermore, the count doesn't necessarily have anything obvious to do with the number of moves you make. The loop code will always increase it by one, even if you're repeatedly trying to move through an impassible wall.
I don't really understand what your algorithm is supposed to be, so I don't have any detailed advice for how to fix your code.
One suggestion I have though is that you probably don't ever want to be calling methods on your Maze class without paying attention to their return values. You have a bunch of places where you call isDone but ignore the return value, which doesn't make any sense. Similarly, you should always be checking the return values from your moveX calls, to see if the move was successful or not. Otherwise you may just blunder around a bunch, without your code having any clue where you are in the maze.

Directed graph and complexity

I have a problem finding the complexity in my program.
It consists of one directed graph where each node has an arraylist of edges.
Do anyone know the complexsity of searching trough this directed graph?
My code:
class Project{
ArrayList<Task> subProject = null;
int manpower, maxNr;
Project(int maxNr, int manpower){
this.maxNr = maxNr;
this.manpower = manpower;
subProject = new ArrayList<Task>();
}
}
class Task {
int id, time, staff, slack;
String name;
int earliestStart, latestStart, finished;
ArrayList<Edge> outEdges = null;
int cntPredecessors;
Boolean rund;
Task(int n){
id = n;
cntPredecessors = 0;
outEdges = new ArrayList<Edge>();
rund = false;
}
}
class Edge {
int edges;
Edge(int edges){
this.edges = edges;
}
}
public void run(){
while(runTasks == false){
System.out.println("Time: " + runT);
for(Task t: subProject){
if(t.cntPredecessors == 0 && t.rund == false){ // Checks if there is no edges to the task and if the task hasen't started
System.out.println(" Starting: " + t.id);
ferdige.add(t);
t.rund = true;
t.earliestStart = runT;
t.finished = runT + t.time;
if(cirdep < t.finished){
cirdep = t.finished;
}
tId = t.id;
workers += t.staff;
System.out.println(" Current staff: " + workers);
}
if(t.cntPredecessors == 0 && t.finished == runT){
maxNr--;
System.out.println(" Finished: " + t.id);
minPre(t.id);
workers -= t.staff;
}
if((t.cntPredecessors == 0 && t.rund == false) || (t.cntPredecessors == 0 && t.finished == runT)){
System.out.println(" Current staff: " + workers);
}
}
With mo more information to go on, you can at least say it's $O(|V|)$ by using depth first search. But you have to do something to take care of isolated components, 'cause DFS can't get to a node if there's no path.

Binary Search Tree Balancing

I had a quesiton here, but it didn't save. I'm having trouble balancing a fully unbalanced tree (nodes 1-15 along the right side).
I'm having trouble because I get stack overflow.
> // balancing
public void balance(node n) {
if(n != null) {
System.out.println(height(n)-levels);
if (height(n.RCN) != height(n.LCN)) {
if (height(n.RCN) > height(n.LCN)) {
if(height(n.RCN) > height(n.LCN)) {
n = rotateL(n);
n = rotateR(n);
} else {
n = rotateL(n);
}
} else {
if(height(n.LCN) > height(n.RCN)) {
n = rotateR(n);
n = rotateL(n);
} else {
n = rotateR(n);
}
}
balance(n.LCN);
balance(n.RCN);
}
}
}
// depth from node to left
public int heightL(node n) {
if (n == null)
return 0;
return height(n.LCN) + 1;
}
// depth from node from the right
public int heightR(node n) {
if (n == null)
return 0;
return height(n.RCN) + 1;
}
// left rotation around node
public node rotateL(node n) {
if (n == null)
return null;
else {
node newRoot = n.RCN;
n.RCN = newRoot.LCN;
newRoot.LCN = n;
return newRoot;
}
}
// right rotation around node
public node rotateR(node n) {
if (n == null)
return null;
else {
node newRoot = n.LCN;
n.LCN = newRoot.RCN;
newRoot.RCN = n;
return newRoot;
}
}
Doing a rotateL followed by a rotateR ends up doing nothing since you are modifying the same node. n is not the original n. It is the newNode from the function. So basically, n is something like this:
newNode = rotateL(n);
n = rotateR(newNode);
So you are basically leaving the tree unchanged.
I am also unsure as to why you repeat the if (height(n.RCN) > height(n.LCN)) check. I think you meant your first check to be more like abs(height(n.RCN) - height(n.LCN)) > 1 and then use the comparison to determine which way to rotate.
Also, could you add the implementation for height(...)?

Resources