Skip to content

Commit fa7d357

Browse files
authored
Add tests, enhance class & function documentation for KnightsTour (TheAlgorithms#5591)
1 parent 2592a08 commit fa7d357

File tree

3 files changed

+133
-66
lines changed

3 files changed

+133
-66
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@
607607
* [ArrayCombinationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/ArrayCombinationTest.java)
608608
* [CombinationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/CombinationTest.java)
609609
* [FloodFillTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/FloodFillTest.java)
610+
* [KnightsTourTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/KnightsTourTest.java)
610611
* [MazeRecursionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/MazeRecursionTest.java)
611612
* [MColoringTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/MColoringTest.java)
612613
* [NQueensTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/NQueensTest.java)

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

Lines changed: 73 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,26 @@
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;
25+
26+
// Possible moves for a knight in chess
3427
private static final int[][] MOVES = {
3528
{1, -2},
3629
{2, -1},
@@ -40,36 +33,40 @@ private KnightsTour() {
4033
{-2, 1},
4134
{-2, -1},
4235
{-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
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;
4643

47-
public static void main(String[] args) {
44+
/**
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.
48+
*/
49+
public static void resetBoard() {
4850
grid = new int[BASE][BASE];
4951
total = (BASE - 4) * (BASE - 4);
50-
5152
for (int r = 0; r < BASE; r++) {
5253
for (int c = 0; c < BASE; c++) {
5354
if (r < 2 || r > BASE - 3 || c < 2 || c > BASE - 3) {
54-
grid[r][c] = -1;
55+
grid[r][c] = -1; // Mark boundary cells
5556
}
5657
}
5758
}
58-
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-
}
6959
}
7060

71-
// Return True when solvable
72-
private static boolean solve(int row, int column, int count) {
61+
/**
62+
* Recursive method to solve the Knight's Tour problem.
63+
*
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
68+
*/
69+
static boolean solve(int row, int column, int count) {
7370
if (count > total) {
7471
return true;
7572
}
@@ -80,49 +77,72 @@ private static boolean solve(int row, int column, int count) {
8077
return false;
8178
}
8279

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

8583
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)) {
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)) {
9088
return true;
9189
}
92-
grid[row][column] = 0;
90+
grid[nextRow][nextCol] = 0; // Backtrack
9391
}
9492

9593
return false;
9694
}
9795

98-
// Returns List of neighbours
99-
private static List<int[]> neighbors(int row, int column) {
96+
/**
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}
103+
*/
104+
static List<int[]> neighbors(int row, int column) {
100105
List<int[]> neighbour = new ArrayList<>();
101106

102107
for (int[] m : MOVES) {
103108
int x = m[0];
104109
int y = m[1];
105-
if (grid[row + y][column + x] == 0) {
110+
if (row + y >= 0 && row + y < BASE && column + x >= 0 && column + x < BASE && grid[row + y][column + x] == 0) {
106111
int num = countNeighbors(row + y, column + x);
107112
neighbour.add(new int[] {row + y, column + x, num});
108113
}
109114
}
110115
return neighbour;
111116
}
112117

113-
// Returns the total count of neighbors
114-
private static int countNeighbors(int row, int column) {
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) {
115126
int num = 0;
116127
for (int[] m : MOVES) {
117-
if (grid[row + m[1]][column + m[0]] == 0) {
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) {
118131
num++;
119132
}
120133
}
121134
return num;
122135
}
123136

124-
// Returns true if it is orphan
125-
private static boolean orphanDetected(int count, int row, int column) {
137+
/**
138+
* Detects if moving to a given position will create an orphan (a position with no further valid moves).
139+
*
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
144+
*/
145+
static boolean orphanDetected(int count, int row, int column) {
126146
if (count < total - 1) {
127147
List<int[]> neighbor = neighbors(row, column);
128148
for (int[] nb : neighbor) {
@@ -133,17 +153,4 @@ private static boolean orphanDetected(int count, int row, int column) {
133153
}
134154
return false;
135155
}
136-
137-
// Prints the result grid
138-
private static void printResult() {
139-
for (int[] row : grid) {
140-
for (int i : row) {
141-
if (i == -1) {
142-
continue;
143-
}
144-
System.out.printf("%2d ", i);
145-
}
146-
System.out.println();
147-
}
148-
}
149156
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.thealgorithms.backtracking;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import java.util.List;
7+
import org.junit.jupiter.api.BeforeEach;
8+
import org.junit.jupiter.api.Test;
9+
10+
public class KnightsTourTest {
11+
12+
@BeforeEach
13+
void setUp() {
14+
// Call the reset method in the KnightsTour class
15+
KnightsTour.resetBoard();
16+
}
17+
18+
@Test
19+
void testGridInitialization() {
20+
for (int r = 0; r < 12; r++) {
21+
for (int c = 0; c < 12; c++) {
22+
if (r < 2 || r > 12 - 3 || c < 2 || c > 12 - 3) {
23+
assertEquals(-1, KnightsTour.grid[r][c], "Border cells should be -1");
24+
} else {
25+
assertEquals(0, KnightsTour.grid[r][c], "Internal cells should be 0");
26+
}
27+
}
28+
}
29+
}
30+
31+
@Test
32+
void testCountNeighbors() {
33+
// Manually place a knight at (3, 3) and mark nearby cells to test counting
34+
KnightsTour.grid[3][3] = 1; // Knight is here
35+
KnightsTour.grid[5][4] = -1; // Block one potential move
36+
37+
int neighborCount = KnightsTour.countNeighbors(3, 3);
38+
assertEquals(3, neighborCount, "Knight at (3, 3) should have 3 neighbors (one blocked)");
39+
40+
KnightsTour.grid[4][1] = -1; // Block another move
41+
neighborCount = KnightsTour.countNeighbors(3, 3);
42+
assertEquals(3, neighborCount, "Knight at (3, 3) should have 3 neighbors (two blocked)");
43+
}
44+
45+
@Test
46+
void testNeighbors() {
47+
// Test the list of valid neighbors for a given cell (3, 3)
48+
List<int[]> neighbors = KnightsTour.neighbors(3, 3);
49+
assertEquals(4, neighbors.size(), "Knight at (3, 3) should have 8 valid neighbors");
50+
}
51+
52+
@Test
53+
void testSolveSuccessful() {
54+
// Test if the solve method works for a successful knight's tour
55+
KnightsTour.grid[2][2] = 1; // Start the knight at (2, 2)
56+
boolean result = KnightsTour.solve(2, 2, 2);
57+
assertTrue(result, "solve() should successfully complete a Knight's tour");
58+
}
59+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy