diff --git a/README.md b/README.md index 8102f91c870..0b927ebdfd2 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,42 @@ ## 우아한테크코스 코드리뷰 - [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) + +## 구현할 기능 목록 +### 게임 진행 +- 백이 먼저 시작한다. +- move 기물 시작위치 목적지 로 움직일 수 있다. + - ex) move P a2 a3 + ![img.png](img.png) +### 게임 종료 +- 자신의 킹이 상대의 기물 한개 이상에 의해 공격받고 있을 때 그 상태를 "체크"라고 한다. +- 만약 체크를 막거나 피할 수 없는 상황이 된다면 자신은 체크메이트당한 것이며 경기는 체크메이트를 당한 선수의 패배로 끝난다. + +### 기물 행마법 / 기물 잡는 법 +#### 킹 (K, k) +- 상하좌우, 대각선 방향으로 각각 1칸씩만 움직일 수 있다. 1 1경기마다 각 선수는 단 1번 "캐슬링"이라는 특별 행마를 할 수 있다. +#### 퀸 (Q, q) +- 상하좌우, 대각선 방향으로 기물이 없는 칸에 한해서 칸수의 제한 없이 움직일 수 있다. +#### 룩 (R, r) +- 상하좌우 방향으로 기물이 없는 칸에 한해서 칸수의 제한 없이 움직일 수 있다. 룩은 캐슬링을 할 때 따라 움직인다. +#### 비숍 (B, b) +- 대각선 방향으로 기물이 없는 칸에 한해서 칸수의 제한 없이 움직일 수 있다. +#### 나이트 (N, n) +- 수직 방향으로 한칸 움직인 후 수평 방향으로 두칸 움직이거나 수직 방향으로 두칸 움직인 후 수평 방향으로 한칸 움직인다. +- 나이트는 유일하게 다른 기물을 넘어다닐 수 있다. +#### 폰 (P, p) +- 행마법과 기물을 잡는 법이 다른 유일한 기물이다. +- 바로 앞의 칸이 비어 있다면 앞으로 한 칸 전진할 수 있다.(바로 앞에 상대의 기물이 있어도 잡을 수 없다.) +- 경기중 단 한번도 움직이지 않은 폰은 바로 앞의 두칸이 비어 있을 때 두칸 전진할 수 있다.(한칸만 전진해도 된다.) +- 폰은 앞쪽으로만 움직이며 절대 뒤쪽으로 행마하지 않는다. +- 폰은 대각선 방향으로 바로 앞에 위치한 기물을 잡을 수 있다.(대각선 방향으로 바로 앞에 위치한 칸이 비어 있더라도 그곳으로 전진할 수 없다.) + +#### 기타 +- 폰을 제외한 기물들은 행마법과 기물 잡는 법이 동일하다. +- 앙파상은 고려하지 않는다. + + +### 고려할 수 있는 규칙들 (현재 적용 X) +- 캐슬링 +- 프로모션 +- 앙파상 \ No newline at end of file diff --git a/img.png b/img.png new file mode 100644 index 00000000000..ad6824ae94c Binary files /dev/null and b/img.png differ diff --git a/src/main/java/chess/Application.java b/src/main/java/chess/Application.java new file mode 100644 index 00000000000..117e6ef42e8 --- /dev/null +++ b/src/main/java/chess/Application.java @@ -0,0 +1,16 @@ +package chess; + +import chess.controller.ChessController; +import chess.domain.ChessBoard; +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.play(); + } +} diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java new file mode 100644 index 00000000000..cde22225997 --- /dev/null +++ b/src/main/java/chess/controller/ChessController.java @@ -0,0 +1,66 @@ +package chess.controller; + +import chess.domain.ChessBoard; +import chess.domain.Column; +import chess.domain.Position; +import chess.domain.Row; +import chess.domain.game.ChessGame; +import chess.domain.game.WhiteTurn; +import chess.view.InputView; +import chess.view.OutputView; + +public class ChessController { + private final InputView inputView; + private final OutputView outputView; + + public ChessController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void play() { + ChessBoard board = ChessBoard.createInitialBoard(); + outputView.displayBoard(board); + + ChessGame game = new ChessGame(new WhiteTurn(board)); + + while (!game.isFinished()) { + playTurn(game, board); + } + + } + + private void playTurn(ChessGame game, ChessBoard board) { + progressWithReturn(() -> { + String[] command = inputView.readMoveCommand(game.getTurnColor().getTeamName()); + String startInput = command[0]; + String targetInput = command[1]; + + Position start = stringToPosition(startInput); + Position target = stringToPosition(targetInput); + + game.move(start, target); + outputView.displayBoard(board); + }); + } + + private void progressWithReturn(Runnable runnable) { + while(true) { + try { + runnable.run(); + } catch (Exception e) { + System.out.println("[Error] " + e.getMessage()); + } + } + } + + private Position stringToPosition(String input) { + char col = input.charAt(0); + Column column = Column.from(col); + char rowChar = input.charAt(1); + int rowNum = Integer.parseInt(String.valueOf(rowChar)); + Row row = Row.from(rowNum); + + return new Position(row, column); + } +} diff --git a/src/main/java/chess/domain/ChessBoard.java b/src/main/java/chess/domain/ChessBoard.java new file mode 100644 index 00000000000..e50845f4341 --- /dev/null +++ b/src/main/java/chess/domain/ChessBoard.java @@ -0,0 +1,91 @@ +package chess.domain; + +import chess.domain.piece.Bishop; +import chess.domain.piece.EmptyPiece; +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 java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ChessBoard { + private final Map board; + + public ChessBoard(Map board) { + this.board = board; + } + + public static ChessBoard createInitialBoard() { + Map piecePositionInfo = new HashMap<>(); + for (Column col : Column.values()) { + piecePositionInfo.put(new Position(Row.TWO, col), new Pawn(TeamColor.WHITE)); + } + piecePositionInfo.put(new Position(Row.ONE, Column.A), new Rook(TeamColor.WHITE)); + piecePositionInfo.put(new Position(Row.ONE, Column.H), new Rook(TeamColor.WHITE)); + + piecePositionInfo.put(new Position(Row.ONE, Column.B), new Knight(TeamColor.WHITE)); + piecePositionInfo.put(new Position(Row.ONE, Column.G), new Knight(TeamColor.WHITE)); + + piecePositionInfo.put(new Position(Row.ONE, Column.C), new Bishop(TeamColor.WHITE)); + piecePositionInfo.put(new Position(Row.ONE, Column.F), new Bishop(TeamColor.WHITE)); + + piecePositionInfo.put(new Position(Row.ONE, Column.D), new Queen(TeamColor.WHITE)); + + piecePositionInfo.put(new Position(Row.ONE, Column.E), new King(TeamColor.WHITE)); + + // 흑 팀 + for (Column col : Column.values()) { + piecePositionInfo.put(new Position(Row.SEVEN, col), new Pawn(TeamColor.BLACK)); + } + + piecePositionInfo.put(new Position(Row.EIGHT, Column.A), new Rook(TeamColor.BLACK)); + piecePositionInfo.put(new Position(Row.EIGHT, Column.H), new Rook(TeamColor.BLACK)); + + piecePositionInfo.put(new Position(Row.EIGHT, Column.B), new Knight(TeamColor.BLACK)); + piecePositionInfo.put(new Position(Row.EIGHT, Column.G), new Knight(TeamColor.BLACK)); + + piecePositionInfo.put(new Position(Row.EIGHT, Column.C), new Bishop(TeamColor.BLACK)); + piecePositionInfo.put(new Position(Row.EIGHT, Column.F), new Bishop(TeamColor.BLACK)); + + piecePositionInfo.put(new Position(Row.EIGHT, Column.D), new Queen(TeamColor.BLACK)); + + piecePositionInfo.put(new Position(Row.EIGHT, Column.E), new King(TeamColor.BLACK)); + + return new ChessBoard(piecePositionInfo); + } + + + public Piece findPieceBy(Position position) { + return board.getOrDefault(position, EmptyPiece.getInstance()); + } + + + public void move(Position start, Position target) { + Piece piece = findPieceBy(start); + + boolean availablePath = piece.availablePath(start, target); + if (!availablePath) { + throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + } + + List routes = piece.findAllRouteToTarget(start, target); + List piecesOnRoute = routes.stream() + .map(this::findPieceBy) + .toList(); + + boolean canMove = piece.canMove(piecesOnRoute, start, target); + + if (!canMove) { + throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + } + + board.remove(start); + board.put(target, piece); + + piece.incrementMoveCount(); + } +} diff --git a/src/main/java/chess/Color.java b/src/main/java/chess/domain/Color.java similarity index 95% rename from src/main/java/chess/Color.java rename to src/main/java/chess/domain/Color.java index 55cd020b681..1a29e732d1a 100644 --- a/src/main/java/chess/Color.java +++ b/src/main/java/chess/domain/Color.java @@ -1,4 +1,4 @@ -package chess; +package chess.domain; public enum Color { diff --git a/src/main/java/chess/Column.java b/src/main/java/chess/domain/Column.java similarity index 54% rename from src/main/java/chess/Column.java rename to src/main/java/chess/domain/Column.java index b64b4dc77a3..91273a0ed98 100644 --- a/src/main/java/chess/Column.java +++ b/src/main/java/chess/domain/Column.java @@ -1,15 +1,39 @@ -package chess; +package chess.domain; + +import java.util.Arrays; public enum Column { - A, - B, - C, - D, - E, - F, - G, - H; + A(1, 'a'), + B(2, 'b'), + C(3, 'c'), + D(4, 'd'), + E(5, 'e'), + F(6, 'f'), + G(7, 'g'), + H(8, 'h'); + + private final int value; + private final char charValue; + + Column(int value, char charValue) { + this.value = value; + this.charValue = charValue; + } + + public static Column from(int value) { + return Arrays.stream(Column.values()) + .filter(col -> col.value == value) + .findAny() + .orElseThrow(); + } + + public static Column from(char charValue) { + return Arrays.stream(Column.values()) + .filter(col -> col.charValue == charValue) + .findAny() + .orElseThrow(); + } public boolean isFarLeft() { return ordinal() == 0; @@ -50,4 +74,9 @@ public Column moveRight(final int step) { throw new IllegalStateException("움직일 수 없는 위치입니다."); } + + public int intValue() { + return value; + }; + } diff --git a/src/main/java/chess/Movement.java b/src/main/java/chess/domain/Movement.java similarity index 97% rename from src/main/java/chess/Movement.java rename to src/main/java/chess/domain/Movement.java index e57c6e91bb9..07b5d153f67 100644 --- a/src/main/java/chess/Movement.java +++ b/src/main/java/chess/domain/Movement.java @@ -1,4 +1,4 @@ -package chess; +package chess.domain; public enum Movement { UP(0, 1), diff --git a/src/main/java/chess/Position.java b/src/main/java/chess/domain/Position.java similarity index 83% rename from src/main/java/chess/Position.java rename to src/main/java/chess/domain/Position.java index 3ebeb0ea185..f77b89e3046 100644 --- a/src/main/java/chess/Position.java +++ b/src/main/java/chess/domain/Position.java @@ -1,4 +1,4 @@ -package chess; +package chess.domain; public record Position( Column column, @@ -167,4 +167,31 @@ public Position moveHorizontal(final int step) { } return this; } + + public boolean onStraight(Position other) { + return this.row() == other.row() || this.column == other.column; + } + + public boolean onSameRow(Position other) { + return this.row == other.row(); + } + + public boolean onSameColumn(Position other) { + return this.column == other.column; + } + + public boolean onDiagonal(Position other) { + int rowDiff = Math.abs(this.row().intValue() - other.row().intValue()); + int colDiff = Math.abs(this.column.intValue() - other.column.intValue()); + + return rowDiff == colDiff; + } + + public int rowValue() { + return this.row().intValue(); + } + + public int colValue() { + return this.column.intValue(); + } } diff --git a/src/main/java/chess/Row.java b/src/main/java/chess/domain/Row.java similarity index 65% rename from src/main/java/chess/Row.java rename to src/main/java/chess/domain/Row.java index 126ed048daa..38f0651af76 100644 --- a/src/main/java/chess/Row.java +++ b/src/main/java/chess/domain/Row.java @@ -1,15 +1,30 @@ -package chess; +package chess.domain; + +import java.util.Arrays; public enum Row { - EIGHT, - SEVEN, - SIX, - FIVE, - FOUR, - THREE, - TWO, - ONE; + 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 static Row from(int value) { + return Arrays.stream(Row.values()) + .filter(row -> row.value == value) + .findAny() + .orElseThrow(); + } public boolean isTop() { return ordinal() == 0; @@ -50,4 +65,8 @@ public Row moveDown(final int step) { throw new IllegalStateException("움직일 수 없는 위치입니다."); } + + public int intValue() { + return value; + }; } diff --git a/src/main/java/chess/domain/TeamColor.java b/src/main/java/chess/domain/TeamColor.java new file mode 100644 index 00000000000..db65f29adcc --- /dev/null +++ b/src/main/java/chess/domain/TeamColor.java @@ -0,0 +1,18 @@ +package chess.domain; + +public enum TeamColor { + WHITE("백팀"), + BLACK("흑팀"), + NONE("X"), + ; + + private final String teamName; + + TeamColor(String teamName) { + this.teamName = teamName; + } + + public String getTeamName() { + return teamName; + } +} diff --git a/src/main/java/chess/domain/game/BlackTurn.java b/src/main/java/chess/domain/game/BlackTurn.java new file mode 100644 index 00000000000..4cb19c2aba5 --- /dev/null +++ b/src/main/java/chess/domain/game/BlackTurn.java @@ -0,0 +1,42 @@ +package chess.domain.game; + +import chess.domain.ChessBoard; +import chess.domain.Position; +import chess.domain.TeamColor; +import chess.domain.piece.Piece; + +public class BlackTurn implements Turn{ + private final ChessBoard board; + private final TeamColor turnColor; + + public BlackTurn(ChessBoard board) { + this.board = board; + this.turnColor = TeamColor.BLACK; + } + + @Override + public Turn movePiece(Position start, Position target) { + Piece startPiece = board.findPieceBy(start); + if(startPiece.isOtherTeamColor(turnColor)) { + throw new IllegalArgumentException("자신의 기물이 아닙니다."); + } + + Piece targetPiece = board.findPieceBy(target); + board.move(start, target); + if(targetPiece.isKing()) { + return new Finished(turnColor); + } + + return new WhiteTurn(board); + } + + @Override + public TeamColor getTeamColor() { + return turnColor; + } + + @Override + public boolean isFinished() { + return false; + } +} diff --git a/src/main/java/chess/domain/game/ChessGame.java b/src/main/java/chess/domain/game/ChessGame.java new file mode 100644 index 00000000000..6af135e44c1 --- /dev/null +++ b/src/main/java/chess/domain/game/ChessGame.java @@ -0,0 +1,25 @@ +package chess.domain.game; + +import chess.domain.Position; +import chess.domain.TeamColor; + + +public class ChessGame { + public ChessGame(Turn turn) { + this.turn = turn; + } + + private Turn turn; + + public void move( Position start, Position target) { + this.turn = turn.movePiece(start, target); + } + + public TeamColor getTurnColor() { + return turn.getTeamColor(); + } + + public boolean isFinished() { + return turn.isFinished(); + } +} diff --git a/src/main/java/chess/domain/game/Finished.java b/src/main/java/chess/domain/game/Finished.java new file mode 100644 index 00000000000..825ad7081e5 --- /dev/null +++ b/src/main/java/chess/domain/game/Finished.java @@ -0,0 +1,28 @@ +package chess.domain.game; + +import chess.domain.Position; +import chess.domain.TeamColor; + +public class Finished implements Turn{ + public Finished(TeamColor winnerColor) { + this.winnerColor = winnerColor; + } + + private final TeamColor winnerColor; + + + @Override + public Turn movePiece(Position start, Position target) { + return null; + } + + @Override + public TeamColor getTeamColor() { + return null; + } + + @Override + public boolean isFinished() { + return true; + } +} diff --git a/src/main/java/chess/domain/game/Turn.java b/src/main/java/chess/domain/game/Turn.java new file mode 100644 index 00000000000..adb735403fa --- /dev/null +++ b/src/main/java/chess/domain/game/Turn.java @@ -0,0 +1,12 @@ +package chess.domain.game; + +import chess.domain.Position; +import chess.domain.TeamColor; + +public interface Turn { + Turn movePiece(Position start, Position target); + + TeamColor getTeamColor(); + + boolean isFinished(); +} diff --git a/src/main/java/chess/domain/game/WhiteTurn.java b/src/main/java/chess/domain/game/WhiteTurn.java new file mode 100644 index 00000000000..77faeba6729 --- /dev/null +++ b/src/main/java/chess/domain/game/WhiteTurn.java @@ -0,0 +1,41 @@ +package chess.domain.game; + +import chess.domain.ChessBoard; +import chess.domain.Position; +import chess.domain.TeamColor; +import chess.domain.piece.Piece; + +public class WhiteTurn implements Turn{ + private final ChessBoard board; + private final TeamColor turnColor; + + public WhiteTurn(ChessBoard board) { + this.board = board; + this.turnColor = TeamColor.WHITE; + } + + @Override + public Turn movePiece(Position start, Position target) { + Piece startPiece = board.findPieceBy(start); + if(startPiece.isOtherTeamColor(turnColor)) { + throw new IllegalArgumentException("자신의 기물이 아닙니다."); + } + + Piece targetPiece = board.findPieceBy(target); + board.move(start, target); + if(targetPiece.isKing()) { + return new Finished(turnColor); + } + + return new BlackTurn(board); + } + @Override + public TeamColor getTeamColor() { + return turnColor; + } + + @Override + public boolean isFinished() { + return false; + } +} 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..ae63cd23af1 --- /dev/null +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -0,0 +1,52 @@ +package chess.domain.piece; + +import chess.domain.Position; +import chess.domain.TeamColor; +import java.util.ArrayList; +import java.util.List; + +public class Bishop extends Piece{ + + public Bishop(TeamColor teamColor) { + super(teamColor, PieceType.BISHOP); + } + + @Override + public boolean availablePath(Position start, Position target) { + return start.onDiagonal(target); + } + + @Override + public List findAllRouteToTarget(Position start, Position target) { + List positions = new ArrayList<>(); + + Position current = start; + int rowStep = (target.rowValue() - start.rowValue()) / Math.abs(target.rowValue() - start.rowValue()); + int colStep = (target.colValue() - start.colValue()) / Math.abs(target.colValue() - start.colValue()); + + while (!current.equals(target)) { + current = current.moveVertical(rowStep).moveHorizontal(colStep); + positions.add(current); + } + + return positions; + } + + @Override + public boolean canMove(List piecesOnRoute, Position start, Position target) { + if(countPieceOnRoute(piecesOnRoute) != 0){ + return false; + } + return piecesOnRoute.getLast().isEmptyPiece() || this.isOtherTeam(piecesOnRoute.getLast()); + } + + @Override + public boolean isEmptyPiece() { + return false; + } + + @Override + public boolean isKing() { + return false; + } +} diff --git a/src/main/java/chess/domain/piece/EmptyPiece.java b/src/main/java/chess/domain/piece/EmptyPiece.java new file mode 100644 index 00000000000..8adee08f1a6 --- /dev/null +++ b/src/main/java/chess/domain/piece/EmptyPiece.java @@ -0,0 +1,42 @@ +package chess.domain.piece; + +import chess.domain.Position; +import chess.domain.TeamColor; +import java.util.List; + +public class EmptyPiece extends Piece{ + private final static EmptyPiece INSTANCE = new EmptyPiece(); + + private EmptyPiece() { + super(TeamColor.NONE, PieceType.NONE); + } + + public static EmptyPiece getInstance() { + return INSTANCE; + } + + @Override + public boolean availablePath(Position start, Position target) { + return false; + } + + @Override + public List findAllRouteToTarget(Position start, Position target) { + return null; + } + + @Override + public boolean canMove(List piecesOnRoute, Position start, Position target) { + return false; + } + + @Override + public boolean isEmptyPiece() { + return true; + } + + @Override + public boolean isKing() { + return false; + } +} 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..8df5acb8d88 --- /dev/null +++ b/src/main/java/chess/domain/piece/King.java @@ -0,0 +1,59 @@ +package chess.domain.piece; + +import chess.domain.Movement; +import chess.domain.Position; +import chess.domain.TeamColor; +import java.util.List; + +public class King extends Piece{ + private final static List kingMovement = + List.of(Movement.UP, + Movement.DOWN, + Movement.LEFT, + Movement.RIGHT, + Movement.LEFT_UP, + Movement.RIGHT_UP, + Movement.LEFT_DOWN, + Movement.RIGHT_DOWN); + + private final List availableMovement; + + public King(TeamColor teamColor) { + super(teamColor, PieceType.KING); + this.availableMovement = kingMovement; + } + + public boolean availablePath(Position start, Position target) { + for (Movement movement : availableMovement) { + if(!start.canMove(movement)) { + continue; + } + + Position moved = start.move(movement); + if(moved.equals(target)) { + return true; + } + } + return false; + } + + @Override + public List findAllRouteToTarget(Position start, Position target) { + return List.of(target); + } + + @Override + public boolean canMove(List piecesOnRoute, Position start, Position target) { + return piecesOnRoute.getLast().isEmptyPiece() || this.isOtherTeam(piecesOnRoute.getLast()); + } + + @Override + public boolean isEmptyPiece() { + return false; + } + + @Override + public boolean isKing() { + return true; + } +} diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java new file mode 100644 index 00000000000..f1f25eefe63 --- /dev/null +++ b/src/main/java/chess/domain/piece/Knight.java @@ -0,0 +1,75 @@ +package chess.domain.piece; + +import static chess.domain.Movement.DOWN_DOWN_LEFT; +import static chess.domain.Movement.DOWN_DOWN_RIGHT; +import static chess.domain.Movement.LEFT_LEFT_DOWN; +import static chess.domain.Movement.LEFT_LEFT_UP; +import static chess.domain.Movement.RIGHT_RIGHT_DOWN; +import static chess.domain.Movement.RIGHT_RIGHT_UP; +import static chess.domain.Movement.UP_UP_LEFT; +import static chess.domain.Movement.UP_UP_RIGHT; + +import chess.domain.Column; +import chess.domain.Movement; +import chess.domain.Position; +import chess.domain.Row; +import chess.domain.TeamColor; +import java.util.List; + +public class Knight extends Piece{ + private final static List knightMovement = + List.of(LEFT_LEFT_UP, + LEFT_LEFT_DOWN, + RIGHT_RIGHT_UP, + RIGHT_RIGHT_DOWN, + DOWN_DOWN_LEFT, + DOWN_DOWN_RIGHT, + UP_UP_LEFT, + UP_UP_RIGHT + ); + + private final List availableMovement; + + public Knight(TeamColor teamColor) { + super(teamColor, PieceType.KNIGHT); + this.availableMovement = knightMovement; + } + + public boolean availablePath(Position start, Position target) { + for (Movement movement : availableMovement) { + if(!start.canMove(movement)) { + continue; + } + + Position moved = start.move(movement); + if(moved.equals(target)) { + return true; + } + } + return false; + } + + @Override + public List findAllRouteToTarget(Position start, Position target) { + Row row = Row.from((start.rowValue() + target.rowValue()) / 2); + Column column = Column.from((start.colValue() + target.colValue()) / 2); + + Position position = new Position(row, column); + return List.of(position, target); + } + + @Override + public boolean canMove(List piecesOnRoute, Position start, Position target) { + return piecesOnRoute.getLast().isEmptyPiece() || this.isOtherTeam(piecesOnRoute.getLast()); + } + + @Override + public boolean isEmptyPiece() { + return false; + } + + @Override + public boolean isKing() { + return false; + } +} 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..97b920a239b --- /dev/null +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -0,0 +1,86 @@ +package chess.domain.piece; + +import chess.domain.Movement; +import chess.domain.Position; +import chess.domain.Row; +import chess.domain.TeamColor; +import java.util.List; + +public class Pawn extends Piece { + private final static Movement pawnMovement = Movement.UP; + private final static List pawnTakeMovements = + List.of(Movement.LEFT_UP, Movement.RIGHT_UP); + + public Pawn(TeamColor teamColor) { + super(teamColor, PieceType.PAWN); + } + + + @Override + public boolean availablePath(Position start, Position target) { + return true; + } + + @Override + public List findAllRouteToTarget(Position start, Position target) { + return List.of(target); + } + + @Override + public boolean canMove(List piecesOnRoute, Position start, Position target) { + + int direction = (teamColor == TeamColor.WHITE) ? 1 : -1; + + // 두 칸 전진 가능 + if (target.rowValue() - start.rowValue() == 2 * direction) { + if (!start.canMoveUp(2 * direction)) { + return false; + } + if(moveCount != 0) { + throw new IllegalArgumentException("폰은 처음에만 두 칸을 이동할 수 있습니다."); + } + Position moved = start.moveUp(2 * direction); + return moved.equals(target); + } + + if(piecesOnRoute.size() != 1) { + return false; + } + // 한 칸 전진 가능 + if (piecesOnRoute.getLast().isEmptyPiece()) { + if (!start.canMoveUp(1 * direction)) { + return false; + } + Position moved = start.moveUp(1 * direction); + return moved.equals(target); + } + + // 적이 있을 때 + for (Movement takeMovement : pawnTakeMovements) { + if (!this.isOtherTeam(piecesOnRoute.getLast())) { + return false; + } + if (!start.canMove(takeMovement)) { + continue; + } + + Position moved = start.move(takeMovement); + if (moved.equals(target)) { + return true; + } + } + return false; + } + + @Override + public boolean isEmptyPiece() { + return false; + } + + @Override + public boolean isKing() { + return false; + } + + +} diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java new file mode 100644 index 00000000000..0764368385a --- /dev/null +++ b/src/main/java/chess/domain/piece/Piece.java @@ -0,0 +1,52 @@ +package chess.domain.piece; + +import chess.domain.Position; +import chess.domain.TeamColor; +import java.util.List; + +public abstract class Piece { + final TeamColor teamColor; + final PieceType pieceType; + int moveCount; + + Piece(TeamColor teamColor, PieceType pieceType) { + this.teamColor = teamColor; + this.pieceType = pieceType; + } + + public abstract boolean availablePath(Position start, Position target); + + public abstract List findAllRouteToTarget(Position start, Position target); + + public abstract boolean canMove(List piecesOnRoute, Position start, Position target); + + public abstract boolean isEmptyPiece(); + + public boolean isOtherTeam(Piece other) { + return this.teamColor != other.teamColor; + } + + public PieceType getPieceType() { + return pieceType; + } + + public TeamColor getTeamColor() { + return teamColor; + } + + public void incrementMoveCount() { + moveCount++; + } + + public boolean isOtherTeamColor(TeamColor teamColor) { + return this.teamColor != teamColor; + } + + public int countPieceOnRoute(List pieces) { + return (int) pieces.stream() + .filter(piece -> !piece.isEmptyPiece()) + .count(); + } + + public abstract boolean isKing(); +} 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..6aff483fdfe --- /dev/null +++ b/src/main/java/chess/domain/piece/PieceType.java @@ -0,0 +1,14 @@ +package chess.domain.piece; + +public enum PieceType { + PAWN, + BISHOP, + KING, + KNIGHT, + QUEEN, + ROOK, + HORSE, + NONE, + ; + +} 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..0d26633eeca --- /dev/null +++ b/src/main/java/chess/domain/piece/Queen.java @@ -0,0 +1,69 @@ +package chess.domain.piece; + +import chess.domain.Position; +import chess.domain.TeamColor; +import java.util.ArrayList; +import java.util.List; + +public class Queen extends Piece { + + public Queen(TeamColor teamColor) { + super(teamColor, PieceType.QUEEN); + } + + @Override + public boolean availablePath(Position start, Position target) { + return start.onStraight(target) || start.onDiagonal(target); + } + + @Override + public List findAllRouteToTarget(Position start, Position target) { + List positions = new ArrayList<>(); + Position current = start; + + if (start.onSameColumn(target)) { + int oneStep = (target.rowValue() - start.rowValue()) / Math.abs(target.rowValue() - start.rowValue()); + while (!current.equals(target)) { + current = current.moveVertical(oneStep); + positions.add(current); + } + } + if (start.onSameRow(target)) { + int oneStep = (target.colValue() - start.colValue()) / Math.abs(target.colValue() - start.colValue()); + while (!current.equals(target)) { + current = current.moveHorizontal(oneStep); + positions.add(current); + } + } + if (start.onDiagonal(target)) { + int rowStep = (target.rowValue() - start.rowValue()) / Math.abs(target.rowValue() - start.rowValue()); + int colStep = (target.colValue() - start.colValue()) / Math.abs(target.colValue() - start.colValue()); + + while (!current.equals(target)) { + current = current.moveVertical(rowStep).moveHorizontal(colStep); + positions.add(current); + } + } + + return positions; + } + + @Override + public boolean canMove(List piecesOnRoute, Position start, Position target) { + if(countPieceOnRoute(piecesOnRoute) != 0){ + return false; + } + return piecesOnRoute.getLast().isEmptyPiece() || this.isOtherTeam(piecesOnRoute.getLast()); + } + + @Override + public boolean isEmptyPiece() { + return false; + } + + @Override + public boolean isKing() { + return false; + } + +} diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java new file mode 100644 index 00000000000..348e15ce9f5 --- /dev/null +++ b/src/main/java/chess/domain/piece/Rook.java @@ -0,0 +1,63 @@ +package chess.domain.piece; + +import chess.domain.Position; +import chess.domain.TeamColor; +import java.util.ArrayList; +import java.util.List; + +public class Rook extends Piece { + + public Rook(TeamColor teamColor) { + super(teamColor, PieceType.ROOK); + } + + @Override + public boolean availablePath(Position start, Position target) { + return start.onStraight(target); + } + + @Override + public List findAllRouteToTarget(Position start, Position target) { + List positions = new ArrayList<>(); + + Position current = new Position(start.row(), start.column()); + + while (!current.equals(target)) { + if (start.onSameRow(target)) { + if (current.column().intValue() < target.column().intValue()) { + current = current.moveRight(); + } else { + current = current.moveLeft(); + } + } + if (start.onSameColumn(target)) { + if (current.row().intValue() < target.row().intValue()) { + current = current.moveUp(); + } else { + current = current.moveDown(); + } + } + positions.add(current); + } + return positions; + } + + @Override + public boolean canMove(List piecesOnRoute, Position start, Position target) { + if(countPieceOnRoute(piecesOnRoute) != 0){ + return false; + } + return piecesOnRoute.getLast().isEmptyPiece() || this.isOtherTeam(piecesOnRoute.getLast()); + } + + @Override + public boolean isEmptyPiece() { + return false; + } + + @Override + public boolean isKing() { + return false; + } + +} diff --git a/src/main/java/chess/piece/Bishop.java b/src/main/java/chess/piece/Bishop.java deleted file mode 100644 index b14ab70f981..00000000000 --- a/src/main/java/chess/piece/Bishop.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.piece; - -public class Bishop { - -} diff --git a/src/main/java/chess/piece/King.java b/src/main/java/chess/piece/King.java deleted file mode 100644 index d64210cad13..00000000000 --- a/src/main/java/chess/piece/King.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.piece; - -public class King { - -} diff --git a/src/main/java/chess/piece/Knight.java b/src/main/java/chess/piece/Knight.java deleted file mode 100644 index 2ee7c47a3bc..00000000000 --- a/src/main/java/chess/piece/Knight.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.piece; - -public class Knight { - -} diff --git a/src/main/java/chess/piece/Pawn.java b/src/main/java/chess/piece/Pawn.java deleted file mode 100644 index c8b6cafa51e..00000000000 --- a/src/main/java/chess/piece/Pawn.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.piece; - -public class Pawn { - -} diff --git a/src/main/java/chess/piece/Queen.java b/src/main/java/chess/piece/Queen.java deleted file mode 100644 index 9b547261c4b..00000000000 --- a/src/main/java/chess/piece/Queen.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.piece; - -public class Queen { - -} diff --git a/src/main/java/chess/piece/Rook.java b/src/main/java/chess/piece/Rook.java deleted file mode 100644 index 7ed4d08bf03..00000000000 --- a/src/main/java/chess/piece/Rook.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.piece; - -public class Rook { - -} diff --git a/src/main/java/chess/view/InputView.java b/src/main/java/chess/view/InputView.java new file mode 100644 index 00000000000..f754db84325 --- /dev/null +++ b/src/main/java/chess/view/InputView.java @@ -0,0 +1,19 @@ +package chess.view; + +import chess.domain.TeamColor; +import java.util.List; +import java.util.Scanner; + +public class InputView { + private final Scanner scanner = new Scanner(System.in); + + public String[] readMoveCommand(String teamName) { + System.out.println(teamName + " 차례입니다."); + System.out.println("이동 명령을 입력해주세요."); + System.out.println("형식: 시작위치 목적위치 ex) b1 b3"); + + String input = scanner.nextLine(); + + return input.split(" "); + } +} diff --git a/src/main/java/chess/view/OutputView.java b/src/main/java/chess/view/OutputView.java new file mode 100644 index 00000000000..3188251fbf5 --- /dev/null +++ b/src/main/java/chess/view/OutputView.java @@ -0,0 +1,44 @@ +package chess.view; + +import chess.domain.ChessBoard; +import chess.domain.Column; +import chess.domain.Position; +import chess.domain.Row; +import chess.domain.TeamColor; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; + +public class OutputView { + private static final String defaultColor = "\033[0m"; + + public void displayBoard(ChessBoard board) { + StringBuilder sb = new StringBuilder(); + sb.append("\033[36m" + " a b c d e f g h \n"); + for(Row row: Row.values()) { + sb.append("\033[36m" + row.intValue() + "| "); + for(Column column : Column.values()) { + Piece piece = board.findPieceBy(new Position(row, column)); + sb.append(colorString(piece.getTeamColor())); + PieceType pieceType = piece.getPieceType(); + PieceTypeView pieceTypeView = PieceTypeView.from(pieceType); + sb.append(pieceTypeView.getPieceNameBy(pieceType) + " "); + } + sb.append(System.lineSeparator()); + } + + sb.append("\033[36m" + " a b c d e f g h \n " + defaultColor); + System.out.println(sb); + + } + + public String colorString(TeamColor teamColor) { + if(teamColor == TeamColor.BLACK) { + return "\033[30m"; + } + if(teamColor == TeamColor.WHITE) { + return "\033[97m"; + } + + return "\033[33m"; + } +} diff --git a/src/main/java/chess/view/PieceTypeView.java b/src/main/java/chess/view/PieceTypeView.java new file mode 100644 index 00000000000..e60a03a4249 --- /dev/null +++ b/src/main/java/chess/view/PieceTypeView.java @@ -0,0 +1,38 @@ +package chess.view; + +import chess.domain.piece.PieceType; +import java.util.Arrays; + +public enum PieceTypeView { + PAWN(PieceType.PAWN, "P"), + BISHOP(PieceType.BISHOP, "B"), + KING(PieceType.KING, "K"), + KNIGHT(PieceType.KNIGHT, "N"), + QUEEN(PieceType.QUEEN, "Q"), + ROOK(PieceType.ROOK, "R"), + HORSE(PieceType.HORSE, "H"), + NONE(PieceType.NONE, "."), + ; + + private final PieceType pieceType; + private final String pieceName; + + PieceTypeView(PieceType pieceType, String pieceName) { + this.pieceType = pieceType; + this.pieceName = pieceName; + } + + public static PieceTypeView from(PieceType pieceType) { + return Arrays.stream(PieceTypeView.values()) + .filter(pieceTypeView -> pieceTypeView.pieceType == pieceType) + .findAny() + .orElse(NONE); + } + public String getPieceNameBy(PieceType pieceType) { + return Arrays.stream(PieceTypeView.values()) + .filter(pieceTypeView -> pieceTypeView.pieceType == pieceType) + .findAny() + .orElse(NONE) + .pieceName; + } +} diff --git a/src/test/java/chess/domain/ChessBoardTest.java b/src/test/java/chess/domain/ChessBoardTest.java new file mode 100644 index 00000000000..5091c503094 --- /dev/null +++ b/src/test/java/chess/domain/ChessBoardTest.java @@ -0,0 +1,43 @@ +package chess.domain; + +import static chess.domain.Fixtures.*; +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.piece.EmptyPiece; +import chess.domain.piece.Piece; +import chess.domain.piece.Rook; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ChessBoardTest { + + @DisplayName("체스판 초기화") + @Test + void check_ChessBoardInitialize() { + // given + ChessBoard board = ChessBoard.createInitialBoard(); + + // when + Piece piece = board.findPieceBy(A1); + + // then + assertThat(piece).isInstanceOf(Rook.class); + } + + @DisplayName("체스 움직임") + @Test + void move_onBoard() { + // given + ChessBoard board = ChessBoard.createInitialBoard(); + Piece piece = board.findPieceBy(B1); + + // when + board.move(B1, C3); + + // then + Piece movedPiece = board.findPieceBy(C3); + assertThat(movedPiece).isEqualTo(piece); + assertThat(board.findPieceBy(B1)).isEqualTo(EmptyPiece.getInstance()); + } +} \ No newline at end of file diff --git a/src/test/java/chess/ColumnTest.java b/src/test/java/chess/domain/ColumnTest.java similarity index 98% rename from src/test/java/chess/ColumnTest.java rename to src/test/java/chess/domain/ColumnTest.java index e43523240f7..88abeed5a31 100644 --- a/src/test/java/chess/ColumnTest.java +++ b/src/test/java/chess/domain/ColumnTest.java @@ -1,5 +1,6 @@ -package chess; +package chess.domain; +import chess.domain.Column; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/chess/Fixtures.java b/src/test/java/chess/domain/Fixtures.java similarity index 97% rename from src/test/java/chess/Fixtures.java rename to src/test/java/chess/domain/Fixtures.java index f940ab37137..466fc3f74de 100644 --- a/src/test/java/chess/Fixtures.java +++ b/src/test/java/chess/domain/Fixtures.java @@ -1,4 +1,8 @@ -package chess; +package chess.domain; + +import chess.domain.Column; +import chess.domain.Position; +import chess.domain.Row; @SuppressWarnings("unused") public final class Fixtures { diff --git a/src/test/java/chess/PositionTest.java b/src/test/java/chess/domain/PositionTest.java similarity index 92% rename from src/test/java/chess/PositionTest.java rename to src/test/java/chess/domain/PositionTest.java index 3ad7cc64084..271a33cca78 100644 --- a/src/test/java/chess/PositionTest.java +++ b/src/test/java/chess/domain/PositionTest.java @@ -1,4 +1,4 @@ -package chess; +package chess.domain; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -7,29 +7,29 @@ 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 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; diff --git a/src/test/java/chess/RowTest.java b/src/test/java/chess/domain/RowTest.java similarity index 98% rename from src/test/java/chess/RowTest.java rename to src/test/java/chess/domain/RowTest.java index fcb65485410..7f015059bad 100644 --- a/src/test/java/chess/RowTest.java +++ b/src/test/java/chess/domain/RowTest.java @@ -1,5 +1,6 @@ -package chess; +package chess.domain; +import chess.domain.Row; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; 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..38d415eae17 --- /dev/null +++ b/src/test/java/chess/domain/piece/BishopTest.java @@ -0,0 +1,52 @@ +package chess.domain.piece; + +import static chess.domain.Fixtures.*; +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.Position; +import chess.domain.TeamColor; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class BishopTest { + + @DisplayName("비숍은 대각선으로 갈 수 있다.") + @Test + void Bishop_canMove_diagonal() { + // given + Bishop bishop = new Bishop(TeamColor.BLACK); + + // when + boolean canMove = bishop.availablePath(A1, B2); + + // then + assertThat(canMove).isTrue(); + } + + @DisplayName("비숍은 직선으로 갈 수 없다.") + @Test + void Bishop_cannotMove_Straight() { + // given + Bishop bishop = new Bishop(TeamColor.BLACK); + + // when + boolean canMove = bishop.availablePath(A1, A2); + + // then + assertThat(canMove).isFalse(); + } + + @DisplayName("이동경로 사이에 있는 모든 좌표 반환") + @Test + void Bishop_findAllRoute() { + // given + Bishop bishop = new Bishop(TeamColor.BLACK); + + // when + List routes = bishop.findAllRouteToTarget(A1, D4); + + // then + assertThat(routes).hasSize(3); + } +} \ No newline at end of file diff --git a/src/test/java/chess/domain/piece/KingTest.java b/src/test/java/chess/domain/piece/KingTest.java new file mode 100644 index 00000000000..38066b10b9d --- /dev/null +++ b/src/test/java/chess/domain/piece/KingTest.java @@ -0,0 +1,47 @@ +package chess.domain.piece; + +import static chess.domain.Fixtures.*; +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.Position; +import chess.domain.TeamColor; +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; + +class KingTest { + + //B2에서 킹이 이동가능한 방향 + static Stream provideKingMovesTo() { + return Stream.of(A2, C2, B3, B1, A3, A1, C3, C1); + } + + @DisplayName("킹은 왼쪽 한칸으로 움직일 수 있다.") + @ParameterizedTest + @MethodSource("provideKingMovesTo") + void King_canMove_Left(Position target) { + // given + King king = new King(TeamColor.BLACK); + + // when + boolean canMove = king.availablePath(B2, target); + + // then + assertThat(canMove).isTrue(); + } + + @DisplayName("킹이 이동할 수 없는 칸이면 false") + @Test + void King_cannotMove() { + King king = new King(TeamColor.BLACK); + + // when + boolean canMove = king.availablePath(B2, D2); + + // then + assertThat(canMove).isFalse(); + } + +} \ No newline at end of file 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..fca52cb135e --- /dev/null +++ b/src/test/java/chess/domain/piece/KnightTest.java @@ -0,0 +1,40 @@ +package chess.domain.piece; + +import static chess.domain.Fixtures.*; +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.TeamColor; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class KnightTest { + + @DisplayName("말은 수평한칸, 수직두칸 이동할 수 있다.") + @Test + void Horse_canMoveTest() { + // given + Piece knight = new Knight(TeamColor.BLACK); + + // when + boolean canMove = knight.availablePath(B1, C3); + boolean canMove2 = knight.availablePath(B1, A3); + + // then + assertThat(canMove).isTrue(); + assertThat(canMove2).isTrue(); + } + + @DisplayName("말이 이동불가능한 경로로는 갈 수 없다.") + @Test + void Horse_canMove_False() { + // given + Piece knight = new Knight(TeamColor.BLACK); + + // when + boolean canMove = knight.availablePath(B1, C2); + + // then + assertThat(canMove).isFalse(); + } + +} \ No newline at end of file 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..4b982131d2f --- /dev/null +++ b/src/test/java/chess/domain/piece/PawnTest.java @@ -0,0 +1,83 @@ +package chess.domain.piece; + +import static chess.domain.Fixtures.*; +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.TeamColor; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PawnTest { + + @DisplayName("폰은 앞에 적이 없을 때, 앞으로 한 칸 이동할 수 있다.") + @Test + void Pawn_canMoveUp() { + // given + Pawn pawn = new Pawn(TeamColor.WHITE); + Piece targetPiece = EmptyPiece.getInstance(); + + // when + boolean canMove = pawn.canMove(List.of(targetPiece), A1, A2); + + // then + assertThat(canMove).isTrue(); + } + + @DisplayName("폰은 앞에 적이 있으면, 앞으로 한 칸 이동할 수 없다.") + @Test + void Pawn_cannotMoveUp_whenExistEnemy() { + // given + Pawn pawn = new Pawn(TeamColor.BLACK); + Piece targetPiece = new Pawn(TeamColor.WHITE); + + // when + boolean canMove = pawn.canMove(List.of(targetPiece), A1, A2); + + // then + assertThat(canMove).isFalse(); + } + + @DisplayName("폰은 대각선에 적이 있으면 대각선 한칸 이동할 수 있다.") + @Test + void Pawn_canMoveDiagonal_whenExistEnemy() { + // given + Pawn pawn = new Pawn(TeamColor.BLACK); + Piece targetPiece = new Pawn(TeamColor.WHITE); + + // when + boolean canMove = pawn.canMove(List.of(targetPiece), B1, C2); + + // then + assertThat(canMove).isTrue(); + } + + @DisplayName("폰은 대각선에 적이 있으면 대각선 한칸 이동할 수 있다.") + @Test + void Pawn_canMoveDiagonal_whenExistEnemy2() { + // given + Pawn pawn = new Pawn(TeamColor.BLACK); + Piece targetPiece = new Pawn(TeamColor.WHITE); + + // when + boolean canMove = pawn.canMove(List.of(targetPiece), B1, A2); + + // then + assertThat(canMove).isTrue(); + } + + @DisplayName("폰은 대각선에 적이 없으면 대각선으로는 이동할 수 없다.") + @Test + void Pawn_cannotMoveDiagonal_whenEmptyTarget() { + // given + Pawn pawn = new Pawn(TeamColor.WHITE); + Piece targetPiece = EmptyPiece.getInstance(); + + // when + boolean canMove = pawn.canMove(List.of(targetPiece), B1, A2); + + // then + assertThat(canMove).isFalse(); + } + +} \ No newline at end of file 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..5b3f7af5bd5 --- /dev/null +++ b/src/test/java/chess/domain/piece/QueenTest.java @@ -0,0 +1,66 @@ +package chess.domain.piece; + +import static chess.domain.Fixtures.*; +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.Position; +import chess.domain.TeamColor; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class QueenTest { + + @DisplayName("퀸은 대각선으로 이동 가능") + @Test + void Queen_canMove_diagonal() { + // given + Queen queen = new Queen(TeamColor.BLACK); + + // when + boolean canMove = queen.availablePath(A1, C3); + + // then + assertThat(canMove).isTrue(); + } + + @DisplayName("퀸은 직선으로 이동 가능") + @Test + void Queen_canMove_Straight() { + // given + Queen queen = new Queen(TeamColor.BLACK); + + // when + boolean canMove = queen.availablePath(A1, A3); + + // then + assertThat(canMove).isTrue(); + } + + @DisplayName("퀸은 직선이나 대각선이 아닌 곳으로는 이동 불가능") + @Test + void Queen_cannotMove() { + // given + Queen queen = new Queen(TeamColor.BLACK); + + // when + boolean canMove = queen.availablePath(A1, B3); + + // then + assertThat(canMove).isFalse(); + } + + @DisplayName("퀸의 이동경로에 있는 모든 좌표 반환") + @Test + void Queen_findAllRoute() { + // given + Queen queen = new Queen(TeamColor.BLACK); + + // when + List routes = queen.findAllRouteToTarget(D4, G7); + + // then + assertThat(routes).hasSize(3); + } + +} \ No newline at end of file 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..e85ca60dedb --- /dev/null +++ b/src/test/java/chess/domain/piece/RookTest.java @@ -0,0 +1,69 @@ +package chess.domain.piece; + +import static chess.domain.Fixtures.*; +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.Position; +import chess.domain.TeamColor; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RookTest { + + @DisplayName("룩은 세로로 움직일 수 잇다.") + @Test + void Rook_canMove_Straight_Row() { + // given + Rook rook = new Rook(TeamColor.BLACK); + + // when + boolean canMove = rook.availablePath(A1, A8); + + // then + assertThat(canMove).isTrue(); + } + + @DisplayName("룩은 가로로 움직일 수 잇다.") + @Test + void Rook_canMove_Straight_Column() { + // given + Rook rook = new Rook(TeamColor.BLACK); + + // when + boolean canMove = rook.availablePath(A1, H1); + + // then + assertThat(canMove).isTrue(); + } + + @DisplayName("룩은 직선이 아니면 움직일 수 없다.") + @Test + void Rook_cannotMove_when_notStraight() { + // given + Rook rook = new Rook(TeamColor.BLACK); + + // when + boolean canMove = rook.availablePath(A1, B2); + + // then + assertThat(canMove).isFalse(); + } + + @DisplayName("룩 이동 경로의 위치를 모두 반환한다.") + @Test + void Rook_findAllRoute() { + // given + Rook rook = new Rook(TeamColor.BLACK); + + // when + List routes = rook.findAllRouteToTarget(A1, A4); + + // then + assertThat(routes).hasSize(3); + assertThat(routes.get(0)).isEqualTo(A2); + assertThat(routes.get(1)).isEqualTo(A3); + assertThat(routes.get(2)).isEqualTo(A4); + } + +} \ No newline at end of file