Skip to content

Commit aafe1b9

Browse files
committed
Refactor KnightsTour to object-oriented design supporting NxN boards
1 parent d717ca4 commit aafe1b9

File tree

1 file changed

+68
-106
lines changed

1 file changed

+68
-106
lines changed

src/main/java/com/thealgorithms/backtracking/KnightsTour.java

Lines changed: 68 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -5,152 +5,114 @@
55
import java.util.List;
66

77
/**
8-
* The KnightsTour class solves the Knight's Tour problem using backtracking.
8+
* Solves the Knight's Tour problem using backtracking combined with
9+
* Warnsdorff's heuristic for improved efficiency.
910
*
10-
* Problem Statement:
11-
* Given an N*N board with a knight placed on the first block, the knight must
12-
* move according to chess rules and visit each square on the board exactly once.
13-
* The class outputs the sequence of moves for the knight.
14-
*
15-
* Example:
16-
* Input: N = 8 (8x8 chess board)
17-
* Output: The sequence of numbers representing the order in which the knight visits each square.
11+
* A knight must visit every square on an N × N chessboard exactly once.
1812
*/
19-
public final class KnightsTour {
20-
private KnightsTour() {
21-
}
13+
public class KnightsTour {
2214

23-
// The size of the chess board (12x12 grid, with 2 extra rows/columns as a buffer around a 8x8 area)
24-
private static final int BASE = 12;
15+
private final int n; // Board dimension
16+
private final int[][] board; // Stores visiting order
17+
private final int totalSquares; // n * n
2518

26-
// Possible moves for a knight in chess
19+
// Knight's possible movements
2720
private static final int[][] MOVES = {
28-
{1, -2},
29-
{2, -1},
30-
{2, 1},
31-
{1, 2},
32-
{-1, 2},
33-
{-2, 1},
34-
{-2, -1},
35-
{-1, -2},
21+
{1, -2}, {2, -1}, {2, 1}, {1, 2},
22+
{-1, 2}, {-2, 1}, {-2, -1}, {-1, -2}
3623
};
3724

38-
// Chess grid representing the board
39-
static int[][] grid;
40-
41-
// Total number of cells the knight needs to visit
42-
static int total;
43-
4425
/**
45-
* Resets the chess board to its initial state.
46-
* Initializes the grid with boundary cells marked as -1 and internal cells as 0.
47-
* Sets the total number of cells the knight needs to visit.
26+
* Creates a Knight's Tour solver for an n × n board.
27+
*
28+
* @param n board size (must be >= 1)
4829
*/
49-
public static void resetBoard() {
50-
grid = new int[BASE][BASE];
51-
total = (BASE - 4) * (BASE - 4);
52-
for (int r = 0; r < BASE; r++) {
53-
for (int c = 0; c < BASE; c++) {
54-
if (r < 2 || r > BASE - 3 || c < 2 || c > BASE - 3) {
55-
grid[r][c] = -1; // Mark boundary cells
56-
}
57-
}
30+
public KnightsTour(int n) {
31+
if (n < 1) {
32+
throw new IllegalArgumentException("Board size must be positive");
5833
}
34+
this.n = n;
35+
this.board = new int[n][n];
36+
this.totalSquares = n * n;
5937
}
6038

6139
/**
62-
* Recursive method to solve the Knight's Tour problem.
40+
* Attempts to solve the Knight's Tour starting from (row, col).
6341
*
64-
* @param row The current row of the knight
65-
* @param column The current column of the knight
66-
* @param count The current move number
67-
* @return True if a solution is found, False otherwise
42+
* @param row starting row
43+
* @param col starting column
44+
* @return true if a complete tour exists
6845
*/
69-
static boolean solve(int row, int column, int count) {
70-
if (count > total) {
71-
return true;
72-
}
73-
74-
List<int[]> neighbor = neighbors(row, column);
46+
public boolean solve(int row, int col) {
47+
board[row][col] = 1;
48+
return backtrack(row, col, 2);
49+
}
7550

76-
if (neighbor.isEmpty() && count != total) {
77-
return false;
51+
/** Recursive solver using Warnsdorff's ordering */
52+
private boolean backtrack(int row, int col, int move) {
53+
if (move > totalSquares) {
54+
return true; // Successfully visited all squares
7855
}
7956

80-
// Sort neighbors by Warnsdorff's rule (fewest onward moves)
81-
neighbor.sort(Comparator.comparingInt(a -> a[2]));
57+
List<int[]> nextMoves = getSortedMoves(row, col);
8258

83-
for (int[] nb : neighbor) {
84-
int nextRow = nb[0];
85-
int nextCol = nb[1];
86-
grid[nextRow][nextCol] = count;
87-
if (!orphanDetected(count, nextRow, nextCol) && solve(nextRow, nextCol, count + 1)) {
59+
for (int[] m : nextMoves) {
60+
int nr = m[0], nc = m[1];
61+
board[nr][nc] = move;
62+
63+
if (backtrack(nr, nc, move + 1)) {
8864
return true;
8965
}
90-
grid[nextRow][nextCol] = 0; // Backtrack
91-
}
9266

67+
board[nr][nc] = 0; // Undo move (backtrack)
68+
}
9369
return false;
9470
}
9571

9672
/**
97-
* Returns a list of valid neighboring cells where the knight can move.
98-
*
99-
* @param row The current row of the knight
100-
* @param column The current column of the knight
101-
* @return A list of arrays representing valid moves, where each array contains:
102-
* {nextRow, nextCol, numberOfPossibleNextMoves}
73+
* Returns valid knight moves sorted by Warnsdorff degree rule.
10374
*/
104-
static List<int[]> neighbors(int row, int column) {
105-
List<int[]> neighbour = new ArrayList<>();
75+
private List<int[]> getSortedMoves(int row, int col) {
76+
List<int[]> moves = new ArrayList<>();
10677

10778
for (int[] m : MOVES) {
108-
int x = m[0];
109-
int y = m[1];
110-
if (row + y >= 0 && row + y < BASE && column + x >= 0 && column + x < BASE && grid[row + y][column + x] == 0) {
111-
int num = countNeighbors(row + y, column + x);
112-
neighbour.add(new int[] {row + y, column + x, num});
79+
int nr = row + m[0];
80+
int nc = col + m[1];
81+
82+
if (isValid(nr, nc)) {
83+
int degree = countDegree(nr, nc);
84+
moves.add(new int[] {nr, nc, degree});
11385
}
11486
}
115-
return neighbour;
87+
88+
moves.sort(Comparator.comparingInt(a -> a[2])); // Fewest onward moves first
89+
return moves;
11690
}
11791

118-
/**
119-
* Counts the number of possible valid moves for a knight from a given position.
120-
*
121-
* @param row The row of the current position
122-
* @param column The column of the current position
123-
* @return The number of valid neighboring moves
124-
*/
125-
static int countNeighbors(int row, int column) {
126-
int num = 0;
92+
/** Counts onward valid knight moves */
93+
private int countDegree(int row, int col) {
94+
int count = 0;
12795
for (int[] m : MOVES) {
128-
int x = m[0];
129-
int y = m[1];
130-
if (row + y >= 0 && row + y < BASE && column + x >= 0 && column + x < BASE && grid[row + y][column + x] == 0) {
131-
num++;
96+
int nr = row + m[0];
97+
int nc = col + m[1];
98+
if (isValid(nr, nc)) {
99+
count++;
132100
}
133101
}
134-
return num;
102+
return count;
103+
}
104+
105+
/** Checks bounds & whether square is unvisited */
106+
private boolean isValid(int row, int col) {
107+
return row >= 0 && row < n && col >= 0 && col < n && board[row][col] == 0;
135108
}
136109

137110
/**
138-
* Detects if moving to a given position will create an orphan (a position with no further valid moves).
111+
* Returns the solved board.
139112
*
140-
* @param count The current move number
141-
* @param row The row of the current position
142-
* @param column The column of the current position
143-
* @return True if an orphan is detected, False otherwise
113+
* @return board with visiting sequence
144114
*/
145-
static boolean orphanDetected(int count, int row, int column) {
146-
if (count < total - 1) {
147-
List<int[]> neighbor = neighbors(row, column);
148-
for (int[] nb : neighbor) {
149-
if (countNeighbors(nb[0], nb[1]) == 0) {
150-
return true;
151-
}
152-
}
153-
}
154-
return false;
115+
public int[][] getBoard() {
116+
return board;
155117
}
156118
}

0 commit comments

Comments
 (0)