Skip to content

Commit 8277350

Browse files
committed
Chapter 18 new Exercises
1 parent 4b7a49d commit 8277350

File tree

2 files changed

+211
-0
lines changed

2 files changed

+211
-0
lines changed

ch_18/Exercise18_32.java

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package ch_18;
2+
3+
import javafx.application.Application;
4+
import javafx.geometry.Point2D;
5+
import javafx.geometry.Pos;
6+
import javafx.scene.Scene;
7+
import javafx.scene.control.Button;
8+
import javafx.scene.image.ImageView;
9+
import javafx.scene.layout.BorderPane;
10+
import javafx.scene.layout.Pane;
11+
import javafx.scene.shape.Line;
12+
import javafx.stage.Stage;
13+
14+
import java.util.ArrayList;
15+
16+
/**
17+
* *18.32 (Game: Knight’s Tour) The Knight’s Tour is an ancient puzzle. The objective is
18+
* to move a knight, starting from any square on a chessboard, to every other square
19+
* once, as shown in Figure 18.15a. Note that the knight makes only L-shaped
20+
* moves (two spaces in one direction and one space in a perpendicular direction).
21+
* As shown in Figure 18.15b, the knight can move to eight squares.
22+
* <p>
23+
* Write a program that displays the moves for the knight, as shown in Figure 18.15c.
24+
* When you click a cell, the knight is placed at the cell. This cell will be starting
25+
* point for the knight. Clicking the Solve button to display the path for a solution.
26+
* <p>
27+
* <p>
28+
* (Hint: A brute-force approach for this problem is to move the knight from one
29+
* square to another available square arbitrarily. Using such an approach, your
30+
* program will take a long time to finish. A better approach is to employ some
31+
* heuristics. A knight has two, three, four, six, or eight possible moves, depending
32+
* on its location. Intuitively, you should attempt to move the knight to the least
33+
* accessible squares first and leave those more accessible squares open, so there
34+
* will be a better chance of success at the end of the search.)
35+
*/
36+
public class Exercise18_32 extends Application {
37+
private static final int SIZE = 8;
38+
private int startX = 0;
39+
private int startY = 0;
40+
private ArrayList<Point2D> moves = null;
41+
42+
public static void main(String[] args) {
43+
Application.launch(args);
44+
}
45+
46+
@Override
47+
public void start(Stage primaryStage) {
48+
BorderPane pane = new BorderPane();
49+
Board board = new Board();
50+
pane.setCenter(board);
51+
Button solveButton = new Button("Solve");
52+
pane.setBottom(solveButton);
53+
BorderPane.setAlignment(solveButton, Pos.CENTER);
54+
55+
Scene scene = new Scene(pane, 250, 250);
56+
primaryStage.setTitle(getClass().getName());
57+
primaryStage.setResizable(false);
58+
primaryStage.setScene(scene);
59+
primaryStage.show();
60+
61+
board.draw();
62+
63+
solveButton.setOnAction(e -> {
64+
boolean[][] moves = new boolean[SIZE][SIZE];
65+
moves[startX][startY] = true;
66+
resetMoves();
67+
addMove(startX, startY);
68+
solvePuzzle(moves, 1, startX, startY);
69+
board.draw();
70+
});
71+
72+
}
73+
74+
private boolean solvePuzzle(boolean[][] moves, int numMoves, int x, int y) {
75+
int nextX = 0;
76+
int nextY = 0;
77+
int bestMoveX = 0;
78+
int bestMoveY = 0;
79+
int bestMoveX2 = 0;
80+
int bestMoveY2 = 0;
81+
int minMoveCount = SIZE;
82+
int moveCount = 0;
83+
84+
for (int i = 2; i >= -2; i += -4) {
85+
for (int j = 1; j >= -1; j += -2) {
86+
nextX = x + i;
87+
nextY = y + j;
88+
if (nextX >= 0 && nextX <= SIZE - 1 && nextY >= 0 && nextY <= SIZE - 1
89+
&& !moves[nextX][nextY]) {
90+
moveCount = lookAheadCount(moves, nextX, nextY);
91+
if (moveCount <= minMoveCount) {
92+
minMoveCount = moveCount;
93+
bestMoveX2 = bestMoveX;
94+
bestMoveY2 = bestMoveY;
95+
bestMoveX = nextX;
96+
bestMoveY = nextY;
97+
}
98+
}
99+
100+
nextX = x + j;
101+
nextY = y + i;
102+
if (nextX >= 0 && nextX <= SIZE - 1 && nextY >= 0 && nextY <= SIZE - 1
103+
&& !moves[nextX][nextY]) {
104+
moveCount = lookAheadCount(moves, nextX, nextY);
105+
if (moveCount <= minMoveCount) {
106+
minMoveCount = moveCount;
107+
bestMoveX2 = bestMoveX;
108+
bestMoveY2 = bestMoveY;
109+
bestMoveX = nextX;
110+
bestMoveY = nextY;
111+
}
112+
}
113+
}
114+
}
115+
moves[bestMoveX][bestMoveY] = true;
116+
addMove(bestMoveX, bestMoveY);
117+
numMoves++;
118+
if (numMoves == (SIZE * SIZE))
119+
return true;
120+
if (moveCount > 0 && solvePuzzle(moves, numMoves, bestMoveX, bestMoveY)) {
121+
return true;
122+
}
123+
moves[bestMoveX][bestMoveY] = false;
124+
moves[bestMoveX2][bestMoveY2] = true;
125+
removeLastMoveHistory();
126+
addMove(bestMoveX2, bestMoveY2);
127+
if (moveCount > 1 && solvePuzzle(moves, numMoves, bestMoveX2, bestMoveY2)) {
128+
return true;
129+
}
130+
moves[bestMoveX2][bestMoveY2] = false;
131+
removeLastMoveHistory();
132+
numMoves--;
133+
return false;
134+
}
135+
136+
private int lookAheadCount(boolean[][] moves, int x, int y) {
137+
int maxCount = 0;
138+
for (int i = -2; i <= 2; i += 4) {
139+
for (int j = -1; j <= 1; j += 2) {
140+
int nextX = x + i;
141+
int nextY = y + j;
142+
if (nextX >= 0 && nextX <= SIZE - 1 && nextY >= 0 && nextY <= SIZE - 1
143+
&& !moves[nextX][nextY]) {
144+
maxCount++;
145+
}
146+
147+
nextX = x + j;
148+
nextY = y + i;
149+
if (nextX >= 0 && nextX <= SIZE - 1 && nextY >= 0 && nextY <= SIZE - 1
150+
&& !moves[nextX][nextY]) {
151+
maxCount++;
152+
}
153+
}
154+
}
155+
return maxCount;
156+
}
157+
158+
public void resetMoves() {
159+
moves = new ArrayList(63);
160+
}
161+
162+
public void addMove(int x, int y) {
163+
moves.add(new Point2D(x, y));
164+
}
165+
166+
public void removeLastMoveHistory() {
167+
moves.remove(moves.size() - 1);
168+
}
169+
170+
private class Board extends Pane {
171+
ImageView theKnight = new ImageView("resources/images/knight.jpg");
172+
173+
Board() {
174+
this.setOnMouseClicked(e -> {
175+
startX = (int) (e.getX() / (getWidth() / SIZE));
176+
startY = (int) (e.getY() / (getHeight() / SIZE));
177+
resetMoves();
178+
draw();
179+
});
180+
}
181+
182+
protected void draw() {
183+
this.getChildren().clear();
184+
185+
this.getChildren().add(theKnight);
186+
theKnight.setX(startX * getWidth() / SIZE);
187+
theKnight.setY(startY * getHeight() / SIZE);
188+
theKnight.setFitWidth(getWidth() / SIZE);
189+
theKnight.setFitHeight(getHeight() / SIZE);
190+
191+
for (int i = 1; i <= SIZE; i++) {
192+
this.getChildren().add(
193+
new Line(0, i * getHeight() / SIZE, getWidth(), i * getHeight() / SIZE));
194+
this.getChildren().add(
195+
new Line(i * getWidth() / SIZE, 0, i * getWidth() / SIZE, getHeight()));
196+
}
197+
198+
if (moves != null) {
199+
for (int i = 1; i < moves.size(); i++) {
200+
Point2D p1 = moves.get(i - 1);
201+
Point2D p2 = moves.get(i);
202+
this.getChildren().add(
203+
new Line(p1.getX() * (getWidth() / SIZE) + getWidth() / SIZE / 2,
204+
p1.getY() * (getHeight() / SIZE) + (getHeight() / SIZE / 2),
205+
p2.getX() * (getWidth() / SIZE) + getWidth() / SIZE / 2,
206+
p2.getY() * (getHeight() / SIZE) + getHeight() / SIZE / 2));
207+
}
208+
}
209+
}
210+
}
211+
}

resources/images/knight.jpg

6.08 KB
Loading

0 commit comments

Comments
 (0)