diff --git a/README.md b/README.md index 8102f91c870..9d953f8ed8a 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,56 @@ 체스 미션 저장소 -## 우아한테크코스 코드리뷰 +## 요구 기능 사항 -- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) +### 체스판 + +- 체스판은 8 X 8 크기로 구성한다. + +### 체스 기물 + +- 체스 기물은 킹, 폰, 나이트, 비숍, 룩, 퀸으로 구성한다. + - 킹은 상, 하, 좌, 우, 대각선 1칸 이동한다. + - 폰은 본진 방향을 제와한 상, 하, 대각선 1칸 이동한다. + - 나이트는 상, 하, 좌, 우로 연속 2칸 이동 후, 좌우 1칸 이동한다. + - 비숍은 대각선으로 이동한다. + - 룩은 상하좌우로 이동한다. + - 퀸은 상, 하, 좌, 우, 대각선으로 이동한다. + +- 모든 기물은 도착지에 자신의 기물이 있으면 이동할 수 없다. +- 나이트를 제외한 기물은 이동 경로에 기물이 존재하면 이동할 수 없다. + +## 입출력 요구 사항 + +- 명령어를 입력한다.(이동, 종료) +- 움직일 기물을 선택하라는 메시지와 함께 위치를 입력받는다(예:A1) +- 이동할 칸을 선택하라는 메시지와 함께 위치를 입력받는다(예:A1) + +```text +8 ♖♘♗♕♔♗♘♖ +7 ♙♙♙♙♙♙♙♙ +6 ________ +5 ________ +4 ________ +3 ________ +2 ♟♟♟♟♟♟♟♟ +1 ♜♞♝♛♚♝♞♜ + ABCDEFGH +명령어를 입력하세요(이동:MOVE 종료:Q) +MOVE +움직일 기물을 선택하세요(예:A1) +A2 +이동할 칸을 선택하세요(예:A1) +A4 +8 ♖♘♗♕♔♗♘♖ +7 ♙♙♙♙♙♙♙♙ +6 ________ +5 ________ +4 ♟_______ +3 ________ +2 _♟♟♟♟♟♟♟ +1 ♜♞♝♛♚♝♞♜ + ABCDEFGH +명령어를 입력하세요(이동:MOVE 종료:Q) +Q +``` diff --git a/build.gradle b/build.gradle index 3697236c6fb..ce846f70cc6 100644 --- a/build.gradle +++ b/build.gradle @@ -9,15 +9,15 @@ repositories { } dependencies { - testImplementation platform('org.junit:junit-bom:5.9.1') - testImplementation platform('org.assertj:assertj-bom:3.25.1') + testImplementation platform('org.junit:junit-bom:5.11.4') + testImplementation platform('org.assertj:assertj-bom:3.27.3') testImplementation('org.junit.jupiter:junit-jupiter') testImplementation('org.assertj:assertj-core') } java { toolchain { - languageVersion = JavaLanguageVersion.of(17) + languageVersion = JavaLanguageVersion.of(21) } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c49b..e6441136f3d 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 80187ac3043..b82aa23a4f0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew.bat b/gradlew.bat index 6689b85beec..7101f8e4676 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/src/main/java/chess/Application.java b/src/main/java/chess/Application.java new file mode 100644 index 00000000000..e7c8d95e488 --- /dev/null +++ b/src/main/java/chess/Application.java @@ -0,0 +1,14 @@ +package chess; + +import chess.controller.ChessController; +import chess.view.InputView; +import chess.view.OutputView; + +public class Application { + public static void main(String[] args) { + InputView inputView = new InputView(); + OutputView outputView = new OutputView(); + ChessController chessController = new ChessController(inputView, outputView); + chessController.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..b2dae861397 --- /dev/null +++ b/src/main/java/chess/controller/ChessController.java @@ -0,0 +1,64 @@ +package chess.controller; + +import chess.domain.Board; +import chess.domain.Position; +import chess.domain.piece.Bishop; +import chess.domain.piece.King; +import chess.domain.piece.Knight; +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import chess.domain.piece.Queen; +import chess.domain.piece.Rook; +import chess.view.InputView; +import chess.view.OutputView; +import java.util.ArrayList; +import java.util.List; + +public class ChessController { + + private final InputView inputView; + private final OutputView outputView; + + public ChessController(final InputView inputView, final OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + Board board = initBoard(); + String command = ""; + do { + command = runCommand(board, command); + } while (!command.equals("Q")); + } + + private String runCommand(final Board board, String command) { + try { + outputView.printBoard(board.getPositionToPiece()); + command = inputView.readCommand(); + if (command.equals("MOVE")) { + movePiece(board); + } + } catch (IllegalArgumentException | IllegalStateException e) { + System.out.println(e.getMessage()); + } + return command; + } + + private void movePiece(final Board board) { + Position departure = inputView.readDeparturePosition(); + Position destination = inputView.readDestinationPosition(); + board.move(departure, destination); + } + + private static Board initBoard() { + List initPiece = new ArrayList<>(); + initPiece.addAll(Pawn.initPawn()); + initPiece.addAll(King.initKing()); + initPiece.addAll(Rook.initRook()); + initPiece.addAll(Bishop.initBishop()); + initPiece.addAll(Queen.initQueen()); + initPiece.addAll(Knight.initKnight()); + return new Board(initPiece); + } +} diff --git a/src/main/java/chess/domain/Board.java b/src/main/java/chess/domain/Board.java new file mode 100644 index 00000000000..93f3e8aadd6 --- /dev/null +++ b/src/main/java/chess/domain/Board.java @@ -0,0 +1,59 @@ +package chess.domain; + +import chess.domain.piece.Empty; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Board { + + private final Map positionToPiece; + + public Board(final List pieces) { + this.positionToPiece = init(); + pieces.forEach(piece -> positionToPiece.put(piece.getPosition(), piece)); + } + + private Map init() { + Map positionToPiece = new HashMap<>(); + Arrays.stream(Column.values()) + .flatMap(column -> Arrays.stream(Row.values()) + .map(row -> new Position(row, column))) + .forEach(position -> positionToPiece.put(position, new Empty(position))); + return positionToPiece; + } + + public void move(Position departure, Position destination) { + Piece targetPiece = positionToPiece.get(departure); + Piece moved = targetPiece.move(this, destination); + removePiece(departure); + positionToPiece.put(destination, moved); + } + + public void removePiece(Position position) { + positionToPiece.put(position, new Empty(position)); + } + + public Map getPositionToPiece() { + return this.positionToPiece; + } + + public boolean isEmpty(final Position position) { + return positionToPiece.get(position).isSameType(PieceType.EMPTY); + } + + public boolean isExist(final Position position) { + return !isEmpty(position); + } + + public boolean isAlly(final Position position, final Color color) { + return isExist(position) && positionToPiece.get(position).getColor() == color; + } + + public boolean isEnemy(final Position position, final Color color) { + return !isAlly(position, color); + } +} diff --git a/src/main/java/chess/domain/Color.java b/src/main/java/chess/domain/Color.java new file mode 100644 index 00000000000..1a29e732d1a --- /dev/null +++ b/src/main/java/chess/domain/Color.java @@ -0,0 +1,28 @@ +package chess.domain; + +public enum Color { + + BLACK, + WHITE, + EMPTY; + + public boolean isWhite() { + return this == WHITE; + } + + public boolean isBlack() { + return this == BLACK; + } + + public boolean isEmpty() { + return this == EMPTY; + } + + public Color opposite() { + return switch (this) { + case BLACK -> WHITE; + case WHITE -> BLACK; + default -> EMPTY; + }; + } +} diff --git a/src/main/java/chess/domain/Column.java b/src/main/java/chess/domain/Column.java new file mode 100644 index 00000000000..1fa80a0f773 --- /dev/null +++ b/src/main/java/chess/domain/Column.java @@ -0,0 +1,73 @@ +package chess.domain; + +import java.util.Arrays; + +public enum Column { + + A, + B, + C, + D, + E, + F, + G, + H; + + public static Column parseChar(final char target) { + return Arrays.stream(Column.values()) + .filter(column -> column.name().charAt(0) == target) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("맞지 않은 문자입니다")); + } + + public boolean isFarLeft() { + return ordinal() == 0; + } + + public boolean isFarRight() { + return ordinal() + 1 == values().length; + } + + public boolean canMoveLeft(final int step) { + return ordinal() - step >= 0; + } + + public Column move(final int step) { + if (step < 0) { + return moveLeft(step); + } + return moveRight(step); + } + + public Column moveLeft() { + return moveLeft(1); + } + + public Column moveLeft(final int step) { + if (canMoveLeft(step)) { + return values()[ordinal() - step]; + } + + throw new IllegalStateException("움직일 수 없는 위치입니다."); + } + + public boolean canMoveRight(final int step) { + return ordinal() + step < values().length; + } + + public Column moveRight() { + return moveRight(1); + } + + public Column moveRight(final int step) { + if (canMoveRight(step)) { + return values()[ordinal() + step]; + } + + throw new IllegalStateException("움직일 수 없는 위치입니다."); + } + + public int calculateColumnDistance(Column column) { + return ordinal() - column.ordinal(); + } +} diff --git a/src/main/java/chess/domain/Direction.java b/src/main/java/chess/domain/Direction.java new file mode 100644 index 00000000000..ef7e3d169af --- /dev/null +++ b/src/main/java/chess/domain/Direction.java @@ -0,0 +1,100 @@ +package chess.domain; + +import java.util.ArrayList; +import java.util.List; + +public class Direction { + + public static List calculateDirection(Position departure, Position destination) { + int columnDistance = destination.column().calculateColumnDistance(departure.column()); + int rowDistance = destination.row().calculateRowDistance(departure.row()); + if (Math.abs(columnDistance) == 0 || Math.abs(rowDistance) == 0) { + return findDirectDirection(columnDistance, rowDistance); + } + + if (Math.abs(columnDistance) == Math.abs(rowDistance)) { + return findDiagonalDirection(columnDistance, rowDistance); + } + return findCurveDirection(columnDistance, rowDistance); + } + + private static List findCurveDirection(final int columnDistance, final int rowDistance) { + int columnCount = Math.abs(columnDistance); + int rowCount = Math.abs(rowDistance); + int totalCount = Math.min(columnCount, rowCount); + + if (Math.max(columnCount, rowCount) != 2 || Math.min(columnCount, rowCount) != 1) { + throw new IllegalArgumentException("맞지 않은 이동입니다"); + } + + if (columnCount > rowCount) { + if (columnDistance > 0 && rowDistance > 0) { + return repeat(totalCount, Movement.DOWN_DOWN_RIGHT); + } + if (columnDistance > 0 && rowDistance < 0) { + return repeat(totalCount, Movement.UP_UP_RIGHT); + } + if (columnDistance < 0 && rowDistance > 0) { + return repeat(totalCount, Movement.UP_UP_LEFT); + } + if (columnDistance < 0 && rowDistance < 0) { + return repeat(totalCount, Movement.DOWN_DOWN_LEFT); + } + } + if (columnCount < rowCount) { + if (columnDistance > 0 && rowDistance > 0) { + return repeat(totalCount, Movement.RIGHT_RIGHT_DOWN); + } + if (columnDistance > 0 && rowDistance < 0) { + return repeat(totalCount, Movement.RIGHT_RIGHT_UP); + } + if (columnDistance < 0 && rowDistance > 0) { + return repeat(totalCount, Movement.LEFT_LEFT_UP); + } + if (columnDistance < 0 && rowDistance < 0) { + return repeat(totalCount, Movement.LEFT_LEFT_DOWN); + } + } + throw new IllegalArgumentException("방향 탐지 실패"); + } + + private static List findDiagonalDirection(final int columnDistance, final int rowDistance) { + if (columnDistance > 0 && rowDistance > 0) { + return repeat(Math.abs(rowDistance), Movement.RIGHT_DOWN); + } + if (columnDistance > 0 && rowDistance < 0) { + return repeat(Math.abs(rowDistance), Movement.RIGHT_UP); + } + if (columnDistance < 0 && rowDistance > 0) { + return repeat(Math.abs(columnDistance), Movement.LEFT_DOWN); + } + if (columnDistance < 0 && rowDistance < 0) { + return repeat(Math.abs(columnDistance), Movement.LEFT_UP); + } + throw new IllegalArgumentException("방향 탐지 실패"); + } + + private static List findDirectDirection(final int columnDistance, final int rowDistance) { + if (columnDistance == 0 && rowDistance > 0) { + return repeat(Math.abs(rowDistance), Movement.DOWN); + } + if (columnDistance == 0 && rowDistance < 0) { + return repeat(Math.abs(rowDistance), Movement.UP); + } + if (columnDistance > 0 && rowDistance == 0) { + return repeat(Math.abs(columnDistance), Movement.RIGHT); + } + if (columnDistance < 0 && rowDistance == 0) { + return repeat(Math.abs(columnDistance), Movement.LEFT); + } + throw new IllegalArgumentException("방향 탐지 실패"); + } + + private static List repeat(int count, Movement movement) { + List movements = new ArrayList<>(); + for (int i = 0; i < count; i++) { + movements.add(movement); + } + return movements; + } +} diff --git a/src/main/java/chess/domain/Movement.java b/src/main/java/chess/domain/Movement.java new file mode 100644 index 00000000000..f911bbe6de0 --- /dev/null +++ b/src/main/java/chess/domain/Movement.java @@ -0,0 +1,51 @@ +package chess.domain; + +public enum Movement { + UP(0, 1), + UP_UP(UP.x * 2, UP.y * 2), + DOWN(0, -1), + DOWN_DOWN(DOWN.x * 2, DOWN.y * 2), + LEFT(-1, 0), + RIGHT(1, 0), + LEFT_UP(LEFT.x, UP.y), + RIGHT_UP(RIGHT.x, UP.y), + LEFT_DOWN(LEFT.x, DOWN.y), + RIGHT_DOWN(RIGHT.x, DOWN.y), + UP_UP_LEFT(LEFT_DOWN.x, UP_UP.y), + UP_UP_RIGHT(RIGHT_DOWN.x, UP_UP.y), + LEFT_LEFT_UP(LEFT.x * 2, UP.y), + LEFT_LEFT_DOWN(LEFT.x * 2, DOWN.y), + RIGHT_RIGHT_UP(RIGHT.x * 2, UP.y), + RIGHT_RIGHT_DOWN(RIGHT.x * 2, DOWN.y), + DOWN_DOWN_LEFT(LEFT_DOWN.x, DOWN_DOWN.y), + DOWN_DOWN_RIGHT(RIGHT_DOWN.x, DOWN_DOWN.y), + ; + + private final int x; + private final int y; + + Movement(final int x, final int y) { + this.x = x; + this.y = y; + } + + public int x() { + return x; + } + + public int y() { + return y; + } + + public boolean isVertical() { + return x == 0 && y != 0; + } + + public boolean isHorizontal() { + return x != 0 && y == 0; + } + + public boolean isDiagonal() { + return x != 0 && y != 0 && Math.abs(x) == Math.abs(y); + } +} diff --git a/src/main/java/chess/domain/Position.java b/src/main/java/chess/domain/Position.java new file mode 100644 index 00000000000..1097b5a6a35 --- /dev/null +++ b/src/main/java/chess/domain/Position.java @@ -0,0 +1,170 @@ +package chess.domain; + +public record Position( + Column column, + Row row +) { + public Position(final Row row, final Column column) { + this(column, row); + } + + public boolean canMoveUp() { + return row.canMoveUp(1); + } + + public boolean canMoveUp(final int step) { + return row.canMoveUp(step); + } + + public Position moveUp() { + return moveUp(1); + } + + public Position moveUp(final int step) { + return new Position(row.moveUp(step), column); + } + + public boolean canMoveDown() { + return canMoveDown(1); + } + + public boolean canMoveDown(final int step) { + return row.canMoveDown(step); + } + + public Position moveDown() { + return moveDown(1); + } + + public Position moveDown(final int step) { + return new Position(row.moveDown(step), column); + } + + public boolean canMoveLeft() { + return canMoveLeft(1); + } + + public boolean canMoveLeft(final int step) { + return column.canMoveLeft(step); + } + + public Position moveLeft() { + return moveLeft(1); + } + + public Position moveLeft(final int step) { + return new Position(row, column.moveLeft(step)); + } + + public boolean canMoveRight() { + return canMoveRight(1); + } + + public boolean canMoveRight(final int step) { + return column.canMoveRight(step); + } + + public Position moveRight() { + return moveRight(1); + } + + public Position moveRight(final int step) { + return new Position(row, column.moveRight(step)); + } + + public boolean canMoveLeftUp() { + return canMoveLeft() && canMoveUp(); + } + + public Position moveLeftUp() { + return moveLeft().moveUp(); + } + + public boolean canMoveLeftDown() { + return canMoveLeft() && canMoveDown(); + } + + public Position moveLeftDown() { + return moveLeft().moveDown(); + } + + public boolean canMoveRightUp() { + return canMoveUp() && canMoveRight(); + } + + public Position moveRightUp() { + return moveRight().moveUp(); + } + + public boolean canMoveRightDown() { + return canMoveRight() && canMoveDown(); + } + + public Position moveRightDown() { + return moveRight().moveDown(); + } + + public boolean isTop() { + return row.isTop(); + } + + public boolean isBottom() { + return row.isBottom(); + } + + public boolean isFarLeft() { + return column.isFarLeft(); + } + + public boolean isFarRight() { + return column.isFarRight(); + } + + public boolean canMove(final Movement movement) { + return canMoveVertical(movement.y()) && canMoveHorizontal(movement.x()); + } + + public boolean canMoveVertical(final int step) { + if (step > 0) { + return canMoveUp(step); + } + if (step < 0) { + return canMoveDown(-step); + } + return true; + } + + public boolean canMoveHorizontal(final int step) { + if (step > 0) { + return canMoveRight(step); + } + if (step < 0) { + return canMoveLeft(-step); + } + return true; + } + + public Position move(final Movement movement) { + return moveVertical(movement.y()).moveHorizontal(movement.x()); + } + + public Position moveVertical(final int step) { + if (step > 0) { + return moveUp(step); + } + if (step < 0) { + return moveDown(-step); + } + return this; + } + + public Position moveHorizontal(final int step) { + if (step > 0) { + return moveRight(step); + } + if (step < 0) { + return moveLeft(-step); + } + return this; + } +} diff --git a/src/main/java/chess/domain/Row.java b/src/main/java/chess/domain/Row.java new file mode 100644 index 00000000000..88f90d4cb89 --- /dev/null +++ b/src/main/java/chess/domain/Row.java @@ -0,0 +1,82 @@ +package chess.domain; + +import java.util.Arrays; + +public enum Row { + + EIGHT('8'), + SEVEN('7'), + SIX('6'), + FIVE('5'), + FOUR('4'), + THREE('3'), + TWO('2'), + ONE('1'); + + private final char number; + + Row(final char number) { + this.number = number; + } + + public static Row parseChar(final char target) { + return Arrays.stream(Row.values()) + .filter(row -> row.getNumber() == target) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("맞지 않은 문자입니다")); + } + + public boolean isTop() { + return ordinal() == 0; + } + + public boolean isBottom() { + return ordinal() + 1 == values().length; + } + + public boolean canMoveUp(final int step) { + return ordinal() - step >= 0; + } + + public Row move(final int step) { + if (step < 0) { + return moveDown(step); + } + return moveUp(step); + } + + public Row moveUp() { + return moveUp(1); + } + + public Row moveUp(final int step) { + if (canMoveUp(step)) { + return values()[ordinal() - step]; + } + throw new IllegalStateException("움직일 수 없는 위치입니다."); + } + + public boolean canMoveDown(final int step) { + return ordinal() + step < values().length; + } + + public Row moveDown() { + return moveDown(1); + } + + public Row moveDown(final int step) { + if (canMoveDown(step)) { + return values()[ordinal() + step]; + } + + throw new IllegalStateException("움직일 수 없는 위치입니다."); + } + + public int calculateRowDistance(Row row) { + return ordinal() - row.ordinal(); + } + + public char getNumber() { + return number; + } +} 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..e0a4e2197fa --- /dev/null +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -0,0 +1,61 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Column; +import chess.domain.Direction; +import chess.domain.Movement; +import chess.domain.Position; +import chess.domain.Row; +import java.util.ArrayList; +import java.util.List; + +public class Bishop extends Piece { + + private static final List INIT_BLACK_POSITION = List.of(new Position(Row.ONE, Column.C), + new Position(Row.ONE, Column.F)); + private static final List INIT_WHITE_POSITION = List.of(new Position(Row.EIGHT, Column.C), + new Position(Row.EIGHT, Column.F)); + + public Bishop(final Position position, final Color color) { + super(position, color, PieceType.BISHOP); + } + + public static List initBishop() { + List bishops = new ArrayList<>(); + INIT_BLACK_POSITION.forEach(position -> bishops.add(new Bishop(position, Color.BLACK))); + INIT_WHITE_POSITION.forEach(position -> bishops.add(new Bishop(position, Color.WHITE))); + return bishops; + } + + @Override + public Piece move(final Board board, final Position destination) { + + List movements = Direction.calculateDirection(position, destination); + + if (!movements.stream().allMatch(Movement::isDiagonal)) { + throw new IllegalArgumentException("규칙에 맞지 않은 움직임 입니다"); + } + + if (position.equals(destination)) { + throw new IllegalArgumentException("같은 위치입니다"); + } + + if (board.isAlly(destination, color)) { + throw new IllegalArgumentException("아군 기물이 존재합니다"); + } + + Position currentPosition = position; + Movement lastMovement = movements.removeLast(); + for (Movement current : movements) { + if (!currentPosition.canMove(current)) { + throw new IllegalArgumentException("이동할 수 없습니다"); + } + currentPosition = currentPosition.move(current); + if (board.isExist(currentPosition)) { + throw new IllegalArgumentException("기물이 존재합니다"); + } + } + return new Bishop(currentPosition.move(lastMovement), color); + } +} diff --git a/src/main/java/chess/domain/piece/Empty.java b/src/main/java/chess/domain/piece/Empty.java new file mode 100644 index 00000000000..479919bcbd6 --- /dev/null +++ b/src/main/java/chess/domain/piece/Empty.java @@ -0,0 +1,16 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Position; + +public class Empty extends Piece { + public Empty(final Position position) { + super(position, Color.EMPTY, PieceType.EMPTY); + } + + @Override + public Piece move(final Board board, final Position destination) { + throw new IllegalArgumentException("비어 있는 칸입니다"); + } +} 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..59b0a868fcc --- /dev/null +++ b/src/main/java/chess/domain/piece/King.java @@ -0,0 +1,34 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Column; +import chess.domain.Movement; +import chess.domain.Position; +import chess.domain.Row; +import java.util.List; + +public class King extends Piece { + + private static final List MOVEMENTS = List.of(Movement.UP, Movement.DOWN, Movement.LEFT_DOWN, + Movement.LEFT_UP, Movement.RIGHT_DOWN, Movement.RIGHT_UP); + private static final Position INIT_BLACK_POSITION = new Position(Row.ONE, Column.E); + private static final Position INIT_WHITE_POSITION = new Position(Row.EIGHT, Column.E); + + public King(final Position position, final Color color) { + super(position, color, PieceType.KING); + } + + public static List initKing() { + return List.of(new King(INIT_BLACK_POSITION, Color.BLACK), new King(INIT_WHITE_POSITION, Color.WHITE)); + } + + @Override + public Piece move(final Board board, final Position destination) { + Movement movement = findMovement(destination, MOVEMENTS); + if (board.isAlly(destination, color)) { + throw new IllegalArgumentException("아군 기물이 존재합니다"); + } + return new King(position.move(movement), color); + } +} 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..5f5d547af9d --- /dev/null +++ b/src/main/java/chess/domain/piece/Knight.java @@ -0,0 +1,50 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Column; +import chess.domain.Direction; +import chess.domain.Movement; +import chess.domain.Position; +import chess.domain.Row; +import java.util.ArrayList; +import java.util.List; + +public class Knight extends Piece { + + private static final List INIT_BLACK_POSITION = List.of(new Position(Row.ONE, Column.B), + new Position(Row.ONE, Column.G)); + private static final List INIT_WHITE_POSITION = List.of(new Position(Row.EIGHT, Column.B), + new Position(Row.EIGHT, Column.G)); + + public Knight(final Position position, final Color color) { + super(position, color, PieceType.KNIGHT); + } + + public static List initKnight() { + List knights = new ArrayList<>(); + INIT_BLACK_POSITION.forEach(position -> knights.add(new Knight(position, Color.BLACK))); + INIT_WHITE_POSITION.forEach(position -> knights.add(new Knight(position, Color.WHITE))); + return knights; + } + + @Override + public Piece move(final Board board, final Position destination) { + + List movements = Direction.calculateDirection(position, destination); + + if (movements.size() > 1) { + throw new IllegalArgumentException("규칙에 맞지 않은 움직임 입니다"); + } + + if (position.equals(destination)) { + throw new IllegalArgumentException("같은 위치입니다"); + } + + if (board.isAlly(destination, color)) { + throw new IllegalArgumentException("아군 기물이 존재합니다"); + } + + return new Knight(position.move(movements.getFirst()), color); + } +} 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..7642dea35dd --- /dev/null +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -0,0 +1,60 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Column; +import chess.domain.Movement; +import chess.domain.Position; +import chess.domain.Row; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Pawn extends Piece { + + private static final List MOVEMENTS = List.of(Movement.UP, Movement.UP_UP, + Movement.DOWN, Movement.DOWN_DOWN, + Movement.LEFT_DOWN, Movement.LEFT_UP, + Movement.RIGHT_DOWN, Movement.RIGHT_UP); + private static final Row INIT_BLACK_ROW = Row.TWO; + private static final Row INIT_WHITE_ROW = Row.SEVEN; + + private final boolean firstNotMoved; + + public Pawn(final Position position, final Color color, boolean firstNotMoved) { + super(position, color, PieceType.PAWN); + this.firstNotMoved = firstNotMoved; + } + + public static List initPawn() { + List initPawn = new ArrayList<>(); + Arrays.stream(Column.values()) + .forEach(column -> initPawn.add(new Pawn(new Position(INIT_BLACK_ROW, column), Color.BLACK, true))); + Arrays.stream(Column.values()) + .forEach(column -> initPawn.add(new Pawn(new Position(INIT_WHITE_ROW, column), Color.WHITE, true))); + return initPawn; + } + + @Override + public Piece move(final Board board, final Position destination) { + Movement movement = findMovement(destination, MOVEMENTS); + + if (movement.isDiagonal() && !board.isEnemy(destination, color)) { + throw new IllegalArgumentException("폰은 적을 잡을 때에만 대각선으로 이동 가능합니다"); + } + + if (movement.isVertical() && board.isExist(destination)) { + throw new IllegalArgumentException("기물이 존재합니다"); + } + + if (!firstNotMoved && (movement == Movement.UP_UP || movement == Movement.DOWN_DOWN)) { + throw new IllegalArgumentException("폰은 처음 이동일 경우에만 2칸 이동 가능합니다"); + } + + if (((movement == Movement.UP_UP || movement == Movement.UP) && color.isWhite()) + || ((movement == Movement.DOWN_DOWN || movement == Movement.DOWN) && color.isBlack())) { + throw new IllegalArgumentException("폰은 본진을 향해 움직일 수 없습니다"); + } + return new Pawn(position.move(movement), color, 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..c30728e17be --- /dev/null +++ b/src/main/java/chess/domain/piece/Piece.java @@ -0,0 +1,45 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Movement; +import chess.domain.Position; +import java.util.List; + +public abstract class Piece { + + protected final Position position; + protected final Color color; + protected final PieceType pieceType; + + public Piece(final Position position, final Color color, final PieceType pieceType) { + this.position = position; + this.color = color; + this.pieceType = pieceType; + } + + public Position getPosition() { + return this.position; + } + + public abstract Piece move(final Board board, final Position destination); + + protected Movement findMovement(final Position destination, final List movements) { + return movements.stream() + .filter(movement -> position.canMove(movement) && position.move(movement).equals(destination)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("규칙에 맞지 않는 이동입니다")); + } + + public boolean isSameType(PieceType pieceType) { + return this.pieceType == pieceType; + } + + public Color getColor() { + return color; + } + + public PieceType getPieceType() { + return pieceType; + } +} 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..45f8de6df38 --- /dev/null +++ b/src/main/java/chess/domain/piece/PieceType.java @@ -0,0 +1,28 @@ +package chess.domain.piece; + +import chess.domain.Color; + +public enum PieceType { + KING("♔", "♚"), + QUEEN("♕", "♛"), + ROOK("♖", "♜"), + KNIGHT("♘", "♞"), + PAWN("♙", "♟"), + BISHOP("♗", "♝"), + EMPTY("_", "_"); + + private final String white; + private final String black; + + PieceType(final String white, final String black) { + this.white = white; + this.black = black; + } + + public String getTeam(Color color) { + if (color.isWhite()) { + return white; + } + return black; + } +} 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..53346890b6a --- /dev/null +++ b/src/main/java/chess/domain/piece/Queen.java @@ -0,0 +1,51 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Column; +import chess.domain.Direction; +import chess.domain.Movement; +import chess.domain.Position; +import chess.domain.Row; +import java.util.List; + +public class Queen extends Piece { + + private static final Position INIT_BLACK_POSITION = new Position(Row.ONE, Column.D); + private static final Position INIT_WHITE_POSITION = new Position(Row.EIGHT, Column.D); + + public Queen(final Position position, final Color color) { + super(position, color, PieceType.QUEEN); + } + + public static List initQueen() { + return List.of(new Queen(INIT_BLACK_POSITION, Color.BLACK), new Queen(INIT_WHITE_POSITION, Color.WHITE)); + } + + @Override + public Piece move(final Board board, final Position destination) { + + List movements = Direction.calculateDirection(position, destination); + + if (position.equals(destination)) { + throw new IllegalArgumentException("같은 위치입니다"); + } + + if (board.isAlly(destination, color)) { + throw new IllegalArgumentException("아군 기물이 존재합니다"); + } + + Position currentPosition = position; + Movement lastMovement = movements.removeLast(); + for (Movement current : movements) { + if (!currentPosition.canMove(current)) { + throw new IllegalArgumentException("이동할 수 없습니다"); + } + currentPosition = currentPosition.move(current); + if (board.isExist(currentPosition)) { + throw new IllegalArgumentException("기물이 존재합니다"); + } + } + return new Queen(currentPosition.move(lastMovement), color); + } +} 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..a09c17ba280 --- /dev/null +++ b/src/main/java/chess/domain/piece/Rook.java @@ -0,0 +1,61 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Column; +import chess.domain.Direction; +import chess.domain.Movement; +import chess.domain.Position; +import chess.domain.Row; +import java.util.ArrayList; +import java.util.List; + +public class Rook extends Piece { + + private static final List INIT_BLACK_POSITION = List.of(new Position(Row.ONE, Column.A), + new Position(Row.ONE, Column.H)); + private static final List INIT_WHITE_POSITION = List.of(new Position(Row.EIGHT, Column.A), + new Position(Row.EIGHT, Column.H)); + + public Rook(final Position position, final Color color) { + super(position, color, PieceType.ROOK); + } + + public static List initRook() { + List rooks = new ArrayList<>(); + INIT_BLACK_POSITION.forEach(position -> rooks.add(new Rook(position, Color.BLACK))); + INIT_WHITE_POSITION.forEach(position -> rooks.add(new Rook(position, Color.WHITE))); + return rooks; + } + + @Override + public Piece move(final Board board, final Position destination) { + + List movements = Direction.calculateDirection(position, destination); + + if (!movements.stream().allMatch(movement -> movement.isVertical() || movement.isHorizontal())) { + throw new IllegalArgumentException("규칙에 맞지 않은 움직임 입니다"); + } + + if (position.equals(destination)) { + throw new IllegalArgumentException("같은 위치입니다"); + } + + if (board.isAlly(destination, color)) { + throw new IllegalArgumentException("아군 기물이 존재합니다"); + } + + Position currentPosition = position; + Movement lastMovement = movements.removeLast(); + for (Movement current : movements) { + if (!currentPosition.canMove(current)) { + throw new IllegalArgumentException("이동할 수 없습니다"); + } + currentPosition = currentPosition.move(current); + if (board.isExist(currentPosition)) { + throw new IllegalArgumentException("기물이 존재합니다"); + } + } + return new Rook(currentPosition.move(lastMovement), color); + } +} diff --git a/src/main/java/chess/view/InputView.java b/src/main/java/chess/view/InputView.java new file mode 100644 index 00000000000..d1c64107147 --- /dev/null +++ b/src/main/java/chess/view/InputView.java @@ -0,0 +1,52 @@ +package chess.view; + +import chess.domain.Column; +import chess.domain.Position; +import chess.domain.Row; +import java.util.Scanner; + +public class InputView { + + private final Scanner scanner; + + public InputView() { + this.scanner = new Scanner(System.in); + } + + public String readCommand() { + System.out.println("명령어를 입력하세요(이동:MOVE 종료:Q)"); + String input = scanner.nextLine(); + validateEmptyInput(input); + return input; + } + + private static void validateEmptyInput(final String input) { + if (input == null || input.isBlank()) { + throw new IllegalArgumentException("빈 입력입니다"); + } + } + + public Position readDeparturePosition() { + System.out.println("움직일 기물을 선택하세요(예:A1)"); + return inputPosition(); + } + + public Position readDestinationPosition() { + System.out.println("이동할 칸을 선택하세요(예:A1)"); + return inputPosition(); + } + + private Position inputPosition() { + String input = scanner.nextLine().strip().toUpperCase(); + validateEmptyInput(input); + if (input.length() != 2) { + throw new IllegalArgumentException("2글자 형식으로 입력하세요"); + } + char column = input.charAt(0); + char row = input.charAt(1); + if ('A' > column || column > 'Z' || row < '1' || row > '8') { + throw new IllegalArgumentException("맞지 않은 위치 형식입니다"); + } + return new Position(Column.parseChar(column), Row.parseChar(row)); + } +} diff --git a/src/main/java/chess/view/OutputView.java b/src/main/java/chess/view/OutputView.java new file mode 100644 index 00000000000..e5e790b8b7b --- /dev/null +++ b/src/main/java/chess/view/OutputView.java @@ -0,0 +1,22 @@ +package chess.view; + +import chess.domain.Column; +import chess.domain.Position; +import chess.domain.Row; +import chess.domain.piece.Piece; +import java.util.Map; + +public class OutputView { + + public void printBoard(Map positionToPiece) { + for (Row row : Row.values()) { + System.out.print(row.getNumber() + " "); + for (Column column : Column.values()) { + Piece piece = positionToPiece.get(new Position(row, column)); + System.out.print(piece.getPieceType().getTeam(piece.getColor())); + } + System.out.println(); + } + System.out.println(" ABCDEFGH"); + } +} diff --git a/src/test/java/.gitkeep b/src/test/java/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/test/java/chess/domain/BoardTest.java b/src/test/java/chess/domain/BoardTest.java new file mode 100644 index 00000000000..1671cbaa415 --- /dev/null +++ b/src/test/java/chess/domain/BoardTest.java @@ -0,0 +1,61 @@ +package chess.domain; + +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class BoardTest { + + @Test + @DisplayName("보드의 크기는 8 * 8 크기이다") + void boardSize() { + //given + //when + Board board = new Board(new ArrayList<>()); + + //then + Map positionPieceMap = board.getPositionToPiece(); + assertThat(positionPieceMap.keySet()).hasSize(8 * 8); + } + + @Test + @DisplayName("보드를 주어진 기물을 기반으로 배치한다") + void initBoard() { + //given + //when + Position position = new Position(Column.A, Row.ONE); + Piece piece = new Pawn(position, Color.BLACK, true); + List pieces = List.of(piece); + Board board = new Board(pieces); + + //then + Map positionPieceMap = board.getPositionToPiece(); + assertThat(positionPieceMap.get(position)).isEqualTo(piece); + } + + @ParameterizedTest + @CsvSource(value = {"A,ONE,true", "A,TWO,false"}) + @DisplayName("보드에서 특정 위치에 기물이 존재하는지 확인한다") + void isExist(Column column, Row row, boolean expected) { + //given + Position position = new Position(Column.A, Row.ONE); + Piece piece = new Pawn(position, Color.BLACK, true); + List pieces = List.of(piece); + Board board = new Board(pieces); + + //when + Position target = new Position(column, row); + boolean actual = board.isExist(target); + + //then + assertThat(actual).isEqualTo(expected); + } +} diff --git a/src/test/java/chess/domain/ColumnTest.java b/src/test/java/chess/domain/ColumnTest.java new file mode 100644 index 00000000000..5cee1cb0215 --- /dev/null +++ b/src/test/java/chess/domain/ColumnTest.java @@ -0,0 +1,95 @@ +package chess.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@DisplayName("열") +class ColumnTest { + + @DisplayName("A는 맨 왼쪽이다.") + @Test + void isFarLeft_A() { + assertThat(Column.A.isFarLeft()).isTrue(); + } + + @DisplayName("B는 맨 왼쪽이 아니다.") + @Test + void isFarLeft_B() { + assertThat(Column.B.isFarLeft()).isFalse(); + } + + @DisplayName("H는 맨 오른쪽이다.") + @Test + void isFarRight_H() { + assertThat(Column.H.isFarRight()).isTrue(); + } + + @DisplayName("F는 맨 오른쪽이 아니다.") + @Test + void isFarRight_F() { + assertThat(Column.F.isFarRight()).isFalse(); + } + + @DisplayName("A는 왼쪽으로 이동할 수 없다.") + @Test + void moveLeft_A() { + assertThatThrownBy(Column.A::moveLeft) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("B는 왼쪽으로 이동하면 A다.") + @Test + void moveLeft_B() { + final var moved = Column.B.moveLeft(); + + assertThat(moved).isEqualTo(Column.A); + } + + @DisplayName("B는 왼쪽으로 2번 이동할 수 없다.") + @Test + void moveLeft_2_B() { + assertThatThrownBy(() -> Column.B.moveLeft(2)) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("C는 왼쪽으로 2번 이동하면 A다.") + @Test + void moveLeft_2_C() { + final var moved = Column.C.moveLeft(2); + + assertThat(moved).isEqualTo(Column.A); + } + + @DisplayName("H는 오른쪽으로 이동할 수 없다.") + @Test + void moveRight_H() { + assertThatThrownBy(Column.H::moveRight) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("G는 오른쪽으로 이동하면 H다.") + @Test + void moveRight_G() { + final var moved = Column.G.moveRight(); + + assertThat(moved).isEqualTo(Column.H); + } + + @DisplayName("G는 오른쪽으로 2번 이동할 수 없다.") + @Test + void moveRight_2_G() { + assertThatThrownBy(() -> Column.G.moveRight(2)) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("F는 오른쪽으로 2번 이동하면 H다.") + @Test + void moveRight_2_F() { + final var moved = Column.F.moveRight(2); + + assertThat(moved).isEqualTo(Column.H); + } +} diff --git a/src/test/java/chess/domain/Fixtures.java b/src/test/java/chess/domain/Fixtures.java new file mode 100644 index 00000000000..340a5d744f8 --- /dev/null +++ b/src/test/java/chess/domain/Fixtures.java @@ -0,0 +1,80 @@ +package chess.domain; + +@SuppressWarnings("unused") +public final class Fixtures { + + public static final Position A1 = new Position(Column.A, Row.ONE); + public static final Position A2 = new Position(Column.A, Row.TWO); + public static final Position A3 = new Position(Column.A, Row.THREE); + public static final Position A4 = new Position(Column.A, Row.FOUR); + public static final Position A5 = new Position(Column.A, Row.FIVE); + public static final Position A6 = new Position(Column.A, Row.SIX); + public static final Position A7 = new Position(Column.A, Row.SEVEN); + public static final Position A8 = new Position(Column.A, Row.EIGHT); + + public static final Position B1 = new Position(Column.B, Row.ONE); + public static final Position B2 = new Position(Column.B, Row.TWO); + public static final Position B3 = new Position(Column.B, Row.THREE); + public static final Position B4 = new Position(Column.B, Row.FOUR); + public static final Position B5 = new Position(Column.B, Row.FIVE); + public static final Position B6 = new Position(Column.B, Row.SIX); + public static final Position B7 = new Position(Column.B, Row.SEVEN); + public static final Position B8 = new Position(Column.B, Row.EIGHT); + + public static final Position C1 = new Position(Column.C, Row.ONE); + public static final Position C2 = new Position(Column.C, Row.TWO); + public static final Position C3 = new Position(Column.C, Row.THREE); + public static final Position C4 = new Position(Column.C, Row.FOUR); + public static final Position C5 = new Position(Column.C, Row.FIVE); + public static final Position C6 = new Position(Column.C, Row.SIX); + public static final Position C7 = new Position(Column.C, Row.SEVEN); + public static final Position C8 = new Position(Column.C, Row.EIGHT); + + public static final Position D1 = new Position(Column.D, Row.ONE); + public static final Position D2 = new Position(Column.D, Row.TWO); + public static final Position D3 = new Position(Column.D, Row.THREE); + public static final Position D4 = new Position(Column.D, Row.FOUR); + public static final Position D5 = new Position(Column.D, Row.FIVE); + public static final Position D6 = new Position(Column.D, Row.SIX); + public static final Position D7 = new Position(Column.D, Row.SEVEN); + public static final Position D8 = new Position(Column.D, Row.EIGHT); + + public static final Position E1 = new Position(Column.E, Row.ONE); + public static final Position E2 = new Position(Column.E, Row.TWO); + public static final Position E3 = new Position(Column.E, Row.THREE); + public static final Position E4 = new Position(Column.E, Row.FOUR); + public static final Position E5 = new Position(Column.E, Row.FIVE); + public static final Position E6 = new Position(Column.E, Row.SIX); + public static final Position E7 = new Position(Column.E, Row.SEVEN); + public static final Position E8 = new Position(Column.E, Row.EIGHT); + + public static final Position F1 = new Position(Column.F, Row.ONE); + public static final Position F2 = new Position(Column.F, Row.TWO); + public static final Position F3 = new Position(Column.F, Row.THREE); + public static final Position F4 = new Position(Column.F, Row.FOUR); + public static final Position F5 = new Position(Column.F, Row.FIVE); + public static final Position F6 = new Position(Column.F, Row.SIX); + public static final Position F7 = new Position(Column.F, Row.SEVEN); + public static final Position F8 = new Position(Column.F, Row.EIGHT); + + public static final Position G1 = new Position(Column.G, Row.ONE); + public static final Position G2 = new Position(Column.G, Row.TWO); + public static final Position G3 = new Position(Column.G, Row.THREE); + public static final Position G4 = new Position(Column.G, Row.FOUR); + public static final Position G5 = new Position(Column.G, Row.FIVE); + public static final Position G6 = new Position(Column.G, Row.SIX); + public static final Position G7 = new Position(Column.G, Row.SEVEN); + public static final Position G8 = new Position(Column.G, Row.EIGHT); + + public static final Position H1 = new Position(Column.H, Row.ONE); + public static final Position H2 = new Position(Column.H, Row.TWO); + public static final Position H3 = new Position(Column.H, Row.THREE); + public static final Position H4 = new Position(Column.H, Row.FOUR); + public static final Position H5 = new Position(Column.H, Row.FIVE); + public static final Position H6 = new Position(Column.H, Row.SIX); + public static final Position H7 = new Position(Column.H, Row.SEVEN); + public static final Position H8 = new Position(Column.H, Row.EIGHT); + + private Fixtures() { + } +} diff --git a/src/test/java/chess/domain/PositionTest.java b/src/test/java/chess/domain/PositionTest.java new file mode 100644 index 00000000000..a58c07b5092 --- /dev/null +++ b/src/test/java/chess/domain/PositionTest.java @@ -0,0 +1,391 @@ +package chess.domain; + +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static chess.domain.Fixtures.A1; +import static chess.domain.Fixtures.A2; +import static chess.domain.Fixtures.A3; +import static chess.domain.Fixtures.A6; +import static chess.domain.Fixtures.A7; +import static chess.domain.Fixtures.A8; +import static chess.domain.Fixtures.B1; +import static chess.domain.Fixtures.B2; +import static chess.domain.Fixtures.B3; +import static chess.domain.Fixtures.B7; +import static chess.domain.Fixtures.B8; +import static chess.domain.Fixtures.C1; +import static chess.domain.Fixtures.F1; +import static chess.domain.Fixtures.F8; +import static chess.domain.Fixtures.G1; +import static chess.domain.Fixtures.G2; +import static chess.domain.Fixtures.G6; +import static chess.domain.Fixtures.G7; +import static chess.domain.Fixtures.G8; +import static chess.domain.Fixtures.H1; +import static chess.domain.Fixtures.H2; +import static chess.domain.Fixtures.H7; +import static chess.domain.Fixtures.H8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@DisplayName("위치") +class PositionTest { + + + @DisplayName("A1은 맨 왼쪽이다.") + @Test + void nonFarLeft_A1() { + assertThat(A1.isFarLeft()).isTrue(); + } + + @DisplayName("B1은 맨 왼쪽이 아니다.") + @Test + void nonFarLeft_B1() { + assertThat(B1.isFarLeft()).isFalse(); + } + + @DisplayName("H1은 맨 오른쪽이다.") + @Test + void nonFarRight_H1() { + assertThat(H1.isFarRight()).isTrue(); + } + + @DisplayName("F1은 맨 오른쪽이 아니다.") + @Test + void nonFarRight_F1() { + assertThat(F1.isFarRight()).isFalse(); + } + + @DisplayName("A1은 왼쪽으로 이동할 수 없다.") + @Test + void canMoveLeft_A1() { + assertThat(A1.canMoveLeft()).isFalse(); + } + + @DisplayName("A1은 왼쪽으로 이동할 수 없다.") + @Test + void canMoveLeft_Movement_A1() { + assertThat(A1.canMove(Movement.LEFT)).isFalse(); + } + + @DisplayName("B1은 왼쪽으로 이동할 수 있다.") + @Test + void canMoveLeft_B1() { + assertThat(B1.canMoveLeft()).isTrue(); + } + + @DisplayName("A1은 왼쪽으로 이동하면 예외가 발생한다.") + @Test + void moveLeft_A1() { + assertThatThrownBy(A1::moveLeft) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("B1은 왼쪽으로 이동하면 A1이다.") + @Test + void moveLeft_B1() { + final var moved = B1.moveLeft(); + + assertThat(moved).isEqualTo(A1); + } + + @DisplayName("B1은 왼쪽으로 2번 이동할 수 없다.") + @Test + void canMoveLeft_2_B1() { + assertThat(B1.canMoveLeft(2)).isFalse(); + } + + @DisplayName("B1은 왼쪽으로 2번 이동하면 예외가 발생한다.") + @Test + void moveLeft_2_B1() { + assertThatThrownBy(() -> B1.moveLeft(2)) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("C1은 왼쪽으로 2번 이동하면 A1이다.") + @Test + void moveLeft_C2() { + final var moved = C1.moveLeft(2); + + assertThat(moved).isEqualTo(A1); + } + + @DisplayName("H1은 오른쪽으로 이동할 수 없다.") + @Test + void canMoveRight_H1() { + assertThat(H1.canMoveRight()).isFalse(); + } + + @DisplayName("H1은 오른쪽으로 이동할 수 없다.") + @Test + void canMoveRight_Movement_H1() { + assertThat(H1.canMove(Movement.RIGHT)).isFalse(); + } + + @DisplayName("H1은 오른쪽으로 이동하면 예외가 발생한다.") + @Test + void moveRight_H1() { + assertThatThrownBy(H1::moveRight) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("G1은 오른쪽으로 이동하면 H1다.") + @Test + void moveRight_G1() { + final var moved = G1.moveRight(); + + assertThat(moved).isEqualTo(H1); + } + + @DisplayName("G1은 오른쪽으로 2번 이동할 수 없다.") + @Test + void canMoveRight_2_G1() { + assertThat(G1.canMoveRight(2)).isFalse(); + } + + @DisplayName("G1은 오른쪽으로 2번 이동하면 예외가 발생한다.") + @Test + void moveRight_2_G1() { + assertThatThrownBy(() -> G1.moveRight(2)) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("F1은 오른쪽으로 2번 이동하면 H1이다.") + @Test + void moveRight_2_F1() { + final var moved = F1.moveRight(2); + + assertThat(moved).isEqualTo(H1); + } + + /// // + + @DisplayName("A8은 맨 위다.") + @Test + void nonTop_A8() { + assertThat(A8.isTop()).isTrue(); + } + + @DisplayName("A7은 맨 위가 아니다.") + @Test + void nonTop_7() { + assertThat(A7.isTop()).isFalse(); + } + + @DisplayName("A1은 맨 아래다.") + @Test + void nonBottom_A1() { + assertThat(A1.isBottom()).isTrue(); + } + + @DisplayName("A2는 맨 아래가 아니다.") + @Test + void nonBottom_A2() { + assertThat(A2.isBottom()).isFalse(); + } + + @DisplayName("A8은 위로 이동할 수 없다.") + @Test + void canMoveUp_A8() { + assertThat(A8.canMoveUp()).isFalse(); + } + + @DisplayName("A8은 위로 이동하면 예외가 발생한다.") + @Test + void moveUp_A8() { + assertThatThrownBy(A8::moveUp) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("A7은 위로 이동하면 A8이다.") + @Test + void moveUp_A7() { + final var moved = A7.moveUp(); + + assertThat(moved).isEqualTo(A8); + } + + @DisplayName("A7은 위로 이동하면 A8이다.") + @Test + void moveUp_Movement_A7() { + final var moved = A7.move(Movement.UP); + + assertThat(moved).isEqualTo(A8); + } + + @DisplayName("A7은 위로 2번 이동할 수 없다.") + @Test + void canMoveUp_2_A7() { + assertThat(A7.canMoveUp(2)).isFalse(); + } + + @DisplayName("A7은 위로 2번 이동하면 예외가 발생한다.") + @Test + void moveUp_2_A7() { + assertThatThrownBy(() -> A7.moveUp(2)) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("A6은 위로 2번 이동하면 A8이다.") + @Test + void moveUp_2_A6() { + final var moved = A6.moveUp(2); + + assertThat(moved).isEqualTo(A8); + } + + @DisplayName("A1은 아래로 이동할 수 없다.") + @Test + void canMoveDown_A1() { + assertThat(A1.canMoveDown()).isFalse(); + } + + @DisplayName("A1은 아래로 이동하면 예외가 발생한다.") + @Test + void moveDown_A1() { + assertThatThrownBy(A1::moveDown) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("A2는 아래로 이동하면 A1이다.") + @Test + void moveDown_A2() { + final var moved = A2.moveDown(); + + assertThat(moved).isEqualTo(A1); + } + + @DisplayName("A2는 아래로 2번 이동할 수 없다.") + @Test + void canMoveDown_2_A2() { + assertThat(A2.canMoveDown(2)).isFalse(); + } + + @DisplayName("A2는 아래로 2번 이동하면 예외가 발생한다.") + @Test + void moveDown_2_A2() { + assertThatThrownBy(() -> A2.moveDown(2)) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("A3는 아래로 2번 이동하면 A1이다.") + @Test + void moveDown_2_A3() { + final var moved = A3.moveDown(2); + + assertThat(moved).isEqualTo(A1); + } + + @DisplayName("왼쪽 위 대각선으로 움직이면 예외가 발생한다.") + @MethodSource("immovableLeftUpSource") + @ParameterizedTest(name = "{0}은 왼쪽 위 대각선으로 움직이면 예외가 발생한다.") + void moveLeftUp_immovable(final Position position) { + assertThatThrownBy(position::moveLeftUp) + .isInstanceOf(IllegalStateException.class); + } + + static Stream immovableLeftUpSource() { + final var canMoveLeftPositions = Stream.of(A1, A2, A7, A8); + final var canMoveUpPositions = Stream.of(A8, B8, F8, H8); + + return Stream.concat(canMoveLeftPositions, canMoveUpPositions); + } + + @DisplayName("왼쪽 위 대각선으로 움직인다.") + @MethodSource("movableLeftUpSource") + @ParameterizedTest(name = "{0}을 왼쪽 위 대각선으로 움직인다.") + void moveLeftUp_movable(final Position position) { + assertThatCode(position::moveLeftUp) + .doesNotThrowAnyException(); + } + + static Stream movableLeftUpSource() { + return Stream.of(B2, B3, B7, G1, G2, G6, G7); + } + + @DisplayName("오른쪽 위 대각선으로 움직이면 예외가 발생한다.") + @MethodSource("immovableRightUpSource") + @ParameterizedTest(name = "{0}은 오른쪽 위 대각선으로 움직이면 예외가 발생한다.") + void moveRightUp_immovable(final Position position) { + assertThatThrownBy(position::moveRightUp) + .isInstanceOf(IllegalStateException.class); + } + + static Stream immovableRightUpSource() { + final var canMoveRightPositions = Stream.of(H1, H2, H7, H8); + final var canMoveUpPositions = Stream.of(A8, B8, F8, H8); + + return Stream.concat(canMoveRightPositions, canMoveUpPositions); + } + + @DisplayName("오른쪽 위 대각선으로 움직인다.") + @MethodSource("movableRightUpSource") + @ParameterizedTest(name = "{0}을 왼쪽 위 대각선으로 움직인다.") + void moveRightUp_movable(final Position position) { + assertThatCode(position::moveRightUp) + .doesNotThrowAnyException(); + } + + static Stream movableRightUpSource() { + return Stream.of(B1, B2, B3, B7, A2, A6, A7, G1, G2, G7); + } + + @DisplayName("왼쪽 아래 대각선으로 움직이면 예외가 발생한다.") + @MethodSource("immovableLeftDownSource") + @ParameterizedTest(name = "{0}은 왼쪽 아래 대각선으로 움직이면 예외가 발생한다.") + void moveLeftDown_immovable(final Position position) { + assertThatThrownBy(position::moveLeftDown) + .isInstanceOf(IllegalStateException.class); + } + + static Stream immovableLeftDownSource() { + final var canMoveLeftPositions = Stream.of(A1, A2, A7, A8); + final var canMoveDownPositions = Stream.of(A1, B1, F1, H1); + + return Stream.concat(canMoveLeftPositions, canMoveDownPositions); + } + + @DisplayName("왼쪽 아래 대각선으로 움직인다.") + @MethodSource("movableLeftDownSource") + @ParameterizedTest(name = "{0}을 왼쪽 아래 대각선으로 움직인다.") + void moveLeftDown_movable(final Position position) { + assertThatCode(position::moveLeftDown) + .doesNotThrowAnyException(); + } + + static Stream movableLeftDownSource() { + return Stream.of(B2, B3, B7, G8, G2, G6, G7); + } + + @DisplayName("오른쪽 아래 대각선으로 움직이면 예외가 발생한다.") + @MethodSource("immovableRightDownSource") + @ParameterizedTest(name = "{0}은 오른쪽 아래 대각선으로 움직이면 예외가 발생한다.") + void moveRightDown_immovable(final Position position) { + assertThatThrownBy(position::moveRightDown) + .isInstanceOf(IllegalStateException.class); + } + + static Stream immovableRightDownSource() { + final var canMoveRightPositions = Stream.of(H1, H2, H7, H8); + final var canMoveDownPositions = Stream.of(A1, B1, F1, H1); + + return Stream.concat(canMoveRightPositions, canMoveDownPositions); + } + + @DisplayName("오른쪽 아래 대각선으로 움직인다.") + @MethodSource("movableRightDownSource") + @ParameterizedTest(name = "{0}을 왼쪽 아래 대각선으로 움직인다.") + void moveRightDown_movable(final Position position) { + assertThatCode(position::moveRightDown) + .doesNotThrowAnyException(); + } + + static Stream movableRightDownSource() { + return Stream.of(B8, B2, B3, B7, A2, A6, A7, G8, G2, G7); + } +} diff --git a/src/test/java/chess/domain/RowTest.java b/src/test/java/chess/domain/RowTest.java new file mode 100644 index 00000000000..ff3c61c1468 --- /dev/null +++ b/src/test/java/chess/domain/RowTest.java @@ -0,0 +1,95 @@ +package chess.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@DisplayName("행") +class RowTest { + + @DisplayName("8은 맨 위다.") + @Test + void isTop_8() { + assertThat(Row.EIGHT.isTop()).isTrue(); + } + + @DisplayName("7은 맨 위가 아니다.") + @Test + void isTop_7() { + assertThat(Row.SEVEN.isTop()).isFalse(); + } + + @DisplayName("1은 맨 아래다.") + @Test + void isBottom_1() { + assertThat(Row.ONE.isBottom()).isTrue(); + } + + @DisplayName("2는 맨 아래가 아니다.") + @Test + void isBottom_2() { + assertThat(Row.TWO.isBottom()).isFalse(); + } + + @DisplayName("8은 위로 이동할 수 없다.") + @Test + void moveUp_8() { + assertThatThrownBy(Row.EIGHT::moveUp) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("7은 위로 이동하면 8이다.") + @Test + void moveUp_7() { + final var moved = Row.SEVEN.moveUp(); + + assertThat(moved).isEqualTo(Row.EIGHT); + } + + @DisplayName("7은 위로 2번 이동할 수 없다.") + @Test + void moveUp_2_7() { + assertThatThrownBy(() -> Row.SEVEN.moveUp(2)) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("6은 위로 2번 이동하면 8이다.") + @Test + void moveUp_2_6() { + final var moved = Row.SIX.moveUp(2); + + assertThat(moved).isEqualTo(Row.EIGHT); + } + + @DisplayName("1은 아래로 이동할 수 없다.") + @Test + void moveDown_1() { + assertThatThrownBy(Row.ONE::moveDown) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("2는 아래로 이동하면 1이다.") + @Test + void moveDown_2() { + final var moved = Row.TWO.moveDown(); + + assertThat(moved).isEqualTo(Row.ONE); + } + + @DisplayName("2는 아래로 2번 이동할 수 없다.") + @Test + void moveDown_2_2() { + assertThatThrownBy(() -> Row.TWO.moveDown(2)) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("3은 아래로 2번 이동하면 1이다.") + @Test + void moveDown_2_3() { + final var moved = Row.THREE.moveDown(2); + + assertThat(moved).isEqualTo(Row.ONE); + } +} diff --git a/src/test/java/chess/domain/piece/BishopTest.java b/src/test/java/chess/domain/piece/BishopTest.java new file mode 100644 index 00000000000..2f0b832525f --- /dev/null +++ b/src/test/java/chess/domain/piece/BishopTest.java @@ -0,0 +1,58 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Column; +import chess.domain.Position; +import chess.domain.Row; +import java.util.List; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class BishopTest { + + @ParameterizedTest + @CsvSource(value = {"EIGHT,A", "EIGHT,G", "TWO,A", "TWO,G"}) + @DisplayName("비숍은 대각선으로 이동한다") + void moveBishop(Row row, Column column) { + //given + Position position = new Position(Row.FIVE, Column.D); + Piece piece = new Bishop(position, Color.BLACK); + List pieces = List.of(piece); + Board board = new Board(pieces); + + //when + Position destination = new Position(column, row); + + //then + assertDoesNotThrow(() -> piece.move(board, destination)); + } + + @ParameterizedTest + @CsvSource(value = {"EIGHT,A", "EIGHT,G", "TWO,A", "TWO,G"}) + @DisplayName("경로에 기물이 존재하면 이동할 수 없다") + void cannotMoveRook(Row row, Column column) { + //given + Position position = new Position(Row.FIVE, Column.D); + Piece piece = new Bishop(position, Color.BLACK); + List pieces = List.of(piece, + new Rook(position.moveLeftUp(), Color.BLACK), + new Rook(position.moveRightUp(), Color.BLACK), + new Rook(position.moveLeftDown(), Color.BLACK), + new Rook(position.moveRightDown(), Color.BLACK) + ); + Board board = new Board(pieces); + + //when + Position destination = new Position(column, row); + + //then + Assertions.assertThatThrownBy(() -> piece.move(board, destination)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("기물이 존재합니다"); + } +} diff --git a/src/test/java/chess/domain/piece/KnightTest.java b/src/test/java/chess/domain/piece/KnightTest.java new file mode 100644 index 00000000000..e9aa05b16ba --- /dev/null +++ b/src/test/java/chess/domain/piece/KnightTest.java @@ -0,0 +1,33 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Column; +import chess.domain.Position; +import chess.domain.Row; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class KnightTest { + + @ParameterizedTest + @CsvSource(value = {"SEVEN,B", "SEVEN,D", "THREE,B", "THREE,D", "SIX,A", "FOUR,A", "SIX,E", "FOUR,E"}) + @DisplayName("나이트는 곡선 모양 형태로 1회 이동한다") + void movePawn(Row row, Column column) { + //given + Position position = new Position(Row.FIVE, Column.C); + Piece piece = new Knight(position, Color.BLACK); + List pieces = List.of(piece); + Board board = new Board(pieces); + + //when + Position destination = new Position(column, row); + + //then + assertDoesNotThrow(() -> piece.move(board, destination)); + } +} diff --git a/src/test/java/chess/domain/piece/PawnTest.java b/src/test/java/chess/domain/piece/PawnTest.java new file mode 100644 index 00000000000..0533e22d364 --- /dev/null +++ b/src/test/java/chess/domain/piece/PawnTest.java @@ -0,0 +1,91 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Column; +import chess.domain.Position; +import chess.domain.Row; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +public class PawnTest { + + @ParameterizedTest + @CsvSource(value = {"SIX,C,BLACK", "FOUR,C,WHITE"}) + @DisplayName("처음이 아닌 움직임에서는 폰은 본진 방향을 제외한 상하 1칸 이동한다") + void movePawn(Row row, Column column, Color color) { + //given + Position position = new Position(Row.FIVE, Column.C); + Piece piece = new Pawn(position, color, false); + List pieces = List.of(piece); + Board board = new Board(pieces); + + //when + Position destination = new Position(column, row); + + //then + assertDoesNotThrow(() -> piece.move(board, destination)); + } + + @ParameterizedTest + @CsvSource(value = {"SIX,C,BLACK", "FOUR,C,WHITE"}) + @DisplayName("처음 움직임에서는 폰은 본진 방향을 제외한 상하 1-2칸 이동한다") + void movePawnNotFirst(Row row, Column column, Color color) { + //given + Position position = new Position(Row.FIVE, Column.C); + Piece piece = new Pawn(position, color, false); + List pieces = List.of(piece); + Board board = new Board(pieces); + + //when + Position destination = new Position(column, row); + + //then + assertDoesNotThrow(() -> piece.move(board, destination)); + } + + private static Stream providePawn() { + return Stream.of( + Arguments.of( + new Pawn(new Position(Row.FIVE, Column.B), Color.WHITE, true), + new Pawn(new Position(Row.SIX, Column.C), Color.BLACK, true), + new Position(Row.SIX, Column.C) + ), + Arguments.of( + new Pawn(new Position(Row.FIVE, Column.B), Color.WHITE, true), + new Pawn(new Position(Row.SIX, Column.A), Color.BLACK, true), + new Position(Row.SIX, Column.A) + ), + Arguments.of( + new Pawn(new Position(Row.FIVE, Column.B), Color.BLACK, true), + new Pawn(new Position(Row.FOUR, Column.C), Color.WHITE, true), + new Position(Row.FOUR, Column.C) + ), + Arguments.of( + new Pawn(new Position(Row.FIVE, Column.B), Color.BLACK, true), + new Pawn(new Position(Row.FOUR, Column.A), Color.WHITE, true), + new Position(Row.FOUR, Column.A) + ) + ); + } + + @ParameterizedTest + @MethodSource("providePawn") + @DisplayName("폰은 적이 있으면 대각선 이동이 가능하다") + void movePawnNotFirst(Pawn pawn, Pawn enemy, Position move) { + //given + List pieces = List.of(pawn, enemy); + Board board = new Board(pieces); + + //when + //then + assertDoesNotThrow(() -> pawn.move(board, move)); + } +} diff --git a/src/test/java/chess/domain/piece/QueenTest.java b/src/test/java/chess/domain/piece/QueenTest.java new file mode 100644 index 00000000000..0feca1062fe --- /dev/null +++ b/src/test/java/chess/domain/piece/QueenTest.java @@ -0,0 +1,62 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Column; +import chess.domain.Position; +import chess.domain.Row; +import java.util.List; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class QueenTest { + + @ParameterizedTest + @CsvSource(value = {"EIGHT,A", "EIGHT,G", "TWO,A", "ONE,H", "ONE,D", "EIGHT,D", "FIVE,A", "FIVE,H"}) + @DisplayName("퀸은 상하좌우, 대각선으로 직선 이동한다") + void movePawn(Row row, Column column) { + //given + Position position = new Position(Row.FIVE, Column.D); + Piece piece = new Queen(position, Color.BLACK); + List pieces = List.of(piece); + Board board = new Board(pieces); + + //when + Position destination = new Position(column, row); + + //then + assertDoesNotThrow(() -> piece.move(board, destination)); + } + + @ParameterizedTest + @CsvSource(value = {"EIGHT,A", "EIGHT,G", "TWO,A", "ONE,H", "ONE,D", "EIGHT,D", "FIVE,A", "FIVE,H"}) + @DisplayName("경로에 기물이 존재하면 이동할 수 없다") + void cannotMoveRook(Row row, Column column) { + //given + Position position = new Position(Row.FIVE, Column.D); + Piece piece = new Queen(position, Color.BLACK); + List pieces = List.of(piece, + new Rook(position.moveUp(), Color.BLACK), + new Rook(position.moveDown(), Color.BLACK), + new Rook(position.moveLeft(), Color.BLACK), + new Rook(position.moveRight(), Color.BLACK), + new Rook(position.moveLeftUp(), Color.BLACK), + new Rook(position.moveRightUp(), Color.BLACK), + new Rook(position.moveLeftDown(), Color.BLACK), + new Rook(position.moveRightDown(), Color.BLACK) + ); + Board board = new Board(pieces); + + //when + Position destination = new Position(column, row); + + //then + Assertions.assertThatThrownBy(() -> piece.move(board, destination)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("기물이 존재합니다"); + } +} diff --git a/src/test/java/chess/domain/piece/RookTest.java b/src/test/java/chess/domain/piece/RookTest.java new file mode 100644 index 00000000000..683c2160faf --- /dev/null +++ b/src/test/java/chess/domain/piece/RookTest.java @@ -0,0 +1,58 @@ +package chess.domain.piece; + +import chess.domain.Board; +import chess.domain.Color; +import chess.domain.Column; +import chess.domain.Position; +import chess.domain.Row; +import java.util.List; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class RookTest { + + @ParameterizedTest + @CsvSource(value = {"ONE,C", "EIGHT,C", "FIVE,A", "FIVE,H"}) + @DisplayName("룩은 상하좌우로 직선 이동한다") + void movePawn(Row row, Column column) { + //given + Position position = new Position(Row.FIVE, Column.C); + Piece piece = new Rook(position, Color.BLACK); + List pieces = List.of(piece); + Board board = new Board(pieces); + + //when + Position destination = new Position(column, row); + + //then + assertDoesNotThrow(() -> piece.move(board, destination)); + } + + @ParameterizedTest + @CsvSource(value = {"ONE,C", "EIGHT,C", "FIVE,A", "FIVE,H"}) + @DisplayName("룩은 경로에 기물이 존재하면 이동할 수 없다") + void cannotMoveRook(Row row, Column column) { + //given + Position position = new Position(Row.FIVE, Column.C); + Piece piece = new Rook(position, Color.BLACK); + List pieces = List.of(piece, + new Rook(position.moveUp(), Color.BLACK), + new Rook(position.moveDown(), Color.BLACK), + new Rook(position.moveLeft(), Color.BLACK), + new Rook(position.moveRight(), Color.BLACK) + ); + Board board = new Board(pieces); + + //when + Position destination = new Position(column, row); + + //then + Assertions.assertThatThrownBy(() -> piece.move(board, destination)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("기물이 존재합니다"); + } +}