-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathChessBoard.java
More file actions
295 lines (262 loc) · 10.4 KB
/
ChessBoard.java
File metadata and controls
295 lines (262 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.UIManager;
import javax.swing.SwingUtilities;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
/**
* <p>Creates a chessboard in a window on the desktop. The ChessBoard has a ChessBoardDisplay object that determines
* how the individual squares of the chessboard should be drawn.</p>
*
* <p>The chessboard uses a ChessGame object to determine how the game should be played. The way the chessboard works
* is as follows. The player selects a piece by clicking on the board, and
* and the chessboard calls the <tt>legalPieceToPlay</tt> method of the ChessGame object.
* If the player is allowed to select the piece, the board highlights it, and the player can select another square on
* the board. The chessboard then calls the <tt>makeMove</tt> method of the ChessGame object. The ChessGame is
* responsible for determining if the move is valid, and if it is to update the game and the chessboard
* with the results of making that move.</p>
*
*/
public class ChessBoard {
private JFrame board; // the game board
private JButton[][] squares; // the squares of the board
private ChessPiece[][] pieces; // stores the pieces
private ChessGame gameRules; // global rules for this particular game
private ChessBoardDisplay boardDisplay; // rules for how to draw the chess board
/**
* Builds a board of the desired size, the display parameters, and the rules for the chess game.
* @param numRows the number of rows for the chessboard
* @param numColumns the number of columns for the chessboard
* @param boardDisplay an object that determines how the squares on the chessboard should be drawn
* @param gameRules an object that determines when player selection is valid and to update the game with the result of a move
*/
public ChessBoard(final int numRows, final int numColumns, final ChessBoardDisplay boardDisplay, ChessGame gameRules) {
// for Mac computers: this allows us to change a button background
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
}
catch (Exception e) {
}
// initialize the board
this.gameRules = gameRules;
this.boardDisplay = boardDisplay;
pieces = new ChessPiece[numRows][numColumns];
squares = new JButton[numRows][numColumns];
// create the board visuals on the event dispatch thread
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
board = new JFrame();
// create a grid for the squares and the listener for the user clicks
JPanel panel = new JPanel(new GridLayout(numRows, numColumns));
ActionListener responder = new ChessAction();
// create the squares
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < numColumns; j++) {
squares[i][j] = new JButton();
squares[i][j].addActionListener(responder);
boardDisplay.displayEmptySquare(squares[i][j], i, j);
panel.add(squares[i][j]);
pieces[i][j] = null;
}
}
board.add(panel);
board.setSize(boardDisplay.getSquareSize() * numColumns, boardDisplay.getSquareSize() * numRows);
board.setVisible(true);
}
});
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* Returns the rules of the game.
* @return the rules of the game
*/
public ChessGame getGameRules() {
return gameRules;
}
/**
* Changes the rules of the game
* @param newRules the new rules for the game
*/
public void setGameRules(ChessGame newRules) {
this.gameRules = newRules;
}
/**
* Returns the number of rows in the board.
* @return the number of rows
*/
public final int numRows() {
return squares.length;
}
/**
* Returns the number of columns in the board.
* @return the number of columns
*/
public final int numColumns() {
return squares[0].length;
}
/**
* Adds a piece to the board at the desired location. Any piece currently
* at that location is lost.
* @param piece the piece to add
* @param row the row for the piece
* @param col the column for the piece
*/
public void addPiece(final ChessPiece piece, final int row, final int col) {
// set the piece on the board, tell the piece where it is, and then use the display rules to display the square
// run the display code on the event dispatch thread
pieces[row][col] = piece;
piece.setLocation(row, col);
Runnable addPiece = new Runnable() {
public void run() {
boardDisplay.displayFilledSquare(squares[row][col], row, col, piece);
}
};
// run the code to change the display on the event dispatch to avoid drawing errors
if (SwingUtilities.isEventDispatchThread())
addPiece.run();
else {
try {
SwingUtilities.invokeAndWait(addPiece);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Removes a piece from the board
* @param row the row of the piece
* @param col the column of the piece
* @return the piece removed of null if there was no piece at that square
*/
public ChessPiece removePiece(final int row, final int col) {
// remove the piece from the board, use the display rules to show an empty square,
// and run the display code on the event dispatch thread
ChessPiece save = pieces[row][col];
pieces[row][col] = null;
Runnable removePiece = new Runnable() {
public void run() {
boardDisplay.displayEmptySquare(squares[row][col], row, col);
}
};
if (SwingUtilities.isEventDispatchThread())
removePiece.run();
else {
try {
SwingUtilities.invokeAndWait(removePiece);
}
catch (Exception e) {
e.printStackTrace();
}
}
return save;
}
/**
* Returns true if there is a piece at a specific location of the board.
* @param row the row to examine
* @param col the column to examine
* @return true if there is a piece a this row and column and false
* if the square is empty
*/
public boolean hasPiece(int row, int col) {
return (pieces[row][col] != null);
}
/**
* Returns the chess piece at a specific location on the board.
* @param row the row for the piece
* @param col the column for the piece
* @return the piece at the row and column or null if there is no piece there.
*/
public ChessPiece getPiece(int row, int col) {
return pieces[row][col];
}
/** The code the responds when the user clicks on the game board */
private class ChessAction implements ActionListener {
private boolean firstPick = true; // if true, we a selecting a piece
private int pieceRow; // remember row of selected piece
private int pieceCol; // remember column of selected piece
/**
* What we do when the user chooses the piece to move.
* @param row the row of the chosen piece
* @param col the column of the chosen piece
*/
private void processFirstSelection(int row, int col) {
if ((pieces[row][col] != null) &&
(getGameRules() == null || getGameRules().legalPieceToPlay(pieces[row][col], row, col))) {
/*
* if this is the first pick and a square with a piece was picked,
* remember the piece's location and highlight the square.
*/
pieceRow = row;
pieceCol = col;
boardDisplay.highlightSquare(true, squares[row][col], row, col, pieces[row][col]);
firstPick = false;
}
}
/**
* What we do when the user chooses the square to move the piece to.
* @param row the row the piece will move to
* @param col the column the piece will move to
*/
private void processSecondSelection(int row, int col) {
if (row == pieceRow && col == pieceCol)
return;
boolean moveMade = getGameRules().makeMove(pieces[pieceRow][pieceCol], row, col);
// if the move was made or if it was not made and the user select a new piece, then reset to choose a new move
if (moveMade || getGameRules().canChangeSelection(pieces[pieceRow][pieceCol], pieceRow, pieceCol)) {
boardDisplay.highlightSquare(false, squares[pieceRow][pieceCol], pieceRow, pieceCol, pieces[pieceRow][pieceCol]);
firstPick = true;
}
}
/**
* Handle a button click. The method alternates between selecting a piece
* and selecting any square. After both are selected, the piece's
* legalMove is called, and if the move is legal, the piece is moved.
* @param e the event that triggered the method
*/
public void actionPerformed(ActionEvent e) {
JButton b = (JButton)e.getSource();
int col = -1;
int row = -1;
// first find which button (board square) was clicked.
for (int i = 0; i < squares.length; i++) {
for (int j = 0; j < squares[i].length; j++) {
if (squares[i][j] == b) {
row = i;
col = j;
}
}
}
if (firstPick) {
processFirstSelection(row, col);
}
else {
processSecondSelection(row, col);
}
}
}
/**
* Returns true if a particular square is threatened by an opposing piece.
* @param row the row of the square
* @param column the column of the square
* @param piece a piece of the game
* @return true if the square can be attacked by a piece of an opposing side as the parameter piece
*/
public boolean squareThreatened(int row, int column, ChessPiece piece) {
for (int i = 0; i < squares.length; i++) {
for (int j = 0; j < squares[i].length; j++) {
if (hasPiece(i,j) && getPiece(i, j).getSide() != piece.getSide() &&
getPiece(i, j).isLegalCaptureMove(row, column))
return true;
}
}
return false;
}
}