Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* (C) Copyright 2018-2021, by Kirill Vishnyakov and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* See the CONTRIBUTORS.md file distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the
* GNU Lesser General Public License v2.1 or later
* which is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
*/
package org.jgrapht.demo;
import org.jgrapht.alg.util.*;
import java.util.*;
/**
* Enum type that represents two knight's tour types: closed and open.
*/
enum TourType
{
CLOSED,
OPEN
}
/**
* Class that represents container for knight's tour.
*/
class KnightTour
{
/**
* Implementation of a doubly linked list data structure that is being used for storing a tour.
*
* @param type of a value storing in a node.
*/
class DoublyLinkedList
{
/**
* Pointer to the head of the list.
*/
private Node head;
/**
* Pointer to the tail of the list.
*/
private Node tail;
/**
* Pointer to the start node. Start node is the node from which we start any traversal
* operation on the list.
*/
private Node startNode;
/**
* Size of the list.
*/
private int size;
public DoublyLinkedList()
{
head = null;
tail = null;
startNode = null;
size = 0;
}
public int getSize()
{
return size;
}
public boolean isEmpty()
{
return head == null;
}
/**
* Adds element to the end of the list.
*
* @param element we want to add.
*/
public void add(E element)
{
Node node = new Node<>(element);
size++;
if (isEmpty()) {
node.next = null;
node.prev = null;
head = node;
tail = node;
return;
}
tail.next = node;
node.prev = tail;
node.next = null;
tail = node;
}
/**
* Removes tail element.
*/
public void remove()
{
if (isEmpty()) {
throw new IndexOutOfBoundsException("The list is empty!");
}
size--;
if (tail.prev == null) {
head = null;
tail = null;
return;
}
tail = tail.prev;
tail.next = null;
}
public Node getHead()
{
return head;
}
public Node getTail()
{
return tail;
}
public void clear()
{
head = null;
tail = null;
size = 0;
}
public void setStartNode(Node startNode)
{
this.startNode = startNode;
}
public Node getStartNode()
{
return startNode;
}
public void setSize(int i)
{
size = i;
}
}
/**
* Static class that represents a node.
*
* @param type of the value stored in the node.
*
*/
static class Node
{
/**
* Pointer to the next node.
*/
private Node next;
/**
* Pointer to the previous node.
*/
private Node prev;
/**
* Value that is being stored in the node.
*/
private E value;
/**
* Boolean flag that is being used in traversal function, such as toList. True if the node
* was visited, otherwise false.
*/
private boolean visited = false;
public Node(E value)
{
this.value = value;
}
public Node()
{
}
public boolean isVisited()
{
return !visited;
}
public void setVisited(boolean visited)
{
this.visited = visited;
}
public E getValue()
{
return value;
}
public Node getNext()
{
return next;
}
public Node getPrev()
{
return prev;
}
public void setPrev(Node prev)
{
this.prev = prev;
}
public void setNext(Node next)
{
this.next = next;
}
}
/**
* Doubly linked list that stores nodes in order of their appearance in the knight's tour.
*/
private final DoublyLinkedList> list;
/*
* Let's call each of the following 8 cells structured:
*
* (enumeration starts with 0 to make the relation between cells and indices in structured array
* more clear)
*
* 0). (2, 0); 1). (0, 1); 2). (n - 1, 0); 3). (n - 2, 2); 4). (1, m - 3); 5). (0, m - 1); 6).
* (n - 1, m - 2); 7). (n - 3, m - 1);
*
* ######################################### #**0***********************************2#
* #1**************************************# #*************************************3*#
* #***************************************# #***************************************#
* #***************************************# #***************************************#
* #***************************************# #***************************************#
* #***************************************# #***************************************#
* #***************************************# #***************************************#
* #***************************************# #*4*************************************#
* #**************************************6# #5***********************************7**#
* #########################################
*
* Structured cells are needed in the the merging procedure in the Parberry's algorithm.
*/
/**
* ArrayList that stores pointers on the structured cells.
*/
private final ArrayList>> structured;
/**
* Used in toList function.
*/
private List> arrayList;
/**
* Constructor of knight's tour container.
*/
public KnightTour()
{
structured = new ArrayList<>(Collections.nCopies(8, new KnightTour.Node<>()));
list = new DoublyLinkedList<>();
arrayList = null;
}
/**
* Converts knight's tour represented as DoublyLinkedList to ArrayList.
*
* @return ArrayList that contains knight's tour.
*/
public List> toList()
{
if (arrayList != null) {
return arrayList;
}
Node> startNode = list.getStartNode();
startNode.setVisited(true);
arrayList = new ArrayList<>();
arrayList.add(startNode.getValue());
/*
* Traverse of the list.
*/
while (startNode.getNext().isVisited() || startNode.getPrev().isVisited()) {
if (startNode.getNext().isVisited())
startNode = startNode.getNext();
else {
startNode = startNode.getPrev();
}
arrayList.add(startNode.getValue());
startNode.setVisited(true);
}
return arrayList;
}
public DoublyLinkedList> getList()
{
return list;
}
public ArrayList>> getStructured()
{
return structured;
}
}
/**
* Implementation of {@literal }Warnsdorff's
* rule{@literal } - heuristic for finding a knight's tour on chessboards.
*
* A knight's tour is a sequence of moves of a knight on a chessboard such that the knight visits
* every square only once. If the knight ends on a square that is one knight's move from the
* beginning square (so that it could tour the board again immediately, following the same path),
* the tour is closed, otherwise it is open.
*
* The knight's tour problem is the mathematical problem of finding a knight's tour.
*
* Description of the Warnsdorff's rule: set a start cell. Always proceed to the cell that have the
* fewest onward moves. In case of a tie(i.e. there exist more than one possible choice for the next
* cell) go to the cell with largest Euclidean distance from the center of the board.
*
* This implementation also allows you to find a structured knight's tour.
*
* Knight's tour on board of size $n \times m$ is called structured if it contains the following $8$
* UNDIRECTED moves:
*
* 1). $(1, 0) \to (0, 2)$ - denoted as $1$ on the picture below. 2). $(2, 0) \to (0, 1)$ - denoted
* as $2$ on the picture below. 3). $(n - 3, 0) \to (n - 1, 1)$ - denoted as $3$ on the picture
* below. 4). $(n - 2, 0) \to (n - 1, 2)$ - denoted as $4$ on the picture below. 5). $(0, m - 3) \to
* (1, m - 1)$ - denoted as $5$ on the picture below. 6). $(0, m - 2) \to (2, m - 1)$ - denoted as
* $6$ on the picture below. 7). $(n - 3, m - 1) \to (n - 1, m - 2)$ - denoted as $7$ on the picture
* below. 8). $(n - 2, m - 1) \to (n - 1, m - 3)$ - denoted as $8$ on the picture below.
*
* ######################################### #*12*********************************34*#
* #2*************************************3# #1*************************************4#
* #***************************************# #***************************************#
* #***************************************# #***************************************#
* #***************************************# #***************************************#
* #***************************************# #***************************************#
* #***************************************# #***************************************#
* #***************************************# #6*************************************8#
* #5*************************************7# #*65*********************************78*#
* #########################################
*
* If you are confused with the formal definition of the structured knight's tour please refer to
* illustration on the page $3$ of the paper "An efficient algorithm for the Knight’s tour problem "
* by Ian Parberry.
*
* One more feature of this implementation is that it provides an option to return a shifted
* knight's tour, where all cell's coordinates are shifted by some values. Basically it is the same
* as knight's tour of some piece of the board.
*/
public class WarnsdorffRuleKnightTourHeuristic
{
/**
* Width of the board.
*/
private int n;
/**
* Height of the board.
*/
private int m;
/**
* 2d array that stores information whether or not the cell has been visited.
*/
private boolean[][] chessBoard;
/**
* Auxiliary array for offset in x coordinate when performing a move.
*/
private final static int[] DX = new int[] { 1, 2, 2, 1, -1, -2, -2, -1 };
/**
* Auxiliary array for offset in y coordinate when performing a move.
*/
private final static int[] DY = new int[] { 2, 1, -1, -2, -2, -1, 1, 2 };
/**
* Constructor.
*
* @param n width and height of the board.
*/
public WarnsdorffRuleKnightTourHeuristic(int n)
{
if (n < 3) {
throw new IllegalArgumentException("Incorrect board size!");
}
this.n = n;
this.m = n;
chessBoard = new boolean[n][n];
}
/**
* Constructor.
*
* @param n width of the board.
* @param m height of the board.
*/
public WarnsdorffRuleKnightTourHeuristic(int n, int m)
{
if ((n < 3 && m < 3) || n <= 1 || m <= 1) {
throw new IllegalArgumentException("Incorrect board size!");
}
this.n = n;
this.m = m;
chessBoard = new boolean[n][m];
}
/**
* Calculates the number of the unvisited neighbours of the given cell.
*
* @param currentCell represents cell for which we want to find the unvisited neighbours.
* @return number of unvisited edges.
*/
private int getNumberOfUnusedNeighbours(Pair currentCell)
{
int ans = 0;
for (int i = 0; i < 8; i++) {
int newX = currentCell.getFirst() + DX[i];
int newY = currentCell.getSecond() + DY[i];
if (newX >= 0 && newX < n && newY >= 0 && newY < m && !chessBoard[newX][newY]) {
ans++;
}
}
return ans;
}
/**
* Function for handling a tie case. In case of a tie the next cell will be the cell with the
* largest Euclidean distance from the center of the board.
*
* @param array that stores the cells with equal number of unvisited neighbours.
* @return index of the next cell in the input array.
*/
private int handleTie(ArrayList> array)
{
int index = -1;
int distance = -1;
int xCenter = n / 2;
int yCenter = m / 2;
for (int i = 0; i < array.size(); i++) {
int x = array.get(i).getFirst();
int y = array.get(i).getSecond();
if ((x - xCenter) * (x - xCenter) + (y - yCenter) * (y - yCenter) > distance) {
distance = (x - xCenter) * (x - xCenter) + (y - yCenter) * (y - yCenter);
index = i;
}
}
return index;
}
/**
* Finds the next cell to move.
*
* @param cell represents start point of the move.
* @return cell represents end point of the move.
*/
private Pair getMoveWarnsdorff(Pair cell)
{
int curValue = Integer.MAX_VALUE;
Pair currentCell = new Pair<>(-1, -1);
Pair nextCell = new Pair<>(-1, -1);
ArrayList> tie = new ArrayList<>();
for (int i = 0; i < 8; i++) {
int newX = cell.getFirst() + DX[i];
int newY = cell.getSecond() + DY[i];
currentCell.setFirst(newX);
currentCell.setSecond(newY);
if (newX >= 0 && newX < n && newY >= 0 && newY < m && !chessBoard[newX][newY]) {
int adjValue = getNumberOfUnusedNeighbours(currentCell);
if (adjValue < curValue) {
curValue = adjValue;
nextCell.setFirst(currentCell.getFirst());
nextCell.setSecond(currentCell.getSecond());
tie.clear();
tie.add(new Pair<>(currentCell.getFirst(), currentCell.getSecond()));
} else if (adjValue == curValue) {
tie.add(new Pair<>(newX, newY));
}
}
}
if (tie.size() > 1) {
int index = handleTie(tie);
nextCell.setFirst(tie.get(index).getFirst());
nextCell.setSecond(tie.get(index).getSecond());
}
return nextCell;
}
/**
* Checks type of the found tour.
*
* @param startX start coordinate on x-axis.
* @param startY start coordinate on y-axis.
* @param endX end coordinate on x-axis.
* @param endY end coordinate on y-axis.
* @param type type of the tour we want to find.
* @return true, if the found tour satisfies the required invariants, otherwise false.
*/
private boolean checkType(int startX, int startY, int endX, int endY, TourType type)
{
if (type == TourType.CLOSED) {
return Math.abs(startX - endX) == 1 && Math.abs(startY - endY) == 2
|| Math.abs(startX - endX) == 2 && Math.abs(startY - endY) == 1;
}
return !(Math.abs(startX - endX) == 1 && Math.abs(startY - endY) == 2
|| Math.abs(startX - endX) == 2 && Math.abs(startY - endY) == 1);
}
/**
* Checks if the found tour is structured. Note, we don't know the direction of the edges in the
* knight's tour, so we have to check both options, i.e. $a \to b$ and $b \to a$.
*
* @param moves preformed in the tour.
* @param structured true if user asked to find a structured knight's tour, false otherwise.
* @return true if the user didn't ask to find a structured knight's tour or if the tour
* contains all the moves needed for tour to be structured, false otherwise.
*/
private boolean checkStructured(
HashSet, Pair>> moves, boolean structured)
{
return !structured || ((moves.contains(new Pair<>(new Pair<>(1, 0), new Pair<>(0, 2)))
|| moves.contains(new Pair<>(new Pair<>(0, 2), new Pair<>(1, 0))))
&& moves.contains(new Pair<>(new Pair<>(2, 0), new Pair<>(0, 1)))
|| moves.contains(new Pair<>(new Pair<>(0, 1), new Pair<>(2, 0)))
&&
moves.contains(new Pair<>(new Pair<>(n - 3, 0), new Pair<>(n - 1, 1)))
|| moves.contains(new Pair<>(new Pair<>(n - 1, 1), new Pair<>(n - 3, 0)))
&& moves.contains(new Pair<>(new Pair<>(n - 2, 0), new Pair<>(n - 1, 2)))
|| moves.contains(new Pair<>(new Pair<>(n - 1, 2), new Pair<>(n - 2, 0)))
&&
moves.contains(new Pair<>(new Pair<>(0, m - 3), new Pair<>(1, m - 1)))
|| moves.contains(new Pair<>(new Pair<>(1, m - 1), new Pair<>(0, m - 3)))
&& moves.contains(new Pair<>(new Pair<>(0, m - 2), new Pair<>(2, m - 1)))
|| moves.contains(new Pair<>(new Pair<>(2, m - 1), new Pair<>(0, m - 2)))
&&
moves.contains(new Pair<>(new Pair<>(n - 3, m - 1), new Pair<>(n - 1, m - 2)))
|| moves.contains(new Pair<>(new Pair<>(n - 1, m - 2), new Pair<>(n - 3, m - 1)))
&& moves.contains(new Pair<>(new Pair<>(n - 2, m - 1), new Pair<>(n - 1, m - 3)))
|| moves.contains(new Pair<>(new Pair<>(n - 1, m - 3), new Pair<>(n - 2, m - 2))));
}
/**
* Converts doubly linked list of chessboard cells to the set of moves.
*
* @param tour we have found.
* @return set of moves of the input tour.
*/
private HashSet, Pair>> getMoves(
KnightTour.DoublyLinkedList> tour)
{
HashSet, Pair>> moves = new HashSet<>();
KnightTour.Node> headNode = tour.getHead();
KnightTour.Node> nextNode = headNode.getNext();
while (nextNode != null) {
moves.add(new Pair<>(headNode.getValue(), nextNode.getValue()));
headNode = headNode.getNext();
nextNode = nextNode.getNext();
}
return moves;
}
/**
* Checks existence of the knight's tour.
*
* @param type of the tour.
* @return true if the tour exists, otherwise false.
*/
private boolean checkExistence(TourType type)
{
int newN = Math.min(n, m);
int newM = Math.max(n, m);
/*
* Allen Schwenk, 1991 Which Rectangular Chessboards Have a Knight's Tour?.
*
* Theorem: An n x m chessboard with n <= m has a closed knight's tour unless one or more of
* these three condition holds: (a) n and m are both odd; (b) n = 1, 2, 4; (c) n = 3 and m =
* 4, 6, 8.
*/
if (type == TourType.CLOSED) {
return !((newN % 2 == 1 && newM % 2 == 1) || newN == 1 || newN == 2 || newN == 4
|| (newN == 3 && (newM == 4 || newM == 6 || newM == 8)));
}
/*
* Regarding open knight's tour existence, refer to
* http://gaebler.us/share/Knight_tour.html.
*
* Rob Gaebler, Tsu-wang Yang, Knight's Tours (August 13, 1999).
*/
return (newN == 3 && newM == 4 || newN == 3 && newM >= 7 || newN >= 4 && newM >= 5);
}
/**
* Updates the pointer on the cell in structured array if the last added cell was structured. If
* it is a non-structured cell then returns -1.
*
* @param cell last added to the tour cell.
* @return the index of the corresponding cell in the structured array and -1 if the last added
* cell is not a structured cell .
*/
private int updateStructuredPosition(Pair cell)
{
if (cell.getFirst() == 2 && cell.getSecond() == 0) {
return 0;
} else if (cell.getFirst() == 0 && cell.getSecond() == 1) {
return 1;
} else if (cell.getFirst() == n - 1 && cell.getSecond() == 0) {
return 2;
} else if (cell.getFirst() == n - 2 && cell.getSecond() == 2) {
return 3;
} else if (cell.getFirst() == 1 && cell.getSecond() == m - 3) {
return 4;
} else if (cell.getFirst() == 0 && cell.getSecond() == m - 1) {
return 5;
} else if (cell.getFirst() == n - 1 && cell.getSecond() == m - 2) {
return 6;
} else if (cell.getFirst() == n - 3 && cell.getSecond() == m - 1) {
return 7;
}
return -1;
}
/**
* Generates a knight's tour that satisfies the input parameters.
*
* Warnsdorff's rule heuristic is an example of a greedy method, which we use to select the next
* cell to move, and thus may fail to find a tour. However, another greedy heuristic is used to
* prevent failing: in case of a tie we will select a cell with the largest euclidean distance
* from the center of the board. Such combination of greedy methods significantly increases our
* chances to find a tour.
*
* @param type of the tour.
* @param structured true if we want the tour to be structured, otherwise false.
* @param shiftX the value will be added to each cell's x-coordinate to reach effect of
* shifting.
* @param shiftY the value will be added to each cell's t-coordinate to reach effect of
* shifting.
* @return knight's tour.
*/
public KnightTour getTour(TourType type, boolean structured, int shiftX, int shiftY)
{
if (shiftX < 0 || shiftY < 0) {
throw new IllegalArgumentException("Incorrect shift value!");
}
if (!checkExistence(type)) {
throw new IllegalArgumentException("No solution exist for such configuration!");
}
KnightTour tour = new KnightTour();
Random rand = new Random();
int startX, startY;
Pair currentCell = new Pair<>(-1, -1);
int visited;
int run = 0;
boolean[][] wasStartingVertex = new boolean[n][m];
boolean found = false;
while (!found) {
visited = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
chessBoard[i][j] = false;
}
}
tour.getList().clear();
startX = rand.nextInt(n);
startY = rand.nextInt(m);
currentCell.setFirst(startX);
currentCell.setSecond(startY);
while (wasStartingVertex[startX][startY]) {
startX = rand.nextInt(n);
startY = rand.nextInt(m);
currentCell.setFirst(startX);
currentCell.setSecond(startY);
}
wasStartingVertex[startX][startY] = true;
run++;
while (visited < n * m) {
chessBoard[currentCell.getFirst()][currentCell.getSecond()] = true;
tour.getList().add(currentCell);
/*
* If we have added the structured cell then update pointer on that cell in the
* structured array.
*/
if (structured) {
int val = updateStructuredPosition(currentCell);
if (val != -1) {
tour.getStructured().set(val, tour.getList().getTail());
}
}
visited++;
currentCell = getMoveWarnsdorff(currentCell);
if (currentCell.getFirst() == -1) {
break;
}
}
Pair endCell = tour.getList().getTail().getValue();
if (visited == n * m
&& checkType(startX, startY, endCell.getFirst(), endCell.getSecond(), type))
{
HashSet, Pair>> moves =
getMoves(tour.getList());
if (checkStructured(moves, structured)) {
found = true;
}
}
/*
* Try again if there is no unused start cells are left.
*/
if (run == (n * m) && !found) {
return null;
}
}
/*
* Perform shifting.
*/
KnightTour.Node> node = tour.getList().getHead();
while (node != null) {
node.getValue().setFirst(node.getValue().getFirst() + shiftX);
node.getValue().setSecond(node.getValue().getSecond() + shiftY);
node = node.getNext();
}
/*
* Make the list cyclic.
*/
tour.getList().getHead().setPrev(tour.getList().getTail());
tour.getList().getTail().setNext(tour.getList().getHead());
/*
* Set the start node.
*/
tour.getList().setStartNode(tour.getList().getHead());
return tour;
}
}