I am trying to find the path to the EndPosition. This is a recursive function. Please help, I'm about to kill myself.
This is the Map given
{ 1, 1, 1, 1 },
{ 0, 0, 1, 0 },
{ 0, 0, 1, 0 },
{ 0, 0, 1, 0 }};
I would like to use the GetPath recursively to get to the EndPosition in the map above. The parameters is the currentPosition, the end position and the map. For this example, the starting position is (0,0) and ending and the EndPosition is (0,3), top right corner. 0's are for walls and 1's are for the path.
I need to return an arraylist filled with the valid points to the Ending Position. Though my array size is always 0 and the base case never returns the path. How can I keep track of the positions in an arraylist?
Please help, would appreciate it
private ArrayList<Point> GetPath(Point CurrentPosition, Point EndPosition, int[][] Map)
{
System.out.println("Current Position: " + CurrentPosition.toString());
ArrayList<Point> p = new ArrayList<Point>();
Path.add(CurrentPosition);
if (CurrentPosition.equals(EndPosition))
{
return Path;
}
Map[(int)CurrentPosition.getX()][(int) CurrentPosition.getY()] = 0; //setting to 0 so my function wont revisit that position in the map
ArrayList<Point> p2 = new ArrayList<Point>(); //Array for the 4 points around the CurrentPosition
p2.add(new Point((int) CurrentPosition.getX(), (int) CurrentPosition.getY()+1));
p2.add(new Point((int) CurrentPosition.getX()+1, (int) CurrentPosition.getY()));
p2.add(new Point((int) CurrentPosition.getX(), (int) CurrentPosition.getY()-1));
p2.add(new Point((int) CurrentPosition.getX()-1, (int) CurrentPosition.getY()));
for (int i = 0; i < p2.size(); i++)
{
int j = 0;
if (((p2.get(i).getX() >= 0 && p2.get(i).getY() >= 0) && (p2.get(i).getX() < Map.length && p2.get(i).getY() < Map[0].length)) && Map[(int) p2.get(i).getX()][(int) p2.get(i).getY()] !=0) //if the points in the array are within range and if the points aren't equal to 0.
{
Map[(int)p2.get(i).getX()][(int)p2.get(i).getY()] = 0;
GetPath(p2.get(i), EndPosition, Map); //recursive method
}
}
return Path;
}
I think I may have found the problem:
You never do anything with the return value of your recursive call:
...
Map[(int)p2.get(i).getX()][(int)p2.get(i).getY()] = 0;
GetPath(p2.get(i), EndPosition, Map); //recursive method
...
You should do the following:
ArrayList<Point> recPath = GetPath(p2.get(i), EndPosition, Map); //recursive method
Path.addAll(recPath);
You actually do need to return Path at the end after all
create a function that takes as parameter the start and end position. have it scan every path it can for clear road. so if you can go north, west, south, east scan those locations for "1". if you find 1, have the function call itself again with the point where you found "1" as the new starting position. pass in the path so far and the targeted end position as well. once one of those finds a match for "1" at the given end position you have reached your path. this may not be optimal path. if you need that, parse all possible paths and pick shortest. in the end traverse your path backward to get all points. since the function gets as argument points visited so far, make sure you dont go to same point again by excluding those from future paths.
public static List<Point> getPath(Point start, Point end, int[][] Map)
{
//Current Position for the currentPosition
// EndPosition, given any point in the map would be the end of the maze
// map is that map that given for example or any other map
// returns an arraylist of positions of the paths
ArrayList<Point> result = new ArrayList<Point>();
boolean solutionExists = buildSolution(start, end, Map, result, new HashSet<Point>());
return solutionExists? result : null;
}
public static boolean buildSolution(Point current, Point end, int[][] map, List<Point> solution, Set<Point> visited) {
visited.add(current);
if (current.equals(end)) {
solution.add(current);
return true;
}
if (map[current.x][current.y] == 0) {
return false;
}
Set<Point> neighbours = getNeighbours(current, map);
neighbours.removeAll(visited);
for (Point neighbour : neighbours) {
ArrayList<Point> temp = new ArrayList<Point>();
Set<Point> tempVisited = new HashSet<Point>(visited);
tempVisited.add(neighbour);
if (buildSolution(neighbour, end, map, temp, tempVisited)) {
solution.add(current);
solution.addAll(temp);
return true;
}
}
return false;
}
public static Set<Point> getNeighbours(Point current, int[][] map) {
int maxX = map.length - 1;
int maxY = map[0].length - 1;
Set<Point> result = new HashSet<Point>();
result.add(new Point(current.x, current.y - 1 < 0 ? current.y :current.y -1));
result.add(new Point(current.x, current.y + 1 > maxY ? current.y : current.y +1));
result.add(new Point(current.x - 1 < 0 ? current.x : current.x -1 , current.y));
result.add(new Point(current.x + 1 > maxX ? current.x : current.x + 1, current.y));
result.remove(current);
return result;
}
Related
I wanted to make an algorithm to create a maze using recursive back-tracker algorithm. I get this problem when running the program:
Exception in thread "main" java.util.NoSuchElementException
at java.base/java.util.Vector.firstElement(Vector.java:481)
at DC_files.Maze.RBT(MazeAlgorithm.java:131)
at DC_files.MazeAlgorithm.main(MazeAlgorithm.java:222)
and honestly, at this point after finding couple of bugs and error i have no idea what might be wrong. The only clue that i have, is that in the switch there is an error, that the stack.firstElement() doesn't update and is always the same and in the cases something is being inserted wrongly.
while(vis < toVis){
Vector<Integer> neighbours = new Vector<>();
int x = stack.firstElement().getKey(); (line 131 with the error.)
int y = stack.firstElement().getValue();
//North neighbour
if(y-1 >= 0 && !matrix[y - 1][x].vis){
neighbours.add(0);
}
entire code
package DC_files;
import javafx.util.Pair;
import java.util.Random;
import java.io.*;
import java.util.*;
class Cell{
/*
each Cell holds information of:
-the state of it's walls,
-the value that the Cell holds(will be used later to customize rooms in the maze),
-and information if the Cell was visited(will be used in the algorithm)
*/
boolean[] walls;
boolean vis;
int value;
public boolean isVis() {
return vis;
}
public boolean[] getWalls() {
return walls;
}
public int getValue() {
return value;
}
/*
by default, each Cell has the value of each wall set to true(they exist/there are no corridors),
vis as visited set to false, because we weren't in the room yet,
value set to zero, for further development phase (0 - not visited, 1 - visited, 2 - ??? (...))
*/
Cell(){
walls = new boolean[]{true, true, true, true}; // {N, S, E, W}
vis = false;
value = 0;
}
}
class Maze{
/*
Maze class was created in order to generate an array of Cells,
that will be later used in the algorithm.
First we create integer ,,size,, to store size of the array, then
we create a matrix, that will hold Cell objects.
In the constructor of the class, we set the required integer s as size,
allocate memory to the matrix, by setting its dimensions to size x size
and we create Cell objects for each spot int the matrix, without any modifications.
*/
int size;
Cell[][] matrix;
Maze(int s){
size = s;
matrix = new Cell[size][size];
for(int y = 0; y < size; y++){
for(int x = 0; x < size; x++){
matrix[y][x] = new Cell();
//System.out.print("1");
}
}
}
void showMaze(){
for(int y = 0; y < size; y++){
for(int x = 0; x < size; x++){
System.out.print(matrix[y][x].getValue() + " ");
}
System.out.println();
}
}
/*
Class ,,MazeAlgorithm'' is responsible for creating connections between cells in matrix.
It uses RBT, which stands for Recursive Back-Tracker, to achieve that goal.
In order to use RBT, we need to give it starting point. After we do that,
it will check, if there is any neighbour, that fulfills the required conditions,
and then goes to that neighbour setting visited to true and value to 1.
After that, it repeats the process, and constantly adds the cells coordinates
on top of the stack, to keep track of the road it went through. If it gets stuck,
it will go back to the previous cell, by removing the top coordinates of the stack,
and going to NOW top coordinates (previous move), checking if that cell has any
neighbours which can fulfill the conditions. If it cannot find any neighbour,
it means the maze was created.
*/
void RBT(int startX, int startY){
//Setting cells to visit to size x size, and the value of visited cells to 0
int toVis = size*size;
int vis = 0;
//Declaring a stack, that will hold the information about our road
Stack<Pair<Integer, Integer>> stack = new Stack<>();
//Setting starting position: pushing it onto the stack,
//setting it's values as visited and 1,
//incrementing visited rooms.
stack.push(new Pair<>(startX, startY));
matrix[startY][startX].vis = true;
matrix[startY][startX].value = 1;
vis += 1;
//we will check, if our current node has any neighbours we can go to,
//saving those neighbours inside a vector
while(vis < toVis){
Vector<Integer> neighbours = new Vector<>();
int x = stack.firstElement().getKey();
int y = stack.firstElement().getValue();
//North neighbour
if(y-1 >= 0 && !matrix[y - 1][x].vis){
neighbours.add(0);
}
//South neighbour
if(y+1 < size && !matrix[y + 1][x].vis){
neighbours.add(1);
}
//East neighbour
if(x+1 < size && !matrix[y][x + 1].vis){
neighbours.add(2);
}
//West neighbour
if(x-1 >= 0 && !matrix[y][x - 1].vis){
neighbours.add(3);
}
//checking, if there are any neighbours we can visit
//if yes, we do our job
//if not, we pop our stack and repeat the process
if(!neighbours.isEmpty()){
Random rand = new Random();
int randDir = neighbours.get(rand.nextInt(neighbours.size()));
switch (randDir) {
//North
case 0 -> {
matrix[y][x].walls[0] = false;
stack.push(new Pair<>(x, y - 1));
matrix[y - 1][x].value = 1;
matrix[y - 1][x].vis = true;
matrix[y - 1][x].walls[1] = false;
}
//South
case 1 -> {
matrix[y][x].walls[1] = false;
stack.push(new Pair<>(x, y + 1));
matrix[y + 1][x].value = 1;
matrix[y + 1][x].vis = true;
matrix[y + 1][x].walls[0] = false;
}
//East
case 2 -> {
matrix[y][x].walls[2] = false;
stack.push(new Pair<>(x + 1, y));
matrix[y][x + 1].value = 1;
matrix[y][x + 1].vis = true;
matrix[y][x + 1].walls[3] = false;
}
//West
case 3 -> {
matrix[y][x].walls[3] = false;
stack.push(new Pair<>(x - 1, y));
matrix[y][x - 1].value = 1;
matrix[y][x - 1].vis = true;
matrix[y][x - 1].walls[2] = false;
}
}
vis += 1;
}else{
stack.pop();
}
}
}
}
public class MazeAlgorithm {
public static void main(String[] args){
Maze m = new Maze(3);
m.RBT(1, 1);
m.showMaze();
}
so i just run the code again, and the error line changed to line 131, i am also watching some tutorial about the Intellij debugger rn.
EDIT: Problem resolved. Had to change lines:
int x = stack.firstElement().getKey();
int y = stack.firstElement().getValue();
into:
int x = stack.peek().getKey();
int y = stack.peek().getValue();
Turns out method .firstElement() doesn't give you first element (the one on top of the stack) but the one that was first inserted (always the same object) so in the end all the operation were performed on one, single element and not multiple ones.
I'm currently trying to implement the A* (A star) - Algorithm. I already got it working when I don't have walls and when it is only needed to go besides the wall. My problem now is that when I'm placing the start inside of my walls, my algorithm is calculating infinitely as I think it doesn't go backward. Can you guys please help me?
Here is the code from the node class:
int[][] mMap; // if there is a wall => 1
int[][] mAStarField;
ArrayList<AStarNode> mAStarPath;
public class AStarNode implements Comparable<AStarNode>{
public int x;
public int y;
public float c;
public AStarNode p;
public AStarNode(int x, int y, float c, AStarNode p) {
this.x = x; //X pos
this.y = y; //Y pos
this.c = c; //Cost to get to the node
this.p = p; //Parent of the node
}
//override the compareTo method
public int compareTo(AStarNode node)
{
if (c == node.c)
return 0;
else if (c > node.c)
return 1;
else
return -1;
}
}
public class Node {
public int x;
public int y;
public int z;
public int w;
public Node(int x, int y, int z, int w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
}
This is the code from my A* algorithm:
//Pathfinding with A*
//return path length
int updateAStar() {
//Needed for drawing:
//Array containing the distance to the start node (filled with max int at start)
mAStarField = new int[mMap.length][mMap[0].length];
//List containing the found path
mAStarPath = new ArrayList<AStarNode>();
for (int j = 0; j < mMap.length; j++) {
for (int k = 0; k < mMap[0].length; k++) {
mAStarField[j][k]=Integer.MAX_VALUE;
}
}
//AStarNode(x,y,c,w)
//x X pos
//y Y pos
//c Cost to get to the node
//p Parent of the node
//List can be sorted expensive but simple by c value with
//Collections.sort(openList);
ArrayList<AStarNode> openList = new ArrayList<AStarNode>();
ArrayList<AStarNode> closedList = new ArrayList<AStarNode>();
int dist = abs(mStartNode[0]-mEndNode[0])+abs(mStartNode[1]-mEndNode[1]);
//If there is any target, that isn't on my field, add start node to list
if (dist>0 && mMap[mStartNode[0]][mStartNode[1]] != 1 && mMap[mEndNode[0]][mEndNode[1]]!=1) {
openList.add(new AStarNode(mStartNode[0], mStartNode[1], 0, null));
mAStarField[mStartNode[0]][mStartNode[1]] = 0;
}
// my code begins here (only everything from here on can be edited!)
while(!openList.isEmpty())
{
Collections.sort(openList);
AStarNode current = openList.get(0);
if(current.x == mEndNode[0] && current.y == mEndNode[1])
{
return 1;
}
openList.remove(0);
closedList.add(current);
ArrayList<AStarNode> neighbors = new ArrayList<AStarNode>();
neighbors.add(new AStarNode(current.x - 1, current.y, current.c + 1, current));
neighbors.add(new AStarNode(current.x + 1, current.y, current.c + 1, current));
neighbors.add(new AStarNode(current.x, current.y - 1, current.c + 1, current));
neighbors.add(new AStarNode(current.x, current.y + 1, current.c + 1, current));
for(AStarNode n : neighbors)
{
if(n.x >= 0 && n.y >= 0 && n.x < mMap.length && n.y < mMap.length && mMap[n.x][n.y] != 1){
float cost = estimateDistanceEnd(n.x, n.y);
n.c = cost;
if(closedList.contains(n) && cost >= n.c) continue;
if(!openList.contains(n) || cost < n.c)
{
n.p = current;
if(!openList.contains(n)){
mAStarField[n.x][n.y] = (int) n.c;
openList.add(n);
Collections.sort(openList);
}
}
}
}
}
return -1;
}
int estimateDistanceEnd(int x, int y){
return abs(x-mEndNode[0])+abs(y-mEndNode[1]);
}
int estimateDistanceStart(AStarNode a){
return abs(a.x-mStartNode[0])+abs(a.y-mStartNode[1]);
}
int estimateDistance(AStarNode a, AStarNode b){
return abs(a.x-b.x)+abs(a.y-b.y);
}
A picture of my current path solving result
Important: I'm only allowed to change code inside of the area I marked.
Thank you!
I can't run the code, but I have a little idea about the the source of the issue. See, the A* algorithm doesn't "walk back" while looking for the best path. It solves the pathfinding issue by calculating the less costly way to get to the end for literally every node that it evaluates. It starts by calculating the itineraries which are the most straightforward, then if it didn't work it'll enlarge it's options until it either, uh, finds a way - or run out of options.
The principle of the closed list to avoid evaluating a node twice. As you guessed, the problem here is that you are creating new nodes for neighbors at every iteration of the pathfinding algorithm, thus making it harder for the closed list to be used correctly.
A complex object like a custom class can be compared through 3 means: it either is the same object (it refer to the same pointer (it's the same instance, it's at the same place in the computer's memory)), or the values are all the same whatever it's pointer is pointing, or you can define a rule to compare them. These methods would be: comparing by reference, comparing by value and operator overloading - although that last one isn't possible in java, but you can write a method to do the same.
When doing closedList.contains(n), you are comparing by reference (which is the default for this kind of operation). Since all the nodes have been created on the fly, even if their coordinates are the same they all have different address in memory, and this is why this condition will never be met.
Assuming that you cannot mess with your tutor's code, you can still fix this. You almost got it right the first time! There are many ways to to fix this, in fact, and as I miss some of the context I'll be rather plain in my suggested approach: you'll write a method to fetch a specific node from a list (like the operator overloading I spoke about, but with the bare minimum effort) and we'll work by reference from this point onward.
First, create a master list of all your AStarNode (if you don't already have one, if you do then use that one instead):
// my code begins here (only everything from here on can be edited!)
ArrayList<AStarNode> nodesList = new ArrayList<AStarNode>();
for (int j = 0; j < mapWidth; j++) {
for (int k = 0; k < mapHeight; k++) {
nodesList.add(new AStarNode(j, k)); // I gimmicked the constructor for my own confort, you'll have to tweak this line so it fits in your code
}
}
Then, write yourself a method which will return a node from an array based on given xy coordinates:
AStarNode GetAStarNodeByPosition(int x, int y, ArrayList<AStarNode> list) {
for (AStarNode m : list) {
if (m.x == x && m.y == y) {
return m;
}
}
return null;
}
Now, you can use these to compare all your nodes by reference. So, now instead of instantiating new nodes all the time, you'll always fetch them from the master list, by reference:
ArrayList<AStarNode> neighbors = new ArrayList<AStarNode>();
neighbors.add(GetAStarNodeByPosition(current.x - 1, current.y, nodesList));
neighbors.add(GetAStarNodeByPosition(current.x + 1, current.y, nodesList));
neighbors.add(GetAStarNodeByPosition(current.x, current.y - 1, nodesList));
neighbors.add(GetAStarNodeByPosition(current.x, current.y + 1, nodesList));
Also, don't forget to fix this line:
//openList.add(new AStarNode(mStartNode[0], mStartNode[1], 0, null));
openList.add(GetAStarNodeByPosition(mStartNode[0], mStartNode[1], nodesList));
Lastly, always remember to test for null if you know that you may have some in your arrays. In this case, the GetAStarNodeByPosition method can return a null if you're too close to the boundaries of the maze. You can either modify the way you add to the neighbors list so there will be no null in there, or you can check for null on this line:
if(n != null && n.x >= 0 && n.y >= 0 && n.x < mMap.length && n.y < mMap.length && mMap[n.x][n.y] != 1){
Honestly I would prevent the inclusion of null in the array at all, that's much safer if you modify the code further later.
Now all your nodes will relate and you'll be able to overcome obstacles which needs your algorithm to search in a more clever way than a straight line.
Have fun!
I am sure most of you have heard about the largest rectangle in a histogram problem. -Link-
In my current project, I need to change this algorithm so that it finds all rectangles which are not a smaller subset of another rectangle in that histogram.
This is how far I am currently. But I cannot figure out how to not count the subsets in here.
//time: O(n), space:O(n)
public ArrayList<int[]> largestRectangles(int[] height) {
ArrayList<int[]> listRect = new ArrayList<int[]>();
if (height == null || height.length == 0) {
return null;
}
Stack<Integer> stack = new Stack<Integer>();
int max = 0;
int i = 0;
while (i < height.length) {
//push index to stack when the current height is larger than the previous one
if (stack.isEmpty() || height[i] >= height[stack.peek()]) {
stack.push(i);
i++;
} else {
//add rectangle when the current height is less than the previous one
int p = stack.pop();
int h = height[p];
int w = stack.isEmpty() ? i : i - stack.peek() - 1;
listRect.add(new int[]{p,h,w});
}
}
while (!stack.isEmpty()) {
int p = stack.pop();
int h = height[p];
int w = stack.isEmpty() ? i : i - stack.peek() - 1;
listRect.add(new int[]{p,h,w});
}
return listRect;
}
public static void main(String[] args) {
for(int[] rect : largestRectangles(new int[]{1,2,2,3,3,2})) {
System.out.print("pos:"+rect[0]+" height"+rect[1]+"
width"+rect[2]);
System.out.println();
}
}
The idea is to check if the new rectangle being added contains the last added rectangle; if so then simply remove the last added rectangle information from the result list before adding this new one (so confirming by height). I don't have Java IDE handy so tried in C#.
Following is the part you'll need to add in two places (please convert to java) right before your listRect.Add(new[] {p,h,w}.).
if (listRect.Count > 0)
{
if (listRect[listRect.Count - 1][1] <= h)
{
listRect.RemoveAt(listRect.Count - 1);
}
}
This is just an idea. You'll have to write logic for omitting above remove logic for histograms with 0 in them i.e. new int[] { 1, 2, 2, 3, 3, 2, 0, 1 } etc. But logic is similar; you'll have to store a flag etc. and bypass removal of last rectangle based on its position.
I have this function below called findNeighboringChains(i, j). You pass in a point (x, y) and it returns all chain objects that exist in the neighboring points (x + 1, y), (x - 1, y), (x, y + 1), and (x, y - 1). If no chain exists at (x, y) then findChainId(x, y) = -1, else it will return an ID >= 0. Only one chain can exist at each (x, y). For more context, this is a function I am using to find adjacent chains to a cell in the game of Go.
I feel like what I have so far is kind of verbose, but I'm not sure how to make it better. It seems like it would be ideal if I could iterate over those points (x + 1, y) ... (x, y - 1) through a loop. Any suggestions?
public ArrayList<Integer> findNeighboringChains(int i, int j) {
ArrayList<Integer> neighboringChains = new ArrayList<>();
int tmp = findChainId(i - 1, j);
if (tmp != -1) {
neighboringChains.add(tmp);
}
tmp = findChainId(i + 1, j);
if (tmp != -1) {
neighboringChains.add(tmp);
}
tmp = findChainId(i, j - 1);
if (tmp != -1) {
neighboringChains.add(tmp);
}
tmp = findChainId(i, j + 1);
if (tmp != -1) {
neighboringChains.add(tmp);
}
return neighboringChains;
}
One way would be to utilize the Point object that is built into Java, and iterate over a list of points- each time calling the same piece of code. In my solution/refactoring, I create a new method called "getNeighboringPoints(Point p)", which retrieves the four neighboring points. Then, in your function findNeighboringChains, you can iterate using a for-each loop over that list of points.
There are a lot of variations on this kind of pattern you could do, but you're definitely right to think that it's possible to be less redundant. It's always a good idea to try to follow the DRY principle.
public ArrayList<Integer> findNeighboringChains(int i, int j) {
ArrayList<Integer> neighboringChains = new ArrayList<>();
Point p = new Point(i, j);
List<Point> neighboringPoints = getNeighboringPoints(p);
for (Point point : neighboringPoints) {
int tmp = findChainId(point.x, point.y);
if (tmp != -1) {
neighboringChains.add(tmp);
}
}
return neighboringChains;
}
/**
*
* #param p
* The input point.
* #return a list of points neighboring point p
*/
private List<Point> getNeighboringPoints(Point p) {
ArrayList<Point> neighboringPoints = new ArrayList<Point>();
neighboringPoints.add(new Point(p.x - 1, p.y));
neighboringPoints.add(new Point(p.x + 1, p.y));
neighboringPoints.add(new Point(p.x, p.y + 1));
neighboringPoints.add(new Point(p.x, p.y - 1));
return neighboringPoints;
}
One of the benefits of the above method, is now you can later on find that you might need to do another operation on all the neighboring points, and you can reuse the method getNeighboringPoints().
Edit:
Another way to reduce redundancy would be to use the extract method technique.
public ArrayList<Integer> findNeighboringChains(int i, int j) {
ArrayList<Integer> neighboringChains = new ArrayList<>();
int tmp = findChainId(i - 1, j);
checkChain(neighboringChains, tmp);
tmp = findChainId(i + 1, j);
checkChain(neighboringChains, tmp);
tmp = findChainId(i, j - 1);
checkChain(neighboringChains, tmp);
tmp = findChainId(i, j + 1);
checkChain(neighboringChains, tmp);
return neighboringChains;
}
private void checkChain(ArrayList<Integer> neighboringChains, int tmp) {
if (tmp != -1) {
neighboringChains.add(tmp);
}
}
This might be better since it doesn't force usage of the point class on a project which already isn't using points. (It can be annoying when there is one method that uses points where everything else requires the input of two ints).
So, I am making a chess engine in Java. Given a current board configuration, the AI should figure out each possible move it can make, add it to a linked list of all possible moves, and return that list. Currently I am testing the following board configuration:
/*bRbNbBbQbKbBbNbR
bPbPbPbPbPbPbPbP
NuNuNuNuNuNuNuNu
NuNuNuwRNuNuNuNu
NuNuNuNuNuNuNuNu
NuNuNuNuNuNuNuNu
wPwPwPwPwPwPwPwP
NuwNwBwQwKwBwNwR*/
"bR" means black rook, "bN" is black knight, etc. "Nu" means null or no piece. In this configuration I moved the bottom-left white rook to the middle of the board.
The following method, possibleMoves(), in my Mobility class is what should generate and return a linked list of all of the possible board configurations. Each index i corresponds to pieces on the board starting from the left. In this case the AI is white, so 0 is the leftmost white pawn, 7 is the rightmost white pawn, 8 is the white rook that is now in the center of the board, 9 is the other white rook, etc. Right now I'm only testing the rooks, so the other conditionals are empty. nonPawnBoardGen() returns a sublist of possible board configurations.
public LL possibleMoves(){
LL children = new LL();
/*
* check each piece
*/
for(int i = 0; i < 16; i++){
if(i < 8){//pawns
}
else if(i < 10){//rooks
children.joinWith(nonPawnBoardGen(i, 0, -1)); //positions to the left
children.joinWith(nonPawnBoardGen(i, 0, 1)); //right
children.joinWith(nonPawnBoardGen(i, -1, 0)); //up
children.joinWith(nonPawnBoardGen(i, 1, 0)); //down
}
else if(i < 12){
// checkKnight(r, c, int dR, int dC)
}
else if(i < 14){//bishops
}
else{ //king, queen
}
}
return children;
}
joinWith(), in my LL class, joins a sublist with the total children linked list.
public void joinWith(LL newList){
if(newList.isEmpty())
return;
if(this.isEmpty()){
first = newList.getFirst();
last = newList.getLast();
}
else{
last.next = newList.getFirst();
last = newList.getLast();
}
}
The following function, nonPawnBoardGen(), is another function in my Mobility which gets passed a piece index and a unit vector. So, if I want to check all of the possible left moves of the rook in the center of the board, I would call nonPawnBoardGen(8, 0, -1) because the rook is index 8, it will remain in the same row, and it will iterate through columns to the left. That function call should return a sublist of all of the possible board configurations involving this rook because I would still need to check everything to the right, up, and down from the rooks current position.
private LL nonPawnBoardGen(int index, int vecR, int vecC){
LL boardSubLst = new LL();
int sR, sC; //source row and col
if(turn == true){//white
//last 16 coords are white pieces
if(coords[index + 16] == null){//if piece does not exist, return
return null;
}
sR = coords[index + 16].getRow(); //each coord is an object containing a row and col value
sC = coords[index + 16].getCol();
}
else{//black
//first 16 coords are black pieces
if(coords[index] == null){
return null;
}
sR = coords[index].getRow();
sC = coords[index].getCol();
}
int curR = sR; //current row
int curC = sC; //current col
curR+=vecR; //iterate by unit vector
curC+=vecC;
while(curR > -1 && curR < 8 && curC > -1 && curC < 8){ //while in range of board
if(turn == true){//white
if(board[curR][curC].charAt(0) != 'w'){ //if space is free or opposite color, valid move
coords[index + 16].setRow(curR); //move rook to new position
coords[index + 16].setCol(curC);
if(board[curR][curC].charAt(0) == 'b'){ //if space contains piece of opposite color,
int r, c; //piece needs to be removed
for(int j = 0; j < 16; j++){ //iterate through 16 opponent pieces
r = coords[j].getRow();
c = coords[j].getCol();
if(curR == r && curC == c){ //check which opponent's piece's coords match
coords[j] = null; //the rook's current coords, then remove opp's piece
boardSubLst.insert(coords); //add board config to sublist
break;
}
}
break;
}
else{ //if the space is null, simply add board config to sublist
boardSubLst.insert(coords);
}
}
else{ //if space is same color, break
break;
}
}
else{//black
if(board[curR][curC].charAt(0) != 'b'){
coords[index].setRow(curR);
coords[index].setCol(curC);
if(board[curR][curC].charAt(0) == 'w'){
int r, c;
for(int j = 0; j < 16; j++){
r = coords[j + 16].getRow();
c = coords[j + 16].getCol();
if(curR == r && curC == c){
coords[j + 16] = null;
boardSubLst.insert(coords);
break;
}
}
break;
}
else{
boardSubLst.insert(coords);
}
}
else{
break;
}
}
curR+=vecR;
curC+=vecC;
}
return boardSubLst;
}
To make this long story short, in nonPawnBoardGen(), every time I get a new valid board configuration, I edit the board coordinates (white in this case):
coords[index + 16].setRow(curR);
coords[index + 16].setCol(curC);
and add them to a list of board configurations:
boardSubLst.insert(coords);
However, every time I edit coords, each value in the boardSubList linked list changes to the current value of coords. Why is this happening?
EDIT:
I think I can avoid this problem just by having nonPawnBoardGen() generate and return only one set of coordinates. The iterator can be saved in the class rather than locally in the function. Each set of coordinates returned can be added directly to the list of children in possibleMoves(). I will try this and see what happens...
When you call
boardSubLst.insert(coords);
You are passing the same reference to the coords array. I think you will find the easiest solution is to copy the array instead, for example using Arrays.copyOf(T[] original, int newLength)
boardSubLst.insert(Arrays.copyOf(coords, coords.length));
Or, assuming coords is of type Coord[] you could use System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
Coord[] coords2 = new Coord[coords.length];
System.arraycopy(coords, 0, coords2, 0, coords.length);
boardSubLst.insert(coords2);
I stopped working on my code for awhile, just came back to it and solved the problem. Thank you Elliott and Anantha. You were both right. My array of chess piece coordinates is actually an array of objects, with each object being a set of coordinates. Originally, I changed my code so that I was making a copy of the array each time before editing the coordinates and adding the array to my list, but this was only making new references to the same set of the array's objects. The solution was to not only make new array references, but to allocate new coordinate objects within the array every time I changed coordinates. I think I'm making this sound a lot more convoluted than it actually is, but yes it was a problem of continuously referencing the same area in memory. It was not too obvious. Thanks a lot!