chessai

college code for ai playing chess in java

git clone https://9o.is/git/chessai.git

commit 4d59ac8f2000cb83e3376121cba071651fe47089
parent c4c21262fe0e90c58de6c6ca3addb67754e9b297
Author: Jul <jul@9o.is>
Date:   Mon,  3 Dec 2012 08:33:02 -0500

Converted to maven project and can communicate with Ben Carle's server.

Diffstat:
Apom.xml | 32++++++++++++++++++++++++++++++++
Dsrc/chess/ChessBoard.java | 86-------------------------------------------------------------------------------
Dsrc/chess/ChessColor.java | 20--------------------
Dsrc/chess/ChessCoordinate.java | 155-------------------------------------------------------------------------------
Dsrc/chess/ChessDirection.java | 25-------------------------
Dsrc/chess/ChessGame.java | 62--------------------------------------------------------------
Dsrc/chess/ChessMove.java | 67-------------------------------------------------------------------
Dsrc/chess/ChessPiece.java | 61-------------------------------------------------------------
Dsrc/chess/ChessPlayer.java | 35-----------------------------------
Dsrc/chess/ChessRules.java | 356-------------------------------------------------------------------------------
Dsrc/chess/ChessType.java | 46----------------------------------------------
Asrc/main/java/chess/ChessBoard.java | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/chess/ChessClock.java -> src/main/java/chess/ChessClock.java | 0
Asrc/main/java/chess/ChessCoordinate.java | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main/java/chess/ChessGame.java | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main/java/chess/ChessMove.java | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main/java/chess/ChessPiece.java | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main/java/chess/ChessPlayer.java | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main/java/chess/ChessRules.java | 360+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/chess/ChessState.java -> src/main/java/chess/ChessState.java | 0
Asrc/main/java/chess/server/BenCarleChessServer.java | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main/java/chess/server/MoveResponse.java | 26++++++++++++++++++++++++++
Asrc/main/java/chess/server/StatusResponse.java | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main/java/chess/utils/ChessColor.java | 20++++++++++++++++++++
Asrc/main/java/chess/utils/ChessDirection.java | 25+++++++++++++++++++++++++
Asrc/main/java/chess/utils/ChessType.java | 46++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test/java/chess/AppTest.java | 38++++++++++++++++++++++++++++++++++++++
27 files changed, 1283 insertions(+), 913 deletions(-)

