How to find all connected numbers in an array? - java

Hey all, back again. Working on a dungeon generator and I'm actually surprising myself with the progress. Yet I still have a straggling room every now and then. I was wondering if there was a way to loop through an array and see if all the '1s' (the floor tiles) are connected, and if not, how to connect them.
Thanks!
EDIT: The array is randomly filled with rooms and corridors; here's the code:
import java.util.Random;
public class Level
{
Random random = new Random();
int[][] A = new int[100][100];
int minimum = 3;
int maximum = 7;
int xFeature = 0;
int yFeature = 0;
private void feature()
{
int i = 0;
while(i>=0)
{
xFeature = random.nextInt(100-1) + 1;
yFeature = random.nextInt(100-1) + 1;
if(A[xFeature][yFeature]==1)//||A[xFeature++][yFeature]==1||A[xFeature][yFeature--]==1||A[xFeature][yFeature++]==1)
break;
i++;
}
}
private void room()
{
int safeFall = 0;
int xCoPLUS = minimum + (int)(Math.random()*minimum);
int yCoPLUS = minimum + (int)(Math.random()*minimum);
if(yCoPLUS >= xCoPLUS)
{
for(int across = xFeature; across < xFeature+xCoPLUS+2; across++)
{
for(int vert = yFeature; vert < yFeature+yCoPLUS+1; vert++)
{
if(A[vert][across] == 0)
safeFall++;
else
break;
}
}
}
if(yCoPLUS < xCoPLUS)
{
for(int across = xFeature; across < xFeature+xCoPLUS+1; across++)
{
for(int vert = yFeature; vert < yFeature+yCoPLUS+2; vert++)
{
if(A[vert][across] == 0)
safeFall++;
else
break;
}
}
}
if((safeFall== (xCoPLUS+1) * (yCoPLUS+2)) || ((safeFall== (xCoPLUS+2) * (yCoPLUS+1))))
{
for(int across = xFeature; across < xFeature+xCoPLUS; across++)
{
for(int vert = yFeature; vert < yFeature+yCoPLUS; vert++)
{
A[vert][across] = 1;
}
}
}
}
private void corridor()
{
int xCoONE = xFeature;
int yCoONE = yFeature;
int xCoTWO = random.nextInt(10)+10;
int yCoTWO = random.nextInt(10)+10;
while(xCoONE > xCoTWO)
{
A[xCoONE][yCoONE] = 1;
xCoONE--;
}
while(xCoONE < xCoTWO)
{
A[xCoONE][yCoONE] = 1;
xCoONE++;
}
while(yCoONE > yCoTWO)
{
A[xCoONE][yCoONE] = 1;
yCoONE--;
}
while(yCoONE < yCoTWO)
{
A[xCoONE][yCoONE] = 1;
yCoONE++;
}
}
public Level()
{
firstroom();
for(int i = 0; i < 500; i++)
{
int x = random.nextInt(50);
feature();
if(x > 1)
room();
else
corridor();
}
troubleShoot();
}
So basically what happens when I create an object of this class is that a 100x100 array is filled with corridors and rooms determined by a random number. (well, a couple of them) But with how I have my room non-overlapping failsafe (safeFall in room()), I get stuck with a room that is one title out of reach every now and then.

The article Maze Generation Algorithm discusses several approaches to generating a maze. It includes links to Java examples.

Related

Keep getting IndexOutOfBounds exception when trying to merge arraylist

I'm doing a school assignment (so still a noob) but cannot seem to figure out why i keep getting IndexOutOfBoundsException. any help is appreciation. It's most likely some stupid mistake but i've gotten very good at not noticing those. It's supposed to separate slice objects in to 3 ArrayLists, scramble those lists, then put them back in the correct order based on color.
public void scramble()
{
ArrayList<Slice> temp = new ArrayList<>();
ArrayList<Slice> red = new ArrayList<>();
ArrayList<Slice> black = new ArrayList<>();
ArrayList<Slice> blue = new ArrayList<>();
int blackListCount = 0;
int redListCount = 0;
int blueListCount = 0;
for (Slice s : slices) {
if (s.getColor().equals("red")) {
red.add(s);
} else if (s.getColor().equals("black")) {
black.add(s);
} else if (s.getColor().equals("blue")) {
blue.add(s);
}
}
blueScram(blue);
blackScram(black);
redScram(red);
for (int i = 0; i < slices.size() - 1; i++) {
if (i % 5 == 0) {
temp.add(i, black.get(blackListCount));
blackListCount++;
}
if (i % 2 == 0) {
temp.add(i, blue.get(blueListCount));
blueListCount++;
}
if (i % 2 == 1) {
temp.add(i, red.get(redListCount));
redListCount++;
}
}
slices.clear();
slices = temp;
}
private void blueScram(ArrayList<Slice> blue) {
for (int i = 0; i < blue.size(); i++) {
switchSlice(blue, i, (int) (Math.random() * (blue.size())));
}
}
private void redScram(ArrayList<Slice> red) {
for (int i = 0; i < red.size(); i++) {
switchSlice(red, i, (int) (Math.random() * (red.size())));
}
}
private void blackScram(ArrayList<Slice> black) {
for (int i = 0; i < black.size(); i++) {
switchSlice(black, i, (int) (Math.random() * (black.size())));
}
}
private void switchSlice(ArrayList<Slice> list, int firstIndex, int lastIndex) {
try {
Slice temp = list.get(firstIndex);
list.set(firstIndex, list.get(lastIndex));
list.set(lastIndex, temp);
} catch (IndexOutOfBoundsException e) {
System.out.println("IndexOutBounds while trying to randomize/switch elements of a slice list");
e.printStackTrace();
}
}
Here's the initial (given) slice creation / sorting code
private static int[] getStandardPrizes()
{
int[] arr = new int[20];
for (int i=0; i < 20; i++)
{
if (i%5 == 0)
arr[i] = i*1000;
else if (i%2 == 1)
arr[i] = i*100;
else
arr[i] = i*200;
}
return arr;
}
I get errors at if(i % 5 == 0) and the other if statements within the scope of the surrounding for loop

Scrolling text effect on 2D array

For a project I am working on I would like to be abbe to "scroll" an array like so:
Here is the code I have so far:
private boolean[][] display = this.parseTo8BitMatrix("Here");
private int scroll = 0;
public void scroll() {
this.scroll++;
}
//sets the clock's display
public void setDisplay(String s) {
this.display = this.parseTo8BitMatrix(s);
}
//determines the current frame of the clock
private boolean[][] currentFrame() {
boolean[][] currentFrame = new boolean[8][32];
int length = this.display[0].length;
if(length == 32) { //do nothing
currentFrame = this.display;
} else if(length <= 24) { //center
for(int i = 0; i < 8; i++) {
for(int j = 0; j < length; j++) {
currentFrame[i][j+((32-length)/2)] = this.display[i][j];
}
}
this.display = currentFrame; //set display to currentFrame so the display doesn't get centered each time
} else { //scroll
for(int i = 0; i < 8; i++) {
for(int j = 0; j < length; j++) {
if(this.scroll+j <= 32) {
currentFrame[i][j] = this.display[i][j+this.scroll];
} else {
//?
}
}
}
}
return currentFrame;
}
The code I have is effective up until the the array needs to "wrap around" to the other side. Where have I gone wrong?
I'm assuming that you're looking for a formula that would work for the else.
Usually Modulos are very helpful for wrapping around.
What you are looking for is basically
currentFrame[i][j]= this.display[i][(j+this.scroll)%length];
which works even when it's not wrapped around.

IF Statement Checking (Not Working Properly)

randomEmpty() returns a random coordinate on the n x n grid that is empty (Method works). randomAdjacent() uses randomEmpty() to select an EMPTY coordinate on the map. Comparisons are then made to see if this coordinate has an VALID adjacent coordinate that is NON-EMPTY. The PROBLEM is that randomAdjacent does not always return the coordinates of space with an adjacent NON-EMPTY space. It will always return valid coordinates but not the latter. I can't spot the problem. Can someone help me identify the problem?
public int[] randomEmpty()
{
Random r = new Random();
int[] random = new int[2];
int row = r.nextInt(array.length);
int column = r.nextInt(array.length);
while(!(isEmpty(row,column)))
{
row = r.nextInt(array.length);
column = r.nextInt(array.length);
}
random[0] = row+1;
random[1] = column+1;
return random;
}
public int[] randomAdjacent()
{
int[] adjacentToX = new int[8];
int[] adjacentToY = new int[8];
int[] adjacentFrom = randomEmpty();
int count;
boolean isTrue = false;
boolean oneAdjacentNotEmpty = false;
while(!(oneAdjacentNotEmpty))
{
count = 0;
if(validIndex(adjacentFrom,1,-1))
{
adjacentToX[count] = adjacentFrom[0]+1;
adjacentToY[count] = adjacentFrom[1]-1;
count++;
}
if(validIndex(adjacentFrom,0,-1))
{
adjacentToX[count] = adjacentFrom[0];
adjacentToY[count] = adjacentFrom[1]-1;
count++;
}
if(validIndex(adjacentFrom,-1,-1))
{
adjacentToX[count] = adjacentFrom[0]-1;
adjacentToY[count] = adjacentFrom[1]-1;
count++;
}
if(validIndex(adjacentFrom,-1,0))
{
adjacentToX[count] = adjacentFrom[0]-1;
adjacentToY[count] = adjacentFrom[1];
count++;
}
if(validIndex(adjacentFrom,-1,1))
{
adjacentToX[count] = adjacentFrom[0]-1;
adjacentToY[count] = adjacentFrom[1]+1;
count++;
}
if(validIndex(adjacentFrom,0,1))
{
adjacentToX[count] = adjacentFrom[0];
adjacentToY[count] = adjacentFrom[1]+1;
count++;
}
if(validIndex(adjacentFrom,1,1))
{
adjacentToX[count] = adjacentFrom[0]+1;
adjacentToY[count] = adjacentFrom[1]+1;
count++;
}
if(validIndex(adjacentFrom,1,0))
{
adjacentToX[count] = adjacentFrom[0]+1;
adjacentToY[count] = adjacentFrom[1];
count++;
}
for(int i = 0; i < count; i++)
{
if(!(isEmpty(adjacentToX[i],adjacentToY[i])))
{
oneAdjacentNotEmpty = true;
isTrue = true;
}
}
if(isTrue)
break;
else
adjacentFrom = randomEmpty();
}
return adjacentFrom;
}
public boolean validIndex(int[] a,int i, int j)
{
try
{
Pebble aPebble = array[a[0]+i][a[1]+j];
return true;
}
catch(ArrayIndexOutOfBoundsException e)
{
return false;
}
}
public void setCell(int xPos, int yPos, Pebble aPebble)
{
array[xPos-1][yPos-1] = aPebble;
}
public Pebble getCell(int xPos, int yPos)
{
return array[xPos-1][yPos-1];
}
JUNIT Test Performed:
#Test
public void testRandomAdjacent() {
final int size = 5;
final Board board2 = new Board(size);
board2.setCell(1, 1, Pebble.O);
board2.setCell(5, 5, Pebble.O);
int[] idx = board2.randomAdjacent();
int x = idx[0];
int y = idx[1];
boolean empty = true;
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y + 1; j++) {
if ((i == x && j == y) || i < 1 || j < 1 || i > size || j > size) {
continue;
}
if (board2.getCell(i, j) != Pebble.EMPTY)
empty = false;
}
}
assertFalse(empty);// NEVER gets SET TO FALSE
assertEquals(Pebble.EMPTY, board2.getCell(x, y));
}
As for the answer: I got carried away optimizing your code for readability. I'd think it's most likely
if (board2.getCell(i, j) != Pebble.EMPTY)
empty = false;
causing the problem as getCell operates in 1-based coordinates, but i, j are in 0-based.
You should think about your logic overall. The way I see it, your code might never terminate as randomEmpty() could keep returning the same field over and over again for an undetermined period of time.
I took the liberty to recode your if-if-if cascade into utility method easier to read:
public boolean hasNonEmptyNeighbor(int[] adjacentFrom) {
for(int i = -1; i <= 1; ++i) {
for(int j = -1; j <= 1; ++j) {
if(validIndex(adjacentFrom, i, j) //Still inside the board
&& // AND
!isEmpty(adjacentFrom[0]+i //not empty
,adjacentFrom[1]+j)) {
return true;
}
}
}
return false;
}
Given my previous comment about random() being not the best of choices if you need to cover the full board, your main check (give me an empty cell with a non-empty neighbor) could be rewritten like this:
public void find() {
List<Point> foundPoints = new ArrayList<Point>();
for(int i = 0; i < Board.height; ++i) { //Assumes you have stored your height
for(int j = 0; j < Board.width; ++j) { //and your width
if(isEmpty(i, j) && hasNonEmptyNeighbor(new int[]{i,j})) {
//Found one.
foundPoints.add(new Point(i, j));
}
}
}
//If you need to return a RANDOM empty field with non-empty neighbor
//you could randomize over length of foundPoints here and select from that list.
}

