diff --git a/README.md b/README.md index 8102f91c870..b241d5e6f6e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,35 @@ 체스 미션 저장소 -## 우아한테크코스 코드리뷰 +## 출력 예시 -- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) +```angular2html + +8 BR BN BB BQ BK BB BN BR +7 BP BP BP BP BP BP BP BP +6 - - - - - - - - +5 - - - - - - - - +4 - - - - - - - - +3 - - - - - - - - +2 WP WP WP WP WP WP WP WP +1 WR WN WB WQ WK WB WN WR + A B C D E F G H + +백색 차례입니다 +시작 좌표를 입력해주세요(ex)x,y +(B,1) + +목표 좌표를 입력해주세요(ex)x,y +(A,3) + +이동하였습니다 +8 BL BN BB BQ BK BB BN BL +7 BP BP BP BP BP BP BP BP +6 - - - - - - - - +5 - - - - - - - - +4 - - - - - - - - +3 WN - - - - - - - +2 WP WP WP WP WP WP WP WP +1 WL - WB WQ WK WB WN WL + A B C D E F G H +``` 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/Application.java b/src/main/java/Application.java new file mode 100644 index 00000000000..5b54781ace8 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,12 @@ +import chess.ChessGame; +import chess.board.ChessBoard; +import view.ConsoleView; +import view.support.OutputSupporter; + +public class Application { + + public static void main(String[] args) { + ChessGame game = new ChessGame(ChessBoard.initialize(), new ConsoleView(new OutputSupporter())); + game.start(); + } +} diff --git a/src/main/java/chess/ChessGame.java b/src/main/java/chess/ChessGame.java new file mode 100644 index 00000000000..9eafc3dd707 --- /dev/null +++ b/src/main/java/chess/ChessGame.java @@ -0,0 +1,34 @@ +package chess; + +import chess.board.ChessBoard; +import chess.board.Position; +import view.ConsoleView; + +public class ChessGame { + + private final ChessBoard chessBoard; + private final ConsoleView consoleView; + + public ChessGame(ChessBoard chessBoard, ConsoleView consoleView) { + this.chessBoard = chessBoard; + this.consoleView = consoleView; + } + + public void start() { + Turn currentTurn = Turn.getStartingTurn(); + boolean isGameStop = false; + while (!isGameStop) { + try { + consoleView.printBoard(chessBoard.getPieces()); + consoleView.printTurn(currentTurn); + Position startPoint = consoleView.requestStartPoint(); + Position destination = consoleView.requestDestination(); + + isGameStop = chessBoard.move(startPoint, destination); + currentTurn = currentTurn.changeTurn(); + } catch (RuntimeException e) { + consoleView.printMessage(e.getMessage()); + } + } + } +} diff --git a/src/main/java/chess/Color.java b/src/main/java/chess/Color.java new file mode 100644 index 00000000000..55cd020b681 --- /dev/null +++ b/src/main/java/chess/Color.java @@ -0,0 +1,28 @@ +package chess; + +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/Turn.java b/src/main/java/chess/Turn.java new file mode 100644 index 00000000000..6a12e742931 --- /dev/null +++ b/src/main/java/chess/Turn.java @@ -0,0 +1,17 @@ +package chess; + +public enum Turn { + WHITE_TURN, + BLACK_TURN; + + public static Turn getStartingTurn() { + return WHITE_TURN; + } + + public Turn changeTurn() { + if (this == WHITE_TURN) { + return BLACK_TURN; + } + return WHITE_TURN; + } +} diff --git a/src/main/java/chess/board/BoardVector.java b/src/main/java/chess/board/BoardVector.java new file mode 100644 index 00000000000..2d458d3b865 --- /dev/null +++ b/src/main/java/chess/board/BoardVector.java @@ -0,0 +1,41 @@ +package chess.board; + +public record BoardVector( + int dx, + int dy +) { + + public int getAbsDx() { + return Math.abs(dx); + } + + public int getAbsDy() { + return Math.abs(dy); + } + + public static BoardVector createVector(Position start, Position end) { + int dx = end.column().ordinal() - start.column().ordinal(); + int dy = end.row().getValue() - start.row().getValue(); + return new BoardVector(dx, dy); + } + + public boolean isOneQuadrant() { + return dx > 0 && dy > 0; + } + + public boolean isTwoQuadrant() { + return dx < 0 && dy > 0; + } + + public boolean isThreeQuadrant() { + return dx < 0 && dy < 0; + } + + public boolean isFourQuadrant() { + return dx > 0 && dy < 0; + } + + public boolean isDxZero() { + return dx == 0; + } +} diff --git a/src/main/java/chess/board/ChessBoard.java b/src/main/java/chess/board/ChessBoard.java new file mode 100644 index 00000000000..ec373117d40 --- /dev/null +++ b/src/main/java/chess/board/ChessBoard.java @@ -0,0 +1,105 @@ +package chess.board; + +import chess.Color; +import chess.piece.Bishop; +import chess.piece.King; +import chess.piece.Knight; +import chess.piece.Pawn; +import chess.piece.Piece; +import chess.piece.Queen; +import chess.piece.Rook; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ChessBoard { + + private final Map pieces; + + public ChessBoard(Map pieces) { + this.pieces = pieces; + } + + public boolean move(Position startPoint, Position destination) { + if (!pieces.containsKey(startPoint)) { + throw new IllegalArgumentException("[ERROR] 해당 위치에 기물이 없습니다"); + } + + Piece startPiece = pieces.get(startPoint); + if (!startPiece.isMovable(startPoint, destination)) { + throw new IllegalArgumentException("[ERROR] 해당 기물은 목표 위치로 움직일 수 없습니다"); + } + + List paths = startPiece.createAllPaths(startPoint, destination); // 목표 위치 제외 + List pathPieces = extractPathPieces(paths); + if (!startPiece.canJumpOver() && !pathPieces.isEmpty()) { + throw new IllegalArgumentException("[ERROR] 경로에 기물이 있어 목표 위치로 움직일 수 없습니다"); + } + + if (pieces.containsKey(destination)) { + Piece destinationPiece = pieces.get(destination); + validateColor(destination, startPiece); + return destinationPiece.isGameStopIfDie(); + } + + pieces.put(destination, startPiece); + pieces.remove(startPoint); + return true; + } + + private void validateColor(Position destination, Piece startPiece) { + Piece destinationPiece = pieces.get(destination); + if (startPiece.isEqualColor(destinationPiece)) { + throw new IllegalArgumentException("[ERROR] 목적지에 같은 색의 기물이 위치해 있어 움직일 수 없습니다"); + } + } + + private List extractPathPieces(List paths) { + return paths.stream() + .filter(pieces::containsKey) + .map(pieces::get) + .toList(); + } + + public static ChessBoard initialize() { + Map initialPieces = new HashMap<>(); + initialPieces.put(new Position(Row.ONE, Column.A), new Rook(Color.WHITE)); + initialPieces.put(new Position(Row.ONE, Column.B), new Knight(Color.WHITE)); + initialPieces.put(new Position(Row.ONE, Column.C), new Bishop(Color.WHITE)); + initialPieces.put(new Position(Row.ONE, Column.D), new Queen(Color.WHITE)); + initialPieces.put(new Position(Row.ONE, Column.E), new King(Color.WHITE)); + initialPieces.put(new Position(Row.ONE, Column.F), new Bishop(Color.WHITE)); + initialPieces.put(new Position(Row.ONE, Column.G), new Knight(Color.WHITE)); + initialPieces.put(new Position(Row.ONE, Column.H), new Rook(Color.WHITE)); + initialPieces.put(new Position(Row.TWO, Column.A), new Pawn(Color.WHITE)); + initialPieces.put(new Position(Row.TWO, Column.B), new Pawn(Color.WHITE)); + initialPieces.put(new Position(Row.TWO, Column.C), new Pawn(Color.WHITE)); + initialPieces.put(new Position(Row.TWO, Column.D), new Pawn(Color.WHITE)); + initialPieces.put(new Position(Row.TWO, Column.E), new Pawn(Color.WHITE)); + initialPieces.put(new Position(Row.TWO, Column.F), new Pawn(Color.WHITE)); + initialPieces.put(new Position(Row.TWO, Column.G), new Pawn(Color.WHITE)); + initialPieces.put(new Position(Row.TWO, Column.H), new Pawn(Color.WHITE)); + + initialPieces.put(new Position(Row.EIGHT, Column.A), new Rook(Color.BLACK)); + initialPieces.put(new Position(Row.EIGHT, Column.B), new Knight(Color.BLACK)); + initialPieces.put(new Position(Row.EIGHT, Column.C), new Bishop(Color.BLACK)); + initialPieces.put(new Position(Row.EIGHT, Column.D), new Queen(Color.BLACK)); + initialPieces.put(new Position(Row.EIGHT, Column.E), new King(Color.BLACK)); + initialPieces.put(new Position(Row.EIGHT, Column.F), new Bishop(Color.BLACK)); + initialPieces.put(new Position(Row.EIGHT, Column.G), new Knight(Color.BLACK)); + initialPieces.put(new Position(Row.EIGHT, Column.H), new Rook(Color.BLACK)); + initialPieces.put(new Position(Row.SEVEN, Column.A), new Pawn(Color.BLACK)); + initialPieces.put(new Position(Row.SEVEN, Column.B), new Pawn(Color.BLACK)); + initialPieces.put(new Position(Row.SEVEN, Column.C), new Pawn(Color.BLACK)); + initialPieces.put(new Position(Row.SEVEN, Column.D), new Pawn(Color.BLACK)); + initialPieces.put(new Position(Row.SEVEN, Column.E), new Pawn(Color.BLACK)); + initialPieces.put(new Position(Row.SEVEN, Column.F), new Pawn(Color.BLACK)); + initialPieces.put(new Position(Row.SEVEN, Column.G), new Pawn(Color.BLACK)); + initialPieces.put(new Position(Row.SEVEN, Column.H), new Pawn(Color.BLACK)); + return new ChessBoard(initialPieces); + } + + public Map getPieces() { + return pieces; + } +} diff --git a/src/main/java/chess/board/Column.java b/src/main/java/chess/board/Column.java new file mode 100644 index 00000000000..301d4dcb205 --- /dev/null +++ b/src/main/java/chess/board/Column.java @@ -0,0 +1,56 @@ +package chess.board; + +import java.util.Arrays; +import java.util.Objects; + +public enum Column { + + A, + B, + C, + D, + E, + F, + G, + H; + + public boolean isFarLeft() { + return ordinal() == 0; + } + + public boolean isFarRight() { + return ordinal() + 1 == values().length; + } + + public Column moveLeft() { + return moveLeft(1); + } + + public Column moveLeft(final int step) { + if (canMoveLeft(step)) { + return values()[ordinal() - step]; + } + + throw new IllegalStateException("움직일 수 없는 위치입니다."); + } + + public Column moveRight() { + return moveRight(1); + } + + public Column moveRight(final int step) { + if (canMoveRight(step)) { + return values()[ordinal() + step]; + } + + throw new IllegalStateException("움직일 수 없는 위치입니다."); + } + + public boolean canMoveRight(final int step) { + return ordinal() + step < values().length; + } + + public boolean canMoveLeft(final int step) { + return ordinal() - step >= 0; + } +} diff --git a/src/main/java/chess/board/Movement.java b/src/main/java/chess/board/Movement.java new file mode 100644 index 00000000000..2c9d0378769 --- /dev/null +++ b/src/main/java/chess/board/Movement.java @@ -0,0 +1,58 @@ +package chess.board; + +import java.util.List; + +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 isDiagonal() { + return x != 0 && y != 0 && Math.abs(x) == Math.abs(y); + } + + public static List getAllCoordinateAxis() { + return List.of(UP, DOWN, LEFT, RIGHT); + } + + public static List getAllDiagonal() { + return List.of(RIGHT_UP, RIGHT_DOWN, LEFT_UP, LEFT_DOWN); + } +} diff --git a/src/main/java/chess/board/Position.java b/src/main/java/chess/board/Position.java new file mode 100644 index 00000000000..a2b53de635a --- /dev/null +++ b/src/main/java/chess/board/Position.java @@ -0,0 +1,198 @@ +package chess.board; + +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +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 move(final List movements) { + Position position = this; + for (Movement movement : movements) { + position = moveVertical(movement.y()).moveHorizontal(movement.x()); + } + return position; + } + + 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; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Position position)) { + return false; + } + return row == position.row && column == position.column; + } + + @Override + public int hashCode() { + return Objects.hash(column, row); + } +} diff --git a/src/main/java/chess/board/Row.java b/src/main/java/chess/board/Row.java new file mode 100644 index 00000000000..e40348f254e --- /dev/null +++ b/src/main/java/chess/board/Row.java @@ -0,0 +1,72 @@ +package chess.board; + +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 int value; + + Row(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + 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 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 static Row findByValue(final int value) { + return Arrays.stream(Row.values()) + .filter(row -> row.value == value) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("[ERROR] 잘못된 Row 값입니다.")); + } +} diff --git a/src/main/java/chess/piece/Bishop.java b/src/main/java/chess/piece/Bishop.java new file mode 100644 index 00000000000..35fe4cadff4 --- /dev/null +++ b/src/main/java/chess/piece/Bishop.java @@ -0,0 +1,54 @@ +package chess.piece; + +import chess.Color; +import chess.board.BoardVector; +import chess.board.Position; +import java.util.ArrayList; +import java.util.List; + +public class Bishop extends Piece{ + + public Bishop(Color color) { + super(color, PieceType.BISHOP); + } + + @Override + public boolean isMovable(Position startPoint, Position destination) { + BoardVector boardVector = BoardVector.createVector(startPoint, destination); + int absDx = boardVector.getAbsDx(); + int absDy = boardVector.getAbsDy(); + return absDx == absDy; + } + + @Override + public List createAllPaths(Position startPoint, Position destination) { + BoardVector boardVector = BoardVector.createVector(startPoint, destination); + List paths = new ArrayList<>(); + Position path = startPoint; + if (boardVector.isOneQuadrant()) { + for (int i = 0; i < boardVector.dx() - 1; i++) { + path = path.moveRightUp(); + paths.add(path); + } + } + if (boardVector.isTwoQuadrant()) { + for (int i = 0; i < boardVector.dx(); i++) { + path = path.moveLeftUp(); + paths.add(path); + } + } + if (boardVector.isThreeQuadrant()) { + for (int i = 0; i < boardVector.dx(); i++) { + path = path.moveLeftDown(); + paths.add(path); + } + } + if (boardVector.isFourQuadrant()) { + for (int i = 0; i < boardVector.dx(); i++) { + path = path.moveRightDown(); + paths.add(path); + } + } + return paths; + } +} diff --git a/src/main/java/chess/piece/King.java b/src/main/java/chess/piece/King.java new file mode 100644 index 00000000000..5e1bf212eb6 --- /dev/null +++ b/src/main/java/chess/piece/King.java @@ -0,0 +1,31 @@ +package chess.piece; + +import chess.Color; +import chess.board.BoardVector; +import chess.board.Position; +import java.util.List; + +public class King extends Piece { + + public King(Color color) { + super(color, PieceType.KING); + } + + @Override + public boolean isMovable(Position startPoint, Position destination) { + BoardVector boardVector = BoardVector.createVector(startPoint, destination); + int absDx = boardVector.getAbsDx(); + int absDy = boardVector.getAbsDy(); + return (absDx + absDy) == 1 || (absDx == 1 && absDy == 1); + } + + @Override + public List createAllPaths(Position startPoint, Position destination) { + return List.of(); + } + + @Override + public boolean isGameStopIfDie() { + return true; + } +} diff --git a/src/main/java/chess/piece/Knight.java b/src/main/java/chess/piece/Knight.java new file mode 100644 index 00000000000..3b2d550bfad --- /dev/null +++ b/src/main/java/chess/piece/Knight.java @@ -0,0 +1,49 @@ +package chess.piece; + +import chess.Color; +import chess.board.BoardVector; +import chess.board.Movement; +import chess.board.Position; +import java.util.ArrayList; +import java.util.List; + +public class Knight extends Piece { + + public Knight(Color color) { + super(color, PieceType.KNIGHT); + } + + @Override + public boolean isMovable(Position startPoint, Position destination) { + BoardVector boardVector = BoardVector.createVector(startPoint, destination); + int absDx = boardVector.getAbsDx(); + int absDy = boardVector.getAbsDy(); + return (absDx == 1 && absDy == 2) || (absDx == 2 && absDy == 1); + } + + @Override + public List createAllPaths(Position startPoint, Position destination) { + List paths = new ArrayList<>(); + + List coordinateAxis = Movement.getAllCoordinateAxis(); + List diagonals = Movement.getAllDiagonal(); + + BoardVector boardVector = BoardVector.createVector(startPoint, destination); + Position current = startPoint; + for (Movement part : coordinateAxis) { + for (Movement diagonal : diagonals) { + if (boardVector.equals(new BoardVector(part.x() + diagonal.x(), part.y() + diagonal.y()))) { + current = current.move(part); + paths.add(current); + } + } + } + + return paths; + } + + @Override + public boolean canJumpOver() { + return true; + } +} diff --git a/src/main/java/chess/piece/Pawn.java b/src/main/java/chess/piece/Pawn.java new file mode 100644 index 00000000000..c01f0831dc7 --- /dev/null +++ b/src/main/java/chess/piece/Pawn.java @@ -0,0 +1,42 @@ +package chess.piece; + +import chess.Color; +import chess.board.BoardVector; +import chess.board.Position; +import java.util.List; + +public class Pawn extends Piece { + + private boolean isMoved; + + public Pawn(Color color) { + super(color, PieceType.PAWN); + } + + @Override + public boolean isMovable(Position startPoint, Position destination) { + BoardVector boardVector = BoardVector.createVector(startPoint, destination); + if (color == Color.BLACK) { + if (!isMoved) { + isMoved = true; + return boardVector.dy() == -2 && boardVector.dx() == 0; + } + return boardVector.dy() == -1 && boardVector.dx() == 0; + } + + if (color == Color.WHITE) { + if (!isMoved) { + isMoved = true; + return boardVector.dy() == 2 && boardVector.dx() == 0; + } + return boardVector.dy() == 1 && boardVector.dx() == 0; + } + + throw new IllegalStateException("[ERROR] 현재 기물의 색이 정해져있지 않습니다"); + } + + @Override + public List createAllPaths(Position startPoint, Position destination) { + return List.of(); + } +} diff --git a/src/main/java/chess/piece/Piece.java b/src/main/java/chess/piece/Piece.java new file mode 100644 index 00000000000..cccdb4a5a9c --- /dev/null +++ b/src/main/java/chess/piece/Piece.java @@ -0,0 +1,40 @@ +package chess.piece; + +import chess.Color; +import chess.board.Position; +import java.util.List; + +public abstract class Piece { + + protected final Color color; + protected final PieceType pieceType; + + public Piece(Color color, PieceType pieceType) { + this.color = color; + this.pieceType = pieceType; + } + + public abstract boolean isMovable(Position startPoint, Position destination); + + public abstract List createAllPaths(Position startPoint, Position destination); + + public boolean canJumpOver() { + return false; + }; + + public boolean isEqualColor(Piece other) { + return this.color == other.color; + } + + public boolean isGameStopIfDie(){ + return false; + }; + + public Color getColor() { + return color; + } + + public PieceType getPieceType() { + return pieceType; + } +} diff --git a/src/main/java/chess/piece/PieceType.java b/src/main/java/chess/piece/PieceType.java new file mode 100644 index 00000000000..2250bce858c --- /dev/null +++ b/src/main/java/chess/piece/PieceType.java @@ -0,0 +1,10 @@ +package chess.piece; + +public enum PieceType { + BISHOP, + KING, + QUEEN, + KNIGHT, + ROOK, + PAWN +} diff --git a/src/main/java/chess/piece/Queen.java b/src/main/java/chess/piece/Queen.java new file mode 100644 index 00000000000..946330d49b8 --- /dev/null +++ b/src/main/java/chess/piece/Queen.java @@ -0,0 +1,102 @@ +package chess.piece; + +import chess.Color; +import chess.board.BoardVector; +import chess.board.Position; +import java.util.ArrayList; +import java.util.List; + +public class Queen extends Piece { + + public Queen(Color color) { + super(color, PieceType.QUEEN); + } + + @Override + public boolean isMovable(Position startPoint, Position destination) { + BoardVector boardVector = BoardVector.createVector(startPoint, destination); + int absDx = boardVector.getAbsDx(); + int absDy = boardVector.getAbsDy(); + + return (absDx == absDy) || !(absDx != 0 && absDy != 0); + } + + @Override + public List createAllPaths(Position startPoint, Position destination) { + BoardVector boardVector = BoardVector.createVector(startPoint, destination); + int absDx = boardVector.getAbsDx(); + int absDy = boardVector.getAbsDy(); + + if (absDx == absDy) { + return createDiagonalPaths(boardVector, startPoint); + } + + return createCoordinate(boardVector, startPoint); + } + + private List createDiagonalPaths(BoardVector boardVector, Position startPoint) { + List paths = new ArrayList<>(); + Position path = startPoint; + if (boardVector.isOneQuadrant()) { + for (int i = 0; i < boardVector.dx() - 1; i++) { + path = path.moveRightUp(); + paths.add(path); + } + } + if (boardVector.isTwoQuadrant()) { + for (int i = 0; i < boardVector.dx(); i++) { + path = path.moveLeftUp(); + paths.add(path); + } + } + if (boardVector.isThreeQuadrant()) { + for (int i = 0; i < boardVector.dx(); i++) { + path = path.moveLeftDown(); + paths.add(path); + } + } + if (boardVector.isFourQuadrant()) { + for (int i = 0; i < boardVector.dx(); i++) { + path = path.moveRightDown(); + paths.add(path); + } + } + return paths; + } + + private List createCoordinate(BoardVector boardVector, Position startPoint) { + List paths = new ArrayList<>(); + Position path = startPoint; + if (boardVector.isDxZero()) { + if (isPositive(boardVector.dy())) { + while (path.canMoveUp()) { + path = path.moveUp(); + paths.add(path); + } + return paths; + } + while (path.canMoveDown()) { + path = path.moveDown(); + paths.add(path); + } + return paths; + } + + if (isPositive(boardVector.dx())) { + while (path.canMoveRight()) { + path = path.moveRight(); + paths.add(path); + } + return paths; + } + while (path.canMoveLeft()) { + path = path.moveLeft(); + paths.add(path); + } + return paths; + } + + private boolean isPositive(int delta) { + return delta > 0; + } +} diff --git a/src/main/java/chess/piece/Rook.java b/src/main/java/chess/piece/Rook.java new file mode 100644 index 00000000000..8f9006750ac --- /dev/null +++ b/src/main/java/chess/piece/Rook.java @@ -0,0 +1,61 @@ +package chess.piece; + +import chess.Color; +import chess.board.BoardVector; +import chess.board.Position; +import java.util.ArrayList; +import java.util.List; + +public class Rook extends Piece { + + public Rook(Color color) { + super(color, PieceType.ROOK); + } + + @Override + public boolean isMovable(Position startPoint, Position destination) { + BoardVector boardVector = BoardVector.createVector(startPoint, destination); + int absDx = boardVector.getAbsDx(); + int absDy = boardVector.getAbsDy(); + return !(absDx != 0 && absDy != 0); + } + + @Override + public List createAllPaths(Position startPoint, Position destination) { + List paths = new ArrayList<>(); + BoardVector boardVector = BoardVector.createVector(startPoint, destination); + + Position path = startPoint; + if (boardVector.isDxZero()) { + if (isPositive(boardVector.dy())) { + while (path.canMoveUp()) { + path = path.moveUp(); + paths.add(path); + } + return paths; + } + while (path.canMoveDown()) { + path = path.moveDown(); + paths.add(path); + } + return paths; + } + + if (isPositive(boardVector.dx())) { + while (path.canMoveRight()) { + path = path.moveRight(); + paths.add(path); + } + return paths; + } + while (path.canMoveLeft()) { + path = path.moveLeft(); + paths.add(path); + } + return paths; + } + + private boolean isPositive(int delta) { + return delta > 0; + } +} diff --git a/src/main/java/view/ConsoleView.java b/src/main/java/view/ConsoleView.java new file mode 100644 index 00000000000..52b957b56e5 --- /dev/null +++ b/src/main/java/view/ConsoleView.java @@ -0,0 +1,74 @@ +package view; + +import chess.Turn; +import chess.board.Column; +import chess.board.Position; +import chess.board.Row; +import chess.piece.Piece; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import view.support.BoardComparator; +import view.support.OutputSupporter; + +public class ConsoleView { + + private static final Scanner SCANNER = new Scanner(System.in); + private final OutputSupporter outputSupporter; + + public ConsoleView(OutputSupporter outputSupporter) { + this.outputSupporter = outputSupporter; + } + + public void printBoard(Map pieces) { + Map filledPieces = outputSupporter.fillBoard(pieces); + List positions = new ArrayList<>(filledPieces.keySet()); + positions.sort(new BoardComparator()); + + StringBuilder builder = new StringBuilder(); + Position printPosition = positions.getFirst(); + builder.append(printPosition.row().getValue()).append(" "); + + for (Position position : positions) { + Piece piece = filledPieces.get(position); + if (printPosition.row() != position.row()) { + builder.append("\n").append(position.row().getValue()).append(" "); + printPosition = position; + } + if (piece == null) { + builder.append("- ").append(" "); + continue; + } + String formattedPiece = outputSupporter.formatPiece(piece); + builder.append(formattedPiece).append(" "); + } + builder.append("\n").append(" A B C D E F G H").append("\n\n"); + System.out.println(builder); + } + + public void printTurn(Turn currentTurn) { + String formattedTurn = outputSupporter.formatTurn(currentTurn); + System.out.printf("%s 차례입니다\n", formattedTurn); + } + + public Position requestStartPoint() { + System.out.println("시작 좌표를 입력해주세요(ex)x,y"); + String[] inputParts = SCANNER.nextLine().split(","); + Column column = Column.valueOf(inputParts[0]); + Row row = Row.findByValue(Integer.parseInt(inputParts[1])); + return new Position(column, row); + } + + public Position requestDestination() { + System.out.println("목표 좌표를 입력해주세요(ex)x,y"); + String[] inputParts = SCANNER.nextLine().split(","); + Column column = Column.valueOf(inputParts[0]); + Row row = Row.findByValue(Integer.parseInt(inputParts[1])); + return new Position(column, row); + } + + public void printMessage(String message) { + System.out.println(message); + } +} diff --git a/src/main/java/view/support/BoardComparator.java b/src/main/java/view/support/BoardComparator.java new file mode 100644 index 00000000000..a70d2ff61cf --- /dev/null +++ b/src/main/java/view/support/BoardComparator.java @@ -0,0 +1,15 @@ +package view.support; + +import chess.board.Position; +import java.util.Comparator; + +public class BoardComparator implements Comparator { + + @Override + public int compare(Position o1, Position o2) { + if (o1.row().ordinal() == o2.row().ordinal()) { + return Integer.compare(o1.column().ordinal(), o2.column().ordinal()); + } + return Integer.compare(o1.row().ordinal(), o2.row().ordinal()); + } +} diff --git a/src/main/java/view/support/OutputSupporter.java b/src/main/java/view/support/OutputSupporter.java new file mode 100644 index 00000000000..a57d217ae85 --- /dev/null +++ b/src/main/java/view/support/OutputSupporter.java @@ -0,0 +1,58 @@ +package view.support; + +import static chess.Color.*; +import static chess.piece.PieceType.*; + +import chess.Color; +import chess.Turn; +import chess.board.Column; +import chess.board.Position; +import chess.board.Row; +import chess.piece.Piece; +import chess.piece.PieceType; +import java.util.HashMap; +import java.util.Map; + +public class OutputSupporter { + + private static final Map EMPTY_PIECES = new HashMap<>(); + private static final Map PIECE_TYPE_FORMATTER = new HashMap<>(); + private static final Map COLOR_FORMATTER = new HashMap<>(); + + static { + PIECE_TYPE_FORMATTER.put(KING, "K"); + PIECE_TYPE_FORMATTER.put(ROOK, "R"); + PIECE_TYPE_FORMATTER.put(BISHOP, "B"); + PIECE_TYPE_FORMATTER.put(QUEEN, "Q"); + PIECE_TYPE_FORMATTER.put(PAWN, "P"); + PIECE_TYPE_FORMATTER.put(KNIGHT, "N"); + } + + static { + COLOR_FORMATTER.put(BLACK, "B"); + COLOR_FORMATTER.put(WHITE, "W"); + } + + public Map fillBoard(Map boardPieces) { + for (Row row : Row.values()) { + for (Column column : Column.values()) { + EMPTY_PIECES.put(new Position(row, column), null); + } + } + EMPTY_PIECES.putAll(boardPieces); + return EMPTY_PIECES; + } + + public String formatPiece(Piece piece) { + PieceType pieceType = piece.getPieceType(); + Color color = piece.getColor(); + return COLOR_FORMATTER.get(color) + PIECE_TYPE_FORMATTER.get(pieceType); + } + + public String formatTurn(Turn currentTurn) { + if (currentTurn == Turn.BLACK_TURN) { + return "흑색"; + } + return "백색"; + } +} 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/ColumnTest.java b/src/test/java/chess/ColumnTest.java new file mode 100644 index 00000000000..235694f5d9d --- /dev/null +++ b/src/test/java/chess/ColumnTest.java @@ -0,0 +1,96 @@ +package chess; + +import chess.board.Column; +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/Fixtures.java b/src/test/java/chess/Fixtures.java new file mode 100644 index 00000000000..3abe944a5df --- /dev/null +++ b/src/test/java/chess/Fixtures.java @@ -0,0 +1,84 @@ +package chess; + +import chess.board.Column; +import chess.board.Position; +import chess.board.Row; + +@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/PositionTest.java b/src/test/java/chess/PositionTest.java new file mode 100644 index 00000000000..487bcf3fc9f --- /dev/null +++ b/src/test/java/chess/PositionTest.java @@ -0,0 +1,394 @@ +package chess; + +import chess.board.Movement; +import chess.board.Position; +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 java.util.stream.Stream; + +import static chess.Fixtures.A1; +import static chess.Fixtures.A2; +import static chess.Fixtures.A3; +import static chess.Fixtures.A6; +import static chess.Fixtures.A7; +import static chess.Fixtures.A8; +import static chess.Fixtures.B1; +import static chess.Fixtures.B2; +import static chess.Fixtures.B3; +import static chess.Fixtures.B7; +import static chess.Fixtures.B8; +import static chess.Fixtures.C1; +import static chess.Fixtures.F1; +import static chess.Fixtures.F8; +import static chess.Fixtures.G1; +import static chess.Fixtures.G2; +import static chess.Fixtures.G6; +import static chess.Fixtures.G7; +import static chess.Fixtures.G8; +import static chess.Fixtures.H1; +import static chess.Fixtures.H2; +import static chess.Fixtures.H7; +import static chess.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/RowTest.java b/src/test/java/chess/RowTest.java new file mode 100644 index 00000000000..938dc7c1fd0 --- /dev/null +++ b/src/test/java/chess/RowTest.java @@ -0,0 +1,96 @@ +package chess; + +import chess.board.Row; +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); + } +}