├── .gitignore
├── LICENSE
├── README.md
├── build.xml
├── jar-in-jar-loader.zip
├── resources
├── bb.png
├── bbishop.png
├── bk.png
├── bking.png
├── bknight.png
├── bn.png
├── bp.png
├── bpawn.png
├── bq.png
├── bqueen.png
├── br.png
├── brook.png
├── wb.png
├── wbishop.png
├── wk.png
├── wking.png
├── wknight.png
├── wn.png
├── wp.png
├── wpawn.png
├── wq.png
├── wqueen.png
├── wr.png
└── wrook.png
└── src
├── Bishop.java
├── Board.java
├── CheckmateDetector.java
├── Clock.java
├── Game.java
├── GameWindow.java
├── King.java
├── Knight.java
├── Pawn.java
├── Piece.java
├── Queen.java
├── Rook.java
├── Square.java
└── StartMenu.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled files
2 | *.class
3 | bin/*
4 |
5 | # Mobile Tools for Java (J2ME)
6 | .mtj.tmp/
7 |
8 | # Package Files #
9 | *.jar
10 | *.war
11 | *.ear
12 |
13 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
14 | hs_err_pid*
15 |
16 |
17 | # Eclipse config files
18 | .classpath
19 | .project
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Jussi Lundstedt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Chess for Java
2 |
3 | In the Spring of 2014, I created a two-player Chess game, with checkmate detection and a chess clock as a part of a Programming course at Penn. Our objective was to develop and test a bug-free standalone game in Java, complete with a GUI and game logic components.
4 |
5 | I developed a bug-free, fast and well-designed product with a clean user interface and received the highest possible score in the assignment. The source code is in this repository.
6 |
7 | ## Technology
8 |
9 | This game is built using core Java, Java Swing GUI libraries and the jUnit test suite. It uses custom drawing for game components and self-programmed logic for checkmate detection. The code is modular, standalone and object oriented, which was a grading criteria for the assignment.
10 |
11 | ## Running
12 |
13 | Compile the project into an executable .jar file by running the following ANT build script on the command line. Make sure jar-in-jar-loader.zip in this repository is in the folder.
14 |
15 | ```
16 | ant -f build.xml
17 | ```
18 |
19 | Then, run the executable .jar file, named _chess-java.jar_ to play.
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/jar-in-jar-loader.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/jar-in-jar-loader.zip
--------------------------------------------------------------------------------
/resources/bb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/bb.png
--------------------------------------------------------------------------------
/resources/bbishop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/bbishop.png
--------------------------------------------------------------------------------
/resources/bk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/bk.png
--------------------------------------------------------------------------------
/resources/bking.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/bking.png
--------------------------------------------------------------------------------
/resources/bknight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/bknight.png
--------------------------------------------------------------------------------
/resources/bn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/bn.png
--------------------------------------------------------------------------------
/resources/bp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/bp.png
--------------------------------------------------------------------------------
/resources/bpawn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/bpawn.png
--------------------------------------------------------------------------------
/resources/bq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/bq.png
--------------------------------------------------------------------------------
/resources/bqueen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/bqueen.png
--------------------------------------------------------------------------------
/resources/br.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/br.png
--------------------------------------------------------------------------------
/resources/brook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/brook.png
--------------------------------------------------------------------------------
/resources/wb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/wb.png
--------------------------------------------------------------------------------
/resources/wbishop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/wbishop.png
--------------------------------------------------------------------------------
/resources/wk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/wk.png
--------------------------------------------------------------------------------
/resources/wking.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/wking.png
--------------------------------------------------------------------------------
/resources/wknight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/wknight.png
--------------------------------------------------------------------------------
/resources/wn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/wn.png
--------------------------------------------------------------------------------
/resources/wp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/wp.png
--------------------------------------------------------------------------------
/resources/wpawn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/wpawn.png
--------------------------------------------------------------------------------
/resources/wq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/wq.png
--------------------------------------------------------------------------------
/resources/wqueen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/wqueen.png
--------------------------------------------------------------------------------
/resources/wr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/wr.png
--------------------------------------------------------------------------------
/resources/wrook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlundstedt/chess-java/bea3ac7905b36f4415821789bae97f42aa3da8ac/resources/wrook.png
--------------------------------------------------------------------------------
/src/Bishop.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | import java.util.List;
4 |
5 | public class Bishop extends Piece {
6 |
7 | public Bishop(int color, Square initSq, String img_file) {
8 | super(color, initSq, img_file);
9 | }
10 |
11 | @Override
12 | public List getLegalMoves(Board b) {
13 | Square[][] board = b.getSquareArray();
14 | int x = this.getPosition().getXNum();
15 | int y = this.getPosition().getYNum();
16 |
17 | return getDiagonalOccupations(board, x, y);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Board.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | import java.awt.Dimension;
4 | import java.awt.Graphics;
5 | import java.awt.GridLayout;
6 | import java.awt.Image;
7 | import java.awt.Point;
8 | import java.awt.event.MouseEvent;
9 | import java.awt.event.MouseListener;
10 | import java.awt.event.MouseMotionListener;
11 | import java.util.LinkedList;
12 | import java.util.List;
13 |
14 | import javax.swing.*;
15 |
16 | @SuppressWarnings("serial")
17 | public class Board extends JPanel implements MouseListener, MouseMotionListener {
18 | // Resource location constants for piece images
19 | private static final String RESOURCES_WBISHOP_PNG = "wbishop.png";
20 | private static final String RESOURCES_BBISHOP_PNG = "bbishop.png";
21 | private static final String RESOURCES_WKNIGHT_PNG = "wknight.png";
22 | private static final String RESOURCES_BKNIGHT_PNG = "bknight.png";
23 | private static final String RESOURCES_WROOK_PNG = "wrook.png";
24 | private static final String RESOURCES_BROOK_PNG = "brook.png";
25 | private static final String RESOURCES_WKING_PNG = "wking.png";
26 | private static final String RESOURCES_BKING_PNG = "bking.png";
27 | private static final String RESOURCES_BQUEEN_PNG = "bqueen.png";
28 | private static final String RESOURCES_WQUEEN_PNG = "wqueen.png";
29 | private static final String RESOURCES_WPAWN_PNG = "wpawn.png";
30 | private static final String RESOURCES_BPAWN_PNG = "bpawn.png";
31 |
32 | // Logical and graphical representations of board
33 | private final Square[][] board;
34 | private final GameWindow g;
35 |
36 | // List of pieces and whether they are movable
37 | public final LinkedList Bpieces;
38 | public final LinkedList Wpieces;
39 | public List movable;
40 |
41 | private boolean whiteTurn;
42 |
43 | private Piece currPiece;
44 | private int currX;
45 | private int currY;
46 |
47 | private CheckmateDetector cmd;
48 |
49 | public Board(GameWindow g) {
50 | this.g = g;
51 | board = new Square[8][8];
52 | Bpieces = new LinkedList();
53 | Wpieces = new LinkedList();
54 | setLayout(new GridLayout(8, 8, 0, 0));
55 |
56 | this.addMouseListener(this);
57 | this.addMouseMotionListener(this);
58 |
59 | for (int x = 0; x < 8; x++) {
60 | for (int y = 0; y < 8; y++) {
61 | int xMod = x % 2;
62 | int yMod = y % 2;
63 |
64 | if ((xMod == 0 && yMod == 0) || (xMod == 1 && yMod == 1)) {
65 | board[x][y] = new Square(this, 1, y, x);
66 | this.add(board[x][y]);
67 | } else {
68 | board[x][y] = new Square(this, 0, y, x);
69 | this.add(board[x][y]);
70 | }
71 | }
72 | }
73 |
74 | initializePieces();
75 |
76 | this.setPreferredSize(new Dimension(400, 400));
77 | this.setMaximumSize(new Dimension(400, 400));
78 | this.setMinimumSize(this.getPreferredSize());
79 | this.setSize(new Dimension(400, 400));
80 |
81 | whiteTurn = true;
82 |
83 | }
84 |
85 | private void initializePieces() {
86 |
87 | for (int x = 0; x < 8; x++) {
88 | board[1][x].put(new Pawn(0, board[1][x], RESOURCES_BPAWN_PNG));
89 | board[6][x].put(new Pawn(1, board[6][x], RESOURCES_WPAWN_PNG));
90 | }
91 |
92 | board[7][3].put(new Queen(1, board[7][3], RESOURCES_WQUEEN_PNG));
93 | board[0][3].put(new Queen(0, board[0][3], RESOURCES_BQUEEN_PNG));
94 |
95 | King bk = new King(0, board[0][4], RESOURCES_BKING_PNG);
96 | King wk = new King(1, board[7][4], RESOURCES_WKING_PNG);
97 | board[0][4].put(bk);
98 | board[7][4].put(wk);
99 |
100 | board[0][0].put(new Rook(0, board[0][0], RESOURCES_BROOK_PNG));
101 | board[0][7].put(new Rook(0, board[0][7], RESOURCES_BROOK_PNG));
102 | board[7][0].put(new Rook(1, board[7][0], RESOURCES_WROOK_PNG));
103 | board[7][7].put(new Rook(1, board[7][7], RESOURCES_WROOK_PNG));
104 |
105 | board[0][1].put(new Knight(0, board[0][1], RESOURCES_BKNIGHT_PNG));
106 | board[0][6].put(new Knight(0, board[0][6], RESOURCES_BKNIGHT_PNG));
107 | board[7][1].put(new Knight(1, board[7][1], RESOURCES_WKNIGHT_PNG));
108 | board[7][6].put(new Knight(1, board[7][6], RESOURCES_WKNIGHT_PNG));
109 |
110 | board[0][2].put(new Bishop(0, board[0][2], RESOURCES_BBISHOP_PNG));
111 | board[0][5].put(new Bishop(0, board[0][5], RESOURCES_BBISHOP_PNG));
112 | board[7][2].put(new Bishop(1, board[7][2], RESOURCES_WBISHOP_PNG));
113 | board[7][5].put(new Bishop(1, board[7][5], RESOURCES_WBISHOP_PNG));
114 |
115 |
116 | for(int y = 0; y < 2; y++) {
117 | for (int x = 0; x < 8; x++) {
118 | Bpieces.add(board[y][x].getOccupyingPiece());
119 | Wpieces.add(board[7-y][x].getOccupyingPiece());
120 | }
121 | }
122 |
123 | cmd = new CheckmateDetector(this, Wpieces, Bpieces, wk, bk);
124 | }
125 |
126 | public Square[][] getSquareArray() {
127 | return this.board;
128 | }
129 |
130 | public boolean getTurn() {
131 | return whiteTurn;
132 | }
133 |
134 | public void setCurrPiece(Piece p) {
135 | this.currPiece = p;
136 | }
137 |
138 | public Piece getCurrPiece() {
139 | return this.currPiece;
140 | }
141 |
142 | @Override
143 | public void paintComponent(Graphics g) {
144 | // super.paintComponent(g);
145 |
146 | for (int x = 0; x < 8; x++) {
147 | for (int y = 0; y < 8; y++) {
148 | Square sq = board[y][x];
149 | sq.paintComponent(g);
150 | }
151 | }
152 |
153 | if (currPiece != null) {
154 | if ((currPiece.getColor() == 1 && whiteTurn)
155 | || (currPiece.getColor() == 0 && !whiteTurn)) {
156 | final Image i = currPiece.getImage();
157 | g.drawImage(i, currX, currY, null);
158 | }
159 | }
160 | }
161 |
162 | @Override
163 | public void mousePressed(MouseEvent e) {
164 | currX = e.getX();
165 | currY = e.getY();
166 |
167 | Square sq = (Square) this.getComponentAt(new Point(e.getX(), e.getY()));
168 |
169 | if (sq.isOccupied()) {
170 | currPiece = sq.getOccupyingPiece();
171 | if (currPiece.getColor() == 0 && whiteTurn)
172 | return;
173 | if (currPiece.getColor() == 1 && !whiteTurn)
174 | return;
175 | sq.setDisplay(false);
176 | }
177 | repaint();
178 | }
179 |
180 | @Override
181 | public void mouseReleased(MouseEvent e) {
182 | Square sq = (Square) this.getComponentAt(new Point(e.getX(), e.getY()));
183 |
184 | if (currPiece != null) {
185 | if (currPiece.getColor() == 0 && whiteTurn)
186 | return;
187 | if (currPiece.getColor() == 1 && !whiteTurn)
188 | return;
189 |
190 | List legalMoves = currPiece.getLegalMoves(this);
191 | movable = cmd.getAllowableSquares(whiteTurn);
192 |
193 | if (legalMoves.contains(sq) && movable.contains(sq)
194 | && cmd.testMove(currPiece, sq)) {
195 | sq.setDisplay(true);
196 | currPiece.move(sq);
197 | cmd.update();
198 |
199 | if (cmd.blackCheckMated()) {
200 | currPiece = null;
201 | repaint();
202 | this.removeMouseListener(this);
203 | this.removeMouseMotionListener(this);
204 | g.checkmateOccurred(0);
205 | } else if (cmd.whiteCheckMated()) {
206 | currPiece = null;
207 | repaint();
208 | this.removeMouseListener(this);
209 | this.removeMouseMotionListener(this);
210 | g.checkmateOccurred(1);
211 | } else {
212 | currPiece = null;
213 | whiteTurn = !whiteTurn;
214 | movable = cmd.getAllowableSquares(whiteTurn);
215 | }
216 |
217 | } else {
218 | currPiece.getPosition().setDisplay(true);
219 | currPiece = null;
220 | }
221 | }
222 |
223 | repaint();
224 | }
225 |
226 | @Override
227 | public void mouseDragged(MouseEvent e) {
228 | currX = e.getX() - 24;
229 | currY = e.getY() - 24;
230 |
231 | repaint();
232 | }
233 |
234 | // Irrelevant methods, do nothing for these mouse behaviors
235 | @Override
236 | public void mouseMoved(MouseEvent e) {
237 | }
238 |
239 | @Override
240 | public void mouseClicked(MouseEvent e) {
241 | }
242 |
243 | @Override
244 | public void mouseEntered(MouseEvent e) {
245 | }
246 |
247 | @Override
248 | public void mouseExited(MouseEvent e) {
249 | }
250 |
251 | }
--------------------------------------------------------------------------------
/src/CheckmateDetector.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | import java.util.HashMap;
4 | import java.util.Iterator;
5 | import java.util.LinkedList;
6 | import java.util.List;
7 | import java.util.Map;
8 | import java.util.concurrent.ConcurrentLinkedDeque;
9 |
10 |
11 | /**
12 | * Component of the Chess game that detects check mates in the game.
13 | *
14 | * @author Jussi Lundstedt
15 | *
16 | */
17 | public class CheckmateDetector {
18 | private Board b;
19 | private LinkedList wPieces;
20 | private LinkedList bPieces;
21 | private LinkedList movableSquares;
22 | private final LinkedList squares;
23 | private King bk;
24 | private King wk;
25 | private HashMap> wMoves;
26 | private HashMap> bMoves;
27 |
28 | /**
29 | * Constructs a new instance of CheckmateDetector on a given board. By
30 | * convention should be called when the board is in its initial state.
31 | *
32 | * @param b The board which the detector monitors
33 | * @param wPieces White pieces on the board.
34 | * @param bPieces Black pieces on the board.
35 | * @param wk Piece object representing the white king
36 | * @param bk Piece object representing the black king
37 | */
38 | public CheckmateDetector(Board b, LinkedList wPieces,
39 | LinkedList bPieces, King wk, King bk) {
40 | this.b = b;
41 | this.wPieces = wPieces;
42 | this.bPieces = bPieces;
43 | this.bk = bk;
44 | this.wk = wk;
45 |
46 | // Initialize other fields
47 | squares = new LinkedList();
48 | movableSquares = new LinkedList();
49 | wMoves = new HashMap>();
50 | bMoves = new HashMap>();
51 |
52 | Square[][] brd = b.getSquareArray();
53 |
54 | // add all squares to squares list and as hashmap keys
55 | for (int x = 0; x < 8; x++) {
56 | for (int y = 0; y < 8; y++) {
57 | squares.add(brd[y][x]);
58 | wMoves.put(brd[y][x], new LinkedList());
59 | bMoves.put(brd[y][x], new LinkedList());
60 | }
61 | }
62 |
63 | // update situation
64 | update();
65 | }
66 |
67 | /**
68 | * Updates the object with the current situation of the game.
69 | */
70 | public void update() {
71 | // Iterators through pieces
72 | Iterator wIter = wPieces.iterator();
73 | Iterator bIter = bPieces.iterator();
74 |
75 | // empty moves and movable squares at each update
76 | for (List pieces : wMoves.values()) {
77 | pieces.removeAll(pieces);
78 | }
79 |
80 | for (List pieces : bMoves.values()) {
81 | pieces.removeAll(pieces);
82 | }
83 |
84 | movableSquares.removeAll(movableSquares);
85 |
86 | // Add each move white and black can make to map
87 | while (wIter.hasNext()) {
88 | Piece p = wIter.next();
89 |
90 | if (!p.getClass().equals(King.class)) {
91 | if (p.getPosition() == null) {
92 | wIter.remove();
93 | continue;
94 | }
95 |
96 | List mvs = p.getLegalMoves(b);
97 | Iterator iter = mvs.iterator();
98 | while (iter.hasNext()) {
99 | List pieces = wMoves.get(iter.next());
100 | pieces.add(p);
101 | }
102 | }
103 | }
104 |
105 | while (bIter.hasNext()) {
106 | Piece p = bIter.next();
107 |
108 | if (!p.getClass().equals(King.class)) {
109 | if (p.getPosition() == null) {
110 | wIter.remove();
111 | continue;
112 | }
113 |
114 | List mvs = p.getLegalMoves(b);
115 | Iterator iter = mvs.iterator();
116 | while (iter.hasNext()) {
117 | List pieces = bMoves.get(iter.next());
118 | pieces.add(p);
119 | }
120 | }
121 | }
122 | }
123 |
124 | /**
125 | * Checks if the black king is threatened
126 | * @return boolean representing whether the black king is in check.
127 | */
128 | public boolean blackInCheck() {
129 | update();
130 | Square sq = bk.getPosition();
131 | if (wMoves.get(sq).isEmpty()) {
132 | movableSquares.addAll(squares);
133 | return false;
134 | } else return true;
135 | }
136 |
137 | /**
138 | * Checks if the white king is threatened
139 | * @return boolean representing whether the white king is in check.
140 | */
141 | public boolean whiteInCheck() {
142 | update();
143 | Square sq = wk.getPosition();
144 | if (bMoves.get(sq).isEmpty()) {
145 | movableSquares.addAll(squares);
146 | return false;
147 | } else return true;
148 | }
149 |
150 | /**
151 | * Checks whether black is in checkmate.
152 | * @return boolean representing if black player is checkmated.
153 | */
154 | public boolean blackCheckMated() {
155 | boolean checkmate = true;
156 | // Check if black is in check
157 | if (!this.blackInCheck()) return false;
158 |
159 | // If yes, check if king can evade
160 | if (canEvade(wMoves, bk)) checkmate = false;
161 |
162 | // If no, check if threat can be captured
163 | List threats = wMoves.get(bk.getPosition());
164 | if (canCapture(bMoves, threats, bk)) checkmate = false;
165 |
166 | // If no, check if threat can be blocked
167 | if (canBlock(threats, bMoves, bk)) checkmate = false;
168 |
169 | // If no possible ways of removing check, checkmate occurred
170 | return checkmate;
171 | }
172 |
173 | /**
174 | * Checks whether white is in checkmate.
175 | * @return boolean representing if white player is checkmated.
176 | */
177 | public boolean whiteCheckMated() {
178 | boolean checkmate = true;
179 | // Check if white is in check
180 | if (!this.whiteInCheck()) return false;
181 |
182 | // If yes, check if king can evade
183 | if (canEvade(bMoves, wk)) checkmate = false;
184 |
185 | // If no, check if threat can be captured
186 | List threats = bMoves.get(wk.getPosition());
187 | if (canCapture(wMoves, threats, wk)) checkmate = false;
188 |
189 | // If no, check if threat can be blocked
190 | if (canBlock(threats, wMoves, wk)) checkmate = false;
191 |
192 | // If no possible ways of removing check, checkmate occurred
193 | return checkmate;
194 | }
195 |
196 | /*
197 | * Helper method to determine if the king can evade the check.
198 | * Gives a false positive if the king can capture the checking piece.
199 | */
200 | private boolean canEvade(Map> tMoves, King tKing) {
201 | boolean evade = false;
202 | List kingsMoves = tKing.getLegalMoves(b);
203 | Iterator iterator = kingsMoves.iterator();
204 |
205 | // If king is not threatened at some square, it can evade
206 | while (iterator.hasNext()) {
207 | Square sq = iterator.next();
208 | if (!testMove(tKing, sq)) continue;
209 | if (tMoves.get(sq).isEmpty()) {
210 | movableSquares.add(sq);
211 | evade = true;
212 | }
213 | }
214 |
215 | return evade;
216 | }
217 |
218 | /*
219 | * Helper method to determine if the threatening piece can be captured.
220 | */
221 | private boolean canCapture(Map> poss,
222 | List threats, King k) {
223 |
224 | boolean capture = false;
225 | if (threats.size() == 1) {
226 | Square sq = threats.get(0).getPosition();
227 |
228 | if (k.getLegalMoves(b).contains(sq)) {
229 | movableSquares.add(sq);
230 | if (testMove(k, sq)) {
231 | capture = true;
232 | }
233 | }
234 |
235 | List caps = poss.get(sq);
236 | ConcurrentLinkedDeque capturers = new ConcurrentLinkedDeque();
237 | capturers.addAll(caps);
238 |
239 | if (!capturers.isEmpty()) {
240 | movableSquares.add(sq);
241 | for (Piece p : capturers) {
242 | if (testMove(p, sq)) {
243 | capture = true;
244 | }
245 | }
246 | }
247 | }
248 |
249 | return capture;
250 | }
251 |
252 | /*
253 | * Helper method to determine if check can be blocked by a piece.
254 | */
255 | private boolean canBlock(List threats,
256 | Map > blockMoves, King k) {
257 | boolean blockable = false;
258 |
259 | if (threats.size() == 1) {
260 | Square ts = threats.get(0).getPosition();
261 | Square ks = k.getPosition();
262 | Square[][] brdArray = b.getSquareArray();
263 |
264 | if (ks.getXNum() == ts.getXNum()) {
265 | int max = Math.max(ks.getYNum(), ts.getYNum());
266 | int min = Math.min(ks.getYNum(), ts.getYNum());
267 |
268 | for (int i = min + 1; i < max; i++) {
269 | List blks =
270 | blockMoves.get(brdArray[i][ks.getXNum()]);
271 | ConcurrentLinkedDeque blockers =
272 | new ConcurrentLinkedDeque();
273 | blockers.addAll(blks);
274 |
275 | if (!blockers.isEmpty()) {
276 | movableSquares.add(brdArray[i][ks.getXNum()]);
277 |
278 | for (Piece p : blockers) {
279 | if (testMove(p,brdArray[i][ks.getXNum()])) {
280 | blockable = true;
281 | }
282 | }
283 |
284 | }
285 | }
286 | }
287 |
288 | if (ks.getYNum() == ts.getYNum()) {
289 | int max = Math.max(ks.getXNum(), ts.getXNum());
290 | int min = Math.min(ks.getXNum(), ts.getXNum());
291 |
292 | for (int i = min + 1; i < max; i++) {
293 | List blks =
294 | blockMoves.get(brdArray[ks.getYNum()][i]);
295 | ConcurrentLinkedDeque blockers =
296 | new ConcurrentLinkedDeque();
297 | blockers.addAll(blks);
298 |
299 | if (!blockers.isEmpty()) {
300 |
301 | movableSquares.add(brdArray[ks.getYNum()][i]);
302 |
303 | for (Piece p : blockers) {
304 | if (testMove(p, brdArray[ks.getYNum()][i])) {
305 | blockable = true;
306 | }
307 | }
308 |
309 | }
310 | }
311 | }
312 |
313 | Class extends Piece> tC = threats.get(0).getClass();
314 |
315 | if (tC.equals(Queen.class) || tC.equals(Bishop.class)) {
316 | int kX = ks.getXNum();
317 | int kY = ks.getYNum();
318 | int tX = ts.getXNum();
319 | int tY = ts.getYNum();
320 |
321 | if (kX > tX && kY > tY) {
322 | for (int i = tX + 1; i < kX; i++) {
323 | tY++;
324 | List blks =
325 | blockMoves.get(brdArray[tY][i]);
326 | ConcurrentLinkedDeque blockers =
327 | new ConcurrentLinkedDeque();
328 | blockers.addAll(blks);
329 |
330 | if (!blockers.isEmpty()) {
331 | movableSquares.add(brdArray[tY][i]);
332 |
333 | for (Piece p : blockers) {
334 | if (testMove(p, brdArray[tY][i])) {
335 | blockable = true;
336 | }
337 | }
338 | }
339 | }
340 | }
341 |
342 | if (kX > tX && tY > kY) {
343 | for (int i = tX + 1; i < kX; i++) {
344 | tY--;
345 | List blks =
346 | blockMoves.get(brdArray[tY][i]);
347 | ConcurrentLinkedDeque blockers =
348 | new ConcurrentLinkedDeque();
349 | blockers.addAll(blks);
350 |
351 | if (!blockers.isEmpty()) {
352 | movableSquares.add(brdArray[tY][i]);
353 |
354 | for (Piece p : blockers) {
355 | if (testMove(p, brdArray[tY][i])) {
356 | blockable = true;
357 | }
358 | }
359 | }
360 | }
361 | }
362 |
363 | if (tX > kX && kY > tY) {
364 | for (int i = tX - 1; i > kX; i--) {
365 | tY++;
366 | List blks =
367 | blockMoves.get(brdArray[tY][i]);
368 | ConcurrentLinkedDeque blockers =
369 | new ConcurrentLinkedDeque();
370 | blockers.addAll(blks);
371 |
372 | if (!blockers.isEmpty()) {
373 | movableSquares.add(brdArray[tY][i]);
374 |
375 | for (Piece p : blockers) {
376 | if (testMove(p, brdArray[tY][i])) {
377 | blockable = true;
378 | }
379 | }
380 | }
381 | }
382 | }
383 |
384 | if (tX > kX && tY > kY) {
385 | for (int i = tX - 1; i > kX; i--) {
386 | tY--;
387 | List blks =
388 | blockMoves.get(brdArray[tY][i]);
389 | ConcurrentLinkedDeque blockers =
390 | new ConcurrentLinkedDeque();
391 | blockers.addAll(blks);
392 |
393 | if (!blockers.isEmpty()) {
394 | movableSquares.add(brdArray[tY][i]);
395 |
396 | for (Piece p : blockers) {
397 | if (testMove(p, brdArray[tY][i])) {
398 | blockable = true;
399 | }
400 | }
401 | }
402 | }
403 | }
404 | }
405 | }
406 |
407 | return blockable;
408 | }
409 |
410 | /**
411 | * Method to get a list of allowable squares that the player can move.
412 | * Defaults to all squares, but limits available squares if player is in
413 | * check.
414 | * @param b boolean representing whether it's white player's turn (if yes,
415 | * true)
416 | * @return List of squares that the player can move into.
417 | */
418 | public List getAllowableSquares(boolean b) {
419 | movableSquares.removeAll(movableSquares);
420 | if (whiteInCheck()) {
421 | whiteCheckMated();
422 | } else if (blackInCheck()) {
423 | blackCheckMated();
424 | }
425 | return movableSquares;
426 | }
427 |
428 | /**
429 | * Tests a move a player is about to make to prevent making an illegal move
430 | * that puts the player in check.
431 | * @param p Piece moved
432 | * @param sq Square to which p is about to move
433 | * @return false if move would cause a check
434 | */
435 | public boolean testMove(Piece p, Square sq) {
436 | Piece c = sq.getOccupyingPiece();
437 |
438 | boolean movetest = true;
439 | Square init = p.getPosition();
440 |
441 | p.move(sq);
442 | update();
443 |
444 | if (p.getColor() == 0 && blackInCheck()) movetest = false;
445 | else if (p.getColor() == 1 && whiteInCheck()) movetest = false;
446 |
447 | p.move(init);
448 | if (c != null) sq.put(c);
449 |
450 | update();
451 |
452 | movableSquares.addAll(squares);
453 | return movetest;
454 | }
455 |
456 | }
457 |
--------------------------------------------------------------------------------
/src/Clock.java:
--------------------------------------------------------------------------------
1 |
2 | public class Clock {
3 | private int hh;
4 | private int mm;
5 | private int ss;
6 |
7 | public Clock(int hh, int mm, int ss) {
8 | this.hh = hh;
9 | this.mm = mm;
10 | this.ss = ss;
11 | }
12 |
13 | public boolean outOfTime() {
14 | return (hh == 0 && mm == 0 && ss == 0);
15 | }
16 |
17 | public void decr() {
18 | if (this.mm == 0 && this.ss == 0) {
19 | this.ss = 59;
20 | this.mm = 59;
21 | this.hh--;
22 | } else if (this.ss == 0) {
23 | this.ss = 59;
24 | this.mm--;
25 | } else this.ss--;
26 | }
27 |
28 | public String getTime() {
29 | String fHrs = String.format("%02d", this.hh);
30 | String fMins = String.format("%02d", this.mm);
31 | String fSecs = String.format("%02d", this.ss);
32 | String fTime = fHrs + ":" + fMins + ":" + fSecs;
33 | return fTime;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Game.java:
--------------------------------------------------------------------------------
1 |
2 | import javax.swing.*;
3 |
4 | public class Game implements Runnable {
5 | public void run() {
6 | SwingUtilities.invokeLater(new StartMenu());
7 | }
8 |
9 | public static void main(String[] args) {
10 | SwingUtilities.invokeLater(new Game());
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/GameWindow.java:
--------------------------------------------------------------------------------
1 |
2 | import java.awt.BorderLayout;
3 | import java.awt.GridLayout;
4 | import java.awt.Image;
5 | import java.awt.event.ActionEvent;
6 | import java.awt.event.ActionListener;
7 | import java.io.File;
8 |
9 | import javax.imageio.ImageIO;
10 | import javax.swing.*;
11 |
12 |
13 | public class GameWindow {
14 | private JFrame gameWindow;
15 |
16 | public Clock blackClock;
17 | public Clock whiteClock;
18 |
19 | private Timer timer;
20 |
21 | private Board board;
22 |
23 |
24 |
25 | public GameWindow(String blackName, String whiteName, int hh,
26 | int mm, int ss) {
27 |
28 | blackClock = new Clock(hh, ss, mm);
29 | whiteClock = new Clock(hh, ss, mm);
30 |
31 | gameWindow = new JFrame("Chess");
32 |
33 |
34 | try {
35 | Image whiteImg = ImageIO.read(getClass().getResource("wp.png"));
36 | gameWindow.setIconImage(whiteImg);
37 | } catch (Exception e) {
38 | System.out.println("Game file wp.png not found");
39 | }
40 |
41 | gameWindow.setLocation(100, 100);
42 |
43 |
44 | gameWindow.setLayout(new BorderLayout(20,20));
45 |
46 | // Game Data window
47 | JPanel gameData = gameDataPanel(blackName, whiteName, hh, mm, ss);
48 | gameData.setSize(gameData.getPreferredSize());
49 | gameWindow.add(gameData, BorderLayout.NORTH);
50 |
51 | this.board = new Board(this);
52 |
53 | gameWindow.add(board, BorderLayout.CENTER);
54 |
55 | gameWindow.add(buttons(), BorderLayout.SOUTH);
56 |
57 | gameWindow.setMinimumSize(gameWindow.getPreferredSize());
58 | gameWindow.setSize(gameWindow.getPreferredSize());
59 | gameWindow.setResizable(false);
60 |
61 | gameWindow.pack();
62 | gameWindow.setVisible(true);
63 | gameWindow.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
64 | }
65 |
66 | // Helper function to create data panel
67 |
68 | private JPanel gameDataPanel(final String bn, final String wn,
69 | final int hh, final int mm, final int ss) {
70 |
71 | JPanel gameData = new JPanel();
72 | gameData.setLayout(new GridLayout(3,2,0,0));
73 |
74 |
75 | // PLAYER NAMES
76 |
77 | JLabel w = new JLabel(wn);
78 | JLabel b = new JLabel(bn);
79 |
80 | w.setHorizontalAlignment(JLabel.CENTER);
81 | w.setVerticalAlignment(JLabel.CENTER);
82 | b.setHorizontalAlignment(JLabel.CENTER);
83 | b.setVerticalAlignment(JLabel.CENTER);
84 |
85 | w.setSize(w.getMinimumSize());
86 | b.setSize(b.getMinimumSize());
87 |
88 | gameData.add(w);
89 | gameData.add(b);
90 |
91 | // CLOCKS
92 |
93 | final JLabel bTime = new JLabel(blackClock.getTime());
94 | final JLabel wTime = new JLabel(whiteClock.getTime());
95 |
96 | bTime.setHorizontalAlignment(JLabel.CENTER);
97 | bTime.setVerticalAlignment(JLabel.CENTER);
98 | wTime.setHorizontalAlignment(JLabel.CENTER);
99 | wTime.setVerticalAlignment(JLabel.CENTER);
100 |
101 | if (!(hh == 0 && mm == 0 && ss == 0)) {
102 | timer = new Timer(1000, null);
103 | timer.addActionListener(new ActionListener() {
104 | public void actionPerformed(ActionEvent e) {
105 | boolean turn = board.getTurn();
106 |
107 | if (turn) {
108 | whiteClock.decr();
109 | wTime.setText(whiteClock.getTime());
110 |
111 | if (whiteClock.outOfTime()) {
112 | timer.stop();
113 | int n = JOptionPane.showConfirmDialog(
114 | gameWindow,
115 | bn + " wins by time! Play a new game? \n" +
116 | "Choosing \"No\" quits the game.",
117 | bn + " wins!",
118 | JOptionPane.YES_NO_OPTION);
119 |
120 | if (n == JOptionPane.YES_OPTION) {
121 | new GameWindow(bn, wn, hh, mm, ss);
122 | gameWindow.dispose();
123 | } else gameWindow.dispose();
124 | }
125 | } else {
126 | blackClock.decr();
127 | bTime.setText(blackClock.getTime());
128 |
129 | if (blackClock.outOfTime()) {
130 | timer.stop();
131 | int n = JOptionPane.showConfirmDialog(
132 | gameWindow,
133 | wn + " wins by time! Play a new game? \n" +
134 | "Choosing \"No\" quits the game.",
135 | wn + " wins!",
136 | JOptionPane.YES_NO_OPTION);
137 |
138 | if (n == JOptionPane.YES_OPTION) {
139 | new GameWindow(bn, wn, hh, mm, ss);
140 | gameWindow.dispose();
141 | } else gameWindow.dispose();
142 | }
143 | }
144 | }
145 | });
146 | timer.start();
147 | } else {
148 | wTime.setText("Untimed game");
149 | bTime.setText("Untimed game");
150 | }
151 |
152 | gameData.add(wTime);
153 | gameData.add(bTime);
154 |
155 | gameData.setPreferredSize(gameData.getMinimumSize());
156 |
157 | return gameData;
158 | }
159 |
160 | private JPanel buttons() {
161 | JPanel buttons = new JPanel();
162 | buttons.setLayout(new GridLayout(1, 3, 10, 0));
163 |
164 | final JButton quit = new JButton("Quit");
165 |
166 | quit.addActionListener(new ActionListener() {
167 | public void actionPerformed(ActionEvent e) {
168 | int n = JOptionPane.showConfirmDialog(
169 | gameWindow,
170 | "Are you sure you want to quit?",
171 | "Confirm quit", JOptionPane.YES_NO_OPTION);
172 |
173 | if (n == JOptionPane.YES_OPTION) {
174 | if (timer != null) timer.stop();
175 | gameWindow.dispose();
176 | }
177 | }
178 | });
179 |
180 | final JButton nGame = new JButton("New game");
181 |
182 | nGame.addActionListener(new ActionListener() {
183 | public void actionPerformed(ActionEvent e) {
184 | int n = JOptionPane.showConfirmDialog(
185 | gameWindow,
186 | "Are you sure you want to begin a new game?",
187 | "Confirm new game", JOptionPane.YES_NO_OPTION);
188 |
189 | if (n == JOptionPane.YES_OPTION) {
190 | SwingUtilities.invokeLater(new StartMenu());
191 | gameWindow.dispose();
192 | }
193 | }
194 | });
195 |
196 | final JButton instr = new JButton("How to play");
197 |
198 | instr.addActionListener(new ActionListener() {
199 | public void actionPerformed(ActionEvent e) {
200 | JOptionPane.showMessageDialog(gameWindow,
201 | "Move the chess pieces on the board by clicking\n"
202 | + "and dragging. The game will watch out for illegal\n"
203 | + "moves. You can win either by your opponent running\n"
204 | + "out of time or by checkmating your opponent.\n"
205 | + "\nGood luck, hope you enjoy the game!",
206 | "How to play",
207 | JOptionPane.PLAIN_MESSAGE);
208 | }
209 | });
210 |
211 | buttons.add(instr);
212 | buttons.add(nGame);
213 | buttons.add(quit);
214 |
215 | buttons.setPreferredSize(buttons.getMinimumSize());
216 |
217 | return buttons;
218 | }
219 |
220 | public void checkmateOccurred (int c) {
221 | if (c == 0) {
222 | if (timer != null) timer.stop();
223 | int n = JOptionPane.showConfirmDialog(
224 | gameWindow,
225 | "White wins by checkmate! Set up a new game? \n" +
226 | "Choosing \"No\" lets you look at the final situation.",
227 | "White wins!",
228 | JOptionPane.YES_NO_OPTION);
229 |
230 | if (n == JOptionPane.YES_OPTION) {
231 | SwingUtilities.invokeLater(new StartMenu());
232 | gameWindow.dispose();
233 | }
234 | } else {
235 | if (timer != null) timer.stop();
236 | int n = JOptionPane.showConfirmDialog(
237 | gameWindow,
238 | "Black wins by checkmate! Set up a new game? \n" +
239 | "Choosing \"No\" lets you look at the final situation.",
240 | "Black wins!",
241 | JOptionPane.YES_NO_OPTION);
242 |
243 | if (n == JOptionPane.YES_OPTION) {
244 | SwingUtilities.invokeLater(new StartMenu());
245 | gameWindow.dispose();
246 | }
247 | }
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/src/King.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | import java.util.LinkedList;
4 | import java.util.List;
5 |
6 | public class King extends Piece {
7 |
8 | public King(int color, Square initSq, String img_file) {
9 | super(color, initSq, img_file);
10 | }
11 |
12 | @Override
13 | public List getLegalMoves(Board b) {
14 | LinkedList legalMoves = new LinkedList();
15 |
16 | Square[][] board = b.getSquareArray();
17 |
18 | int x = this.getPosition().getXNum();
19 | int y = this.getPosition().getYNum();
20 |
21 | for (int i = 1; i > -2; i--) {
22 | for (int k = 1; k > -2; k--) {
23 | if(!(i == 0 && k == 0)) {
24 | try {
25 | if(!board[y + k][x + i].isOccupied() ||
26 | board[y + k][x + i].getOccupyingPiece().getColor()
27 | != this.getColor()) {
28 | legalMoves.add(board[y + k][x + i]);
29 | }
30 | } catch (ArrayIndexOutOfBoundsException e) {
31 | continue;
32 | }
33 | }
34 | }
35 | }
36 |
37 | return legalMoves;
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/Knight.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | import java.util.LinkedList;
4 | import java.util.List;
5 |
6 | public class Knight extends Piece {
7 |
8 | public Knight(int color, Square initSq, String img_file) {
9 | super(color, initSq, img_file);
10 | }
11 |
12 | @Override
13 | public List getLegalMoves(Board b) {
14 | LinkedList legalMoves = new LinkedList();
15 | Square[][] board = b.getSquareArray();
16 |
17 | int x = this.getPosition().getXNum();
18 | int y = this.getPosition().getYNum();
19 |
20 | for (int i = 2; i > -3; i--) {
21 | for (int k = 2; k > -3; k--) {
22 | if(Math.abs(i) == 2 ^ Math.abs(k) == 2) {
23 | if (k != 0 && i != 0) {
24 | try {
25 | legalMoves.add(board[y + k][x + i]);
26 | } catch (ArrayIndexOutOfBoundsException e) {
27 | continue;
28 | }
29 | }
30 | }
31 | }
32 | }
33 |
34 | return legalMoves;
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/Pawn.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | import java.util.List;
4 | import java.util.LinkedList;
5 |
6 | public class Pawn extends Piece {
7 | private boolean wasMoved;
8 |
9 | public Pawn(int color, Square initSq, String img_file) {
10 | super(color, initSq, img_file);
11 | }
12 |
13 | @Override
14 | public boolean move(Square fin) {
15 | boolean b = super.move(fin);
16 | wasMoved = true;
17 | return b;
18 | }
19 |
20 | @Override
21 | public List getLegalMoves(Board b) {
22 | LinkedList legalMoves = new LinkedList();
23 |
24 | Square[][] board = b.getSquareArray();
25 |
26 | int x = this.getPosition().getXNum();
27 | int y = this.getPosition().getYNum();
28 | int c = this.getColor();
29 |
30 | if (c == 0) {
31 | if (!wasMoved) {
32 | if (!board[y+2][x].isOccupied()) {
33 | legalMoves.add(board[y+2][x]);
34 | }
35 | }
36 |
37 | if (y+1 < 8) {
38 | if (!board[y+1][x].isOccupied()) {
39 | legalMoves.add(board[y+1][x]);
40 | }
41 | }
42 |
43 | if (x+1 < 8 && y+1 < 8) {
44 | if (board[y+1][x+1].isOccupied()) {
45 | legalMoves.add(board[y+1][x+1]);
46 | }
47 | }
48 |
49 | if (x-1 >= 0 && y+1 < 8) {
50 | if (board[y+1][x-1].isOccupied()) {
51 | legalMoves.add(board[y+1][x-1]);
52 | }
53 | }
54 | }
55 |
56 | if (c == 1) {
57 | if (!wasMoved) {
58 | if (!board[y-2][x].isOccupied()) {
59 | legalMoves.add(board[y-2][x]);
60 | }
61 | }
62 |
63 | if (y-1 >= 0) {
64 | if (!board[y-1][x].isOccupied()) {
65 | legalMoves.add(board[y-1][x]);
66 | }
67 | }
68 |
69 | if (x+1 < 8 && y-1 >= 0) {
70 | if (board[y-1][x+1].isOccupied()) {
71 | legalMoves.add(board[y-1][x+1]);
72 | }
73 | }
74 |
75 | if (x-1 >= 0 && y-1 >= 0) {
76 | if (board[y-1][x-1].isOccupied()) {
77 | legalMoves.add(board[y-1][x-1]);
78 | }
79 | }
80 | }
81 |
82 | return legalMoves;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/Piece.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | import java.awt.Graphics;
4 | import java.awt.Image;
5 | import java.awt.image.BufferedImage;
6 | import java.io.IOException;
7 | import java.util.LinkedList;
8 | import java.util.List;
9 |
10 | import javax.imageio.ImageIO;
11 |
12 | public abstract class Piece {
13 | private final int color;
14 | private Square currentSquare;
15 | private BufferedImage img;
16 |
17 | public Piece(int color, Square initSq, String img_file) {
18 | this.color = color;
19 | this.currentSquare = initSq;
20 |
21 | try {
22 | if (this.img == null) {
23 | this.img = ImageIO.read(getClass().getResource(img_file));
24 | }
25 | } catch (IOException e) {
26 | System.out.println("File not found: " + e.getMessage());
27 | }
28 | }
29 |
30 | public boolean move(Square fin) {
31 | Piece occup = fin.getOccupyingPiece();
32 |
33 | if (occup != null) {
34 | if (occup.getColor() == this.color) return false;
35 | else fin.capture(this);
36 | }
37 |
38 | currentSquare.removePiece();
39 | this.currentSquare = fin;
40 | currentSquare.put(this);
41 | return true;
42 | }
43 |
44 | public Square getPosition() {
45 | return currentSquare;
46 | }
47 |
48 | public void setPosition(Square sq) {
49 | this.currentSquare = sq;
50 | }
51 |
52 | public int getColor() {
53 | return color;
54 | }
55 |
56 | public Image getImage() {
57 | return img;
58 | }
59 |
60 | public void draw(Graphics g) {
61 | int x = currentSquare.getX();
62 | int y = currentSquare.getY();
63 |
64 | g.drawImage(this.img, x, y, null);
65 | }
66 |
67 | public int[] getLinearOccupations(Square[][] board, int x, int y) {
68 | int lastYabove = 0;
69 | int lastXright = 7;
70 | int lastYbelow = 7;
71 | int lastXleft = 0;
72 |
73 | for (int i = 0; i < y; i++) {
74 | if (board[i][x].isOccupied()) {
75 | if (board[i][x].getOccupyingPiece().getColor() != this.color) {
76 | lastYabove = i;
77 | } else lastYabove = i + 1;
78 | }
79 | }
80 |
81 | for (int i = 7; i > y; i--) {
82 | if (board[i][x].isOccupied()) {
83 | if (board[i][x].getOccupyingPiece().getColor() != this.color) {
84 | lastYbelow = i;
85 | } else lastYbelow = i - 1;
86 | }
87 | }
88 |
89 | for (int i = 0; i < x; i++) {
90 | if (board[y][i].isOccupied()) {
91 | if (board[y][i].getOccupyingPiece().getColor() != this.color) {
92 | lastXleft = i;
93 | } else lastXleft = i + 1;
94 | }
95 | }
96 |
97 | for (int i = 7; i > x; i--) {
98 | if (board[y][i].isOccupied()) {
99 | if (board[y][i].getOccupyingPiece().getColor() != this.color) {
100 | lastXright = i;
101 | } else lastXright = i - 1;
102 | }
103 | }
104 |
105 | int[] occups = {lastYabove, lastYbelow, lastXleft, lastXright};
106 |
107 | return occups;
108 | }
109 |
110 | public List getDiagonalOccupations(Square[][] board, int x, int y) {
111 | LinkedList diagOccup = new LinkedList();
112 |
113 | int xNW = x - 1;
114 | int xSW = x - 1;
115 | int xNE = x + 1;
116 | int xSE = x + 1;
117 | int yNW = y - 1;
118 | int ySW = y + 1;
119 | int yNE = y - 1;
120 | int ySE = y + 1;
121 |
122 | while (xNW >= 0 && yNW >= 0) {
123 | if (board[yNW][xNW].isOccupied()) {
124 | if (board[yNW][xNW].getOccupyingPiece().getColor() == this.color) {
125 | break;
126 | } else {
127 | diagOccup.add(board[yNW][xNW]);
128 | break;
129 | }
130 | } else {
131 | diagOccup.add(board[yNW][xNW]);
132 | yNW--;
133 | xNW--;
134 | }
135 | }
136 |
137 | while (xSW >= 0 && ySW < 8) {
138 | if (board[ySW][xSW].isOccupied()) {
139 | if (board[ySW][xSW].getOccupyingPiece().getColor() == this.color) {
140 | break;
141 | } else {
142 | diagOccup.add(board[ySW][xSW]);
143 | break;
144 | }
145 | } else {
146 | diagOccup.add(board[ySW][xSW]);
147 | ySW++;
148 | xSW--;
149 | }
150 | }
151 |
152 | while (xSE < 8 && ySE < 8) {
153 | if (board[ySE][xSE].isOccupied()) {
154 | if (board[ySE][xSE].getOccupyingPiece().getColor() == this.color) {
155 | break;
156 | } else {
157 | diagOccup.add(board[ySE][xSE]);
158 | break;
159 | }
160 | } else {
161 | diagOccup.add(board[ySE][xSE]);
162 | ySE++;
163 | xSE++;
164 | }
165 | }
166 |
167 | while (xNE < 8 && yNE >= 0) {
168 | if (board[yNE][xNE].isOccupied()) {
169 | if (board[yNE][xNE].getOccupyingPiece().getColor() == this.color) {
170 | break;
171 | } else {
172 | diagOccup.add(board[yNE][xNE]);
173 | break;
174 | }
175 | } else {
176 | diagOccup.add(board[yNE][xNE]);
177 | yNE--;
178 | xNE++;
179 | }
180 | }
181 |
182 | return diagOccup;
183 | }
184 |
185 | // No implementation, to be implemented by each subclass
186 | public abstract List getLegalMoves(Board b);
187 | }
--------------------------------------------------------------------------------
/src/Queen.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | import java.util.LinkedList;
4 | import java.util.List;
5 |
6 | public class Queen extends Piece {
7 |
8 | public Queen(int color, Square initSq, String img_file) {
9 | super(color, initSq, img_file);
10 | }
11 |
12 | @Override
13 | public List getLegalMoves(Board b) {
14 | LinkedList legalMoves = new LinkedList();
15 | Square[][] board = b.getSquareArray();
16 |
17 | int x = this.getPosition().getXNum();
18 | int y = this.getPosition().getYNum();
19 |
20 | int[] occups = getLinearOccupations(board, x, y);
21 |
22 | for (int i = occups[0]; i <= occups[1]; i++) {
23 | if (i != y) legalMoves.add(board[i][x]);
24 | }
25 |
26 | for (int i = occups[2]; i <= occups[3]; i++) {
27 | if (i != x) legalMoves.add(board[y][i]);
28 | }
29 |
30 | List bMoves = getDiagonalOccupations(board, x, y);
31 |
32 | legalMoves.addAll(bMoves);
33 |
34 | return legalMoves;
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/Rook.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | import java.util.LinkedList;
4 | import java.util.List;
5 |
6 | public class Rook extends Piece {
7 |
8 | public Rook(int color, Square initSq, String img_file) {
9 | super(color, initSq, img_file);
10 | }
11 |
12 | @Override
13 | public List getLegalMoves(Board b) {
14 | LinkedList legalMoves = new LinkedList();
15 | Square[][] board = b.getSquareArray();
16 |
17 | int x = this.getPosition().getXNum();
18 | int y = this.getPosition().getYNum();
19 |
20 | int[] occups = getLinearOccupations(board, x, y);
21 |
22 | for (int i = occups[0]; i <= occups[1]; i++) {
23 | if (i != y) legalMoves.add(board[i][x]);
24 | }
25 |
26 | for (int i = occups[2]; i <= occups[3]; i++) {
27 | if (i != x) legalMoves.add(board[y][i]);
28 | }
29 |
30 | return legalMoves;
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/Square.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.*;
7 |
8 | @SuppressWarnings("serial")
9 | public class Square extends JComponent {
10 | private Board b;
11 |
12 | private final int color;
13 | private Piece occupyingPiece;
14 | private boolean dispPiece;
15 |
16 | private int xNum;
17 | private int yNum;
18 |
19 | public Square(Board b, int c, int xNum, int yNum) {
20 |
21 | this.b = b;
22 | this.color = c;
23 | this.dispPiece = true;
24 | this.xNum = xNum;
25 | this.yNum = yNum;
26 |
27 |
28 | this.setBorder(BorderFactory.createEmptyBorder());
29 | }
30 |
31 | public int getColor() {
32 | return this.color;
33 | }
34 |
35 | public Piece getOccupyingPiece() {
36 | return occupyingPiece;
37 | }
38 |
39 | public boolean isOccupied() {
40 | return (this.occupyingPiece != null);
41 | }
42 |
43 | public int getXNum() {
44 | return this.xNum;
45 | }
46 |
47 | public int getYNum() {
48 | return this.yNum;
49 | }
50 |
51 | public void setDisplay(boolean v) {
52 | this.dispPiece = v;
53 | }
54 |
55 | public void put(Piece p) {
56 | this.occupyingPiece = p;
57 | p.setPosition(this);
58 | }
59 |
60 | public Piece removePiece() {
61 | Piece p = this.occupyingPiece;
62 | this.occupyingPiece = null;
63 | return p;
64 | }
65 |
66 | public void capture(Piece p) {
67 | Piece k = getOccupyingPiece();
68 | if (k.getColor() == 0) b.Bpieces.remove(k);
69 | if (k.getColor() == 1) b.Wpieces.remove(k);
70 | this.occupyingPiece = p;
71 | }
72 |
73 | public void paintComponent(Graphics g) {
74 | super.paintComponent(g);
75 |
76 | if (this.color == 1) {
77 | g.setColor(new Color(221,192,127));
78 | } else {
79 | g.setColor(new Color(101,67,33));
80 | }
81 |
82 | g.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());
83 |
84 | if(occupyingPiece != null && dispPiece) {
85 | occupyingPiece.draw(g);
86 | }
87 | }
88 |
89 | @Override
90 | public int hashCode() {
91 | int prime = 31;
92 | int result = 1;
93 | result = prime * result + xNum;
94 | result = prime * result + yNum;
95 | return result;
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/StartMenu.java:
--------------------------------------------------------------------------------
1 |
2 | import java.awt.BorderLayout;
3 | import java.awt.Component;
4 | import java.awt.Image;
5 | import java.awt.event.ActionEvent;
6 | import java.awt.event.ActionListener;
7 |
8 | import javax.imageio.ImageIO;
9 | import javax.swing.Box;
10 | import javax.swing.ImageIcon;
11 | import javax.swing.JButton;
12 | import javax.swing.JComboBox;
13 | import javax.swing.JFrame;
14 | import javax.swing.JLabel;
15 | import javax.swing.JOptionPane;
16 | import javax.swing.JPanel;
17 | import javax.swing.JTextField;
18 |
19 | public class StartMenu implements Runnable {
20 | public void run() {
21 | final JFrame startWindow = new JFrame("Chess");
22 | // Set window properties
23 | startWindow.setLocation(300,100);
24 | startWindow.setResizable(false);
25 | startWindow.setSize(260, 240);
26 |
27 | Box components = Box.createVerticalBox();
28 | startWindow.add(components);
29 |
30 | // Game title
31 | final JPanel titlePanel = new JPanel();
32 | components.add(titlePanel);
33 | final JLabel titleLabel = new JLabel("Chess");
34 | titlePanel.add(titleLabel);
35 |
36 | // Black player selections
37 | final JPanel blackPanel = new JPanel();
38 | components.add(blackPanel, BorderLayout.EAST);
39 | final JLabel blackPiece = new JLabel();
40 | try {
41 | Image blackImg = ImageIO.read(getClass().getResource("bp.png"));
42 | blackPiece.setIcon(new ImageIcon(blackImg));
43 | blackPanel.add(blackPiece);
44 | } catch (Exception e) {
45 | System.out.println("Required game file bp.png missing");
46 | }
47 |
48 |
49 |
50 | final JTextField blackInput = new JTextField("Black", 10);
51 | blackPanel.add(blackInput);
52 |
53 | // White player selections
54 | final JPanel whitePanel = new JPanel();
55 | components.add(whitePanel);
56 | final JLabel whitePiece = new JLabel();
57 |
58 | try {
59 | Image whiteImg = ImageIO.read(getClass().getResource("wp.png"));
60 | whitePiece.setIcon(new ImageIcon(whiteImg));
61 | whitePanel.add(whitePiece);
62 | startWindow.setIconImage(whiteImg);
63 | } catch (Exception e) {
64 | System.out.println("Required game file wp.png missing");
65 | }
66 |
67 |
68 | final JTextField whiteInput = new JTextField("White", 10);
69 | whitePanel.add(whiteInput);
70 |
71 | // Timer settings
72 | final String[] minSecInts = new String[60];
73 | for (int i = 0; i < 60; i++) {
74 | if (i < 10) {
75 | minSecInts[i] = "0" + Integer.toString(i);
76 | } else {
77 | minSecInts[i] = Integer.toString(i);
78 | }
79 | }
80 |
81 | final JComboBox seconds = new JComboBox(minSecInts);
82 | final JComboBox minutes = new JComboBox(minSecInts);
83 | final JComboBox hours =
84 | new JComboBox(new String[] {"0","1","2","3"});
85 |
86 | Box timerSettings = Box.createHorizontalBox();
87 |
88 | hours.setMaximumSize(hours.getPreferredSize());
89 | minutes.setMaximumSize(minutes.getPreferredSize());
90 | seconds.setMaximumSize(minutes.getPreferredSize());
91 |
92 | timerSettings.add(hours);
93 | timerSettings.add(Box.createHorizontalStrut(10));
94 | timerSettings.add(seconds);
95 | timerSettings.add(Box.createHorizontalStrut(10));
96 | timerSettings.add(minutes);
97 |
98 | timerSettings.add(Box.createVerticalGlue());
99 |
100 | components.add(timerSettings);
101 |
102 | // Buttons
103 | Box buttons = Box.createHorizontalBox();
104 | final JButton quit = new JButton("Quit");
105 |
106 | quit.addActionListener(new ActionListener() {
107 | public void actionPerformed(ActionEvent e) {
108 | startWindow.dispose();
109 | }
110 | });
111 |
112 | final JButton instr = new JButton("Instructions");
113 |
114 | instr.addActionListener(new ActionListener() {
115 | public void actionPerformed(ActionEvent e) {
116 | JOptionPane.showMessageDialog(startWindow,
117 | "To begin a new game, input player names\n" +
118 | "next to the pieces. Set the clocks and\n" +
119 | "click \"Start\". Setting the timer to all\n" +
120 | "zeroes begins a new untimed game.",
121 | "How to play",
122 | JOptionPane.PLAIN_MESSAGE);
123 | }
124 | });
125 |
126 | final JButton start = new JButton("Start");
127 |
128 | start.addActionListener(new ActionListener() {
129 | public void actionPerformed(ActionEvent e) {
130 | String bn = blackInput.getText();
131 | String wn = whiteInput.getText();
132 | int hh = Integer.parseInt((String) hours.getSelectedItem());
133 | int mm = Integer.parseInt((String) minutes.getSelectedItem());
134 | int ss = Integer.parseInt((String) seconds.getSelectedItem());
135 |
136 | new GameWindow(bn, wn, hh, mm, ss);
137 | startWindow.dispose();
138 | }
139 | });
140 |
141 | buttons.add(start);
142 | buttons.add(Box.createHorizontalStrut(10));
143 | buttons.add(instr);
144 | buttons.add(Box.createHorizontalStrut(10));
145 | buttons.add(quit);
146 | components.add(buttons);
147 |
148 | Component space = Box.createGlue();
149 | components.add(space);
150 |
151 | startWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
152 | startWindow.setVisible(true);
153 | }
154 | }
155 |
--------------------------------------------------------------------------------