diff --git a/build.gradle b/build.gradle index 3697236c6fb..20ad08a5a5e 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,8 @@ dependencies { testImplementation platform('org.assertj:assertj-bom:3.25.1') testImplementation('org.junit.jupiter:junit-jupiter') testImplementation('org.assertj:assertj-core') + + runtimeOnly("com.mysql:mysql-connector-j:8.3.0") } java { diff --git a/docker/db/mysql/init/ddl.sql b/docker/db/mysql/init/ddl.sql new file mode 100644 index 00000000000..439f08487e5 --- /dev/null +++ b/docker/db/mysql/init/ddl.sql @@ -0,0 +1,21 @@ +DROP DATABASE IF EXISTS chess; +CREATE DATABASE chess DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; +GRANT ALL PRIVILEGES ON `chess`.* TO 'user'@'%'; +FLUSH PRIVILEGES; +SHOW TABLES in chess; +USE chess; + +CREATE TABLE Turn +( + turn_id INT AUTO_INCREMENT PRIMARY KEY, + turn VARCHAR(255) NOT NULL +); + +CREATE TABLE Piece +( + piece_id INT AUTO_INCREMENT PRIMARY KEY, + color VARCHAR(255) NOT NULL, + pieceType VARCHAR(255) NOT NULL, + `rank` INT NOT NULL, + file INT NOT NULL +); diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 00000000000..558a1d5a53f --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,18 @@ +version: "3.9" +services: + db: + image: mysql:8.0.28 + platform: linux/x86_64 + restart: always + ports: + - "13306:3306" + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: chess + MYSQL_USER: user + MYSQL_PASSWORD: password + TZ: Asia/Seoul + volumes: + - ./db/mysql/data:/var/lib/mysql + - ./db/mysql/config:/etc/mysql/conf.d + - ./db/mysql/init:/docker-entrypoint-initdb.d diff --git a/docs/README.md b/docs/README.md index 9c01906f5cb..db2227be08d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,14 +7,24 @@ ## 보드 -- [x] 보드는 체스 말과 위치를 가진다. +- [x] 보드는 체스 말별 위치를 가진다. - [x] 보드에서 체스 말을 이동시킬 수 있다. -- [x] 보드에서 체스 말을 이동할 수 없는 경우 예외가 발생한다. + - [x] 이동하려는 말의 팀이 현재 턴이여야 이동이 가능하다. + - [x] 보드에서 체스 말을 이동할 수 없는 경우 예외가 발생한다. - [x] 체스 말과 위치 정보를 반환할 수 있다. +- [x] King이 잡힐 시에 게임이 종료된다. + +### 게임 턴 + +- [x] 게임 시작 시 턴은 검정팀부터 시작한다. +- [x] 다음 차례를 반환한다. + - [x] 2팀이 턴을 번갈아가며 수행한다. +- [x] 해당 색상의 팀 차례인지 확인한다. ## 체스 말 -- [x] 체스 말은 진영의 색깔과 종류를 가진다. +- [x] 체스 말은 말의 종류를 가진다. +- [x] 체스 말은 이동을 가진다. - [x] 체스 말은 출발 위치, 도착 위치, 첫 이동 여부, 말의 위치 정보 목록을 바탕으로 움직일 수 있는지 확인한다. - [x] 폰 - [x] 이동이 가능한 경우 @@ -38,7 +48,8 @@ ## 말 종류 -- [x] 체스 말은 이동을 가진다. +- [x] 체스 말 종류는 각각의 이동 목록을 가진다. +- [x] 체스 말 종류는 각각의 점수 규칙을 가진다. ## 이동 @@ -82,7 +93,19 @@ - [x] File값이 최소인지 확인한다. - [x] File값이 최대인지 확인한다. -## 장애물 규칙 +### 점수 규칙 + +- [x] 점수 규칙은 기본점수를 가진다. +- [x] 폰에 대한 점수 규칙은 다음과 같다. + - [x] 같은 세로 줄에 같은 색의 폰이 여러개 있는 경우, 각각 0.5점 + - [x] 같은 세로 줄에 같은 색의 폰이 하나만 있는 경우, 1점(기본점수) +- [x] 폰을 제외한 다른 말들은 기본 점수 * 말의 개수이다. + +## 심판 + +- [x] 팀별 피스 목록을 점수 규칙에 맞게 점수를 계산한다. + +## 공격 유형 ### 공격 공통 @@ -90,13 +113,21 @@ - [x] 목표 위치가 상대편 말일 경우, 공격할 수 있기 때문에 장애물 목록에서 제거 - [x] 출발 위치는 현재 자신의 위치이므로, 장애물 목록에서 제거 -### 대각선 공격 시 (공격 공통 + 추가) +### 이동 방향으로 공격 + +- [x] EMPTY가 아닌 Piece들의 위치 목록 조회 +- [x] 목표 위치가 상대편 말일 경우, 공격할 수 있기 때문에 장애물 목록에서 제거 +- [x] 출발 위치는 현재 자신의 위치이므로, 장애물 목록에서 제거 + +### 이동 방향과 다른 대각선 공격 아래의 경우에 장애물 목록에 도착 위치 Piece를 추가한다. - [x] 도착 위치가 상대편 말이지만 출발 위치와 도착 위치가 직선 이동일 경우 - [x] 도착 위치가 EMPTY이고 출발 위치와 도착 위치가 대각 이동일 경우 +--- + ## 입출력 - [x] 입력 기능 구현 diff --git a/src/main/java/chess/Application.java b/src/main/java/chess/Application.java index d3b4c4b5091..7d4ca05dbbb 100644 --- a/src/main/java/chess/Application.java +++ b/src/main/java/chess/Application.java @@ -1,13 +1,17 @@ package chess; import chess.controller.ChessGameController; +import chess.repository.piece.JdbcPieceRepository; +import chess.repository.turn.JdbcTurnRepository; +import chess.service.ChessGameService; import chess.view.InputView; import chess.view.OutputView; public class Application { public static void main(String[] args) { try { - ChessGameController chessGameController = new ChessGameController(new InputView(), new OutputView()); + ChessGameController chessGameController = new ChessGameController(new InputView(), new OutputView(), + new ChessGameService(new JdbcPieceRepository(), new JdbcTurnRepository())); chessGameController.start(); } catch (RuntimeException e) { System.out.println(e.getMessage()); diff --git a/src/main/java/chess/controller/ChessGameController.java b/src/main/java/chess/controller/ChessGameController.java index 64082cfe20a..c8ddc8dce51 100644 --- a/src/main/java/chess/controller/ChessGameController.java +++ b/src/main/java/chess/controller/ChessGameController.java @@ -1,61 +1,127 @@ package chess.controller; -import chess.domain.board.Board; -import chess.domain.board.BoardInitializer; +import chess.domain.board.ClearBoardInitializer; +import chess.domain.board.SavedBoardInitializer; +import chess.domain.game.Game; +import chess.domain.piece.Color; +import chess.domain.piece.Piece; import chess.domain.position.Position; +import chess.domain.scorerule.Referee; +import chess.service.ChessGameService; import chess.view.GameCommand; import chess.view.InputView; import chess.view.OutputView; +import java.util.Map; public class ChessGameController { private final OutputView outputView; private final InputView inputView; + private final ChessGameService gameService; - public ChessGameController(final InputView inputView, final OutputView outputView) { + public ChessGameController(final InputView inputView, final OutputView outputView, + final ChessGameService gameService) { this.inputView = inputView; this.outputView = outputView; + this.gameService = gameService; } public void start() { outputView.printInitialMessage(); - GameCommand gameCommand = inputView.getGameCommand(); - checkStart(gameCommand); + checkStart(inputView.getGameCommand()); + + if (gameService.existSavedGame() && inputView.isPlaySavedGame()) { + playSavedGame(); + return; + } - play(); + playNewGame(); } private void checkStart(final GameCommand gameCommand) { - if (gameCommand != GameCommand.START) { + if (gameCommand.isNotStartCommand()) { throw new IllegalArgumentException("시작 명령어를 입력해주세요."); } } - private void play() { - Board board = initializeBoard(); + private void playSavedGame() { + Color turnColor = gameService.findTurn() + .orElseThrow(() -> new IllegalStateException("저장된 게임이 정상적이지 않습니다. 다시 실행해주세요.")); + Game game = Game.of(new SavedBoardInitializer(gameService.findAllPiece()), turnColor); + outputView.printBoard(game.getBoard()); + outputView.printCurrentTurn(game.getTurn()); + execute(game); + } + + private void playNewGame() { + Game game = new Game(new ClearBoardInitializer()); + outputView.printBoard(game.getBoard()); + execute(game); + } + + private void execute(final Game game) { GameCommand gameCommand = inputView.getGameCommand(); - while (gameCommand == GameCommand.MOVE) { - playTurn(board); + while (isGameActionIfOngoing(game, gameCommand)) { + executeMoveCommand(game, gameCommand); + executeStatusCommand(game, gameCommand); gameCommand = inputView.getGameCommand(); } - if (gameCommand == GameCommand.START) { - play(); + checkGameRestart(gameCommand); + checkGameFinish(game); + checkGameTerminate(game, gameCommand); + } + + private boolean isGameActionIfOngoing(final Game game, final GameCommand gameCommand) { + return !game.isFinish() && gameCommand.isOnGoingGameCommand(); + } + + private void executeMoveCommand(final Game game, final GameCommand gameCommand) { + if (gameCommand.isMoveCommand()) { + executeTurn(game); + } + } + + private void executeStatusCommand(final Game game, final GameCommand gameCommand) { + if (gameCommand.isViewStatusCommand()) { + viewScore(game.getBoard()); + } + } + + private void checkGameRestart(final GameCommand gameCommand) { + if (gameCommand.isStartGameCommand()) { + playNewGame(); + } + } + + private void checkGameFinish(final Game game) { + if (game.isFinish()) { + outputView.printFinish(); + gameService.delete(); } } - private Board initializeBoard() { - Board board = new Board(new BoardInitializer()); - outputView.printBoard(board); - return board; + private void checkGameTerminate(final Game game, final GameCommand gameCommand) { + if (gameCommand.isEndCommand()) { + gameService.saveGame(game); + } } - private void playTurn(final Board board) { + private void executeTurn(final Game game) { Position source = Position.of(inputView.getPosition()); Position target = Position.of(inputView.getPosition()); - board.tryMove(source, target); - outputView.printBoard(board); + game.move(source, target); + outputView.printBoard(game.getBoard()); + } + + private void viewScore(final Map board) { + Referee referee = new Referee(board); + + double blackTeamScore = referee.calculateScore(Color.BLACK); + outputView.printScore(blackTeamScore, Color.BLACK); + double whiteTeamScore = referee.calculateScore(Color.WHITE); + outputView.printScore(whiteTeamScore, Color.WHITE); } } diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index a469970940c..b37cada992e 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -1,36 +1,46 @@ package chess.domain.board; +import chess.domain.piece.Empty; import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; import chess.domain.position.Position; import java.util.Collections; import java.util.Map; public class Board { - - private final BoardInitializer boardInitializer; private final Map board; public Board(final BoardInitializer boardInitializer) { - this.boardInitializer = boardInitializer; this.board = boardInitializer.initialize(); } - public void tryMove(final Position source, final Position target) { - Piece piece = board.get(source); - if (piece.canMove(source, target, getBoard())) { - move(source, target, piece); - return; + public boolean tryMove(final Position source, final Position target) { + Piece sourcePiece = board.get(source); + if (sourcePiece.canMove(source, target, board)) { + move(source, target); + return true; } - throw new IllegalArgumentException("이동이 불가능한 위치입니다."); + return false; + } + + private void move(final Position source, final Position target) { + board.put(target, board.get(source)); + board.put(source, Empty.getInstance()); } - private void move(final Position source, final Position target, final Piece piece) { - board.put(target, piece); - board.put(source, Piece.getEmptyPiece()); + public boolean isKingKilled() { + int kingCount = (int) board.entrySet().stream() + .filter(entry -> entry.getValue().getPieceType() == PieceType.KING) + .count(); + return kingCount < 2; } public Map getBoard() { return Collections.unmodifiableMap(board); } + + public Piece get(final Position source) { + return board.get(source); + } } diff --git a/src/main/java/chess/domain/board/BoardInitializer.java b/src/main/java/chess/domain/board/BoardInitializer.java index 44a85ced731..261599a9ac8 100644 --- a/src/main/java/chess/domain/board/BoardInitializer.java +++ b/src/main/java/chess/domain/board/BoardInitializer.java @@ -1,61 +1,10 @@ package chess.domain.board; -import static chess.domain.piece.Color.BLACK; -import static chess.domain.piece.Color.WHITE; - -import chess.domain.piece.Color; import chess.domain.piece.Piece; import chess.domain.position.Position; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.stream.Collector; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -public class BoardInitializer { - - private static final int MINIMUM_BOARD_POSITION = 1; - private static final int MAXIMUM_BOARD_POSITION = 8; - - private final Map initialPiecePositions; - - public BoardInitializer() { - Map initialPiecePositions = generateEmptyBoard(); - initialPiecePositions.putAll(getPiecesByColor(WHITE)); - initialPiecePositions.putAll(getPiecesByColor(BLACK)); - this.initialPiecePositions = initialPiecePositions; - } - - public Map initialize() { - return new HashMap<>(initialPiecePositions); - } - - private Map generateEmptyBoard() { - return IntStream.rangeClosed(MINIMUM_BOARD_POSITION, MAXIMUM_BOARD_POSITION) - .boxed() - .flatMap(this::generateHorizontalLine) - .collect(generateEntry(Piece.getEmptyPiece())); - } - - private Stream generateHorizontalLine(final int rank) { - return IntStream.rangeClosed(MINIMUM_BOARD_POSITION, MAXIMUM_BOARD_POSITION) - .mapToObj(file -> Position.of(file, rank)); - } - private Collector> generateEntry(final Piece piece) { - return Collectors.toMap( - position -> position, - position -> piece - ); - } +public interface BoardInitializer { - private Map getPiecesByColor(final Color color) { - List whitePieces = InitialPiecePosition.getInitialPositionByColor(color); - return whitePieces.stream() - .flatMap(initialPiecePosition -> initialPiecePosition.getInitialPositions().stream() - .collect(generateEntry(initialPiecePosition.getPiece())).entrySet().stream()) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (existing, replacement) -> existing)); - } + Map initialize(); } diff --git a/src/main/java/chess/domain/board/ClearBoardInitializer.java b/src/main/java/chess/domain/board/ClearBoardInitializer.java new file mode 100644 index 00000000000..e1c9fafe6d8 --- /dev/null +++ b/src/main/java/chess/domain/board/ClearBoardInitializer.java @@ -0,0 +1,63 @@ +package chess.domain.board; + +import static chess.domain.piece.Color.BLACK; +import static chess.domain.piece.Color.WHITE; + +import chess.domain.piece.Color; +import chess.domain.piece.Empty; +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class ClearBoardInitializer implements BoardInitializer { + + private static final int MINIMUM_BOARD_POSITION = 1; + private static final int MAXIMUM_BOARD_POSITION = 8; + + private final Map initialPiecePositions; + + public ClearBoardInitializer() { + Map initialPiecePositions = generateEmptyBoard(); + initialPiecePositions.putAll(getPiecesByColor(WHITE)); + initialPiecePositions.putAll(getPiecesByColor(BLACK)); + this.initialPiecePositions = initialPiecePositions; + } + + @Override + public Map initialize() { + return new HashMap<>(initialPiecePositions); + } + + private Map generateEmptyBoard() { + return IntStream.rangeClosed(MINIMUM_BOARD_POSITION, MAXIMUM_BOARD_POSITION) + .boxed() + .flatMap(this::generateHorizontalLine) + .collect(generateEntry(Empty.getInstance())); + } + + private Stream generateHorizontalLine(final int rank) { + return IntStream.rangeClosed(MINIMUM_BOARD_POSITION, MAXIMUM_BOARD_POSITION) + .mapToObj(file -> Position.of(file, rank)); + } + + private Collector> generateEntry(final Piece piece) { + return Collectors.toMap( + position -> position, + position -> piece + ); + } + + private Map getPiecesByColor(final Color color) { + List whitePieces = InitialPiecePosition.getInitialPositionByColor(color); + return whitePieces.stream() + .flatMap(initialPiecePosition -> initialPiecePosition.getInitialPositions().stream() + .collect(generateEntry(initialPiecePosition.getPiece())).entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (existing, replacement) -> existing)); + } +} diff --git a/src/main/java/chess/domain/board/InitialPiecePosition.java b/src/main/java/chess/domain/board/InitialPiecePosition.java index 98a461beac3..62c7429d195 100644 --- a/src/main/java/chess/domain/board/InitialPiecePosition.java +++ b/src/main/java/chess/domain/board/InitialPiecePosition.java @@ -1,41 +1,47 @@ package chess.domain.board; +import chess.domain.piece.Bishop; import chess.domain.piece.Color; +import chess.domain.piece.King; +import chess.domain.piece.Knight; +import chess.domain.piece.Pawn; import chess.domain.piece.Piece; import chess.domain.piece.PieceType; +import chess.domain.piece.Queen; +import chess.domain.piece.Rook; import chess.domain.position.Position; import java.util.Arrays; import java.util.List; public enum InitialPiecePosition { - BLACK_KING(new Piece(PieceType.KING, Color.BLACK), List.of(Position.of(5, 8))), - WHITE_KING(new Piece(PieceType.KING, Color.WHITE), List.of(Position.of(5, 1))), + BLACK_KING(new King(Color.BLACK), List.of(Position.of(5, 8))), + WHITE_KING(new King(Color.WHITE), List.of(Position.of(5, 1))), - BLACK_QUEEN(new Piece(PieceType.QUEEN, Color.BLACK), List.of(Position.of(4, 8))), - WHITE_QUEEN(new Piece(PieceType.QUEEN, Color.WHITE), List.of(Position.of(4, 1))), + BLACK_QUEEN(new Queen(Color.BLACK), List.of(Position.of(4, 8))), + WHITE_QUEEN(new Queen(Color.WHITE), List.of(Position.of(4, 1))), - BLACK_BISHOP(new Piece(PieceType.BISHOP, Color.BLACK), List.of( + BLACK_BISHOP(new Bishop(Color.BLACK), List.of( Position.of(3, 8), Position.of(6, 8))), - WHITE_BISHOP(new Piece(PieceType.BISHOP, Color.WHITE), List.of( + WHITE_BISHOP(new Bishop(Color.WHITE), List.of( Position.of(3, 1), Position.of(6, 1))), - BLACK_ROOK(new Piece(PieceType.ROOK, Color.BLACK), List.of( + BLACK_ROOK(new Rook(Color.BLACK), List.of( Position.of(1, 8), Position.of(8, 8))), - WHITE_ROOK(new Piece(PieceType.ROOK, Color.WHITE), List.of( + WHITE_ROOK(new Rook(Color.WHITE), List.of( Position.of(1, 1), Position.of(8, 1))), - BLACK_KNIGHT(new Piece(PieceType.KNIGHT, Color.BLACK), List.of( + BLACK_KNIGHT(new Knight(Color.BLACK), List.of( Position.of(2, 8), Position.of(7, 8))), - WHITE_KNIGHT(new Piece(PieceType.KNIGHT, Color.WHITE), List.of( + WHITE_KNIGHT(new Knight(Color.WHITE), List.of( Position.of(2, 1), Position.of(7, 1))), - BLACK_PAWN(new Piece(PieceType.PAWN, Color.BLACK), List.of( + BLACK_PAWN(new Pawn(Color.BLACK), List.of( Position.of(1, 7), Position.of(2, 7), Position.of(3, 7), @@ -45,7 +51,7 @@ public enum InitialPiecePosition { Position.of(7, 7), Position.of(8, 7))), - WHITE_PAWN(new Piece(PieceType.PAWN, Color.WHITE), List.of( + WHITE_PAWN(new Pawn(Color.WHITE), List.of( Position.of(1, 2), Position.of(2, 2), Position.of(3, 2), @@ -70,7 +76,7 @@ public static List getInitialPositionByColor(Color color) } public boolean isSameColor(final Color color) { - return piece.color() == color; + return color.isSameColor(piece.getColor()); } public List getInitialPositions() { @@ -82,7 +88,7 @@ public Piece getPiece() { } public boolean isPawnFirstMove(final Position position) { - return piece.pieceType() == PieceType.PAWN && + return piece.isSameType(PieceType.PAWN) && initialPositions.contains(position); } } diff --git a/src/main/java/chess/domain/board/SavedBoardInitializer.java b/src/main/java/chess/domain/board/SavedBoardInitializer.java new file mode 100644 index 00000000000..f0a2b02f75c --- /dev/null +++ b/src/main/java/chess/domain/board/SavedBoardInitializer.java @@ -0,0 +1,19 @@ +package chess.domain.board; + +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import java.util.Map; + +public class SavedBoardInitializer implements BoardInitializer { + + private final Map savedBoard; + + public SavedBoardInitializer(final Map savedBoard) { + this.savedBoard = savedBoard; + } + + @Override + public Map initialize() { + return savedBoard; + } +} diff --git a/src/main/java/chess/domain/game/Game.java b/src/main/java/chess/domain/game/Game.java new file mode 100644 index 00000000000..d4e63dbadc6 --- /dev/null +++ b/src/main/java/chess/domain/game/Game.java @@ -0,0 +1,51 @@ +package chess.domain.game; + +import chess.domain.board.Board; +import chess.domain.board.BoardInitializer; +import chess.domain.game.status.BlackTurn; +import chess.domain.game.status.GameStatus; +import chess.domain.game.status.WhiteTurn; +import chess.domain.piece.Color; +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import java.util.Map; + +public class Game { + private GameStatus gameStatus; + + public Game(final BoardInitializer boardInitializer) { + this.gameStatus = new BlackTurn(new Board(boardInitializer)); + } + + public Game(final GameStatus gameStatus) { + this.gameStatus = gameStatus; + } + + public static Game of(final BoardInitializer boardInitializer, final Color turnColor) { + return new Game(findGameStatus(boardInitializer, turnColor)); + } + + private static GameStatus findGameStatus(final BoardInitializer boardInitializer, final Color turnColor) { + if (turnColor.isSameColor(Color.BLACK)) { + return new BlackTurn(new Board(boardInitializer)); + } + + return new WhiteTurn(new Board(boardInitializer)); + } + + public void move(final Position source, final Position target) { + gameStatus = gameStatus.move(source, target); + } + + public boolean isFinish() { + return gameStatus.isFinish(); + } + + public Map getBoard() { + return gameStatus.getBoard().getBoard(); + } + + public Color getTurn() { + return gameStatus.getTurn(); + } +} diff --git a/src/main/java/chess/domain/game/status/BlackTurn.java b/src/main/java/chess/domain/game/status/BlackTurn.java new file mode 100644 index 00000000000..52a9c95f780 --- /dev/null +++ b/src/main/java/chess/domain/game/status/BlackTurn.java @@ -0,0 +1,46 @@ +package chess.domain.game.status; + +import chess.domain.board.Board; +import chess.domain.piece.Color; +import chess.domain.piece.Piece; +import chess.domain.position.Position; + +public class BlackTurn extends Running { + public BlackTurn(final Board board) { + super(board); + } + + @Override + public GameStatus move(final Position source, final Position target) { + checkTurn(source); + if (board().tryMove(source, target)) { + return checkKingKilled(); + } + + throw new IllegalArgumentException("해당 위치로 이동이 불가능합니다."); + } + + private void checkTurn(final Position source) { + Piece sourcePiece = board().get(source); + if (sourcePiece.isSameTeamColor(Color.WHITE)) { + throw new IllegalArgumentException("검정팀 차례입니다. 이동이 불가능합니다."); + } + } + + private GameStatus checkKingKilled() { + if (board().isKingKilled()) { + return new Finished(); + } + return new WhiteTurn(board()); + } + + @Override + public Color getTurn() { + return Color.BLACK; + } + + @Override + public Board getBoard() { + return board(); + } +} diff --git a/src/main/java/chess/domain/game/status/Finished.java b/src/main/java/chess/domain/game/status/Finished.java new file mode 100644 index 00000000000..d52633cb540 --- /dev/null +++ b/src/main/java/chess/domain/game/status/Finished.java @@ -0,0 +1,26 @@ +package chess.domain.game.status; + +import chess.domain.board.Board; +import chess.domain.piece.Color; +import chess.domain.position.Position; + +public class Finished implements GameStatus { + @Override + public GameStatus move(final Position source, final Position target) { + throw new IllegalStateException("게임이 종료되어 이동이 불가능합니다."); + } + + @Override + public boolean isFinish() { + return true; + } + + @Override + public Color getTurn() { + throw new IllegalStateException("게임이 종료되어 턴을 조회할 수 없습니다."); + } + + public Board getBoard() { + throw new IllegalStateException("게임이 종료되어 보드를 조회할 수 없습니다."); + } +} diff --git a/src/main/java/chess/domain/game/status/GameStatus.java b/src/main/java/chess/domain/game/status/GameStatus.java new file mode 100644 index 00000000000..1a7d5ba50e4 --- /dev/null +++ b/src/main/java/chess/domain/game/status/GameStatus.java @@ -0,0 +1,16 @@ +package chess.domain.game.status; + +import chess.domain.board.Board; +import chess.domain.piece.Color; +import chess.domain.position.Position; + +public interface GameStatus { + + GameStatus move(final Position source, final Position target); + + boolean isFinish(); + + Color getTurn(); + + Board getBoard(); +} diff --git a/src/main/java/chess/domain/game/status/Running.java b/src/main/java/chess/domain/game/status/Running.java new file mode 100644 index 00000000000..e6d7c903c48 --- /dev/null +++ b/src/main/java/chess/domain/game/status/Running.java @@ -0,0 +1,20 @@ +package chess.domain.game.status; + +import chess.domain.board.Board; + +public abstract class Running implements GameStatus { + private final Board board; + + protected Running(final Board board) { + this.board = board; + } + + @Override + public boolean isFinish() { + return false; + } + + protected Board board() { + return board; + } +} diff --git a/src/main/java/chess/domain/game/status/WhiteTurn.java b/src/main/java/chess/domain/game/status/WhiteTurn.java new file mode 100644 index 00000000000..0420e2e756e --- /dev/null +++ b/src/main/java/chess/domain/game/status/WhiteTurn.java @@ -0,0 +1,46 @@ +package chess.domain.game.status; + +import chess.domain.board.Board; +import chess.domain.piece.Color; +import chess.domain.piece.Piece; +import chess.domain.position.Position; + +public class WhiteTurn extends Running { + public WhiteTurn(final Board board) { + super(board); + } + + @Override + public GameStatus move(final Position source, final Position target) { + checkTurn(source); + if (board().tryMove(source, target)) { + return checkKingKilled(); + } + + throw new IllegalArgumentException("해당 위치로 이동이 불가능합니다."); + } + + private void checkTurn(final Position source) { + Piece sourcePiece = board().get(source); + if (sourcePiece.isSameTeamColor(Color.BLACK)) { + throw new IllegalArgumentException("화이트팀 차례입니다. 이동이 불가능합니다."); + } + } + + private GameStatus checkKingKilled() { + if (board().isKingKilled()) { + return new Finished(); + } + return new BlackTurn(board()); + } + + @Override + public Color getTurn() { + return Color.WHITE; + } + + @Override + public Board getBoard() { + return board(); + } +} diff --git a/src/main/java/chess/domain/movement/Movement.java b/src/main/java/chess/domain/movement/Movement.java index 2ee893ef6d1..3163a798ca2 100644 --- a/src/main/java/chess/domain/movement/Movement.java +++ b/src/main/java/chess/domain/movement/Movement.java @@ -3,6 +3,7 @@ import chess.domain.movement.direction.Direction; import chess.domain.movement.policy.Policy; import chess.domain.piece.Color; +import chess.domain.piece.Piece; import chess.domain.position.Position; public class Movement { @@ -15,8 +16,8 @@ public Movement(final Policy policy, final Direction direction) { this.direction = direction; } - public boolean isSatisfied(final Color color, final Position currentPosition, final boolean existEnemy) { - return policy.isSatisfied(color, currentPosition, existEnemy); + public boolean isSatisfied(final Color color, final Position currentPosition, final Piece tartgetPiece) { + return policy.isSatisfied(color, currentPosition, tartgetPiece); } public Direction getDirection() { diff --git a/src/main/java/chess/domain/movement/policy/ColorPolicy.java b/src/main/java/chess/domain/movement/policy/ColorPolicy.java index 183368dfe56..7bd7723165b 100644 --- a/src/main/java/chess/domain/movement/policy/ColorPolicy.java +++ b/src/main/java/chess/domain/movement/policy/ColorPolicy.java @@ -1,6 +1,7 @@ package chess.domain.movement.policy; import chess.domain.piece.Color; +import chess.domain.piece.Piece; import chess.domain.position.Position; public class ColorPolicy implements Policy { @@ -12,7 +13,7 @@ public ColorPolicy(final Color color) { } @Override - public boolean isSatisfied(final Color color, final Position currentPosition, final boolean existEnemy) { + public boolean isSatisfied(final Color color, final Position currentPosition, final Piece targetPiece) { return this.color == color; } } diff --git a/src/main/java/chess/domain/movement/policy/CombinationPolicy.java b/src/main/java/chess/domain/movement/policy/CombinationPolicy.java index 80460314a3b..527f91cdb23 100644 --- a/src/main/java/chess/domain/movement/policy/CombinationPolicy.java +++ b/src/main/java/chess/domain/movement/policy/CombinationPolicy.java @@ -1,6 +1,7 @@ package chess.domain.movement.policy; import chess.domain.piece.Color; +import chess.domain.piece.Piece; import chess.domain.position.Position; import java.util.List; @@ -13,8 +14,8 @@ public CombinationPolicy(final Policy... policies) { } @Override - public boolean isSatisfied(final Color color, final Position currentPosition, final boolean existEnemy) { + public boolean isSatisfied(final Color color, final Position currentPosition, final Piece targetPiece) { return policies.stream() - .allMatch(policy -> policy.isSatisfied(color, currentPosition, existEnemy)); + .allMatch(policy -> policy.isSatisfied(color, currentPosition, targetPiece)); } } diff --git a/src/main/java/chess/domain/movement/policy/EnemyExistPolicy.java b/src/main/java/chess/domain/movement/policy/EnemyExistPolicy.java index a5a85caaa96..e5f613265be 100644 --- a/src/main/java/chess/domain/movement/policy/EnemyExistPolicy.java +++ b/src/main/java/chess/domain/movement/policy/EnemyExistPolicy.java @@ -1,12 +1,14 @@ package chess.domain.movement.policy; import chess.domain.piece.Color; +import chess.domain.piece.Piece; import chess.domain.position.Position; public class EnemyExistPolicy implements Policy { @Override - public boolean isSatisfied(final Color color, final Position currentPosition, final boolean existEnemy) { - return existEnemy; + public boolean isSatisfied(final Color color, final Position currentPosition, final Piece targetPiece) { + return !targetPiece.isSameTeamColor(color) && + !targetPiece.isEmpty(); } } diff --git a/src/main/java/chess/domain/movement/policy/NoRestrictionPolicy.java b/src/main/java/chess/domain/movement/policy/NoRestrictionPolicy.java index 7e6468ef14c..4fa9df9dc08 100644 --- a/src/main/java/chess/domain/movement/policy/NoRestrictionPolicy.java +++ b/src/main/java/chess/domain/movement/policy/NoRestrictionPolicy.java @@ -1,12 +1,13 @@ package chess.domain.movement.policy; import chess.domain.piece.Color; +import chess.domain.piece.Piece; import chess.domain.position.Position; public class NoRestrictionPolicy implements Policy { @Override - public boolean isSatisfied(final Color color, final Position currentPosition, final boolean existEnemy) { + public boolean isSatisfied(final Color color, final Position currentPosition, final Piece targetPiece) { return true; } } diff --git a/src/main/java/chess/domain/movement/policy/PawnFirstMovePolicy.java b/src/main/java/chess/domain/movement/policy/PawnFirstMovePolicy.java index 7201ca46447..2fa0472c545 100644 --- a/src/main/java/chess/domain/movement/policy/PawnFirstMovePolicy.java +++ b/src/main/java/chess/domain/movement/policy/PawnFirstMovePolicy.java @@ -4,12 +4,13 @@ import static chess.domain.board.InitialPiecePosition.WHITE_PAWN; import chess.domain.piece.Color; +import chess.domain.piece.Piece; import chess.domain.position.Position; public class PawnFirstMovePolicy implements Policy { @Override - public boolean isSatisfied(final Color color, final Position currentPosition, final boolean existEnemy) { + public boolean isSatisfied(final Color color, final Position currentPosition, final Piece targetPiece) { if (color == Color.BLACK) { return BLACK_PAWN.isPawnFirstMove(currentPosition); } diff --git a/src/main/java/chess/domain/movement/policy/Policy.java b/src/main/java/chess/domain/movement/policy/Policy.java index 2b271848417..fd3b99bab1e 100644 --- a/src/main/java/chess/domain/movement/policy/Policy.java +++ b/src/main/java/chess/domain/movement/policy/Policy.java @@ -1,9 +1,10 @@ package chess.domain.movement.policy; import chess.domain.piece.Color; +import chess.domain.piece.Piece; import chess.domain.position.Position; public interface Policy { - boolean isSatisfied(final Color color, final Position currentPosition, final boolean existEnemy); + boolean isSatisfied(final Color color, final Position currentPosition, final Piece piece); } 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..b52deeb0d8e --- /dev/null +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -0,0 +1,26 @@ +package chess.domain.piece; + +import chess.domain.movement.Movement; +import chess.domain.piece.obstaclefinder.MovementDirectionObstacleFinder; +import chess.domain.piece.obstaclefinder.ObstacleFinder; +import chess.domain.position.Position; +import java.util.Map; + +public class Bishop extends Piece { + private final ObstacleFinder obstacleFinder; + + public Bishop(final Color color) { + super(PieceType.BISHOP, color); + this.obstacleFinder = new MovementDirectionObstacleFinder(); + } + + @Override + public boolean canMove(final Position source, final Position target, final Map pieces) { + return pieceType.getMovements() + .stream() + .filter(movement -> movement.isSatisfied(color, source, pieces.get(target))) + .map(Movement::getDirection) + .anyMatch(direction -> direction.canReach(source, target, + obstacleFinder.findObstacle(source, target, pieces))); + } +} diff --git a/src/main/java/chess/domain/piece/Color.java b/src/main/java/chess/domain/piece/Color.java index 1c9fba899bf..43ff75c8da3 100644 --- a/src/main/java/chess/domain/piece/Color.java +++ b/src/main/java/chess/domain/piece/Color.java @@ -6,7 +6,7 @@ public enum Color { NONE, ; - public boolean isNotSameTeam(final Piece piece) { - return this != piece.color(); + public boolean isSameColor(final Color color) { + return this == color; } } diff --git a/src/main/java/chess/domain/piece/Empty.java b/src/main/java/chess/domain/piece/Empty.java new file mode 100644 index 00000000000..ef5c1510fad --- /dev/null +++ b/src/main/java/chess/domain/piece/Empty.java @@ -0,0 +1,21 @@ +package chess.domain.piece; + +import chess.domain.position.Position; +import java.util.Map; + +public class Empty extends Piece { + private static final Empty EMPTY = new Empty(); + + private Empty() { + super(PieceType.EMPTY, Color.NONE); + } + + public static Empty getInstance() { + return EMPTY; + } + + @Override + public boolean canMove(final Position source, final Position target, final Map pieces) { + 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..2dc30612f5b --- /dev/null +++ b/src/main/java/chess/domain/piece/King.java @@ -0,0 +1,26 @@ +package chess.domain.piece; + +import chess.domain.movement.Movement; +import chess.domain.piece.obstaclefinder.MovementDirectionObstacleFinder; +import chess.domain.piece.obstaclefinder.ObstacleFinder; +import chess.domain.position.Position; +import java.util.Map; + +public class King extends Piece { + private final ObstacleFinder obstacleFinder; + + public King(final Color color) { + super(PieceType.KING, color); + this.obstacleFinder = new MovementDirectionObstacleFinder(); + } + + @Override + public boolean canMove(final Position source, final Position target, final Map pieces) { + return pieceType.getMovements() + .stream() + .filter(movement -> movement.isSatisfied(color, source, pieces.get(target))) + .map(Movement::getDirection) + .anyMatch(direction -> direction.canReach(source, target, + obstacleFinder.findObstacle(source, target, pieces))); + } +} 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..5c4af3851ba --- /dev/null +++ b/src/main/java/chess/domain/piece/Knight.java @@ -0,0 +1,26 @@ +package chess.domain.piece; + +import chess.domain.movement.Movement; +import chess.domain.piece.obstaclefinder.KnightObstacleFinder; +import chess.domain.piece.obstaclefinder.ObstacleFinder; +import chess.domain.position.Position; +import java.util.Map; + +public class Knight extends Piece { + private final ObstacleFinder obstacleFinder; + + public Knight(final Color color) { + super(PieceType.KNIGHT, color); + this.obstacleFinder = new KnightObstacleFinder(); + } + + @Override + public boolean canMove(final Position source, final Position target, final Map pieces) { + return pieceType.getMovements() + .stream() + .filter(movement -> movement.isSatisfied(color, source, pieces.get(target))) + .map(Movement::getDirection) + .anyMatch(direction -> direction.canReach(source, target, + obstacleFinder.findObstacle(source, target, pieces))); + } +} 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..3e37873fea5 --- /dev/null +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -0,0 +1,27 @@ +package chess.domain.piece; + +import chess.domain.movement.Movement; +import chess.domain.piece.obstaclefinder.ObstacleFinder; +import chess.domain.piece.obstaclefinder.PawnObstacleFinder; +import chess.domain.position.Position; +import java.util.Map; + +public class Pawn extends Piece { + private final ObstacleFinder obstacleFinder; + + public Pawn(final Color color) { + super(PieceType.PAWN, color); + this.obstacleFinder = new PawnObstacleFinder(); + } + + @Override + public boolean canMove(final Position source, final Position target, final Map pieces) { + return pieceType.getMovements() + .stream() + .filter(movement -> movement.isSatisfied(color, source, + pieces.getOrDefault(target, Empty.getInstance()))) + .map(Movement::getDirection) + .anyMatch(direction -> direction.canReach(source, target, + obstacleFinder.findObstacle(source, target, pieces))); + } +} diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index 1b68ba860c5..5c4b15aeca7 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -1,39 +1,58 @@ package chess.domain.piece; -import chess.domain.movement.Movement; import chess.domain.position.Position; import java.util.Map; +import java.util.Objects; -public record Piece(PieceType pieceType, Color color) { - private static final Piece EMPTY_PIECE = new Piece(PieceType.EMPTY, Color.NONE); +public abstract class Piece { + protected final PieceType pieceType; + protected final Color color; - public static Piece getEmptyPiece() { - return EMPTY_PIECE; + protected Piece(final PieceType pieceType, final Color color) { + this.pieceType = pieceType; + this.color = color; } - public boolean canMove(final Position source, final Position target, final Map pieces) { - return pieceType.getMovements() - .stream() - .filter(movement -> movement.isSatisfied(color, source, existEnemyAtTarget(target, pieces))) - .map(Movement::getDirection) - .anyMatch(direction -> direction.canReach(source, target, - pieceType.getObstacle(source, target, pieces))); + public abstract boolean canMove(final Position source, final Position target, final Map pieces); + + public boolean isSameTeam(final Piece piece) { + return color.isSameColor(piece.color); } - private boolean existEnemyAtTarget(final Position target, final Map pieces) { - return pieces.containsKey(target) - && color.isNotSameTeam(pieces.get(target)); + public boolean isSameTeamColor(final Color color) { + return color.isSameColor(this.color); } - public boolean isNotSameTeam(final Piece piece) { - return color.isNotSameTeam(piece); + public boolean isEmpty() { + return this == Empty.getInstance(); } - public boolean isRankMove(final Position source, final Position target) { - return source.file() == target.file(); + public boolean isSameType(final PieceType pieceType) { + return this.pieceType == pieceType; } - public boolean isEmpty() { - return this == EMPTY_PIECE; + public PieceType getPieceType() { + return pieceType; + } + + public Color getColor() { + return color; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Piece piece = (Piece) o; + return pieceType == piece.pieceType && color == piece.color; + } + + @Override + public int hashCode() { + return Objects.hash(pieceType, color); } } diff --git a/src/main/java/chess/domain/piece/PieceFactory.java b/src/main/java/chess/domain/piece/PieceFactory.java new file mode 100644 index 00000000000..09c00d94d8b --- /dev/null +++ b/src/main/java/chess/domain/piece/PieceFactory.java @@ -0,0 +1,25 @@ +package chess.domain.piece; + +public class PieceFactory { + public static Piece create(final PieceType pieceType, final Color color) { + if (pieceType == PieceType.KING) { + return new King(color); + } + if (pieceType == PieceType.QUEEN) { + return new Queen(color); + } + if (pieceType == PieceType.KNIGHT) { + return new Knight(color); + } + if (pieceType == PieceType.ROOK) { + return new Rook(color); + } + if (pieceType == PieceType.PAWN) { + return new Pawn(color); + } + if (pieceType == PieceType.BISHOP) { + return new Bishop(color); + } + return Empty.getInstance(); + } +} diff --git a/src/main/java/chess/domain/piece/PieceType.java b/src/main/java/chess/domain/piece/PieceType.java index d9666bde454..8fd06248a6b 100644 --- a/src/main/java/chess/domain/piece/PieceType.java +++ b/src/main/java/chess/domain/piece/PieceType.java @@ -15,17 +15,15 @@ import chess.domain.movement.policy.EnemyExistPolicy; import chess.domain.movement.policy.NoRestrictionPolicy; import chess.domain.movement.policy.PawnFirstMovePolicy; -import chess.domain.piece.obstaclerule.DiagonalCaptureObstacleRule; -import chess.domain.piece.obstaclerule.NoObstacleRule; -import chess.domain.piece.obstaclerule.ObstacleRule; -import chess.domain.piece.obstaclerule.StraightCaptureObstacleRule; import chess.domain.position.Position; +import chess.domain.scorerule.NoScoreRule; +import chess.domain.scorerule.NormalScoreRule; +import chess.domain.scorerule.PawnScoreRule; +import chess.domain.scorerule.ScoreRule; import java.util.List; -import java.util.Map; public enum PieceType { - KING(new StraightCaptureObstacleRule(), - new Movement(new NoRestrictionPolicy(), new UpDirection(1)), + KING(new NoScoreRule(), new Movement(new NoRestrictionPolicy(), new UpDirection(1)), new Movement(new NoRestrictionPolicy(), new DownDirection(1)), new Movement(new NoRestrictionPolicy(), new LeftDirection(1)), new Movement(new NoRestrictionPolicy(), new RightDirection(1)), @@ -35,8 +33,7 @@ public enum PieceType { new Movement(new NoRestrictionPolicy(), new DownRightDirection(1)) ), - QUEEN(new StraightCaptureObstacleRule(), - new Movement(new NoRestrictionPolicy(), new UpDirection(8)), + QUEEN(new NormalScoreRule(9), new Movement(new NoRestrictionPolicy(), new UpDirection(8)), new Movement(new NoRestrictionPolicy(), new DownDirection(8)), new Movement(new NoRestrictionPolicy(), new LeftDirection(8)), new Movement(new NoRestrictionPolicy(), new RightDirection(8)), @@ -46,25 +43,22 @@ public enum PieceType { new Movement(new NoRestrictionPolicy(), new DownRightDirection(8)) ), - BISHOP(new StraightCaptureObstacleRule(), - new Movement(new NoRestrictionPolicy(), new UpLeftDirection(8)), + BISHOP(new NormalScoreRule(3), new Movement(new NoRestrictionPolicy(), new UpLeftDirection(8)), new Movement(new NoRestrictionPolicy(), new UpRightDirection(8)), new Movement(new NoRestrictionPolicy(), new DownLeftDirection(8)), new Movement(new NoRestrictionPolicy(), new DownRightDirection(8)) ), - ROOK(new StraightCaptureObstacleRule(), - new Movement(new NoRestrictionPolicy(), new UpDirection(8)), + ROOK(new NormalScoreRule(5), new Movement(new NoRestrictionPolicy(), new UpDirection(8)), new Movement(new NoRestrictionPolicy(), new DownDirection(8)), new Movement(new NoRestrictionPolicy(), new LeftDirection(8)), new Movement(new NoRestrictionPolicy(), new RightDirection(8)) ), - KNIGHT(new StraightCaptureObstacleRule(), - new Movement(new NoRestrictionPolicy(), new KnightDirection()) + KNIGHT(new NormalScoreRule(2.5), new Movement(new NoRestrictionPolicy(), new KnightDirection()) ), - PAWN(new DiagonalCaptureObstacleRule(), + PAWN(new PawnScoreRule(), new Movement(new CombinationPolicy(new ColorPolicy(Color.WHITE), new PawnFirstMovePolicy()), new UpDirection(2)), new Movement(new CombinationPolicy(new ColorPolicy(Color.WHITE), new EnemyExistPolicy()), @@ -82,14 +76,14 @@ public enum PieceType { new Movement(new ColorPolicy(Color.BLACK), new DownDirection(1)) ), - EMPTY(new NoObstacleRule()), + EMPTY(new NoScoreRule()), ; - private final ObstacleRule obstacleRule; + private final ScoreRule scoreRule; private final List movements; - PieceType(final ObstacleRule obstacleRule, final Movement... movements) { - this.obstacleRule = obstacleRule; + PieceType(final ScoreRule scoreRule, final Movement... movements) { + this.scoreRule = scoreRule; this.movements = List.of(movements); } @@ -97,7 +91,7 @@ public List getMovements() { return movements; } - public List getObstacle(final Position source, final Position target, final Map pieces) { - return obstacleRule.findObstacle(source, target, pieces); + public double findScore(final List piecesPosition) { + return scoreRule.findScore(piecesPosition); } } 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..1673f6b1a55 --- /dev/null +++ b/src/main/java/chess/domain/piece/Queen.java @@ -0,0 +1,26 @@ +package chess.domain.piece; + +import chess.domain.movement.Movement; +import chess.domain.piece.obstaclefinder.MovementDirectionObstacleFinder; +import chess.domain.piece.obstaclefinder.ObstacleFinder; +import chess.domain.position.Position; +import java.util.Map; + +public class Queen extends Piece { + private final ObstacleFinder obstacleFinder; + + public Queen(final Color color) { + super(PieceType.QUEEN, color); + this.obstacleFinder = new MovementDirectionObstacleFinder(); + } + + @Override + public boolean canMove(final Position source, final Position target, final Map pieces) { + return pieceType.getMovements() + .stream() + .filter(movement -> movement.isSatisfied(color, source, pieces.get(target))) + .map(Movement::getDirection) + .anyMatch(direction -> direction.canReach(source, target, + obstacleFinder.findObstacle(source, target, pieces))); + } +} 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..605f7ebb9c7 --- /dev/null +++ b/src/main/java/chess/domain/piece/Rook.java @@ -0,0 +1,26 @@ +package chess.domain.piece; + +import chess.domain.movement.Movement; +import chess.domain.piece.obstaclefinder.MovementDirectionObstacleFinder; +import chess.domain.piece.obstaclefinder.ObstacleFinder; +import chess.domain.position.Position; +import java.util.Map; + +public class Rook extends Piece { + private final ObstacleFinder obstacleFinder; + + public Rook(final Color color) { + super(PieceType.ROOK, color); + this.obstacleFinder = new MovementDirectionObstacleFinder(); + } + + @Override + public boolean canMove(final Position source, final Position target, final Map pieces) { + return pieceType.getMovements() + .stream() + .filter(movement -> movement.isSatisfied(color, source, pieces.get(target))) + .map(Movement::getDirection) + .anyMatch(direction -> direction.canReach(source, target, + obstacleFinder.findObstacle(source, target, pieces))); + } +} diff --git a/src/main/java/chess/domain/piece/obstaclefinder/KnightObstacleFinder.java b/src/main/java/chess/domain/piece/obstaclefinder/KnightObstacleFinder.java new file mode 100644 index 00000000000..c2f524c2ce7 --- /dev/null +++ b/src/main/java/chess/domain/piece/obstaclefinder/KnightObstacleFinder.java @@ -0,0 +1,22 @@ +package chess.domain.piece.obstaclefinder; + +import chess.domain.piece.Empty; +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import java.util.List; +import java.util.Map; + +public class KnightObstacleFinder implements ObstacleFinder { + + @Override + public List findObstacle(final Position source, final Position target, + final Map pieces) { + Piece sourcePiece = pieces.get(source); + Piece targetPiece = pieces.getOrDefault(target, Empty.getInstance()); + + if (sourcePiece.isSameTeamColor(targetPiece.getColor())) { + return List.of(target); + } + return List.of(); + } +} diff --git a/src/main/java/chess/domain/piece/obstaclerule/StraightCaptureObstacleRule.java b/src/main/java/chess/domain/piece/obstaclefinder/MovementDirectionObstacleFinder.java similarity index 52% rename from src/main/java/chess/domain/piece/obstaclerule/StraightCaptureObstacleRule.java rename to src/main/java/chess/domain/piece/obstaclefinder/MovementDirectionObstacleFinder.java index 17806e1d544..1ef39f20643 100644 --- a/src/main/java/chess/domain/piece/obstaclerule/StraightCaptureObstacleRule.java +++ b/src/main/java/chess/domain/piece/obstaclefinder/MovementDirectionObstacleFinder.java @@ -1,18 +1,20 @@ -package chess.domain.piece.obstaclerule; +package chess.domain.piece.obstaclefinder; +import chess.domain.piece.Empty; import chess.domain.piece.Piece; import chess.domain.position.Position; import java.util.List; import java.util.Map; -public class StraightCaptureObstacleRule extends ObstacleRule { - +public class MovementDirectionObstacleFinder implements ObstacleFinder { @Override public List findObstacle(final Position source, final Position target, final Map pieces) { List obstacles = getNotEmptyPiecePositions(pieces); - removeSourcePosition(source, obstacles); - removeCapturableTargetFromObstacle(source, target, pieces, obstacles); + obstacles.remove(source); + + removeCapturableTargetFromObstacle(target, pieces.getOrDefault(source, Empty.getInstance()), + pieces.getOrDefault(target, Empty.getInstance()), obstacles); return obstacles; } } diff --git a/src/main/java/chess/domain/piece/obstaclefinder/ObstacleFinder.java b/src/main/java/chess/domain/piece/obstaclefinder/ObstacleFinder.java new file mode 100644 index 00000000000..352aea56bb3 --- /dev/null +++ b/src/main/java/chess/domain/piece/obstaclefinder/ObstacleFinder.java @@ -0,0 +1,30 @@ +package chess.domain.piece.obstaclefinder; + +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +public interface ObstacleFinder { + + List findObstacle(final Position source, final Position target, + final Map pieces); + + default void removeCapturableTargetFromObstacle(final Position target, final Piece sourcePiece, + final Piece targetPiece, + final List obstacles) { + if (obstacles.contains(target) + && !sourcePiece.isSameTeam(targetPiece)) { + obstacles.remove(target); + } + } + + default List getNotEmptyPiecePositions(final Map pieces) { + return pieces.entrySet().stream() + .filter(entry -> !entry.getValue().isEmpty()) + .map(Entry::getKey) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/chess/domain/piece/obstaclerule/DiagonalCaptureObstacleRule.java b/src/main/java/chess/domain/piece/obstaclefinder/PawnObstacleFinder.java similarity index 64% rename from src/main/java/chess/domain/piece/obstaclerule/DiagonalCaptureObstacleRule.java rename to src/main/java/chess/domain/piece/obstaclefinder/PawnObstacleFinder.java index febda049393..63d1c843396 100644 --- a/src/main/java/chess/domain/piece/obstaclerule/DiagonalCaptureObstacleRule.java +++ b/src/main/java/chess/domain/piece/obstaclefinder/PawnObstacleFinder.java @@ -1,29 +1,30 @@ -package chess.domain.piece.obstaclerule; +package chess.domain.piece.obstaclefinder; +import chess.domain.piece.Empty; import chess.domain.piece.Piece; import chess.domain.position.Position; import java.util.List; import java.util.Map; -public class DiagonalCaptureObstacleRule extends ObstacleRule { +public class PawnObstacleFinder implements ObstacleFinder { @Override public List findObstacle(final Position source, final Position target, final Map pieces) { List obstacles = getNotEmptyPiecePositions(pieces); - removeCapturableTargetFromObstacle(source, target, pieces, obstacles); - removeSourcePosition(source, obstacles); - addTargetToObstacle(source, target, pieces, obstacles); + obstacles.remove(source); + + Piece sourcePiece = pieces.getOrDefault(source, Empty.getInstance()); + Piece targetPiece = pieces.getOrDefault(target, Empty.getInstance()); + removeCapturableTargetFromObstacle(target, sourcePiece, targetPiece, obstacles); + addTargetToObstacle(source, target, sourcePiece, targetPiece, obstacles); return obstacles; } private void addTargetToObstacle(final Position source, final Position target, - final Map pieces, final List obstacles) { - Piece sourcePiece = pieces.get(source); - Piece targetPiece = pieces.getOrDefault(target, Piece.getEmptyPiece()); - + final Piece sourcePiece, final Piece targetPiece, final List obstacles) { if (canNotKillTargetByStraightMove(source, target, sourcePiece, targetPiece) - || canNotMoveToTargetByDiagonalMove(source, target, sourcePiece, targetPiece)) { + || canNotMoveToTargetByDiagonalMove(source, target, targetPiece)) { obstacles.add(target); } } @@ -31,14 +32,14 @@ private void addTargetToObstacle(final Position source, final Position target, // 도착 위치가 상대편 말이지만 출발 위치와 도착 위치가 직선 이동일 경우 private boolean canNotKillTargetByStraightMove(final Position source, final Position target, final Piece sourcePiece, final Piece targetPiece) { - return targetPiece.isNotSameTeam(sourcePiece) && + return !targetPiece.isSameTeam(sourcePiece) && !targetPiece.isEmpty() && - sourcePiece.isRankMove(source, target); + source.isRankMove(target); } // 도착 위치가 비어있고 출발 위치와 도착 위치가 대각 이동일 경우 private boolean canNotMoveToTargetByDiagonalMove(final Position source, final Position target, - final Piece sourcePiece, final Piece targetPiece) { + final Piece targetPiece) { return targetPiece.isEmpty() && source.isDiagonalBy(target); } diff --git a/src/main/java/chess/domain/piece/obstaclerule/NoObstacleRule.java b/src/main/java/chess/domain/piece/obstaclerule/NoObstacleRule.java deleted file mode 100644 index 703aebcc287..00000000000 --- a/src/main/java/chess/domain/piece/obstaclerule/NoObstacleRule.java +++ /dev/null @@ -1,16 +0,0 @@ -package chess.domain.piece.obstaclerule; - -import chess.domain.piece.Piece; -import chess.domain.position.Position; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class NoObstacleRule extends ObstacleRule { - - @Override - public List findObstacle(final Position source, final Position target, - final Map pieces) { - return new ArrayList<>(); - } -} diff --git a/src/main/java/chess/domain/piece/obstaclerule/ObstacleRule.java b/src/main/java/chess/domain/piece/obstaclerule/ObstacleRule.java deleted file mode 100644 index 73f859b20c8..00000000000 --- a/src/main/java/chess/domain/piece/obstaclerule/ObstacleRule.java +++ /dev/null @@ -1,34 +0,0 @@ -package chess.domain.piece.obstaclerule; - -import chess.domain.piece.Piece; -import chess.domain.position.Position; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.stream.Collectors; - -public abstract class ObstacleRule { - public abstract List findObstacle(final Position source, final Position target, - final Map pieces); - - protected void removeCapturableTargetFromObstacle(final Position source, final Position target, - final Map pieces, - final List obstacles) { - Piece sourcePiece = pieces.getOrDefault(source, Piece.getEmptyPiece()); - if (obstacles.contains(target) - && sourcePiece.isNotSameTeam(pieces.getOrDefault(target, Piece.getEmptyPiece()))) { - obstacles.remove(target); - } - } - - protected List getNotEmptyPiecePositions(final Map pieces) { - return pieces.entrySet().stream() - .filter(entry -> !entry.getValue().isEmpty()) - .map(Entry::getKey) - .collect(Collectors.toList()); - } - - protected void removeSourcePosition(final Position source, final List positions) { - positions.remove(source); - } -} diff --git a/src/main/java/chess/domain/position/File.java b/src/main/java/chess/domain/position/File.java index 63287b20548..4acc3348c6f 100644 --- a/src/main/java/chess/domain/position/File.java +++ b/src/main/java/chess/domain/position/File.java @@ -1,16 +1,22 @@ package chess.domain.position; public enum File { - A, - B, - C, - D, - E, - F, - G, - H, + A(1), + B(2), + C(3), + D(4), + E(5), + F(6), + G(7), + H(8), ; + private final int value; + + File(final int value) { + this.value = value; + } + public static File of(final int x) { try { File[] values = File.values(); @@ -121,4 +127,8 @@ public boolean canMoveOneSpace(final File file) { return canMoveOne; } + + public int getValue() { + return value; + } } diff --git a/src/main/java/chess/domain/position/Position.java b/src/main/java/chess/domain/position/Position.java index 4aa497ccaa6..2534d651eef 100644 --- a/src/main/java/chess/domain/position/Position.java +++ b/src/main/java/chess/domain/position/Position.java @@ -60,4 +60,8 @@ public boolean isDiagonalBy(final Position target) { return file.canMoveOneSpace(target.file) && rank.canMoveOneSpace(target.rank); } + + public boolean isRankMove(final Position target) { + return this.file == target.file; + } } diff --git a/src/main/java/chess/domain/position/Rank.java b/src/main/java/chess/domain/position/Rank.java index 757329cec57..54499b2dd62 100644 --- a/src/main/java/chess/domain/position/Rank.java +++ b/src/main/java/chess/domain/position/Rank.java @@ -1,16 +1,22 @@ package chess.domain.position; public enum Rank { - ONE, - TWO, - THREE, - FOUR, - FIVE, - SIX, - SEVEN, - EIGHT, + ONE(1), + TWO(2), + THREE(3), + FOUR(4), + FIVE(5), + SIX(6), + SEVEN(7), + EIGHT(8), ; + private final int value; + + Rank(final int value) { + this.value = value; + } + public static Rank of(final int y) { try { Rank[] values = Rank.values(); @@ -119,4 +125,8 @@ public boolean canMoveOneSpace(final Rank rank) { return canMoveOne; } + + public int getValue() { + return value; + } } diff --git a/src/main/java/chess/domain/scorerule/NoScoreRule.java b/src/main/java/chess/domain/scorerule/NoScoreRule.java new file mode 100644 index 00000000000..6ef4b5d7875 --- /dev/null +++ b/src/main/java/chess/domain/scorerule/NoScoreRule.java @@ -0,0 +1,15 @@ +package chess.domain.scorerule; + +import chess.domain.position.Position; +import java.util.List; + +public class NoScoreRule extends ScoreRule { + public NoScoreRule() { + super(0); + } + + @Override + public double findScore(final List positionPerPieces) { + return 0; + } +} diff --git a/src/main/java/chess/domain/scorerule/NormalScoreRule.java b/src/main/java/chess/domain/scorerule/NormalScoreRule.java new file mode 100644 index 00000000000..df061b87015 --- /dev/null +++ b/src/main/java/chess/domain/scorerule/NormalScoreRule.java @@ -0,0 +1,16 @@ +package chess.domain.scorerule; + +import chess.domain.position.Position; +import java.util.List; + +public class NormalScoreRule extends ScoreRule { + + public NormalScoreRule(final double score) { + super(score); + } + + @Override + public double findScore(final List positionPerPieces) { + return positionPerPieces.size() * score; + } +} diff --git a/src/main/java/chess/domain/scorerule/PawnScoreRule.java b/src/main/java/chess/domain/scorerule/PawnScoreRule.java new file mode 100644 index 00000000000..90e370c55d4 --- /dev/null +++ b/src/main/java/chess/domain/scorerule/PawnScoreRule.java @@ -0,0 +1,48 @@ +package chess.domain.scorerule; + +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.toList; + +import chess.domain.position.File; +import chess.domain.position.Position; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public class PawnScoreRule extends ScoreRule { + public PawnScoreRule() { + super(1); + } + + @Override + public double findScore(final List positionPerPieces) { + double baseScore = positionPerPieces.size() * score; + + int sameFilePositionCount = getSameFilePositionCount(positionPerPieces); + baseScore -= sameFilePositionCount * (score / 2); + return baseScore; + } + + private int getSameFilePositionCount(List positionPerPieces) { + int count = 0; + Map> sameFilePositions = getSameFilePositions(positionPerPieces); + for (Entry> entry : sameFilePositions.entrySet()) { + count += getSameFileCount(entry); + } + + return count; + } + + private Map> getSameFilePositions(final List positionPerPieces) { + return positionPerPieces.stream() + .collect(groupingBy(Position::file, toList())); + } + + private int getSameFileCount(final Entry> entry) { + int size = entry.getValue().size(); + if (size > 1) { + return size; + } + return 0; + } +} diff --git a/src/main/java/chess/domain/scorerule/Referee.java b/src/main/java/chess/domain/scorerule/Referee.java new file mode 100644 index 00000000000..8d24fd11da0 --- /dev/null +++ b/src/main/java/chess/domain/scorerule/Referee.java @@ -0,0 +1,39 @@ +package chess.domain.scorerule; + +import chess.domain.piece.Color; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import chess.domain.position.Position; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class Referee { + private final Map piecePositions; + + public Referee(final Map piecePositions) { + this.piecePositions = piecePositions; + } + + public double calculateScore(final Color color) { + Map teamPieces = getTeamPieces(color); + Map> positionsByPieceType = getPositionsByPieceType(teamPieces); + + return positionsByPieceType.entrySet().stream() + .mapToDouble(entry -> entry.getKey().findScore(entry.getValue())) + .sum(); + } + + private Map getTeamPieces(final Color color) { + return piecePositions.entrySet().stream() + .filter(entry -> entry.getValue().isSameTeamColor(color)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + private Map> getPositionsByPieceType(final Map teamPieces) { + return teamPieces.entrySet().stream() + .collect(Collectors.groupingBy( + entry -> entry.getValue().getPieceType(), + Collectors.mapping(Map.Entry::getKey, Collectors.toList()))); + } +} diff --git a/src/main/java/chess/domain/scorerule/ScoreRule.java b/src/main/java/chess/domain/scorerule/ScoreRule.java new file mode 100644 index 00000000000..80e88d49b7f --- /dev/null +++ b/src/main/java/chess/domain/scorerule/ScoreRule.java @@ -0,0 +1,14 @@ +package chess.domain.scorerule; + +import chess.domain.position.Position; +import java.util.List; + +public abstract class ScoreRule { + protected final double score; + + protected ScoreRule(final double score) { + this.score = score; + } + + public abstract double findScore(List positionPerPieces); +} diff --git a/src/main/java/chess/repository/DatabaseConfig.java b/src/main/java/chess/repository/DatabaseConfig.java new file mode 100644 index 00000000000..879767bb601 --- /dev/null +++ b/src/main/java/chess/repository/DatabaseConfig.java @@ -0,0 +1,24 @@ +package chess.repository; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public final class DatabaseConfig { + + private static final String SERVER = "localhost:13306"; + private static final String DATABASE = "chess"; + private static final String OPTION = "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Seoul"; + private static final String USERNAME = "user"; + private static final String PASSWORD = "password"; + + public static Connection getConnection() { + try { + return DriverManager.getConnection("jdbc:mysql://" + SERVER + "/" + DATABASE + OPTION, USERNAME, PASSWORD); + } catch (final SQLException e) { + System.err.println("DB 연결 오류:" + e.getMessage()); + e.printStackTrace(); + return null; + } + } +} diff --git a/src/main/java/chess/repository/piece/JdbcPieceRepository.java b/src/main/java/chess/repository/piece/JdbcPieceRepository.java new file mode 100644 index 00000000000..022a9da05bb --- /dev/null +++ b/src/main/java/chess/repository/piece/JdbcPieceRepository.java @@ -0,0 +1,89 @@ +package chess.repository.piece; + +import static chess.repository.DatabaseConfig.getConnection; + +import chess.domain.piece.Color; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceFactory; +import chess.domain.piece.PieceType; +import chess.domain.position.Position; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.Map; + +public class JdbcPieceRepository implements PieceRepository { + + @Override + public void save(final Piece piece, final Position position) { + final var query = "INSERT INTO piece (color, pieceType, `rank`, file) VALUES(?, ?, ?, ?)"; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + preparedStatement.setString(1, piece.getColor().name()); + preparedStatement.setString(2, piece.getPieceType().name()); + preparedStatement.setInt(3, position.rank().getValue()); + preparedStatement.setInt(4, position.file().getValue()); + preparedStatement.executeUpdate(); + } catch (final SQLException e) { + throw new RuntimeException("[METHOD] save [TABLE] piece" + e.getMessage(), e); + } + } + + @Override + public Map findAllPiece() { + final var query = "SELECT * FROM piece"; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + final var resultSet = preparedStatement.executeQuery(); + return mapToPieces(resultSet); + } catch (final SQLException e) { + throw new RuntimeException("[METHOD] findAll [TABLE] piece" + e.getMessage(), e); + } + } + + private Map mapToPieces(final ResultSet resultSet) throws SQLException { + Map pieces = new LinkedHashMap<>(); + while (resultSet.next()) { + var file = resultSet.getInt("file"); + var rank = resultSet.getInt("rank"); + var color = resultSet.getString("color"); + var pieceType = resultSet.getString("pieceType"); + + Position position = Position.of(file, rank); + Piece piece = PieceFactory.create(PieceType.valueOf(pieceType), Color.valueOf(color)); + pieces.put(position, piece); + } + + return pieces; + } + + @Override + public void deleteAll() { + final var query = "DELETE FROM piece"; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + preparedStatement.executeUpdate(); + } catch (SQLException e) { + throw new RuntimeException("[METHOD] deleteAll [TABLE] piece" + e.getMessage(), e); + } + } + + @Override + public boolean existPieces() { + final var query = "SELECT EXISTS(SELECT 1 FROM PIECE)"; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + final var resultSet = preparedStatement.executeQuery(); + return checkPieceExist(resultSet); + } catch (SQLException e) { + throw new RuntimeException("[METHOD] existPieces [TABLE] piece" + e.getMessage(), e); + } + } + + private boolean checkPieceExist(final ResultSet resultSet) throws SQLException { + if (resultSet.next()) { + return resultSet.getBoolean(1); + } + return false; + } +} diff --git a/src/main/java/chess/repository/piece/PieceRepository.java b/src/main/java/chess/repository/piece/PieceRepository.java new file mode 100644 index 00000000000..64d39667daf --- /dev/null +++ b/src/main/java/chess/repository/piece/PieceRepository.java @@ -0,0 +1,15 @@ +package chess.repository.piece; + +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import java.util.Map; + +public interface PieceRepository { + void save(final Piece piece, final Position position); + + Map findAllPiece(); + + void deleteAll(); + + boolean existPieces(); +} diff --git a/src/main/java/chess/repository/turn/JdbcTurnRepository.java b/src/main/java/chess/repository/turn/JdbcTurnRepository.java new file mode 100644 index 00000000000..d3e0ff63b8a --- /dev/null +++ b/src/main/java/chess/repository/turn/JdbcTurnRepository.java @@ -0,0 +1,57 @@ +package chess.repository.turn; + +import static chess.repository.DatabaseConfig.getConnection; + +import chess.domain.piece.Color; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Optional; + +public class JdbcTurnRepository implements TurnRepository { + + @Override + public void save(final String color) { + final var query = "INSERT INTO turn (turn) VALUES(?)"; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + preparedStatement.setString(1, color); + preparedStatement.executeUpdate(); + } catch (final SQLException e) { + throw new RuntimeException("[METHOD] save [TABLE] turn" + e.getMessage(), e); + } + } + + @Override + public Optional findAny() { + final var query = "SELECT * FROM turn"; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + final var resultSet = preparedStatement.executeQuery(); + + boolean existTurn = resultSet.next(); + return getTurnDto(resultSet, existTurn); + } catch (final SQLException e) { + throw new RuntimeException("[METHOD] findAll [TABLE] piece" + e.getMessage(), e); + } + } + + private Optional getTurnDto(final ResultSet resultSet, final boolean existTurn) + throws SQLException { + if (existTurn) { + var turnColor = resultSet.getString("turn"); + return Optional.of(Color.valueOf(turnColor)); + } + return Optional.empty(); + } + + @Override + public void deleteAll() { + final var query = "DELETE FROM turn"; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + preparedStatement.executeUpdate(); + } catch (SQLException e) { + throw new RuntimeException("[METHOD] deleteAll [TABLE] turn" + e.getMessage(), e); + } + } +} diff --git a/src/main/java/chess/repository/turn/TurnRepository.java b/src/main/java/chess/repository/turn/TurnRepository.java new file mode 100644 index 00000000000..99761ddbeb5 --- /dev/null +++ b/src/main/java/chess/repository/turn/TurnRepository.java @@ -0,0 +1,13 @@ +package chess.repository.turn; + +import chess.domain.piece.Color; +import java.util.Optional; + +public interface TurnRepository { + + void save(final String color); + + Optional findAny(); + + void deleteAll(); +} diff --git a/src/main/java/chess/service/ChessGameService.java b/src/main/java/chess/service/ChessGameService.java new file mode 100644 index 00000000000..80e3cbdde49 --- /dev/null +++ b/src/main/java/chess/service/ChessGameService.java @@ -0,0 +1,53 @@ +package chess.service; + +import chess.domain.game.Game; +import chess.domain.piece.Color; +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import chess.repository.piece.PieceRepository; +import chess.repository.turn.TurnRepository; +import java.util.Map; +import java.util.Optional; + +public class ChessGameService { + + private final PieceRepository pieceRepository; + private final TurnRepository turnRepository; + + public ChessGameService(final PieceRepository pieceRepository, final TurnRepository turnRepository) { + this.pieceRepository = pieceRepository; + this.turnRepository = turnRepository; + } + + public Map findAllPiece() { + return pieceRepository.findAllPiece(); + } + + public Optional findTurn() { + return turnRepository.findAny(); + } + + public void saveGame(final Game game) { + delete(); + saveAllPiece(game.getBoard()); + saveTurn(game.getTurn()); + } + + private void saveAllPiece(final Map pieces) { + pieces.entrySet() + .forEach(entry -> pieceRepository.save(entry.getValue(), entry.getKey())); + } + + private void saveTurn(final Color color) { + turnRepository.save(color.name()); + } + + public void delete() { + pieceRepository.deleteAll(); + turnRepository.deleteAll(); + } + + public boolean existSavedGame() { + return pieceRepository.existPieces(); + } +} diff --git a/src/main/java/chess/view/ColorDisplay.java b/src/main/java/chess/view/ColorDisplay.java new file mode 100644 index 00000000000..f6cb6b0d6f9 --- /dev/null +++ b/src/main/java/chess/view/ColorDisplay.java @@ -0,0 +1,27 @@ +package chess.view; + +import chess.domain.piece.Color; +import java.util.Arrays; + +public enum ColorDisplay { + BLACK(Color.BLACK, "블랙"), + WHITE(Color.WHITE, "화이트"), + ; + + private final Color color; + private final String value; + + ColorDisplay(final Color color, final String value) { + this.color = color; + this.value = value; + } + + public static String getValue(final Color color) { + ColorDisplay colorDisplay = Arrays.stream(values()) + .filter(display -> color.equals(display.color)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("해당하는 팀 색상이 없습니다.")); + + return colorDisplay.value; + } +} diff --git a/src/main/java/chess/view/GameCommand.java b/src/main/java/chess/view/GameCommand.java index 5af0fc85eb4..c6731e63c01 100644 --- a/src/main/java/chess/view/GameCommand.java +++ b/src/main/java/chess/view/GameCommand.java @@ -6,7 +6,7 @@ public enum GameCommand { START("start", "게임 시작 : start"), END("end", "게임 종료 : end"), MOVE("move", "게임 이동 : move source위치 target위치 - 예. move b2 b3"), - ; + STATUS("status", "현재 스코어 확인 : status"); private final String consoleCommand; private final String helperMassage; @@ -26,4 +26,28 @@ public static GameCommand getGameCommand(final String input) { public String getHelperMessage() { return helperMassage; } + + public boolean isOnGoingGameCommand() { + return this != GameCommand.START && this != GameCommand.END; + } + + public boolean isStartGameCommand() { + return this == GameCommand.START; + } + + public boolean isNotStartCommand() { + return this != GameCommand.START; + } + + public boolean isMoveCommand() { + return this == GameCommand.MOVE; + } + + public boolean isViewStatusCommand() { + return this == GameCommand.STATUS; + } + + public boolean isEndCommand() { + return this == GameCommand.END; + } } diff --git a/src/main/java/chess/view/InputView.java b/src/main/java/chess/view/InputView.java index d187bde4325..db49bdc8659 100644 --- a/src/main/java/chess/view/InputView.java +++ b/src/main/java/chess/view/InputView.java @@ -1,6 +1,7 @@ package chess.view; import java.awt.Point; +import java.util.Objects; import java.util.Scanner; public class InputView { @@ -22,4 +23,17 @@ private void validateEmpty(final String input) { throw new IllegalArgumentException("이동 명령어의 위치 값이 필요합니다."); } } + + public boolean isPlaySavedGame() { + System.out.println("저장된 게임이 있습니다. 이어하시겠습니까? (y or n)"); + String option = scanner.next(); + validateSaveGameOption(option); + return Objects.equals(option, "y"); + } + + private void validateSaveGameOption(final String option) { + if (!Objects.equals(option, "y") && !Objects.equals(option, "n")) { + throw new IllegalArgumentException("올바른 입력이 아닙니다."); + } + } } diff --git a/src/main/java/chess/view/OutputView.java b/src/main/java/chess/view/OutputView.java index 99b3b753119..e721b255521 100644 --- a/src/main/java/chess/view/OutputView.java +++ b/src/main/java/chess/view/OutputView.java @@ -1,6 +1,6 @@ package chess.view; -import chess.domain.board.Board; +import chess.domain.piece.Color; import chess.domain.piece.Piece; import chess.domain.position.Position; import java.util.Arrays; @@ -24,8 +24,7 @@ public void printInitialMessage() { System.out.println(commandMessage); } - public void printBoard(final Board board) { - Map positions = board.getBoard(); + public void printBoard(final Map positions) { for (int rank = MAXIMUM_RANK_RANGE; rank >= MINIMUM_RANK_RANGE; rank--) { printRankLine(positions, rank); } @@ -40,4 +39,16 @@ private void printRankLine(final Map positions, final int rank) .collect(Collectors.joining("")); System.out.println(rankLine); } + + public void printScore(final double teamScore, final Color color) { + System.out.println(color + "팀의 현재 점수는 " + teamScore + "입니다."); + } + + public void printFinish() { + System.out.println("King이 잡혔습니다. 게임을 종료합니다."); + } + + public void printCurrentTurn(final Color color) { + System.out.println(ColorDisplay.getValue(color) + "팀 차례입니다."); + } } diff --git a/src/main/java/chess/view/PieceSymbol.java b/src/main/java/chess/view/PieceSymbol.java index 4f990b29903..fe6ef21d44b 100644 --- a/src/main/java/chess/view/PieceSymbol.java +++ b/src/main/java/chess/view/PieceSymbol.java @@ -25,10 +25,10 @@ public enum PieceSymbol { public static String getDisplay(final Piece piece) { PieceSymbol findSymbol = Arrays.stream(PieceSymbol.values()) - .filter(pieceSymbol -> pieceSymbol.pieceType == piece.pieceType()) + .filter(pieceSymbol -> pieceSymbol.pieceType == piece.getPieceType()) .findFirst() .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 종류의 말입니다.")); - return convertByColor(findSymbol, piece.color()); + return convertByColor(findSymbol, piece.getColor()); } private static String convertByColor(final PieceSymbol pieceSymbol, final Color color) { diff --git a/src/test/java/chess/domain/board/BoardInitializerTest.java b/src/test/java/chess/domain/board/BoardInitializerTest.java deleted file mode 100644 index cf80b817810..00000000000 --- a/src/test/java/chess/domain/board/BoardInitializerTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package chess.domain.board; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -import chess.domain.piece.Color; -import chess.domain.piece.Piece; -import chess.domain.piece.PieceType; -import chess.domain.position.Position; -import java.util.Map; -import java.util.stream.IntStream; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class BoardInitializerTest { - - @Test - @DisplayName("전체 말들의 초기 위치 정보를 반환한다. - 기물들의 위치 확인") - void initializeAllPieces() { - Map initialPiecePositions = new BoardInitializer().initialize(); - assertAll( - () -> assertThat(initialPiecePositions) - .containsAllEntriesOf(Map.of( - Position.of(1, 1), new Piece(PieceType.ROOK, Color.WHITE), - Position.of(2, 1), new Piece(PieceType.KNIGHT, Color.WHITE), - Position.of(3, 1), new Piece(PieceType.BISHOP, Color.WHITE), - Position.of(4, 1), new Piece(PieceType.QUEEN, Color.WHITE), - Position.of(5, 1), new Piece(PieceType.KING, Color.WHITE), - Position.of(6, 1), new Piece(PieceType.BISHOP, Color.WHITE), - Position.of(7, 1), new Piece(PieceType.KNIGHT, Color.WHITE), - Position.of(8, 1), new Piece(PieceType.ROOK, Color.WHITE) - )), - () -> assertThat(initialPiecePositions) - .containsAllEntriesOf(Map.of( - Position.of(1, 2), new Piece(PieceType.PAWN, Color.WHITE), - Position.of(2, 2), new Piece(PieceType.PAWN, Color.WHITE), - Position.of(3, 2), new Piece(PieceType.PAWN, Color.WHITE), - Position.of(4, 2), new Piece(PieceType.PAWN, Color.WHITE), - Position.of(5, 2), new Piece(PieceType.PAWN, Color.WHITE), - Position.of(6, 2), new Piece(PieceType.PAWN, Color.WHITE), - Position.of(7, 2), new Piece(PieceType.PAWN, Color.WHITE), - Position.of(8, 2), new Piece(PieceType.PAWN, Color.WHITE) - )), - () -> assertThat(initialPiecePositions) - .containsAllEntriesOf(Map.of( - Position.of(1, 7), new Piece(PieceType.PAWN, Color.BLACK), - Position.of(2, 7), new Piece(PieceType.PAWN, Color.BLACK), - Position.of(3, 7), new Piece(PieceType.PAWN, Color.BLACK), - Position.of(4, 7), new Piece(PieceType.PAWN, Color.BLACK), - Position.of(5, 7), new Piece(PieceType.PAWN, Color.BLACK), - Position.of(6, 7), new Piece(PieceType.PAWN, Color.BLACK), - Position.of(7, 7), new Piece(PieceType.PAWN, Color.BLACK), - Position.of(8, 7), new Piece(PieceType.PAWN, Color.BLACK) - )), - () -> assertThat(initialPiecePositions) - .containsAllEntriesOf(Map.of( - Position.of(1, 8), new Piece(PieceType.ROOK, Color.BLACK), - Position.of(2, 8), new Piece(PieceType.KNIGHT, Color.BLACK), - Position.of(3, 8), new Piece(PieceType.BISHOP, Color.BLACK), - Position.of(4, 8), new Piece(PieceType.QUEEN, Color.BLACK), - Position.of(5, 8), new Piece(PieceType.KING, Color.BLACK), - Position.of(6, 8), new Piece(PieceType.BISHOP, Color.BLACK), - Position.of(7, 8), new Piece(PieceType.KNIGHT, Color.BLACK), - Position.of(8, 8), new Piece(PieceType.ROOK, Color.BLACK) - )) - ); - } - - @Test - @DisplayName("말의 위치가 비어있는 경우 비어있는 말의 타입을 반환한다.") - void initializeEmptyPieces() { - Map initialPiecePositions = new BoardInitializer().initialize(); - IntStream.rangeClosed(3, 6).boxed() - .flatMap(rank -> IntStream.rangeClosed(1, 8).boxed() - .map(file -> Position.of(file, rank))) - .forEach(position -> assertThat(initialPiecePositions.get(position)).isEqualTo( - new Piece(PieceType.EMPTY, Color.NONE))); - } -} diff --git a/src/test/java/chess/domain/board/BoardTest.java b/src/test/java/chess/domain/board/BoardTest.java index 7d47d8ea937..f789a76f236 100644 --- a/src/test/java/chess/domain/board/BoardTest.java +++ b/src/test/java/chess/domain/board/BoardTest.java @@ -1,10 +1,10 @@ package chess.domain.board; -import static chess.domain.pixture.PieceFixture.WHITE_PAWN; +import static chess.domain.pixture.PieceFixture.BLACK_PAWN; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; +import chess.domain.piece.Empty; import chess.domain.piece.Piece; import chess.domain.position.Position; import java.util.Map; @@ -16,22 +16,60 @@ class BoardTest { @Test @DisplayName("보드에서 체스 말을 이동시킬 수 있다.") void movePiece() { - Board board = new Board(new BoardInitializer()); - board.tryMove(Position.of(1, 2), Position.of(1, 4)); + Board board = new Board(new ClearBoardInitializer()); + board.tryMove(Position.of(1, 7), Position.of(1, 5)); Map boardPieces = board.getBoard(); assertAll( - () -> assertThat(boardPieces.get(Position.of(1, 2))) - .isEqualTo(Piece.getEmptyPiece()), - () -> assertThat(boardPieces.get(Position.of(1, 4))) - .isEqualTo(WHITE_PAWN.getPiece())); + () -> assertThat(boardPieces.get(Position.of(1, 7))) + .isEqualTo(Empty.getInstance()), + () -> assertThat(boardPieces.get(Position.of(1, 5))) + .isEqualTo(BLACK_PAWN.getPiece())); } @Test - @DisplayName("보드에서 체스 말을 이동할 수 없는 경우 예외가 발생한다.") + @DisplayName("보드에서 체스 말을 이동할 수 없는 경우 거짓을 발생한다.") void movePieceThrowException() { - Board board = new Board(new BoardInitializer()); - assertThatThrownBy(() -> board.tryMove(Position.of(1, 1), Position.of(1, 2))) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동이 불가능한 위치입니다."); + Board board = new Board(new ClearBoardInitializer()); + assertThat(board.tryMove(Position.of(1, 7), Position.of(1, 2))).isFalse(); + } + + + @Test + @DisplayName("움직이려는 말의 진영 차례가 아닌 경우 예외가 발생한다.") + void isTurnThrowException() { + Board board = new Board(new ClearBoardInitializer()); + assertThat(board.tryMove(Position.of(1, 7), Position.of(1, 2))).isFalse(); + + } + + @Test + @DisplayName("King이 잡힐 시에 게임이 종료된다.") + void isNotFinish() { + Board board = new Board(new ClearBoardInitializer()); + assertThat(board.isKingKilled()).isFalse(); + } + + @Test + @DisplayName("King이 잡힐 시에 게임이 종료된다.") + void isFinish() { + Board board = new Board(new ClearBoardInitializer()); + + // RNBQqB.R <- king이 죽는 기보 + // PPPP..PP + // .....P.N + // ....P... + // ........ + // ....p... + // pppp.ppp + // rnb.kbnr + + board.tryMove(Position.of(6, 7), Position.of(6, 6)); + board.tryMove(Position.of(5, 2), Position.of(5, 3)); + board.tryMove(Position.of(7, 8), Position.of(8, 6)); + board.tryMove(Position.of(4, 1), Position.of(8, 5)); + board.tryMove(Position.of(5, 7), Position.of(5, 5)); + board.tryMove(Position.of(8, 5), Position.of(5, 8)); + + assertThat(board.isKingKilled()).isTrue(); } } diff --git a/src/test/java/chess/domain/board/ClearBoardInitializerTest.java b/src/test/java/chess/domain/board/ClearBoardInitializerTest.java new file mode 100644 index 00000000000..17a5e040c5d --- /dev/null +++ b/src/test/java/chess/domain/board/ClearBoardInitializerTest.java @@ -0,0 +1,85 @@ +package chess.domain.board; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import chess.domain.piece.Bishop; +import chess.domain.piece.Color; +import chess.domain.piece.Empty; +import chess.domain.piece.King; +import chess.domain.piece.Knight; +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import chess.domain.piece.Queen; +import chess.domain.piece.Rook; +import chess.domain.position.Position; +import java.util.Map; +import java.util.stream.IntStream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ClearBoardInitializerTest { + + @Test + @DisplayName("전체 말들의 초기 위치 정보를 반환한다. - 기물들의 위치 확인") + void initializeAllPieces() { + Map initialPiecePositions = new ClearBoardInitializer().initialize(); + assertAll( + () -> assertThat(initialPiecePositions) + .containsAllEntriesOf(Map.of( + Position.of(1, 1), new Rook(Color.WHITE), + Position.of(2, 1), new Knight(Color.WHITE), + Position.of(3, 1), new Bishop(Color.WHITE), + Position.of(4, 1), new Queen(Color.WHITE), + Position.of(5, 1), new King(Color.WHITE), + Position.of(6, 1), new Bishop(Color.WHITE), + Position.of(7, 1), new Knight(Color.WHITE), + Position.of(8, 1), new Rook(Color.WHITE) + )), + () -> assertThat(initialPiecePositions) + .containsAllEntriesOf(Map.of( + Position.of(1, 2), new Pawn(Color.WHITE), + Position.of(2, 2), new Pawn(Color.WHITE), + Position.of(3, 2), new Pawn(Color.WHITE), + Position.of(4, 2), new Pawn(Color.WHITE), + Position.of(5, 2), new Pawn(Color.WHITE), + Position.of(6, 2), new Pawn(Color.WHITE), + Position.of(7, 2), new Pawn(Color.WHITE), + Position.of(8, 2), new Pawn(Color.WHITE) + )), + () -> assertThat(initialPiecePositions) + .containsAllEntriesOf(Map.of( + Position.of(1, 7), new Pawn(Color.BLACK), + Position.of(2, 7), new Pawn(Color.BLACK), + Position.of(3, 7), new Pawn(Color.BLACK), + Position.of(4, 7), new Pawn(Color.BLACK), + Position.of(5, 7), new Pawn(Color.BLACK), + Position.of(6, 7), new Pawn(Color.BLACK), + Position.of(7, 7), new Pawn(Color.BLACK), + Position.of(8, 7), new Pawn(Color.BLACK) + )), + () -> assertThat(initialPiecePositions) + .containsAllEntriesOf(Map.of( + Position.of(1, 8), new Rook(Color.BLACK), + Position.of(2, 8), new Knight(Color.BLACK), + Position.of(3, 8), new Bishop(Color.BLACK), + Position.of(4, 8), new Queen(Color.BLACK), + Position.of(5, 8), new King(Color.BLACK), + Position.of(6, 8), new Bishop(Color.BLACK), + Position.of(7, 8), new Knight(Color.BLACK), + Position.of(8, 8), new Rook(Color.BLACK) + )) + ); + } + + @Test + @DisplayName("말의 위치가 비어있는 경우 비어있는 말의 타입을 반환한다.") + void initializeEmptyPieces() { + Map initialPiecePositions = new ClearBoardInitializer().initialize(); + IntStream.rangeClosed(3, 6).boxed() + .flatMap(rank -> IntStream.rangeClosed(1, 8).boxed() + .map(file -> Position.of(file, rank))) + .forEach(position -> assertThat(initialPiecePositions.get(position)).isEqualTo( + Empty.getInstance())); + } +} diff --git a/src/test/java/chess/domain/game/GameTest.java b/src/test/java/chess/domain/game/GameTest.java new file mode 100644 index 00000000000..d0eecfccd57 --- /dev/null +++ b/src/test/java/chess/domain/game/GameTest.java @@ -0,0 +1,20 @@ +package chess.domain.game; + +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.board.ClearBoardInitializer; +import chess.domain.piece.Color; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class GameTest { + + @ParameterizedTest + @CsvSource({"BLACK", "WHITE"}) + @DisplayName("게임 생성 시 해당하는 색에 맞는 차례인 상태로 생성한다.") + void of(final Color color) { + Game game = Game.of(new ClearBoardInitializer(), color); + assertThat(game.getTurn()).isEqualTo(color); + } +} diff --git a/src/test/java/chess/domain/game/status/BlackTurnTest.java b/src/test/java/chess/domain/game/status/BlackTurnTest.java new file mode 100644 index 00000000000..524dc80cb52 --- /dev/null +++ b/src/test/java/chess/domain/game/status/BlackTurnTest.java @@ -0,0 +1,74 @@ +package chess.domain.game.status; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.domain.board.Board; +import chess.domain.board.ClearBoardInitializer; +import chess.domain.board.SavedBoardInitializer; +import chess.domain.piece.Color; +import chess.domain.piece.King; +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import chess.domain.piece.Rook; +import chess.domain.position.Position; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class BlackTurnTest { + + @Test + @DisplayName("Piece가 이동을 했다면 화이트 팀의 차례를 나타내는 상태를 반환한다.") + void successMoveThenReturnWhiteTurn() { + Map positionPieces = new HashMap<>(); + positionPieces.put(Position.of(1, 1), new King(Color.WHITE)); + positionPieces.put(Position.of(4, 1), new King(Color.BLACK)); + positionPieces.put(Position.of(4, 2), new Rook(Color.WHITE)); + BlackTurn blackTurn = new BlackTurn(new Board(new SavedBoardInitializer(positionPieces))); + + assertThat(blackTurn.move(Position.of(4, 1), Position.of(4, 2))) + .isInstanceOf(WhiteTurn.class); + } + + @Test + @DisplayName("Piece가 이동을 했고, 왕이 잡혔다면 게임 종료의 상태를 반환한다.") + void moveAndKingKilledThenReturnFinish() { + Map positionPieces = new HashMap<>(); + positionPieces.put(Position.of(4, 1), new King(Color.BLACK)); + positionPieces.put(Position.of(1, 2), new Rook(Color.BLACK)); + positionPieces.put(Position.of(1, 1), new King(Color.WHITE)); + BlackTurn blackTurn = new BlackTurn(new Board(new SavedBoardInitializer(positionPieces))); + + assertThat(blackTurn.move(Position.of(1, 2), Position.of(1, 1))) + .isInstanceOf(Finished.class); + } + + @Test + @DisplayName("Piece가 이동이 불가능하다면 예외가 발생한다.") + void canNotMove() { + Map positionPieces = new HashMap<>(); + positionPieces.put(Position.of(1, 2), new Pawn(Color.BLACK)); + positionPieces.put(Position.of(1, 1), new King(Color.WHITE)); + BlackTurn blackTurn = new BlackTurn(new Board(new SavedBoardInitializer(positionPieces))); + + assertThatThrownBy(() -> blackTurn.move(Position.of(1, 2), Position.of(1, 1))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("해당 위치로 이동이 불가능합니다."); + } + + @Test + @DisplayName("현재 차례 조회 시 검정울 반환한다.") + void getTurn() { + BlackTurn blackTurn = new BlackTurn(new Board(new ClearBoardInitializer())); + assertThat(blackTurn.getTurn()).isEqualTo(Color.BLACK); + } + + @Test + @DisplayName("게임 종료를 물어볼 경우 항상 거짓을 반환한다.") + void isFinished() { + BlackTurn blackTurn = new BlackTurn(new Board(new ClearBoardInitializer())); + assertThat(blackTurn.isFinish()).isFalse(); + } +} diff --git a/src/test/java/chess/domain/game/status/FinishedTest.java b/src/test/java/chess/domain/game/status/FinishedTest.java new file mode 100644 index 00000000000..8b94460703b --- /dev/null +++ b/src/test/java/chess/domain/game/status/FinishedTest.java @@ -0,0 +1,40 @@ +package chess.domain.game.status; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.domain.position.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class FinishedTest { + @Test + @DisplayName("Piece가 이동을 했다면 블랙 팀의 차례를 나타내는 상태를 반환한다.") + void canNotMove() { + assertThatThrownBy(() -> new Finished().move(Position.of(1, 2), Position.of(1, 4))) + .isInstanceOf(IllegalStateException.class) + .hasMessage("게임이 종료되어 이동이 불가능합니다."); + } + + @Test + @DisplayName("현재 차례 조회 시 예외가 발생한다.") + void getTurn() { + assertThatThrownBy(() -> new Finished().getTurn()) + .isInstanceOf(IllegalStateException.class) + .hasMessage("게임이 종료되어 턴을 조회할 수 없습니다."); + } + + @Test + @DisplayName("현재 보드 조회 시 예외가 발생한다.") + void getBoard() { + assertThatThrownBy(() -> new Finished().getBoard()) + .isInstanceOf(IllegalStateException.class) + .hasMessage("게임이 종료되어 보드를 조회할 수 없습니다."); + } + + @Test + @DisplayName("게임 종료를 물어볼 경우 항상 참을 반환한다.") + void isFinished() { + assertThat(new Finished().isFinish()).isTrue(); + } +} diff --git a/src/test/java/chess/domain/game/status/WhiteTurnTest.java b/src/test/java/chess/domain/game/status/WhiteTurnTest.java new file mode 100644 index 00000000000..5a469d712d3 --- /dev/null +++ b/src/test/java/chess/domain/game/status/WhiteTurnTest.java @@ -0,0 +1,74 @@ +package chess.domain.game.status; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import chess.domain.board.Board; +import chess.domain.board.ClearBoardInitializer; +import chess.domain.board.SavedBoardInitializer; +import chess.domain.piece.Color; +import chess.domain.piece.King; +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import chess.domain.piece.Rook; +import chess.domain.position.Position; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class WhiteTurnTest { + + @Test + @DisplayName("Piece가 이동을 했다면 블랙 팀의 차례를 나타내는 상태를 반환한다.") + void successMoveThenReturnWhiteTurn() { + Map positionPieces = new HashMap<>(); + positionPieces.put(Position.of(1, 1), new King(Color.WHITE)); + positionPieces.put(Position.of(4, 1), new King(Color.WHITE)); + positionPieces.put(Position.of(4, 2), new Rook(Color.BLACK)); + WhiteTurn whiteTurn = new WhiteTurn(new Board(new SavedBoardInitializer(positionPieces))); + + assertThat(whiteTurn.move(Position.of(4, 1), Position.of(4, 2))) + .isInstanceOf(BlackTurn.class); + } + + @Test + @DisplayName("Piece가 이동을 했고, 왕이 잡혔다면 게임 종료의 상태를 반환한다.") + void moveAndKingKilledThenReturnFinish() { + Map positionPieces = new HashMap<>(); + positionPieces.put(Position.of(4, 1), new King(Color.BLACK)); + positionPieces.put(Position.of(1, 2), new Rook(Color.WHITE)); + positionPieces.put(Position.of(1, 1), new King(Color.BLACK)); + WhiteTurn whiteTurn = new WhiteTurn(new Board(new SavedBoardInitializer(positionPieces))); + + assertThat(whiteTurn.move(Position.of(1, 2), Position.of(1, 1))) + .isInstanceOf(Finished.class); + } + + @Test + @DisplayName("Piece가 이동이 불가능하다면 예외가 발생한다.") + void canNotMove() { + Map positionPieces = new HashMap<>(); + positionPieces.put(Position.of(1, 2), new Pawn(Color.WHITE)); + positionPieces.put(Position.of(1, 1), new King(Color.BLACK)); + WhiteTurn whiteTurn = new WhiteTurn(new Board(new SavedBoardInitializer(positionPieces))); + + assertThatThrownBy(() -> whiteTurn.move(Position.of(1, 2), Position.of(1, 1))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("해당 위치로 이동이 불가능합니다."); + } + + @Test + @DisplayName("현재 차례 조회 시 화이트를 반환한다.") + void getTurn() { + WhiteTurn whiteTurn = new WhiteTurn(new Board(new ClearBoardInitializer())); + assertThat(whiteTurn.getTurn()).isEqualTo(Color.WHITE); + } + + @Test + @DisplayName("게임 종료를 물어볼 경우 항상 거짓을 반환한다.") + void isFinished() { + WhiteTurn whiteTurn = new WhiteTurn(new Board(new ClearBoardInitializer())); + assertThat(whiteTurn.isFinish()).isFalse(); + } +} diff --git a/src/test/java/chess/domain/movement/MovementTest.java b/src/test/java/chess/domain/movement/MovementTest.java index 617e0159925..5842e6f476b 100644 --- a/src/test/java/chess/domain/movement/MovementTest.java +++ b/src/test/java/chess/domain/movement/MovementTest.java @@ -8,6 +8,7 @@ import chess.domain.movement.direction.UpRightDirection; import chess.domain.movement.policy.ColorPolicy; import chess.domain.piece.Color; +import chess.domain.piece.Empty; import chess.domain.position.Position; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -25,8 +26,8 @@ void createMovement() { void isSatisfied() { Movement movement = new Movement(new ColorPolicy(Color.WHITE), new UpRightDirection(2)); assertAll( - () -> assertThat(movement.isSatisfied(Color.WHITE, Position.of(1, 1), false)).isTrue(), - () -> assertThat(movement.isSatisfied(Color.BLACK, Position.of(1, 1), false)).isFalse() + () -> assertThat(movement.isSatisfied(Color.WHITE, Position.of(1, 1), Empty.getInstance())).isTrue(), + () -> assertThat(movement.isSatisfied(Color.BLACK, Position.of(1, 1), Empty.getInstance())).isFalse() ); } diff --git a/src/test/java/chess/domain/movement/policy/ColorPolicyTest.java b/src/test/java/chess/domain/movement/policy/ColorPolicyTest.java index 762c502019c..d23728a8827 100644 --- a/src/test/java/chess/domain/movement/policy/ColorPolicyTest.java +++ b/src/test/java/chess/domain/movement/policy/ColorPolicyTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertAll; import chess.domain.piece.Color; +import chess.domain.piece.Empty; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -20,9 +21,9 @@ void isSatisfied(boolean existEnemy) { assertAll( () -> assertThat(policy.isSatisfied(Color.WHITE, PAWN_NOT_FIRST_MOVE_POSITION.getPosition(), - existEnemy)).isTrue(), + Empty.getInstance())).isTrue(), () -> assertThat(policy.isSatisfied(Color.WHITE, WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), - existEnemy)).isTrue()); + Empty.getInstance())).isTrue()); } @ParameterizedTest @@ -31,6 +32,7 @@ void isSatisfied(boolean existEnemy) { void isNotSatisfied(boolean existEnemy) { ColorPolicy policy = new ColorPolicy(Color.WHITE); - assertThat(policy.isSatisfied(Color.BLACK, WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), existEnemy)).isFalse(); + assertThat(policy.isSatisfied(Color.BLACK, WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), + Empty.getInstance())).isFalse(); } } diff --git a/src/test/java/chess/domain/movement/policy/CombinationPolicyTest.java b/src/test/java/chess/domain/movement/policy/CombinationPolicyTest.java index 3f6346fc6b7..3a2dee16065 100644 --- a/src/test/java/chess/domain/movement/policy/CombinationPolicyTest.java +++ b/src/test/java/chess/domain/movement/policy/CombinationPolicyTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertAll; import chess.domain.piece.Color; +import chess.domain.piece.Empty; import chess.domain.position.Position; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -16,8 +17,8 @@ void isSatisfied() { CombinationPolicy policy = new CombinationPolicy(new ColorPolicy(Color.WHITE), new PawnFirstMovePolicy()); assertAll( - () -> assertThat(policy.isSatisfied(Color.WHITE, Position.of(1, 2), false)), - () -> assertThat(policy.isSatisfied(Color.WHITE, Position.of(1, 2), true)) + () -> assertThat(policy.isSatisfied(Color.WHITE, Position.of(1, 2), Empty.getInstance())), + () -> assertThat(policy.isSatisfied(Color.WHITE, Position.of(1, 2), Empty.getInstance())) ); } @@ -27,8 +28,8 @@ void isNotSatisfied() { CombinationPolicy policy = new CombinationPolicy(new ColorPolicy(Color.WHITE), new PawnFirstMovePolicy()); assertAll( - () -> assertThat(policy.isSatisfied(Color.BLACK, Position.of(1, 2), false)), - () -> assertThat(policy.isSatisfied(Color.WHITE, Position.of(1, 7), true)) + () -> assertThat(policy.isSatisfied(Color.BLACK, Position.of(1, 2), Empty.getInstance())), + () -> assertThat(policy.isSatisfied(Color.WHITE, Position.of(1, 7), Empty.getInstance())) ); } diff --git a/src/test/java/chess/domain/movement/policy/EnemyExistPolicyTest.java b/src/test/java/chess/domain/movement/policy/EnemyExistPolicyTest.java index 5dc67cda7c1..0222a3f97b0 100644 --- a/src/test/java/chess/domain/movement/policy/EnemyExistPolicyTest.java +++ b/src/test/java/chess/domain/movement/policy/EnemyExistPolicyTest.java @@ -1,30 +1,31 @@ package chess.domain.movement.policy; +import static chess.domain.pixture.PieceFixture.BLACK_BISHOP; +import static chess.domain.pixture.PieceFixture.WHITE_PAWN; import static org.assertj.core.api.Assertions.assertThat; import chess.domain.piece.Color; +import chess.domain.piece.Empty; import chess.domain.position.Position; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.api.Test; class EnemyExistPolicyTest { - @ParameterizedTest - @EnumSource(Color.class) - @DisplayName("해당 위치의 적 유무 정책과 일치 할 경우 해당 정책을 만족한다.") - void isSatisfied(Color color) { + @Test + @DisplayName("해당 위치의 적이 있을 경우, 해당 정책을 만족한다.") + void isSatisfied() { EnemyExistPolicy policy = new EnemyExistPolicy(); - assertThat(policy.isSatisfied(color, Position.of(1, 1), true)).isTrue(); + assertThat(policy.isSatisfied(Color.BLACK, Position.of(1, 1), BLACK_BISHOP.getPiece())).isFalse(); + assertThat(policy.isSatisfied(Color.BLACK, Position.of(1, 1), Empty.getInstance())).isFalse(); } - @ParameterizedTest - @EnumSource(Color.class) - @DisplayName("해당 위치의 적 유무 정책과 일치하지 않을 경우 해당 정책을 만족하지 않는다.") - void isNotSatisfied(Color color) { + @Test + @DisplayName("해당 위치의 적이 없는 경우, 해당 정책을 만족하지 않는다.") + void isNotSatisfied() { EnemyExistPolicy policy = new EnemyExistPolicy(); - assertThat(policy.isSatisfied(color, Position.of(1, 1), false)).isFalse(); + assertThat(policy.isSatisfied(Color.BLACK, Position.of(1, 1), WHITE_PAWN.getPiece())).isTrue(); } } diff --git a/src/test/java/chess/domain/movement/policy/NoRestrictionPolicyTest.java b/src/test/java/chess/domain/movement/policy/NoRestrictionPolicyTest.java index 6daec102ea4..13f3124859d 100644 --- a/src/test/java/chess/domain/movement/policy/NoRestrictionPolicyTest.java +++ b/src/test/java/chess/domain/movement/policy/NoRestrictionPolicyTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertAll; import chess.domain.piece.Color; +import chess.domain.piece.Empty; import chess.domain.position.Position; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; @@ -17,10 +18,10 @@ void isSatisfied(Color color) { NoRestrictionPolicy policy = new NoRestrictionPolicy(); assertAll( - () -> assertThat(policy.isSatisfied(color, Position.of(1, 1), false)).isTrue(), - () -> assertThat(policy.isSatisfied(color, Position.of(1, 8), true)).isTrue(), - () -> assertThat(policy.isSatisfied(color, Position.of(8, 1), false)).isTrue(), - () -> assertThat(policy.isSatisfied(color, Position.of(8, 8), true)).isTrue() + () -> assertThat(policy.isSatisfied(color, Position.of(1, 1), Empty.getInstance())).isTrue(), + () -> assertThat(policy.isSatisfied(color, Position.of(1, 8), Empty.getInstance())).isTrue(), + () -> assertThat(policy.isSatisfied(color, Position.of(8, 1), Empty.getInstance())).isTrue(), + () -> assertThat(policy.isSatisfied(color, Position.of(8, 8), Empty.getInstance())).isTrue() ); } } diff --git a/src/test/java/chess/domain/movement/policy/PawnFirstMovePolicyTest.java b/src/test/java/chess/domain/movement/policy/PawnFirstMovePolicyTest.java index 2c97d5f6cd2..6597e574adb 100644 --- a/src/test/java/chess/domain/movement/policy/PawnFirstMovePolicyTest.java +++ b/src/test/java/chess/domain/movement/policy/PawnFirstMovePolicyTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertAll; import chess.domain.piece.Color; +import chess.domain.piece.Empty; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -18,13 +19,17 @@ void isSatisfied() { assertAll( () -> assertThat( - policy.isSatisfied(Color.BLACK, BLACK_PAWN_FIRST_MOVE_POSITION.getPosition(), false)).isTrue(), + policy.isSatisfied(Color.BLACK, BLACK_PAWN_FIRST_MOVE_POSITION.getPosition(), + Empty.getInstance())).isTrue(), () -> assertThat( - policy.isSatisfied(Color.BLACK, BLACK_PAWN_FIRST_MOVE_POSITION.getPosition(), true)).isTrue(), + policy.isSatisfied(Color.BLACK, BLACK_PAWN_FIRST_MOVE_POSITION.getPosition(), + Empty.getInstance())).isTrue(), () -> assertThat( - policy.isSatisfied(Color.WHITE, WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), false)).isTrue(), + policy.isSatisfied(Color.WHITE, WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), + Empty.getInstance())).isTrue(), () -> assertThat( - policy.isSatisfied(Color.WHITE, WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), true)).isTrue() + policy.isSatisfied(Color.WHITE, WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), + Empty.getInstance())).isTrue() ); } @@ -35,13 +40,17 @@ void isNotSatisfied() { assertAll( () -> assertThat( - policy.isSatisfied(Color.BLACK, WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), false)).isFalse(), + policy.isSatisfied(Color.BLACK, WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), + Empty.getInstance())).isFalse(), () -> assertThat( - policy.isSatisfied(Color.BLACK, WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), true)).isFalse(), + policy.isSatisfied(Color.BLACK, WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), + Empty.getInstance())).isFalse(), () -> assertThat( - policy.isSatisfied(Color.WHITE, BLACK_PAWN_FIRST_MOVE_POSITION.getPosition(), false)).isFalse(), + policy.isSatisfied(Color.WHITE, BLACK_PAWN_FIRST_MOVE_POSITION.getPosition(), + Empty.getInstance())).isFalse(), () -> assertThat( - policy.isSatisfied(Color.WHITE, BLACK_PAWN_FIRST_MOVE_POSITION.getPosition(), true)).isFalse() + policy.isSatisfied(Color.WHITE, BLACK_PAWN_FIRST_MOVE_POSITION.getPosition(), + Empty.getInstance())).isFalse() ); } } 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..d2ae18bc239 --- /dev/null +++ b/src/test/java/chess/domain/piece/BishopTest.java @@ -0,0 +1,90 @@ +package chess.domain.piece; + +import static chess.domain.pixture.PieceFixture.BLACK_QUEEN; +import static chess.domain.pixture.PieceFixture.WHITE_BISHOP; +import static chess.domain.pixture.PieceFixture.WHITE_QUEEN; +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.position.Position; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class BishopTest { + /* + * .......* + * *.....*. + * .*...*... + * ..*.*... + * ...k.... + * ..*.*... + * .*...*.. + * *.....*. + * */ + @ParameterizedTest + @CsvSource({"1,1", "2,2", "3,3", "5,5", "6,6", "7,7", "8,8", + "7,1", "6,2", "3,5", "2,6", "1,7"}) + @DisplayName("비숍은 도착 위치가 비어있는 경우 이동할 수 있다.") + void canMoveWhenTargetIsEmpty(int file, int rank) { + assertThat(WHITE_BISHOP.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), Map.of())).isTrue(); + } + + /* + * .......R + * R.....R. + * .R...R... + * ..R.R... + * ...k.... + * ..R.R... + * .R...R.. + * R.....R. + * */ + @ParameterizedTest + @CsvSource({"1,1", "2,2", "3,3", "5,5", "6,6", "7,7", "8,8", + "7,1", "6,2", "3,5", "2,6", "1,7"}) + @DisplayName("비숍은 도착 위치에 상대편 말이 있는 경우 이동할 수 있다.") + void canMoveWhenTargetIsOtherColor(int file, int rank) { + assertThat(WHITE_BISHOP.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), + Map.of(Position.of(file, rank), BLACK_QUEEN.getPiece(), + Position.of(4, 4), WHITE_BISHOP.getPiece()))).isTrue(); + } + + /* + * .......r + * r.....r. + * .r...r... + * ..r.r... + * ...k.... + * ..r.r... + * .r...r.. + * r.....r. + * */ + @ParameterizedTest + @CsvSource({"1,1", "2,2", "3,3", "5,5", "6,6", "7,7", "8,8", + "7,1", "6,2", "3,5", "2,6", "1,7"}) + @DisplayName("비숍은 도착 위치에 우리편 말이 있는 경우 이동할 수 없다.") + void canNotMoveWhenTargetIsSameColor(int file, int rank) { + assertThat(WHITE_BISHOP.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), + Map.of(Position.of(4, 4), WHITE_BISHOP.getPiece(), + Position.of(file, rank), WHITE_QUEEN.getPiece()))).isFalse(); + } + + /* + * .......r + * ........ + * .....P... + * ........ + * ...k.... + * ........ + * ........ + * ........ + * */ + @Test + @DisplayName("비숍은 이동 경로에 말이 있는 경우 이동할 수 없다.") + void canNotMoveWhenPieceExistIn() { + assertThat(WHITE_BISHOP.getPiece().canMove(Position.of(4, 4), Position.of(8, 8), + Map.of(Position.of(6, 6), WHITE_QUEEN.getPiece()))).isFalse(); + } +} diff --git a/src/test/java/chess/domain/piece/ColorTest.java b/src/test/java/chess/domain/piece/ColorTest.java index a8bbff59531..d984358dd25 100644 --- a/src/test/java/chess/domain/piece/ColorTest.java +++ b/src/test/java/chess/domain/piece/ColorTest.java @@ -1,6 +1,7 @@ package chess.domain.piece; import static chess.domain.piece.Color.BLACK; +import static chess.domain.piece.Color.NONE; import static chess.domain.piece.Color.WHITE; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; @@ -11,17 +12,17 @@ class ColorTest { @Test - @DisplayName("같은 색이 아닌 경우 참을 반환한다.") + @DisplayName("같은 색이 아닌 경우 거짓을 반환한다.") void isEnemyColor() { assertAll( - () -> assertThat(WHITE.isNotSameTeam(new Piece(PieceType.PAWN, BLACK))).isTrue(), - () -> assertThat(WHITE.isNotSameTeam(Piece.getEmptyPiece())).isTrue() + () -> assertThat(WHITE.isSameColor(BLACK)).isFalse(), + () -> assertThat(WHITE.isSameColor(NONE)).isFalse() ); } @Test - @DisplayName("같은 색인 경우 거짓을 반환한다.") + @DisplayName("같은 색인 경우 참을 반환한다.") void isNotEnemyColor() { - assertThat(BLACK.isNotSameTeam(new Piece(PieceType.PAWN, BLACK))).isFalse(); + assertThat(BLACK.isSameColor(BLACK)).isTrue(); } } 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..3f8ee85e6dd --- /dev/null +++ b/src/test/java/chess/domain/piece/KingTest.java @@ -0,0 +1,78 @@ +package chess.domain.piece; + +import static chess.domain.pixture.PieceFixture.BLACK_QUEEN; +import static chess.domain.pixture.PieceFixture.WHITE_KING; +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.position.Position; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class KingTest { + /* + * ........ + * ........ + * ........ + * ..EEE... + * ..EkE... + * ..EEE... + * ........ + * ........ + * */ + @ParameterizedTest + @CsvSource({"3,3", "5,5", + "3,5", + "5,4", "3,4", + "4,3", "4,5",}) + @DisplayName("킹은 도착 위치가 비어있는 경우 이동할 수 있다.") + void canMoveWhenTargetIsEmpty(int file, int rank) { + assertThat(WHITE_KING.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), Map.of())).isTrue(); + } + + /* + * ........ + * ........ + * ........ + * ..QQQ... + * ..QkQ... + * ..QQQ... + * ........ + * ........ + * */ + @ParameterizedTest + @CsvSource({"3,3", "5,5", + "3,5", "5,3", + "5,4", "3,4", + "4,3", "4,5",}) + @DisplayName("킹은 도착 위치에 상대편 말이 있는 경우 이동할 수 있다.") + void canMoveWhenTargetIsOtherColor(int file, int rank) { + assertThat(WHITE_KING.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), + Map.of(Position.of(file, rank), BLACK_QUEEN.getPiece()))).isTrue(); + } + + /* + * ........ + * ........ + * ........ + * ..qqq... + * ..qkq... + * ..qqq... + * ........ + * ........ + * */ + @ParameterizedTest + @CsvSource({"3,3", "5,5", + "3,5", "5,3", + "5,4", "3,4", + "4,3", "4,5",}) + @DisplayName("킹은 도착 위치에 우리편 말이 있는 경우 이동할 수 없다.") + void canNotMoveWhenTargetIsSameColor(int rank, int file) { + Position source = Position.of(4, 4); + Position target = Position.of(file, rank); + assertThat(WHITE_KING.getPiece().canMove(source, target, + Map.of(source, WHITE_KING.getPiece(), + target, new Queen(Color.WHITE)))).isFalse(); + } +} 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..2464b2f51ce --- /dev/null +++ b/src/test/java/chess/domain/piece/KnightTest.java @@ -0,0 +1,71 @@ +package chess.domain.piece; + +import static chess.domain.pixture.PieceFixture.BLACK_QUEEN; +import static chess.domain.pixture.PieceFixture.WHITE_KNIGHT; +import static chess.domain.pixture.PieceFixture.WHITE_QUEEN; +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.position.Position; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class KnightTest { + + /* + * ........ + * ........ + * ..E.E... + * .E...E.. + * ...k.... + * .E...E.. + * ..E.E... + * ........ + * */ + @ParameterizedTest + @CsvSource({"5,6", "5,2", "3,6", "3,2", "6,5", "6,3", "2,5", "2,3"}) + @DisplayName("나이트는 도착 위치가 비어있는 경우 이동할 수 있다.") + void canMoveWhenTargetIsEmpty(int file, int rank) { + assertThat(WHITE_KNIGHT.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), + Map.of(Position.of(4, 4), WHITE_KNIGHT.getPiece()))).isTrue(); + } + + /* + * ........ + * ........ + * ..*.*... + * .*...*.. + * ...k.... + * .*...*.. + * ..*.*... + * ........ + * */ + @ParameterizedTest + @CsvSource({"5,6", "5,2", "3,6", "3,2", "6,5", "6,3", "2,5", "2,3"}) + @DisplayName("나이트는 도착 위치에 상대편 말이 있는 경우 이동할 수 있다.") + void canMoveWhenTargetIsOtherColor(int file, int rank) { + assertThat(WHITE_KNIGHT.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), + Map.of(Position.of(4, 4), WHITE_KNIGHT.getPiece(), + Position.of(file, rank), BLACK_QUEEN.getPiece()))).isTrue(); + } + + /* + * ........ + * ........ + * ..k.k... + * .k...k.. + * ...k.... + * .k...k.. + * ..k.k... + * ........ + * */ + @ParameterizedTest + @CsvSource({"5,6", "5,2", "3,6", "3,2", "6,5", "6,3", "2,5", "2,3"}) + @DisplayName("나이트는 도착 위치에 우리편 말이 있는 경우 이동할 수 없다.") + void canNotMoveWhenTargetIsSameColor(int file, int rank) { + assertThat(WHITE_KNIGHT.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), + Map.of(Position.of(4, 4), WHITE_KNIGHT.getPiece(), + Position.of(file, rank), WHITE_QUEEN.getPiece()))).isFalse(); + } +} 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..8038372b5c6 --- /dev/null +++ b/src/test/java/chess/domain/piece/PawnTest.java @@ -0,0 +1,118 @@ +package chess.domain.piece; + +import static chess.domain.pixture.PieceFixture.BLACK_QUEEN; +import static chess.domain.pixture.PieceFixture.WHITE_PAWN; +import static chess.domain.pixture.PieceFixture.WHITE_QUEEN; +import static chess.domain.pixture.PositionFixture.WHITE_PAWN_FIRST_MOVE_POSITION; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import chess.domain.position.Position; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class PawnTest { + + // p** <- (1,3) + // p** <- (1,4) + // P** + @Test + @DisplayName("폰이 직선으로 이동할 경우 경로에 어떠한 말도 없다면 이동 가능하다.") + void canMoveStraight() { + assertAll( + () -> assertThat(WHITE_PAWN.getPiece() + .canMove(WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), Position.of(1, 3), + Map.of(WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), + WHITE_PAWN.getPiece()))).isTrue(), + + () -> assertThat(WHITE_PAWN.getPiece() + .canMove(WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), Position.of(1, 4), + Map.of(WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), + WHITE_PAWN.getPiece()))).isTrue()); + } + + // **** + // *p*p <- target + // **P* <- source + @Test + @DisplayName("폰이 대각선 이동할 경우 도착 위치에 상대방의 말이 있다면 이동 가능하다.") + void canMoveDiagonal() { + Position source = Position.of(3, 1); + assertAll( + () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(2, 2), + Map.of(source, WHITE_PAWN.getPiece(), + Position.of(2, 2), BLACK_QUEEN.getPiece()))).isTrue(), + + () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(4, 2), + Map.of(source, WHITE_PAWN.getPiece(), + Position.of(4, 2), BLACK_QUEEN.getPiece()))).isTrue()); + } + + // **** + // p*** <- target(p, P) + // p*** <- source + @Test + @DisplayName("폰이 직선으로 이동할 경우 도착 위치에 말이 있다면 이동이 불가능하다.") + void canNotMoveStraightWhenPieceExistInTarget() { + Position source = Position.of(1, 1); + assertAll( + () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(1, 2), + Map.of(source, WHITE_PAWN.getPiece(), + Position.of(1, 2), WHITE_QUEEN.getPiece()))).isFalse(), + + () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(1, 2), + Map.of(source, WHITE_PAWN.getPiece(), + Position.of(1, 2), BLACK_QUEEN.getPiece()))).isFalse()); + } + + + // p*** <- target(p, P) + // q*** + // p*** <- source + @Test + @DisplayName("폰이 직선으로 이동할 경우 도착 위치 전의 경로에 말이 있다면 이동이 불가능하다.") + void canNotMoveStraightWhenPieceExistInPath() { + Position source = Position.of(1, 1); + assertAll( + () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(1, 3), + Map.of(source, WHITE_PAWN.getPiece(), + Position.of(1, 2), BLACK_QUEEN.getPiece(), + Position.of(1, 3), Empty.getInstance()))).isFalse(), + () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(1, 3), + Map.of(source, WHITE_PAWN.getPiece(), + Position.of(1, 2), WHITE_QUEEN.getPiece(), + Position.of(1, 3), Empty.getInstance()))).isFalse()); + } + + // *p*p <- target + // **p* <- source + @Test + @DisplayName("폰이 대각선으로 이동할 경우 도착 위치가 같은 색의 말인 경우 이동이 불가능하다.") + void canNotMoveDiagonalWhenTargetIsSameColor() { + Position source = Position.of(3, 1); + assertAll( + () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(2, 2), + Map.of(source, WHITE_PAWN.getPiece(), + Position.of(2, 2), WHITE_QUEEN.getPiece()))).isFalse(), + () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(4, 2), + Map.of(source, WHITE_PAWN.getPiece(), + Position.of(4, 2), WHITE_QUEEN.getPiece()))).isFalse()); + } + + // *E*E <- target(EMPTY) + // **p* <- source + @Test + @DisplayName("폰이 대각선으로 이동할 경우 도착 위치가 비어있을 경우 이동이 불가능하다.") + void canNotMoveDiagonalWhenTargetIsEmpty() { + assertAll( + () -> assertThat(WHITE_PAWN.getPiece().canMove(Position.of(3, 1), Position.of(2, 2), + Map.of(Position.of(3, 1), WHITE_PAWN.getPiece(), + Position.of(2, 2), Empty.getInstance()))).isFalse(), + + () -> assertThat( + WHITE_PAWN.getPiece().canMove(Position.of(3, 1), Position.of(4, 2), + Map.of(Position.of(3, 1), WHITE_PAWN.getPiece(), + Position.of(4, 2), Empty.getInstance()))).isFalse()); + } +} diff --git a/src/test/java/chess/domain/piece/PieceFactoryTest.java b/src/test/java/chess/domain/piece/PieceFactoryTest.java new file mode 100644 index 00000000000..5aec24c021d --- /dev/null +++ b/src/test/java/chess/domain/piece/PieceFactoryTest.java @@ -0,0 +1,58 @@ +package chess.domain.piece; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PieceFactoryTest { + + @Test + @DisplayName("기물 타입이 폰일 경우 폰 객체를 생성한다.") + void createPawn() { + Piece whitePawn = PieceFactory.create(PieceType.PAWN, Color.WHITE); + assertThat(whitePawn).isEqualTo(new Pawn(Color.WHITE)); + } + + @Test + @DisplayName("기물 타입이 킹일 경우 킹 객체를 생성한다.") + void createKing() { + Piece whiteKing = PieceFactory.create(PieceType.KING, Color.WHITE); + assertThat(whiteKing).isEqualTo(new King(Color.WHITE)); + } + + @Test + @DisplayName("기물 타입이 퀸일 경우 퀸 객체를 생성한다.") + void createQueen() { + Piece whiteQueen = PieceFactory.create(PieceType.QUEEN, Color.WHITE); + assertThat(whiteQueen).isEqualTo(new Queen(Color.WHITE)); + } + + @Test + @DisplayName("기물 타입이 나이트일 경우 나이트 객체를 생성한다.") + void createKnight() { + Piece whiteKnight = PieceFactory.create(PieceType.KNIGHT, Color.WHITE); + assertThat(whiteKnight).isEqualTo(new Knight(Color.WHITE)); + } + + @Test + @DisplayName("기물 타입이 룩일 경우 룩 객체를 생성한다.") + void createRook() { + Piece whiteRook = PieceFactory.create(PieceType.ROOK, Color.WHITE); + assertThat(whiteRook).isEqualTo(new Rook(Color.WHITE)); + } + + @Test + @DisplayName("기물 타입이 비숍일 경우 비숍 객체를 생성한다.") + void createBishop() { + Piece whiteBishop = PieceFactory.create(PieceType.BISHOP, Color.WHITE); + assertThat(whiteBishop).isEqualTo(new Bishop(Color.WHITE)); + } + + @Test + @DisplayName("기물 타입이 없을 경우 Empty 객체를 생성한다.") + void createEmpty() { + Piece empty = PieceFactory.create(PieceType.EMPTY, Color.NONE); + assertThat(empty).isEqualTo(Empty.getInstance()); + } +} diff --git a/src/test/java/chess/domain/piece/PieceTest.java b/src/test/java/chess/domain/piece/PieceTest.java deleted file mode 100644 index f6b2b2bef79..00000000000 --- a/src/test/java/chess/domain/piece/PieceTest.java +++ /dev/null @@ -1,491 +0,0 @@ -package chess.domain.piece; - -import static chess.domain.pixture.PieceFixture.BLACK_QUEEN; -import static chess.domain.pixture.PieceFixture.WHITE_BISHOP; -import static chess.domain.pixture.PieceFixture.WHITE_KING; -import static chess.domain.pixture.PieceFixture.WHITE_KNIGHT; -import static chess.domain.pixture.PieceFixture.WHITE_PAWN; -import static chess.domain.pixture.PieceFixture.WHITE_QUEEN; -import static chess.domain.pixture.PieceFixture.WHITE_ROOK; -import static chess.domain.pixture.PositionFixture.WHITE_PAWN_FIRST_MOVE_POSITION; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -import chess.domain.position.Position; -import java.util.Map; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -class PieceTest { - - @Nested - class PawnMove { - - // p** <- (1,3) - // p** <- (1,4) - // P** - @Test - @DisplayName("폰이 직선으로 이동할 경우 경로에 어떠한 말도 없다면 이동 가능하다.") - void canMoveStraight() { - assertAll( - () -> assertThat(WHITE_PAWN.getPiece() - .canMove(WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), Position.of(1, 3), - Map.of(WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), - WHITE_PAWN.getPiece()))).isTrue(), - - () -> assertThat(WHITE_PAWN.getPiece() - .canMove(WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), Position.of(1, 4), - Map.of(WHITE_PAWN_FIRST_MOVE_POSITION.getPosition(), - WHITE_PAWN.getPiece()))).isTrue()); - } - - // **** - // *p*p <- target - // **P* <- source - @Test - @DisplayName("폰이 대각선 이동할 경우 도착 위치에 상대방의 말이 있다면 이동 가능하다.") - void canMoveDiagonal() { - Position source = Position.of(3, 1); - assertAll( - () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(2, 2), - Map.of(source, WHITE_PAWN.getPiece(), - Position.of(2, 2), BLACK_QUEEN.getPiece()))).isTrue(), - - () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(4, 2), - Map.of(source, WHITE_PAWN.getPiece(), - Position.of(4, 2), BLACK_QUEEN.getPiece()))).isTrue()); - } - - // **** - // p*** <- target(p, P) - // p*** <- source - @Test - @DisplayName("폰이 직선으로 이동할 경우 도착 위치에 말이 있다면 이동이 불가능하다.") - void canNotMoveStraightWhenPieceExistInTarget() { - Position source = Position.of(1, 1); - assertAll( - () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(1, 2), - Map.of(source, WHITE_PAWN.getPiece(), - Position.of(1, 2), WHITE_QUEEN.getPiece()))).isFalse(), - - () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(1, 2), - Map.of(source, WHITE_PAWN.getPiece(), - Position.of(1, 2), BLACK_QUEEN.getPiece()))).isFalse()); - } - - - // p*** <- target(p, P) - // q*** - // p*** <- source - @Test - @DisplayName("폰이 직선으로 이동할 경우 도착 위치 전의 경로에 말이 있다면 이동이 불가능하다.") - void canNotMoveStraightWhenPieceExistInPath() { - Position source = Position.of(1, 1); - assertAll( - () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(1, 3), - Map.of(source, WHITE_PAWN.getPiece(), - Position.of(1, 2), BLACK_QUEEN.getPiece()))).isFalse(), - () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(1, 3), - Map.of(source, WHITE_PAWN.getPiece(), - Position.of(1, 2), WHITE_QUEEN.getPiece()))).isFalse()); - } - - // *p*p <- target - // **p* <- source - @Test - @DisplayName("폰이 대각선으로 이동할 경우 도착 위치가 같은 색의 말인 경우 이동이 불가능하다.") - void canNotMoveDiagonalWhenTargetIsSameColor() { - Position source = Position.of(3, 1); - assertAll( - () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(2, 2), - Map.of(source, WHITE_PAWN.getPiece(), - Position.of(2, 2), WHITE_QUEEN.getPiece()))).isFalse(), - () -> assertThat(WHITE_PAWN.getPiece().canMove(source, Position.of(4, 2), - Map.of(source, WHITE_PAWN.getPiece(), - Position.of(4, 2), WHITE_QUEEN.getPiece()))).isFalse()); - } - - // *E*E <- target(EMPTY) - // **p* <- source - @Test - @DisplayName("폰이 대각선으로 이동할 경우 도착 위치가 비어있을 경우 이동이 불가능하다.") - void canNotMoveDiagonalWhenTargetIsEmpty() { - assertAll( - () -> assertThat(WHITE_PAWN.getPiece().canMove(Position.of(3, 1), Position.of(2, 2), - Map.of(Position.of(3, 1), WHITE_PAWN.getPiece()))).isFalse(), - - () -> assertThat( - WHITE_PAWN.getPiece().canMove(Position.of(3, 1), Position.of(4, 2), - Map.of(Position.of(3, 1), WHITE_PAWN.getPiece()))).isFalse()); - } - } - - @Nested - class KnightMove { - - /* - * ........ - * ........ - * ..E.E... - * .E...E.. - * ...k.... - * .E...E.. - * ..E.E... - * ........ - * */ - @ParameterizedTest - @CsvSource({"5,6", "5,2", "3,6", "3,2", "6,5", "6,3", "2,5", "2,3"}) - @DisplayName("나이트는 도착 위치가 비어있는 경우 이동할 수 있다.") - void canMoveWhenTargetIsEmpty(int file, int rank) { - assertThat(WHITE_KNIGHT.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), Map.of())).isTrue(); - } - - /* - * ........ - * ........ - * ..*.*... - * .*...*.. - * ...k.... - * .*...*.. - * ..*.*... - * ........ - * */ - @ParameterizedTest - @CsvSource({"5,6", "5,2", "3,6", "3,2", "6,5", "6,3", "2,5", "2,3"}) - @DisplayName("나이트는 도착 위치에 상대편 말이 있는 경우 이동할 수 있다.") - void canMoveWhenTargetIsOtherColor(int file, int rank) { - assertThat(WHITE_KNIGHT.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), - Map.of(Position.of(file, rank), BLACK_QUEEN.getPiece()))).isTrue(); - } - - /* - * ........ - * ........ - * ..k.k... - * .k...k.. - * ...k.... - * .k...k.. - * ..k.k... - * ........ - * */ - @ParameterizedTest - @CsvSource({"5,6", "5,2", "3,6", "3,2", "6,5", "6,3", "2,5", "2,3"}) - @DisplayName("나이트는 도착 위치에 우리편 말이 있는 경우 이동할 수 없다.") - void canNotMoveWhenTargetIsSameColor(int file, int rank) { - assertThat(WHITE_KNIGHT.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), - Map.of(Position.of(4, 4), WHITE_KNIGHT.getPiece(), - Position.of(file, rank), WHITE_QUEEN.getPiece()))).isFalse(); - } - } - - @Nested - class BishopMove { - - /* - * .......* - * *.....*. - * .*...*... - * ..*.*... - * ...k.... - * ..*.*... - * .*...*.. - * *.....*. - * */ - @ParameterizedTest - @CsvSource({"1,1", "2,2", "3,3", "5,5", "6,6", "7,7", "8,8", - "7,1", "6,2", "3,5", "2,6", "1,7"}) - @DisplayName("비숍은 도착 위치가 비어있는 경우 이동할 수 있다.") - void canMoveWhenTargetIsEmpty(int file, int rank) { - assertThat(WHITE_BISHOP.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), Map.of())).isTrue(); - } - - /* - * .......R - * R.....R. - * .R...R... - * ..R.R... - * ...k.... - * ..R.R... - * .R...R.. - * R.....R. - * */ - @ParameterizedTest - @CsvSource({"1,1", "2,2", "3,3", "5,5", "6,6", "7,7", "8,8", - "7,1", "6,2", "3,5", "2,6", "1,7"}) - @DisplayName("비숍은 도착 위치에 상대편 말이 있는 경우 이동할 수 있다.") - void canMoveWhenTargetIsOtherColor(int file, int rank) { - assertThat(WHITE_BISHOP.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), - Map.of(Position.of(file, rank), BLACK_QUEEN.getPiece()))).isTrue(); - } - - /* - * .......r - * r.....r. - * .r...r... - * ..r.r... - * ...k.... - * ..r.r... - * .r...r.. - * r.....r. - * */ - @ParameterizedTest - @CsvSource({"1,1", "2,2", "3,3", "5,5", "6,6", "7,7", "8,8", - "7,1", "6,2", "3,5", "2,6", "1,7"}) - @DisplayName("비숍은 도착 위치에 우리편 말이 있는 경우 이동할 수 없다.") - void canNotMoveWhenTargetIsSameColor(int file, int rank) { - assertThat(WHITE_BISHOP.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), - Map.of(Position.of(4, 4), WHITE_BISHOP.getPiece(), - Position.of(file, rank), WHITE_QUEEN.getPiece()))).isFalse(); - } - - /* - * .......r - * ........ - * .....P... - * ........ - * ...k.... - * ........ - * ........ - * ........ - * */ - @Test - @DisplayName("비숍은 이동 경로에 말이 있는 경우 이동할 수 없다.") - void canNotMoveWhenPieceExistIn() { - assertThat(WHITE_BISHOP.getPiece().canMove(Position.of(4, 4), Position.of(8, 8), - Map.of(Position.of(6, 6), WHITE_QUEEN.getPiece()))).isFalse(); - } - } - - @Nested - class RookMove { - - /* - * ...E.... - * ...E.... - * ...E..... - * ...E.... - * EEErEEEE - * ...E.... - * ...E.... - * ...E.... - * */ - @ParameterizedTest - @CsvSource({"8,4", "7,4", "6,4", "5,4", "3,4", "2,4", "1,4", - "4,1", "4,2", "4,3", "4,5", "4,6", "4,7", "4,8"}) - @DisplayName("룩은 도착 위치가 비어있는 경우 이동할 수 있다.") - void canMoveWhenTargetIsEmpty(int file, int rank) { - assertThat(WHITE_ROOK.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), Map.of())).isTrue(); - } - - /* - * ...R.... - * ...R.... - * ...R..... - * ...R.... - * RRRrRRRR - * ...R.... - * ...R.... - * ...R.... - * */ - @ParameterizedTest - @CsvSource({"8,4", "7,4", "6,4", "5,4", "3,4", "2,4", "1,4", - "4,1", "4,2", "4,3", "4,5", "4,6", "4,7", "4,8"}) - @DisplayName("룩은 도착 위치에 상대편 말이 있는 경우 이동할 수 있다.") - void canMoveWhenTargetIsOtherColor(int file, int rank) { - assertThat(WHITE_ROOK.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), - Map.of(Position.of(file, rank), BLACK_QUEEN.getPiece()))).isTrue(); - } - - /* - * ...p.... - * ...p.... - * ...p..... - * ...p.... - * ppprpppp - * ...p.... - * ...p.... - * ...p.... - * */ - @ParameterizedTest - @CsvSource({"8,4", "7,4", "6,4", "5,4", "3,4", "2,4", "1,4", - "4,1", "4,2", "4,3", "4,5", "4,6", "4,7", "4,8"}) - @DisplayName("룩은 도착 위치에 우리편 말이 있는 경우 이동할 수 없다.") - void canNotMoveWhenTargetIsSameColor(int file, int rank) { - assertThat(WHITE_ROOK.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), - Map.of(Position.of(4, 4), WHITE_ROOK.getPiece(), - Position.of(file, rank), WHITE_QUEEN.getPiece()))).isFalse(); - } - - /* - * ........ - * ........ - * ......... - * ........ - * ...r...p - * ........ - * ........ - * ........ - * */ - @Test - @DisplayName("룩은 이동 경로에 말이 있는 경우 이동할 수 없다.") - void canNotMoveWhenPieceExistIn() { - assertThat(WHITE_ROOK.getPiece().canMove(Position.of(4, 4), Position.of(8, 4), - Map.of(Position.of(5, 4), new Piece(PieceType.QUEEN, Color.WHITE)))).isFalse(); - } - } - - @Nested - class QueenMove { - - /* - * ...E...E - * E..E..E. - * .E.E.E... - * ..EEE... - * EEEqEEEE - * ..EEE... - * .E.E.E.. - * E..E..E. - * */ - @ParameterizedTest - @CsvSource({"1,1", "2,2", "3,3", "5,5", "6,6", "7,7", "8,8", - "7,1", "6,2", "3,5", "2,6", "1,7", - "8,4", "7,4", "6,4", "5,4", "3,4", "2,4", "1,4", - "4,1", "4,2", "4,3", "4,5", "4,6", "4,7", "4,8"}) - @DisplayName("퀸은 도착 위치가 비어있는 경우 이동할 수 있다.") - void canMoveWhenTargetIsEmpty(int file, int rank) { - assertThat(WHITE_QUEEN.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), Map.of())).isTrue(); - } - - /* - * ...R...R - * R..R..R. - * .R.R.R... - * ..RRR... - * RRRqRRRR - * ..RRR... - * .R.R.R.. - * R..R..R. - * */ - @ParameterizedTest - @CsvSource({"1,1", "2,2", "3,3", "5,5", "6,6", "7,7", "8,8", - "7,1", "6,2", "3,5", "2,6", "1,7", - "8,4", "7,4", "6,4", "5,4", "3,4", "2,4", "1,4", - "4,1", "4,2", "4,3", "4,5", "4,6", "4,7", "4,8"}) - @DisplayName("퀸은 도착 위치에 상대편 말이 있는 경우 이동할 수 있다.") - void canMoveWhenTargetIsOtherColor(int file, int rank) { - assertThat(WHITE_QUEEN.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), - Map.of(Position.of(file, rank), BLACK_QUEEN.getPiece()))).isTrue(); - } - - /* - * ...r...r - * r..r..r. - * .r.r.r... - * ..rrr... - * rrrqrrrr - * ..rrr... - * .r.r.r.. - * r..r..r. - * */ - @ParameterizedTest - @CsvSource({"1,1", "2,2", "3,3", "5,5", "6,6", "7,7", "8,8", - "7,1", "6,2", "3,5", "2,6", "1,7", - "8,4", "7,4", "6,4", "5,4", "3,4", "2,4", "1,4", - "4,1", "4,2", "4,3", "4,5", "4,6", "4,7", "4,8"}) - @DisplayName("퀸은 도착 위치에 우리편 말이 있는 경우 이동할 수 없다.") - void canNotMoveWhenTargetIsSameColor(int file, int rank) { - assertThat(WHITE_QUEEN.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), - Map.of(Position.of(4, 4), piece, - Position.of(file, rank), WHITE_QUEEN.getPiece()))).isFalse(); - } - - /* - * .......Q - * ........ - * .....R.. - * ........ - * ...q.... - * ........ - * ........ - * ........ - * */ - @Test - @DisplayName("퀸은 이동 경로에 말이 있는 경우 이동할 수 없다.") - void canNotMoveWhenPieceExistIn() { - assertThat(WHITE_QUEEN.getPiece().canMove(Position.of(4, 4), Position.of(8, 8), - Map.of(Position.of(6, 6), WHITE_QUEEN.getPiece()))).isFalse(); - } - } - - @Nested - class KingMove { - - /* - * ........ - * ........ - * ........ - * ..EEE... - * ..EkE... - * ..EEE... - * ........ - * ........ - * */ - @ParameterizedTest - @CsvSource({"3,3", "5,5", - "3,5", - "5,4", "3,4", - "4,3", "4,5",}) - @DisplayName("킹은 도착 위치가 비어있는 경우 이동할 수 있다.") - void canMoveWhenTargetIsEmpty(int file, int rank) { - assertThat(WHITE_KING.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), Map.of())).isTrue(); - } - - /* - * ........ - * ........ - * ........ - * ..QQQ... - * ..QkQ... - * ..QQQ... - * ........ - * ........ - * */ - @ParameterizedTest - @CsvSource({"3,3", "5,5", - "3,5", "5,3", - "5,4", "3,4", - "4,3", "4,5",}) - @DisplayName("킹은 도착 위치에 상대편 말이 있는 경우 이동할 수 있다.") - void canMoveWhenTargetIsOtherColor(int file, int rank) { - assertThat(WHITE_KING.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), - Map.of(Position.of(file, rank), BLACK_QUEEN.getPiece()))).isTrue(); - } - - /* - * ........ - * ........ - * ........ - * ..qqq... - * ..qkq... - * ..qqq... - * ........ - * ........ - * */ - @ParameterizedTest - @CsvSource({"3,3", "5,5", - "3,5", "5,3", - "5,4", "3,4", - "4,3", "4,5",}) - @DisplayName("킹은 도착 위치에 우리편 말이 있는 경우 이동할 수 없다.") - void canNotMoveWhenTargetIsSameColor(int rank, int file) { - assertThat(WHITE_KING.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), - Map.of(Position.of(4, 4), WHITE_KING.getPiece(), - Position.of(file, rank), new Piece(PieceType.QUEEN, Color.WHITE)))).isFalse(); - } - } -} - diff --git a/src/test/java/chess/domain/piece/PieceTypeTest.java b/src/test/java/chess/domain/piece/PieceTypeTest.java new file mode 100644 index 00000000000..7d6ab66935e --- /dev/null +++ b/src/test/java/chess/domain/piece/PieceTypeTest.java @@ -0,0 +1,88 @@ +package chess.domain.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import chess.domain.position.Position; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class PieceTypeTest { + + @Test + @DisplayName("킹은 점수가 없다.") + void findScoreKing() { + assertAll( + () -> assertThat(PieceType.KING.findScore(List.of(Position.of(1, 2), Position.of(2, 2)))).isEqualTo(0), + () -> assertThat(PieceType.KING.findScore(List.of(Position.of(1, 2)))) + .isEqualTo(0)); + } + + @Test + @DisplayName("퀸은 피스의 수별 9점을 받는다.") + void findScoreQueen() { + assertAll( + () -> assertThat(PieceType.QUEEN.findScore(List.of())).isEqualTo(0), + () -> assertThat(PieceType.QUEEN.findScore(List.of(Position.of(1, 2)))).isEqualTo(9), + () -> assertThat(PieceType.QUEEN.findScore(List.of(Position.of(1, 2), Position.of(1, 2)))) + .isEqualTo(18)); + } + + @Test + @DisplayName("비숍은 피스의 수별 3점을 받는다.") + void findScoreBishop() { + assertAll( + () -> assertThat(PieceType.BISHOP.findScore(List.of())).isEqualTo(0), + () -> assertThat(PieceType.BISHOP.findScore(List.of(Position.of(1, 2)))).isEqualTo(3), + () -> assertThat(PieceType.BISHOP.findScore(List.of(Position.of(1, 2), Position.of(1, 2)))) + .isEqualTo(6)); + } + + @Test + @DisplayName("룩은 피스의 수별 3점을 받는다.") + void findScoreRook() { + assertAll( + () -> assertThat(PieceType.ROOK.findScore(List.of())).isEqualTo(0), + () -> assertThat(PieceType.ROOK.findScore(List.of(Position.of(1, 2)))).isEqualTo(5), + () -> assertThat(PieceType.ROOK.findScore(List.of(Position.of(1, 2), Position.of(1, 2)))) + .isEqualTo(10)); + } + + @Test + @DisplayName("나이트는 피스의 수별 2.5점을 받는다.") + void findScoreKnight() { + assertAll( + () -> assertThat(PieceType.KNIGHT.findScore(List.of())).isEqualTo(0), + () -> assertThat(PieceType.KNIGHT.findScore(List.of(Position.of(1, 2)))).isEqualTo(2.5), + () -> assertThat(PieceType.KNIGHT.findScore(List.of(Position.of(1, 2), Position.of(1, 2)))) + .isEqualTo(5)); + } + + @Nested + class pawnScore { + + @Test + @DisplayName("같은 세로 줄에 같은 색의 폰이 여러개 있는 경우, 각각 0.5점") + void findScorePawn() { + double score = PieceType.PAWN.findScore(List.of( + Position.of(1, 2), + Position.of(1, 3) + )); + + assertThat(score).isEqualTo(1); + } + + @Test + @DisplayName("같은 세로 줄에 같은 색의 폰이 하나만 있는 경우, 1점(기본점수)") + void findScoreSameFilePawn() { + double score = PieceType.PAWN.findScore(List.of( + Position.of(1, 2), + Position.of(2, 2) + )); + + assertThat(score).isEqualTo(2); + } + } +} 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..ae835f5a490 --- /dev/null +++ b/src/test/java/chess/domain/piece/QueenTest.java @@ -0,0 +1,95 @@ +package chess.domain.piece; + +import static chess.domain.pixture.PieceFixture.BLACK_QUEEN; +import static chess.domain.pixture.PieceFixture.WHITE_QUEEN; +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.position.Position; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class QueenTest { + + /* + * ...E...E + * E..E..E. + * .E.E.E... + * ..EEE... + * EEEqEEEE + * ..EEE... + * .E.E.E.. + * E..E..E. + * */ + @ParameterizedTest + @CsvSource({"1,1", "2,2", "3,3", "5,5", "6,6", "7,7", "8,8", + "7,1", "6,2", "3,5", "2,6", "1,7", + "8,4", "7,4", "6,4", "5,4", "3,4", "2,4", "1,4", + "4,1", "4,2", "4,3", "4,5", "4,6", "4,7", "4,8"}) + @DisplayName("퀸은 도착 위치가 비어있는 경우 이동할 수 있다.") + void canMoveWhenTargetIsEmpty(int file, int rank) { + assertThat(WHITE_QUEEN.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), Map.of())).isTrue(); + } + + /* + * ...R...R + * R..R..R. + * .R.R.R... + * ..RRR... + * RRRqRRRR + * ..RRR... + * .R.R.R.. + * R..R..R. + * */ + @ParameterizedTest + @CsvSource({"1,1", "2,2", "3,3", "5,5", "6,6", "7,7", "8,8", + "7,1", "6,2", "3,5", "2,6", "1,7", + "8,4", "7,4", "6,4", "5,4", "3,4", "2,4", "1,4", + "4,1", "4,2", "4,3", "4,5", "4,6", "4,7", "4,8"}) + @DisplayName("퀸은 도착 위치에 상대편 말이 있는 경우 이동할 수 있다.") + void canMoveWhenTargetIsOtherColor(int file, int rank) { + assertThat(WHITE_QUEEN.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), + Map.of(Position.of(file, rank), BLACK_QUEEN.getPiece()))).isTrue(); + } + + /* + * ...r...r + * r..r..r. + * .r.r.r... + * ..rrr... + * rrrqrrrr + * ..rrr... + * .r.r.r.. + * r..r..r. + * */ + @ParameterizedTest + @CsvSource({"1,1", "2,2", "3,3", "5,5", "6,6", "7,7", "8,8", + "7,1", "6,2", "3,5", "2,6", "1,7", + "8,4", "7,4", "6,4", "5,4", "3,4", "2,4", "1,4", + "4,1", "4,2", "4,3", "4,5", "4,6", "4,7", "4,8"}) + @DisplayName("퀸은 도착 위치에 우리편 말이 있는 경우 이동할 수 없다.") + void canNotMoveWhenTargetIsSameColor(int file, int rank) { + assertThat(WHITE_QUEEN.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), + Map.of(Position.of(4, 4), WHITE_QUEEN.getPiece(), + Position.of(file, rank), WHITE_QUEEN.getPiece()))).isFalse(); + } + + /* + * .......Q + * ........ + * .....R.. + * ........ + * ...q.... + * ........ + * ........ + * ........ + * */ + @Test + @DisplayName("퀸은 이동 경로에 말이 있는 경우 이동할 수 없다.") + void canNotMoveWhenPieceExistIn() { + assertThat(WHITE_QUEEN.getPiece().canMove(Position.of(4, 4), Position.of(8, 8), + Map.of(Position.of(6, 6), WHITE_QUEEN.getPiece()))).isFalse(); + } +} 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..d0a69bb4b71 --- /dev/null +++ b/src/test/java/chess/domain/piece/RookTest.java @@ -0,0 +1,90 @@ +package chess.domain.piece; + +import static chess.domain.pixture.PieceFixture.BLACK_QUEEN; +import static chess.domain.pixture.PieceFixture.WHITE_QUEEN; +import static chess.domain.pixture.PieceFixture.WHITE_ROOK; +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.position.Position; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class RookTest { + + /* + * ...E.... + * ...E.... + * ...E..... + * ...E.... + * EEErEEEE + * ...E.... + * ...E.... + * ...E.... + * */ + @ParameterizedTest + @CsvSource({"8,4", "7,4", "6,4", "5,4", "3,4", "2,4", "1,4", + "4,1", "4,2", "4,3", "4,5", "4,6", "4,7", "4,8"}) + @DisplayName("룩은 도착 위치가 비어있는 경우 이동할 수 있다.") + void canMoveWhenTargetIsEmpty(int file, int rank) { + assertThat(WHITE_ROOK.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), Map.of())).isTrue(); + } + + /* + * ...R.... + * ...R.... + * ...R..... + * ...R.... + * RRRrRRRR + * ...R.... + * ...R.... + * ...R.... + * */ + @ParameterizedTest + @CsvSource({"8,4", "7,4", "6,4", "5,4", "3,4", "2,4", "1,4", + "4,1", "4,2", "4,3", "4,5", "4,6", "4,7", "4,8"}) + @DisplayName("룩은 도착 위치에 상대편 말이 있는 경우 이동할 수 있다.") + void canMoveWhenTargetIsOtherColor(int file, int rank) { + assertThat(WHITE_ROOK.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), + Map.of(Position.of(file, rank), BLACK_QUEEN.getPiece()))).isTrue(); + } + + /* + * ...p.... + * ...p.... + * ...p..... + * ...p.... + * ppprpppp + * ...p.... + * ...p.... + * ...p.... + * */ + @ParameterizedTest + @CsvSource({"8,4", "7,4", "6,4", "5,4", "3,4", "2,4", "1,4", + "4,1", "4,2", "4,3", "4,5", "4,6", "4,7", "4,8"}) + @DisplayName("룩은 도착 위치에 우리편 말이 있는 경우 이동할 수 없다.") + void canNotMoveWhenTargetIsSameColor(int file, int rank) { + assertThat(WHITE_ROOK.getPiece().canMove(Position.of(4, 4), Position.of(file, rank), + Map.of(Position.of(4, 4), WHITE_ROOK.getPiece(), + Position.of(file, rank), WHITE_QUEEN.getPiece()))).isFalse(); + } + + /* + * ........ + * ........ + * ......... + * ........ + * ...r...p + * ........ + * ........ + * ........ + * */ + @Test + @DisplayName("룩은 이동 경로에 말이 있는 경우 이동할 수 없다.") + void canNotMoveWhenPieceExistIn() { + assertThat(WHITE_ROOK.getPiece().canMove(Position.of(4, 4), Position.of(8, 4), + Map.of(Position.of(5, 4), new Queen(Color.WHITE)))).isFalse(); + } +} diff --git a/src/test/java/chess/domain/piece/obstaclerule/DiagonalCaptureObstacleRuleTest.java b/src/test/java/chess/domain/piece/obstaclerule/DiagonalCaptureObstacleRuleTest.java deleted file mode 100644 index 9b737d38ee0..00000000000 --- a/src/test/java/chess/domain/piece/obstaclerule/DiagonalCaptureObstacleRuleTest.java +++ /dev/null @@ -1,118 +0,0 @@ -package chess.domain.piece.obstaclerule; - -import static chess.domain.pixture.PieceFixture.BLACK_PAWN; -import static chess.domain.pixture.PieceFixture.WHITE_PAWN; -import static org.assertj.core.api.Assertions.assertThat; - -import chess.domain.piece.Color; -import chess.domain.piece.Piece; -import chess.domain.piece.PieceType; -import chess.domain.position.Position; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class DiagonalCaptureObstacleRuleTest { - - /* - * ........ - * ........ - * ........ - * ........ - * ........ - * ...P.... <- target - * ..p..... <- source - * ........ - * */ - - @Test - @DisplayName("출발 위치와 도착 위치가 대각선 이동이고 사이에 말이 존재하지 않을 경우, " - + "도착 위치의 말이 상대편 말이면 공격이 가능하므로 장애물이 아니다.") - void findObstacleWhenCapturableTargetByDiagonalMove() { - DiagonalCaptureObstacleRule diagonalCaptureObstacleRule = new DiagonalCaptureObstacleRule(); - - Position sourcePosition = Position.of(3, 2); - Position targetPosition = Position.of(4, 3); - - List obstacle = diagonalCaptureObstacleRule.findObstacle(sourcePosition, targetPosition, - Map.of(sourcePosition, new Piece(PieceType.PAWN, Color.BLACK), targetPosition, - new Piece(PieceType.PAWN, Color.WHITE))); - - assertThat(obstacle).isEmpty(); - } - - /* - * ........ - * ........ - * ........ - * ........ - * ........ <- target(Empty) - * ..P..... - * ..p..... <- source - * ........ - * */ - @Test - @DisplayName("도착 위치가 상대편 말이지만 출발 위치와 도착 위치가 직선 이동일 경우, 공격이 불가능하므로 장애물이다.") - void findObstacleWhenUnCapturableTargetByStraightMove() { - DiagonalCaptureObstacleRule diagonalCaptureObstacleRule = new DiagonalCaptureObstacleRule(); - - Position sourcePosition = Position.of(3, 2); - Position targetPosition = Position.of(3, 3); - - List obstacle = diagonalCaptureObstacleRule.findObstacle(sourcePosition, targetPosition, - Map.of(sourcePosition, WHITE_PAWN.getPiece(), targetPosition, BLACK_PAWN.getPiece())); - - assertThat(obstacle).containsExactly(targetPosition); - } - - /* - * ........ - * ........ - * ........ - * ........ - * ........ - * ..E..... <- target(Empty) - * ..P..... <- source - * ........ - * */ - @Test - @DisplayName("도착 위치가 비어있고 출발 위치와 도착 위치가 직선 이동일 경우, 이동이 가능하므로 장애물에 추가되지 않는다.") - void findObstacleWhenTargetIsEmptyByStraight() { - DiagonalCaptureObstacleRule diagonalCaptureObstacleRule = new DiagonalCaptureObstacleRule(); - - Position sourcePosition = Position.of(3, 2); - Position targetPosition = Position.of(3, 3); - - List obstacle = diagonalCaptureObstacleRule.findObstacle(sourcePosition, targetPosition, - Map.of(sourcePosition, BLACK_PAWN.getPiece(), - targetPosition, Piece.getEmptyPiece())); - - assertThat(obstacle).isEqualTo(List.of()); - } - - /* - * ........ - * ........ - * ........ - * ........ - * ........ - * ...E.... <- target(Empty) - * ..P..... <- source - * ........ - * */ - @Test - @DisplayName("도착 위치가 비어있고 출발 위치와 도착 위치가 대각 이동일 경우, 이동이 불가능하므로 장애물에 추가된다.") - void findObstacleWhenTargetIsEmptyByDiagonal() { - DiagonalCaptureObstacleRule diagonalCaptureObstacleRule = new DiagonalCaptureObstacleRule(); - - Position sourcePosition = Position.of(3, 2); - Position targetPosition = Position.of(4, 3); - - List obstacle = diagonalCaptureObstacleRule.findObstacle(sourcePosition, targetPosition, - Map.of(sourcePosition, BLACK_PAWN.getPiece(), - targetPosition, Piece.getEmptyPiece())); - - assertThat(obstacle).containsExactly(targetPosition); - } -} diff --git a/src/test/java/chess/domain/piece/obstaclerule/NoObstacleRuleTest.java b/src/test/java/chess/domain/piece/obstaclerule/NoObstacleRuleTest.java deleted file mode 100644 index 65defe791c9..00000000000 --- a/src/test/java/chess/domain/piece/obstaclerule/NoObstacleRuleTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package chess.domain.piece.obstaclerule; - -import static org.assertj.core.api.Assertions.assertThat; - -import chess.domain.piece.Color; -import chess.domain.piece.Piece; -import chess.domain.piece.PieceType; -import chess.domain.position.Position; -import java.util.Map; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class NoObstacleRuleTest { - - @Test - @DisplayName("장애물 조건이 없는 경우, 장애물 조회 시 빈 리스트를 반환한다.") - void findObstacle() { - NoObstacleRule noObstacleRule = new NoObstacleRule(); - Position sourcePosition = Position.of(3, 2); - Position targetPosition = Position.of(3, 4); - Position betweenPosition = Position.of(3, 3); - - assertThat(noObstacleRule.findObstacle(sourcePosition, targetPosition, - Map.of(betweenPosition, new Piece(PieceType.PAWN, Color.BLACK)))).isEmpty(); - } -} diff --git a/src/test/java/chess/domain/piece/obstaclerule/StraightCaptureObstacleRuleTest.java b/src/test/java/chess/domain/piece/obstaclerule/StraightCaptureObstacleRuleTest.java deleted file mode 100644 index 56869df8119..00000000000 --- a/src/test/java/chess/domain/piece/obstaclerule/StraightCaptureObstacleRuleTest.java +++ /dev/null @@ -1,120 +0,0 @@ -package chess.domain.piece.obstaclerule; - -import static org.assertj.core.api.Assertions.assertThat; - -import chess.domain.piece.Color; -import chess.domain.piece.Piece; -import chess.domain.piece.PieceType; -import chess.domain.position.Position; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -class StraightCaptureObstacleRuleTest { - - /* - * ........ - * ........ - * ........ - * ........ - * ........ <- target - * ..p..... - * ..p..... <- source - * ........ - * */ - @ParameterizedTest - @CsvSource({"BLACK, WHITE"}) - @DisplayName("출발 위치와 도착 위치가 직선 이동이고 사이에 말이 존재할 경우, 팀에 상관없이 그 말은 장애물이다.") - void findObstacleBetweenSourceAndTarget(final Color color) { - StraightCaptureObstacleRule straightCaptureObstacleRule = new StraightCaptureObstacleRule(); - - Position sourcePosition = Position.of(3, 2); - Position targetPosition = Position.of(3, 4); - Position obstaclePosition = Position.of(3, 3); - - List obstacle = straightCaptureObstacleRule.findObstacle(sourcePosition, targetPosition, - Map.of(sourcePosition, new Piece(PieceType.PAWN, Color.BLACK), - targetPosition, Piece.getEmptyPiece(), - obstaclePosition, new Piece(PieceType.PAWN, color))); - - assertThat(obstacle).containsExactly(obstaclePosition); - } - - /* - * ........ - * ........ - * ........ - * ........ - * ..P..... <- target - * ........ - * ..p..... <- source - * ........ - * */ - @Test - @DisplayName("출발 위치와 도착 위치가 직선 이동이고 사이에 말이 존재하지 않을 경우, 도착 위치의 말이 상대편 말이면 공격이 가능하므로 장애물이 아니다.") - void findObstacleWhenTargetIsOtherSide() { - StraightCaptureObstacleRule straightCaptureObstacleRule = new StraightCaptureObstacleRule(); - - Position sourcePosition = Position.of(3, 2); - Position targetPosition = Position.of(3, 4); - - List obstacle = straightCaptureObstacleRule.findObstacle(sourcePosition, targetPosition, - Map.of(sourcePosition, new Piece(PieceType.PAWN, Color.BLACK), - targetPosition, new Piece(PieceType.PAWN, Color.WHITE))); - - assertThat(obstacle).isEqualTo(List.of()); - } - - /* - * ........ - * ........ - * ........ - * ........ - * ........ <- target - * ........ - * ..p..... <- source - * ........ - * */ - @Test - @DisplayName("출발 위치와 도착 위치가 직선 이동이고 사이에 말이 존재하지 않을 경우, 도착 위치의 말이 비어있다면 말이면 공격이 가능하므로 장애물이 아니다.") - void findObstacleWhenTargetIsEmpty() { - StraightCaptureObstacleRule straightCaptureObstacleRule = new StraightCaptureObstacleRule(); - - Position sourcePosition = Position.of(3, 2); - Position targetPosition = Position.of(3, 4); - - List obstacle = straightCaptureObstacleRule.findObstacle(sourcePosition, targetPosition, - Map.of(sourcePosition, new Piece(PieceType.PAWN, Color.BLACK), - targetPosition, Piece.getEmptyPiece())); - - assertThat(obstacle).isEqualTo(List.of()); - } - - /* - * ........ - * ........ - * ........ - * ........ - * ..p..... <- target - * ........ - * ..p..... <- source - * ........ - * */ - @Test - @DisplayName("출발 위치와 도착 위치가 직선 이동이고 사이에 말이 존재하지 않을 경우, 도착 위치의 말이 우리편 말이면 장애물이다.") - void findObstacleWhenTargetIsOurSide() { - StraightCaptureObstacleRule straightCaptureObstacleRule = new StraightCaptureObstacleRule(); - - Position sourcePosition = Position.of(3, 2); - Position targetPosition = Position.of(3, 4); - - List obstacle = straightCaptureObstacleRule.findObstacle(sourcePosition, targetPosition, - Map.of(sourcePosition, new Piece(PieceType.PAWN, Color.BLACK), - targetPosition, new Piece(PieceType.PAWN, Color.BLACK))); - - assertThat(obstacle).containsExactly(targetPosition); - } -} diff --git a/src/test/java/chess/domain/pixture/PieceFixture.java b/src/test/java/chess/domain/pixture/PieceFixture.java index a0e90796e46..084cbebebb9 100644 --- a/src/test/java/chess/domain/pixture/PieceFixture.java +++ b/src/test/java/chess/domain/pixture/PieceFixture.java @@ -1,27 +1,32 @@ package chess.domain.pixture; +import chess.domain.piece.Bishop; import chess.domain.piece.Color; +import chess.domain.piece.King; +import chess.domain.piece.Knight; +import chess.domain.piece.Pawn; import chess.domain.piece.Piece; -import chess.domain.piece.PieceType; +import chess.domain.piece.Queen; +import chess.domain.piece.Rook; public enum PieceFixture { - BLACK_PAWN(new Piece(PieceType.PAWN, Color.BLACK)), - WHITE_PAWN(new Piece(PieceType.PAWN, Color.WHITE)), + BLACK_PAWN(new Pawn(Color.BLACK)), + WHITE_PAWN(new Pawn(Color.WHITE)), - BLACK_KNIGHT(new Piece(PieceType.KNIGHT, Color.BLACK)), - WHITE_KNIGHT(new Piece(PieceType.KNIGHT, Color.WHITE)), + BLACK_KNIGHT(new Knight(Color.BLACK)), + WHITE_KNIGHT(new Knight(Color.WHITE)), - BLACK_QUEEN(new Piece(PieceType.QUEEN, Color.BLACK)), - WHITE_QUEEN(new Piece(PieceType.QUEEN, Color.WHITE)), + BLACK_QUEEN(new Queen(Color.BLACK)), + WHITE_QUEEN(new Queen(Color.WHITE)), - BLACK_BISHOP(new Piece(PieceType.BISHOP, Color.BLACK)), - WHITE_BISHOP(new Piece(PieceType.BISHOP, Color.WHITE)), + BLACK_BISHOP(new Bishop(Color.BLACK)), + WHITE_BISHOP(new Bishop(Color.WHITE)), - BLACK_ROOK(new Piece(PieceType.ROOK, Color.BLACK)), - WHITE_ROOK(new Piece(PieceType.ROOK, Color.WHITE)), + BLACK_ROOK(new Rook(Color.BLACK)), + WHITE_ROOK(new Rook(Color.WHITE)), - BLACK_KING(new Piece(PieceType.KING, Color.BLACK)), - WHITE_KING(new Piece(PieceType.KING, Color.WHITE)), + BLACK_KING(new King(Color.BLACK)), + WHITE_KING(new King(Color.WHITE)), ; private final Piece piece; diff --git a/src/test/java/chess/domain/position/PositionTest.java b/src/test/java/chess/domain/position/PositionTest.java index 5016ad5b258..d0a53d54ad5 100644 --- a/src/test/java/chess/domain/position/PositionTest.java +++ b/src/test/java/chess/domain/position/PositionTest.java @@ -126,4 +126,16 @@ void canMoveOneSpace(final int file, final int rank) { void canNotMoveOneSpace(final int file, final int rank) { assertThat(Position.of(2, 2).isDiagonalBy(Position.of(file, rank))).isFalse(); } + + @Test + @DisplayName("file이 동일한 경우 참을 반환한다.") + void isRankMove() { + assertThat(Position.of(1, 2).isRankMove(Position.of(1, 3))).isTrue(); + } + + @Test + @DisplayName("file이 동일한 경우 거짓을 반환한다.") + void isNotRankMove() { + assertThat(Position.of(1, 2).isRankMove(Position.of(2, 2))).isFalse(); + } } diff --git a/src/test/java/chess/domain/scorerule/NoScoreRuleTest.java b/src/test/java/chess/domain/scorerule/NoScoreRuleTest.java new file mode 100644 index 00000000000..01821e6e43c --- /dev/null +++ b/src/test/java/chess/domain/scorerule/NoScoreRuleTest.java @@ -0,0 +1,22 @@ +package chess.domain.scorerule; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import chess.domain.position.Position; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class NoScoreRuleTest { + + @Test + @DisplayName("점수가 없는 경우는 0을 반환한다.") + void findScore() { + NoScoreRule noScoreRule = new NoScoreRule(); + assertAll( + () -> assertThat(noScoreRule.findScore(List.of())).isEqualTo(0), + () -> assertThat(noScoreRule.findScore(List.of(Position.of(1, 3)))).isEqualTo(0) + ); + } +} diff --git a/src/test/java/chess/domain/scorerule/NormalScoreRuleTest.java b/src/test/java/chess/domain/scorerule/NormalScoreRuleTest.java new file mode 100644 index 00000000000..ace6f63d2fa --- /dev/null +++ b/src/test/java/chess/domain/scorerule/NormalScoreRuleTest.java @@ -0,0 +1,20 @@ +package chess.domain.scorerule; + +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.position.Position; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class NormalScoreRuleTest { + + @Test + @DisplayName("기본 규칙은 기본 점수 * 말의 개수이다.") + void findScore() { + NormalScoreRule normalScoreRule = new NormalScoreRule(3); + assertThat(normalScoreRule.findScore(List.of( + Position.of(1, 2), + Position.of(1, 3)))).isEqualTo(6); + } +} diff --git a/src/test/java/chess/domain/scorerule/PawnScoreRuleTest.java b/src/test/java/chess/domain/scorerule/PawnScoreRuleTest.java new file mode 100644 index 00000000000..d16c0b23b2e --- /dev/null +++ b/src/test/java/chess/domain/scorerule/PawnScoreRuleTest.java @@ -0,0 +1,31 @@ +package chess.domain.scorerule; + +import static org.assertj.core.api.Assertions.assertThat; + +import chess.domain.position.Position; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PawnScoreRuleTest { + + @Test + @DisplayName("같은 세로 줄에 같은 색의 폰이 여러개 있는 경우, 각각 0.5점") + void findScoreWithSameFilePositions() { + PawnScoreRule pawnScoreRule = new PawnScoreRule(); + assertThat(pawnScoreRule.findScore(List.of( + Position.of(1, 2), + Position.of(1, 3) + ))).isEqualTo(1); + } + + @Test + @DisplayName("같은 세로 줄에 같은 색의 폰이 하나만 있는 경우, 1점(기본점수)") + void findScore() { + PawnScoreRule pawnScoreRule = new PawnScoreRule(); + assertThat(pawnScoreRule.findScore(List.of( + Position.of(1, 2), + Position.of(2, 3) + ))).isEqualTo(2); + } +} diff --git a/src/test/java/chess/domain/scorerule/RefereeTest.java b/src/test/java/chess/domain/scorerule/RefereeTest.java new file mode 100644 index 00000000000..ef7fa069a95 --- /dev/null +++ b/src/test/java/chess/domain/scorerule/RefereeTest.java @@ -0,0 +1,44 @@ +package chess.domain.scorerule; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import chess.domain.piece.Bishop; +import chess.domain.piece.Color; +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.position.Position; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RefereeTest { + + @Test + @DisplayName("팀별 피스 목록을 점수 규칙에 맞게 점수를 계산한다.") + void calculateScore() { + Map piecePosition = new HashMap<>(); + Map whiteTeam = Map.of( + Position.of(4, 2), new Queen(Color.WHITE), // 9 + Position.of(1, 2), new Pawn(Color.WHITE), // 0.5 + Position.of(1, 3), new Pawn(Color.WHITE), // 0.5 + Position.of(2, 2), new Pawn(Color.WHITE)); // 1 + piecePosition.putAll(whiteTeam); + + Map blackTeam = Map.of( + Position.of(1, 8), new Queen(Color.BLACK), // 9 + Position.of(2, 4), new King(Color.BLACK), // 0 + Position.of(3, 4), new Knight(Color.BLACK), // 2.5 + Position.of(4, 4), new Bishop(Color.BLACK)); // 3 + piecePosition.putAll(blackTeam); + + Referee referee = new Referee(piecePosition); + assertAll( + () -> assertThat(referee.calculateScore(Color.WHITE)).isEqualTo(11), + () -> assertThat(referee.calculateScore(Color.BLACK)).isEqualTo(14.5)); + } +} diff --git a/src/test/java/chess/repository/DatabaseConfigTest.java b/src/test/java/chess/repository/DatabaseConfigTest.java new file mode 100644 index 00000000000..f80a44115a1 --- /dev/null +++ b/src/test/java/chess/repository/DatabaseConfigTest.java @@ -0,0 +1,19 @@ +package chess.repository; + +import static chess.repository.DatabaseConfig.getConnection; +import static org.assertj.core.api.Assertions.assertThat; + +import java.sql.SQLException; +import org.junit.jupiter.api.Test; + +class DatabaseConfigTest { + + @Test + void connection() { + try (final var connection = getConnection()) { + assertThat(connection).isNotNull(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/chess/repository/piece/FakePieceRepository.java b/src/test/java/chess/repository/piece/FakePieceRepository.java new file mode 100644 index 00000000000..30a59dbbda5 --- /dev/null +++ b/src/test/java/chess/repository/piece/FakePieceRepository.java @@ -0,0 +1,30 @@ +package chess.repository.piece; + +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import java.util.LinkedHashMap; +import java.util.Map; + +public class FakePieceRepository implements PieceRepository { + private Map pieces = new LinkedHashMap<>(); + + @Override + public void save(final Piece piece, final Position position) { + pieces.put(position, piece); + } + + @Override + public Map findAllPiece() { + return pieces; + } + + @Override + public void deleteAll() { + pieces = new LinkedHashMap<>(); + } + + @Override + public boolean existPieces() { + return !pieces.isEmpty(); + } +} diff --git a/src/test/java/chess/repository/piece/PieceRepositoryTest.java b/src/test/java/chess/repository/piece/PieceRepositoryTest.java new file mode 100644 index 00000000000..481a1717a3c --- /dev/null +++ b/src/test/java/chess/repository/piece/PieceRepositoryTest.java @@ -0,0 +1,58 @@ +package chess.repository.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; + +import chess.domain.piece.Color; +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PieceRepositoryTest { + private PieceRepository pieceRepository; + + @BeforeEach + void init() { + pieceRepository = new FakePieceRepository(); + } + + @Test + @DisplayName("Piece 객체와 Position 객체로부터 Piece 정보를 저장한다.") + void addPiece() { + assertThatCode(() -> pieceRepository.save(new Pawn(Color.WHITE), Position.of(1, 2))); + } + + @Test + @DisplayName("전체 피스와 포지션을 조회한다.") + void findAll() { + Piece pawn = new Pawn(Color.WHITE); + Position position = Position.of(1, 2); + pieceRepository.save(pawn, position); + + assertThat(pieceRepository.findAllPiece()).isEqualTo(Map.of(position, pawn)); + } + + @Test + @DisplayName("전체 데이터를 삭제한다.") + void deleteAll() { + pieceRepository.deleteAll(); + assertThat(pieceRepository.findAllPiece()).isEqualTo(Map.of()); + } + + @Test + @DisplayName("Pieces가 존재하지 않을 경우 거짓을 반환한다.") + void notExistPieces() { + assertThat(pieceRepository.existPieces()).isFalse(); + } + + @Test + @DisplayName("Pieces가 존재할 경우 참을 반환한다.") + void existPieces() { + pieceRepository.save(new Pawn(Color.WHITE), Position.of(1, 2)); + assertThat(pieceRepository.existPieces()).isTrue(); + } +} diff --git a/src/test/java/chess/repository/turn/FakeTurnRepository.java b/src/test/java/chess/repository/turn/FakeTurnRepository.java new file mode 100644 index 00000000000..9098f26891b --- /dev/null +++ b/src/test/java/chess/repository/turn/FakeTurnRepository.java @@ -0,0 +1,28 @@ +package chess.repository.turn; + +import chess.domain.piece.Color; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class FakeTurnRepository implements TurnRepository { + private List colors = new ArrayList<>(); + + @Override + public void save(final String color) { + colors.add(color); + } + + @Override + public Optional findAny() { + if (colors.isEmpty()) { + return Optional.empty(); + } + return Optional.of(Color.valueOf(colors.get(0))); + } + + @Override + public void deleteAll() { + colors = new ArrayList<>(); + } +} diff --git a/src/test/java/chess/repository/turn/TurnRepositoryTest.java b/src/test/java/chess/repository/turn/TurnRepositoryTest.java new file mode 100644 index 00000000000..99db984ea74 --- /dev/null +++ b/src/test/java/chess/repository/turn/TurnRepositoryTest.java @@ -0,0 +1,46 @@ +package chess.repository.turn; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; + +import chess.domain.piece.Color; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TurnRepositoryTest { + + private TurnRepository turnRepository; + + @BeforeEach + void init() { + turnRepository = new FakeTurnRepository(); + } + + @Test + @DisplayName("색상의 문자열 값을 저장한다.") + void addPiece() { + assertThatCode(() -> turnRepository.save(Color.BLACK.name())); + } + + @Test + @DisplayName("하나의 Turn을 조회한다.") + void findOne() { + turnRepository.save(Color.BLACK.name()); + assertThat(turnRepository.findAny()).isEqualTo(Optional.of(Color.BLACK)); + } + + @Test + @DisplayName("하나의 Turn을 조회 시 테이블이 비어있는 경우 빈 옵셔널을 반환한다.") + void notFindOne() { + assertThat(turnRepository.findAny()).isEqualTo(Optional.empty()); + } + + @Test + @DisplayName("전체 데이터를 삭제한다.") + void deleteAll() { + turnRepository.deleteAll(); + assertThat(turnRepository.findAny()).isEmpty(); + } +} diff --git a/src/test/java/chess/service/ChessGameServiceTest.java b/src/test/java/chess/service/ChessGameServiceTest.java new file mode 100644 index 00000000000..f272c78ebe7 --- /dev/null +++ b/src/test/java/chess/service/ChessGameServiceTest.java @@ -0,0 +1,83 @@ +package chess.service; + +import static chess.domain.pixture.PieceFixture.BLACK_PAWN; +import static chess.domain.pixture.PieceFixture.WHITE_PAWN; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import chess.domain.board.SavedBoardInitializer; +import chess.domain.game.Game; +import chess.domain.piece.Color; +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import chess.repository.piece.FakePieceRepository; +import chess.repository.piece.PieceRepository; +import chess.repository.turn.FakeTurnRepository; +import chess.repository.turn.TurnRepository; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ChessGameServiceTest { + private ChessGameService chessGameService; + private PieceRepository pieceRepository; + private TurnRepository turnRepository; + private Game game; + + @BeforeEach + void init() { + pieceRepository = new FakePieceRepository(); + turnRepository = new FakeTurnRepository(); + chessGameService = new ChessGameService(pieceRepository, turnRepository); + + game = new Game(new SavedBoardInitializer( + Map.of(Position.of(1, 1), BLACK_PAWN.getPiece(), Position.of(3, 1), WHITE_PAWN.getPiece()))); + } + + @Test + @DisplayName("현재 Board를 저장한다.") + void save() { + chessGameService.saveGame(game); + Map pieces = new LinkedHashMap<>(); + pieces.put(Position.of(1, 1), new Pawn(Color.BLACK)); + pieces.put(Position.of(3, 1), new Pawn(Color.WHITE)); + + assertAll( + () -> assertThat(pieceRepository.findAllPiece()).containsAllEntriesOf(pieces), + () -> assertThat(turnRepository.findAny()).isEqualTo(Optional.of(Color.BLACK))); + } + + @Test + @DisplayName("저장된 위치별 피스를 조회한다.") + void findAllPiece() { + chessGameService.saveGame(game); + assertThat(chessGameService.findAllPiece()).containsExactlyInAnyOrderEntriesOf( + Map.of(Position.of(1, 1), BLACK_PAWN.getPiece(), + Position.of(3, 1), WHITE_PAWN.getPiece())); + } + + @Test + @DisplayName("전체 내역을 삭제한다.") + void delete() { + chessGameService.delete(); + assertThat(chessGameService.findAllPiece()).isEmpty(); + } + + @Test + @DisplayName("저장된 게임이 있는지 확인한다.") + void existGame() { + chessGameService.saveGame(game); + assertThat(chessGameService.existSavedGame()).isTrue(); + } + + @Test + @DisplayName("저장된 게임이 있는지 확인한다.") + void notExistGame() { + chessGameService.delete(); + assertThat(chessGameService.existSavedGame()).isFalse(); + } +}