Limiting duplicate random numbers

I just want to know how to limit to number of times a random number appears. I have generated random numbers of 1 to 10 and want to limit each number to appear 4 times.
myArray[i][j] = rand.nextInt(11);
for (int i=0; i < myArray.length; i++) {
for (int j=0; j < myArray[i].length; j++) {
myArray[i][j] = rand.nextInt(11);
System.out.print(" " + myArray[i][j]);
The code above creates the randoms numbers. Just want to limit them.
Since you are limited to 10 * 4 = 40 numbers you can use a list and randomize the index :
List<Integer> numbers = new ArrayList<Integer>();
for (int i = 1; i < 11; ++i) {
for (int j = 0; j < 4; ++j)
numbers.add(i);
}
And then when you assign a random number :
int i = rand.nextInt(numbers.size());
myArray[i][j] = numbers.get(i);
numbers.remove(i);
This assumes your two dimensional will not contain more then 40 numbers
My solution stores the result in arrayList:
public class Example {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
final int range = 10;
int[] numbers = new int[range + 1];
int sum = 0;
final int noOfOccurances = 4;
final int size = range * noOfOccurances;
Arrays.fill(numbers, 0);
Random generator = new Random();
List<Integer> numbersArray = new ArrayList<>();
while (sum != size) {
int randomNumber = generator.nextInt(range) + 1;
if (numbers[randomNumber] != noOfOccurances) {
numbers[randomNumber]++;
sum++;
numbersArray.add(randomNumber);
}
}
System.out.println(numbersArray);
}
}
How about storing the count of the generated int's in an array, or Map, or anything?
Map<Integer, Integer> randomCounts = new HashMap<Integer, Integer>();
... your for loops
myArray[i][j] = rand.nextInt(11);
if (randomCounts.containsKey(myArray[i][j])) {
randomCounts.put(myArray[i][j],randomCounts.get(myArray[i][j])+1);
} else {
randomCounts.put(myArray[i][j],1);
}
And if you want to check them, just iterate through your map, and voilá. :)
You can make a method to check if the generated number exists more than 4 times in the array and create a new random number if it does. It should look like this:
import java.util.Random;
public class rndNumberGenerator {
public static void main (String[] args) {
int[][] myArray = new int[2][5];
Random rand = new Random();
int randomNumber;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 5; j++) {
do {
randomNumber = rand.nextInt(11);
} while(overMax(myArray, randomNumber) == true);
myArray[i][j] = randomNumber;
System.out.print(" " + myArray[i][j]);
}
}
}
public static boolean overMax(int[][] array, int number) {
int max = 4;
int count = 0;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 5; j++) {
if (array[i][j] == number) {
count++;
}
}
}
if (count >= max)
return true;
else
return false;
}
}
Hope this helped you, if you have any other questions feel free to ask.
I take suggestion by pshemek (vote up): instead the ArrayList, I use the Set because it can't contain duplicate numbers and you have'nt to espicitate control.
An implementation: the copy{right, left} is of pshemek, I had only extended the idea:)
public class Example {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
int[] numbers = new int[11];
int sum = 0;
final int range = 10;
final int noOfOccurances = 4;
Arrays.fill(numbers, 0);
Random generator = new Random();
Set<Integer> numbersArray = new TreeSet<Integer>();
while (sum != range * noOfOccurances) {
int randomNumber = generator.nextInt(range) + 1;
sum++;//correction for first comment
numbersArray.add(randomNumber); // randomNumber will never be twice: a Set cointains ever one and only one instance of an determinated element
}
System.out.println(numbersArray);
}
}//end class
You could write your own:
public static class CountedRandom {
// My rng.
Random rand = new Random();
// Keeps track of the counts so far.
Map<Integer, Integer> counts = new HashMap<Integer, Integer>();
// The limit I must apply.
final int limit;
public CountedRandom(int limit) {
this.limit = limit;
}
public int nextInt(int l) {
int r;
do {
// Keep getting a new number until we hit one that has'n been overused.
r = rand.nextInt(l);
} while (count(r) >= limit);
return r;
}
private int count(int r) {
// How many times have we seen this one so far.
Integer counted = counts.get(r);
if ( counted == null ) {
// Never!
counted = new Integer(0);
}
// Remember the new value.
counts.put(r, counted + 1);
// Returns 0 first time around.
return counted;
}
}
public void test() {
CountedRandom cr = new CountedRandom(4);
for ( int i = 0; i < 50; i++ ) {
System.out.print(cr.nextInt(4)+",");
}
System.out.println();
}
Note that this will hang if you ask for too may numbers in too small a range (as I have in my test).
Prints
2,0,1,2,1,1,3,3,0,3,0,2,2,0,1,3,
and then hangs.

