diff --git a/chess/Main.java b/chess/Main.java new file mode 100644 index 0000000..3f7628f --- /dev/null +++ b/chess/Main.java @@ -0,0 +1,18 @@ +package de.vivi.chess; + +import de.vivi.chess.board.Board; +import de.vivi.chess.board.Field; +import de.vivi.chess.game.Game; +import de.vivi.chess.pieces.Piece; + +import java.util.Arrays; + +public class Main { + + public static void main(String[] args) { + Game game = new Game(); + + game.play(); + + } +} diff --git a/chess/board/Board.java b/chess/board/Board.java new file mode 100644 index 0000000..3abd3a5 --- /dev/null +++ b/chess/board/Board.java @@ -0,0 +1,148 @@ +package de.vivi.chess.board; + +import de.vivi.chess.pieces.*; +import de.vivi.chess.game.Game; + +public class Board { + + private static final int SIZE = 8; + private final Piece[/* column */][/* row */] grid; + + public Board() { + grid = new Piece[SIZE][SIZE]; + setup(); + } + + /** + * TODO: implement + *
+ * This method should setup the board like it is common + * for a chess game. Black is in the top two rows, white + * in the bottom two rows. "White queen, white field, black + * queen, black field". + */ + + private void setup() { + grid[0][0] = new Rook(Color.BLACK, Type.ROOK, new Field(0, 0)); + grid[1][0] = new Knight(Color.BLACK, Type.KNIGHT, new Field(1, 0)); + grid[2][0] = new Bishop(Color.BLACK, Type.BISHOP, new Field(2, 0)); + grid[3][0] = new Queen(Color.BLACK, Type.QUEEN, new Field(3, 0)); + grid[4][0] = new King(Color.BLACK, Type.KING, new Field(4, 0)); + grid[5][0] = new Bishop(Color.BLACK, Type.BISHOP, new Field(5, 0)); + grid[6][0] = new Knight(Color.BLACK, Type.KNIGHT, new Field(6, 0)); + grid[7][0] = new Rook(Color.BLACK, Type.ROOK, new Field(7, 0)); + //grid[0][0] = new Rook(Color.BLACK, Type.ROOK); +// grid[1][0] = new Knight(Color.BLACK, Type.KNIGHT); +// grid[2][0] = new Bishop(Color.BLACK, Type.BISHOP); +// grid[3][0] = new Queen(Color.BLACK, Type.QUEEN); +// grid[4][0] = new King(Color.BLACK, Type.KING); +// grid[5][0] = new Bishop(Color.BLACK, Type.BISHOP); +// grid[6][0] = new Knight(Color.BLACK, Type.KNIGHT); +// grid[7][0] = new Rook(Color.BLACK, Type.ROOK); + + for (int i = 0; i < SIZE; i++) { + //grid[i][1] = new Pawn(Color.BLACK, Type.PAWN); + grid[i][1] = new Pawn(Color.BLACK, Type.PAWN, new Field(i, 0)); + } + + grid[0][7] = new Rook(Color.WHITE, Type.ROOK, new Field(0, 7)); + grid[1][7] = new Knight(Color.WHITE, Type.KNIGHT, new Field(1, 7)); + grid[2][7] = new Bishop(Color.WHITE, Type.BISHOP, new Field(2, 7)); + grid[3][7] = new Queen(Color.WHITE, Type.QUEEN, new Field(3, 7)); + grid[4][7] = new King(Color.WHITE, Type.KING, new Field(4, 7)); + grid[5][7] = new Bishop(Color.WHITE, Type.BISHOP, new Field(5, 7)); + grid[6][7] = new Knight(Color.WHITE, Type.KNIGHT, new Field(6, 7)); + grid[7][7] = new Rook(Color.WHITE, Type.ROOK, new Field(7, 7)); + +// grid[0][7] = new Rook(Color.WHITE, Type.ROOK); +// grid[1][7] = new Knight(Color.WHITE, Type.KNIGHT); +// grid[2][7] = new Bishop(Color.WHITE, Type.BISHOP); +// grid[3][7] = new Queen(Color.WHITE, Type.QUEEN); +// grid[4][7] = new King(Color.WHITE, Type.KING); +// grid[5][7] = new Bishop(Color.WHITE, Type.BISHOP); +// grid[6][7] = new Knight(Color.WHITE, Type.KNIGHT); +// grid[7][7] = new Rook(Color.WHITE, Type.ROOK); + + for (int i = 0; i < SIZE; i++) { + //grid[i][6] = new Pawn(Color.WHITE, Type.PAWN); + grid[i][6] = new Pawn(Color.WHITE, Type.PAWN, new Field(i, 6)); + } + } + + /** + * TODO: implement + *
+ * Moves the piece at the from field to the to field. + * Doesn't check for validity, this has to be done somewhere + * else. If a piece is already at the to field, this piece is + * just getting replaced. + */ + public void move(Field from, Field to) { + if (grid[from.getColumn()][from.getRow()] != null) { + grid[to.getColumn()][to.getRow()] = grid[from.getColumn()][from.getRow()]; + grid[to.getColumn()][to.getRow()].setField(to); + grid[from.getColumn()][from.getRow()] = null; + } + } + + /** + * TODO: implement + *
+ * Returns the piece at the given field. If no piece is + * present it just returns null. + */ + public Piece getPiece(Field field) { + return grid[field.getColumn()][field.getRow()]; + } + + public Piece[][] getBoard() { + return grid; + } + + public Piece getPiece(int column, int row) { + return grid[column][row]; + } + + public void setPiece(int column, int row, Piece piece) { + grid[column][row] = piece; + if (piece != null) { + piece.setField(new Field(column, row)); + } + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + boolean whiteField = true; + + result.append(" "); + + for (int column = 0; column < SIZE; column++) { + result.append((char) ('A' + column)).append(' '); + } + + result.append('\n'); + + for (int row = 0; row < SIZE; row++) { + result.append((char) ('1' + (char) row)).append(' '); + + for (int column = 0; column < SIZE; column++) { + Piece piece = grid[column][row]; + + if (piece == null) { + result.append(whiteField ? '\u25a0' : '\u25a1'); + } else { + result.append(piece.getSymbol()); + } + + result.append(' '); + whiteField = !whiteField; + } + + result.append('\n'); + whiteField = !whiteField; + } + + return result.toString(); + } +} diff --git a/chess/board/Field.java b/chess/board/Field.java new file mode 100644 index 0000000..3d5e3a6 --- /dev/null +++ b/chess/board/Field.java @@ -0,0 +1,118 @@ +package de.vivi.chess.board; + +import de.vivi.chess.pieces.Piece; + +/** + * TODO: implement fields and constructor + *
+ * This class contains information to identify a single field + * on the board by its column and row. + */ +public class Field { + + private int column; + private int row; + + public Field(int column, int row) { + this.column = column; + this.row = row; + } + + /** + * TODO: implement + *
+ * Returns a new field that represents the field at the + * column and row indices. + */ + public static Field from(int column, int row) { + return new Field(column, row); + } + + /** + * TODO: implement + *
+ * Returns a new field that represents the field represented + * by the provided string (like "A5", "E7", ...). If the string + * is not valid, because it is garbage (like "hello", "world", ...) + * or the indices are out of bounds (like "K3", "B9", ...") + * it should throw an IllegalArgument exception. + */ + public static Field fromString(String value) throws IllegalArgumentException { + try { + char[] array = value.toCharArray(); + int column_ = 0; + int row_ = 0; + + switch (array[0]) { + case 'A': + column_ = 0; + break; + case 'B': + column_ = 1; + break; + case 'C': + column_ = 2; + break; + case 'D': + column_ = 3; + break; + case 'E': + column_ = 4; + break; + case 'F': + column_ = 5; + break; + case 'G': + column_ = 6; + break; + case 'H': + column_ = 7; + break; + default: + System.out.println("Wrong input"); + } + + switch (array[1]) { + case '1': + row_ = 0; + break; + case '2': + row_ = 1; + break; + case '3': + row_ = 2; + break; + case '4': + row_ = 3; + break; + case '5': + row_ = 4; + break; + case '6': + row_ = 5; + break; + case '7': + row_ = 6; + break; + case '8': + row_ = 7; + break; + default: + System.out.println("Wrong input"); + } + return from(column_, row_); + } catch (RuntimeException e) { + throw new RuntimeException(e); + } + } + + public int getColumn() { + // TODO: implement + return column; + } + + public int getRow() { + // TODO: implement + return row; + } +} diff --git a/chess/game/Game.java b/chess/game/Game.java new file mode 100644 index 0000000..671d3d4 --- /dev/null +++ b/chess/game/Game.java @@ -0,0 +1,330 @@ +package de.vivi.chess.game; + +import de.vivi.chess.board.Board; +import de.vivi.chess.board.Field; +import de.vivi.chess.pieces.Color; +import de.vivi.chess.pieces.King; +import de.vivi.chess.pieces.Piece; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +import static de.vivi.chess.pieces.Color.BLACK; +import static de.vivi.chess.pieces.Color.WHITE; + +public class Game { + + private Board board; + private Color player; + private int count = 0; + private boolean whiteTurn = true; + + /** + * TODO: implement + */ + public Game() { + this.board = new Board(); + player = null; + } + + /** + * TODO: implement + *
+ * Starts the game and should process the game like it is
+ * specified inside example-output.txt.
+ */
+ public void play() {
+ System.out.println("Welcome to Vivi's Chess Game!");
+ System.out.println(board);
+ player = ((count % 2) == 0) ? WHITE : BLACK;
+ System.out.println("It is white's turn: [from:to]");
+
+ Scanner scanner = new Scanner(System.in);
+
+ while (scanner.hasNext()) {
+
+ scanner.useDelimiter(":\\s*");
+ String fromString = scanner.nextLine();
+
+ char[] arrayInRange = fromString.toCharArray();
+
+ if ((arrayInRange[0] == 'A' ||
+ arrayInRange[0] == 'B' ||
+ arrayInRange[0] == 'C' ||
+ arrayInRange[0] == 'D' ||
+ arrayInRange[0] == 'E' ||
+ arrayInRange[0] == 'F' ||
+ arrayInRange[0] == 'G' ||
+ arrayInRange[0] == 'H' ) &&
+ (arrayInRange[1] == '0' ||
+ arrayInRange[1] == '1' ||
+ arrayInRange[1] == '2' ||
+ arrayInRange[1] == '3' ||
+ arrayInRange[1] == '4' ||
+ arrayInRange[1] == '5' ||
+ arrayInRange[1] == '6' ||
+ arrayInRange[1] == '7' ||
+ arrayInRange[1] == '8' )) {
+
+ StringBuilder sb=new StringBuilder(fromString);
+ int l=sb.length();
+
+ if (l == 2) {
+ String toString = scanner.nextLine();
+ Field from = Field.fromString(fromString);
+ Field to = Field.fromString(toString);
+
+ System.out.println("player == WHITE && board.getPiece(from).getColor() == WHITE " +
+ (player == WHITE && board.getPiece(from).getColor() == WHITE));
+
+
+ System.out.println("board.getPiece(from).isValidMove(board, from,to) " +
+ (board.getPiece(from).isValidMove(board, from,to)));
+
+ if (player == WHITE && board.getPiece(from).getColor() == WHITE) {
+ if (board.getPiece(from).isValidMove(board, from,to)) {
+ count++;
+ board.move(from, to);
+ System.out.println("Moving white " + board.getPiece(to).getType()
+ + " from " + fromString + " to " + toString);
+ }
+ System.out.println(board);
+ } else if (player == BLACK && board.getPiece(from).getColor() == BLACK) {
+ if (board.getPiece(from).isValidMove(board, from,to)) {
+ count++;
+ board.move(from, to);
+ System.out.println("Moving black " + board.getPiece(to).getType()
+ + " from " + fromString + " to " + toString);
+ }
+ System.out.println(board);
+ } else {
+ System.err.println("Illegal move, try again: ");
+ }
+
+ player = ((count % 2) == 0) ? WHITE : BLACK;
+ if (player == BLACK) {
+ System.out.println("It is black's turn: [from:to]");
+ } else {
+ System.out.println("It is white's turn: [from:to]");
+ }
+ } else {
+ System.err.println("Illegal move, try again: ");
+ }
+ } else
+ System.err.println("Illegal move, try again: ");
+ }
+ }
+
+ public Board getBoard() {
+ return this.board;
+ }
+
+ public void resetGame() {
+ this.board = new Board();
+ this.whiteTurn = true;
+ }
+
+ public Color getCurrentPlayerColor() {
+ return whiteTurn ? Color.WHITE : Color.BLACK;
+ }
+
+ private Field selectedField;
+
+ public boolean isPieceSelected() {
+ return selectedField != null;
+ }
+
+ public boolean handleSquareSelection(int row, int col) {
+ if (selectedField == null) {
+ Piece selectedPiece = board.getPiece(row, col);
+ if (selectedPiece != null
+ && selectedPiece.getColor() == (whiteTurn ? Color.WHITE : Color.BLACK)) {
+ selectedField = new Field(row, col);
+ return false;
+ }
+ } else {
+ boolean moveMade = makeMove(selectedField, new Field(row, col));
+ selectedField = null;
+ return moveMade;
+ }
+ return false;
+ }
+
+ public boolean makeMove(Field from, Field to) {
+ Piece movingPiece = board.getPiece(Field.from(from.getRow(), from.getColumn()));
+ if (movingPiece == null || movingPiece.getColor() != (whiteTurn ? WHITE : BLACK)) {
+ return false;
+ }
+
+ if (movingPiece.isValidMove(board, movingPiece.getField(), to)) {
+ board.move(from, to);
+ whiteTurn = !whiteTurn;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isInCheck(Color kingColor) {
+ Field kingPosition = findKingPosition(kingColor);
+ for (int row = 0; row < 8; row++) {
+ for (int col = 0; col < 8; col++) {
+ Piece piece = board.getPiece(Field.from(row, col));
+ if (piece != null && piece.getColor() != kingColor) {
+ if (piece.isValidMove(board, piece.getField(), kingPosition)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+
+ }
+
+ private Field findKingPosition(Color color) {
+ for (int row = 0; row < 8; row++) {
+ for (int col = 0; col < 8; col++) {
+ Piece piece = board.getPiece(Field.from(row, col));
+ if (piece instanceof King && piece.getColor() == color) {
+ return new Field(row, col);
+ }
+ }
+ }
+ throw new RuntimeException("King not found, which should never happen.");
+ }
+
+ public boolean isCheckmate(Color kingColor) {
+ if (!isInCheck(kingColor)) {
+ return false;
+ }
+
+ Field kingPosition = findKingPosition(kingColor);
+ King king = (King) board.getPiece(Field.from(kingPosition.getRow(), kingPosition.getColumn()));
+
+ for (int rowOffset = -1; rowOffset <= 1; rowOffset++) {
+ for (int colOffset = -1; colOffset <= 1; colOffset++) {
+ if (rowOffset == 0 && colOffset == 0) {
+ continue;
+ }
+ Field newPosition = new Field(kingPosition.getRow() + rowOffset,
+ kingPosition.getColumn() + colOffset);
+ if (
+ isPositionOnBoard(newPosition) &&
+ king.isValidMove(board, king.getField() , newPosition)
+ && !wouldBeInCheckAfterMove(kingColor, kingPosition, newPosition)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+
+ }
+
+ private boolean isPositionOnBoard(Field field) {
+ return field.getRow() >= 0 && field.getRow() < 8 &&
+ field.getColumn() >= 0 && field.getColumn() < 8;
+
+ }
+
+ private boolean wouldBeInCheckAfterMove(Color kingColor, Field from, Field to) {
+ Piece temp = board.getPiece(Field.from(to.getRow(), to.getColumn()));
+ board.setPiece(to.getRow(), to.getColumn(), board.getPiece(to));
+ board.setPiece(from.getRow(), from.getColumn(), null);
+
+ boolean inCheck = isInCheck(kingColor);
+
+ board.setPiece(from.getRow(), from.getColumn(), board.getPiece(to.getRow(), to.getColumn()));
+ board.setPiece(to.getRow(), to.getColumn(), temp);
+
+ return inCheck;
+ }
+
+ public List
+ * This method needs to be implemented in each piece by
+ * using inheritance. It returns true if the move from the
+ * from field to the to field is considered a valid move
+ * for the type of piece this method is called on. Otherwise
+ * it returns false.
+ */
+ public abstract boolean isValidMove(Board board, Field from, Field to);
+
+
+ public Color getColor() {
+ // TODO: implement
+ return color;
+ }
+
+ public Type getType() {
+ // TODO: implement
+ return type;
+ }
+
+ /**
+ * TODO: implement
+ *
+ * Use unicode symbols for the chess pieces. You can find
+ * them here:
+ * Unicode chess pieces
+ */
+ public char getSymbol() {
+ return symbol;
+ }
+
+ public Field getField() {
+ return field;
+ }
+
+ public void setField(Field field) {
+ this.field = field;
+ }
+}
diff --git a/chess/pieces/Queen.java b/chess/pieces/Queen.java
new file mode 100644
index 0000000..2a0afb0
--- /dev/null
+++ b/chess/pieces/Queen.java
@@ -0,0 +1,64 @@
+package de.vivi.chess.pieces;
+
+import de.vivi.chess.board.Board;
+import de.vivi.chess.board.Field;
+
+public class Queen extends Piece {
+ public Queen(Color color, Type type) {
+ super(color, type);
+ }
+
+ public Queen(Color color, Type type, Field field) {
+ super(color, type, field);
+ }
+
+ @Override
+ public char getSymbol() {
+ if (getColor() == Color.WHITE) {
+ if (getType() == Type.QUEEN) {
+ symbol = '\u265B';
+ }
+
+ } else if (getColor() == Color.BLACK) {
+ if (getType() == Type.QUEEN) {
+ symbol = '\u2655';
+ }
+ }
+ return symbol;
+ }
+
+ @Override
+ public boolean isValidMove(Board board, Field from, Field to) {
+ if (to.equals(from)) {
+ return false;
+ }
+
+ int rowDiff = Math.abs(to.getRow() - from.getRow());
+ int colDiff = Math.abs(to.getColumn() - from.getColumn());
+
+ boolean straightLine = from.getRow() == to.getRow()
+ || from.getColumn() == to.getColumn();
+
+ boolean diagonal = rowDiff == colDiff;
+
+ if (!straightLine && !diagonal) {
+ return false;
+ }
+
+ int rowDirection = Integer.compare(to.getRow(), from.getRow());
+ int colDirection = Integer.compare(to.getColumn(), from.getColumn());
+
+ int currentRow = from.getRow() + rowDirection;
+ int currentCol = from.getColumn() + colDirection;
+ while (currentRow != to.getRow() || currentCol != to.getColumn()) {
+ if (board.getPiece(Field.from(currentCol, currentRow)) != null) {
+ return false;
+ }
+ currentRow += rowDirection;
+ currentCol += colDirection;
+ }
+
+ Piece destinationPiece = board.getPiece(Field.from(to.getColumn(), to.getRow()));
+ return destinationPiece == null || destinationPiece.getColor() != this.getColor();
+ }
+}
diff --git a/chess/pieces/Rook.java b/chess/pieces/Rook.java
new file mode 100644
index 0000000..8fbd800
--- /dev/null
+++ b/chess/pieces/Rook.java
@@ -0,0 +1,68 @@
+package de.vivi.chess.pieces;
+
+import de.vivi.chess.board.Board;
+import de.vivi.chess.board.Field;
+
+public class Rook extends Piece {
+
+ private boolean isValid = false;
+
+ public Rook(Color color, Type type) {
+ super(color, type);
+ }
+
+ public Rook(Color color, Field field) {
+ super(color, field);
+ }
+
+ public Rook(Color color, Type type, Field field) {
+ super(color, type, field);
+ }
+
+ @Override
+ public char getSymbol() {
+ if (getColor() == Color.WHITE) {
+ if (getType() == Type.ROOK) {
+ symbol = '\u265C';
+ }
+
+ } else if (getColor() == Color.BLACK) {
+ if (getType() == Type.ROOK) {
+ symbol = '\u2656';
+ }
+ }
+ return symbol;
+ }
+
+ @Override
+ public boolean isValidMove(Board board, Field from, Field to) {
+ if (from.getRow() == to.getRow()) {
+ int columnStart = Math.min(from.getColumn(), to.getColumn()) + 1;
+ int columnEnd = Math.max(from.getColumn(), to.getColumn());
+ for (int column = columnStart; column < columnEnd; column++) {
+ if (board.getPiece(Field.from(column, from.getRow())) != null) {
+ return false;
+ }
+ }
+ } else if (from.getColumn() == to.getColumn()) {
+ int rowStart = Math.min(from.getRow(), to.getRow()) + 1;
+ int rowEnd = Math.max(from.getRow(), to.getRow());
+ for (int row = rowStart; row < rowEnd; row++) {
+ if (board.getPiece(Field.from(from.getColumn(), row)) != null) {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+
+ Piece destinationPiece = board.getPiece(to);
+ if (destinationPiece == null) {
+ return true;
+ } else if (destinationPiece.getColor() != this.getColor()) {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/chess/pieces/Type.java b/chess/pieces/Type.java
new file mode 100644
index 0000000..1b7f635
--- /dev/null
+++ b/chess/pieces/Type.java
@@ -0,0 +1,23 @@
+package de.vivi.chess.pieces;
+
+/**
+ * TODO: add types
+ *
+ * This enum should contain all chess piece types.
+ * The types are:
+ *
+ * - king (König)
+ * - queen (Dame)
+ * - bishop (Läufer)
+ * - knight (Springer/Pferd)
+ * - rook (Turm)
+ * - pawn (Bauer)
+ */
+public enum Type {
+ KING,
+ QUEEN,
+ BISHOP,
+ KNIGHT,
+ ROOK,
+ PAWN
+}