Java vs C# speed comparison in path search algorithm - java

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.

Related

My sorting algorithms are only going through once

I am writing a program that sorts 2 arrays of the same values by using two different algorithms. It loops through the arrays once each time a button is clicked. My problem is that it only sorts the first time you click the button. Every other time nothing happens. I put a count incrementer to see if the code was running and it was. I'm stumped. Any help will be appreciated.
I fixed the selection sort.
public class TwoSortsPanel extends JPanel
{
private int width = 10, gap = 3, x = 200, y = 50;
private int[] ranArr1 = new int[15], ranArr2 = new int[15];
private Color blue = Color.blue, red = Color.red, pink = Color.pink, gray = Color.gray;
public static int count = 0, indexSel = 0;
{
for(int i = 0; i < ranArr1.length-1; i++)
{
ranArr1[i] = (int) (Math.random() * 15);
ranArr2[i] = ranArr1[i];
}
}
public TwoSortsPanel()
{
printArray(ranArr1);
setBackground(pink);
setPreferredSize (new Dimension (400,300));
JButton sort = new JButton ("Sort");
sort.addActionListener(new ButtonListener());
add (sort);
}
public static void printArray(int[] c)
{
System.out.println(Arrays.toString(c));
}
public void paintComponent (Graphics page)
{
super.paintComponent(page);
page.drawString("" + count, 10, 12);
if (insertion() == false || selection() == false)
{
int yPlus = 50;
for(int i = 0; i < ranArr1.length; i++)
{
page.setColor(blue);
page.fillRect(x, yPlus, ranArr1[i]*10, width);
yPlus += width + gap;
}
yPlus = 50;
for(int i = 0; i < ranArr2.length; i++)
{
page.setColor(red);
page.fillRect(x, yPlus, -ranArr2[i]*10, width);
yPlus += width + gap;
}
}
else
{
int yPlus = 50;
for(int i = 0; i < ranArr1.length; i++)
{
page.setColor(gray);
page.fillRect(x, yPlus, ranArr1[i]*10, width);
yPlus += width + gap;
}
yPlus = 50;
for(int i = 0; i < ranArr2.length; i++)
{
page.setColor(gray);
page.fillRect(x, yPlus, -ranArr2[i]*10, width);
yPlus += width + gap;
}
}
}
private class ButtonListener implements ActionListener
{
public void actionPerformed (ActionEvent e)
{
displaySelection(ranArr1);
displayInsertion(ranArr2);
count++;
printArray(ranArr1);
repaint();
}
}
public static void displaySelection(int[] a)
{
int temp;
int min = indexSel;
for (int scan = indexSel+1; scan < a.length ; scan++)
if (a[scan] < a[min])
min = scan;
temp = a[min];
a[min] = a[indexSel];
a[indexSel] = temp;
count++;
indexSel++;
}
public static void displayInsertion(int[] b)
{
int index = 1;
int key = b[index];
int position = index;
while (position > 0 && b[position-1] > key )
{
b[position] = b[position-1];
position--;
}
b[position] = key;
}
public boolean selection()
{
for (int i = 0; i < ranArr1.length-1; i++)
{
if(ranArr1[i] > ranArr1[i+1])
{
return false;
}
}
return true;
}
public boolean insertion()
{
for (int i = 0; i < ranArr2.length-1; i++)
{
if(ranArr2[i] > ranArr2[i+1])
{
return false;
}
}
return true;
}
}
Sorry. I didn't want the post to look too busy and I guess it lost meaning.

Java - Get value between 1&0 from pixels on a bufferedimage (Heightmap)

Im trying to implement Heightmaps to my game, so now i have made several methods, but they are so slow and also, the height returns numbers like 1.463563298e14!
How can i get a value between 0&1 from the pixel on the heightmap?
These are my methods:
public void setupHeights() {
int pixWidth = heightfile.getWidth();
int pixHeight = heightfile.getHeight();
int w = pixWidth;
int h = pixHeight;
this.heights = new float[w][h];
boolean countWidth = true;
for (int z = 0; z < pixHeight; z++) {
depth++;
for (int x = 0; x < pixWidth; x++) {
if (countWidth)
width++;
int height;
if (whiteHigh) {
height = 256 - (-1 * heightfile.getRGB(x, z));
} else {
height = -1 * heightfile.getRGB(x, z);
}
heights[x][z] = height;
numPoints++;
}
countWidth = false;
}
System.out.println("Heights have been set up!");
}
#Override
public void update() {
if (!this.hasWorldObj())
return;
if (heightfile!=null) {
for (Entity e : entities) {
if (e != null && !(e.posX < pos.getX() || e.posZ < pos.getZ() || e.posX > pos.getX() + 1
|| e.posZ > pos.getZ() + 1)) {
{
if (heightfile != null) {
double localX = Math.abs(pos.getX() - e.posX);
double localZ = Math.abs(pos.getZ() - e.posZ);
int x = (int) (localX * width);
int y = (int) (localZ * depth);
e.posY = pos.getY() + getHeight(x, y);
}
}
} else
e = null;
}
}
}
public float getHeight(float xf, float zf) {
int x = worldCoordToIndex(xf);
int z = worldCoordToIndex(zf);
System.out.println("getting height for: " + x + ", " + z);
if (x < 0)
x = 0;
if (z < 0)
z = 0;
if (z >= heights.length) {
System.out.println("WARN getHeight z index out of bounds: " + z);
z = heights.length - 1;
}
if (x >= heights[z].length) {
System.out.println("WARN getHeight x index out of bounds: " + x);
x = heights[z].length - 1;
}
System.out.println("Returned height: " + heights[z][x] * heightScale);
return heights[z][x] * heightScale;
}
private int worldCoordToIndex(float f) {
return (int) Math.floor(f / widthScale);
}

Java - A* algorithm walks into unwalkable tiles

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;
}

B spline basis function seems to produce incorrect values

i wanted to implement B-splines in an Java Swing application. And since 4. May i tried nearly everything and spend every minute of my free time, but i don't get them right - i have headach since yesterday :/
To implement the B spline i used the specification from wikipedia
And i made (relating to MCVE) an demo version which can be found here: gist.github.com/soraphis/b-spline
the code shows the problem, its made to be called in an swing JPanel drawcomponent method. But you can comment this line and uncomment 71.
Some additional informations:
a and b in my basis function returns values < 0 or >1 which should not be (see wikipedia)
i want to implement the b splines myself - i dont want to use a library
referencinc to the wikipedia artikel:
basisFunc is B(x)
DeBoor is S(x)
i rly need help with the basis function, and i would like to know how to build up the knot vector "correctly"
Im thankful for every kind of reply
and thx for reading this
I can't imagine an appropriate answer that does not consist of a piece of code that you just can compile and run (without having to insert a main method and the surrounding GUI ... * wink *), regardless of how different it is from your code.
However, the weights that you have assigned to your T-array seemed odd, and the indices of the basis function seemed to be off +/-1. I tried to fix this, but it's still not entirely correct, maybe you or someone else likes to continue debugging this.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.util.Arrays;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class BSplineTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new GridLayout(1, 1));
BSplineTestPanel bSplineTestPanel = new BSplineTestPanel();
frame.getContentPane().add(bSplineTestPanel);
frame.setSize(500,500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class BSplineTestPanel extends JPanel
{
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Bspline bspline = new Bspline();
bspline.calculatePath(g);
}
}
class Bspline
{
double[][] P;
double[] T;
int _k = 3;
public Bspline()
{
P = new double[4][2];
P[0][0] = 100;
P[0][1] = 100;
P[1][0] = 50;
P[1][1] = 50;
P[2][0] = 100;
P[2][1] = 200;
P[3][0] = 50;
P[3][1] = 200;
update();
}
private void update()
{
if (P.length < 2)
return;
T = new double[_k + P.length + 1];
double d = 1.0 / (T.length-1);
for (int i = 0; i<T.length; i++)
{
T[i] = i * d;
}
System.out.println(Arrays.toString(T));
}
private double basisFunc(int i, int k, double t)
{
if (k == 0)
{
if (T[i] <= t && t < T[i + 1])
return 1;
return 0;
}
double a = (t - T[i]) / (T[i + k] - T[i]);
double b = (T[i + k + 1] - t) / (T[i + k + 1] - T[i + 1]);
return a * basisFunc(i, k - 1, t) + b * basisFunc(i + 1, k - 1, t);
}
private double[] DeBoor(double t)
{
double[] V = new double[2];
for (int i = 0; i < P.length; i++)
{
double scale = basisFunc(i, _k, t);
V[0] += P[i][0] * scale;
V[1] += P[i][1] * scale;
}
return V;
}
public void calculatePath(Graphics g)
{
if (P.length < 2)
{
return; // zu wenige punkte um ein pfad zu zeichnen
}
double[] v = null;
double delta = 1 / 32.0;
for (double t = T[2]; t < T[5]; t += delta)
{
double[] p = DeBoor(t);
if (v != null)
{
g.setColor(new Color((int)(t*255), 0, 0));
g.drawLine((int) v[0], (int) v[1], (int) p[0], (int) p[1]);
}
v = p;
}
for (int i = 0; i < P.length; i++)
{
int x = (int)P[i][0];
int y = (int)P[i][1];
g.setColor(Color.RED);
g.fillOval(x-2, y-2, 4, 4);
g.drawString(String.valueOf(i), x, y+15);
}
}
}
(Compare to http://www.ibiblio.org/e-notes/Splines/basis.html, "Quadratic B-spline (n = 3, k = 3)")
Apart from that I wonder how such an overly complicated **** like deBoors algorithm could become so "famous". Use De-Casteljau and you'll be done in a few minutes, without a single debugging run.
EDIT A port of the code from http://chi3x10.wordpress.com/2009/10/18/de-boor-algorithm-in-c/
// From http://chi3x10.wordpress.com/2009/10/18/de-boor-algorithm-in-c/
double[] deBoor(int k,int degree, int i, double x, double knots[], double ctrlPoints[][])
{
if( k == 0)
{
i = Math.max(0, Math.min(ctrlPoints.length-1, i));
return ctrlPoints[i];
}
else
{
double alpha = (x-knots[i])/(knots[i+degree+1-k]-knots[i]);
double p0[] = deBoor(k-1,degree, i-1, x, knots, ctrlPoints);
double p1[] = deBoor(k-1,degree, i, x, knots, ctrlPoints);
double p[] = new double[2];
p[0] = p0[0] *(1-alpha ) + p1[0]*alpha;
p[1] = p0[1] *(1-alpha ) + p1[1]*alpha;
return p;
}
}
int WhichInterval(double x, double knot[], int ti)
{
int index = -1;
for(int i = 1; i <= ti - 1; i++)
{
if(x < knot[i]) {
index = i - 1;
break;
}
}
if(x == knot[ti - 1]) {
index = ti - 1;
}
return index;
}
private double[] DeBoor(double t)
{
int i = WhichInterval(t, T, T.length);
return deBoor(_k, 3, i, t, T, P);
}

Java Tetris - Confused on grid[row][col] and coordinates (x, y)

I'm making Tetris in java for fun... I pretty much had everything working... but later found out that when I wanted to change the dimensions so it was square ([10 row][10 col] matrix, but instead a [12 row][10 col] matrix), that I started getting Index Out of Bound exceptions... see here: Java Tetris - weird row clearing issue
So I tried fixing everything so that the rows and columns weren't flip flopped... But am now getting hung up on the fact that the grid takes [row][col], but I’m moving around the tiles as (x, y) coordinates…
What’s confusing me is that row = y and col = x… which is reversed… so when I pass in coordinates I’m not sure when to swap them.
I know it’s a simple thing, but it’s confusing the hell out of me and I keep getting out of bounds exceptions whenever I think I have it right.
I'm not sure where the exact issue is, so I'm posting a full Sscce of my program... I think the issue is in the Board class...
Here, the block should still be able to move down... but if it tries to go down further than this...
I get:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 10
at Board.getTileAt(Board.java:177)
at Tile.collision(Tile.java:31)
at Piece.isCollision(Piece.java:172)
at Board.collisionCheck(Board.java:192)
at Piece.movePieceCheck(Piece.java:87)
at Board.keyPressed(Board.java:160)
Sscce:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MainSscce extends JPanel {
static MainSscce runMe;
BoardSscce gameBoard, scoreBoard;
public MainSscce() { //creates a new frame window and sets properties
JFrame f = new JFrame("Tetris");
//width (height), length, tilesize
gameBoard = new BoardSscce(12, 10, 35);
// scoreBoard = new BoardSscce(10, 10, 35);
f.add(gameBoard);
f.setSize(gameBoard.getWidth(), gameBoard.getHeight());
f.setVisible(true);
f.setResizable(false);
f.setVisible(true);
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize();
//set j frame location to appear in middle of screen
f.setLocation( (screensize.width - f.getWidth())/2,
(screensize.height - f.getHeight())/2-100 );
}
public static void main(String[] args) {
runMe = new MainSscce();
}
}
import java.awt.Graphics;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.event.*; // for ActionListener and ActionEvent
import java.util.Random;
public class BoardSscce extends JPanel implements KeyListener {
private TileSscce grid[][];
private int totalRows, totalCols, tilesize, level, totalScore;
private final int changeLevelMultiplier;
private PieceSscce newPiece, nextPiece;
private String randomPiece;
private boolean gameLost;
public BoardSscce(int r, int c, int ts) {
totalRows = r;
totalCols = c;
tilesize = ts;
//set grid size to [# rows][# columns], aka [height][width]
grid = new TileSscce[totalRows][totalCols];
gameLost = false;
System.out.println("TotalRows: " + totalRows + ", " + "TotalCols: " + totalCols);
//multiplier to determine what score the level changes, which is:
//level * changeLevelMultiplier;
changeLevelMultiplier = 40;
//initialize score to 0
totalScore = 0;
//initialize level to 0
level = 0;
newPiece = new PieceSscce(this, randomPiece(), getColor());
addKeyListener(this);
setFocusable(true);
//getTranspose();
timer();
}
public String randomPiece() {
String[] Pieces = {"L", "O", "Z", "RevZ", "Bar", "T", "RevL"};
int rand = (int) (Math.random() * Pieces.length);
randomPiece = Pieces[rand];
return randomPiece;
}
public Color getColor() {
Color color;
if (randomPiece.equals("L"))
color = new Color(17, 255, 0);
else if(randomPiece.equals("O"))
color = new Color(117, 168, 255);
else if(randomPiece.equals("Z"))
color = new Color(255, 187, 82);
else if(randomPiece.equals("RevZ"))
color = new Color(206, 27, 72);
else if(randomPiece.equals("Bar"))
color = new Color(50, 216, 219);
else if(randomPiece.equals("T"))
color = new Color(252, 148, 240);
else
color = new Color(255, 255, 52);
//Random rand = new Random();
//float r = rand.nextFloat();
//float g = rand.nextFloat();
//float b = rand.nextFloat();
//Color randomColor = new Color(r, g, b);
return color;
}
//dimensions of board = width * tilesize
public int getWidth() {
return totalCols * tilesize;
}
public int getHeight() {
return totalRows * tilesize;
}
public int getTileSize() {
return tilesize;
}
public void paintComponent(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
for(int row = 0; row < grid.length; row++) {
for(int col = 0; col < grid[row].length; col++) {
//System.out.println(row + ", " + col);
g.drawString("[" + row + "][" + col + "]", col * tilesize, row * tilesize+10);
System.out.println(row + ", " + col);
//if there is a non-null space, that is a Tetris piece... fill it
if(grid[row][col] != null) {
g.setColor(grid[row][col].getColor());
g.fillRect(row * tilesize, col * tilesize, tilesize, tilesize);
g.setColor(Color.WHITE);
}
}
}
// g.drawString("Level: " + level, this.getWidth()/2, this.getHeight()/2-130);
// g.drawString("Score: " + totalScore, this.getWidth()/2, this.getHeight()/2-100);
if (gameLost == true) {
g.drawString("Way to go, loser...", this.getWidth()/2, this.getHeight()/2);
messageTimer();
}
}
//Auto move piece
public void timer () {
int interval;
switch (level) {
//each level increases drop speed by .10 seconds
case 1: interval = 800;
break;
case 2: interval = 700;
break;
case 3: interval = 600;
break;
case 4: interval = 500;
break;
default: interval = 1000;
break;
}
Timer t = new Timer(interval, new ActionListener() {
public void actionPerformed(ActionEvent e) {
//newPiece.autoMove();
//repaint();
}
});
t.start();
}
public void messageTimer() {
Timer t = new Timer(5000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
gameLost = false;
}
});
t.start();
}
//move piece on key input
public void keyPressed(KeyEvent e) {
newPiece.movePieceCheck(e.getKeyCode());
repaint();
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public boolean isValidCoordinate(int x, int y) {
return x >= 0 && y >= 0 && x < totalCols && y < totalRows;
}
// returns the tile at (x, y) or null if empty
public Tile getTileAt(int x, int y) {
if(isValidCoordinate(x, y))
return grid[x][y];
return null;
}
// sets the tile at (x, y) to tile
public void setTileAt(Tile tile, int x, int y) {
if(isValidCoordinate(x, y))
grid[x][y] = tile;
}
public boolean isOpen(int x, int y) {
return isValidCoordinate(x, y) && (getTileAt(x, y) == null);
}
public void collisionCheck() {
if (newPiece.isCollision()){
newPiece = new PieceSscce(this, randomPiece(), getColor());
}
}
public void changeLevel () {
int max = (level+1)*changeLevelMultiplier;
if (totalScore >= max) {
System.out.println(max + "reached... next level");
level++;
totalScore = 0;
timer();
}
}
public int tallyScore(int totalLines) {
int score = 0;
switch (totalLines) {
case 1: score = 40 * (level + 1);
break;
case 2: score = 100 * (level + 1);
break;
case 3: score = 300 * (level + 1);
break;
case 4: score = 1200 * (level + 1);
break;
default: break;
}
return score;
}
//loop through all rows starting at bottom (12 rows)
public void checkBottomFull() {
int lines = 0;
for(int row = 12; row > 0; row--) {
/* while (isFull(row)) {
lines++;
// clearRow(row);
}*/
}
totalScore += tallyScore(lines);
//check if level needs to be changed based on current score...
changeLevel();
//reset lines after score has been incremented
lines=0;
}
//loop through all columns in that row (10 columns)
public boolean isFull(int row) {
for (int col = 0; col <= 10; col++) {
System.out.println(row + ", " + col);
if(grid[row][col] == null) {
return false;
}
}
return true;
}
public void clearRow(int rowToClear) {
for(int row = rowToClear; row > 0; row--) {
for(int col = 0; col < grid[row].length; col++) {
grid[col][row] = grid[col][row-1];
}
}
}
public void checkEndGame(int x, int y) {
//if currPiece y location = 0 AND the space below is filled...
if (y <= 2 && !isOpen(x, y+1)) {
gameLost = true;
level = 0;
totalScore = 0;
//reset timer
timer();
for(int row = 0; row < grid.length; row++) {
for(int col = 0; col < grid[row].length; col++) {
grid[row][col] = null;
}
}
}
}
}
import java.awt.Color;
import java.awt.event.KeyEvent;
public class PieceSscce {
public int[] pieceCoordinates;
public String shape, currRotation;
public Color color;
public BoardSscce board;
public int rotationsCounter;
public TileSscce tile[];
public int[] newPositionX, newPositionY, currPositionX, currPositionY;
//don't need to pass in board because I'm already utilizing the Tiles class, which knows about the board
public Piece(Board b, String randomPiece, Color randomColor) {
shape = randomPiece;
color = randomColor;
board = b;
newPositionX = new int[4];
newPositionY = new int[4];
currPositionX = new int[4];
currPositionY = new int[4];
pieceCoordinates = new int[8];
//set pieceCoordinates global variable
getShape(shape);
tile = new TileSscce[4];
int counterX = 0, counterY = 1;
System.out.print("\"" + shape + "\" Coordinates: ");
//generate 4 new Tiles at specified coordinates that will compose the Piece
for (int i = 0; i < tile.length; i++) {
tile[i] = new TileSscce(board, pieceCoordinates[counterX], pieceCoordinates[counterY]);
System.out.print("(" + pieceCoordinates[counterX] + ", " + pieceCoordinates[counterY] + ") ");
//increment by 2 because x,y values are next to each other in array
counterX+=2;
counterY+=2;
}
System.out.println("\n");
for (int i = 0; i < tile.length; i++) {
tile[i].setColor(color);
}
}
public void calcNewPosition(int newX, int newY, int currTile) {
newPositionX[currTile] = newX;
newPositionY[currTile] = newY;
}
public void clearCurrPosition() {
for (int i = 0; i < tile.length; i++) {
currPositionX[i] = tile[i].getX();
currPositionY[i] = tile[i].getY();
board.setTileAt(null, currPositionX[i], currPositionY[i]);
}
}
public void autoMove() {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getX(), tile[i].getY()+1, i);
}
clearCurrPosition();
for (int i = 0; i < tile.length; i++) {
board.checkEndGame(tile[i].getX(), tile[i].getY());
System.out.println("Checking..." + tile[i].getX() + ", " + tile[i].getY());
}
board.checkBottomFull();
board.collisionCheck();
move();
}
public void movePieceCheck(int keycode) {
if (keycode == KeyEvent.VK_DOWN) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getX(), tile[i].getY()+1, i);
}
clearCurrPosition();
for (int i = 0; i < tile.length; i++) {
board.checkEndGame(tile[i].getX(), tile[i].getY());
System.out.println("Checking..." + tile[i].getX() + ", " + tile[i].getY());
}
board.checkBottomFull();
board.collisionCheck();
move();
}
if (keycode == KeyEvent.VK_RIGHT) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getX()+1, tile[i].getY(), i);
}
clearCurrPosition();
move();
}
if (keycode == KeyEvent.VK_LEFT) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getX()-1, tile[i].getY(), i);
}
clearCurrPosition();
move();
}
//rotate left
if (keycode == KeyEvent.VK_A) {
int[] rotatedCoords = calcRotation("left");
clearCurrPosition();
rotate(rotatedCoords, "left");
}
//rotate right
if (keycode == KeyEvent.VK_D) {
int[] rotatedCoords = calcRotation("right");
clearCurrPosition();
rotate(rotatedCoords, "right");
}
}
public boolean movePieceValid() {
boolean valid = true;
for (int i = 0; i < tile.length; i++) {
if(!tile[i].checkNewLocation(newPositionX[i], newPositionY[i]))
valid = false;
}
return valid;
}
public boolean validRotation(int[] rotatedCoordinates) {
boolean valid = true;
int counterX = 0, counterY = 1;
for (int i = 0; i < tile.length; i++) {
if(!tile[i].checkNewLocation(rotatedCoordinates[counterX], rotatedCoordinates[counterY]))
valid = false;
counterX +=2;
counterY +=2;
}
return valid;
}
public void move() {
if (movePieceValid()) {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(newPositionX[i], newPositionY[i]);
}
} else {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(currPositionX[i], currPositionY[i]);
}
}
}
public void rotate(int[] rotatedCoordinates, String rotation) {
int counterX = 0, counterY = 1;
if (validRotation(rotatedCoordinates)) {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(rotatedCoordinates[counterX], rotatedCoordinates[counterY]);
counterX+=2;
counterY+=2;
}
//else, if not valid move set the original location
} else {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(currPositionX[i], currPositionY[i]);
}
}
}
public boolean isCollision() {
boolean collision = false;
for (int i = 0; i < tile.length; i++) {
if(tile[i].collision(newPositionX[i], newPositionY[i])) {
collision = true;
}
}
return collision;
}
//calc curr coordinates, send them to getRotation... which will create new piece based on coords
public int[] calcRotation(String direction) {
for (int i = 0; i < tile.length; i++) {
currPositionX[i] = tile[i].getX();
currPositionY[i] = tile[i].getY();
System.out.println("Current position: (" + currPositionX[i] + "," + currPositionY[i]+")");
}
return getRotation(currPositionX, currPositionY, direction);
}
public int[] getRotation (int coordinatesX[], int coordinatesY[], String direction) {
int[] rotationDirection;
int[] coordinates = new int[8];
int[] origin = new int[2];
int[] newCoordinates = new int[8];
int[] resultCoordinates = new int[8];
int[] finalCoordinates = new int[8];
int vectorMatrix[][] = new int[2][4];
//set either R(90) or R(-90) rotation matrix values:
if (direction.equals("right")) {
rotationDirection = new int[] {0, -1, 1, 0};
}
else {
rotationDirection = new int[] {0, 1, -1, 0};
}
int counterX = 0, counterY = 1, x = 0;
while (counterY < coordinates.length) {
//add arrays coordinatesX and coordinatesY into a single array: coordinates
coordinates[counterX] = coordinatesX[x];
coordinates[counterY] = coordinatesY[x];
counterX+=2;
counterY+=2;
x++;
}
//set origin so it rotates around center...
if (shape.equals("RevZ")) {
origin[0] = coordinates[6];
origin[1] = coordinates[7];
}
else if (shape.equals("T")) {
origin[0] = coordinates[4];
origin[1] = coordinates[5];
}
else {
origin[0] = coordinates[2];
origin[1] = coordinates[3];
}
//subtract origin from vectors
System.out.println();
counterX = 0;
counterY = 1;
while (counterY < newCoordinates.length) {
//System.out.println(coordinates[counterX] + ", " + coordinates[counterY]);
newCoordinates[counterX] = coordinates[counterX] - origin[0];
newCoordinates[counterY] = coordinates[counterY] - origin[1];
System.out.println("Translated coordinates: (" + newCoordinates[counterX] + ", " + newCoordinates[counterY] + ")");
counterX+=2;
counterY+=2;
}
System.out.println();
System.out.println("vector matrix:");
//fill up vectorMatrix with coordinates
int k = 0;
for (int col = 0; col < 4; col++) {
for (int row = 0; row < 2; row++) {
vectorMatrix[row][col] = newCoordinates[k++];
}
}
//print vectorMatrix:
for (int i = 0; i < vectorMatrix.length; i++) {
System.out.print("[");
for (int j = 0; j < vectorMatrix[i].length; j++) {
System.out.print(vectorMatrix[i][j]);
}
System.out.println("]");
}
int rotationMatrix[][] = new int[2][2];
//fill up rotationMatrix
System.out.println();
System.out.println("multiplicative matrix:");
k = 0;
for (int row = 0; row < 2; row++) {
System.out.print("[");
for (int col = 0; col < 2; col++) {
rotationMatrix[row][col] = rotationDirection[k++];
System.out.print(rotationMatrix[row][col]);
}
System.out.println("]");
}
//perform matrix multiplication
int[][] result = multiplyMatrices(rotationMatrix, vectorMatrix);
//print resulting matrix
System.out.println();
System.out.println("result matrix:");
for (int i = 0; i < result.length; i++) {
System.out.print("[");
for (int j = 0; j < result[i].length; j++) {
System.out.print(result[i][j]);
}
System.out.println("]");
}
//load new matrix coordinates back into array
k = 0;
for (int col = 0; col < 4; col++) {
for (int row = 0; row < 2; row++) {
resultCoordinates[k] = result[row][col];
k++;
}
}
System.out.println();
System.out.println("result coordinates:");
counterX = 0;
counterY = 1;
while (counterY < resultCoordinates.length) {
finalCoordinates[counterX] = resultCoordinates[counterX] + origin[0];
finalCoordinates[counterY] = resultCoordinates[counterY] + origin[1];
System.out.print("("+finalCoordinates[counterX] + ", " + finalCoordinates[counterY]+")");
counterX+=2;
counterY+=2;
}
return finalCoordinates;
}
public int[][] multiplyMatrices(int rotationMatrix[][], int vectorMatrix[][]) {
int mA = rotationMatrix.length;
int nA = rotationMatrix[0].length;
int mB = vectorMatrix.length;
int nB = vectorMatrix[0].length;
if (nA != mB) throw new RuntimeException("Illegal matrix dimensions.");
int[][] C = new int[mA][nB];
for (int i = 0; i < mA; i++) {
for (int j = 0; j < nB; j++) {
for (int k = 0; k < nA; k++) {
C[i][j] += (rotationMatrix[i][k] * vectorMatrix[k][j]);
}
}
}
return C;
}
public int[] getShape(String shape) {
if (shape.equals("L")) {
//pieceCoordinates = new int[] {0, 1, 0, 2, 1, 2, 2, 2};
pieceCoordinates = new int[] {4, 0, 4, 1, 5, 1, 6, 1};
}
else if (shape.equals("O")) {
pieceCoordinates = new int[] {0, 1, 1, 1, 0, 2, 1, 2};
}
else if (shape.equals("Z")) {
pieceCoordinates = new int[] {0, 1, 1, 1, 1, 2, 2, 2};
}
else if (shape.equals("RevZ")) {
pieceCoordinates = new int[] {1, 1, 2, 1, 0, 2, 1, 2};
}
else if (shape.equals("Bar")) {
//pieceCoordinates = new int[] {0, 1, 1, 1, 2, 1, 3, 1};
pieceCoordinates = new int[] {0, 1, 1, 1, 2, 1, 3, 1};
}
else if (shape.equals("T")) {
pieceCoordinates = new int[] {1, 1, 0, 2, 1, 2, 2, 2};
}
else if (shape.equals("RevL")) {
pieceCoordinates = new int[] {0, 2, 1, 2, 2, 2, 2, 1};
}
return pieceCoordinates;
}
}
import java.awt.Color;
import java.util.Random;
public class TileSscce {
private BoardSscce board;
private int currX, currY;
private Color color;
public TileSscce(BoardSscce b, int x, int y) {
board = b;
//when Tile is instantiated, set its position
setLocation(x, y);
}
public int getX() {
return currX;
}
public int getY() {
return currY;
}
public boolean checkNewLocation(int newX, int newY) {
boolean newLocationOK = board.isOpen(newX, newY);
return newLocationOK;
}
public boolean collision(int newX, int newY) {
boolean collision = this.getY() == ((board.getHeight()/board.getTileSize()))-2 || board.getTileAt(newX, newY) != null;
return collision;
}
public void setLocation(int newX, int newY) {
// board.setTileAt(null, currX, currY);
currX = newX;
currY = newY;
board.setTileAt(this, currX, currY);
}
public Color getColor() {
return setColor(color);
}
public Color setColor(Color myColor) {
color = myColor;
return color;
}
}
Thanks!
EDIT----------
I've tried implementing both ValarDohaeris and Svend Hansen's suggestions... Now the block is moving right when I press down, up when I press left, and down when I press right...
It seems to have to do with these methods in Board class which get and set tile locations...
// returns the tile at (x, y) or null if empty
public Tile getTileAt(int row, int col) {
System.out.println("getTileAt: " + row + ", " + col);
if(isValidCoordinate(row, col))
return grid[row][col];
return null;
}
// sets the tile at (x, y) to tile
public void setTileAt(Tile tile, int row, int col) {
System.out.println("setTileAt: " + row + ", " + col);
if(isValidCoordinate(row, col))
grid[row][col] = tile;
}
And in Piece class... movements are defined as:
public void movePieceCheck(int keycode) {
if (keycode == KeyEvent.VK_DOWN) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getRow()+1, tile[i].getCol(), i);
}
clearCurrPosition();
for (int i = 0; i < tile.length; i++) {
board.checkEndGame(tile[i].getRow(), tile[i].getCol());
}
board.checkBottomFull();
if (isCollision()) board.createNewPiece();
move();
}
if (keycode == KeyEvent.VK_RIGHT) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getRow(), tile[i].getCol()+1, i);
}
clearCurrPosition();
move();
}
if (keycode == KeyEvent.VK_LEFT) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getRow(), tile[i].getCol()-1, i);
}
clearCurrPosition();
move();
}
You have
grid = new TileSscce[totalRows][totalCols];
So when you want to access grid[x][y], you should check
x >= 0 && y >= 0 && x < totalRows && y < totalCols
in isValidCoordinate(x, y).
Emm... Quite interesting question. So to find out where the problem(s) may be I'll try to analyze your code a little bit...
You paste stack trace as
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 10
at Board.getTileAt(Board.java:177)
...
and at the same time the getTileAt()
// returns the tile at (x, y) or null if empty
public Tile getTileAt(int row, int col) {
System.out.println("getTileAt: " + row + ", " + col);
if(isValidCoordinate(row, col))//isValidCoordinate()?
return grid[row][col];
return null;
}
public boolean isValidCoordinate(int x, int y) {
return x >= 0 && y >= 0 && x < totalCols && y < totalRows;
}
... so the isValidCoordinate method return terms as
x >= 0 && y >= 0 && x < totalCols && y < totalRows
...the method doesn't allow to avoid array out-of-bounds problems; Seems like you put wrong array element indexes.
A. As I can notice, you trying to put a classic math matrix on Java [][] arrays as
public void clearRow(int rowToClear) {
for(int row = rowToClear; row > 0; row--) {
for(int col = 0; col < grid[row].length; col++) {//<-- ?
grid[col][row] = grid[col][row-1];
}
}
}
... and here I must say that you should know that in [][] arrays x,y are backwards and it is y,x because :
y (or classic i) - sub-array index (vertical)
x (or classic j) - sub-array's element index (horizontal)
so you should use array index something this way grid[y][x] or grid[i][j]
As a useful tip, I recommend you to analyze your code for logic errors in this field...
B. According to your app screenshot as
... it seems like the x,y problem takes place here too because you trying to control y (vertical) coordinates but (in real) you control x (horizontal) coordinates only :S It is still because of the row,col instead of a classic Java (col,row or y,x) [][] array index positions.
C. And again concerning to the wrong directions...
...up when I press left, and down when I press right...
if (keycode == KeyEvent.VK_RIGHT) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getRow(), tile[i].getCol()+1, i);
}
clearCurrPosition();
move();
}
Here I'll try to analyze the event as (you press right but move down)...
OK... according to one of your tasks you need to move by x coordinate (horizontally) but look closer... you make tile[i].getCol()+1 so it is newY and, of course, it moves vertically :S In your case it really moves down because you make increment as y++ ...
public void calcNewPosition(int newX, int newY, int currTile) {
newPositionX[currTile] = newX;
newPositionY[currTile] = newY;
}
public void clearCurrPosition() {
for (int i = 0; i < tile.length; i++) {
currPositionX[i] = tile[i].getX();
currPositionY[i] = tile[i].getY();
board.setTileAt(null, currPositionX[i], currPositionY[i]);
}
}
public void move() {
if (movePieceValid()) {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(newPositionX[i], newPositionY[i]);//<-- !
}
} else {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(currPositionX[i], currPositionY[i]);
}
}
}
...as a conclusion, I may recommend to change code (move right) something this way...
if (keycode == KeyEvent.VK_RIGHT) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getRow()+1, tile[i].getCol(), i);
}
clearCurrPosition();
move();
}
I hope my tips will help you to figure out what to look closer. Anyway, if you have some additional information please do comment my answer
Report if that helped you
This is based on x corresponds to columns and y corresponds to rows.
However grid is indexed by [row][col].
TileSscce grid[][] = new TileSscce[totalRows][totalCols]; // 12 => totalRows, 10 => totalCols
public int getWidth() {
return totalCols * tilesize;
}
public int getHeight() {
return totalRows * tilesize;
}
Following changes (based on your initial code - Sscce: - without later edits) will get rid of the exception and allow drawing till bottom of the board.
public void paintComponent(Graphics g) {
for (int row = 0; row < grid.length; row++) {
for (int col = 0; col < grid[row].length; col++) {
if (grid[row][col] != null) {
g.setColor(grid[row][col].getColor());
g.fillRect(col * tilesize, row * tilesize, tilesize, tilesize); // changed, check below snippet from fillRect documentation
g.setColor(Color.WHITE);
}
}
}
}
public TileSscce getTileAt(int x, int y) {
if (isValidCoordinate(x, y))
return grid[y][x]; // changed to [y][x] as grid is indexed by [row][col]
return null;
}
public void setTileAt(TileSscce tile, int x, int y) {
if (isValidCoordinate(x, y))
grid[y][x] = tile; // changed to [y][x] as grid is indexed by [row][col]
}
From fillRect documentation.
public abstract void fillRect(int x, int y, int width, int height)
The left and right edges of the rectangle are at x and x + width - 1.
The top and bottom edges are at y and y + height - 1.
This is correct.
public boolean isValidCoordinate(int x, int y) {
return x >= 0 && y >= 0 && x < totalCols && y < totalRows;
}

Categories

Resources