diff --git a/README.md b/README.md index 8102f91c870..b7fa263d3db 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,48 @@ ## 우아한테크코스 코드리뷰 - [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) + +## 기존 코드 + +Column +A: 가장 왼쪽 +H: 가장 오른쪽 + +Row +EIGHT: 가장 위 +ONE: 가장 아래 + +Position +말의 움직임 가능 여부 확인 및 움직임 + +## 장기말 + +공통 내용 +- [ ] 각 말은 팀(Color)를 가지고 있다. +- [ ] 도착지에 같은 팀이 있다면 이동할 수 없다. +- [x] 장기판 말 밖으로 이동할 수 없다. + +비숍 +- [x] 대각선 원하는만큼 +- [ ] 뛰어넘기 불가 + +왕 +- [x] 상하좌우 + 대각선 + +나이트 +- [ ] 상하좌우 한 칸 + 진행 방향으로 대각선 한 칸 +- [ ] 뛰어넘기 가능 + +폰 +- [x] 각 팀마다 진행방향으로 한칸 이동 가능 +- [x] 처음 이동에는 두 칸 이동 가능 +- [ ] 상대편 말을 잡을 경우 대각선 이동 가능 + +퀸 +- [x] 상하좌우 원하는 만큼 +- [x] 대각선 원하는 만큼 +- [ ] 뛰어넘기 불가 + +룩 +- [x] 상하좌우 원하는 만큼 +- [ ] 뛰어넘기 불가 diff --git a/src/main/java/chess/Application.java b/src/main/java/chess/Application.java new file mode 100644 index 00000000000..2e4b2fe3f87 --- /dev/null +++ b/src/main/java/chess/Application.java @@ -0,0 +1,11 @@ +package chess; + +import chess.board.BoardManager; + +public class Application { + + public static void main(String[] args) { + BoardManager manager = new BoardManager(); + manager.move(); + } +} diff --git a/src/main/java/chess/board/Board.java b/src/main/java/chess/board/Board.java new file mode 100644 index 00000000000..2d14efa668a --- /dev/null +++ b/src/main/java/chess/board/Board.java @@ -0,0 +1,41 @@ +package chess.board; + +import chess.position.Position; +import chess.piece.Piece; +import chess.piece.Pieces; +import java.util.List; +import java.util.Set; + +public class Board { + + private final Pieces pieces; + + public Board(Pieces pieces) { + this.pieces = pieces; + } + + public void move(Position current, Position destination) { + Piece pickedPiece = pieces.findByPosition(current); + int differenceX = destination.column().ordinal() - current.column().ordinal(); + int differenceY = current.row().ordinal() - destination.row().ordinal(); + if (pieces.existsByPosition(destination)) { + Piece targetPiece = pieces.findByPosition(destination); + if (pickedPiece.isSameColor(targetPiece)) { + throw new IllegalArgumentException("같은 팀이 있는 위치로는 이동할 수 없습니다."); + } + } + + Set path = pickedPiece.calculatePath(differenceX, differenceY); + boolean existsPieceOnPath = path.stream() + .anyMatch(pieces::existsByPosition); + if (existsPieceOnPath) { + throw new IllegalArgumentException("경로 상에 말이 존재합니다"); + } + + pickedPiece.move(differenceX, differenceY); + } + + public List getPieces() { + return pieces.getPieces(); + } +} diff --git a/src/main/java/chess/board/BoardFactory.java b/src/main/java/chess/board/BoardFactory.java new file mode 100644 index 00000000000..f57f6212b82 --- /dev/null +++ b/src/main/java/chess/board/BoardFactory.java @@ -0,0 +1,53 @@ +package chess.board; + +import chess.position.Color; +import chess.position.Column; +import chess.position.Position; +import chess.position.Row; +import chess.piece.Bishop; +import chess.piece.King; +import chess.piece.Knight; +import chess.piece.Pawn; +import chess.piece.Piece; +import chess.piece.Pieces; +import chess.piece.Queen; +import chess.piece.Rook; +import java.util.ArrayList; +import java.util.List; + +public class BoardFactory { + + private BoardFactory() { + } + + public static Board init() { + List initPieces = new ArrayList<>(); + for (Column column : Column.values()) { + initPieces.add(new Pawn(Color.WHITE, new Position(column, Row.TWO))); + initPieces.add(new Pawn(Color.BLACK, new Position(column, Row.SEVEN))); + } + initPieces.add(new Rook(Color.WHITE, new Position(Column.A, Row.ONE))); + initPieces.add(new Rook(Color.WHITE, new Position(Column.H, Row.ONE))); + initPieces.add(new Rook(Color.BLACK, new Position(Column.A, Row.EIGHT))); + initPieces.add(new Rook(Color.BLACK, new Position(Column.H, Row.EIGHT))); + + initPieces.add(new Knight(Color.WHITE, new Position(Column.B, Row.ONE))); + initPieces.add(new Knight(Color.WHITE, new Position(Column.G, Row.ONE))); + initPieces.add(new Knight(Color.BLACK, new Position(Column.B, Row.EIGHT))); + initPieces.add(new Knight(Color.BLACK, new Position(Column.G, Row.EIGHT))); + + initPieces.add(new Bishop(Color.WHITE, new Position(Column.C, Row.ONE))); + initPieces.add(new Bishop(Color.WHITE, new Position(Column.F, Row.ONE))); + initPieces.add(new Bishop(Color.BLACK, new Position(Column.C, Row.EIGHT))); + initPieces.add(new Bishop(Color.BLACK, new Position(Column.F, Row.EIGHT))); + + initPieces.add(new King(Color.WHITE, new Position(Column.D, Row.ONE))); + initPieces.add(new Queen(Color.WHITE, new Position(Column.E, Row.ONE))); + + initPieces.add(new King(Color.BLACK, new Position(Column.D, Row.EIGHT))); + initPieces.add(new Queen(Color.BLACK, new Position(Column.E, Row.EIGHT))); + + Pieces pieces = new Pieces(initPieces); + return new Board(pieces); + } +} diff --git a/src/main/java/chess/board/BoardManager.java b/src/main/java/chess/board/BoardManager.java new file mode 100644 index 00000000000..d1dd9d0993d --- /dev/null +++ b/src/main/java/chess/board/BoardManager.java @@ -0,0 +1,34 @@ +package chess.board; + +import chess.position.Column; +import chess.position.Position; +import chess.position.Row; +import chess.view.InputView; +import chess.view.OutputView; + +public class BoardManager { + + public void move() { + Board board = BoardFactory.init(); + while(true) + { + OutputView.printBoard(board); + String inputCurrentCoordinate = InputView.inputCurrentCoordinate(); + if (inputCurrentCoordinate.equals("Q")) { + break; + } + String inputDestinationCoordinate = InputView.inputDestinationCoordinate(); + Position currentPosition = parsePosition(inputCurrentCoordinate); + Position destinationPosition = parsePosition(inputDestinationCoordinate); + board.move(currentPosition, destinationPosition); + } + } + + private Position parsePosition(String input) { + String[] coordinate = input.split(""); + Column column = Column.valueOf(coordinate[0]); + int rowNumber = Integer.parseInt(coordinate[1]); + Row row = Row.values()[8 - rowNumber]; + return new Position(column, row); + } +} diff --git a/src/main/java/chess/piece/Bishop.java b/src/main/java/chess/piece/Bishop.java index b14ab70f981..6447449e011 100644 --- a/src/main/java/chess/piece/Bishop.java +++ b/src/main/java/chess/piece/Bishop.java @@ -1,5 +1,54 @@ package chess.piece; -public class Bishop { +import chess.position.Color; +import chess.position.Position; +import java.util.HashSet; +import java.util.Set; +public class Bishop extends Piece { + + public Bishop(final Color color, final Position position) { + super(color, position); + } + + @Override + public void move(final int x, final int y) { + if (Math.abs(x) != Math.abs(y)) { + throw new IllegalArgumentException("대각선으로만 이동 가능합니다."); + } + boolean canMove = position.canMoveDiagonal(x, y); + + if (canMove) { + position = position.moveDiagonal(x, y); + return; + } + throw new IllegalArgumentException("이동할 수 없습니다"); + } + + @Override + public Set calculatePath(final int x, final int y) { + Set path = new HashSet<>(); + int step = Math.abs(x); + if (x > 0 && y > 0) { + for (int i = 0; i < step; i++) { + path.add(position.moveRightUp()); + } + } + if (x > 0 && y < 0) { + for (int i = 0; i < step; i++) { + path.add(position.moveRightDown()); + } + } + if (x < 0 && y > 0) { + for (int i = 0; i < step; i++) { + path.add(position.moveLeftUp()); + } + } + if (x < 0 && y < 0) { + for (int i = 0; i < step; i++) { + path.add(position.moveLeftDown()); + } + } + return path; + } } diff --git a/src/main/java/chess/piece/King.java b/src/main/java/chess/piece/King.java index d64210cad13..ee1dbd9da1a 100644 --- a/src/main/java/chess/piece/King.java +++ b/src/main/java/chess/piece/King.java @@ -1,5 +1,34 @@ package chess.piece; -public class King { +import chess.position.Color; +import chess.position.Position; +import java.util.Set; +public class King extends Piece { + + public King(final Color color, final Position position) { + super(color, position); + } + + @Override + public void move(final int x, final int y) { + if (x > 1 || y > 1) { + throw new IllegalArgumentException("한 칸만 이동 가능합니다"); + } + + boolean canMoveHorizontal = position.canMoveHorizontal(x); + boolean canMoveVertical = position.canMoveVertical(y); + + if (canMoveHorizontal && canMoveVertical) { + position = position.moveHorizontal(x); + position = position.moveVertical(y); + return; + } + throw new IllegalArgumentException("움직일 수 없습니다."); + } + + @Override + public Set calculatePath(int x, int y) { + return Set.of(); + } } diff --git a/src/main/java/chess/piece/Knight.java b/src/main/java/chess/piece/Knight.java index 2ee7c47a3bc..1cb6ad9e22c 100644 --- a/src/main/java/chess/piece/Knight.java +++ b/src/main/java/chess/piece/Knight.java @@ -1,5 +1,34 @@ package chess.piece; -public class Knight { +import chess.position.Color; +import chess.position.Position; +import java.util.Set; +public class Knight extends Piece { + + public Knight(Color color, Position position) { + super(color, position); + } + + @Override + public void move(final int x, final int y) { + boolean canMove = position.canMoveHorizontal(x) && position.canMoveVertical(y); + + if (isMovingRule(x, y) && canMove) { + position = position.moveDiagonal(x, y); + return; + } + throw new IllegalArgumentException("이동할 수 없습니다."); + } + + @Override + public Set calculatePath(int x, int y) { + return Set.of(); + } + + private boolean isMovingRule(final int x, final int y) { + int absX = Math.abs(x); + int absY = Math.abs(y); + return ((absX == 2 && absY == 1) || (absX == 1 && absY == 2)); + } } diff --git a/src/main/java/chess/piece/Pawn.java b/src/main/java/chess/piece/Pawn.java index c8b6cafa51e..25cd704687d 100644 --- a/src/main/java/chess/piece/Pawn.java +++ b/src/main/java/chess/piece/Pawn.java @@ -1,5 +1,47 @@ package chess.piece; -public class Pawn { +import chess.position.Color; +import chess.position.Position; +import chess.position.Row; +import java.util.Set; +public class Pawn extends Piece { + + public Pawn(final Color color, final Position position) { + super(color, position); + } + + @Override + public void move(final int x, final int y) { + boolean canMove = false; + if (color == Color.WHITE && y > 0 && y <= 2) { + canMove = position.canMoveUp(); + } + if (color == Color.BLACK && y < 0 && y >= -2) { + canMove = position.canMoveDown(); + } + + boolean isNotFirstMove = true; + if (color == Color.WHITE && position.row() == Row.TWO) { + isNotFirstMove = false; + } + if (color == Color.BLACK && position.row() == Row.SEVEN) { + isNotFirstMove = false; + } + + if (isNotFirstMove && y != 1) { + canMove = false; + } + + if (canMove) { + position = position.moveVertical(y); + return; + } + throw new IllegalArgumentException("이동할 수 없습니다."); + } + + @Override + public Set calculatePath(int x, int y) { + return Set.of(); + } } diff --git a/src/main/java/chess/piece/Piece.java b/src/main/java/chess/piece/Piece.java new file mode 100644 index 00000000000..edcb31eabbd --- /dev/null +++ b/src/main/java/chess/piece/Piece.java @@ -0,0 +1,32 @@ +package chess.piece; + +import chess.position.Color; +import chess.position.Position; +import java.util.Set; + +public abstract class Piece { + + protected final Color color; + protected Position position; + + public Piece(Color color, Position position) { + this.color = color; + this.position = position; + } + + public boolean isSameColor(Piece other) { + return this.color == other.color; + } + + public abstract void move(int x, int y); + + public abstract Set calculatePath(int x, int y); + + public Color getColor() { + return color; + } + + public Position getPosition() { + return position; + } +} diff --git a/src/main/java/chess/piece/Pieces.java b/src/main/java/chess/piece/Pieces.java new file mode 100644 index 00000000000..6de1ce98df6 --- /dev/null +++ b/src/main/java/chess/piece/Pieces.java @@ -0,0 +1,30 @@ +package chess.piece; + +import chess.position.Position; +import java.util.Collections; +import java.util.List; + +public class Pieces { + + private final List pieces; + + public Pieces(List pieces) { + this.pieces = pieces; + } + + public Piece findByPosition(Position position) { + return pieces.stream() + .filter(piece -> piece.getPosition().equals(position)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("말이 존재하지 않습니다.")); + } + + public boolean existsByPosition(Position position) { + return pieces.stream() + .anyMatch(piece -> piece.getPosition().equals(position)); + } + + public List getPieces() { + return Collections.unmodifiableList(pieces); + } +} diff --git a/src/main/java/chess/piece/Queen.java b/src/main/java/chess/piece/Queen.java index 9b547261c4b..623f6f05409 100644 --- a/src/main/java/chess/piece/Queen.java +++ b/src/main/java/chess/piece/Queen.java @@ -1,5 +1,118 @@ package chess.piece; -public class Queen { +import chess.position.Color; +import chess.position.Position; +import java.util.HashSet; +import java.util.Set; +public class Queen extends Piece { + + public Queen(final Color color, final Position position) { + super(color, position); + } + + @Override + public void move(final int x, final int y) { + if (x == 0 || y == 0) { + moveHorizontalOrVertical(x, y); + return; + } + if (Math.abs(x) == Math.abs(y)) { + moveDiagonal(x, y); + return; + } + throw new IllegalArgumentException("이동할 수 없습니다."); + } + + private void moveHorizontalOrVertical(final int x, final int y) { + boolean canMove = false; + if (x != 0) { + canMove = position.canMoveHorizontal(x); + } + if (y != 0) { + canMove = position.canMoveVertical(y); + } + + if (canMove) { + if (x != 0) { + position = position.moveHorizontal(x); + return; + } + position = position.moveVertical(y); + return; + } + throw new IllegalArgumentException("이동할 수 없습니다."); + } + + private void moveDiagonal(final int x, final int y) { + boolean canMove = position.canMoveDiagonal(x, y); + + if (canMove) { + position = position.moveDiagonal(x, y); + return; + } + throw new IllegalArgumentException("이동할 수 없습니다"); + } + + @Override + public Set calculatePath(final int x, final int y) { + if (x != 0 && y != 0) { + return calculateDiagonalPath(x, y); + } + return calculateHorizontalOrVerticalPath(x, y); + } + + public Set calculateDiagonalPath(final int x, final int y) { + Set path = new HashSet<>(); + int step = Math.abs(x); + if (x > 0 && y > 0) { + for (int i = 0; i < step; i++) { + path.add(position.moveRightUp()); + } + } + if (x > 0 && y < 0) { + for (int i = 0; i < step; i++) { + path.add(position.moveRightDown()); + } + } + if (x < 0 && y > 0) { + for (int i = 0; i < step; i++) { + path.add(position.moveLeftUp()); + } + } + if (x < 0 && y < 0) { + for (int i = 0; i < step; i++) { + path.add(position.moveLeftDown()); + } + } + return path; + } + + public Set calculateHorizontalOrVerticalPath(final int x, final int y) { + Set path = new HashSet<>(); + int horizontalStep = Math.abs(x); + if (x > 0) { + for (int i = 0; i < horizontalStep; i++) { + path.add(position.moveRight()); + } + } + if (x < 0) { + for (int i = 0; i < horizontalStep; i++) { + path.add(position.moveLeftDown()); + } + } + + int verticalStep = Math.abs(y); + if (y > 0) { + for (int i = 0; i < verticalStep; i++) { + path.add(position.moveUp()); + } + } + if (y < 0) { + for (int i = 0; i < verticalStep; i++) { + path.add(position.moveRightDown()); + } + } + return path; + } } diff --git a/src/main/java/chess/piece/Rook.java b/src/main/java/chess/piece/Rook.java index 7ed4d08bf03..b44a78fae67 100644 --- a/src/main/java/chess/piece/Rook.java +++ b/src/main/java/chess/piece/Rook.java @@ -1,5 +1,67 @@ package chess.piece; -public class Rook { +import chess.position.Color; +import chess.position.Position; +import java.util.HashSet; +import java.util.Set; +public class Rook extends Piece { + + public Rook(final Color color, final Position position) { + super(color, position); + } + + @Override + public void move(final int x, final int y) { + if (x != 0 && y != 0) { + throw new IllegalArgumentException("대각선으로는 이동할 수 없습니다."); + } + + boolean canMove = false; + if (x != 0) { + canMove = position.canMoveHorizontal(x); + } + if (y != 0) { + canMove = position.canMoveVertical(y); + } + + if (canMove) { + if (x != 0) { + position = position.moveHorizontal(x); + return; + } + position = position.moveVertical(y); + return; + } + throw new IllegalArgumentException("이동할 수 없습니다."); + } + + @Override + public Set calculatePath(final int x, final int y) { + Set path = new HashSet<>(); + int horizontalStep = Math.abs(x); + if (x > 0) { + for (int i = 0; i < horizontalStep; i++) { + path.add(position.moveRight()); + } + } + if (x < 0) { + for (int i = 0; i < horizontalStep; i++) { + path.add(position.moveLeftDown()); + } + } + + int verticalStep = Math.abs(y); + if (y > 0) { + for (int i = 0; i < verticalStep; i++) { + path.add(position.moveUp()); + } + } + if (y < 0) { + for (int i = 0; i < verticalStep; i++) { + path.add(position.moveRightDown()); + } + } + return path; + } } diff --git a/src/main/java/chess/Color.java b/src/main/java/chess/position/Color.java similarity index 94% rename from src/main/java/chess/Color.java rename to src/main/java/chess/position/Color.java index 55cd020b681..6c385d831ba 100644 --- a/src/main/java/chess/Color.java +++ b/src/main/java/chess/position/Color.java @@ -1,4 +1,4 @@ -package chess; +package chess.position; public enum Color { diff --git a/src/main/java/chess/Column.java b/src/main/java/chess/position/Column.java similarity index 97% rename from src/main/java/chess/Column.java rename to src/main/java/chess/position/Column.java index b64b4dc77a3..4099a335b6a 100644 --- a/src/main/java/chess/Column.java +++ b/src/main/java/chess/position/Column.java @@ -1,4 +1,4 @@ -package chess; +package chess.position; public enum Column { diff --git a/src/main/java/chess/Movement.java b/src/main/java/chess/position/Movement.java similarity index 97% rename from src/main/java/chess/Movement.java rename to src/main/java/chess/position/Movement.java index e57c6e91bb9..60d4815db36 100644 --- a/src/main/java/chess/Movement.java +++ b/src/main/java/chess/position/Movement.java @@ -1,4 +1,4 @@ -package chess; +package chess.position; public enum Movement { UP(0, 1), diff --git a/src/main/java/chess/Position.java b/src/main/java/chess/position/Position.java similarity index 77% rename from src/main/java/chess/Position.java rename to src/main/java/chess/position/Position.java index 3ebeb0ea185..5e89e9a8928 100644 --- a/src/main/java/chess/Position.java +++ b/src/main/java/chess/position/Position.java @@ -1,4 +1,4 @@ -package chess; +package chess.position; public record Position( Column column, @@ -76,14 +76,26 @@ public boolean canMoveLeftUp() { return canMoveLeft() && canMoveUp(); } + public boolean canMoveLeftUp(final int step) { + return canMoveLeft(step) && canMoveUp(step); + } + public Position moveLeftUp() { return moveLeft().moveUp(); } + public Position moveLeftUp(final int step) { + return moveLeft(step).moveUp(step); + } + public boolean canMoveLeftDown() { return canMoveLeft() && canMoveDown(); } + public boolean canMoveLeftDown(final int step) { + return canMoveLeft(step) && canMoveDown(step); + } + public Position moveLeftDown() { return moveLeft().moveDown(); } @@ -92,18 +104,34 @@ public boolean canMoveRightUp() { return canMoveUp() && canMoveRight(); } + public boolean canMoveRightUp(final int step) { + return canMoveUp(step) && canMoveRight(step); + } + public Position moveRightUp() { return moveRight().moveUp(); } + public Position moveRightUp(final int step) { + return moveRight(step).moveUp(step); + } + public boolean canMoveRightDown() { return canMoveRight() && canMoveDown(); } + public boolean canMoveRightDown(final int step) { + return canMoveRight(step) && canMoveDown(step); + } + public Position moveRightDown() { return moveRight().moveDown(); } + public Position moveRightDown(final int step) { + return moveRight(step).moveDown(step); + } + public boolean isTop() { return row.isTop(); } @@ -144,6 +172,10 @@ public boolean canMoveHorizontal(final int step) { return true; } + public boolean canMoveDiagonal(final int x, final int y) { + return canMoveHorizontal(x) && canMoveVertical(y); + } + public Position move(final Movement movement) { return moveVertical(movement.y()).moveHorizontal(movement.x()); } @@ -167,4 +199,8 @@ public Position moveHorizontal(final int step) { } return this; } + + public Position moveDiagonal(final int horizontalStep, final int verticalStep) { + return moveHorizontal(horizontalStep).moveVertical(verticalStep); + } } diff --git a/src/main/java/chess/Row.java b/src/main/java/chess/position/Row.java similarity index 97% rename from src/main/java/chess/Row.java rename to src/main/java/chess/position/Row.java index 126ed048daa..12ff8956d5f 100644 --- a/src/main/java/chess/Row.java +++ b/src/main/java/chess/position/Row.java @@ -1,4 +1,4 @@ -package chess; +package chess.position; public enum Row { diff --git a/src/main/java/chess/view/InputView.java b/src/main/java/chess/view/InputView.java new file mode 100644 index 00000000000..517c55c5e96 --- /dev/null +++ b/src/main/java/chess/view/InputView.java @@ -0,0 +1,18 @@ +package chess.view; + +import java.util.Scanner; + +public class InputView { + + private final static Scanner scanner = new Scanner(System.in); + + public static String inputCurrentCoordinate() { + System.out.println("움직이고 싶은 말의 좌표를 입력하시오. (x,y)"); + return scanner.nextLine(); + } + + public static String inputDestinationCoordinate() { + System.out.println("도착지의 좌표를 입력하시오. (x,y)"); + return scanner.nextLine(); + } +} diff --git a/src/main/java/chess/view/OutputView.java b/src/main/java/chess/view/OutputView.java new file mode 100644 index 00000000000..72d9195c605 --- /dev/null +++ b/src/main/java/chess/view/OutputView.java @@ -0,0 +1,78 @@ +package chess.view; + +import chess.position.Color; +import chess.board.Board; +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 java.util.List; + +public class OutputView { + + public static void printBoard(Board board) { + String[][] boardOutput = new String[8][8]; + List pieces = board.getPieces(); + for (int i = 0; i < boardOutput.length; i++) { + for (int j = 0; j < boardOutput[i].length; j++) { + boardOutput[i][j] = "."; + } + } + for (Piece piece : pieces) { + int row = piece.getPosition().row().ordinal(); + int column = piece.getPosition().column().ordinal(); + boardOutput[row][column] = PieceOutput.getPieceOutputByPieceAndColor(piece); + } + for (int i = 0; i < 8; i++) { + for (int j = 0; j < boardOutput.length; j++) { + System.out.printf("%s\t", boardOutput[i][j]); + } + System.out.printf("\t%d%n", 8 - i); + } + System.out.println(); + System.out.println("A\tB\tC\tD\tE\tF\tG\tH"); + } + + enum PieceOutput { + BISHOP("B"), + KING("K"), + KNIGHT("N"), + PAWN("P"), + QUEEN("Q"), + ROOK("R"); + + private final String output; + + PieceOutput(String output) { + this.output = output; + } + + private static String getPieceOutputByPieceAndColor(Piece piece) { + if (piece.getColor() == Color.BLACK) { + return getPieceOutputByPiece(piece).toLowerCase(); + } + return getPieceOutputByPiece(piece); + } + + private static String getPieceOutputByPiece(Piece piece) { + if (piece instanceof Bishop) { + return BISHOP.output; + } + if (piece instanceof King) { + return KING.output; + } + if (piece instanceof Knight) { + return KNIGHT.output; + } + if (piece instanceof Pawn) { + return PAWN.output; + } + if (piece instanceof Queen) { + return QUEEN.output; + } + return ROOK.output; + } + } +} diff --git a/src/test/java/chess/ColumnTest.java b/src/test/java/chess/ColumnTest.java index e43523240f7..571288f13b4 100644 --- a/src/test/java/chess/ColumnTest.java +++ b/src/test/java/chess/ColumnTest.java @@ -1,5 +1,6 @@ package chess; +import chess.position.Column; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/chess/Fixtures.java b/src/test/java/chess/Fixtures.java index f940ab37137..64cac0f44f1 100644 --- a/src/test/java/chess/Fixtures.java +++ b/src/test/java/chess/Fixtures.java @@ -1,5 +1,9 @@ package chess; +import chess.position.Column; +import chess.position.Position; +import chess.position.Row; + @SuppressWarnings("unused") public final class Fixtures { diff --git a/src/test/java/chess/PositionTest.java b/src/test/java/chess/PositionTest.java index 3ad7cc64084..02384b050db 100644 --- a/src/test/java/chess/PositionTest.java +++ b/src/test/java/chess/PositionTest.java @@ -1,5 +1,7 @@ package chess; +import chess.position.Movement; +import chess.position.Position; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/test/java/chess/RowTest.java b/src/test/java/chess/RowTest.java index fcb65485410..62d98ca8de9 100644 --- a/src/test/java/chess/RowTest.java +++ b/src/test/java/chess/RowTest.java @@ -1,5 +1,6 @@ package chess; +import chess.position.Row; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/chess/piece/BishopTest.java b/src/test/java/chess/piece/BishopTest.java new file mode 100644 index 00000000000..994f88a628c --- /dev/null +++ b/src/test/java/chess/piece/BishopTest.java @@ -0,0 +1,35 @@ +package chess.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.position.Color; +import chess.Fixtures; +import chess.position.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class BishopTest { + + @DisplayName("대각선으로 이동할 수 있다.") + @Test + void move_diagonal() { + Position current = Fixtures.C1; + Position dest = Fixtures.B2; + Bishop bishop = new Bishop(Color.WHITE, current); + + bishop.move(-1, 1); + + assertThat(bishop.getPosition()).isEqualTo(dest); + } + + @DisplayName("대각선이 아니라면 이동할 수 없다.") + @Test + void cant_move_not_diagonal() { + Position current = Fixtures.C1; + Bishop bishop = new Bishop(Color.WHITE, current); + + assertThatThrownBy(() -> bishop.move(1, 0)) + .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 00000000000..7c86c524698 --- /dev/null +++ b/src/test/java/chess/piece/KingTest.java @@ -0,0 +1,72 @@ +package chess.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.position.Color; +import chess.Fixtures; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class KingTest { + + @DisplayName("상하좌우로 한 칸 이상 이동하면 예외를 던진다.") + @ParameterizedTest + @CsvSource(value = { + "2, 0", + "0, 2", + "2, 1", + "1, 2" + }) + void cant_move_over_two_step(int x, int y) { + King king = new King(Color.WHITE, Fixtures.D1); + + assertThatThrownBy(() -> king.move(x, y)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("상하로 한 칸 이동 가능하다.") + @Test + void move_vertical_one_step() { + King king = new King(Color.WHITE, Fixtures.D1); + + king.move(0, 1); + + assertThat(king.getPosition()).isEqualTo(Fixtures.D2); + } + + @DisplayName("좌우로 한 칸 이동 가능하다.") + @Test + void move_horizontal_one_step() { + King king = new King(Color.WHITE, Fixtures.D1); + + king.move(1, 0); + + assertThat(king.getPosition()).isEqualTo(Fixtures.E1); + } + + @DisplayName("대각선으로 한 칸 이동 가능하다.") + @Test + void move_diagonal_one_step() { + King king = new King(Color.WHITE, Fixtures.D1); + + king.move(1, 1); + + assertThat(king.getPosition()).isEqualTo(Fixtures.E2); + } + + @DisplayName("장기판 밖으로는 이동 불가능하다.") + @ParameterizedTest + @CsvSource(value = { + "-1, 0", + "0, -1", + }) + void cant_move_out_of_board(int x, int y) { + King king = new King(Color.WHITE, Fixtures.A1); + + assertThatThrownBy(() -> king.move(x, y)) + .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 00000000000..ff0b3a341f7 --- /dev/null +++ b/src/test/java/chess/piece/KnightTest.java @@ -0,0 +1,86 @@ +package chess.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.position.Color; +import chess.Fixtures; +import chess.position.Position; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class KnightTest { + + Knight knight; + + @BeforeEach + void initKnight() { + knight = new Knight(Color.WHITE, Fixtures.D4); + } + + @DisplayName("말은 상하좌우 한 칸과 대각선으로 이동 가능하다.") + @ParameterizedTest + @MethodSource("knightDestination") + void move_up_and_up_right(int x, int y, Position dest) { + knight.move(x, y); + + assertThat(knight.getPosition()).isEqualTo(dest); + } + + static Stream knightDestination() { + return Stream.of( + Arguments.of(1, 2, Fixtures.E6), + Arguments.of(-1, 2, Fixtures.C6), + Arguments.of(1, -2, Fixtures.E2), + Arguments.of(-1, -2, Fixtures.C2), + Arguments.of(2, 1, Fixtures.F5), + Arguments.of(2, -1, Fixtures.F3), + Arguments.of(-2, 1, Fixtures.B5), + Arguments.of(-2, -1, Fixtures.B3) + ); + } + + @DisplayName("말은 상하좌우 한 칸과 대각선이 아니면 이동이 불가능하다.") + @ParameterizedTest + @MethodSource("notKnightMovingRule") + void move_up_and_up_right(int x, int y) { + assertThatThrownBy(() -> knight.move(x, y)) + .isInstanceOf(IllegalArgumentException.class); + } + + static Stream notKnightMovingRule() { + return Stream.of( + Arguments.of(2, 2), + Arguments.of(0, 2), + Arguments.of(2, 0), + Arguments.of(3, 2), + Arguments.of(2, 3), + Arguments.of(5, 2) + ); + } + + @DisplayName("말은 장기판 밖으로 이동할 수 없다.") + @ParameterizedTest + @MethodSource("outOfBoardMove") + void cant_move_out_of_board(int x, int y) { + Knight knight_A1 = new Knight(Color.WHITE, Fixtures.A1); + + assertThatThrownBy(() -> knight_A1.move(x, y)) + .isInstanceOf(IllegalArgumentException.class); + } + + static Stream outOfBoardMove() { + return Stream.of( + Arguments.of(-1, 2), + Arguments.of(1, -2), + Arguments.of(-1, -2), + Arguments.of(2, -1), + Arguments.of(-2, 1), + Arguments.of(-2, -1) + ); + } +} diff --git a/src/test/java/chess/piece/PawnTest.java b/src/test/java/chess/piece/PawnTest.java new file mode 100644 index 00000000000..ff30f2292a2 --- /dev/null +++ b/src/test/java/chess/piece/PawnTest.java @@ -0,0 +1,106 @@ +package chess.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.position.Color; +import chess.Fixtures; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PawnTest { + + @DisplayName("백팀이라면 위로 한 칸 이동 가능하다.") + @Test + void move_up_one_step_white_color() { + Pawn pawn = new Pawn(Color.WHITE, Fixtures.D2); + + pawn.move(0, 1); + + assertThat(pawn.getPosition()).isEqualTo(Fixtures.D3); + } + + @DisplayName("흑팀이라면 아래로 한 칸 이동 가능하다.") + @Test + void move_down_one_step_black_color() { + Pawn pawn = new Pawn(Color.BLACK, Fixtures.D7); + + pawn.move(0, -1); + + assertThat(pawn.getPosition()).isEqualTo(Fixtures.D6); + } + + @DisplayName("백팀이라면 처음 이동할 때, 위로 두 칸 이동 가능하다.") + @Test + void move_up_two_step_white_color_first_move() { + Pawn pawn = new Pawn(Color.WHITE, Fixtures.D2); + + pawn.move(0, 2); + + assertThat(pawn.getPosition()).isEqualTo(Fixtures.D4); + } + + @DisplayName("흑팀이라면 처음 이동할 때 아래로 두 칸 이동 가능하다.") + @Test + void move_down_two_step_black_color_first_move() { + Pawn pawn = new Pawn(Color.BLACK, Fixtures.D7); + + pawn.move(0, -2); + + assertThat(pawn.getPosition()).isEqualTo(Fixtures.D5); + } + + @DisplayName("처음 이동 아니라면, 백팀은 위로 두 칸 이동할 수 없다.") + @Test + void cant_move_up_two_step_white_color_not_first_move() { + Pawn pawn = new Pawn(Color.WHITE, Fixtures.D3); + + assertThatThrownBy(()->pawn.move(0, 2)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("처음 이동 아니라면, 흑팀은 아래로 두 칸 이동할 수 없다.") + @Test + void cnat_move_down_two_step_black_color_not_first_move() { + Pawn pawn = new Pawn(Color.BLACK, Fixtures.D6); + + assertThatThrownBy(()->pawn.move(0, -2)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("백팀이라면 아래로 이동 불가능하다.") + @Test + void cant_move_down_white_color() { + Pawn pawn = new Pawn(Color.WHITE, Fixtures.D2); + + assertThatThrownBy(()->pawn.move(0, -1)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("흑팀이라면 위로 이동 불가능하다.") + @Test + void cant_move_up_black_color() { + Pawn pawn = new Pawn(Color.BLACK, Fixtures.D7); + + assertThatThrownBy(()->pawn.move(0, 1)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("백팀은 위로 두 칸 이상 이동할 수 없다.") + @Test + void cant_move_up_over_two_step_white_color() { + Pawn pawn = new Pawn(Color.WHITE, Fixtures.D3); + + assertThatThrownBy(()->pawn.move(0, 3)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("흑팀은 아래로 두 칸 이상 이동할 수 없다.") + @Test + void cnat_move_down_over_two_step_black_color() { + Pawn pawn = new Pawn(Color.BLACK, Fixtures.D6); + + assertThatThrownBy(()->pawn.move(0, -3)) + .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 00000000000..cb3294b6a2d --- /dev/null +++ b/src/test/java/chess/piece/QueenTest.java @@ -0,0 +1,76 @@ +package chess.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.position.Color; +import chess.Fixtures; +import chess.position.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class QueenTest { + + @DisplayName("상하로 원하는 만큼 이동 가능하다.") + @Test + void move_vertical() { + Queen queen = new Queen(Color.WHITE, Fixtures.A1); + + queen.move(0, 5); + + assertThat(queen.getPosition()).isEqualTo(Fixtures.A6); + } + + @DisplayName("좌우로 원하는 만큼 이동 가능하다.") + @Test + void move_horizontal() { + Queen queen = new Queen(Color.WHITE, Fixtures.A1); + + queen.move(5, 0); + + assertThat(queen.getPosition()).isEqualTo(Fixtures.F1); + } + + @DisplayName("대각선으로 이동할 수 있다.") + @Test + void move_diagonal() { + Position current = Fixtures.C1; + Position dest = Fixtures.B2; + Queen queen = new Queen(Color.WHITE, current); + + queen.move(-1, 1); + + assertThat(queen.getPosition()).isEqualTo(dest); + } + + @DisplayName("상하좌우 대각선외에는 이동 불가능하다.") + @ParameterizedTest + @CsvSource(value = { + "5, 4", + "2, 1", + }) + void cant_move_not_diagonal_not_horizontal_or_vertical(int x, int y) { + Position current = Fixtures.C1; + Queen queen = new Queen(Color.WHITE, current); + + assertThatThrownBy(() -> queen.move(x, y)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("장기판 밖으로는 이동 불가능하다.") + @ParameterizedTest + @CsvSource(value = { + "-1, 0", + "100, 0", + "0, -1", + "0, 100" + }) + void cant_move_out_of_board(int x, int y) { + Queen queen = new Queen(Color.WHITE, Fixtures.A1); + + assertThatThrownBy(() -> queen.move(x, y)) + .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 00000000000..035a5834ad6 --- /dev/null +++ b/src/test/java/chess/piece/RookTest.java @@ -0,0 +1,58 @@ +package chess.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.position.Color; +import chess.Fixtures; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class RookTest { + + @DisplayName("대각선으로 이동하려고 하면, 예외를 던진다.") + @Test + void throw_exception_move_diagonal() { + Rook rook = new Rook(Color.WHITE, Fixtures.A1); + + assertThatThrownBy(() -> rook.move(1, 1)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("상하로 원하는 만큼 이동 가능하다.") + @Test + void move_vertical() { + Rook rook = new Rook(Color.WHITE, Fixtures.A1); + + rook.move(0, 5); + + assertThat(rook.getPosition()).isEqualTo(Fixtures.A6); + } + + @DisplayName("좌우로 원하는 만큼 이동 가능하다.") + @Test + void move_horizontal() { + Rook rook = new Rook(Color.WHITE, Fixtures.A1); + + rook.move(5, 0); + + assertThat(rook.getPosition()).isEqualTo(Fixtures.F1); + } + + @DisplayName("장기판 밖으로는 이동 불가능하다.") + @ParameterizedTest + @CsvSource(value = { + "-1, 0", + "100, 0", + "0, -1", + "0, 100" + }) + void cant_move_out_of_board(int x, int y) { + Rook rook = new Rook(Color.WHITE, Fixtures.A1); + + assertThatThrownBy(() -> rook.move(x, y)) + .isInstanceOf(IllegalArgumentException.class); + } +}