diff --git a/src/main/java/chess/Board.java b/src/main/java/chess/Board.java new file mode 100644 index 0000000000..7002cf0126 --- /dev/null +++ b/src/main/java/chess/Board.java @@ -0,0 +1,66 @@ +package chess; + +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.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class Board { + private final List pieces; + + public Board(List pieces) { + this.pieces = pieces; + } + + public static Board initialize() { + List pieces = new ArrayList<>(); + + for (Color color : Color.validColors()) { + pieces.addAll(Bishop.initialize(color)); + pieces.addAll(King.initialize(color)); + pieces.addAll(Knight.initialize(color)); + pieces.addAll(Pawn.initialize(color)); + pieces.addAll(Queen.initialize(color)); + pieces.addAll(Rook.initialize(color)); + } + return new Board(pieces); + } + + //TODO 존재하는 포지션만 주기 + public Hurdles findHurdlePositions() { + return new Hurdles(pieces.stream() + .map(Piece::getPosition) + .toList()); + } + + public Optional findByPosition(Position position) { + return pieces.stream() + .filter(piece -> piece.getPosition().equals(position)) //TODO 수정 + .findFirst(); + } + + public List getPieces() { + return Collections.unmodifiableList(pieces); + } + + public Piece findByPositionOrThrow(Position position) { + return findByPosition(position) + .orElseThrow(() -> new IllegalArgumentException("해당하는 좌표에 기물이 없습니다.")); + } + + public void move(Piece movingPiece, Position targetPosition) { + movingPiece.moveTo(targetPosition, this); + } + + public void remove(Position targetPosition) { + Piece piece = findByPositionOrThrow(targetPosition); + this.pieces.remove(piece); + } +} diff --git a/src/main/java/chess/Color.java b/src/main/java/chess/Color.java index 55cd020b68..345e4f26a8 100644 --- a/src/main/java/chess/Color.java +++ b/src/main/java/chess/Color.java @@ -1,10 +1,25 @@ package chess; +import java.util.Arrays; +import java.util.List; + public enum Color { - BLACK, - WHITE, - EMPTY; + BLACK(new Position(Column.A, Row.SEVEN)), + WHITE(new Position(Column.A, Row.ONE)), + EMPTY(null); + + private final Position firstPawnPosition; + + Color(Position firstPawnPosition) { + this.firstPawnPosition = firstPawnPosition; + } + + public static List validColors() { + return Arrays.stream(Color.values()) + .filter(color -> !color.isEmpty()) + .toList(); + } public boolean isWhite() { return this == WHITE; @@ -25,4 +40,8 @@ public Color opposite() { default -> EMPTY; }; } + + public Position getFirstPawnPosition() { + return firstPawnPosition; + } } diff --git a/src/main/java/chess/Column.java b/src/main/java/chess/Column.java index b64b4dc77a..e505565466 100644 --- a/src/main/java/chess/Column.java +++ b/src/main/java/chess/Column.java @@ -1,5 +1,7 @@ package chess; +import java.util.Arrays; + public enum Column { A, @@ -11,6 +13,14 @@ public enum Column { G, H; + public static String ofOrdinal(int ordinal) { + return Arrays.stream(Column.values()) + .filter(column -> column.ordinal() == ordinal) + .map(Enum::name) + .findFirst() + .orElseThrow(IllegalStateException::new); + } + public boolean isFarLeft() { return ordinal() == 0; } diff --git a/src/main/java/chess/Hurdles.java b/src/main/java/chess/Hurdles.java new file mode 100644 index 0000000000..1f94a9241c --- /dev/null +++ b/src/main/java/chess/Hurdles.java @@ -0,0 +1,19 @@ +package chess; + +import java.util.List; + +public class Hurdles { + + //TODO 가능하면 교체 + private final List positions; + + public Hurdles(List positions) { + this.positions = positions; + } + + public void checkCrash(Position position) { + if (positions.contains(position)) { + throw new IllegalArgumentException("장애물이 존재합니다."); + } + } +} diff --git a/src/main/java/chess/Position.java b/src/main/java/chess/Position.java index 3ebeb0ea18..86bf8e5a31 100644 --- a/src/main/java/chess/Position.java +++ b/src/main/java/chess/Position.java @@ -148,6 +148,10 @@ public Position move(final Movement movement) { return moveVertical(movement.y()).moveHorizontal(movement.x()); } + public Position move(final int x, final int y) { + return moveVertical(x).moveHorizontal(y); + } + public Position moveVertical(final int step) { if (step > 0) { return moveUp(step); @@ -167,4 +171,30 @@ public Position moveHorizontal(final int step) { } return this; } + + ///// + + public boolean isRowEquals(Position targetPosition) { + return this.row == targetPosition.row; + } + + public boolean isColumnEquals(Position targetPosition) { + return this.column == targetPosition.column; + } + + public int calculateRowGap(Position targetPosition) { + return this.row.ordinal() - targetPosition.row.ordinal(); + } + + public int calculateColumnGap(Position targetPosition) { + return this.column.ordinal() - targetPosition.column.ordinal(); + } + + public int getRow() { + return row.ordinal(); + } + + public int getColumn() { + return column.ordinal(); + } } diff --git a/src/main/java/chess/Row.java b/src/main/java/chess/Row.java index 126ed048da..fc14d0271d 100644 --- a/src/main/java/chess/Row.java +++ b/src/main/java/chess/Row.java @@ -1,8 +1,10 @@ package chess; +import java.util.Arrays; + public enum Row { - EIGHT, + EIGHT, // 0 SEVEN, SIX, FIVE, @@ -11,6 +13,21 @@ public enum Row { TWO, ONE; + public static Row fromNumber(int number) { + return Arrays.stream(Row.values()) + .filter(row -> row.ordinal() == 8 - number) + .findFirst() + .orElseThrow(IllegalStateException::new); + } + + public static String ofOrdinal(int ordinal) { + return Arrays.stream(Row.values()) + .filter(row -> row.ordinal() == ordinal) + .map(row -> ((8 - row.ordinal()) + "")) + .findFirst() + .orElseThrow(IllegalStateException::new); + } + public boolean isTop() { return ordinal() == 0; } diff --git a/src/main/java/chess/game/ChessApplication.java b/src/main/java/chess/game/ChessApplication.java new file mode 100644 index 0000000000..95c0fcf7c0 --- /dev/null +++ b/src/main/java/chess/game/ChessApplication.java @@ -0,0 +1,33 @@ +package chess.game; + +import chess.Board; +import chess.Color; +import chess.Position; +import chess.piece.Piece; +import chess.view.BoardView; +import chess.view.InputView; + +public class ChessApplication { + public static void main(String[] args) { + ChessApplication chessApplication = new ChessApplication(); + + chessApplication.start(); + } + + private void start() { + Board board = Board.initialize(); + + Color color = Color.WHITE; + while (true) { //TODO 승패 조건 추가 + BoardView.printBoard(board); + + Position movingPosition = InputView.readMovingPosition(); + Piece movingPiece = board.findByPositionOrThrow(movingPosition); + Position targetPosition = InputView.readTargetPosition(); + + board.move(movingPiece, targetPosition); + + color = color.opposite(); + } + } +} diff --git a/src/main/java/chess/piece/Bishop.java b/src/main/java/chess/piece/Bishop.java index b14ab70f98..6432ed42b8 100644 --- a/src/main/java/chess/piece/Bishop.java +++ b/src/main/java/chess/piece/Bishop.java @@ -1,5 +1,147 @@ package chess.piece; -public class Bishop { +import chess.Board; +import chess.Color; +import chess.Column; +import chess.Movement; +import chess.Position; +import chess.Row; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +public class Bishop implements Piece{ + private final Color color; + private Position position; + + public Bishop(Color color, Position position) { + this.color = color; + this.position = position; + } + + @Override + public void moveTo(Position targetPosition, Board board) { + Movement movement = findMovement(targetPosition); + int step = calculateStep(targetPosition); + repeatMove(movement, board, step); + } + + private Movement findMovement(Position targetPosition) { + int columnGap = position.calculateColumnGap(targetPosition); + int rowGap = position.calculateRowGap(targetPosition); + validateStraightMove(rowGap, columnGap); + if (rowGap > 0) { + if (columnGap > 0) { + return Movement.LEFT_UP; + } + if (columnGap < 0) { + return Movement.RIGHT_UP; + } + } + if (rowGap < 0) { + if (columnGap > 0) { + return Movement.LEFT_DOWN; + } + if (columnGap < 0) { + return Movement.RIGHT_DOWN; + } + } + throw new IllegalArgumentException("비숍은 대각선 방향으로만 이동 가능합니다."); + } + + private void validateStraightMove(int rowGap, int columnGap) { + if (Math.abs(rowGap) != Math.abs(columnGap)) { + throw new IllegalArgumentException("비숍은 한 방향으로만 이동 가능합니다."); + } + } + + private int calculateStep(Position targetPosition) { + return Math.abs(position.calculateRowGap(targetPosition)); + } + + private void repeatMove(Movement movement, Board board, int step) { + for (int pointer = 0; pointer < step; pointer++) { + if (!this.position.canMove(movement)) { + throw new IllegalArgumentException("보드의 범위를 벗어난 위치입니다."); + } + Position newPosition = this.position.move(movement); + + if (canAttack(step, pointer, board, newPosition)) { + attack(board, newPosition); + } else { + simplyMove(board, newPosition); + } + } + } + + private boolean canAttack(int step, int pointer, Board board, Position newPosition) { + if (pointer == step - 1) { + Optional existingPiece = board.findByPosition(newPosition); + return existingPiece.isPresent() + && existingPiece.get().isEnemyWith(this); + } + return false; + } + + private void simplyMove(Board board, Position newPosition) { + if (board.findByPosition(newPosition).isPresent()) { + throw new IllegalArgumentException("장애물이 존재합니다."); + } + this.position = newPosition; + } + + private void attack(Board board, Position newPosition) { + this.position = newPosition; + board.remove(newPosition); + } + + public static List initialize(Color color) { + Position standard = new Position(Column.C, Row.ONE); + if (color.isBlack()) { + standard = new Position(Column.C, Row.EIGHT); + } + return List.of(new Bishop(color, standard), + new Bishop(color, standard.move(0, 3)) + ); + } + + @Override + public boolean isEnemyWith(Piece piece) { + if (this.isBlack()) { + return !piece.isBlack(); + } + return piece.isBlack(); + } + + @Override + public boolean isBlack() { + return this.color.isBlack(); + } + + @Override + public Position getPosition() { + return this.position; + } + + @Override + public String getName() { + return "비"; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Bishop bishop = (Bishop) o; + return color == bishop.color && Objects.equals(position, bishop.position); + } + + @Override + public int hashCode() { + return Objects.hash(color, position); + } } diff --git a/src/main/java/chess/piece/King.java b/src/main/java/chess/piece/King.java index d64210cad1..de767e3258 100644 --- a/src/main/java/chess/piece/King.java +++ b/src/main/java/chess/piece/King.java @@ -1,5 +1,149 @@ package chess.piece; -public class King { +import chess.Board; +import chess.Color; +import chess.Column; +import chess.Movement; +import chess.Position; +import chess.Row; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +public class King implements Piece { + private final Color color; + private Position position; + + public King(Color color, Position position) { + this.color = color; + this.position = position; + } + + @Override + public void moveTo(Position targetPosition, Board board) { + Movement movement = findMovement(targetPosition); + int step = calculateStep(targetPosition); + if (step != 1) { + throw new IllegalArgumentException("킹은 1칸만 전진할 수 있습니다."); + } + repeatMove(movement, board, 1); + } + + private Movement findMovement(Position targetPosition) { + int columnGap = position.calculateColumnGap(targetPosition); + int rowGap = position.calculateRowGap(targetPosition); + if (rowGap == 0) { + if (columnGap > 0) { + return Movement.LEFT; + } + return Movement.RIGHT; + } + if (columnGap == 0) { + if (rowGap > 0) { + return Movement.UP; + } + return Movement.DOWN; + } + if (rowGap > 0) { + if (columnGap > 0) { + return Movement.LEFT_UP; + } + return Movement.RIGHT_UP; + } + if (columnGap > 0) { + return Movement.LEFT_DOWN; + } + return Movement.RIGHT_DOWN; + } + + private int calculateStep(Position targetPosition) { + int rowGap = position.calculateRowGap(targetPosition); + int columnGap = position.calculateColumnGap(targetPosition); + if (rowGap == 0) { + return Math.abs(columnGap); + } + return Math.abs(rowGap); + } + + private void repeatMove(Movement movement, Board board, int step) { + for (int pointer = 0; pointer < step; pointer++) { + if (!this.position.canMove(movement)) { + throw new IllegalArgumentException("보드의 범위를 벗어난 위치입니다."); + } + Position newPosition = this.position.move(movement); + if (canAttack(step, pointer, board, newPosition)) { + attack(board, newPosition); + } else { + simplyMove(board, newPosition); + } + } + } + + private boolean canAttack(int step, int pointer, Board board, Position newPosition) { + if (pointer == step - 1) { + Optional existingPiece = board.findByPosition(newPosition); + return existingPiece.isPresent() + && existingPiece.get().isEnemyWith(this); + } + return false; + } + + private void attack(Board board, Position newPosition) { + this.position = newPosition; + board.remove(newPosition); + } + + private void simplyMove(Board board, Position newPosition) { + if (board.findByPosition(newPosition).isPresent()) { + throw new IllegalArgumentException("장애물이 존재합니다."); + } + this.position = newPosition; + } + + public static List initialize(Color color) { + if (color.isWhite()) { + return List.of(new King(color, new Position(Column.E, Row.ONE))); + } + return List.of(new King(color, new Position(Column.E, Row.EIGHT))); + } + + @Override + public boolean isEnemyWith(Piece piece) { + if (this.isBlack()) { + return !piece.isBlack(); + } + return piece.isBlack(); + } + + @Override + public boolean isBlack() { + return this.color.isBlack(); + } + + @Override + public String getName() { + return "킹"; + } + + @Override + public Position getPosition() { + return this.position; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + King king = (King) o; + return color == king.color && Objects.equals(position, king.position); + } + + @Override + public int hashCode() { + return Objects.hash(color, position); + } } diff --git a/src/main/java/chess/piece/Knight.java b/src/main/java/chess/piece/Knight.java index 2ee7c47a3b..7335a5d887 100644 --- a/src/main/java/chess/piece/Knight.java +++ b/src/main/java/chess/piece/Knight.java @@ -1,5 +1,155 @@ package chess.piece; -public class Knight { +import chess.Board; +import chess.Color; +import chess.Column; +import chess.Movement; +import chess.Position; +import chess.Row; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +public class Knight implements Piece { + private final Color color; + private Position position; + + public Knight(Color color, Position position) { + this.color = color; + this.position = position; + } + + @Override + public void moveTo(Position targetPosition, Board board) { + List movements = findMovement(targetPosition); + repeatMove(movements, board); + } + + private List findMovement(Position targetPosition) { + int columnGap = position.calculateColumnGap(targetPosition); + int rowGap = position.calculateRowGap(targetPosition); + Movement firstMovement = decideFirstMovement(rowGap, columnGap); + Movement secondMovement = decideSecondMovement(rowGap, columnGap); + return List.of(firstMovement, firstMovement, secondMovement); + } + + private void repeatMove(List movements, Board board) { + int step = 0; + for (Movement movement : movements) { + step++; + if (!this.position.canMove(movement)) { + throw new IllegalArgumentException("보드의 범위를 벗어난 위치입니다."); + } + Position newPosition = this.position.move(movement); + + if (canAttack(step, board, newPosition)) { + attack(board, newPosition); + } else { + simplyMove(board, newPosition); + } + } + } + + private boolean canAttack(int step, Board board, Position newPosition) { + if (step == 3) { + Optional existingPiece = board.findByPosition(newPosition); + return existingPiece.isPresent() + && existingPiece.get().isEnemyWith(this); + } + return false; + } + + private void attack(Board board, Position newPosition) { + this.position = newPosition; + board.remove(newPosition); + } + + private void simplyMove(Board board, Position newPosition) { + if (board.findByPosition(newPosition).isPresent()) { + throw new IllegalArgumentException("장애물이 존재합니다."); + } + this.position = newPosition; + } + + private static Movement decideFirstMovement(int rowGap, int columnGap) { + if (rowGap == 2) { + return Movement.UP; + } + if (rowGap == -2) { + return Movement.DOWN; + } + if (columnGap == 2) { + return Movement.LEFT; + } + if (columnGap == -2) { + return Movement.RIGHT; + } + throw new IllegalArgumentException("나이트는 L자모양 이동만 가능합니다."); + } + + private static Movement decideSecondMovement(int rowGap, int columnGap) { + if (rowGap == 1) { + return Movement.UP; + } + if (rowGap == -1) { + return Movement.DOWN; + } + if (columnGap == 1) { + return Movement.LEFT; + } + if (columnGap == -1) { + return Movement.RIGHT; + } + throw new IllegalArgumentException("나이트는 L자모양 이동만 가능합니다."); + } + + public static List initialize(Color color) { + Position standard = new Position(Column.B, Row.ONE); + if (color.isBlack()) { + standard = new Position(Column.B, Row.EIGHT); + } + return List.of(new Knight(color, standard), + new Knight(color, standard.move(0, 5)) + ); + } + + @Override + public boolean isEnemyWith(Piece piece) { + if (this.isBlack()) { + return !piece.isBlack(); + } + return piece.isBlack(); + } + + @Override + public boolean isBlack() { + return this.color.isBlack(); + } + + @Override + public String getName() { + return "나"; + } + + @Override + public Position getPosition() { + return this.position; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Knight knight = (Knight) o; + return color == knight.color && Objects.equals(position, knight.position); + } + + @Override + public int hashCode() { + return Objects.hash(color, position); + } } diff --git a/src/main/java/chess/piece/Pawn.java b/src/main/java/chess/piece/Pawn.java index c8b6cafa51..1671019d44 100644 --- a/src/main/java/chess/piece/Pawn.java +++ b/src/main/java/chess/piece/Pawn.java @@ -1,5 +1,209 @@ package chess.piece; -public class Pawn { +import chess.Board; +import chess.Color; +import chess.Column; +import chess.Movement; +import chess.Position; +import chess.Row; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +public class Pawn implements Piece { + private final Color color; + private Position position; + + public Pawn(Color color, Position position) { + this.color = color; + this.position = position; + } + + @Override + public void moveTo(Position targetPosition, Board board) { + Movement movement = findMovement(targetPosition); + validateAvailableDirection(movement, board, targetPosition); + int step = calculateStep(targetPosition); + if (isMovingInitially()) { + if (step > 2 || step == 0) { + throw new IllegalArgumentException("폰은 시작 시 1칸 또는 2칸만 전진할 수 있습니다."); + } + repeatMove(movement, board, step); + return; + } + if (step >= 2 || step == 0) { + throw new IllegalArgumentException("폰은 1칸만 전진할 수 없습니다."); + } + repeatMove(movement, board, step); + } + + private int calculateStep(Position targetPosition) { + if (position.isRowEquals(targetPosition)) { + return Math.abs(position.calculateColumnGap(targetPosition)); + } + return Math.abs(position.calculateRowGap(targetPosition)); + } + + private Movement findMovement(Position targetPosition) { + int columnGap = position.calculateColumnGap(targetPosition); + int rowGap = position.calculateRowGap(targetPosition); + Movement movement = null; + if (rowGap == 0) { + if (columnGap > 0) { + movement = Movement.LEFT; + } + if (columnGap < 0) { + movement = Movement.RIGHT; + } + } + if (columnGap == 0) { + if (rowGap > 0) { + movement = Movement.UP; + } + if (rowGap < 0) { + movement = Movement.DOWN; + } + } + if (rowGap > 0) { + if (columnGap > 0) { + movement = Movement.LEFT_UP; + } + if (columnGap < 0) { + movement = Movement.RIGHT_UP; + } + } + if (rowGap < 0) { + if (columnGap > 0) { + movement = Movement.LEFT_DOWN; + } + if (columnGap < 0) { + movement = Movement.RIGHT_DOWN; + } + } + return movement; + } + + private void validateAvailableDirection(Movement movement, Board board, Position targetPosition) { + if (movement == null) { + throw new IllegalArgumentException("폰은 동서남북 방향으로만 이동 가능합니다."); + } + if (color.isWhite()) { + if (movement == Movement.DOWN + || movement == Movement.LEFT_DOWN + || movement == Movement.RIGHT_DOWN + ) { + throw new IllegalArgumentException("폰은 뒤로 이동할 수 없습니다."); + } + } + if (color.isBlack()) { + if (movement == Movement.UP + || movement == Movement.LEFT_UP + || movement == Movement.RIGHT_UP + ) { + throw new IllegalArgumentException("폰은 뒤로 이동할 수 없습니다."); + } + } + if (movement.isDiagonal()) { + if (board.findByPosition(targetPosition).isEmpty()) { + throw new IllegalArgumentException("폰은 공격 시에만 대각선으로 이동할 수 있습니다."); + } + } + } + + private void repeatMove(Movement movement, Board board, int step) { + for (int pointer = 0; pointer < step; pointer++) { + if (!this.position.canMove(movement)) { + throw new IllegalArgumentException("보드의 범위를 벗어난 위치입니다."); + } + Position newPosition = this.position.move(movement); + + if (canAttack(movement, step, pointer)) { + attack(board, newPosition); + } else { + simplyMove(board, newPosition); + } + } + } + + private static boolean canAttack(Movement movement, int step, int pointer) { + return pointer == step - 1 + && movement.isDiagonal(); + } + + private void simplyMove(Board board, Position newPosition) { + if (board.findByPosition(newPosition).isPresent()) { + throw new IllegalArgumentException("장애물이 존재합니다."); + } + this.position = newPosition; + } + + private void attack(Board board, Position newPosition) { + Optional existingPiece = board.findByPosition(newPosition); + if (existingPiece.isPresent() && existingPiece.get().isEnemyWith(this)) { + this.position = newPosition; + board.remove(newPosition); + return; + } + throw new IllegalArgumentException("공격할 수 없습니다."); + } + + private boolean isMovingInitially() { + if (color.isWhite()) { + return position.isRowEquals(new Position(Column.A, Row.TWO)); + } + return position.isRowEquals(new Position(Column.A, Row.SEVEN)); + } + + public static List initialize(Color color) { + List pieces = new ArrayList<>(); + Position standardPawnPosition = new Position(Column.A, Row.TWO); + if (color.isBlack()) { + standardPawnPosition = new Position(Column.A, Row.SEVEN); + } + for (int i = 0; i <= 7; i++) { + pieces.add(new Pawn(color, standardPawnPosition.move(0, i))); + } + return pieces; + } + + @Override + public boolean isEnemyWith(Piece piece) { + if (this.isBlack()) { + return !piece.isBlack(); + } + return piece.isBlack(); + } + + @Override + public boolean isBlack() { + return this.color.isBlack(); + } + + @Override + public String getName() { + return "폰"; + } + + @Override + public Position getPosition() { + return this.position; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Pawn pawn = (Pawn) o; + return color == pawn.color && Objects.equals(position, pawn.position); + } + + @Override + public int hashCode() { + return Objects.hash(color, position); + } } diff --git a/src/main/java/chess/piece/Piece.java b/src/main/java/chess/piece/Piece.java new file mode 100644 index 0000000000..9733b86ece --- /dev/null +++ b/src/main/java/chess/piece/Piece.java @@ -0,0 +1,16 @@ +package chess.piece; + +import chess.Board; +import chess.Position; + +public interface Piece { + Position getPosition(); + + String getName(); + + boolean isBlack(); + + void moveTo(Position targetPosition, Board board); + + boolean isEnemyWith(Piece piece); +} diff --git a/src/main/java/chess/piece/Queen.java b/src/main/java/chess/piece/Queen.java index 9b547261c4..45fb120fac 100644 --- a/src/main/java/chess/piece/Queen.java +++ b/src/main/java/chess/piece/Queen.java @@ -1,5 +1,155 @@ package chess.piece; -public class Queen { +import chess.Board; +import chess.Color; +import chess.Column; +import chess.Movement; +import chess.Position; +import chess.Row; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +public class Queen implements Piece { + private final Color color; + private Position position; + + public Queen(Color color, Position position) { + this.color = color; + this.position = position; + } + + @Override + public void moveTo(Position targetPosition, Board board) { + Movement movement = findMovement(targetPosition); + int step = calculateStep(targetPosition); + repeatMove(movement, board, step); + } + + private Movement findMovement(Position targetPosition) { + int columnGap = position.calculateColumnGap(targetPosition); + int rowGap = position.calculateRowGap(targetPosition); + validateStraightMove(rowGap, columnGap); + if (rowGap == 0) { + if (position.calculateColumnGap(targetPosition) > 0) { + return Movement.LEFT; + } + return Movement.RIGHT; + } + if (columnGap == 0) { + if (position.calculateRowGap(targetPosition) > 0) { + return Movement.UP; + } + return Movement.DOWN; + } + if (rowGap > 0) { + if (columnGap > 0) { + return Movement.LEFT_UP; + } + return Movement.RIGHT_UP; + } + if (columnGap > 0) { + return Movement.LEFT_DOWN; + } + return Movement.RIGHT_DOWN; + } + + private void validateStraightMove(int rowGap, int columnGap) { + if (rowGap == 0 || columnGap == 0 || Math.abs(rowGap) == Math.abs(columnGap)) { + return; + } + throw new IllegalArgumentException("퀸은 한 방향으로만 움직일 수 있습니다."); + } + + private int calculateStep(Position targetPosition) { + int rowGap = position.calculateRowGap(targetPosition); + int columnGap = position.calculateColumnGap(targetPosition); + if (rowGap == 0) { + return Math.abs(columnGap); + } + return Math.abs(rowGap); + } + + private void repeatMove(Movement movement, Board board, int step) { + for (int pointer = 0; pointer < step; pointer++) { + if (!this.position.canMove(movement)) { + throw new IllegalArgumentException("보드의 범위를 벗어난 위치입니다."); + } + Position newPosition = this.position.move(movement); + + if (canAttack(step, pointer, board, newPosition)) { + attack(board, newPosition); + } else { + simplyMove(board, newPosition); + } + } + } + + private boolean canAttack(int step, int pointer, Board board, Position newPosition) { + if (pointer == step - 1) { + Optional existingPiece = board.findByPosition(newPosition); + return existingPiece.isPresent() + && existingPiece.get().isEnemyWith(this); + } + return false; + } + + private void simplyMove(Board board, Position newPosition) { + if (board.findByPosition(newPosition).isPresent()) { + throw new IllegalArgumentException("장애물이 존재합니다."); + } + this.position = newPosition; + } + + private void attack(Board board, Position newPosition) { + this.position = newPosition; + board.remove(newPosition); + } + + public static List initialize(Color color) { + if (color.isWhite()) { + return List.of(new Queen(color, new Position(Column.D, Row.ONE))); + } + return List.of(new Queen(color, new Position(Column.D, Row.EIGHT))); + } + + @Override + public boolean isEnemyWith(Piece piece) { + if (this.isBlack()) { + return !piece.isBlack(); + } + return piece.isBlack(); + } + + @Override + public boolean isBlack() { + return this.color.isBlack(); + } + + @Override + public String getName() { + return "퀸"; + } + + @Override + public Position getPosition() { + return this.position; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Queen queen = (Queen) o; + return color == queen.color && Objects.equals(position, queen.position); + } + + @Override + public int hashCode() { + return Objects.hash(color, position); + } } diff --git a/src/main/java/chess/piece/Rook.java b/src/main/java/chess/piece/Rook.java index 7ed4d08bf0..b17f058dee 100644 --- a/src/main/java/chess/piece/Rook.java +++ b/src/main/java/chess/piece/Rook.java @@ -1,5 +1,140 @@ package chess.piece; -public class Rook { +import chess.Board; +import chess.Color; +import chess.Column; +import chess.Movement; +import chess.Position; +import chess.Row; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +public class Rook implements Piece { + private final Color color; + private Position position; + + public Rook(Color color, Position position) { + this.color = color; + this.position = position; + } + + @Override + public void moveTo(Position targetPosition, Board board) { + Movement movement = findMovement(targetPosition); + int step = calculateStep(targetPosition); + repeatMove(movement, board, step); + } + + private int calculateStep(Position targetPosition) { + if (position.isRowEquals(targetPosition)) { + return Math.abs(position.calculateColumnGap(targetPosition)); + } + return Math.abs(position.calculateRowGap(targetPosition)); + } + + private Movement findMovement(Position targetPosition) { + int columnGap = position.calculateColumnGap(targetPosition); + int rowGap = position.calculateRowGap(targetPosition); + if (rowGap == 0) { + if (position.calculateColumnGap(targetPosition) > 0) { + return Movement.LEFT; + } + return Movement.RIGHT; + } + if (columnGap == 0) { + if (position.calculateRowGap(targetPosition) > 0) { + return Movement.UP; + } + return Movement.DOWN; + } + throw new IllegalArgumentException("룩은 동서남북 방향으로만 이동 가능합니다."); + } + + private void repeatMove(Movement movement, Board board, int step) { + for (int pointer = 0; pointer < step; pointer++) { + if (!this.position.canMove(movement)) { + throw new IllegalArgumentException("보드의 범위를 벗어난 위치입니다."); + } + Position newPosition = this.position.move(movement); + + if (canAttack(step, pointer, board, newPosition)) { + attack(board, newPosition); + } else { + simplyMove(board, newPosition); + } + } + } + + private boolean canAttack(int step, int pointer, Board board, Position newPosition) { + if (pointer == step - 1) { + Optional existingPiece = board.findByPosition(newPosition); + return existingPiece.isPresent() + && existingPiece.get().isEnemyWith(this); + } + return false; + } + + private void simplyMove(Board board, Position newPosition) { + if (board.findByPosition(newPosition).isPresent()) { + throw new IllegalArgumentException("장애물이 존재합니다."); + } + this.position = newPosition; + } + + private void attack(Board board, Position newPosition) { + this.position = newPosition; + board.remove(newPosition); + } + + public static List initialize(Color color) { + Position standard = new Position(Column.A, Row.ONE); + if (color.isBlack()) { + standard = new Position(Column.A, Row.EIGHT); + } + return List.of(new Rook(color, standard), + new Rook(color, standard.move(0, 7)) + ); + } + + @Override + public boolean isEnemyWith(Piece piece) { + if (this.isBlack()) { + return !piece.isBlack(); + } + return piece.isBlack(); + } + + @Override + public boolean isBlack() { + return this.color.isBlack(); + } + + @Override + public String getName() { + return "룩"; + } + + @Override + public Position getPosition() { + return this.position; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Rook rook = (Rook) o; + return color == rook.color && Objects.equals(position, rook.position); + } + + @Override + public int hashCode() { + return Objects.hash(color, position); + } } + diff --git a/src/main/java/chess/view/BoardView.java b/src/main/java/chess/view/BoardView.java new file mode 100644 index 0000000000..f1c1ceb0f6 --- /dev/null +++ b/src/main/java/chess/view/BoardView.java @@ -0,0 +1,53 @@ +package chess.view; + +import chess.Board; +import chess.Column; +import chess.Position; +import chess.Row; +import chess.piece.Piece; +import java.util.Arrays; + +public class BoardView { + private static final int MAX_COLUMN = 8; + private static final int MAX_ROW = 8; + + private static final String GREEN_CODE = "\u001B[32m"; + private static final String WHITE_CODE = "\u001B[37m"; + private static final String BLACK_CODE = "\u001B[30m"; + private static final String COLOR_FINISH_CODE = "\u001B[0m"; + + + private static final String[][] matrix = new String[MAX_ROW + 1][MAX_COLUMN + 1]; + + public static void printBoard(Board board) { + clearBoard(); + for (Piece piece : board.getPieces()) { + Position position = piece.getPosition(); + matrix[position.getRow() + 1][position.getColumn() + 1] = getPieceNameMessage(piece); + } + + for (String[] row : matrix) { + System.out.println(String.join(" | ", row)); + } + } + + private static String getPieceNameMessage(Piece piece) { + if (piece.isBlack()) { + return GREEN_CODE + piece.getName() + COLOR_FINISH_CODE; + } + return WHITE_CODE + piece.getName() + COLOR_FINISH_CODE; + } + + private static void clearBoard() { + for (int row = 0; row < MAX_ROW + 1; row++) { + Arrays.fill(matrix[row], BLACK_CODE + "ㅁ" + COLOR_FINISH_CODE); + if (row == 0) { + for (int column = 0; column < MAX_COLUMN; column++) { + matrix[row][column + 1] = Column.ofOrdinal(column); + } + continue; + } + matrix[row][0] = Row.ofOrdinal(row - 1); + } + } +} diff --git a/src/main/java/chess/view/InputView.java b/src/main/java/chess/view/InputView.java new file mode 100644 index 0000000000..36fce12854 --- /dev/null +++ b/src/main/java/chess/view/InputView.java @@ -0,0 +1,29 @@ +package chess.view; + +import chess.Column; +import chess.Position; +import chess.Row; +import java.util.Scanner; + +public class InputView { + private static final Scanner sc = new Scanner(System.in); + + //TODO 입력값 예외 처리 + public static Position readMovingPosition() { + System.out.println("\n움직일 기물의 열과 행을 선택해주세요. (ex. a,1)"); + String[] input = sc.nextLine().split(","); + return new Position( + Column.valueOf(input[0].toUpperCase()), + Row.fromNumber(Integer.parseInt(input[1])) + ); + } + + public static Position readTargetPosition() { + System.out.println("어느 열과 행으로 움직일지 선택해주세요. (ex. b,2)"); + String[] input = sc.nextLine().split(","); + return new Position( + Column.valueOf(input[0].toUpperCase()), + Row.fromNumber(Integer.parseInt(input[1])) + ); + } +} diff --git a/src/test/java/chess/piece/BishopTest.java b/src/test/java/chess/piece/BishopTest.java new file mode 100644 index 0000000000..c0d045a2a6 --- /dev/null +++ b/src/test/java/chess/piece/BishopTest.java @@ -0,0 +1,65 @@ +package chess.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.Board; +import chess.Color; +import chess.Column; +import chess.Position; +import chess.Row; +import java.util.List; +import org.junit.jupiter.api.Test; + +class BishopTest { + + @Test + void moveToLeftUpTwice() { + Bishop bishop = new Bishop(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + bishop + )); + Position newPosition = new Position(Column.C, Row.SEVEN); + + bishop.moveTo(newPosition, board); + + assertThat(bishop).isEqualTo(new Bishop(Color.WHITE, newPosition)); + } + + @Test + void failIfCardinalMoveTo() { + Bishop bishop = new Bishop(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + bishop + )); + Position newPosition = new Position(Column.C, Row.FIVE); + + assertThatThrownBy(() -> bishop.moveTo(newPosition, board)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void failIfNotStraight() { + Bishop bishop = new Bishop(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + bishop + )); + Position newPosition = new Position(Column.G, Row.SIX); + + assertThatThrownBy(() -> bishop.moveTo(newPosition, board)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void failIfHurdleExists() { + Bishop bishop = new Bishop(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + new Pawn(Color.BLACK, new Position(Column.D, Row.SIX)), + bishop + )); + Position newPosition = new Position(Column.C, Row.SEVEN); + + assertThatThrownBy(() -> bishop.moveTo(newPosition, board)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/chess/piece/KingTest.java b/src/test/java/chess/piece/KingTest.java new file mode 100644 index 0000000000..ffd6771c1c --- /dev/null +++ b/src/test/java/chess/piece/KingTest.java @@ -0,0 +1,85 @@ +package chess.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.Board; +import chess.Color; +import chess.Column; +import chess.Position; +import chess.Row; +import java.util.List; +import org.junit.jupiter.api.Test; + +class KingTest { + @Test + void moveToUpOnce() { + King king = new King(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + king + )); + + king.moveTo(new Position(Column.E, Row.SIX), board); + + assertThat(king).isEqualTo(new King(Color.WHITE, new Position(Column.E, Row.SIX))); + } + + @Test + void moveToRightOnce() { + King king = new King(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + king + )); + Position newPosition = new Position(Column.F, Row.FIVE); + + king.moveTo(newPosition, board); + + assertThat(king).isEqualTo(new King(Color.WHITE, newPosition)); + } + + @Test + void moveToLeftUpOnce() { + King king = new King(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + king + )); + + king.moveTo(new Position(Column.D, Row.SIX), board); + + assertThat(king).isEqualTo(new King(Color.WHITE, new Position(Column.D, Row.SIX))); + } + + @Test + void moveToRightDownOnce() { + King king = new King(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + king + )); + + king.moveTo(new Position(Column.F, Row.FOUR), board); + + assertThat(king).isEqualTo(new King(Color.WHITE, new Position(Column.F, Row.FOUR))); + } + + @Test + void failToMoveToTwice() { + King king = new King(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + king + )); + + assertThatThrownBy(() -> king.moveTo(new Position(Column.E, Row.THREE), board)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void failIfNotStraight() { + King king = new King(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + king + )); + + assertThatThrownBy(() -> king.moveTo(new Position(Column.D, Row.THREE), board)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/chess/piece/KnightTest.java b/src/test/java/chess/piece/KnightTest.java new file mode 100644 index 0000000000..2b95b4b416 --- /dev/null +++ b/src/test/java/chess/piece/KnightTest.java @@ -0,0 +1,53 @@ +package chess.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.Board; +import chess.Color; +import chess.Column; +import chess.Position; +import chess.Row; +import java.util.List; +import org.junit.jupiter.api.Test; + +class KnightTest { + @Test + void moveToUpUpLeftOnce() { + Knight knight = new Knight(Color.WHITE, new Position(Column.D, Row.FIVE)); + Board board = new Board(List.of( + knight + )); + Position newPosition = new Position(Column.C, Row.SEVEN); + + knight.moveTo(newPosition, board); + + assertThat(knight).isEqualTo(new Knight(Color.WHITE, newPosition)); + } + + @Test + void moveToRightRightDownOnce() { + Knight knight = new Knight(Color.WHITE, new Position(Column.D, Row.FIVE)); + Board board = new Board(List.of( + knight + )); + Position newPosition = new Position(Column.F, Row.FOUR); + + + knight.moveTo(newPosition, board); + + assertThat(knight).isEqualTo(new Knight(Color.WHITE, newPosition)); + } + + @Test + void failToMoveToOther() { + King king = new King(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + king + )); + Position newPosition = new Position(Column.F, Row.THREE); + + assertThatThrownBy(() -> king.moveTo(newPosition, board)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/chess/piece/PawnTest.java b/src/test/java/chess/piece/PawnTest.java new file mode 100644 index 0000000000..d1128f26d8 --- /dev/null +++ b/src/test/java/chess/piece/PawnTest.java @@ -0,0 +1,96 @@ +package chess.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.Board; +import chess.Color; +import chess.Column; +import chess.Position; +import chess.Row; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class PawnTest { + + @Test + void moveToUpOnce() { + Pawn pawn = new Pawn(Color.WHITE, new Position(Column.A, Row.TWO)); + Board board = new Board(List.of( + pawn + )); + + pawn.moveTo(new Position(Column.A, Row.THREE), board); + + assertThat(pawn).isEqualTo(new Pawn(Color.WHITE, new Position(Column.A, Row.THREE))); + } + + @Test + void moveToDownOnce() { + Pawn pawn = new Pawn(Color.WHITE, new Position(Column.A, Row.THREE)); + Board board = new Board(List.of( + pawn + )); + + pawn.moveTo(new Position(Column.A, Row.TWO), board); + + assertThat(pawn).isEqualTo(new Pawn(Color.WHITE, new Position(Column.A, Row.TWO))); + } + + @Test + void failToMoveToUpMoreThanOnce() { + Pawn pawn = new Pawn(Color.WHITE, new Position(Column.A, Row.TWO)); + Board board = new Board(List.of( + pawn + )); + + assertThatThrownBy(() -> pawn.moveTo(new Position(Column.A, Row.FIVE), board)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void moveToUpTwiceIfStartMoving() { + Pawn pawn = new Pawn(Color.WHITE, new Position(Column.A, Row.TWO)); + Board board = new Board(List.of( + pawn + )); + + pawn.moveTo(new Position(Column.A, Row.FOUR), board); + + assertThat(pawn).isEqualTo(new Pawn(Color.WHITE, new Position(Column.A, Row.FOUR))); + } + + @Test + void failIfHurdleExists() { + Pawn pawn = new Pawn(Color.WHITE, new Position(Column.A, Row.TWO)); + Board hurdleBoard = new Board(List.of( + new Pawn(Color.BLACK, new Position(Column.A, Row.THREE)), + pawn + )); + + assertThatThrownBy(() -> pawn.moveTo(new Position(Column.A, Row.FOUR), hurdleBoard)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void failIfMoveToBackWhenColorBlack() { + Pawn pawn = new Pawn(Color.BLACK, new Position(Column.A, Row.SEVEN)); + Board hurdleBoard = new Board(List.of( + pawn + )); + + assertThatThrownBy(() -> pawn.moveTo(new Position(Column.A, Row.EIGHT), hurdleBoard)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void failIfMoveToBackWhenColorWhite() { + Pawn pawn = new Pawn(Color.WHITE, new Position(Column.A, Row.TWO)); + Board hurdleBoard = new Board(List.of( + pawn + )); + + assertThatThrownBy(() -> pawn.moveTo(new Position(Column.A, Row.ONE), hurdleBoard)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/chess/piece/QueenTest.java b/src/test/java/chess/piece/QueenTest.java new file mode 100644 index 0000000000..3dcc3d8c2f --- /dev/null +++ b/src/test/java/chess/piece/QueenTest.java @@ -0,0 +1,78 @@ +package chess.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.Board; +import chess.Color; +import chess.Column; +import chess.Position; +import chess.Row; +import java.util.List; +import org.junit.jupiter.api.Test; + +class QueenTest { + @Test + void moveToUpTwice() { + Queen queen = new Queen(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + queen + )); + Position newPosition = new Position(Column.E, Row.SEVEN); + + queen.moveTo(newPosition, board); + + assertThat(queen).isEqualTo(new Queen(Color.WHITE, newPosition)); + } + + @Test + void moveToRightTwice() { + Queen queen = new Queen(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + queen + )); + Position newPosition = new Position(Column.G, Row.FIVE); + + queen.moveTo(newPosition, board); + + assertThat(queen).isEqualTo(new Queen(Color.WHITE, newPosition)); + } + + @Test + void moveToLeftUpTwice() { + Queen queen = new Queen(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + queen + )); + Position newPosition = new Position(Column.C, Row.SEVEN); + + queen.moveTo(newPosition, board); + + assertThat(queen).isEqualTo(new Queen(Color.WHITE, newPosition)); + } + + @Test + void failIfHurdleExists() { + Queen queen = new Queen(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + new Pawn(Color.BLACK, new Position(Column.E, Row.SIX)), + queen + )); + Position newPosition = new Position(Column.E, Row.SEVEN); + + assertThatThrownBy(() -> queen.moveTo(newPosition, board)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void failIfNotStraight() { + Queen queen = new Queen(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + queen + )); + Position newPosition = new Position(Column.F, Row.SEVEN); + + assertThatThrownBy(() -> queen.moveTo(newPosition, board)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/chess/piece/RookTest.java b/src/test/java/chess/piece/RookTest.java new file mode 100644 index 0000000000..32bc4f0a0c --- /dev/null +++ b/src/test/java/chess/piece/RookTest.java @@ -0,0 +1,77 @@ +package chess.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.Board; +import chess.Color; +import chess.Column; +import chess.Position; +import chess.Row; +import java.util.List; +import org.junit.jupiter.api.Test; + +class RookTest { + @Test + void moveToUpTwice() { + Rook rook = new Rook(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + rook + )); + Position newPosition = new Position(Column.E, Row.SEVEN); + + rook.moveTo(newPosition, board); + + assertThat(rook).isEqualTo(new Rook(Color.WHITE, newPosition)); + } + + @Test + void moveToRightTwice() { + Rook rook = new Rook(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + rook + )); + Position newPosition = new Position(Column.G, Row.FIVE); + + rook.moveTo(newPosition, board); + + assertThat(rook).isEqualTo(new Rook(Color.WHITE, newPosition)); + } + + @Test + void failIfDiagonalMoveTo() { + Rook rook = new Rook(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + rook + )); + Position newPosition = new Position(Column.D, Row.FOUR); + + assertThatThrownBy(() -> rook.moveTo(newPosition, board)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void failIfHurdleExists() { + Rook rook = new Rook(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + new Pawn(Color.BLACK, new Position(Column.E, Row.SIX)), + rook + )); + Position newPosition = new Position(Column.E, Row.SEVEN); + + assertThatThrownBy(() -> rook.moveTo(newPosition, board)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void failIfNotStraight() { + Rook rook = new Rook(Color.WHITE, new Position(Column.E, Row.FIVE)); + Board board = new Board(List.of( + rook + )); + Position newPosition = new Position(Column.F, Row.SEVEN); + + assertThatThrownBy(() -> rook.moveTo(newPosition, board)) + .isInstanceOf(IllegalArgumentException.class); + } +}