Skip to content

Commit a6ce01a

Browse files
committed
Working Puzzle
1 parent bd1693f commit a6ce01a

23 files changed

+419
-927
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ settings.xml
1212
TeachingKidsProgramming.Source.Java.iml
1313

1414
*.orig
15+
*.received.*

src/main/java/org/teachingextensions/approvals/lite/Approvals.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
import org.teachingextensions.approvals.lite.util.StringUtils;
99
import org.teachingextensions.approvals.lite.util.lambda.Function1;
1010
import org.teachingextensions.approvals.lite.writers.ApprovalTextWriter;
11+
import org.teachingextensions.approvals.lite.writers.ComponentApprovalWriter;
1112
import org.teachingextensions.approvals.lite.writers.ImageApprovalWriter;
1213

14+
import java.awt.*;
1315
import java.awt.image.BufferedImage;
1416
import java.util.Arrays;
1517

@@ -51,6 +53,11 @@ public static void verifyHtml(String response) throws Exception {
5153
verify(new ApprovalTextWriter(response, "html"), FileTypes.Html);
5254
}
5355

56+
public static void verify(Component component) {
57+
BufferedImage image = ComponentApprovalWriter.drawComponent(component);
58+
Approvals.verify(image);
59+
}
60+
5461
public static void verify(BufferedImage bufferedImage) {
5562
verify(new ImageApprovalWriter(bufferedImage), FileTypes.Image);
5663
}

src/main/java/org/teachingextensions/logo/Puzzle.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,8 @@ public int getDistanceToGoal() {
114114
}
115115
return distance;
116116
}
117+
118+
public int[] getCells() {
119+
return Arrays.copyOf(cells, cells.length);
120+
}
117121
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package org.teachingextensions.logo;
2+
3+
import javax.swing.*;
4+
5+
/**
6+
* Animates a puzzle from its initial state to its solution.
7+
*/
8+
public class PuzzleAnimation implements Runnable {
9+
private final PuzzleBoard board;
10+
11+
public PuzzleAnimation(PuzzleBoard board) {
12+
this.board = board;
13+
}
14+
15+
private static void update(PuzzleBoard board) {
16+
boolean done = animate(board);
17+
if(!done){
18+
return;
19+
}
20+
21+
moveNextTile(board);
22+
}
23+
24+
private static void moveNextTile(PuzzleBoard board) {
25+
if (board.hasMoves()){
26+
TileMove move = board.getNextMove();
27+
board.swap(move.getStart(), move.getEnd());
28+
}
29+
}
30+
31+
private static boolean animate(PuzzleBoard board) {
32+
Tile[] tiles = board.getTiles();
33+
for (Tile tile : tiles){
34+
if(tile != null && !tile.isAtTarget()){
35+
tile.step();
36+
return false;
37+
}
38+
}
39+
40+
return true;
41+
}
42+
43+
@Override
44+
public void run() {
45+
while (board.isVisible()) {
46+
SwingUtilities.invokeLater(new Runnable() {
47+
@Override
48+
public void run() {
49+
board.repaint();
50+
}
51+
});
52+
53+
update(board);
54+
55+
try {
56+
Thread.sleep(10);
57+
} catch (InterruptedException ignored) {
58+
}
59+
60+
}
61+
}
62+
63+
64+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package org.teachingextensions.logo;
2+
3+
import javax.swing.*;
4+
import java.awt.*;
5+
import java.util.ArrayDeque;
6+
import java.util.Arrays;
7+
import java.util.Queue;
8+
9+
/**
10+
* Draws and animates a 9-puzzle
11+
*/
12+
public class PuzzleBoard extends JPanel {
13+
private final static int blank = 8;
14+
private final Tile[] tiles;
15+
private final Queue<TileMove> solution;
16+
17+
public PuzzleBoard(Puzzle puzzle, PuzzleState solution) {
18+
super();
19+
this.solution = createSolution(solution, puzzle);
20+
this.tiles = createTiles(puzzle.getCells());
21+
}
22+
23+
private static Queue<TileMove> createSolution(PuzzleState solution, Puzzle puzzle) {
24+
if (solution == null) {
25+
return new ArrayDeque<>();
26+
}
27+
28+
int i = puzzle.getBlankIndex();
29+
Queue<TileMove> moves = new ArrayDeque<>();
30+
for (PuzzleState.Direction d : solution.getHistory()) {
31+
int m = d.getValue();
32+
TileMove move = new TileMove(i + m, i);
33+
moves.add(move);
34+
i = move.getStart();
35+
}
36+
return moves;
37+
}
38+
39+
private static Tile[] createTiles(int[] cells) {
40+
Tile[] t = new Tile[9];
41+
for (int i = 0; i < 9; i++) {
42+
if (cells[i] == blank) {
43+
continue;
44+
}
45+
46+
t[i] = new Tile(i, cells[i]);
47+
}
48+
return t;
49+
}
50+
51+
private static void drawBorder(Graphics g) {
52+
g.setColor(PenColors.Blues.DarkBlue);
53+
g.fillRect(20, 20, 410, 410);
54+
}
55+
56+
private static void drawField(Graphics g) {
57+
g.setColor(PenColors.Blues.SkyBlue);
58+
g.fillRect(30, 30, 386, 386);
59+
}
60+
61+
private static void drawTiles(Graphics g, Tile[] tiles) {
62+
Graphics2D g2d = (Graphics2D) g.create();
63+
for (Tile tile : tiles) {
64+
if (tile == null) {
65+
continue;
66+
}
67+
tile.paint(g2d);
68+
}
69+
g2d.dispose();
70+
}
71+
72+
@Override
73+
protected void paintComponent(Graphics g) {
74+
super.paintComponent(g);
75+
drawBorder(g);
76+
drawField(g);
77+
drawTiles(g, tiles);
78+
}
79+
80+
public Tile[] getTiles() {
81+
return Arrays.copyOf(tiles, tiles.length);
82+
}
83+
84+
public boolean hasMoves() {
85+
return !solution.isEmpty();
86+
}
87+
88+
public TileMove getNextMove() {
89+
return solution.isEmpty() ? null : solution.remove();
90+
}
91+
92+
public void swap(int start, int end) {
93+
Point p = Tile.getPosition(end);
94+
Tile tile = tiles[start];
95+
tile.moveTo(p);
96+
tiles[start] = null;
97+
tiles[end] = tile;
98+
99+
}
100+
}

src/main/java/org/teachingextensions/logo/PuzzlePlayer.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ public PuzzleState solve() {
3636
getVisited().add(state);
3737
if (!state.isSolution()) {
3838
this.search(state);
39+
if (getFrontier().isEmpty()){
40+
throw new IllegalStateException("Cannot solve puzzle.");
41+
}
3942
state = getFrontier().remove();
4043
}
4144
} while (!state.isSolution());

src/main/java/org/teachingkidsprogramming/section08tdd/PuzzleWindow.java renamed to src/main/java/org/teachingextensions/logo/PuzzleWindow.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
package org.teachingkidsprogramming.section08tdd;
1+
package org.teachingextensions.logo;
22

33
import org.teachingextensions.windows.ProgramWindow;
44

55
import java.awt.*;
66

7+
/**
8+
* A program window for a 9-puzzle
9+
*/
710
public class PuzzleWindow extends ProgramWindow {
811
public PuzzleWindow() {
912
super("Puzzle");
1013
this.setLayout(new BorderLayout());
1114
}
1215

13-
private static final long serialVersionUID = -1526978082665818880L;
14-
}
16+
public PuzzleWindow(PuzzleBoard board) {
17+
this();
18+
this.add(board);
19+
}
20+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.teachingextensions.logo;
2+
3+
import org.teachingextensions.approvals.lite.util.ObjectUtils;
4+
5+
import java.awt.*;
6+
7+
/**
8+
* A tile is a square on the board with an image on it.
9+
*/
10+
public class Tile {
11+
private static final String[] resources = {
12+
"Batgirl1a.png", "Batgirl2a.png", "Batgirl3a.png",
13+
"Batgirl1b.png", "Batgirl2b.png", "Batgirl3b.png",
14+
"Batgirl1c.png", "Batgirl2c.png", "Batgirl3c.png"
15+
};
16+
17+
private static final Point origin = new Point(35, 35);
18+
19+
private final Image image;
20+
private final Dimension dimension = new Dimension(122, 122);
21+
private Point position;
22+
private Point target;
23+
24+
public Tile(int cell, int image) {
25+
this.position = getPosition(cell);
26+
this.image = getImage(image);
27+
}
28+
29+
public static Point getPosition(int cell) {
30+
Point offset = Puzzle.getPosition(cell);
31+
return new Point(origin.x + (127 * offset.x), origin.y + (127 * offset.y));
32+
}
33+
34+
private static Image getImage(int image) {
35+
return ObjectUtils.loadImage(Tile.class, resources[image]);
36+
}
37+
38+
private static int stepTowardGoal(int current, int goal) {
39+
if (current < goal) {
40+
return current + 1;
41+
}
42+
if (goal < current) {
43+
return current - 1;
44+
}
45+
return current;
46+
}
47+
48+
public void paint(Graphics2D g2d) {
49+
g2d.drawImage(this.image, this.position.x, this.position.y, this.dimension.width, this.dimension.height,
50+
null);
51+
}
52+
53+
public boolean isAtTarget() {
54+
return target == null ||
55+
(target.x == position.x && target.y == position.y);
56+
}
57+
58+
public void step() {
59+
int x = stepTowardGoal(position.x, target.x);
60+
int y = stepTowardGoal(position.y, target.y);
61+
this.position = new Point(x, y);
62+
}
63+
64+
public void moveTo(Point goal) {
65+
target = goal;
66+
}
67+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.teachingextensions.logo;
2+
3+
/**
4+
* Describes a tile swap.
5+
*/
6+
public class TileMove {
7+
private final int start;
8+
private final int end;
9+
10+
public TileMove(int start, int end) {
11+
12+
this.start = start;
13+
this.end = end;
14+
}
15+
16+
public int getStart() {
17+
return start;
18+
}
19+
20+
public int getEnd() {
21+
return end;
22+
}
23+
}

0 commit comments

Comments
 (0)