diff --git a/README.md b/README.md index 8102f91c870..c2d1909c919 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,13 @@ ## 우아한테크코스 코드리뷰 - [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) + +## 기능 구현 목록 + +- 체스 게임판을 초기화 한다. +- 체스 게임판을 출력한다. +- 백의 이동 좌표를 입력 받는다. +- 백 기물을 이동한다. +- 흑 기물 이동 좌표를 입력 받는다. +- 흑 기물을 이동한다. +- 왕이 죽으면 게임이 끝난다. diff --git a/src/main/java/.gitkeep b/src/main/java/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/main/java/chess/Application.java b/src/main/java/chess/Application.java new file mode 100644 index 00000000000..c0e8def9db2 --- /dev/null +++ b/src/main/java/chess/Application.java @@ -0,0 +1,12 @@ +package chess; + +import chess.controller.ChessController; +import chess.util.ErrorUtil; + +public class Application { + + public static void main(String[] args) { + ChessController controller = new ChessController(); + ErrorUtil.computeError(controller::run); + } +} diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java new file mode 100644 index 00000000000..2eff1dbd3f9 --- /dev/null +++ b/src/main/java/chess/controller/ChessController.java @@ -0,0 +1,40 @@ +package chess.controller; + +import chess.domain.board.ChessBoard; +import chess.domain.board.InitBoardGenerator; +import chess.domain.piece.Column; +import chess.domain.piece.Row; +import chess.view.InputView; +import chess.view.OutputVIew; + +import java.util.Map.Entry; + +public class ChessController { + + private ChessBoard chessBoard; + + public void run() { + chessBoard = new ChessBoard(InitBoardGenerator.initChessBoard()); + OutputVIew.printChessBoard(chessBoard); + + while (!chessBoard.isEnd()) { + moveWhite(); + OutputVIew.printChessBoard(chessBoard); + if (chessBoard.isEnd()) break; + moveBlack(); + OutputVIew.printChessBoard(chessBoard); + } + } + + private void moveWhite() { + Entry currentPosition = InputView.choiceWhitePiece(); + Entry destination = InputView.choiceDestination(); + chessBoard.moveWhite(currentPosition, destination); + } + + private void moveBlack() { + Entry currentPosition = InputView.choiceBlackPiece(); + Entry destination = InputView.choiceDestination(); + chessBoard.moveBlack(currentPosition, destination); + } +} diff --git a/src/main/java/chess/domain/board/ChessBoard.java b/src/main/java/chess/domain/board/ChessBoard.java new file mode 100644 index 00000000000..7e2d63df61d --- /dev/null +++ b/src/main/java/chess/domain/board/ChessBoard.java @@ -0,0 +1,87 @@ +package chess.domain.board; + +import chess.domain.piece.Color; +import chess.domain.piece.Column; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import chess.domain.piece.Position; +import chess.domain.piece.Row; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class ChessBoard { + + private List pieces; + + public ChessBoard(List pieces) { + this.pieces = new ArrayList<>(pieces); + } + + public List getPieces() { + return pieces; + } + + public boolean isEnd() { + int kingCount = (int) pieces.stream() + .filter(piece -> piece.getPieceType() == PieceType.KING) + .count(); + return kingCount != 2; + } + + public void moveWhite(Map.Entry currentPosition, Map.Entry destination) { + validateExistPiece(currentPosition, Color.WHITE); + Piece choicedPiece = findChoicedPiece(currentPosition); + validateStop(choicedPiece, destination); + List allPiecesExceptMovingPiece = getAllPiecesExceptMovingPiece(choicedPiece); + choicedPiece.move(destination, allPiecesExceptMovingPiece); + removeDestinationPiece(Color.WHITE, destination); + } + + public void moveBlack(Map.Entry currentPosition, Map.Entry destination) { + validateExistPiece(currentPosition, Color.BLACK); + Piece choicedPiece = findChoicedPiece(currentPosition); + validateStop(choicedPiece, destination); + List allPiecesExceptMovingPiece = getAllPiecesExceptMovingPiece(choicedPiece); + choicedPiece.move(destination, allPiecesExceptMovingPiece); + removeDestinationPiece(Color.BLACK, destination); + } + + private void validateExistPiece(Map.Entry position, Color color) { + boolean isExist = pieces.stream() + .anyMatch(piece -> piece.isExist(position.getKey(), position.getValue()) + && piece.getColor() == color); + if (!isExist) { + throw new IllegalArgumentException("해당 좌표에 움직일 수 있는 기물이 없습니다."); + } + } + + private void validateStop(Piece choicedPiece, Map.Entry destination) { + if (choicedPiece.isExist(destination.getKey(), destination.getValue())) { + throw new IllegalArgumentException("선택된 기물은 반드시 다른 위치로 이동해야 합니다."); + } + } + + private List getAllPiecesExceptMovingPiece(Piece choicedPiece) { + List allPieces = new ArrayList<>(pieces); + allPieces.remove(choicedPiece); + return allPieces; + } + + private Piece findChoicedPiece(Map.Entry currentPosition) { + return pieces.stream() + .filter(piece -> piece.isExist(currentPosition.getKey(), currentPosition.getValue())) + .findFirst().get(); + } + + private void removeDestinationPiece(Color color, Map.Entry destination) { + Optional enemyPieceAtDestination = pieces.stream() + .filter(piece -> piece.isExist(destination.getKey(), destination.getValue()) + && piece.getColor() == color.opposite()) + .findFirst(); + + enemyPieceAtDestination.ifPresent(piece -> pieces.remove(piece)); + } +} diff --git a/src/main/java/chess/domain/board/InitBoardGenerator.java b/src/main/java/chess/domain/board/InitBoardGenerator.java new file mode 100644 index 00000000000..16632357141 --- /dev/null +++ b/src/main/java/chess/domain/board/InitBoardGenerator.java @@ -0,0 +1,61 @@ +package chess.domain.board; + +import chess.domain.piece.Bishop; +import chess.domain.piece.Color; +import chess.domain.piece.Column; +import chess.domain.piece.King; +import chess.domain.piece.Knight; +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import chess.domain.piece.Position; +import chess.domain.piece.Queen; +import chess.domain.piece.Rook; +import chess.domain.piece.Row; + +import java.util.ArrayList; +import java.util.List; + +public class InitBoardGenerator { + + public static List initChessBoard() { + List initPieces = new ArrayList<>(); + + for (Row row : Row.getAllRows()) { + if (row == Row.EIGHT) { + initPieces.add(new Rook(PieceType.ROOK, new Position(row, Column.A), Color.BLACK)); + initPieces.add(new Knight(PieceType.KNIGHT, new Position(row, Column.B), Color.BLACK)); + initPieces.add(new Bishop(PieceType.BISHOP, new Position(row, Column.C), Color.BLACK)); + initPieces.add(new Queen(PieceType.QUEEN, new Position(row, Column.D), Color.BLACK)); + initPieces.add(new King(PieceType.KING, new Position(row, Column.E), Color.BLACK)); + initPieces.add(new Bishop(PieceType.BISHOP, new Position(row, Column.F), Color.BLACK)); + initPieces.add(new Knight(PieceType.KNIGHT, new Position(row, Column.G), Color.BLACK)); + initPieces.add(new Rook(PieceType.ROOK, new Position(row, Column.H), Color.BLACK)); + } + + if (row == Row.SEVEN) { + for (Column column : Column.getAllColumns()) { + initPieces.add(new Pawn(PieceType.PAWN, new Position(row, column), Color.BLACK)); + } + } + + if (row == Row.TWO) { + for (Column column : Column.getAllColumns()) { + initPieces.add(new Pawn(PieceType.PAWN, new Position(row, column), Color.WHITE)); + } + } + + if (row == Row.ONE) { + initPieces.add(new Rook(PieceType.ROOK, new Position(row, Column.A), Color.WHITE)); + initPieces.add(new Knight(PieceType.KNIGHT, new Position(row, Column.B), Color.WHITE)); + initPieces.add(new Bishop(PieceType.BISHOP, new Position(row, Column.C), Color.WHITE)); + initPieces.add(new Queen(PieceType.QUEEN, new Position(row, Column.D), Color.WHITE)); + initPieces.add(new King(PieceType.KING, new Position(row, Column.E), Color.WHITE)); + initPieces.add(new Bishop(PieceType.BISHOP, new Position(row, Column.F), Color.WHITE)); + initPieces.add(new Knight(PieceType.KNIGHT, new Position(row, Column.G), Color.WHITE)); + initPieces.add(new Rook(PieceType.ROOK, new Position(row, Column.H), Color.WHITE)); + } + } + return initPieces; + } +} diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java new file mode 100644 index 00000000000..4c9e38ba929 --- /dev/null +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -0,0 +1,85 @@ +package chess.domain.piece; + +import java.util.List; +import java.util.Map; + +public class Bishop extends Piece { + + public Bishop(PieceType pieceType, Position position, Color color) { + super(pieceType, position, color); + } + + @Override + protected boolean isMoveablePosition(Position destinationPosition) { + Map.Entry columAndRowDistance = this.position.calculateColumnAndRowDistance(destinationPosition); + int xDistance = Math.abs(columAndRowDistance.getKey()); + int yDistance = Math.abs(columAndRowDistance.getValue()); + return xDistance == yDistance; + } + + @Override + protected boolean isMoveablePath(Position destinationPosition, List allPiecesExceptMovingPiece) { + Map.Entry columAndRowDistance = this.position.calculateColumnAndRowDistance(destinationPosition); + int xDistance = columAndRowDistance.getKey(); + int yDistance = columAndRowDistance.getValue(); + + List otherPiecesPositions = allPiecesExceptMovingPiece.stream() + .map(Piece::getPosition) + .toList(); + + Position testMovingPosition = this.position; + if (xDistance > 0) { + if (yDistance > 0) { + for (int i = 0; i < Math.abs(xDistance); i++) { + testMovingPosition = testMovingPosition.moveRightUp(); + if (otherPiecesPositions.contains(testMovingPosition)) { + Position finalTestMovingPosition = testMovingPosition; + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + } + } + if (yDistance < 0) { + for (int i = 0; i < Math.abs(xDistance); i++) { + testMovingPosition = testMovingPosition.moveRightDown(); + if (otherPiecesPositions.contains(testMovingPosition)) { + Position finalTestMovingPosition = testMovingPosition; + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + } + } + } + if (xDistance < 0) { + if (yDistance > 0) { + for (int i = 0; i < Math.abs(xDistance); i++) { + testMovingPosition = testMovingPosition.moveLeftUp(); + if (otherPiecesPositions.contains(testMovingPosition)) { + Position finalTestMovingPosition = testMovingPosition; + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + } + } + if (yDistance < 0) { + for (int i = 0; i < Math.abs(xDistance); i++) { + testMovingPosition = testMovingPosition.moveLeftDown(); + if (otherPiecesPositions.contains(testMovingPosition)) { + Position finalTestMovingPosition = testMovingPosition; + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + } + } + } + return true; + } +} diff --git a/src/main/java/chess/Color.java b/src/main/java/chess/domain/piece/Color.java similarity index 93% rename from src/main/java/chess/Color.java rename to src/main/java/chess/domain/piece/Color.java index 55cd020b681..d599dbed3fb 100644 --- a/src/main/java/chess/Color.java +++ b/src/main/java/chess/domain/piece/Color.java @@ -1,4 +1,4 @@ -package chess; +package chess.domain.piece; public enum Color { diff --git a/src/main/java/chess/Column.java b/src/main/java/chess/domain/piece/Column.java similarity index 78% rename from src/main/java/chess/Column.java rename to src/main/java/chess/domain/piece/Column.java index b64b4dc77a3..792e19684bb 100644 --- a/src/main/java/chess/Column.java +++ b/src/main/java/chess/domain/piece/Column.java @@ -1,4 +1,7 @@ -package chess; +package chess.domain.piece; + +import java.util.Arrays; +import java.util.List; public enum Column { @@ -50,4 +53,12 @@ public Column moveRight(final int step) { throw new IllegalStateException("움직일 수 없는 위치입니다."); } + + public static List getAllColumns() { + return Arrays.stream(values()).toList(); + } + + public int calculateDistance(Column column) { + return column.ordinal() - this.ordinal(); + } } diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java new file mode 100644 index 00000000000..bfdd32a68f2 --- /dev/null +++ b/src/main/java/chess/domain/piece/King.java @@ -0,0 +1,61 @@ +package chess.domain.piece; + +import java.util.List; +import java.util.Map; + +public class King extends Piece { + + public King(PieceType pieceType, Position position, Color color) { + super(pieceType, position, color); + } + + @Override + protected boolean isMoveablePosition(Position destinationPosition) { + if (this.position.moveRight().equals(destinationPosition)) { + return true; + } + if (this.position.moveLeft().equals(destinationPosition)) { + return true; + } + if (this.position.moveRight().equals(destinationPosition)) { + return true; + } + if (this.position.moveDown().equals(destinationPosition)) { + return true; + } + return false; + } + + @Override + protected boolean isMoveablePath(Position destinationPosition, List allPiecesExceptMovingPiece) { + List allPositions = allPiecesExceptMovingPiece.stream() + .map(Piece::getPosition) + .toList(); + + if (allPositions.contains(this.position.moveRight())) { + Position finalTestMovingPosition = this.position.moveRight(); + return allPiecesExceptMovingPiece.stream() + .noneMatch(piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (allPositions.contains(this.position.moveLeft())) { + Position finalTestMovingPosition = this.position.moveLeft(); + return allPiecesExceptMovingPiece.stream() + .noneMatch(piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (allPositions.contains(this.position.moveDown())) { + Position finalTestMovingPosition = this.position.moveDown(); + return allPiecesExceptMovingPiece.stream() + .noneMatch(piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (allPositions.contains(this.position.moveUp())) { + Position finalTestMovingPosition = this.position.moveUp(); + return allPiecesExceptMovingPiece.stream() + .noneMatch(piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + return true; + } +} diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java new file mode 100644 index 00000000000..217806c2b76 --- /dev/null +++ b/src/main/java/chess/domain/piece/Knight.java @@ -0,0 +1,77 @@ +package chess.domain.piece; + +import java.util.List; +import java.util.Map; + +public class Knight extends Piece { + + public Knight(PieceType pieceType, Position position, Color color) { + super(pieceType, position, color); + } + + @Override + protected boolean isMoveablePosition(Position destinationPosition) { + return true; + } + + @Override + protected boolean isMoveablePath(Position destinationPosition, List allPiecesExceptMovingPiece) { + if (destinationPosition.equals(position.moveUp().moveRightUp())) { + Position finalTestMovingPosition = position.moveUp().moveRightUp(); + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (destinationPosition.equals(position.moveUp().moveLeftUp())) { + Position finalTestMovingPosition = position.moveUp().moveLeftUp(); + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (destinationPosition.equals(position.moveDown().moveRightDown())) { + Position finalTestMovingPosition = position.moveDown().moveRightDown(); + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (destinationPosition.equals(position.moveDown().moveLeftDown())) { + Position finalTestMovingPosition = position.moveDown().moveLeftDown(); + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (destinationPosition.equals(position.moveLeft().moveLeftUp())) { + Position finalTestMovingPosition = position.moveLeft().moveLeftUp(); + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (destinationPosition.equals(position.moveLeft().moveLeftDown())) { + Position finalTestMovingPosition = position.moveLeft().moveLeftDown(); + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (destinationPosition.equals(position.moveRight().moveRightUp())) { + Position finalTestMovingPosition = position.moveRight().moveRightUp(); + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (destinationPosition.equals(position.moveRight().moveRightDown())) { + Position finalTestMovingPosition = position.moveRight().moveRightDown(); + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + return false; + } +} diff --git a/src/main/java/chess/Movement.java b/src/main/java/chess/domain/piece/Movement.java similarity index 97% rename from src/main/java/chess/Movement.java rename to src/main/java/chess/domain/piece/Movement.java index e57c6e91bb9..0912c0a5226 100644 --- a/src/main/java/chess/Movement.java +++ b/src/main/java/chess/domain/piece/Movement.java @@ -1,4 +1,4 @@ -package chess; +package chess.domain.piece; public enum Movement { UP(0, 1), diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java new file mode 100644 index 00000000000..efd0106ff44 --- /dev/null +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -0,0 +1,66 @@ +package chess.domain.piece; + +import java.util.List; + +public class Pawn extends Piece { + + public Pawn(PieceType pieceType, Position position, Color color) { + super(pieceType, position, color); + } + + @Override + protected boolean isMoveablePosition(Position destinationPosition) { + if (this.color == Color.BLACK) { + if (this.position.hasSameRow(Row.SEVEN)) return position.moveDown(2).equals(destinationPosition); + if (this.position.moveUp().equals(destinationPosition)) return true; + if (this.position.moveRight().equals(destinationPosition)) return true; + if (this.position.moveLeft().equals(destinationPosition)) return true; + if (this.position.moveRightUp().equals(destinationPosition) + || this.position.moveLeftUp().equals(destinationPosition)) return true; + return false; + } + if (this.position.hasSameRow(Row.TWO)) return position.moveUp(2).equals(destinationPosition); + if (this.position.moveDown().equals(destinationPosition)) return true; + if (this.position.moveRight().equals(destinationPosition)) return true; + if (this.position.moveLeft().equals(destinationPosition)) return true; + if (this.position.moveRightDown().equals(destinationPosition) + || this.position.moveLeftDown().equals(destinationPosition)) return true; + return false; + } + + @Override + protected boolean isMoveablePath(Position destinationPosition, List allPiecesExceptMovingPiece) { + if (this.color == Color.BLACK) { + if (this.position.hasSameRow(Row.SEVEN)) return position.moveDown(2).equals(destinationPosition); + if (this.position.moveUp().equals(destinationPosition)) return true; + if (this.position.moveRight().equals(destinationPosition)) return true; + if (this.position.moveLeft().equals(destinationPosition)) return true; + if (this.position.moveRightUp().equals(destinationPosition)) { + return allPiecesExceptMovingPiece.stream() + .noneMatch(piece -> piece.isExist(destinationPosition) + && piece.getColor() != this.color); + } + if (this.position.moveLeftUp().equals(destinationPosition)) { + return allPiecesExceptMovingPiece.stream() + .noneMatch(piece -> piece.isExist(destinationPosition) + && piece.getColor() != this.color); + } + return false; + } + if (this.position.hasSameRow(Row.TWO)) return position.moveUp(2).equals(destinationPosition); + if (this.position.moveDown().equals(destinationPosition)) return true; + if (this.position.moveRight().equals(destinationPosition)) return true; + if (this.position.moveLeft().equals(destinationPosition)) return true; + if (this.position.moveRightDown().equals(destinationPosition)) { + return allPiecesExceptMovingPiece.stream() + .noneMatch(piece -> piece.isExist(destinationPosition) + && piece.getColor() != this.color); + } + if (this.position.moveLeftDown().equals(destinationPosition)) { + return allPiecesExceptMovingPiece.stream() + .noneMatch(piece -> piece.isExist(destinationPosition) + && piece.getColor() != this.color); + } + return false; + } +} diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java new file mode 100644 index 00000000000..be64e1fb62d --- /dev/null +++ b/src/main/java/chess/domain/piece/Piece.java @@ -0,0 +1,56 @@ +package chess.domain.piece; + +import java.util.List; +import java.util.Map; + +public abstract class Piece { + + protected PieceType pieceType; + protected Position position; + protected Color color; + + public Piece(PieceType pieceType, Position position, Color color) { + this.pieceType = pieceType; + this.position = position; + this.color = color; + } + + public PieceType getPieceType() { + return pieceType; + } + + public Position getPosition() { + return position; + } + + public Color getColor() { + return color; + } + + public boolean isExist(Column column, Row row) { + return position.hasSameRowAndColumn(column, row); + } + + public boolean isExist(Position destination) { + return this.position.equals(destination); + } + + public void move(Map.Entry destination, List allPiecesExceptMovingPiece) { + Position destinationPosition = new Position(destination.getKey(), destination.getValue()); + if (!isMoveablePosition(destinationPosition)) { + throw new IllegalArgumentException("이동할 수 없는 좌표입니다."); + } + if (!isMoveablePath(destinationPosition, allPiecesExceptMovingPiece)) { + throw new IllegalArgumentException("이동할 수 없는 경로입니다."); + } + this.position = destinationPosition; + } + + protected Map.Entry calculateDistance(Position destination) { + return position.calculateColumnAndRowDistance(destination); + } + + protected abstract boolean isMoveablePosition(Position destinationPosition); + + protected abstract boolean isMoveablePath(Position destinationPosition, List allPiecesExceptMovingPiece); +} diff --git a/src/main/java/chess/domain/piece/PieceType.java b/src/main/java/chess/domain/piece/PieceType.java new file mode 100644 index 00000000000..38e8430a344 --- /dev/null +++ b/src/main/java/chess/domain/piece/PieceType.java @@ -0,0 +1,11 @@ +package chess.domain.piece; + +public enum PieceType { + + PAWN, + BISHOP, + KING, + KNIGHT, + QUEEN, + ROOK +} diff --git a/src/main/java/chess/Position.java b/src/main/java/chess/domain/piece/Position.java similarity index 80% rename from src/main/java/chess/Position.java rename to src/main/java/chess/domain/piece/Position.java index 3ebeb0ea185..86ba658c277 100644 --- a/src/main/java/chess/Position.java +++ b/src/main/java/chess/domain/piece/Position.java @@ -1,4 +1,7 @@ -package chess; +package chess.domain.piece; + +import java.util.Map; +import java.util.Objects; public record Position( Column column, @@ -167,4 +170,29 @@ public Position moveHorizontal(final int step) { } return this; } + + public boolean hasSameRowAndColumn(Column column, Row row) { + return this.row == row && this.column == column; + } + + public boolean hasSameRow(Row row) { + return this.row == row; + } + + public Map.Entry calculateColumnAndRowDistance(Position destination) { + int columnDistance = this.column.calculateDistance(destination.column); + int rowDistance = this.row.calculateDistance(destination.row); + return Map.entry(columnDistance, rowDistance); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Position(Column column1, Row row1))) return false; + return row == row1 && column == column1; + } + + @Override + public int hashCode() { + return Objects.hash(column, row); + } } diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java new file mode 100644 index 00000000000..fe678d97d0a --- /dev/null +++ b/src/main/java/chess/domain/piece/Queen.java @@ -0,0 +1,132 @@ +package chess.domain.piece; + +import java.util.List; +import java.util.Map; + +public class Queen extends Piece { + + public Queen(PieceType pieceType, Position position, Color color) { + super(pieceType, position, color); + } + + @Override + protected boolean isMoveablePosition(Position destinationPosition) { + Map.Entry columnAndRowDistance = position.calculateColumnAndRowDistance(destinationPosition); + int xDistance = columnAndRowDistance.getKey(); + int yDistance = columnAndRowDistance.getValue(); + + if (xDistance == 0) { + return yDistance != 0; + } + if (yDistance == 0) { + return true; + } + return Math.abs(xDistance) == Math.abs(yDistance); + } + + @Override + protected boolean isMoveablePath(Position destinationPosition, List allPiecesExceptMovingPiece) { + Map.Entry columAndRowDistance = this.position.calculateColumnAndRowDistance(destinationPosition); + int xDistance = columAndRowDistance.getKey(); + int yDistance = columAndRowDistance.getValue(); + + List otherPiecesPositions = allPiecesExceptMovingPiece.stream() + .map(Piece::getPosition) + .toList(); + + Position testMovingPosition = this.position; + if (xDistance > 0) { + if (yDistance > 0) { + for (int i = 0; i < Math.abs(xDistance); i++) { + testMovingPosition = testMovingPosition.moveRightUp(); + if (otherPiecesPositions.contains(testMovingPosition)) { + Position finalTestMovingPosition = testMovingPosition; + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + } + } + if (yDistance < 0) { + for (int i = 0; i < Math.abs(xDistance); i++) { + testMovingPosition = testMovingPosition.moveRightDown(); + if (otherPiecesPositions.contains(testMovingPosition)) { + Position finalTestMovingPosition = testMovingPosition; + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + } + } + } + if (xDistance < 0) { + if (yDistance > 0) { + for (int i = 0; i < Math.abs(xDistance); i++) { + testMovingPosition = testMovingPosition.moveLeftUp(); + if (otherPiecesPositions.contains(testMovingPosition)) { + Position finalTestMovingPosition = testMovingPosition; + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + } + } + if (yDistance < 0) { + for (int i = 0; i < Math.abs(xDistance); i++) { + testMovingPosition = testMovingPosition.moveLeftDown(); + if (otherPiecesPositions.contains(testMovingPosition)) { + Position finalTestMovingPosition = testMovingPosition; + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + } + } + } + if (xDistance == 0) { + for (Position position : otherPiecesPositions) { + Map.Entry integerIntegerEntry = this.position.calculateColumnAndRowDistance(position); + int otherPieceYDistance = integerIntegerEntry.getValue(); + if (yDistance == otherPieceYDistance) { + Position finalTestMovingPosition = this.position.moveUp(yDistance); + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (yDistance > 0 && otherPieceYDistance > 0) { + return yDistance > otherPieceYDistance; + } + if (yDistance < 0 && otherPieceYDistance < 0) { + return yDistance < otherPieceYDistance; + } + return false; + } + } + if (yDistance == 0) { + for (Position position : otherPiecesPositions) { + Map.Entry integerIntegerEntry = this.position.calculateColumnAndRowDistance(position); + int otherPieceXDistance = integerIntegerEntry.getKey(); + if (xDistance == otherPieceXDistance) { + Position finalTestMovingPosition = this.position.moveUp(yDistance); + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (xDistance > 0 && otherPieceXDistance > 0) { + return xDistance > otherPieceXDistance; + } + if (xDistance < 0 && otherPieceXDistance < 0) { + return xDistance < otherPieceXDistance; + } + return false; + } + } + return false; + } +} diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java new file mode 100644 index 00000000000..922d9ce7ceb --- /dev/null +++ b/src/main/java/chess/domain/piece/Rook.java @@ -0,0 +1,73 @@ +package chess.domain.piece; + +import java.util.List; +import java.util.Map; + +public class Rook extends Piece{ + + public Rook(PieceType pieceType, Position position, Color color) { + super(pieceType, position, color); + } + + @Override + protected boolean isMoveablePosition(Position destinationPosition) { + Map.Entry columnAndRowDistance = position.calculateColumnAndRowDistance(destinationPosition); + int xDistance = columnAndRowDistance.getKey(); + int yDistance = columnAndRowDistance.getValue(); + + return xDistance == 0 || yDistance == 0; + } + + @Override + protected boolean isMoveablePath(Position destinationPosition, List allPiecesExceptMovingPiece) { + Map.Entry columnAndRowDistance = position.calculateColumnAndRowDistance(destinationPosition); + int xDistance = columnAndRowDistance.getKey(); + int yDistance = columnAndRowDistance.getValue(); + + List positions = allPiecesExceptMovingPiece.stream() + .map(Piece::getPosition) + .toList(); + + if (xDistance == 0) { + for (Position position : positions) { + Map.Entry integerIntegerEntry = this.position.calculateColumnAndRowDistance(position); + int otherPieceYDistance = integerIntegerEntry.getValue(); + if (yDistance == otherPieceYDistance) { + Position finalTestMovingPosition = this.position.moveUp(yDistance); + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (yDistance > 0 && otherPieceYDistance > 0) { + return yDistance > otherPieceYDistance; + } + if (yDistance < 0 && otherPieceYDistance < 0) { + return yDistance < otherPieceYDistance; + } + return false; + } + } + if (yDistance == 0) { + for (Position position : positions) { + Map.Entry integerIntegerEntry = this.position.calculateColumnAndRowDistance(position); + int otherPieceXDistance = integerIntegerEntry.getKey(); + if (xDistance == otherPieceXDistance) { + Position finalTestMovingPosition = this.position.moveUp(yDistance); + return allPiecesExceptMovingPiece.stream() + .noneMatch( + piece -> piece.isExist(finalTestMovingPosition) + && piece.getColor() == this.color); + } + if (xDistance > 0 && otherPieceXDistance > 0) { + return xDistance > otherPieceXDistance; + } + if (xDistance < 0 && otherPieceXDistance < 0) { + return xDistance < otherPieceXDistance; + } + return false; + } + } + return false; + } +} diff --git a/src/main/java/chess/Row.java b/src/main/java/chess/domain/piece/Row.java similarity index 78% rename from src/main/java/chess/Row.java rename to src/main/java/chess/domain/piece/Row.java index 126ed048daa..0ab460028fe 100644 --- a/src/main/java/chess/Row.java +++ b/src/main/java/chess/domain/piece/Row.java @@ -1,4 +1,7 @@ -package chess; +package chess.domain.piece; + +import java.util.Arrays; +import java.util.List; public enum Row { @@ -50,4 +53,12 @@ public Row moveDown(final int step) { throw new IllegalStateException("움직일 수 없는 위치입니다."); } + + public static List getAllRows() { + return Arrays.stream(values()).toList(); + } + + public int calculateDistance(Row row) { + return row.ordinal() - this.ordinal(); + } } diff --git a/src/main/java/chess/piece/Bishop.java b/src/main/java/chess/piece/Bishop.java deleted file mode 100644 index b14ab70f981..00000000000 --- a/src/main/java/chess/piece/Bishop.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.piece; - -public class Bishop { - -} diff --git a/src/main/java/chess/piece/King.java b/src/main/java/chess/piece/King.java deleted file mode 100644 index d64210cad13..00000000000 --- a/src/main/java/chess/piece/King.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.piece; - -public class King { - -} diff --git a/src/main/java/chess/piece/Knight.java b/src/main/java/chess/piece/Knight.java deleted file mode 100644 index 2ee7c47a3bc..00000000000 --- a/src/main/java/chess/piece/Knight.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.piece; - -public class Knight { - -} diff --git a/src/main/java/chess/piece/Pawn.java b/src/main/java/chess/piece/Pawn.java deleted file mode 100644 index c8b6cafa51e..00000000000 --- a/src/main/java/chess/piece/Pawn.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.piece; - -public class Pawn { - -} diff --git a/src/main/java/chess/piece/Queen.java b/src/main/java/chess/piece/Queen.java deleted file mode 100644 index 9b547261c4b..00000000000 --- a/src/main/java/chess/piece/Queen.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.piece; - -public class Queen { - -} diff --git a/src/main/java/chess/piece/Rook.java b/src/main/java/chess/piece/Rook.java deleted file mode 100644 index 7ed4d08bf03..00000000000 --- a/src/main/java/chess/piece/Rook.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.piece; - -public class Rook { - -} diff --git a/src/main/java/chess/util/ErrorUtil.java b/src/main/java/chess/util/ErrorUtil.java new file mode 100644 index 00000000000..9fa26e7dc84 --- /dev/null +++ b/src/main/java/chess/util/ErrorUtil.java @@ -0,0 +1,15 @@ +package chess.util; + +import java.util.function.Consumer; + +public class ErrorUtil { + + public static void computeError (Runnable runnable) { + try { + runnable.run(); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + runnable.run(); + } + } +} diff --git a/src/main/java/chess/view/InputView.java b/src/main/java/chess/view/InputView.java new file mode 100644 index 00000000000..bc124d92fc0 --- /dev/null +++ b/src/main/java/chess/view/InputView.java @@ -0,0 +1,86 @@ +package chess.view; + +import chess.domain.piece.Column; +import chess.domain.piece.Row; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.Scanner; + +public class InputView { + + private static Scanner scanner = new Scanner(System.in); + + public static Entry choiceWhitePiece() { + System.out.println("움직일 백 기물을 선택해주세요 예) A,3"); + return choicePiece(); + } + + public static Entry choiceBlackPiece() { + System.out.println("움직일 흑 기물을 선택해주세요 예) A,3"); + return choicePiece(); + } + + public static Entry choiceDestination() { + System.out.println("움직이고 싶은 좌표를 입력해주세요 예) A,3"); + return choicePiece(); + } + + private static Entry choicePiece() { + String[] input = scanner.nextLine().split(","); + return Map.entry(convertToColumn(input[0]), convertToRaw(input[1])); + } + + private static Column convertToColumn(String input) { + if (input.equals("A")) { + return Column.A; + } + if (input.equals("B")) { + return Column.B; + } + if (input.equals("C")) { + return Column.C; + } + if (input.equals("D")) { + return Column.D; + } + if (input.equals("E")) { + return Column.E; + } + if (input.equals("F")) { + return Column.F; + } + if (input.equals("G")) { + return Column.G; + } + throw new IllegalArgumentException("좌표를 잘못 입력하셨습니다."); + } + + private static Row convertToRaw(String input) { + if (input.equals("1")) { + return Row.ONE; + } + if (input.equals("2")) { + return Row.TWO; + } + if (input.equals("3")) { + return Row.THREE; + } + if (input.equals("4")) { + return Row.FOUR; + } + if (input.equals("5")) { + return Row.FIVE; + } + if (input.equals("6")) { + return Row.SIX; + } + if (input.equals("7")) { + return Row.SEVEN; + } + if (input.equals("8")) { + return Row.EIGHT; + } + throw new IllegalArgumentException("좌표를 잘못 입력하셨습니다."); + } +} diff --git a/src/main/java/chess/view/OutputVIew.java b/src/main/java/chess/view/OutputVIew.java new file mode 100644 index 00000000000..d4ed02fbb40 --- /dev/null +++ b/src/main/java/chess/view/OutputVIew.java @@ -0,0 +1,135 @@ +package chess.view; + +import chess.domain.board.ChessBoard; +import chess.domain.piece.Color; +import chess.domain.piece.Column; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import chess.domain.piece.Row; + +import java.util.List; + +public class OutputVIew { + public static void printChessBoard(ChessBoard chessBoard) { + List allPieces = chessBoard.getPieces(); + + System.out.print(" "); + for (Column column : Column.getAllColumns()) { + System.out.printf("%s", convertToString(column)); + } + + System.out.println(); + + for (Row row : Row.getAllRows()) { + System.out.printf("%s", convertToString(row)); + for (Column column : Column.getAllColumns()) { + if (hasPiece(allPieces, row, column)) { + Piece findPiece = getPieceByRowAndColum(allPieces, row, column); + System.out.printf((convertToString(findPiece.getPieceType(), findPiece.getColor()))); + continue; + } + System.out.print("."); + } + System.out.println(); + } + + } + + private static Piece getPieceByRowAndColum(List pieces, Row row, Column column) { + return pieces.stream() + .filter(piece -> piece.isExist(column, row)) + .findFirst().get(); + } + + private static boolean hasPiece(List allPieces, Row row, Column column) { + return allPieces.stream() + .anyMatch(piece -> piece.isExist(column, row)); + } + + private static String convertToString(Row row) { + if (row == Row.EIGHT) { + return "8 "; + } + if (row == Row.SEVEN) { + return "7 "; + } + if (row == Row.SIX) { + return "6 "; + } + if (row == Row.FIVE) { + return "5 "; + } + if (row == Row.FOUR) { + return "4 "; + } + if (row == Row.THREE) { + return "3 "; + } + if (row == Row.TWO) { + return "2 "; + } + return "1 "; + } + + private static String convertToString(PieceType pieceType, Color color) { + if (color == Color.BLACK) { + if (pieceType == PieceType.BISHOP) { + return "B"; + } + if (pieceType == PieceType.ROOK) { + return "R"; + } + if (pieceType == PieceType.KING) { + return "K"; + } + if (pieceType == PieceType.KNIGHT) { + return "N"; + } + if (pieceType == PieceType.QUEEN) { + return "Q"; + } + return "P"; + } + if (pieceType == PieceType.BISHOP) { + return "b"; + } + if (pieceType == PieceType.ROOK) { + return "r"; + } + if (pieceType == PieceType.KING) { + return "k"; + } + if (pieceType == PieceType.KNIGHT) { + return "n"; + } + if (pieceType == PieceType.QUEEN) { + return "q"; + } + return "p"; + } + + private static String convertToString(Column column) { + if (column == Column.A) { + return "A"; + } + if (column == Column.B) { + return "B"; + } + if (column == Column.C) { + return "C"; + } + if (column == Column.D) { + return "D"; + } + if (column == Column.E) { + return "E"; + } + if (column == Column.F) { + return "F"; + } + if (column == Column.G) { + return "G"; + } + return "H "; + } +} diff --git a/src/test/java/chess/ColumnTest.java b/src/test/java/chess/ColumnTest.java index e43523240f7..36924537515 100644 --- a/src/test/java/chess/ColumnTest.java +++ b/src/test/java/chess/ColumnTest.java @@ -1,5 +1,6 @@ package chess; +import chess.domain.piece.Column; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/chess/Fixtures.java b/src/test/java/chess/Fixtures.java index f940ab37137..ad9f6769b6b 100644 --- a/src/test/java/chess/Fixtures.java +++ b/src/test/java/chess/Fixtures.java @@ -1,5 +1,9 @@ package chess; +import chess.domain.piece.Column; +import chess.domain.piece.Position; +import chess.domain.piece.Row; + @SuppressWarnings("unused") public final class Fixtures { diff --git a/src/test/java/chess/PositionTest.java b/src/test/java/chess/PositionTest.java index 3ad7cc64084..6f4952ded49 100644 --- a/src/test/java/chess/PositionTest.java +++ b/src/test/java/chess/PositionTest.java @@ -1,5 +1,7 @@ package chess; +import chess.domain.piece.Movement; +import chess.domain.piece.Position; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/test/java/chess/RowTest.java b/src/test/java/chess/RowTest.java index fcb65485410..9a0375d75de 100644 --- a/src/test/java/chess/RowTest.java +++ b/src/test/java/chess/RowTest.java @@ -1,5 +1,6 @@ package chess; +import chess.domain.piece.Row; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test;