Optimizing N queens puzzle

I'm trying to solve the problem of positioning N queens on NxN board without row, column and diagonal conflicts. I use an algorithm with minimizing the conflicts. Firstly, on each column randomly a queen is positioned. After that, of all conflict queens randomly one is chosen and for her column are calculated the conflicts of each possible position. Then, the queen moves to the best position with min number of conflicts. It works, but it runs extremely slow. My goal is to make it run fast for 10000 queens. Would you, please, suggest me some improvements or maybe notice some mistakes in my logic?
Here is my code:
public class Queen {
int column;
int row;
int d1;
int d2;
public Queen(int column, int row, int d1, int d2) {
super();
this.column = column;
this.row = row;
this.d1 = d1;
this.d2 = d2;
}
#Override
public String toString() {
return "Queen [column=" + column + ", row=" + row + ", d1=" + d1
+ ", d2=" + d2 + "]";
}
#Override
public boolean equals(Object obj) {
return ((Queen)obj).column == this.column && ((Queen)obj).row == this.row;
}
}
And:
import java.util.HashSet;
import java.util.Random;
public class SolveQueens {
public static boolean printBoard = false;
public static int N = 100;
public static int maxSteps = 2000000;
public static int[] queens = new int[N];
public static Random random = new Random();
public static HashSet<Queen> q = new HashSet<Queen>();
public static HashSet rowConfl[] = new HashSet[N];
public static HashSet d1Confl[] = new HashSet[2*N - 1];
public static HashSet d2Confl[] = new HashSet[2*N - 1];
public static void init () {
int r;
rowConfl = new HashSet[N];
d1Confl = new HashSet[2*N - 1];
d2Confl = new HashSet[2*N - 1];
for (int i = 0; i < N; i++) {
r = random.nextInt(N);
queens[i] = r;
Queen k = new Queen(i, r, i + r, N - 1 + i - r);
q.add(k);
if (rowConfl[k.row] == null) {
rowConfl[k.row] = new HashSet<Queen>();
}
if (d1Confl[k.d1] == null) {
d1Confl[k.d1] = new HashSet<Queen>();
}
if (d2Confl[k.d2] == null) {
d2Confl[k.d2] = new HashSet<Queen>();
}
((HashSet<Queen>)rowConfl[k.row]).add(k);
((HashSet<Queen>)d1Confl[k.d1]).add(k);
((HashSet<Queen>)d2Confl[k.d2]).add(k);
}
}
public static void print () {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(queens[i] == j ? "♕ " : "◻◻◻ ");
}
System.out.println();
}
System.out.println();
}
public static boolean checkItLinear() {
Queen r = choseConflictQueen();
if (r == null) {
return true;
}
Queen newQ = findNewBestPosition(r);
q.remove(r);
q.add(newQ);
rowConfl[r.row].remove(r);
d1Confl[r.d1].remove(r);
d2Confl[r.d2].remove(r);
if (rowConfl[newQ.row] == null) {
rowConfl[newQ.row] = new HashSet<Queen>();
}
if (d1Confl[newQ.d1] == null) {
d1Confl[newQ.d1] = new HashSet<Queen>();
}
if (d2Confl[newQ.d2] == null) {
d2Confl[newQ.d2] = new HashSet<Queen>();
}
((HashSet<Queen>)rowConfl[newQ.row]).add(newQ);
((HashSet<Queen>)d1Confl[newQ.d1]).add(newQ);
((HashSet<Queen>)d2Confl[newQ.d2]).add(newQ);
queens[r.column] = newQ.row;
return false;
}
public static Queen choseConflictQueen () {
HashSet<Queen> conflictSet = new HashSet<Queen>();
boolean hasConflicts = false;
for (int i = 0; i < 2*N - 1; i++) {
if (i < N && rowConfl[i] != null) {
hasConflicts = hasConflicts || rowConfl[i].size() > 1;
conflictSet.addAll(rowConfl[i]);
}
if (d1Confl[i] != null) {
hasConflicts = hasConflicts || d1Confl[i].size() > 1;
conflictSet.addAll(d1Confl[i]);
}
if (d2Confl[i] != null) {
hasConflicts = hasConflicts || d2Confl[i].size() > 1;
conflictSet.addAll(d2Confl[i]);
}
}
if (hasConflicts) {
int c = random.nextInt(conflictSet.size());
return (Queen) conflictSet.toArray()[c];
}
return null;
}
public static Queen findNewBestPosition(Queen old) {
int[] row = new int[N];
int min = Integer.MAX_VALUE;
int minInd = old.row;
for (int i = 0; i < N; i++) {
if (rowConfl[i] != null) {
row[i] = rowConfl[i].size();
}
if (d1Confl[old.column + i] != null) {
row[i] += d1Confl[old.column + i].size();
}
if (d2Confl[N - 1 + old.column - i] != null) {
row[i] += d2Confl[N - 1 + old.column - i].size();
}
if (i == old.row) {
row[i] = row[i] - 3;
}
if (row[i] <= min && i != minInd) {
min = row[i];
minInd = i;
}
}
return new Queen(old.column, minInd, old.column + minInd, N - 1 + old.column - minInd);
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
init();
int steps = 0;
while(!checkItLinear()) {
if (++steps > maxSteps) {
init();
steps = 0;
}
}
long endTime = System.currentTimeMillis();
System.out.println("Done for " + (endTime - startTime) + "ms\n");
if(printBoard){
print();
}
}
}
Edit:
Here is my a-little-bit-optimized solution with removing some unused objects and putting the queens on diagonal positions when initializing.
import java.util.Random;
import java.util.Vector;
public class SolveQueens {
public static boolean PRINT_BOARD = true;
public static int N = 10;
public static int MAX_STEPS = 5000;
public static int[] queens = new int[N];
public static Random random = new Random();
public static int[] rowConfl = new int[N];
public static int[] d1Confl = new int[2*N - 1];
public static int[] d2Confl = new int[2*N - 1];
public static Vector<Integer> conflicts = new Vector<Integer>();
public static void init () {
random = new Random();
for (int i = 0; i < N; i++) {
queens[i] = i;
}
}
public static int getD1Pos (int col, int row) {
return col + row;
}
public static int getD2Pos (int col, int row) {
return N - 1 + col - row;
}
public static void print () {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(queens[i] == j ? "Q " : "* ");
}
System.out.println();
}
System.out.println();
}
public static boolean hasConflicts() {
generateConflicts();
if (conflicts.isEmpty()) {
return false;
}
int r = random.nextInt(conflicts.size());
int conflQueenCol = conflicts.get(r);
int currentRow = queens[conflQueenCol];
int bestRow = currentRow;
int minConfl = getConflicts(conflQueenCol, queens[conflQueenCol]) - 3;
int tempConflCount;
for (int i = 0; i < N ; i++) {
tempConflCount = getConflicts(conflQueenCol, i);
if (i != currentRow && tempConflCount <= minConfl) {
minConfl = tempConflCount;
bestRow = i;
}
}
queens[conflQueenCol] = bestRow;
return true;
}
public static void generateConflicts () {
conflicts = new Vector<Integer>();
rowConfl = new int[N];
d1Confl = new int[2*N - 1];
d2Confl = new int[2*N - 1];
for (int i = 0; i < N; i++) {
int r = queens[i];
rowConfl[r]++;
d1Confl[getD1Pos(i, r)]++;
d2Confl[getD2Pos(i, r)]++;
}
for (int i = 0; i < N; i++) {
int conflictsCount = getConflicts(i, queens[i]) - 3;
if (conflictsCount > 0) {
conflicts.add(i);
}
}
}
public static int getConflicts(int col, int row) {
return rowConfl[row] + d1Confl[getD1Pos(col, row)] + d2Confl[getD2Pos(col, row)];
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
init();
int steps = 0;
while(hasConflicts()) {
if (++steps > MAX_STEPS) {
init();
steps = 0;
}
}
long endTime = System.currentTimeMillis();
System.out.println("Done for " + (endTime - startTime) + "ms\n");
if(PRINT_BOARD){
print();
}
}
}
Comments would have been helpful :)
Rather than recreating your conflict set and your "worst conflict" queen everything, could you create it once, and then just update the changed rows/columns?
EDIT 0:
I tried playing around with your code a bit. Since the code is randomized, it's hard to find out if a change is good or not, since you might start with a good initial state or a crappy one. I tried making 10 runs with 10 queens, and got wildly different answers, but results are below.
I psuedo-profiled to see which statements were being executed the most, and it turns out the inner loop statements in chooseConflictQueen are executed the most. I tried inserting a break to pull the first conflict queen if found, but it didn't seem to help much.
Grouping only runs that took more than a second:
I realize I only have 10 runs, which is not really enough to be statistically valid, but hey.
So adding breaks didn't seem to help. I think a constructive solution will likely be faster, but randomness will again make it harder to check.
Your approach is good : Local search algorithm with minimum-conflicts constraint. I would suggest try improving your initial state. Instead of randomly placing all queens, 1 per column, try to place them so that you minimize the number of conflicts. An example would be to try placing you next queen based on the position of the previous one ... or maybe position of previous two ... Then you local search will have less problematic columns to deal with.
If you randomly select, you could be selecting the same state as a previous state. Theoretically, you might never find a solution even if there is one.
I think you woud be better to iterate normally through the states.
Also, are you sure boards other than 8x8 are solvable?
By inspection, 2x2 is not, 3x3 is not, 4x4 is not.

Categories

Resources