Skip to content

Commit 02189b9

Browse files
committed
Add tests,enhance class & function documentation for KnightsTour.java, remove print and main methods
1 parent f34fe4d commit 02189b9

File tree

2 files changed

+152
-75
lines changed

2 files changed

+152
-75
lines changed

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

Lines changed: 82 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -4,72 +4,52 @@
44
import java.util.Comparator;
55
import java.util.List;
66

7-
/*
8-
* Problem Statement: -
9-
10-
Given a N*N board with the Knight placed on the first block of an empty board. Moving according
11-
to the rules of chess knight must visit each square exactly once. Print the order of each cell in
12-
which they are visited.
13-
14-
Example: -
15-
16-
Input : N = 8
17-
18-
Output:
19-
0 59 38 33 30 17 8 63
20-
37 34 31 60 9 62 29 16
21-
58 1 36 39 32 27 18 7
22-
35 48 41 26 61 10 15 28
23-
42 57 2 49 40 23 6 19
24-
47 50 45 54 25 20 11 14
25-
56 43 52 3 22 13 24 5
26-
51 46 55 44 53 4 21 12
27-
7+
/**
8+
* The KnightsTour class solves the Knight's Tour problem using backtracking.
9+
*
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.
2818
*/
2919
public final class KnightsTour {
3020
private KnightsTour() {
3121
}
3222

23+
// The size of the chess board (12x12 grid, with 2 extra rows/columns as a buffer around a 8x8 area)
3324
private static final int BASE = 12;
34-
private static final int[][] MOVES = {
35-
{1, -2},
36-
{2, -1},
37-
{2, 1},
38-
{1, 2},
39-
{-1, 2},
40-
{-2, 1},
41-
{-2, -1},
42-
{-1, -2},
43-
}; // Possible moves by knight on chess
44-
private static int[][] grid; // chess grid
45-
private static int total; // total squares in chess
46-
47-
public static void main(String[] args) {
48-
grid = new int[BASE][BASE];
49-
total = (BASE - 4) * (BASE - 4);
50-
51-
for (int r = 0; r < BASE; r++) {
52-
for (int c = 0; c < BASE; c++) {
53-
if (r < 2 || r > BASE - 3 || c < 2 || c > BASE - 3) {
54-
grid[r][c] = -1;
55-
}
56-
}
57-
}
5825

59-
int row = 2 + (int) (Math.random() * (BASE - 4));
60-
int col = 2 + (int) (Math.random() * (BASE - 4));
61-
62-
grid[row][col] = 1;
63-
64-
if (solve(row, col, 2)) {
65-
printResult();
66-
} else {
67-
System.out.println("no result");
68-
}
69-
}
70-
71-
// Return True when solvable
72-
private static boolean solve(int row, int column, int count) {
26+
// Possible moves for a knight in chess
27+
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},
36+
};
37+
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+
44+
/**
45+
* Recursive method to solve the Knight's Tour problem.
46+
*
47+
* @param row The current row of the knight
48+
* @param column The current column of the knight
49+
* @param count The current move number
50+
* @return True if a solution is found, False otherwise
51+
*/
52+
static boolean solve(int row, int column, int count) {
7353
if (count > total) {
7454
return true;
7555
}
@@ -80,49 +60,72 @@ private static boolean solve(int row, int column, int count) {
8060
return false;
8161
}
8262

63+
// Sort neighbors by Warnsdorff's rule (fewest onward moves)
8364
neighbor.sort(Comparator.comparingInt(a -> a[2]));
8465

8566
for (int[] nb : neighbor) {
86-
row = nb[0];
87-
column = nb[1];
88-
grid[row][column] = count;
89-
if (!orphanDetected(count, row, column) && solve(row, column, count + 1)) {
67+
int nextRow = nb[0];
68+
int nextCol = nb[1];
69+
grid[nextRow][nextCol] = count;
70+
if (!orphanDetected(count, nextRow, nextCol) && solve(nextRow, nextCol, count + 1)) {
9071
return true;
9172
}
92-
grid[row][column] = 0;
73+
grid[nextRow][nextCol] = 0; // Backtrack
9374
}
9475

9576
return false;
9677
}
9778

98-
// Returns List of neighbours
99-
private static List<int[]> neighbors(int row, int column) {
79+
/**
80+
* Returns a list of valid neighboring cells where the knight can move.
81+
*
82+
* @param row The current row of the knight
83+
* @param column The current column of the knight
84+
* @return A list of arrays representing valid moves, where each array contains:
85+
* {nextRow, nextCol, numberOfPossibleNextMoves}
86+
*/
87+
static List<int[]> neighbors(int row, int column) {
10088
List<int[]> neighbour = new ArrayList<>();
10189

10290
for (int[] m : MOVES) {
10391
int x = m[0];
10492
int y = m[1];
105-
if (grid[row + y][column + x] == 0) {
93+
if (row + y >= 0 && row + y < BASE && column + x >= 0 && column + x < BASE && grid[row + y][column + x] == 0) {
10694
int num = countNeighbors(row + y, column + x);
10795
neighbour.add(new int[] {row + y, column + x, num});
10896
}
10997
}
11098
return neighbour;
11199
}
112100

113-
// Returns the total count of neighbors
114-
private static int countNeighbors(int row, int column) {
101+
/**
102+
* Counts the number of possible valid moves for a knight from a given position.
103+
*
104+
* @param row The row of the current position
105+
* @param column The column of the current position
106+
* @return The number of valid neighboring moves
107+
*/
108+
static int countNeighbors(int row, int column) {
115109
int num = 0;
116110
for (int[] m : MOVES) {
117-
if (grid[row + m[1]][column + m[0]] == 0) {
111+
int x = m[0];
112+
int y = m[1];
113+
if (row + y >= 0 && row + y < BASE && column + x >= 0 && column + x < BASE && grid[row + y][column + x] == 0) {
118114
num++;
119115
}
120116
}
121117
return num;
122118
}
123119

124-
// Returns true if it is orphan
125-
private static boolean orphanDetected(int count, int row, int column) {
120+
/**
121+
* Detects if moving to a given position will create an orphan (a position with no further valid moves).
122+
*
123+
* @param count The current move number
124+
* @param row The row of the current position
125+
* @param column The column of the current position
126+
* @return True if an orphan is detected, False otherwise
127+
*/
128+
static boolean orphanDetected(int count, int row, int column) {
126129
if (count < total - 1) {
127130
List<int[]> neighbor = neighbors(row, column);
128131
for (int[] nb : neighbor) {
@@ -134,14 +137,18 @@ private static boolean orphanDetected(int count, int row, int column) {
134137
return false;
135138
}
136139

137-
// Prints the result grid
140+
/**
141+
* Prints the resulting chess board after solving the Knight's Tour.
142+
* Visited cells are displayed with their move numbers, while boundary/invalid cells are marked as "XX".
143+
*/
138144
private static void printResult() {
139145
for (int[] row : grid) {
140146
for (int i : row) {
141147
if (i == -1) {
142-
continue;
148+
System.out.print("XX "); // Invalid cells
149+
} else {
150+
System.out.printf("%2d ", i); // Print the move number
143151
}
144-
System.out.printf("%2d ", i);
145152
}
146153
System.out.println();
147154
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.thealgorithms.backtracking;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import java.util.List;
8+
import org.junit.jupiter.api.BeforeEach;
9+
import org.junit.jupiter.api.Test;
10+
11+
12+
public class KnightsTourTest {
13+
14+
@BeforeEach
15+
void setUp() {
16+
// Reset the grid and total for each test
17+
KnightsTour.grid = new int[12][12];
18+
KnightsTour.total = (12 - 4) * (12 - 4);
19+
for (int r = 0; r < 12; r++) {
20+
for (int c = 0; c < 12; c++) {
21+
if (r < 2 || r > 12 - 3 || c < 2 || c > 12 - 3) {
22+
KnightsTour.grid[r][c] = -1;
23+
}
24+
}
25+
}
26+
}
27+
28+
@Test
29+
void testGridInitialization() {
30+
// Ensure that the border squares are -1 and internal grid is 0
31+
for (int r = 0; r < 12; r++) {
32+
for (int c = 0; c < 12; c++) {
33+
if (r < 2 || r > 12 - 3 || c < 2 || c > 12 - 3) {
34+
assertEquals(-1, KnightsTour.grid[r][c], "Border cells should be -1");
35+
} else {
36+
assertEquals(0, KnightsTour.grid[r][c], "Internal cells should be 0");
37+
}
38+
}
39+
}
40+
}
41+
42+
@Test
43+
void testCountNeighbors() {
44+
// Manually place a knight at (3, 3) and mark nearby cells to test counting
45+
KnightsTour.grid[3][3] = 1; // Knight is here
46+
KnightsTour.grid[5][4] = -1; // Block one potential move
47+
48+
int neighborCount = KnightsTour.countNeighbors(3, 3);
49+
assertEquals(3, neighborCount, "Knight at (3, 3) should have 3 neighbors (one blocked)");
50+
51+
KnightsTour.grid[4][1] = -1; // Block another move
52+
neighborCount = KnightsTour.countNeighbors(3, 3);
53+
assertEquals(3, neighborCount, "Knight at (3, 3) should have 3 neighbors (two blocked)");
54+
}
55+
56+
@Test
57+
void testNeighbors() {
58+
// Test the list of valid neighbors for a given cell (3, 3)
59+
List<int[]> neighbors = KnightsTour.neighbors(3, 3);
60+
assertEquals(4, neighbors.size(), "Knight at (3, 3) should have 8 valid neighbors");
61+
}
62+
63+
@Test
64+
void testSolveSuccessful() {
65+
// Test if the solve method works for a successful knight's tour
66+
KnightsTour.grid[2][2] = 1; // Start the knight at (2, 2)
67+
boolean result = KnightsTour.solve(2, 2, 2);
68+
assertTrue(result, "solve() should successfully complete a Knight's tour");
69+
}
70+
}

0 commit comments

Comments
 (0)