diff --git a/pom.xml b/pom.xml @@ -0,0 +1,32 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>chess</groupId> + <artifactId>chess</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>chess</name> + <url>http://maven.apache.org</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>3.8.1</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>net.sf.json-lib</groupId> + <artifactId>json-lib</artifactId> + <version>2.4</version> + <classifier>jdk15</classifier> + </dependency> + </dependencies> +</project> diff --git a/src/chess/ChessBoard.java b/src/chess/ChessBoard.java @@ -1,86 +0,0 @@ -package chess; - -/** - * - */ -public class ChessBoard { - private ChessState state; - - public ChessBoard() { - state = new ChessState(); - init(); - } - - //CHECK - public void init() { - state.set(new ChessCoordinate('a',8), ChessPiece.BLACK_ROOK); - state.set(new ChessCoordinate('b',8), ChessPiece.BLACK_KNIGHT); - state.set(new ChessCoordinate('c',8), ChessPiece.BLACK_BISHOP); - state.set(new ChessCoordinate('d',8), ChessPiece.BLACK_QUEEN); - state.set(new ChessCoordinate('e',8), ChessPiece.BLACK_KING); - state.set(new ChessCoordinate('f',8), ChessPiece.BLACK_BISHOP); - state.set(new ChessCoordinate('g',8), ChessPiece.BLACK_KNIGHT); - state.set(new ChessCoordinate('h',8), ChessPiece.BLACK_ROOK); - - for(int i=1; i<= ChessCoordinate.MAX; i++) - state.set(new ChessCoordinate(i,7), ChessPiece.BLACK_PAWN); - - state.set(new ChessCoordinate('a',1), ChessPiece.WHITE_ROOK); - state.set(new ChessCoordinate('b',1), ChessPiece.WHITE_KNIGHT); - state.set(new ChessCoordinate('c',1), ChessPiece.WHITE_BISHOP); - state.set(new ChessCoordinate('d',1), ChessPiece.WHITE_QUEEN); - state.set(new ChessCoordinate('e',1), ChessPiece.WHITE_KING); - state.set(new ChessCoordinate('f',1), ChessPiece.WHITE_BISHOP); - state.set(new ChessCoordinate('g',1), ChessPiece.WHITE_KNIGHT); - state.set(new ChessCoordinate('h',1), ChessPiece.WHITE_ROOK); - - for(int i=1; i<= ChessCoordinate.MAX; i++) - state.set(new ChessCoordinate(i,2), ChessPiece.WHITE_PAWN); - } - - - public void move(ChessMove move) { - ChessCoordinate src = move.getCoordinate(); - move(src, src.getCoordinate(state, move)); - } - //CHECK - public void move( - ChessCoordinate src, - ChessDirection direction, - int steps) { - ChessMove move = new ChessMove(src, direction, steps); - move(src, src.getCoordinate(state, move)); - } - - //CHECK - public boolean move(ChessCoordinate src, ChessCoordinate dest) { - ChessRules.INSTANCE.setLog(true); - ChessRules.INSTANCE.setState(state); - ChessRules.INSTANCE.setSrc(src); - ChessRules.INSTANCE.setDest(dest); - - if(ChessRules.INSTANCE.validMove()) { - ChessRules.INSTANCE.applyCaptureRule(); - state.move(src, dest); - return true; - } else - return false; - } - - //CHECK - public String printState() { - byte[][] state = this.state.getState(); - String result = ""; - - for(int i=0; i< ChessCoordinate.MAX; i++) { - for(int j=0; j< ChessCoordinate.MAX; j++) - result += ChessPiece.find(state[i][j])+" | "; - result += "\n"; - } - return result; - } - - public ChessState getState() { - return state; - } -} diff --git a/src/chess/ChessColor.java b/src/chess/ChessColor.java @@ -1,20 +0,0 @@ -package chess; - -/** - * Check - */ -public enum ChessColor { - BLACK ("B"), - WHITE ("W"), - NA (" "); - - private String symbol; - - ChessColor(String symbol) { - this.symbol = symbol; - } - - public String toString() { - return symbol; - } -} diff --git a/src/chess/ChessCoordinate.java b/src/chess/ChessCoordinate.java @@ -1,155 +0,0 @@ -package chess; - -import chess.ChessPiece; - -/** - * Check - */ -public class ChessCoordinate { - - public static final int MAX = 8; - - private char file; - private int rank; - - - public ChessCoordinate(char file, int rank) { - this.file = file; - this.rank = rank; - } - - public ChessCoordinate(int file, int rank) { - this((char) (file-1+97), rank); // 97 = 'a' - } - - public char getFile() { - return file; - } - - public int getRank() { - return rank; - } - - public int getFileNumber() { - return file - 96; // 97 = 'a' - } - - //CHECK - public ChessCoordinate getCoordinate( - ChessState state, ChessMove move) { - - int file = getFileNumber(); - int rank = getRank(); - - int steps = move.getSteps(); - ChessDirection dir = move.getDirection(); - - final ChessPiece srcPiece = - ChessPiece.find(state.get(move.getCoordinate())); - - if(srcPiece.getColor() == ChessColor.WHITE) { - if(dir == ChessDirection.FORWARD) rank += steps; - else if(dir == ChessDirection.BACK) rank -= steps; - else if(dir == ChessDirection.LEFT) file -= steps; - else if(dir == ChessDirection.RIGHT) file += steps; - - else if(dir == ChessDirection.FORWARD_LEFT) { - file -= steps; - rank += steps; - } else if(dir == ChessDirection.FORWARD_RIGHT) { - file += steps; - rank += steps; - } else if(dir == ChessDirection.BACK_LEFT) { - file -= steps; - rank -= steps; - } else if(dir == ChessDirection.BACK_RIGHT) { - file += steps; - rank -= steps; - } else if(dir == ChessDirection.KNIGHT_FORWARD_LEFT) { - file -= steps; - rank += steps+1; - } else if(dir == ChessDirection.KNIGHT_FORWARD_RIGHT) { - file += steps; - rank += steps+1; - } else if(dir == ChessDirection.KNIGHT_BACK_LEFT) { - file -= steps; - rank -= steps+1; - } else if(dir == ChessDirection.KNIGHT_BACK_RIGHT) { - file += steps; - rank -= steps+1; - } else if(dir == ChessDirection.KNIGHT_LEFT_FORWARD) { - file -= steps+1; - rank += steps; - } else if(dir == ChessDirection.KNIGHT_LEFT_BACK) { - file -= steps+1; - rank -= steps; - } else if(dir == ChessDirection.KNIGHT_RIGHT_FORWARD) { - file += steps+1; - rank += steps; - } else if(dir == ChessDirection.KNIGHT_RIGHT_BACK) { - file += steps+1; - rank -= steps; - } - } else { - if(dir == ChessDirection.FORWARD) rank -= steps; - else if(dir == ChessDirection.BACK) rank += steps; - else if(dir == ChessDirection.LEFT) file += steps; - else if(dir == ChessDirection.RIGHT) file -= steps; - - else if(dir == ChessDirection.FORWARD_LEFT) { - file += steps; - rank -= steps; - } else if(dir == ChessDirection.FORWARD_RIGHT) { - file -= steps; - rank -= steps; - } else if(dir == ChessDirection.BACK_LEFT) { - file += steps; - rank += steps; - } else if(dir == ChessDirection.BACK_RIGHT) { - file -= steps; - rank += steps; - } else if(dir == ChessDirection.KNIGHT_FORWARD_LEFT) { - file += steps; - rank -= steps+1; - } else if(dir == ChessDirection.KNIGHT_FORWARD_RIGHT) { - file -= steps; - rank -= steps+1; - } else if(dir == ChessDirection.KNIGHT_BACK_LEFT) { - file += steps; - rank += steps+1; - } else if(dir == ChessDirection.KNIGHT_BACK_RIGHT) { - file -= steps; - rank += steps+1; - } else if(dir == ChessDirection.KNIGHT_LEFT_FORWARD) { - file += steps+1; - rank -= steps; - } else if(dir == ChessDirection.KNIGHT_LEFT_BACK) { - file += steps+1; - rank += steps; - } else if(dir == ChessDirection.KNIGHT_RIGHT_FORWARD) { - file -= steps+1; - rank -= steps; - } else if(dir == ChessDirection.KNIGHT_RIGHT_BACK) { - file -= steps+1; - rank += steps; - } - } - - return new ChessCoordinate(file,rank); - } - - public boolean invalid() { - return file > 'h' || file < 'a' || - rank > ChessCoordinate.MAX || rank < 1; - } - - public String toString() { - return ""+file+rank; - } - - public boolean equals(Object o) { - ChessCoordinate coor = (ChessCoordinate) o; - return coor.getRank() == this.getRank() && - coor.getFile() == this.getFile(); - } -} diff --git a/src/chess/ChessDirection.java b/src/chess/ChessDirection.java @@ -1,25 +0,0 @@ -package chess; - -/** - * Check - */ -public enum ChessDirection { - FORWARD, - BACK, - LEFT, - RIGHT, - FORWARD_LEFT, - FORWARD_RIGHT, - BACK_LEFT, - BACK_RIGHT, - - // exclusive knight moves - KNIGHT_LEFT_FORWARD, - KNIGHT_LEFT_BACK, - KNIGHT_RIGHT_FORWARD, - KNIGHT_RIGHT_BACK, - KNIGHT_FORWARD_LEFT, - KNIGHT_FORWARD_RIGHT, - KNIGHT_BACK_LEFT, - KNIGHT_BACK_RIGHT -} diff --git a/src/chess/ChessGame.java b/src/chess/ChessGame.java @@ -1,62 +0,0 @@ -package chess; - -import java.util.List; - -/** - * - */ -public class ChessGame { - - public static void main(String[] args) { - ChessPlayer player = new ChessPlayer(ChessColor.WHITE); - ChessPlayer opponent = new ChessPlayer(ChessColor.BLACK); - ChessColor turn = player.getColor(); - ChessBoard board = new ChessBoard(); - - /* - while (true) { - // our turn - while(turn == player.getColor()) { - - } - - // opponent's turn - while(turn == opponent.getColor()) { - - } - } - */ - - board.move(new ChessCoordinate('b', 2), ChessDirection.FORWARD, 1); - board.move(new ChessCoordinate('d', 2), ChessDirection.FORWARD, 1); - ChessCoordinate src = new ChessCoordinate('b',1); - - ChessRules.INSTANCE.setLog(false); - - List<ChessMove> moves = - ChessMove.possibleMoves(src, board.getState()); - - for(ChessMove move : moves) - System.out.println(move.toString()); - - /* - board.move(new ChessCoordinate('d', 2), ChessDirection.FORWARD, 1); - board.move(new ChessCoordinate('d', 3), ChessDirection.FORWARD, 1); - board.move(new ChessCoordinate('d', 1), ChessDirection.FORWARD, 2); - board.move(new ChessCoordinate('d', 3), ChessDirection.FORWARD_LEFT, 2); - //game.move(new ChessCoordinate('d',4), ChessDirection.LEFT, 1); - */ - System.out.println("Moves: "+player.getMoves()+ - "\n"+board.printState()); - - ChessClock clock = new ChessClock(60); // 60 seconds - try { - Thread.sleep((long)(5 * 1000)); // wait 5 seconds - } catch (InterruptedException e) { - - } - // 55 seconds - System.out.println(clock.timeRemaining()+" seconds remaining"); - - } -} diff --git a/src/chess/ChessMove.java b/src/chess/ChessMove.java @@ -1,67 +0,0 @@ -package chess; - -import java.util.ArrayList; -import java.util.List; - -/** - * - */ -public class ChessMove { - - private ChessCoordinate coordinate; - private ChessDirection direction; - private int steps; - - public ChessMove( - ChessCoordinate coordinate, - ChessDirection direction, - int steps) { - this.coordinate = coordinate; - this.direction = direction; - this.steps = steps; - } - - public int getSteps() { - return steps; - } - - public ChessDirection getDirection() { - return direction; - } - - public ChessCoordinate getCoordinate() { - return coordinate; - } - - public static List<ChessMove> possibleMoves( - ChessCoordinate src, ChessState state) { - - List<ChessMove> moves = new ArrayList<ChessMove>(); - ChessRules.INSTANCE.setState(state); - ChessRules.INSTANCE.setSrc(src); - - final ChessDirection[] directions = - ChessRules.INSTANCE.possibleDirections(); - - for(ChessDirection direction : directions) { - boolean valid = true; - int steps = 1; - while (valid) { - ChessMove move = new ChessMove(src, direction, steps); - ChessCoordinate dest = src; - dest = dest.getCoordinate(state, move); - ChessRules.INSTANCE.setDest(dest); - valid = ChessRules.INSTANCE.validMove(); - - if(valid) moves.add(move); - steps++; - } - } - - return moves; - } - - public String toString() { - return coordinate+" "+direction+" "+steps+" steps"; - } -} diff --git a/src/chess/ChessPiece.java b/src/chess/ChessPiece.java @@ -1,61 +0,0 @@ -package chess; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * Check - */ -public enum ChessPiece { - BLACK_KING (12, ChessColor.BLACK, ChessType.KING), - BLACK_QUEEN (11, ChessColor.BLACK, ChessType.QUEEN), - BLACK_BISHOP (10, ChessColor.BLACK, ChessType.BISHOP), - BLACK_KNIGHT (9, ChessColor.BLACK, ChessType.KNIGHT), - BLACK_ROOK (8, ChessColor.BLACK, ChessType.ROOK), - BLACK_PAWN (7, ChessColor.BLACK, ChessType.PAWN), - WHITE_KING (6, ChessColor.WHITE, ChessType.KING), - WHITE_QUEEN (5, ChessColor.WHITE, ChessType.QUEEN), - WHITE_BISHOP (4, ChessColor.WHITE, ChessType.BISHOP), - WHITE_KNIGHT (3, ChessColor.WHITE, ChessType.KNIGHT), - WHITE_ROOK (2, ChessColor.WHITE, ChessType.ROOK), - WHITE_PAWN (1, ChessColor.WHITE, ChessType.PAWN), - NA (-1, ChessColor.NA, ChessType.NA); - - private int num; - private ChessColor color; - private ChessType type; - - ChessPiece(int num, ChessColor color, ChessType type) { - this.num = num; - this.color = color; - this.type = type; - } - - public ChessType getType() { - return type; - } - - public ChessColor getColor() { - return color; - } - - public int getNum() { - return num; - } - - public String toString() { - return color.toString() + type.toString(); - } - - public static ChessPiece find(int num) { - for (ChessPiece piece : VALUES) - if(piece.getNum() == num) return piece; - - return ChessPiece.NA; - } - - /* Cached list of all possible values. */ - private static final List<ChessPiece> VALUES = - Collections.unmodifiableList(Arrays.asList(values())); -} diff --git a/src/chess/ChessPlayer.java b/src/chess/ChessPlayer.java @@ -1,35 +0,0 @@ -package chess; - -/** - * Check - */ -public class ChessPlayer { - - private static final String ERROR_MSG = - ChessPlayer.class+": Invalid color."; - - private int points; - private ChessColor color; - private int moves; - - public ChessPlayer(ChessColor color) { - if(color == ChessColor.NA) - throw new IllegalArgumentException(ERROR_MSG); - - this.color = color; - points = 0; - moves = 0; - } - - public ChessColor getColor() { - return color; - } - - public int getPoints() { - return points; - } - - public int getMoves() { - return moves; - } -} diff --git a/src/chess/ChessRules.java b/src/chess/ChessRules.java @@ -1,356 +0,0 @@ -package chess; - -import java.util.Arrays; - -/** - * Check - */ -public enum ChessRules { - - INSTANCE; - - private ChessState state; - private ChessCoordinate src; - private ChessCoordinate dest; - - private ChessPiece srcPiece; - private ChessPiece destPiece; - private ChessType srcType; - - private boolean log = true; - - public void setState(ChessState state) { - this.state = state; - } - - public void setSrc(ChessCoordinate src) { - this.src = src; - this.srcPiece = !src.invalid() ? - ChessPiece.find(state.get(src)) : null; - this.srcType = srcPiece != null ? srcPiece.getType() : null; - } - - public void setDest(ChessCoordinate dest) { - this.dest = dest; - this.destPiece = !dest.invalid() ? - ChessPiece.find(state.get(dest)) : null; - } - - public void setLog(boolean log) { - this.log = log; - } - - // must be called after testing invalid is false - // eg. if(validMove(...)) applyCaptureRule(); - // CHECK - public void applyCaptureRule() { - // are the source and destination pieces different colors? - if(srcPiece.getColor() != destPiece.getColor() && - srcPiece != ChessPiece.NA && - destPiece != ChessPiece.NA) { - - // CAPTURE! - // TODO handle player points here - - System.out.println(ChessRules.class + ": CAPTURED -> " + - srcPiece + src + destPiece + dest); - } - } - - // CHECK - public boolean validMove() { - return pieceMoves() && - srcHasPiece() && - insideBoard() && - unoccupiedSpace() && - legalSkip() && - validRookMove() && - validKnightMove() && - validBishopMove() && - validQueenMove() && - validKingMove() && - validPawnMove(); - } - - /* - * Checks if the piece moved. It cannot stay in same spot. - */ - private boolean pieceMoves() { - if(src.equals(dest)) { - logFailure("pieceMoves"); - return false; - } - return true; - } - - /* - * Checks if source coordinate has a chess piece. - */ - private boolean srcHasPiece() { - if(srcPiece == ChessPiece.NA) { - logFailure("srcHasPiece"); - return false; - } - return true; - } - - /* - * Checks if the destination coordinate is inside the chess - * board. - */ - private boolean insideBoard() { - if(dest.invalid()) { - logFailure("insideBoard"); - return false; - } - return true; - } - - /* - * Checks if player moved a chess piece onto one of his own pieces. - */ - private boolean unoccupiedSpace() { - // are the source and destination pieces same color? - if(srcPiece.getColor() == destPiece.getColor()) { - logFailure("unoccupiedSpace"); - return false; - } - return true; - } - - /* - * Checks if source piece skips over other pieces when moving. - */ - private boolean legalSkip() { - // Knight is an exception. - if(srcType==ChessType.KNIGHT) - return true; - - final int hor = dest.getFile() - src.getFile(); - final int ver = dest.getRank() - src.getRank(); - - int rank = dest.getRank(); - int file = dest.getFile(); - - if(hor != 0) { - if(hor > 0) file--; - else file++; - } - if(ver != 0) { - if(ver > 0) rank--; - else rank++; - } - - while (src.getFile() != file || src.getRank() != rank) { - - final byte pieceValue = - state.get(new ChessCoordinate((char)file, rank)); - ChessPiece check = ChessPiece.find(pieceValue); - - if(check != ChessPiece.NA) { - logFailure("legalSkip"); - return false; - } - - if(hor != 0) { - if(hor > 0) file--; - else file++; - } - if(ver != 0) { - if(ver > 0) rank--; - else rank++; - } - } - return true; - } - - private boolean validRookMove(){ - //Rank must be the same exclusive or file must be the - // same for a rook to move - if(srcType==ChessType.ROOK){ - if(rookMove()) - return true; - - logFailure("validRookMove"); - return false; - } - return true; - } - - private boolean validKnightMove(){ - if(srcType==ChessType.KNIGHT){ - final int rankDiff = Math.abs(src.getRank()-dest.getRank()); - final int fileDiff = Math.abs(src.getFile()-dest.getFile()); - - if((rankDiff == 2 && fileDiff == 1) || - (rankDiff == 1 && fileDiff == 2)) { - return true; - } - - logFailure("validKnightMove"); - return false; - } - return true; - } - - private boolean validPawnMove(){ - if(srcType==ChessType.PAWN){ - //Pawn move up one - if (srcPiece.getColor() == ChessColor.WHITE){ - - if(src.getRank()+1 == dest.getRank() && - src.getFile() == dest.getFile()){ - return true; - } - - //Pawn capture - if(src.getRank()+1 == dest.getRank() && - Math.abs(src.getFile()-dest.getFile()) == 1 && - srcPiece.getColor() != destPiece.getColor() && - destPiece != ChessPiece.NA) { - return true; - } - } else { - - if(src.getRank() == dest.getRank()+1 && - src.getFile() == dest.getFile()) { - return true; - } - - //Pawn capture - if(src.getRank()+1 == dest.getRank()+1 && - Math.abs(src.getFile()-dest.getFile()) == 1 && - srcPiece.getColor() != destPiece.getColor() && - destPiece != ChessPiece.NA){ - return true; - } - } - //System.out.println("Pawn Cannot Capture"); - /*Pawns can also move 2 spaces on first turn - *Pawns can also capture en passant - Still functional without these features though*/ - logFailure("validPawnMove"); - return false; - } - return true; - } - - private boolean validBishopMove(){ - if(srcType==ChessType.BISHOP){ - if(bishopMove()) - return true; - - logFailure("validBishopMove"); - return false; - } - return true; - } - - private boolean validKingMove(){ - if(srcType==ChessType.KING){ - if(src.getRank()-dest.getRank() < 2 && - src.getFile()-dest.getFileNumber() < 2) { - return true; - } - - logFailure("validKingMove"); - //Moves that get the king captured are invalid - return false; - } - return true; - } - - private boolean validQueenMove() { - if(srcType == ChessType.QUEEN){ - if(!(rookMove() ^ bishopMove())) { - logFailure("validQueenMove"); - return false; - } - } - return true; - } - - public ChessDirection[] possibleDirections() { - if(srcType==ChessType.PAWN) return pawnDirections(); - if(srcType==ChessType.ROOK) return rookDirections(); - if(srcType==ChessType.BISHOP) return bishopDirections(); - if(srcType==ChessType.KNIGHT) return knightDirections(); - if(srcType==ChessType.QUEEN) return queenDirections(); - if(srcType==ChessType.KING) return kingDirections(); - else - return new ChessDirection[]{}; - } - - private ChessDirection[] pawnDirections() { - return new ChessDirection[] { - ChessDirection.FORWARD, - ChessDirection.FORWARD_LEFT, - ChessDirection.FORWARD_RIGHT - }; - } - - private ChessDirection[] rookDirections() { - return new ChessDirection[] { - ChessDirection.FORWARD, - ChessDirection.BACK, - ChessDirection.LEFT, - ChessDirection.RIGHT - }; - } - - private ChessDirection[] bishopDirections() { - return new ChessDirection[] { - ChessDirection.FORWARD_LEFT, - ChessDirection.FORWARD_RIGHT, - ChessDirection.BACK_LEFT, - ChessDirection.BACK_RIGHT - }; - } - - private ChessDirection[] knightDirections() { - return new ChessDirection[] { - ChessDirection.KNIGHT_LEFT_FORWARD, - ChessDirection.KNIGHT_LEFT_BACK, - ChessDirection.KNIGHT_RIGHT_FORWARD, - ChessDirection.KNIGHT_RIGHT_BACK, - ChessDirection.KNIGHT_FORWARD_LEFT, - ChessDirection.KNIGHT_FORWARD_RIGHT, - ChessDirection.KNIGHT_BACK_LEFT, - ChessDirection.KNIGHT_BACK_RIGHT - }; - } - - private ChessDirection[] queenDirections() { - return concat(bishopDirections(), rookDirections()); - } - - private ChessDirection[] kingDirections() { - return queenDirections(); - } - - private boolean rookMove() { - return src.getRank() == dest.getRank() ^ - src.getFile() == dest.getFile(); - } - - private boolean bishopMove() { - final int rankDiff = Math.abs(src.getRank()-dest.getRank()); - final int fileDiff = Math.abs(src.getFile()-dest.getFile()); - - return rankDiff == fileDiff; - } - - private void logFailure(String method) { - if(log) - System.err.println(ChessRules.class + ": FAILURE -> "+ - method+" test --- Move: "+ - srcPiece+src+dest); - } - - // TODO where should this be placed? - private static <T> T[] concat(T[] first, T[] second) { - T[] result = Arrays.copyOf(first, first.length + second.length); - System.arraycopy(second, 0, result, first.length, second.length); - return result; - } -} diff --git a/src/chess/ChessType.java b/src/chess/ChessType.java @@ -1,46 +0,0 @@ -package chess; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * Check - */ -public enum ChessType { - KING ("K", 1000), - QUEEN ("Q", 9), - BISHOP ("B", 3), - KNIGHT ("N", 3), - ROOK ("R", 5), - PAWN ("P", 1), - NA (" ", 0); - - private int value; - private String symbol; - - ChessType(String symbol, int value) { - this.symbol = symbol; - this.value = value; - } - - public int getValue() { - return value; - } - - public String toString() { - return symbol; - } - - public static ChessType find(String symbol) { - for (ChessType type : VALUES) - if(type.toString().equals(symbol.toUpperCase())) - return type; - - return ChessType.NA; - } - - /* Cached list of all possible values. */ - private static final List<ChessType> VALUES = - Collections.unmodifiableList(Arrays.asList(values())); -} diff --git a/src/main/java/chess/ChessBoard.java b/src/main/java/chess/ChessBoard.java @@ -0,0 +1,88 @@ +package chess; + +import chess.utils.ChessDirection; + +/** + * + */ +public class ChessBoard { + private ChessState state; + + public ChessBoard() { + state = new ChessState(); + init(); + } + + //CHECK + public void init() { + state.set(new ChessCoordinate('a',8), ChessPiece.BLACK_ROOK); + state.set(new ChessCoordinate('b',8), ChessPiece.BLACK_KNIGHT); + state.set(new ChessCoordinate('c',8), ChessPiece.BLACK_BISHOP); + state.set(new ChessCoordinate('d',8), ChessPiece.BLACK_QUEEN); + state.set(new ChessCoordinate('e',8), ChessPiece.BLACK_KING); + state.set(new ChessCoordinate('f',8), ChessPiece.BLACK_BISHOP); + state.set(new ChessCoordinate('g',8), ChessPiece.BLACK_KNIGHT); + state.set(new ChessCoordinate('h',8), ChessPiece.BLACK_ROOK); + + for(int i=1; i<= ChessCoordinate.MAX; i++) + state.set(new ChessCoordinate(i,7), ChessPiece.BLACK_PAWN); + + state.set(new ChessCoordinate('a',1), ChessPiece.WHITE_ROOK); + state.set(new ChessCoordinate('b',1), ChessPiece.WHITE_KNIGHT); + state.set(new ChessCoordinate('c',1), ChessPiece.WHITE_BISHOP); + state.set(new ChessCoordinate('d',1), ChessPiece.WHITE_QUEEN); + state.set(new ChessCoordinate('e',1), ChessPiece.WHITE_KING); + state.set(new ChessCoordinate('f',1), ChessPiece.WHITE_BISHOP); + state.set(new ChessCoordinate('g',1), ChessPiece.WHITE_KNIGHT); + state.set(new ChessCoordinate('h',1), ChessPiece.WHITE_ROOK); + + for(int i=1; i<= ChessCoordinate.MAX; i++) + state.set(new ChessCoordinate(i,2), ChessPiece.WHITE_PAWN); + } + + + public boolean move(ChessMove move) { + ChessCoordinate src = move.getCoordinate(); + return move(src, src.getCoordinate(state, move)); + } + //CHECK + public boolean move( + ChessCoordinate src, + ChessDirection direction, + int steps) { + ChessMove move = new ChessMove(src, direction, steps); + return move(src, src.getCoordinate(state, move)); + } + + //CHECK + public boolean move(ChessCoordinate src, ChessCoordinate dest) { + ChessRules.INSTANCE.setLog(true); + ChessRules.INSTANCE.setState(state); + ChessRules.INSTANCE.setSrc(src); + ChessRules.INSTANCE.setDest(dest); + + if(ChessRules.INSTANCE.validMove()) { + ChessRules.INSTANCE.applyCaptureRule(); + state.move(src, dest); + return true; + } else + return false; + } + + //CHECK + public String printState() { + byte[][] state = this.state.getState(); + String result = ""; + + for(int i=0; i< ChessCoordinate.MAX; i++) { + for(int j=0; j< ChessCoordinate.MAX; j++) + result += ChessPiece.find(state[i][j])+" | "; + result += "\n"; + } + return result; + } + + public ChessState getState() { + return state; + } +} diff --git a/src/chess/ChessClock.java b/src/main/java/chess/ChessClock.java diff --git a/src/main/java/chess/ChessCoordinate.java b/src/main/java/chess/ChessCoordinate.java @@ -0,0 +1,156 @@ +package chess; + +import chess.utils.ChessColor; +import chess.utils.ChessDirection; + +/** + * Check + */ +public class ChessCoordinate { + + public static final int MAX = 8; + + private char file; + private int rank; + + + public ChessCoordinate(char file, int rank) { + this.file = file; + this.rank = rank; + } + + public ChessCoordinate(int file, int rank) { + this((char) (file-1+97), rank); // 97 = 'a' + } + + public char getFile() { + return file; + } + + public int getRank() { + return rank; + } + + public int getFileNumber() { + return file - 96; // 97 = 'a' + } + + //CHECK + public ChessCoordinate getCoordinate( + ChessState state, ChessMove move) { + + int file = getFileNumber(); + int rank = getRank(); + + int steps = move.getSteps(); + ChessDirection dir = move.getDirection(); + + final ChessPiece srcPiece = + ChessPiece.find(state.get(move.getCoordinate())); + + if(srcPiece.getColor() == ChessColor.WHITE) { + if(dir == ChessDirection.FORWARD) rank += steps; + else if(dir == ChessDirection.BACK) rank -= steps; + else if(dir == ChessDirection.LEFT) file -= steps; + else if(dir == ChessDirection.RIGHT) file += steps; + + else if(dir == ChessDirection.FORWARD_LEFT) { + file -= steps; + rank += steps; + } else if(dir == ChessDirection.FORWARD_RIGHT) { + file += steps; + rank += steps; + } else if(dir == ChessDirection.BACK_LEFT) { + file -= steps; + rank -= steps; + } else if(dir == ChessDirection.BACK_RIGHT) { + file += steps; + rank -= steps; + } else if(dir == ChessDirection.KNIGHT_FORWARD_LEFT) { + file -= steps; + rank += steps+1; + } else if(dir == ChessDirection.KNIGHT_FORWARD_RIGHT) { + file += steps; + rank += steps+1; + } else if(dir == ChessDirection.KNIGHT_BACK_LEFT) { + file -= steps; + rank -= steps+1; + } else if(dir == ChessDirection.KNIGHT_BACK_RIGHT) { + file += steps; + rank -= steps+1; + } else if(dir == ChessDirection.KNIGHT_LEFT_FORWARD) { + file -= steps+1; + rank += steps; + } else if(dir == ChessDirection.KNIGHT_LEFT_BACK) { + file -= steps+1; + rank -= steps; + } else if(dir == ChessDirection.KNIGHT_RIGHT_FORWARD) { + file += steps+1; + rank += steps; + } else if(dir == ChessDirection.KNIGHT_RIGHT_BACK) { + file += steps+1; + rank -= steps; + } + } else { + if(dir == ChessDirection.FORWARD) rank -= steps; + else if(dir == ChessDirection.BACK) rank += steps; + else if(dir == ChessDirection.LEFT) file += steps; + else if(dir == ChessDirection.RIGHT) file -= steps; + + else if(dir == ChessDirection.FORWARD_LEFT) { + file += steps; + rank -= steps; + } else if(dir == ChessDirection.FORWARD_RIGHT) { + file -= steps; + rank -= steps; + } else if(dir == ChessDirection.BACK_LEFT) { + file += steps; + rank += steps; + } else if(dir == ChessDirection.BACK_RIGHT) { + file -= steps; + rank += steps; + } else if(dir == ChessDirection.KNIGHT_FORWARD_LEFT) { + file += steps; + rank -= steps+1; + } else if(dir == ChessDirection.KNIGHT_FORWARD_RIGHT) { + file -= steps; + rank -= steps+1; + } else if(dir == ChessDirection.KNIGHT_BACK_LEFT) { + file += steps; + rank += steps+1; + } else if(dir == ChessDirection.KNIGHT_BACK_RIGHT) { + file -= steps; + rank += steps+1; + } else if(dir == ChessDirection.KNIGHT_LEFT_FORWARD) { + file += steps+1; + rank -= steps; + } else if(dir == ChessDirection.KNIGHT_LEFT_BACK) { + file += steps+1; + rank += steps; + } else if(dir == ChessDirection.KNIGHT_RIGHT_FORWARD) { + file -= steps+1; + rank -= steps; + } else if(dir == ChessDirection.KNIGHT_RIGHT_BACK) { + file -= steps+1; + rank += steps; + } + } + + return new ChessCoordinate(file,rank); + } + + public boolean invalid() { + return file > 'h' || file < 'a' || + rank > ChessCoordinate.MAX || rank < 1; + } + + public String toString() { + return ""+file+rank; + } + + public boolean equals(Object o) { + ChessCoordinate coor = (ChessCoordinate) o; + return coor.getRank() == this.getRank() && + coor.getFile() == this.getFile(); + } +} diff --git a/src/main/java/chess/ChessGame.java b/src/main/java/chess/ChessGame.java @@ -0,0 +1,88 @@ +package chess; + +import chess.server.*; +import chess.utils.ChessDirection; + +/** + * + */ +public class ChessGame { + + public static void main(String[] args) { + final int gameId = 7; + final int teamNo = 1; + final String secret = "32c68cae"; + + BenCarleChessServer server = + new BenCarleChessServer(gameId, teamNo, secret); + + ChessPlayer player = new ChessPlayer(teamNo); + ChessPlayer opponent = new ChessPlayer(teamNo==1 ? 2 : 1); + + ChessBoard board = new ChessBoard(); + + StatusResponse status = server.poll(); + + while (true) { + + // our turn + while(status.isReady()) { + + // just a test + ChessMove move = new ChessMove( + new ChessCoordinate('c',2), + ChessDirection.FORWARD, + 1); + + ChessClock clock = + new ChessClock(status.getSecondsLeft()); + + // TODO iterate board & get player's pieces + // TODO for each piece, get all possible moves + // TODO evaluate capture/protection points + // TODO store tree in memory (or other storage option) + // TODO eval clock and pick best move + + MoveResponse response = server.push(board.getState(), move); + if(response.isResult() && board.move(move)) { + player.incrementMoves(); + System.out.println("Player Moved: "+ + status.getLastMove()+" with "+ + status.getSecondsLeft()+" seconds left."); + } + + status = server.poll(); + } + + // TODO minmax for every opponents possible move + // TODO concurrently hopefully? + + // print board in console every 5 moves + if(player.getMoves() % 5 == 0) { + System.out.println("Moves: "+ + status.getLastMoveNumber()+"\n"+ + board.printState()); + } + + // opponent's turn + while(!status.isReady()) { + // poll every 5 seconds + try { + Thread.sleep((long)(5 * 1000)); + status = server.poll(); + } catch (InterruptedException e) {} + } + + // move opponent's last move + if(board.move(status.srcLastMove(), status.destLastMove())) { + opponent.incrementMoves(); + System.out.println("Opponent Moved: "+ + status.getLastMove()+" with "+ + status.getSecondsLeft()+" seconds left."); + } + + // TODO if generated minmax table during opponent's move, + // TODO choose move the opponent chose and continue from there + } + } +} diff --git a/src/main/java/chess/ChessMove.java b/src/main/java/chess/ChessMove.java @@ -0,0 +1,69 @@ +package chess; + +import chess.utils.ChessDirection; + +import java.util.ArrayList; +import java.util.List; + +/** + * + */ +public class ChessMove { + + private ChessCoordinate coordinate; + private ChessDirection direction; + private int steps; + + public ChessMove( + ChessCoordinate coordinate, + ChessDirection direction, + int steps) { + this.coordinate = coordinate; + this.direction = direction; + this.steps = steps; + } + + public int getSteps() { + return steps; + } + + public ChessDirection getDirection() { + return direction; + } + + public ChessCoordinate getCoordinate() { + return coordinate; + } + + public static List<ChessMove> possibleMoves( + ChessCoordinate src, ChessState state) { + + List<ChessMove> moves = new ArrayList<ChessMove>(); + ChessRules.INSTANCE.setState(state); + ChessRules.INSTANCE.setSrc(src); + + final ChessDirection[] directions = + ChessRules.INSTANCE.possibleDirections(); + + for(ChessDirection direction : directions) { + boolean valid = true; + int steps = 1; + while (valid) { + ChessMove move = new ChessMove(src, direction, steps); + ChessCoordinate dest = src; + dest = dest.getCoordinate(state, move); + ChessRules.INSTANCE.setDest(dest); + valid = ChessRules.INSTANCE.validMove(); + + if(valid) moves.add(move); + steps++; + } + } + + return moves; + } + + public String toString() { + return coordinate+" "+direction+" "+steps+" steps"; + } +} diff --git a/src/main/java/chess/ChessPiece.java b/src/main/java/chess/ChessPiece.java @@ -0,0 +1,64 @@ +package chess; + +import chess.utils.ChessColor; +import chess.utils.ChessType; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Check + */ +public enum ChessPiece { + BLACK_KING (12, ChessColor.BLACK, ChessType.KING), + BLACK_QUEEN (11, ChessColor.BLACK, ChessType.QUEEN), + BLACK_BISHOP (10, ChessColor.BLACK, ChessType.BISHOP), + BLACK_KNIGHT (9, ChessColor.BLACK, ChessType.KNIGHT), + BLACK_ROOK (8, ChessColor.BLACK, ChessType.ROOK), + BLACK_PAWN (7, ChessColor.BLACK, ChessType.PAWN), + WHITE_KING (6, ChessColor.WHITE, ChessType.KING), + WHITE_QUEEN (5, ChessColor.WHITE, ChessType.QUEEN), + WHITE_BISHOP (4, ChessColor.WHITE, ChessType.BISHOP), + WHITE_KNIGHT (3, ChessColor.WHITE, ChessType.KNIGHT), + WHITE_ROOK (2, ChessColor.WHITE, ChessType.ROOK), + WHITE_PAWN (1, ChessColor.WHITE, ChessType.PAWN), + NA (-1, ChessColor.NA, ChessType.NA); + + private int num; + private ChessColor color; + private ChessType type; + + ChessPiece(int num, ChessColor color, ChessType type) { + this.num = num; + this.color = color; + this.type = type; + } + + public ChessType getType() { + return type; + } + + public ChessColor getColor() { + return color; + } + + public int getNum() { + return num; + } + + public String toString() { + return color.toString() + type.toString(); + } + + public static ChessPiece find(int num) { + for (ChessPiece piece : VALUES) + if(piece.getNum() == num) return piece; + + return ChessPiece.NA; + } + + /* Cached list of all possible values. */ + private static final List<ChessPiece> VALUES = + Collections.unmodifiableList(Arrays.asList(values())); +} diff --git a/src/main/java/chess/ChessPlayer.java b/src/main/java/chess/ChessPlayer.java @@ -0,0 +1,57 @@ +package chess; + +import chess.utils.ChessColor; + +/** + * Check + */ +public class ChessPlayer { + + private static final String ERROR_MSG = + ChessPlayer.class+": Invalid color."; + + private int points; + private ChessColor color; + private int moves; + + public ChessPlayer(ChessColor color) { + if(color == ChessColor.NA) + throw new IllegalArgumentException(ERROR_MSG); + + this.color = color; + points = 0; + moves = 0; + } + + public ChessPlayer(int colorNo) { + if(colorNo > 2 || colorNo < 1) + throw new IllegalArgumentException(ERROR_MSG); + + ChessColor color = + colorNo==1 ? ChessColor.WHITE : ChessColor.BLACK; + + this.color = color; + points = 0; + moves = 0; + } + + public ChessColor getColor() { + return color; + } + + public int getPoints() { + return points; + } + + public int getMoves() { + return moves; + } + + public void setPoints(int points) { + this.points = points; + } + + public void incrementMoves() { + this.moves++; + } +} diff --git a/src/main/java/chess/ChessRules.java b/src/main/java/chess/ChessRules.java @@ -0,0 +1,360 @@ +package chess; + +import chess.utils.ChessColor; +import chess.utils.ChessDirection; +import chess.utils.ChessType; + +import java.util.Arrays; + +/** + * Check + */ +public enum ChessRules { + + INSTANCE; + + private ChessState state; + private ChessCoordinate src; + private ChessCoordinate dest; + + private ChessPiece srcPiece; + private ChessPiece destPiece; + private ChessType srcType; + + private boolean log = true; + + public void setState(ChessState state) { + this.state = state; + } + + public void setSrc(ChessCoordinate src) { + this.src = src; + this.srcPiece = !src.invalid() ? + ChessPiece.find(state.get(src)) : null; + this.srcType = srcPiece != null ? srcPiece.getType() : null; + } + + public void setDest(ChessCoordinate dest) { + this.dest = dest; + this.destPiece = !dest.invalid() ? + ChessPiece.find(state.get(dest)) : null; + } + + public void setLog(boolean log) { + this.log = log; + } + + // must be called after testing invalid is false + // eg. if(validMove(...)) applyCaptureRule(); + // CHECK + public void applyCaptureRule() { + // are the source and destination pieces different colors? + if(srcPiece.getColor() != destPiece.getColor() && + srcPiece != ChessPiece.NA && + destPiece != ChessPiece.NA) { + + // CAPTURE! + // TODO handle player points here + + System.out.println(ChessRules.class + ": CAPTURED -> " + + srcPiece + src + destPiece + dest); + } + } + + // CHECK + public boolean validMove() { + return pieceMoves() && + srcHasPiece() && + insideBoard() && + unoccupiedSpace() && + legalSkip() && + validRookMove() && + validKnightMove() && + validBishopMove() && + validQueenMove() && + validKingMove() && + validPawnMove(); + } + + /* + * Checks if the piece moved. It cannot stay in same spot. + */ + private boolean pieceMoves() { + if(src.equals(dest)) { + logFailure("pieceMoves"); + return false; + } + return true; + } + + /* + * Checks if source coordinate has a chess piece. + */ + private boolean srcHasPiece() { + if(srcPiece == ChessPiece.NA) { + logFailure("srcHasPiece"); + return false; + } + return true; + } + + /* + * Checks if the destination coordinate is inside the chess + * board. + */ + private boolean insideBoard() { + if(dest.invalid()) { + logFailure("insideBoard"); + return false; + } + return true; + } + + /* + * Checks if player moved a chess piece onto one of his own pieces. + */ + private boolean unoccupiedSpace() { + // are the source and destination pieces same color? + if(srcPiece.getColor() == destPiece.getColor()) { + logFailure("unoccupiedSpace"); + return false; + } + return true; + } + + /* + * Checks if source piece skips over other pieces when moving. + */ + private boolean legalSkip() { + // Knight is an exception. + if(srcType==ChessType.KNIGHT) + return true; + + final int hor = dest.getFile() - src.getFile(); + final int ver = dest.getRank() - src.getRank(); + + int rank = dest.getRank(); + int file = dest.getFile(); + + if(hor != 0) { + if(hor > 0) file--; + else file++; + } + if(ver != 0) { + if(ver > 0) rank--; + else rank++; + } + + while (src.getFile() != file || src.getRank() != rank) { + + final byte pieceValue = + state.get(new ChessCoordinate((char)file, rank)); + ChessPiece check = ChessPiece.find(pieceValue); + + if(check != ChessPiece.NA) { + logFailure("legalSkip"); + return false; + } + + if(hor != 0) { + if(hor > 0) file--; + else file++; + } + if(ver != 0) { + if(ver > 0) rank--; + else rank++; + } + } + return true; + } + + private boolean validRookMove(){ + //Rank must be the same exclusive or file must be the + // same for a rook to move + if(srcType==ChessType.ROOK){ + if(rookMove()) + return true; + + logFailure("validRookMove"); + return false; + } + return true; + } + + private boolean validKnightMove(){ + if(srcType==ChessType.KNIGHT){ + final int rankDiff = Math.abs(src.getRank()-dest.getRank()); + final int fileDiff = Math.abs(src.getFile()-dest.getFile()); + + if((rankDiff == 2 && fileDiff == 1) || + (rankDiff == 1 && fileDiff == 2)) { + return true; + } + + logFailure("validKnightMove"); + return false; + } + return true; + } + + private boolean validPawnMove(){ + if(srcType==ChessType.PAWN){ + //Pawn move up one + if (srcPiece.getColor() == ChessColor.WHITE){ + + if(src.getRank()+1 == dest.getRank() && + src.getFile() == dest.getFile()){ + return true; + } + + //Pawn capture + if(src.getRank()+1 == dest.getRank() && + Math.abs(src.getFile()-dest.getFile()) == 1 && + srcPiece.getColor() != destPiece.getColor() && + destPiece != ChessPiece.NA) { + return true; + } + } else { + + if(src.getRank() == dest.getRank()+1 && + src.getFile() == dest.getFile()) { + return true; + } + + //Pawn capture + if(src.getRank()+1 == dest.getRank()+1 && + Math.abs(src.getFile()-dest.getFile()) == 1 && + srcPiece.getColor() != destPiece.getColor() && + destPiece != ChessPiece.NA){ + return true; + } + } + //System.out.println("Pawn Cannot Capture"); + /*Pawns can also move 2 spaces on first turn + *Pawns can also capture en passant + Still functional without these features though*/ + logFailure("validPawnMove"); + return false; + } + return true; + } + + private boolean validBishopMove(){ + if(srcType==ChessType.BISHOP){ + if(bishopMove()) + return true; + + logFailure("validBishopMove"); + return false; + } + return true; + } + + private boolean validKingMove(){ + if(srcType==ChessType.KING){ + if(src.getRank()-dest.getRank() < 2 && + src.getFile()-dest.getFileNumber() < 2) { + return true; + } + + logFailure("validKingMove"); + //Moves that get the king captured are invalid + return false; + } + return true; + } + + private boolean validQueenMove() { + if(srcType == ChessType.QUEEN){ + if(!(rookMove() ^ bishopMove())) { + logFailure("validQueenMove"); + return false; + } + } + return true; + } + + public ChessDirection[] possibleDirections() { + if(srcType==ChessType.PAWN) return pawnDirections(); + if(srcType==ChessType.ROOK) return rookDirections(); + if(srcType==ChessType.BISHOP) return bishopDirections(); + if(srcType==ChessType.KNIGHT) return knightDirections(); + if(srcType==ChessType.QUEEN) return queenDirections(); + if(srcType==ChessType.KING) return kingDirections(); + else + return new ChessDirection[]{}; + } + + private ChessDirection[] pawnDirections() { + return new ChessDirection[] { + ChessDirection.FORWARD, + ChessDirection.FORWARD_LEFT, + ChessDirection.FORWARD_RIGHT + }; + } + + private ChessDirection[] rookDirections() { + return new ChessDirection[] { + ChessDirection.FORWARD, + ChessDirection.BACK, + ChessDirection.LEFT, + ChessDirection.RIGHT + }; + } + + private ChessDirection[] bishopDirections() { + return new ChessDirection[] { + ChessDirection.FORWARD_LEFT, + ChessDirection.FORWARD_RIGHT, + ChessDirection.BACK_LEFT, + ChessDirection.BACK_RIGHT + }; + } + + private ChessDirection[] knightDirections() { + return new ChessDirection[] { + ChessDirection.KNIGHT_LEFT_FORWARD, + ChessDirection.KNIGHT_LEFT_BACK, + ChessDirection.KNIGHT_RIGHT_FORWARD, + ChessDirection.KNIGHT_RIGHT_BACK, + ChessDirection.KNIGHT_FORWARD_LEFT, + ChessDirection.KNIGHT_FORWARD_RIGHT, + ChessDirection.KNIGHT_BACK_LEFT, + ChessDirection.KNIGHT_BACK_RIGHT + }; + } + + private ChessDirection[] queenDirections() { + return concat(bishopDirections(), rookDirections()); + } + + private ChessDirection[] kingDirections() { + return queenDirections(); + } + + private boolean rookMove() { + return src.getRank() == dest.getRank() ^ + src.getFile() == dest.getFile(); + } + + private boolean bishopMove() { + final int rankDiff = Math.abs(src.getRank()-dest.getRank()); + final int fileDiff = Math.abs(src.getFile()-dest.getFile()); + + return rankDiff == fileDiff; + } + + private void logFailure(String method) { + if(log) + System.err.println(ChessRules.class + ": FAILURE -> "+ + method+" test --- Move: "+ + srcPiece+src+dest); + } + + // TODO where should this be placed? + private static <T> T[] concat(T[] first, T[] second) { + T[] result = Arrays.copyOf(first, first.length + second.length); + System.arraycopy(second, 0, result, first.length, second.length); + return result; + } +} diff --git a/src/chess/ChessState.java b/src/main/java/chess/ChessState.java diff --git a/src/main/java/chess/server/BenCarleChessServer.java b/src/main/java/chess/server/BenCarleChessServer.java @@ -0,0 +1,134 @@ +package chess.server; + +import chess.*; +import chess.utils.ChessType; +import net.sf.json.JSONObject; +import net.sf.json.JSONSerializer; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * + */ +public class BenCarleChessServer { + + private static final String protocol = "http"; + private static final String host = "www.bencarle.com"; + private static final String statusPath = "chess/poll"; + private static final String movePath = "chess/move"; + + private int gameId; + private int teamNo; + private String secret; + + public BenCarleChessServer(int gameId, int teamNo, String secret) { + this.gameId = gameId; + this.teamNo = teamNo; + this.secret = secret; + } + + public StatusResponse poll() { + StatusResponse response = new StatusResponse(); + try { + URL url = new URL(statusUrl()); + HttpURLConnection conn = + (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Accept", "application/json"); + + if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { + throw new RuntimeException( + "Failed : HTTP error code : " + + conn.getResponseCode()); + } + + BufferedReader br = new BufferedReader( + new InputStreamReader((conn.getInputStream()))); + + String output; + JSONObject json = null; + while ((output = br.readLine()) != null) { + json = (JSONObject) JSONSerializer.toJSON(output); + } + + if(json.has("ready")) + response.setReady(json.getBoolean("ready")); + if(json.has("secondsleft")) + response.setSecondsLeft((int)json.getDouble("secondsleft")); + if(json.has("lastmove")) + response.setLastMove(json.getString("lastmove")); + if(json.has("lastmovenumber")) + response.setLastMoveNumber(json.getInt("lastmovenumber")); + + conn.disconnect(); + + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return response; + } + + public MoveResponse push(ChessState state, ChessMove move) { + MoveResponse response = new MoveResponse(); + try { + URL url = new URL(moveUrl(state,move)); + HttpURLConnection conn = + (HttpURLConnection) url.openConnection(); + conn.setDoOutput(true); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Accept", "application/json"); + + if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { + throw new RuntimeException("Failed : HTTP error code : " + + conn.getResponseCode()); + } + + BufferedReader br = new BufferedReader( + new InputStreamReader((conn.getInputStream()))); + + String output; + JSONObject json = null; + while ((output = br.readLine()) != null) { + json = (JSONObject) JSONSerializer.toJSON(output); + } + + if(json.has("result")) + response.setResult(json.getBoolean("result")); + if(json.has("message")) + response.setMessage(json.getString("message")); + + conn.disconnect(); + + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return response; + } + + private String statusUrl() { + return protocol+"://"+host+"/"+statusPath+"/"+ + gameId+"/"+teamNo+"/"+secret+"/"; + } + + private String moveUrl(ChessState state, ChessMove move) { + final ChessCoordinate src = move.getCoordinate(); + final ChessCoordinate dest = src.getCoordinate(state, move); + final ChessType type = + ChessPiece.find(state.get(src)).getType(); + + final String moveString = + type.toString()+src.toString()+dest.toString(); + + return protocol+"://"+host+"/"+movePath+"/"+ + gameId+"/"+teamNo+"/"+secret+"/"+moveString+"/"; + } +} diff --git a/src/main/java/chess/server/MoveResponse.java b/src/main/java/chess/server/MoveResponse.java @@ -0,0 +1,25 @@ +package chess.server; + +/** + * + */ +public class MoveResponse { + private boolean result; + private String message; + + public boolean isResult() { + return result; + } + + public void setResult(boolean result) { + this.result = result; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} +\ No newline at end of file diff --git a/src/main/java/chess/server/StatusResponse.java b/src/main/java/chess/server/StatusResponse.java @@ -0,0 +1,80 @@ +package chess.server; + +import chess.ChessCoordinate; +import chess.utils.ChessType; + +/** + * + */ +public class StatusResponse { + + private boolean ready; + private String lastMove; + private int secondsLeft; + + private long lastMoveNumber; + + public boolean isReady() { + return ready; + } + + public void setReady(boolean ready) { + this.ready = ready; + } + + public long getLastMoveNumber() { + return lastMoveNumber; + } + + public void setLastMoveNumber(long lastMoveNumber) { + this.lastMoveNumber = lastMoveNumber; + } + + public int getSecondsLeft() { + return secondsLeft; + } + + public void setSecondsLeft(int secondsLeft) { + this.secondsLeft = secondsLeft; + } + + public String getLastMove() { + return lastMove; + } + + public void setLastMove(String lastMove) { + this.lastMove = lastMove; + } + + public ChessCoordinate srcLastMove() { + if(ready && lastMove != null && lastMove.length() >= 5) { + char file = lastMove.charAt(1); + int rank = Integer.parseInt(lastMove.substring(2, 3)); + return new ChessCoordinate(file,rank); + } + return null; + } + + public ChessCoordinate destLastMove() { + if(ready && lastMove != null && lastMove.length() >= 5) { + char file = lastMove.charAt(3); + int rank = Integer.parseInt(lastMove.substring(4, 5)); + return new ChessCoordinate(file,rank); + } + return null; + } + + public ChessType typeLastMove() { + if(ready && lastMove != null && lastMove.length() >= 5) { + return ChessType.find(lastMove.substring(0,1)); + } + return null; + } + + public ChessType promotionLastMove() { + if(ready && lastMove != null && lastMove.length() == 6) { + return ChessType.find(lastMove.substring(5,6)); + } + return null; + } +} diff --git a/src/main/java/chess/utils/ChessColor.java b/src/main/java/chess/utils/ChessColor.java @@ -0,0 +1,20 @@ +package chess.utils; + +/** + * Check + */ +public enum ChessColor { + BLACK ("B"), + WHITE ("W"), + NA (" "); + + private String symbol; + + ChessColor(String symbol) { + this.symbol = symbol; + } + + public String toString() { + return symbol; + } +} diff --git a/src/main/java/chess/utils/ChessDirection.java b/src/main/java/chess/utils/ChessDirection.java @@ -0,0 +1,25 @@ +package chess.utils; + +/** + * Check + */ +public enum ChessDirection { + FORWARD, + BACK, + LEFT, + RIGHT, + FORWARD_LEFT, + FORWARD_RIGHT, + BACK_LEFT, + BACK_RIGHT, + + // exclusive knight moves + KNIGHT_LEFT_FORWARD, + KNIGHT_LEFT_BACK, + KNIGHT_RIGHT_FORWARD, + KNIGHT_RIGHT_BACK, + KNIGHT_FORWARD_LEFT, + KNIGHT_FORWARD_RIGHT, + KNIGHT_BACK_LEFT, + KNIGHT_BACK_RIGHT +} diff --git a/src/main/java/chess/utils/ChessType.java b/src/main/java/chess/utils/ChessType.java @@ -0,0 +1,46 @@ +package chess.utils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Check + */ +public enum ChessType { + KING ("K", 1000), + QUEEN ("Q", 9), + BISHOP ("B", 3), + KNIGHT ("N", 3), + ROOK ("R", 5), + PAWN ("P", 1), + NA (" ", 0); + + private int value; + private String symbol; + + ChessType(String symbol, int value) { + this.symbol = symbol; + this.value = value; + } + + public int getValue() { + return value; + } + + public String toString() { + return symbol; + } + + public static ChessType find(String symbol) { + for (ChessType type : VALUES) + if(type.toString().equals(symbol.toUpperCase())) + return type; + + return ChessType.NA; + } + + /* Cached list of all possible values. */ + private static final List<ChessType> VALUES = + Collections.unmodifiableList(Arrays.asList(values())); +} diff --git a/src/test/java/chess/AppTest.java b/src/test/java/chess/AppTest.java @@ -0,0 +1,38 @@ +package chess; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +}