├── .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 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 | --------------------------------------------------------------------------------