How can I check efficiently if a curve is closed? For example look this figure:
The curve will always be white on a black background.
I tried with flood fill algorithm but not works well with this situation (I don't understand how modify it).
Here the code:
public static boolean isWhite(BufferedImage image, int posX, int posY) {
Color color = new Color(image.getRGB(posX, posY));
int r=color.getRed();
int g=color.getGreen();
int b=color.getBlue();
if(r==0&&g==0&&b==0)
return false;
return true;
}
public static void checkClosed(BufferedImage bimg) {
boolean[][] painted = new boolean[bimg.getHeight()][bimg.getWidth()];
for (int i = 0; i < bimg.getHeight(); i++) {
for (int j = 0; j < bimg.getWidth(); j++) {
if (isWhite(bimg, j, i) && !painted[i][j]) {
Queue<Point> queue = new LinkedList<Point>();
queue.add(new Point(j, i));
int pixelCount = 0;
while (!queue.isEmpty()) {
Point p = queue.remove();
if ((p.x >= 0) && (p.x < bimg.getWidth() && (p.y >= 0) && (p.y < bimg.getHeight()))) {
if (!painted[p.y][p.x] && isWhite(bimg, p.x, p.y)) {
painted[p.y][p.x] = true;
pixelCount++;
queue.add(new Point(p.x + 1, p.y));
queue.add(new Point(p.x - 1, p.y));
queue.add(new Point(p.x, p.y + 1));
queue.add(new Point(p.x, p.y - 1));
}
}
}
System.out.println("Blob detected : " + pixelCount + " pixels");
}
}
}
}
The way to see if the boundary in your image is closed is by doing a flood fill of the boundary starting at all the image edge pixels. That is, you put all the background pixels that are at the image edge on the queue, then flood fill from there.
Next, check to see if any background pixels are left. If the flood fill filled inside the object, the boundary wasn’t closed.
Firstly I'm not sure if this is the right place to post this question, so if I am wrong, please, move it. Thanks.
I had an assignment to compare same algorithm performance in Java and C#. The algorithm is supposed to be A* search, but I think I made it more like flooding, but it works well and I'm not here to fix it. Firstly I'll post the code I was using in Java and C# and then explain what I got.
As body of question is limited to 30000 characters and I entered more, I had to delete functions readFile() from Java and C# to make it fit.
UPDATED
After Jim Mischel has pointed out I updated hash function in C# version to be same as in Java which resulted in better performance.
Also thanks to Matt Timmermans I realized that all this time I was running C# in debug (Result of not thinking it through) and changing to release increased performance even more.
C# version:
File: Program.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
namespace A_Star_Compare
{
class Program
{
static int StartX = 0;
static int StartY = 0;
static int TargetX = 0;
static int TargetY = 0;
static int Width = 0;
static int Height = 0;
static TimeSpan TotalTime = TimeSpan.Zero;
static double[] TrialTimes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static Dictionary<int, List<int>> Obstacles = new Dictionary<int, List<int>>();
static void Main(string[] args)
{
for (int z = 0; z < 10; z++)
{
int Index = 0;
Console.WriteLine("z: " + z);
for (int x = 0; x < 14; x++)
{
if (x < 10)
Index += 10;
else
Index += 100;
string Line = string.Empty;
string FileName = "Maps-" + Index + ".txt";
TotalTime = TimeSpan.Zero;
readFile(FileName);
TrialTimes[x] += (double)TotalTime.TotalSeconds / 100;
}
}
int Index0 = 0;
for (int i = 0; i < 14; i++)
{
if (i < 10)
Index0 += 10;
else
Index0 += 100;
string FileName = "Maps-" + Index0 + ".txt";
Console.WriteLine("{0} Map size: {1}*{2}. On average map solved in: {3}", FileName, Index0, Index0, (double)TrialTimes[i] / 10);
}
}
static void measureTime()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Algorithm Solve = new Algorithm(StartX, StartY, TargetX, TargetY, Width, Height, Obstacles);
Solve.FullSolve();
stopwatch.Stop();
TotalTime += stopwatch.Elapsed;
}
}
}
File: Algorithm.cs
using System.Collections.Generic;
namespace A_Star_Compare
{
public class Node
{
public int X { get; set; }
public int Y { get; set; }
public int G { get; set; }
public int F { get; set; }
public int H { get; set; }
public Node PointsTo { get; set; }
public Node(int x, int y, int g, int f, int h, Node point)
{
this.X = x;
this.Y = y;
this.G = g;
this.F = f;
this.H = h;
this.PointsTo = point;
}
public override bool Equals(object obj)
{
Node rhs = obj as Node;
return rhs.X == this.X && rhs.Y == this.Y;
}
public override int GetHashCode()
{
int hash = 7;
hash = 83 * hash + this.X;
hash = 83 * hash + this.Y;
return hash;
}
}
class Algorithm
{
private Dictionary<int, List<int>> Obstacles { get; set; }
public HashSet<Node> OpenList { get; set; }
public HashSet<Node> ClosedList { get; set; }
private Node Parent { get; set; }
private Node LowestCost { get; set; }
private int StartX { get; set; }
private int StartY { get; set; }
private int TargetX { get; set; }
private int TargetY { get; set; }
private int Width { get; set; }
private int Height { get; set; }
private bool FirstIter = true;
public Algorithm(int stX, int stY, int tgX, int tgY, int wid, int hei, Dictionary<int, List<int>> obs)
{
this.StartX = stX;
this.StartY = stY;
this.TargetX = tgX;
this.TargetY = tgY;
this.Width = wid - 1;
this.Height = hei - 1;
this.Obstacles = new Dictionary<int, List<int>>(obs);
this.Parent = new Node(StartX, StartY, 0, 0, 0, null);
this.LowestCost = new Node(int.MaxValue, int.MaxValue, 0, int.MaxValue, 0, null);
this.ClosedList = new HashSet<Node>();
this.OpenList = new HashSet<Node>();
}
private bool IsBlockObstacle(int X, int Y)
{
if (Obstacles.ContainsKey(X) == false || (Obstacles.ContainsKey(X) == true && Obstacles[X].Contains(Y) == false))
return false;
return true;
}
private void Calculate(ref int H, int G, ref int F, int MovedX, int MovedY, Node AddToList)
{
int H1 = 0;
H = (TargetX - MovedX) * 10;
if (H < 0)
H *= -1;
H1 = (TargetY - MovedY) * 10;
if (H1 < 0)
H1 *= -1;
H += H1;
F = G + H;
AddToList.F = F;
AddToList.H = H;
AddToList.PointsTo = Parent;
}
private Node GetNodeFromOpen(Node Find)
{
Node Ret = null;
foreach (Node Nfo in OpenList)
{
if (Nfo.Equals(Find))
{
Ret = Nfo;
break;
}
}
return Ret;
}
private bool CheckNode(Node AddToList, int G)
{
if (!OpenList.Contains(AddToList))
{
OpenList.Add(AddToList);
return true;
}
else
{
Node Check = GetNodeFromOpen(AddToList);
if (Parent.G + G < Check.G)
{
int Offset = Check.G - Parent.G - G;
Check.G -= Offset;
Check.F -= Offset;
Check.PointsTo = Parent;
}
}
return false;
}
private void ChooseNode()
{
foreach (Node Nfo in OpenList)
{
if (Nfo.X == TargetX && Nfo.Y == TargetY)
{
LowestCost = Nfo;
break;
}
if (Nfo.F < LowestCost.F)
LowestCost = Nfo;
}
}
private void CountCost()
{
int[] Directions = { 1, -1 };
int[] Diagnoly = { 1, 1, -1, 1, 1, -1, -1, -1 };
int ParentX = Parent.X;
int ParentY = Parent.Y;
int MovedX = 0;
int MovedY = 0;
int H = 0;
int F = 0;
Node AddToList = null;
//Left and right
for (int i = 0; i < 2; i++)
{
//Check if it is possible to move right or left
if (ParentX + Directions[i] <= Width && ParentX + Directions[i] >= 0)
{
//Check if blocks to the right and left of parent aren't obstacles
if (!IsBlockObstacle(ParentX + Directions[i], ParentY))
{
AddToList = new Node(ParentX + Directions[i], ParentY, Parent.G + 10, 0, 0, null);
//Check if it is not on closed list
if (!ClosedList.Contains(AddToList))
{
MovedX = AddToList.X;
MovedY = AddToList.Y;
Calculate(ref H, AddToList.G, ref F, MovedX, MovedY, AddToList);
CheckNode(AddToList, 10);
}
}
}
}
//Up and down
for (int i = 0; i < 2; i++)
{
//Check if possible to move up or down
if (ParentY + Directions[i] <= Height && ParentY + Directions[i] >= 0)
{
//Check if higher and lower block of parent aren't obstacles
if (!IsBlockObstacle(ParentX, ParentY + Directions[i]))
{
AddToList = new Node(ParentX, ParentY + Directions[i], Parent.G + 10, 0, 0, null);
if (!ClosedList.Contains(AddToList))
{
MovedX = ParentX;
MovedY = ParentY + Directions[i];
Calculate(ref H, AddToList.G, ref F, MovedX, MovedY, AddToList);
CheckNode(AddToList, 10);
}
}
}
}
//Diagnoly
for (int i = 0; i < 8; i += 2)
{
if (ParentX + Diagnoly[i] <= Width && ParentX + Diagnoly[i] >= 0 && ParentY + Diagnoly[i + 1] <= Height && ParentY + Diagnoly[i + 1] >= 0)
{
if (!IsBlockObstacle(ParentX + Diagnoly[i], ParentY + Diagnoly[i + 1]))
{
AddToList = new Node(ParentX + Diagnoly[i], ParentY + Diagnoly[i + 1], Parent.G + 14, 0, 0, null);
if (!ClosedList.Contains(AddToList))
{
MovedX = ParentX + Diagnoly[i];
MovedY = ParentY + Diagnoly[i + 1];
Calculate(ref H, AddToList.G, ref F, MovedX, MovedY, AddToList);
CheckNode(AddToList, 14);
}
}
}
}
}
public void FullSolve()
{
Node Final = null;
if (FirstIter)
{
CountCost();
ChooseNode();
OpenList.Remove(Parent);
ClosedList.Add(Parent);
Parent = LowestCost;
OpenList.Remove(Parent);
ClosedList.Add(Parent);
FirstIter = false;
FullSolve();
}
else
{
while (true)
{
if (OpenList.Count == 0)
break;
CountCost();
HashSet<Node> Copy = new HashSet<Node>(OpenList);
foreach (Node Nfo in Copy)
{
Parent = Nfo;
CountCost();
ClosedList.Add(Parent);
OpenList.Remove(Parent);
if (Parent.X == TargetX && Parent.Y == TargetY)
{
Final = Parent;
break;
}
}
ChooseNode();
OpenList.Remove(Parent);
ClosedList.Add(Parent);
Parent = LowestCost;
LowestCost.F = int.MaxValue;
if (Parent.X == TargetX && Parent.Y == TargetY)
{
Final = Parent;
break;
}
}
}
}
}
}
Java version:
File: AStar_Compare.java
package a.star_compare;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.lang3.time.StopWatch;
public class AStar_Compare {
static double totalTime;
static double[] trialTimes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static int startX;
static int startY;
static int targetX;
static int targetY;
static int width;
static int heigth;
static HashMap<Integer, List<Integer>> obstacles = new HashMap<>();
static NumberFormat formatter = new DecimalFormat("#0.000000000");
public static void main(String[] args) throws FileNotFoundException, IOException {
for (int z = 0; z < 10; z++) {
int Index = 0;
System.out.println("z: " + z);
for (int x = 0; x < 5; x++) {
if (x < 10) {
Index += 10;
} else {
Index += 100;
}
String fileName = "Maps-" + Index + ".txt";
totalTime = 0;
readFile(fileName);
trialTimes[x] += totalTime / 1E9 / 100;
}
}
int index0 = 0;
for (int i = 0; i < 14; i++) {
if (i < 10) {
index0 += 10;
} else {
index0 += 100;
}
trialTimes[i] /= 10;
String fileName = "Maps-" + index0 + ".txt";
System.out.println(fileName + " Map size: " + index0 + "*" + index0 + ". On average map solved in: " + formatter.format(trialTimes[i]));
}
}
static void measureTime() {
StopWatch time = new StopWatch();
time.start();
Algorithm solve = new Algorithm(obstacles, startX, startY, targetX, targetY, width, heigth);
solve.FullSolve();
time.stop();
totalTime += time.getNanoTime();
}
}
File: Node.java
package a.star_compare;
public class Node {
public int x;
public int y;
public int g;
public int h;
public int f;
public Node pointsTo;
public Node(int gx, int gy, int gg, int gh, int gf, Node point){
this.x = gx;
this.y = gy;
this.g = gg;
this.h = gh;
this.f = gf;
this.pointsTo = point;
}
#Override
public boolean equals(Object other){
if(other == null) return false;
if(other == this) return true;
if(!(other instanceof Node)) return false;
Node rhs = (Node)other;
return this.x == rhs.x && this.y == rhs.y;
}
#Override
public int hashCode() {
int hash = 7;
hash = 83 * hash + this.x;
hash = 83 * hash + this.y;
return hash;
}
}
File: Algorithm.java
package a.star_compare;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
public class Algorithm {
private final HashMap<Integer, List<Integer>> obstacles;
private final HashSet<Node> closedList;
private final HashSet<Node> openList;
private Node parent;
private Node lowestCost;
private final int startX;
private final int startY;
private final int targetX;
private final int targetY;
private final int width;
private final int height;
private boolean firstIter = true;
public Algorithm(HashMap<Integer, List<Integer>> obs, int stX, int stY, int tgX, int tgY, int wid, int hei) {
this.obstacles = new HashMap(obs);
this.startX = stX;
this.startY = stY;
this.targetX = tgX;
this.targetY = tgY;
this.width = wid - 1;
this.height = hei - 1;
this.parent = new Node(startX, startY, 0, 0, 0, null);
this.lowestCost = new Node(Integer.MAX_VALUE, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, 0, null);
this.closedList = new HashSet<>();
this.openList = new HashSet<>();
}
private boolean isBlockObstacle(Integer x, Integer y) {
if (obstacles.containsKey(x) == false || (obstacles.containsKey(x) == true && obstacles.get(x).contains(y) == false)) {
return false;
}
return true;
}
private void calculate(int h, int g, int f, int movedX, int movedY, Node addToList) {
int h1 = 0;
h = (targetX - movedX) * 10;
if (h < 0) {
h *= -1;
}
h1 = (targetY - movedY) * 10;
if (h1 < 0) {
h1 *= -1;
}
h += h1;
f = g + h;
addToList.f = f;
addToList.h = h;
addToList.pointsTo = parent;
}
private Node getNodeFromOpen(Node find) {
Node ret = null;
for (Node nfo : openList) {
if (nfo.equals(find)) {
ret = nfo;
break;
}
}
return ret;
}
private boolean checkNode(Node addToList, int g) {
if (!openList.contains(addToList)) {
openList.add(addToList);
return true;
} else {
Node check = getNodeFromOpen(addToList);
if (parent.g + g < check.g) {
int offset = check.g - parent.g - g;
check.g -= offset;
check.f -= offset;
check.pointsTo = parent;
}
}
return false;
}
private void chooseNode() {
for (Node nfo : openList) {
if (nfo.x == targetX && nfo.y == targetY) {
lowestCost = nfo;
break;
}
if (nfo.f < lowestCost.f) {
lowestCost = nfo;
}
}
}
private void countCost() {
int[] directions = {1, -1};
int[] diagnoly = {1, 1, -1, 1, 1, -1, -1, -1};
int parentX = parent.x;
int parentY = parent.y;
int movedX = 0;
int movedY = 0;
int h = 0;
int f = 0;
Node addToList = null;
//Left and right
for (int i = 0; i < 2; i++) {
//Check if it is possible to move right or left
if (parentX + directions[i] <= width && parentX + directions[i] >= 0) {
//Check if blocks to the right and left of parent aren't obstacles
if (!isBlockObstacle(parentX + directions[i], parentY)) {
addToList = new Node(parentX + directions[i], parentY, parent.g + 10, 0, 0, null);
//Check if it is not on closed list
if (!closedList.contains(addToList)) {
movedX = addToList.x;
movedY = addToList.y;
calculate(h, addToList.g, f, movedX, movedY, addToList);
checkNode(addToList, 10);
}
}
}
}
//Up and down
for (int i = 0; i < 2; i++) {
//Check if possible to move up or down
if (parentY + directions[i] <= height && parentY + directions[i] >= 0) {
//Check if higher and lower block of parent aren't obstacles
if (!isBlockObstacle(parentX, parentY + directions[i])) {
addToList = new Node(parentX, parentY + directions[i], parent.g + 10, 0, 0, null);
if (!closedList.contains(addToList)) {
movedX = parentX;
movedY = parentY + directions[i];
calculate(h, addToList.g, f, movedX, movedY, addToList);
checkNode(addToList, 10);
}
}
}
}
//diagnoly
for (int i = 0; i < 8; i += 2) {
if (parentX + diagnoly[i] <= width && parentX + diagnoly[i] >= 0 && parentY + diagnoly[i + 1] <= height && parentY + diagnoly[i + 1] >= 0) {
if (!isBlockObstacle(parentX + diagnoly[i], parentY + diagnoly[i + 1])) {
addToList = new Node(parentX + diagnoly[i], parentY + diagnoly[i + 1], parent.g + 14, 0, 0, null);
if (!closedList.contains(addToList)) {
movedX = parentX + diagnoly[i];
movedY = parentY + diagnoly[i + 1];
calculate(h, addToList.g, f, movedX, movedY, addToList);
checkNode(addToList, 14);
}
}
}
}
}
public void FullSolve() {
Node finalPath = null;
if (firstIter) {
countCost();
chooseNode();
openList.remove(parent);
closedList.add(parent);
parent = lowestCost;
openList.remove(parent);
closedList.add(parent);
firstIter = false;
FullSolve();
} else {
while (true) {
if (openList.isEmpty()) {
break;
}
countCost();
HashSet<Node> copy = new HashSet<>(openList);
for (Node nfo : copy) {
parent = nfo;
countCost();
closedList.add(parent);
openList.remove(parent);
if (parent.x == targetX && parent.y == targetY) {
finalPath = parent;
break;
}
}
chooseNode();
openList.remove(parent);
closedList.add(parent);
parent = lowestCost;
lowestCost.f = Integer.MAX_VALUE;
if (parent.x == targetX && parent.y == targetY) {
finalPath = parent;
break;
}
}
}
}
}
The testing was done with pregenerated map files. I have 14 map files each of them contains a 100 maps with specific size. With lowest one being map by 10 * 10 and highest being by 500 * 500.
Also note that if each map has 100 examples it means that algorithm was tested 100 times to work with one specific size, furthermore I wanted to increase accuracy even more so I repeat whole process 10 times. Which gives me 1000 test with one map. I of course average those times.
I'm not really familiar with high accuracy time measuring methods so I used StopWatch() in both Java and C# (To use it in Java I downloaded it from apache commons). What I did was after reading one map information I called function measureTime() and started StopWatch() then call Algorithm class and make it solve puzzle after that I'd stop StopWatch() and take time.
Here are the results I got:
I'm posting image because I'm not sure how to make a table here. Times are in second, how much it took to solve one map in average.
Note after "-" symbol there is map size. (Maps-20.txt means map by 20 * 20 and so on)
Also a graph:
These results really surprised me, I was expecting one language having a bit of an advantage, but not like this. After update C# graph looks similar to Java graph, but has steeper growth rate. First I thought that I made some mistake while copying algorithm to Java (Firstly I wrote in C#), but I couldn't find any. So assuming that I didn't make some silly mistake.
How can I improve C# performance even more?
Also one thing I thought about getting these results that in Dictionary<int, List<int>> instead of using List<int> I could use HashSet<int> since I only need to confirm if element exists or not. But as I am not dealing with thousands of elements I don't think that it could be major factor.
Thanks.
I'm writing automation scripts for a popular game, and part of it requires a pathfinder to walk extended distances.
I wrote this A* pathfinder to achieve this, but there are a few problems with it:
Sometimes, the pathfinder will go into unwalkable tiles which will eventually lead to it going to the top corners of the map and then out of bounds
The pathfinder will also get lost and go a long way away from the desired position and then back, leaving a path of 10000+ nodes even though there's only a distance of about 100 tiles sometimes
Here is my AStar.java:
package Webwalker;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.osbot.rs07.api.map.Position;
import org.osbot.rs07.script.Script;
import Pickpocketer.PickpocketScript;
public class AStar {
List<Position> open, closed;
List<PathTile> holdingList;
Position s, e, current;
int horizontalCost, diagonalCost, index;
Script scr;
public AStar(Script script) {
open = new ArrayList<Position>();
closed = new ArrayList<Position>();
holdingList = new ArrayList<PathTile>();
horizontalCost = 10;
index = 0;
diagonalCost = 14;
scr = script;
}
public List<Position> findPath(Position start, Position end) {
s = current = start;
closed.add(s);
holdingList.add(new PathTile(start, 0, 0, null));
e = end;
while (!nextToEnd()) {
//scr.log("Finding next node");
PathTile[] nodes = getAdjacent(current);
PathTile node = getLeastCost(nodes);
current = node.p;
if (!node.walkable) {
scr.log("Node " + node.p + " - f " + node.f);
}
closed.add(current);
open.remove(current);
Position mapP = PickpocketScript.map.getMapPos(current);
int x = mapP.getX();
int y = mapP.getY();
PickpocketScript.map.drawPixel(x, y, node.walkable);
//holdingList.add(node);
//scr.log(current);
index++;
}
/*scr.log("Cleaning nodes!");
List<Position> actualPath = new ArrayList<Position>();
for (PathTile pt : holdingList) {
if (pt == null || pt.parent == null) continue;
if (pt.parent.p.equals(start)) {
actualPath.add(pt.parent.p);
break;
}
if (pt.p.equals(start)) break;
actualPath.add(pt.parent.p);
Position mapP = PickpocketScript.map.getMapPos(pt.parent.p);
int x = mapP.getX();
int y = mapP.getY();
PickpocketScript.map.drawPixel(x, y, pt.parent.walkable);
}*/
scr.log("Finished finding " + closed.size() + " nodes");
//Collections.reverse(actualPath);
return closed;
}
public boolean nextToEnd() {
int mh = ManhattenValue(current);
return (mh < 21);
//return false;
}
public int[] distance(Position a, Position b) {
int x = Math.abs(a.getX() - b.getX());
int y = Math.abs(a.getY() - b.getY());
return new int[] { x, y };
}
public PathTile getLeastCost(PathTile[] nodes) {
int lowestIndex = 0;
int lowestF = Integer.MAX_VALUE;
for (int i = 0; i < nodes.length; i++) {
//if (!nodes[i].walkable) scr.log("Node " + nodes[i].p + " - f " + nodes[i].f +" getleastcost");
if (nodes[i] != null && nodes[i].walkable && nodes[i].f < lowestF && !closed.contains(nodes[i].p)) {
lowestF = nodes[i].f;
lowestIndex = i;
}
}
//scr.log(lowestIndex);
return nodes[lowestIndex];
}
public PathTile[] getAdjacent(Position p) {
PathTile[] nodes = new PathTile[8];
int x = p.getX();
int y = p.getY();
PathTile old = null;
if (holdingList.size() > 0) old = holdingList.get(holdingList.size() - 1);
Position newNode = new Position(x - 1, y - 1, p.getZ()); //top left
if (!open.contains(newNode)) open.add(newNode);
int h = OctileValue(newNode);
int g = diagonalCost;
nodes[0] = new PathTile(newNode, g, h, old);
//nodes[0] = nodes[2] = nodes[4] = nodes[5] = nodes[7] = null;
newNode = new Position(x, y - 1, p.getZ()); //top centre
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = horizontalCost;
nodes[1] = new PathTile(newNode, g, h, old);
newNode = new Position(x + 1, y - 1, p.getZ()); //top right
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = diagonalCost;
nodes[2] = new PathTile(newNode, g, h, old);
newNode = new Position(x - 1, y, p.getZ()); //centre left
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = horizontalCost;
nodes[3] = new PathTile(newNode, g, h, old);
/* Centre centre would be "p", so we skip it (we are not moving back to p) */
newNode = new Position(x + 1, y, p.getZ()); //centre right
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = horizontalCost;
nodes[4] = new PathTile(newNode, g, h, old);
newNode = new Position(x - 1, y + 1, p.getZ()); //bottom left
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = diagonalCost;
nodes[5] = new PathTile(newNode, g, h, old);
newNode = new Position(x, y + 1, p.getZ()); //bottom centre
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = horizontalCost;
nodes[6] = new PathTile(newNode, g, h, old);
newNode = new Position(x + 1, y + 1, p.getZ()); //bottom right
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = diagonalCost;
nodes[7] = new PathTile(newNode, g, h, old);
/*PathTile[] temp = nodes.clone();
for (int i = 0; i < nodes.length; i++) {
if (!temp[i].walkable) {
nodes[i] = null;
}
}*/
return nodes;
}
public int OctileValue(Position start) { //This is our "H" value
int x = start.getX();
int y = start.getY();
int horizontal = Math.abs(x - e.getX());
int vertical = Math.abs(y - e.getY());
//return Math.max(horizontal, vertical) * horizontalCost; //bad algorithm lol
//return (horizontal + vertical) * horizontalCost; //Manhatten
//double tieBreaker = (1.0 + (diagonalCost / ManhattenValue(start)));
if (horizontal == 0 && vertical == 0) return 0;
//int h = horizontalCost * (horizontal + vertical) + (diagonalCost - 2 * horizontalCost) * Math.min(horizontal, vertical);
int h = Math.max(horizontal, vertical) + (diagonalCost - horizontalCost) * Math.min(horizontal, vertical);
//h *= (tieBreaker);
return h; //Octile distance
}
public int ManhattenValue(Position start) {
int x = start.getX();
int y = start.getY();
int horizontal = Math.abs(x - e.getX());
int vertical = Math.abs(y - e.getY());
return (horizontal + vertical) * horizontalCost;
}
}
class PathTile {
public Position p;
public int g, h, f;
public boolean walkable;
public PathTile parent;
public PathTile(Position pos, int G, int H, PathTile par) {
p = pos;
g = G;
h = H;
if (par != null) {
g += par.g;
parent = par;
}
if (h == 0) f = 0;
else f = g + h;
System.out.println(p + " - g " + g + " - h " + h + " - f " + f);
walkable = (PickpocketScript.map.isWalkable(pos));
if (!walkable) {
f = Integer.MAX_VALUE;
}
}
}
I know that these tiles are unwalkable because they have an F value of int.maxvalue (as defined in PathTile class)
A Position in this scenario simply has an int for x,y,z.
I'm not too sure how to fix these problems, as I'm quite new to writing algorithms like this :)
Your findPath() method looks like it's performing a greedy best-first search. Check the line PathTile node = getLeastCost(nodes);... You are selecting the least-cost node from among the neighbors of the current PathTile, not from among the entries in the open set. Instead, use a PriorityQueue sorted by f-value for the open list and get your next node from the head of the open list.
Additional Resources: I recommend you follow Wikipedia's A* Search Algorithm Pseudocode. Also, you may want to check out the introduction to A* at Redblobgames.com.
Final Consideration: When working with pathfinding in a grid graph, it is helpful to use an enum to define the directions. This makes your code more readable and maintainable. Here's an example:
enum Direction{
TOP_LEFT(-1,-1,diagonalCost),
TOP_CENTER(0,-1,horizontalCost),
TOP_RIGHT(1,-1,diagonalCost),
MIDDLE_LEFT(-1,0,horizontalCost),
MIDDLE_RIGHT(1,0,horizontalCost),
BOTTOM_LEFT(-1,1,diagonalCost),
BOTTOM_CENTER(0,1,horizontalCost),
BOTTOM_RIGHT(1,1,diagonalCost);
public final int x;
public final int y;
public final int cost;
Direction(int x, int y, int cost){
this.x = x;
this.y = y;
this.cost = cost;
}
}
Your (uncorrected) getAdjacent method can be simplified to the following:
public PathTile[] getAdjacent(Position p) {
PathTile[] nodes = new PathTile[8];
int x = p.getX();
int y = p.getY();
PathTile old = null;
if (holdingList.size() > 0) old = holdingList.get(holdingList.size() - 1);
for(Direction d: Direction.values()){
newNode = new Position(x+ d.x, y + d.y, p.getZ()); //top centre
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = d.cost;
nodes[d.ordinal()] = new PathTile(newNode, g, h, old);
}
return nodes;
}
I am in the process of making a GUI which shows three JToolBars above a big JPanel. These toolbars are collectively very large, so I'm using a FlowLayout to make them wrap to the next line if they reach the JFrame border. The problem is that when they wrap to the next line, they become hidden by the JPanel below.. I wish I could force the JPanel containing the toolbars to grow enough to show all toolbars..
Is there a way to do this? Or is there another way to make these toolbars visible?
I have run into this problem before. I found the best solution is to use a modified version of FlowLayout that takes into account vertical changes and wraps them to the next line. Here is the code for such a layout.
import java.awt.*;
/**
* A modified version of FlowLayout that allows containers using this
* Layout to behave in a reasonable manner when placed inside a
* JScrollPane
* #author Babu Kalakrishnan
* Modifications by greearb and jzd
*/
public class ModifiedFlowLayout extends FlowLayout {
public ModifiedFlowLayout() {
super();
}
public ModifiedFlowLayout(int align) {
super(align);
}
public ModifiedFlowLayout(int align, int hgap, int vgap) {
super(align, hgap, vgap);
}
public Dimension minimumLayoutSize(Container target) {
// Size of largest component, so we can resize it in
// either direction with something like a split-pane.
return computeMinSize(target);
}
public Dimension preferredLayoutSize(Container target) {
return computeSize(target);
}
private Dimension computeSize(Container target) {
synchronized (target.getTreeLock()) {
int hgap = getHgap();
int vgap = getVgap();
int w = target.getWidth();
// Let this behave like a regular FlowLayout (single row)
// if the container hasn't been assigned any size yet
if (w == 0) {
w = Integer.MAX_VALUE;
}
Insets insets = target.getInsets();
if (insets == null){
insets = new Insets(0, 0, 0, 0);
}
int reqdWidth = 0;
int maxwidth = w - (insets.left + insets.right + hgap * 2);
int n = target.getComponentCount();
int x = 0;
int y = insets.top + vgap; // FlowLayout starts by adding vgap, so do that here too.
int rowHeight = 0;
for (int i = 0; i < n; i++) {
Component c = target.getComponent(i);
if (c.isVisible()) {
Dimension d = c.getPreferredSize();
if ((x == 0) || ((x + d.width) <= maxwidth)) {
// fits in current row.
if (x > 0) {
x += hgap;
}
x += d.width;
rowHeight = Math.max(rowHeight, d.height);
}
else {
// Start of new row
x = d.width;
y += vgap + rowHeight;
rowHeight = d.height;
}
reqdWidth = Math.max(reqdWidth, x);
}
}
y += rowHeight;
y += insets.bottom;
return new Dimension(reqdWidth+insets.left+insets.right, y);
}
}
private Dimension computeMinSize(Container target) {
synchronized (target.getTreeLock()) {
int minx = Integer.MAX_VALUE;
int miny = Integer.MIN_VALUE;
boolean found_one = false;
int n = target.getComponentCount();
for (int i = 0; i < n; i++) {
Component c = target.getComponent(i);
if (c.isVisible()) {
found_one = true;
Dimension d = c.getPreferredSize();
minx = Math.min(minx, d.width);
miny = Math.min(miny, d.height);
}
}
if (found_one) {
return new Dimension(minx, miny);
}
return new Dimension(0, 0);
}
}
}
Take a look at WrapLayout. It worked for me. Here is the code.