package aima.core.environment.nqueens;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import aima.core.util.datastructure.XYLocation;
/**
* Represents a quadratic board with a matrix of squares on which queens can be
* placed (only one per square) and moved.
*
* @author Ravi Mohan
* @author Ruediger Lunde
*/
public class NQueensBoard {
/** Parameters for initialization. */
public enum Config {
EMPTY, QUEENS_IN_FIRST_ROW, QUEEN_IN_EVERY_COL
}
/**
* X---> increases left to right with zero based index Y increases top to
* bottom with zero based index | | V
*/
int[][] squares;
/**
* Creates a board with <code>size</code> rows and size columns. Column and
* row indices start with 0.
*/
public NQueensBoard(int size) {
squares = new int[size][size];
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
squares[i][j] = 0;
}
}
}
/**
* Creates a board with <code>size</code> rows and size columns. Column and
* row indices start with 0.
*
* @param config
* Controls whether the board is initially empty or contains some
* queens.
*/
public NQueensBoard(int size, Config config) {
this(size);
if (config == Config.QUEENS_IN_FIRST_ROW) {
for (int i = 0; i < size; i++)
addQueenAt(new XYLocation(i, 0));
} else if (config == Config.QUEEN_IN_EVERY_COL) {
Random r = new Random();
for (int i = 0; i < size; i++)
addQueenAt(new XYLocation(i, r.nextInt(size)));
}
}
public void clear() {
for (int i = 0; i < getSize(); i++) {
for (int j = 0; j < getSize(); j++) {
squares[i][j] = 0;
}
}
}
public void setBoard(List<XYLocation> locations) {
clear();
for (XYLocation anAl : locations) {
addQueenAt(anAl);
}
}
public int getSize() {
return squares.length;
}
/** Column and row indices start with 0! */
public void addQueenAt(XYLocation l) {
if (!(queenExistsAt(l)))
squares[l.getXCoOrdinate()][l.getYCoOrdinate()] = 1;
}
public void removeQueenFrom(XYLocation l) {
if (squares[l.getXCoOrdinate()][l.getYCoOrdinate()] == 1) {
squares[l.getXCoOrdinate()][l.getYCoOrdinate()] = 0;
}
}
/**
* Moves the queen in the specified column (x-value of <code>l</code>) to
* the specified row (y-value of <code>l</code>). The action assumes a
* complete-state formulation of the n-queens problem.
*
* @param l
*/
public void moveQueenTo(XYLocation l) {
for (int i = 0; i < getSize(); i++)
squares[l.getXCoOrdinate()][i] = 0;
squares[l.getXCoOrdinate()][l.getYCoOrdinate()] = 1;
}
public void moveQueen(XYLocation from, XYLocation to) {
if ((queenExistsAt(from)) && (!(queenExistsAt(to)))) {
removeQueenFrom(from);
addQueenAt(to);
}
}
public boolean queenExistsAt(XYLocation l) {
return (queenExistsAt(l.getXCoOrdinate(), l.getYCoOrdinate()));
}
private boolean queenExistsAt(int x, int y) {
return (squares[x][y] == 1);
}
public int getNumberOfQueensOnBoard() {
int count = 0;
for (int i = 0; i < getSize(); i++) {
for (int j = 0; j < getSize(); j++) {
if (squares[i][j] == 1)
count++;
}
}
return count;
}
public List<XYLocation> getQueenPositions() {
ArrayList<XYLocation> result = new ArrayList<>();
for (int i = 0; i < getSize(); i++) {
for (int j = 0; j < getSize(); j++) {
if (queenExistsAt(i, j))
result.add(new XYLocation(i, j));
}
}
return result;
}
public int getNumberOfAttackingPairs() {
int result = 0;
for (XYLocation location : getQueenPositions()) {
result += getNumberOfAttacksOn(location);
}
return result / 2;
}
public int getNumberOfAttacksOn(XYLocation l) {
int x = l.getXCoOrdinate();
int y = l.getYCoOrdinate();
return numberOfHorizontalAttacksOn(x, y) + numberOfVerticalAttacksOn(x, y) + numberOfDiagonalAttacksOn(x, y);
}
public boolean isSquareUnderAttack(XYLocation l) {
int x = l.getXCoOrdinate();
int y = l.getYCoOrdinate();
return (isSquareHorizontallyAttacked(x, y) || isSquareVerticallyAttacked(x, y)
|| isSquareDiagonallyAttacked(x, y));
}
private boolean isSquareHorizontallyAttacked(int x, int y) {
return numberOfHorizontalAttacksOn(x, y) > 0;
}
private boolean isSquareVerticallyAttacked(int x, int y) {
return numberOfVerticalAttacksOn(x, y) > 0;
}
private boolean isSquareDiagonallyAttacked(int x, int y) {
return numberOfDiagonalAttacksOn(x, y) > 0;
}
private int numberOfHorizontalAttacksOn(int x, int y) {
int retVal = 0;
for (int i = 0; i < getSize(); i++) {
if ((queenExistsAt(i, y)))
if (i != x)
retVal++;
}
return retVal;
}
private int numberOfVerticalAttacksOn(int x, int y) {
int retVal = 0;
for (int j = 0; j < getSize(); j++) {
if ((queenExistsAt(x, j)))
if (j != y)
retVal++;
}
return retVal;
}
private int numberOfDiagonalAttacksOn(int x, int y) {
int retVal = 0;
int i;
int j;
// forward up diagonal
for (i = (x + 1), j = (y - 1); (i < getSize() && (j > -1)); i++, j--) {
if (queenExistsAt(i, j))
retVal++;
}
// forward down diagonal
for (i = (x + 1), j = (y + 1); ((i < getSize()) && (j < getSize())); i++, j++) {
if (queenExistsAt(i, j))
retVal++;
}
// backward up diagonal
for (i = (x - 1), j = (y - 1); ((i > -1) && (j > -1)); i--, j--) {
if (queenExistsAt(i, j))
retVal++;
}
// backward down diagonal
for (i = (x - 1), j = (y + 1); ((i > -1) && (j < getSize())); i--, j++) {
if (queenExistsAt(i, j))
retVal++;
}
return retVal;
}
@Override
public int hashCode() {
List<XYLocation> locs = getQueenPositions();
int result = 17;
for (XYLocation loc : locs) {
result = 37 * loc.hashCode();
}
return result;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o != null && getClass() == o.getClass()) {
NQueensBoard aBoard = (NQueensBoard) o;
if (aBoard.getQueenPositions().size() != getQueenPositions().size())
return false;
for (int i = 0; i < getSize(); i++) {
for (int j = 0; j < getSize(); j++) {
if (queenExistsAt(i, j) != aBoard.queenExistsAt(i, j))
return false;
}
}
return true;
}
return false;
}
public void print() {
System.out.println(getBoardPic());
}
public String getBoardPic() {
StringBuilder builder = new StringBuilder();
for (int row = 0; (row < getSize()); row++) { // row
for (int col = 0; (col < getSize()); col++) { // col
if (queenExistsAt(col, row))
builder.append(" Q ");
else
builder.append(" - ");
}
builder.append("\n");
}
return builder.toString();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (int row = 0; row < getSize(); row++) { // rows
for (int col = 0; col < getSize(); col++) { // columns
if (queenExistsAt(col, row))
builder.append('Q');
else
builder.append('-');
}
builder.append("\n");
}
return builder.toString();
}
}