diff --git a/.gitignore b/.gitignore index 6c018781387..25dfdf4c336 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ out/ ### VS Code ### .vscode/ + +### docker ### +./src/main/java/chess/docker/db.mysql diff --git a/README.md b/README.md index df3c4a89958..bd2cdc19102 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,55 @@ ## Pair: 져니 [⛄️](http://github.com/cl8d), 제나 [❤️](https://github.com/yenawee) -## 1단계 피드백 사항 및 추가 수정 사항 +### 프로그램 실행방법 +./java-chess/src/main/java/chess/docker 경로에서 아래 명령어를 입력해주세요 + +``` +docker-compose -p chess up -d +``` + +정지 시에는 아래 명령어를 입력해주세요 +``` +docker-compose -p chess down +``` + +## 3, 4 단계 피드백 사항 및 추가 수정사항 +- [x] Readme 에 도커 환경 세팅 설명 추가 +- [x] DB connect 연결 에러 발생 시 처리 +- [x] 매직 넘버 상수화 및 메소드 이름 구체화 +- [ ] Command 처리 로직 수정 +- [x] 하나의 요청에 대해 하나의 Connection 으로 처리하도록 수정 +- [ ] update 쿼리 수정 + + +## ✔ 4단계 기능 요구사항 + +- [x] 애플리케이션 재시작 시 이전에 하던 체스 게임을 재시작할 수 있어야 한다 +- [x] table + - [x] Piece 정보 : piece_type, team_color, rank, file + - [x] Chessgame 정보 : turn (chessGame id 는 추후 적용 필요) + - [x] CRUD + - [x] Create + - [x] DB에서 불러 올 체스 게임이 없으면 만든다 + - [x] Read + - [x] DB에 체스 게임 정보가 저장되어 있다면 DB에서 읽어온다 + - [x] Update + - [x] 기물을 움직일 때마다 DB 업데이트 해준다 + - [x] 우선 delete 후 save 로 구현 (UPDATE 쿼리 사용으로 변경 필요) + - [x] Delete + - [x] King 이 죽으면 DB 초기화 해준다 + +## ✔ 3단계 기능 요구사항 + +- [x] King 이 잡혔을 때 게임 종료 +- [x] status 명령 입력 시 점수 출력, 어느 진영이 이겼는지 출력 + - [x] commandType 에 status 추가 +- [x] 점수 계산 + - [x] Queen : 9, Rook : 5, Bishop : 3, Knight : 2.5 + - [x] Pawn : 기본 1. 같은 세로 줄에 같은 색의 폰이 있으면 0.5 + +## 1, 2단계 피드백 사항 및 추가 수정 사항 + - [x] 출력 시 View 와 Domain 의존성 제거 - DTO 도입 - [x] 매직 넘버 상수화 - [x] 클래스 및 메소드 네이밍 의미있는 표현으로 변경 @@ -13,8 +61,7 @@ - [ ] Readme 에 네이밍 사전, 클래스 다이어그램, 프로그래밍 흐름도 정리하기 - [x] 예외 발생 시 재입력 - -## ✔ 기능 요구사항 +## ✔ 1, 2단계 기능 요구사항 ### ChessBoard @@ -30,7 +77,7 @@ - [x] 체스 게임을 진행한다. -### CampType +### TeamColor - [x] 사용자의 진영을 관리한다. - [x] 입력받은 위치의 열이 소문자면 WHITE, 대문자면 BLACK 진영으로 나눈다. diff --git a/src/main/java/chess/ChessApplication.java b/src/main/java/chess/ChessApplication.java index 468aaaf1f63..a8b66b97dd7 100644 --- a/src/main/java/chess/ChessApplication.java +++ b/src/main/java/chess/ChessApplication.java @@ -1,10 +1,11 @@ package chess; import chess.controller.ChessController; +import chess.dao.ChessBoardDao; public class ChessApplication { public static void main(String[] args) { - final ChessController chessController = new ChessController(); + final ChessController chessController = new ChessController(new ChessBoardDao()); chessController.run(); } } diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java index c3b2115e7f5..65f95bf3771 100644 --- a/src/main/java/chess/controller/ChessController.java +++ b/src/main/java/chess/controller/ChessController.java @@ -1,8 +1,10 @@ package chess.controller; -import chess.controller.status.Start; -import chess.controller.status.Status; -import chess.domain.chess.ChessGame; +import chess.controller.state.Start; +import chess.controller.state.State; +import chess.dao.ChessGameDao; +import chess.dao.ChessGameLoader; +import chess.domain.game.ChessGame; import chess.domain.piece.Piece; import chess.domain.position.Position; import chess.view.InputView; @@ -10,29 +12,33 @@ import java.util.List; import java.util.Map; -import java.util.function.Consumer; public class ChessController { + private final ChessGameDao chessGameDao; + + public ChessController(final ChessGameDao chessGameDao) { + this.chessGameDao = chessGameDao; + } public void run() { OutputView.printStartMessage(); - final ChessGame chessGame = new ChessGame(); - play(chessGame, gameStatus -> { - if (gameStatus.isRun()) { - printChessBoard(chessGame.getChessBoard()); - } - }); + ChessGame chessGame = ChessGameLoader.load(chessGameDao); + State gameStatus = new Start(chessGame); + play(chessGame, gameStatus); } - private void play(final ChessGame chessGame, Consumer consumer) { - Status gameStatus = new Start(chessGame); - while (gameStatus.isRun()) { + private void play(ChessGame chessGame, State gameStatus) { + while (!chessGame.isEnd() && gameStatus.isRun()) { gameStatus = getStatus(gameStatus); - consumer.accept(gameStatus); + chessGameDao.update(chessGame); + printChessBoard(gameStatus, chessGame.getChessBoard()); + } + if (chessGame.isEnd()) { + chessGameDao.init(chessGame); } } - private Status getStatus(Status gameStatus) { + private State getStatus(State gameStatus) { try { List commands = InputView.getCommand(); final Command command = Command.findCommand(commands); @@ -43,8 +49,10 @@ private Status getStatus(Status gameStatus) { } } - private void printChessBoard(Map board) { - ChessBoardDto chessBoardDTO = new ChessBoardDto(board); - OutputView.print(chessBoardDTO.getBoardMessage().toString()); + private void printChessBoard(State gameStatus, Map board) { + if (gameStatus.isRun()) { + ChessBoardDto chessBoardDTO = new ChessBoardDto(board); + OutputView.print(chessBoardDTO.getBoardMessage().toString()); + } } } diff --git a/src/main/java/chess/controller/Command.java b/src/main/java/chess/controller/Command.java index b7bcf89cbc7..a6fa325cdc6 100644 --- a/src/main/java/chess/controller/Command.java +++ b/src/main/java/chess/controller/Command.java @@ -1,6 +1,6 @@ package chess.controller; -import chess.controller.status.CommandType; +import chess.controller.state.CommandType; import java.util.Arrays; import java.util.List; @@ -9,7 +9,7 @@ public final class Command { public static final int COMMAND_INDEX = 0; public static final int MOVE_COMMAND_SIZE = 3; private static final String COMMAND_ERROR_MESSAGE = "잘못된 명령어 입력입니다."; - + private final CommandType type; private final List commands; @@ -38,6 +38,10 @@ public boolean isEnd() { return type == CommandType.END; } + public boolean isStatus() { + return type == CommandType.STATUS; + } + public boolean isCorrectWhenMove() { return commands.size() == MOVE_COMMAND_SIZE; } diff --git a/src/main/java/chess/controller/ScoreDto.java b/src/main/java/chess/controller/ScoreDto.java new file mode 100644 index 00000000000..d6b7b84b0c7 --- /dev/null +++ b/src/main/java/chess/controller/ScoreDto.java @@ -0,0 +1,22 @@ +package chess.controller; + +import chess.domain.piece.TeamColor; +import chess.domain.result.Score; + +public class ScoreDto { + private final Double whiteScore; + private final Double blackScore; + + public ScoreDto(Score score) { + whiteScore = score.getScore(TeamColor.WHITE); + blackScore = score.getScore(TeamColor.BLACK); + } + + public Double getWhiteScore() { + return whiteScore; + } + + public Double getBlackScore() { + return blackScore; + } +} diff --git a/src/main/java/chess/controller/state/CommandType.java b/src/main/java/chess/controller/state/CommandType.java new file mode 100644 index 00000000000..887643a3d96 --- /dev/null +++ b/src/main/java/chess/controller/state/CommandType.java @@ -0,0 +1,5 @@ +package chess.controller.state; + +public enum CommandType { + START, MOVE, END, STATUS +} diff --git a/src/main/java/chess/controller/state/End.java b/src/main/java/chess/controller/state/End.java new file mode 100644 index 00000000000..c00312dd7e1 --- /dev/null +++ b/src/main/java/chess/controller/state/End.java @@ -0,0 +1,30 @@ +package chess.controller.state; + +import chess.controller.Command; +import chess.controller.ScoreDto; +import chess.domain.game.ChessGame; +import chess.domain.piece.TeamColor; +import chess.domain.result.Score; +import chess.view.OutputView; + +public final class End implements State { + @Override + public State checkCommand(final Command command) { + throw new IllegalArgumentException("게임이 끝났습니다."); + } + + @Override + public boolean isRun() { + return false; + } + + public State run(ChessGame chessGame) { + Score score = Score.calculate(chessGame.getChessBoard()); + OutputView.printStatus(new ScoreDto(score)); + if (chessGame.isEnd()) { + TeamColor winner = chessGame.getCurrentTeamColor(); + OutputView.print(winner.name()); + } + return new End(); + } +} diff --git a/src/main/java/chess/controller/status/Move.java b/src/main/java/chess/controller/state/Move.java similarity index 76% rename from src/main/java/chess/controller/status/Move.java rename to src/main/java/chess/controller/state/Move.java index 4ba2903c429..c78f60f25ca 100644 --- a/src/main/java/chess/controller/status/Move.java +++ b/src/main/java/chess/controller/state/Move.java @@ -1,14 +1,14 @@ -package chess.controller.status; +package chess.controller.state; import chess.controller.Command; -import chess.domain.chess.ChessGame; +import chess.domain.game.ChessGame; import chess.domain.piece.TeamColor; import chess.domain.position.Position; import chess.domain.position.PositionConverter; import java.util.List; -public final class Move implements Status { +public final class Move implements State { public static final int SOURCE_INDEX = 1; public static final int TARGET_INDEX = 2; @@ -21,22 +21,28 @@ public Move(final ChessGame chessGame, final TeamColor teamColor) { } @Override - public Status checkCommand(final Command command) { + public State checkCommand(final Command command) { if (command.isStart()) { throw new IllegalArgumentException("이미 시작이 완료되었습니다."); } if (command.isEnd()) { - return new End(); + return new End().run(chessGame); + } + if (command.isStatus()) { + return new Status(chessGame, teamColor).run(); } validateCommand(command); return move(command); } - private Status move(final Command command) { + private State move(final Command command) { final List commands = command.getCommands(); final Position source = PositionConverter.convert(commands.get(SOURCE_INDEX)); final Position target = PositionConverter.convert(commands.get(TARGET_INDEX)); chessGame.setUp(source, target, teamColor); + if (chessGame.isEnd()) { + return new End().run(chessGame); + } return new Move(chessGame, teamColor.changeTurn()); } diff --git a/src/main/java/chess/controller/state/Start.java b/src/main/java/chess/controller/state/Start.java new file mode 100644 index 00000000000..2ef14a10b81 --- /dev/null +++ b/src/main/java/chess/controller/state/Start.java @@ -0,0 +1,29 @@ +package chess.controller.state; + +import chess.controller.Command; +import chess.domain.game.ChessGame; + +public final class Start implements State { + + private final ChessGame chessGame; + + public Start(final ChessGame chessGame) { + this.chessGame = chessGame; + } + + @Override + public State checkCommand(final Command command) { + if (command.isStart()) { + return new Move(chessGame, chessGame.getCurrentTeamColor()); + } + if (command.isEnd()) { + return new End(); + } + throw new IllegalArgumentException("게임이 시작되지 않았습니다."); + } + + @Override + public boolean isRun() { + return true; + } +} diff --git a/src/main/java/chess/controller/state/State.java b/src/main/java/chess/controller/state/State.java new file mode 100644 index 00000000000..dc7945bb564 --- /dev/null +++ b/src/main/java/chess/controller/state/State.java @@ -0,0 +1,9 @@ +package chess.controller.state; + +import chess.controller.Command; + +public interface State { + State checkCommand(final Command command); + + boolean isRun(); +} diff --git a/src/main/java/chess/controller/state/Status.java b/src/main/java/chess/controller/state/Status.java new file mode 100644 index 00000000000..767703fc38f --- /dev/null +++ b/src/main/java/chess/controller/state/Status.java @@ -0,0 +1,44 @@ +package chess.controller.state; + +import chess.controller.Command; +import chess.controller.ScoreDto; +import chess.domain.game.ChessGame; +import chess.domain.piece.TeamColor; +import chess.domain.result.Score; +import chess.view.OutputView; + +public class Status implements State { + private final ChessGame chessGame; + private final TeamColor teamColor; + + public Status(ChessGame chessGame, TeamColor teamColor) { + this.chessGame = chessGame; + this.teamColor = teamColor; + } + + State run() { + Score score = Score.calculate(chessGame.getChessBoard()); + OutputView.printStatus(new ScoreDto(score)); + return new Status(chessGame, teamColor); + } + + @Override + public State checkCommand(Command command) { + if (command.isEnd()) { + return new End().run(chessGame); + } + if (command.isMove()) { + Move move = new Move(chessGame, teamColor); + return move.checkCommand(command); + } + if (command.isStatus()) { + return run(); + } + throw new UnsupportedOperationException("end, move 명령어만 입력 가능합니다."); + } + + @Override + public boolean isRun() { + return true; + } +} diff --git a/src/main/java/chess/controller/status/CommandType.java b/src/main/java/chess/controller/status/CommandType.java deleted file mode 100644 index 20749ace7ae..00000000000 --- a/src/main/java/chess/controller/status/CommandType.java +++ /dev/null @@ -1,5 +0,0 @@ -package chess.controller.status; - -public enum CommandType { - START, MOVE, END -} diff --git a/src/main/java/chess/controller/status/End.java b/src/main/java/chess/controller/status/End.java deleted file mode 100644 index 2a0ba6f7945..00000000000 --- a/src/main/java/chess/controller/status/End.java +++ /dev/null @@ -1,15 +0,0 @@ -package chess.controller.status; - -import chess.controller.Command; - -public final class End implements Status { - @Override - public Status checkCommand(final Command command) { - throw new IllegalArgumentException("게임이 끝났습니다."); - } - - @Override - public boolean isRun() { - return false; - } -} diff --git a/src/main/java/chess/controller/status/Start.java b/src/main/java/chess/controller/status/Start.java deleted file mode 100644 index 635af752d87..00000000000 --- a/src/main/java/chess/controller/status/Start.java +++ /dev/null @@ -1,32 +0,0 @@ -package chess.controller.status; - -import chess.controller.Command; -import chess.domain.chess.ChessGame; -import chess.domain.piece.TeamColor; - -public final class Start implements Status { - - private final ChessGame chessGame; - - public Start(final ChessGame chessGame) { - this.chessGame = chessGame; - } - - @Override - public Status checkCommand(final Command command) { - if (command.isEnd()) { - return new End(); - } - - if (command.isMove()) { - throw new IllegalArgumentException("게임이 시작되지 않았습니다."); - } - - return new Move(chessGame, TeamColor.WHITE); - } - - @Override - public boolean isRun() { - return true; - } -} diff --git a/src/main/java/chess/controller/status/Status.java b/src/main/java/chess/controller/status/Status.java deleted file mode 100644 index 72773ffb554..00000000000 --- a/src/main/java/chess/controller/status/Status.java +++ /dev/null @@ -1,9 +0,0 @@ -package chess.controller.status; - -import chess.controller.Command; - -public interface Status { - Status checkCommand(final Command command); - - boolean isRun(); -} diff --git a/src/main/java/chess/dao/ChessBoardDao.java b/src/main/java/chess/dao/ChessBoardDao.java new file mode 100644 index 00000000000..e94640cfd25 --- /dev/null +++ b/src/main/java/chess/dao/ChessBoardDao.java @@ -0,0 +1,88 @@ +package chess.dao; + +import chess.domain.board.ChessBoard; +import chess.domain.game.ChessGame; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import chess.domain.piece.TeamColor; +import chess.domain.position.Position; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +public class ChessBoardDao implements ChessGameDao { + private static final DBConnection dbConnection = new DBConnection(); + + @Override + public void save(ChessGame chessGame) { + final var query = "INSERT INTO chess_game(piece_type, piece_rank, piece_file, team, turn) VALUES (?, ?, ?, ?, ?)"; + Map board = chessGame.getChessBoard(); + try (final var connection = dbConnection.getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + for (Map.Entry boardEntry : board.entrySet()) { + preparedStatement.setString(1, boardEntry.getValue().getPieceType().name()); + preparedStatement.setInt(2, boardEntry.getKey().getRank()); + preparedStatement.setInt(3, boardEntry.getKey().getFile()); + preparedStatement.setString(4, boardEntry.getValue().getTeamColor().name()); + preparedStatement.setString(5, chessGame.getCurrentTeamColor().name()); + preparedStatement.executeUpdate(); + } + } catch (final SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public ChessGame findChessGame() { + Map board = new HashMap<>(); + TeamColor turn = null; + final var query = "SELECT piece_type, piece_rank, piece_file, team, turn FROM chess_game"; + try (final var connection = dbConnection.getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + final var resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + PieceType pieceType = PieceType.valueOf(resultSet.getString("piece_type")); + int rank = resultSet.getInt("piece_rank"); + int file = resultSet.getInt("piece_file"); + TeamColor teamColor = TeamColor.valueOf(resultSet.getString("team")); + turn = TeamColor.valueOf(resultSet.getString("turn")); + Piece piece = PieceType.toPiece(pieceType, teamColor); + board.put(Position.of(rank, file), piece); + } + } catch (final SQLException e) { + throw new RuntimeException(e); + } + if (board.isEmpty()) { + return null; + } + return new ChessGame(new ChessBoard(board), turn.changeTurn()); + } + + @Override + public void update(ChessGame chessGame) { + delete(chessGame); + save(chessGame); + } + + public void delete(ChessGame chessGame) { + final var query = "DELETE from chess_game"; + try (final var connection = dbConnection.getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + final var resultSet = preparedStatement.executeUpdate(); + } catch (final SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public void init(ChessGame chessGame) { + final var query = "TRUNCATE TABLE chess_game"; + try (final var connection = dbConnection.getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + final var resultSet = preparedStatement.executeUpdate(); + } catch (final SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/chess/dao/ChessGameDao.java b/src/main/java/chess/dao/ChessGameDao.java new file mode 100644 index 00000000000..441a6cb1793 --- /dev/null +++ b/src/main/java/chess/dao/ChessGameDao.java @@ -0,0 +1,13 @@ +package chess.dao; + +import chess.domain.game.ChessGame; + +public interface ChessGameDao { + void save(ChessGame chessGame); + + ChessGame findChessGame(); + + void update(ChessGame chessGame); + + void init(ChessGame chessGame); +} diff --git a/src/main/java/chess/dao/ChessGameLoader.java b/src/main/java/chess/dao/ChessGameLoader.java new file mode 100644 index 00000000000..5d4eaa69fbe --- /dev/null +++ b/src/main/java/chess/dao/ChessGameLoader.java @@ -0,0 +1,14 @@ +package chess.dao; + +import chess.domain.game.ChessGame; + +public class ChessGameLoader { + public static ChessGame load(ChessGameDao chessGameDao) { + ChessGame chessGame = chessGameDao.findChessGame(); + if (chessGame == null) { + chessGame = new ChessGame(); + chessGameDao.save(chessGame); + } + return chessGame; + } +} diff --git a/src/main/java/chess/dao/DBConnection.java b/src/main/java/chess/dao/DBConnection.java new file mode 100644 index 00000000000..9bed3c9cc37 --- /dev/null +++ b/src/main/java/chess/dao/DBConnection.java @@ -0,0 +1,23 @@ +package chess.dao; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class DBConnection { + private static final String SERVER = "localhost:13306"; // MySQL 서버 주소 + private static final String DATABASE = "chess"; // MySQL DATABASE 이름 + private static final String OPTION = "?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true"; + private static final String USERNAME = "root"; // MySQL 서버 아이디 + private static final String PASSWORD = "root"; // MySQL 서버 비밀번호 + + public Connection getConnection() { + // 드라이버 연결 + try { + return DriverManager.getConnection("jdbc:mysql://" + SERVER + "/" + DATABASE + OPTION, USERNAME, PASSWORD); + } catch (final SQLException e) { + throw new DBConnectionException("DB 연결 오류"); + } + } + +} diff --git a/src/main/java/chess/dao/DBConnectionException.java b/src/main/java/chess/dao/DBConnectionException.java new file mode 100644 index 00000000000..51befa9521c --- /dev/null +++ b/src/main/java/chess/dao/DBConnectionException.java @@ -0,0 +1,7 @@ +package chess.dao; + +public class DBConnectionException extends RuntimeException { + public DBConnectionException(String message) { + super(message); + } +} diff --git a/src/main/java/chess/dao/InMemoryChessGameDao.java b/src/main/java/chess/dao/InMemoryChessGameDao.java new file mode 100644 index 00000000000..021394f0278 --- /dev/null +++ b/src/main/java/chess/dao/InMemoryChessGameDao.java @@ -0,0 +1,28 @@ +package chess.dao; + +import chess.domain.game.ChessGame; + +public class InMemoryChessGameDao implements ChessGameDao { + private ChessGame chessGame; + + @Override + public void save(ChessGame chessGame) { + this.chessGame = chessGame; + + } + + @Override + public ChessGame findChessGame() { + return chessGame; + } + + @Override + public void update(ChessGame chessGame) { + this.chessGame = chessGame; + } + + @Override + public void init(ChessGame chessGame) { + this.chessGame = null; + } +} diff --git a/src/main/java/chess/docker/db/mysql/init/init.sql b/src/main/java/chess/docker/db/mysql/init/init.sql new file mode 100644 index 00000000000..a2f8a60b035 --- /dev/null +++ b/src/main/java/chess/docker/db/mysql/init/init.sql @@ -0,0 +1,7 @@ +CREATE TABLE chess_game ( + piece_type VARCHAR(255) NOT NULL, + piece_rank TINYINT(10) NOT NULL, + piece_file VARCHAR(255) NOT NULL, + team VARCHAR(255) NOT NULL, + turn VARCHAR(255) NOT NULL +) diff --git a/src/main/java/chess/docker/docker-compose.yml b/src/main/java/chess/docker/docker-compose.yml new file mode 100644 index 00000000000..f9fa3218791 --- /dev/null +++ b/src/main/java/chess/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/src/main/java/chess/domain/board/ChessBoard.java b/src/main/java/chess/domain/board/ChessBoard.java index d10aa36a630..47b7336b703 100644 --- a/src/main/java/chess/domain/board/ChessBoard.java +++ b/src/main/java/chess/domain/board/ChessBoard.java @@ -1,22 +1,30 @@ package chess.domain.board; -import chess.domain.chess.ChessGame; +import chess.domain.game.ChessGame; import chess.domain.piece.Piece; import chess.domain.piece.TeamColor; import chess.domain.position.Position; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public final class ChessBoard { + public static final int INITIAL_KING_COUNT = 2; + private static final Map CACHE = new ConcurrentHashMap<>(); + private final Map board; - private ChessBoard(final Map board) { + public ChessBoard(final Map board) { this.board = board; } public static ChessBoard getInstance(final ChessGame chessGame) { - final Map board = ChessBoardFactory.getInstance(chessGame).createBoard(); - return new ChessBoard(board); + if (CACHE.containsKey(chessGame)) { + return CACHE.get(chessGame); + } + final ChessBoard chessBoard = new ChessBoard(new ChessBoardFactory().createBoard()); + CACHE.put(chessGame, chessBoard); + return chessBoard; } public boolean contains(final Position position) { @@ -58,8 +66,14 @@ private boolean isObstructed(final Position target, final Position unitPosition, return isObstructed(target, unitPosition, currentPosition); } - public Map getBoard() { return Map.copyOf(board); } + + public boolean checkKingDie() { + final double kingCount = board.values().stream() + .filter(Piece::isKing) + .count(); + return kingCount != INITIAL_KING_COUNT; + } } diff --git a/src/main/java/chess/domain/board/ChessBoardFactory.java b/src/main/java/chess/domain/board/ChessBoardFactory.java index 8526f30c60d..e3b1eee88a4 100644 --- a/src/main/java/chess/domain/board/ChessBoardFactory.java +++ b/src/main/java/chess/domain/board/ChessBoardFactory.java @@ -1,31 +1,14 @@ package chess.domain.board; -import chess.domain.chess.ChessGame; import chess.domain.piece.*; import chess.domain.position.Position; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import static chess.domain.piece.PieceType.*; public final class ChessBoardFactory { - - private static final Map CACHE = new ConcurrentHashMap<>(); - - private ChessBoardFactory() { - } - - static ChessBoardFactory getInstance(final ChessGame chessGame) { - if (CACHE.containsKey(chessGame)) { - return CACHE.get(chessGame); - } - final ChessBoardFactory chessBoardFactory = new ChessBoardFactory(); - CACHE.put(chessGame, chessBoardFactory); - return chessBoardFactory; - } - public Map createBoard() { final Map board = new HashMap<>(); createWhiteArea(board); diff --git a/src/main/java/chess/domain/chess/ChessGame.java b/src/main/java/chess/domain/game/ChessGame.java similarity index 76% rename from src/main/java/chess/domain/chess/ChessGame.java rename to src/main/java/chess/domain/game/ChessGame.java index 06315b978a1..b9da70dd34e 100644 --- a/src/main/java/chess/domain/chess/ChessGame.java +++ b/src/main/java/chess/domain/game/ChessGame.java @@ -1,4 +1,4 @@ -package chess.domain.chess; +package chess.domain.game; import chess.domain.board.ChessBoard; import chess.domain.piece.Piece; @@ -13,6 +13,12 @@ public final class ChessGame { public ChessGame() { this.chessBoard = ChessBoard.getInstance(this); + this.currentTeamColor = TeamColor.WHITE; + } + + public ChessGame(final ChessBoard chessBoard, final TeamColor turn) { + this.chessBoard = chessBoard; + this.currentTeamColor = turn; } public void setUp(final Position source, final Position target, final TeamColor teamColor) { @@ -25,7 +31,7 @@ public void play(final Position source, final Position target) { if (piece == null) { throw new IllegalArgumentException("기물이 존재하는 위치를 입력해주세요."); } - validateCamp(piece); + validateTurn(piece); if (!piece.canMove(source, target, chessBoard.getPiece(target)) || !chessBoard.isPossibleRoute(source, target, currentTeamColor)) { throw new IllegalArgumentException("기물 규칙 상 움직일 수 없는 위치입니다."); @@ -33,7 +39,7 @@ public void play(final Position source, final Position target) { movePiece(source, target, piece); } - private void validateCamp(final Piece piece) { + private void validateTurn(final Piece piece) { if (!piece.isSameTeam(currentTeamColor)) { throw new IllegalArgumentException("현재 차례가 아닙니다."); } @@ -47,4 +53,12 @@ private void movePiece(final Position source, final Position target, final Piece public Map getChessBoard() { return chessBoard.getBoard(); } + + public boolean isEnd() { + return chessBoard.checkKingDie(); + } + + public TeamColor getCurrentTeamColor() { + return currentTeamColor; + } } diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java index df7a69363bb..d14a5c765d2 100644 --- a/src/main/java/chess/domain/piece/Bishop.java +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -5,6 +5,7 @@ public class Bishop extends Piece { private static final int BISHOP_MAX_MOVE_COUNT = 8; + private static final Double SCORE_VALUE = 3.0; public Bishop(final PieceType pieceType, final TeamColor teamColor) { super(pieceType, teamColor); @@ -16,4 +17,9 @@ public boolean canMove(final Position source, final Position target, final Piece PossibleDestinations.of(source, Direction.getDiagonalDirections(), BISHOP_MAX_MOVE_COUNT); return allPositions.contains(target); } + + @Override + public Double getScoreValue() { + return SCORE_VALUE; + } } diff --git a/src/main/java/chess/domain/piece/Direction.java b/src/main/java/chess/domain/piece/Direction.java index 8814219d31c..55c5b056fde 100644 --- a/src/main/java/chess/domain/piece/Direction.java +++ b/src/main/java/chess/domain/piece/Direction.java @@ -34,6 +34,14 @@ public static List getDiagonalDirections() { return List.of(UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT); } + public static boolean isUpsideDiagonal(final int file, final int rank) { + return (file == UP_LEFT.fileMove || file == UP_RIGHT.fileMove) && rank == UP.rankMove; + } + + public static boolean isDownsideDiagonal(final int file, final int rank) { + return (file == DOWN_LEFT.fileMove || file == DOWN_RIGHT.fileMove) && rank == DOWN.rankMove; + } + public Position calculate(final Position before) { return before.calculate(this.rankMove, this.fileMove); } diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java index eceb180f8f1..3d42cf0f7a4 100644 --- a/src/main/java/chess/domain/piece/King.java +++ b/src/main/java/chess/domain/piece/King.java @@ -5,6 +5,7 @@ public class King extends Piece { private static final int KING_MAX_MOVE_COUNT = 1; + private static final Double SCORE_VALUE = 0.0; public King(final PieceType pieceType, final TeamColor teamColor) { super(pieceType, teamColor); @@ -16,4 +17,14 @@ public boolean canMove(final Position source, final Position target, final Piece PossibleDestinations.of(source, Direction.getAllDirections(), KING_MAX_MOVE_COUNT); return allPositions.contains(target); } + + @Override + public boolean isKing() { + return true; + } + + @Override + public Double getScoreValue() { + return SCORE_VALUE; + } } diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java index d6d21eddb9c..4f53d35d502 100644 --- a/src/main/java/chess/domain/piece/Knight.java +++ b/src/main/java/chess/domain/piece/Knight.java @@ -3,6 +3,8 @@ import chess.domain.position.Position; public class Knight extends Piece { + private static final Double SCORE_VALUE = 2.5; + public Knight(final PieceType pieceType, final TeamColor teamColor) { super(pieceType, teamColor); } @@ -13,4 +15,9 @@ public boolean canMove(final Position source, final Position target, final Piece final int fileGap = target.getFile() - source.getFile(); return Math.abs(rankGap * fileGap) == 2; } + + @Override + public Double getScoreValue() { + return SCORE_VALUE; + } } diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java index f8dec186878..69617be264f 100644 --- a/src/main/java/chess/domain/piece/Pawn.java +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -2,13 +2,40 @@ import chess.domain.position.Position; -public abstract class Pawn extends Piece { +public class Pawn extends Piece { protected static final int PAWN_MAX_MOVE_COUNT = 1; protected static final int PAWN_FIRST_MAX_MOVE_COUNT = 2; + private static final Double SCORE_VALUE = 1.0; + private static final Double SCORE_VALUE_SAME_FILE = 0.5; + private static final int PAWN_COUNT_CRITERION_FOR_SCORING = 1; Pawn(final PieceType pieceType, final TeamColor teamColor) { super(pieceType, teamColor); } - abstract boolean canAttack(Position source, Position target, Piece piece); + public static Double calculateScore(int count) { + if (count == PAWN_COUNT_CRITERION_FOR_SCORING) { + return SCORE_VALUE; + } + return SCORE_VALUE_SAME_FILE * count; + } + + boolean canAttack(Position source, Position target, Piece piece) { + return false; + } + + @Override + public boolean isPawn() { + return true; + } + + @Override + public Double getScoreValue() { + return null; + } + + @Override + public boolean canMove(Position source, Position target, Piece targetPiece) { + return false; + } } diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index b7496aa462e..86a4af08632 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -11,10 +11,6 @@ public Piece(final PieceType pieceType, final TeamColor teamColor) { this.teamColor = teamColor; } - public boolean compareCamp(final Piece other) { - return teamColor == other.teamColor; - } - public boolean isSameTeam(final TeamColor diffType) { return teamColor == diffType; } @@ -23,6 +19,20 @@ public PieceType getPieceType() { return pieceType; } + public boolean isKing() { + return false; + } + + public boolean isPawn() { + return false; + } + + public abstract Double getScoreValue(); + + public TeamColor getTeamColor() { + return teamColor; + } + @Override public boolean equals(final Object o) { if (this == o) return true; diff --git a/src/main/java/chess/domain/piece/PieceType.java b/src/main/java/chess/domain/piece/PieceType.java index e61a1ce622f..f9483a3ab68 100644 --- a/src/main/java/chess/domain/piece/PieceType.java +++ b/src/main/java/chess/domain/piece/PieceType.java @@ -1,10 +1,37 @@ package chess.domain.piece; +import java.util.Arrays; +import java.util.function.BiFunction; + public enum PieceType { - QUEEN, - ROOK, - KNIGHT, - PAWN, - BISHOP, - KING + QUEEN(Queen::new), + ROOK(Rook::new), + KNIGHT(Knight::new), + PAWN(Pawn::new), + BISHOP(Bishop::new), + KING(King::new); + + private final BiFunction toPiece; + + PieceType(BiFunction toPiece) { + this.toPiece = toPiece; + } + + public static Piece toPiece(final PieceType pieceType, final TeamColor color) { + if (pieceType == PAWN) { + return getPawn(color); + } + return Arrays.stream(values()) + .filter(it -> it.name().equalsIgnoreCase(pieceType.name())) + .map(it -> it.toPiece.apply(pieceType, color)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("기물을 찾을 수 없습니다.")); + } + + private static Pawn getPawn(TeamColor color) { + if (color == TeamColor.BLACK) { + return new BlackPawn(PieceType.PAWN); + } + return new WhitePawn(PieceType.PAWN); + } } diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java index 111a6ff476b..51fdc3f7692 100644 --- a/src/main/java/chess/domain/piece/Queen.java +++ b/src/main/java/chess/domain/piece/Queen.java @@ -5,6 +5,7 @@ public class Queen extends Piece { private static final int QUEEN_MAX_MOVE_COUNT = 8; + private static final Double SCORE_VALUE = 9.0; public Queen(final PieceType pieceType, final TeamColor teamColor) { super(pieceType, teamColor); @@ -16,4 +17,9 @@ public boolean canMove(final Position source, final Position target, final Piece PossibleDestinations.of(source, Direction.getAllDirections(), QUEEN_MAX_MOVE_COUNT); return allPositions.contains(target); } + + @Override + public Double getScoreValue() { + return SCORE_VALUE; + } } diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java index ca629d06fd1..4a2076fafab 100644 --- a/src/main/java/chess/domain/piece/Rook.java +++ b/src/main/java/chess/domain/piece/Rook.java @@ -5,6 +5,7 @@ public class Rook extends Piece { private static final int ROOK_MAX_MOVE_COUNT = 8; + private static final Double SCORE_VALUE = 5.0; public Rook(final PieceType pieceType, final TeamColor teamColor) { super(pieceType, teamColor); @@ -16,4 +17,9 @@ public boolean canMove(final Position source, final Position target, final Piece PossibleDestinations.of(source, Direction.getFourDirections(), ROOK_MAX_MOVE_COUNT); return allPositions.contains(target); } + + @Override + public Double getScoreValue() { + return SCORE_VALUE; + } } diff --git a/src/main/java/chess/domain/position/Position.java b/src/main/java/chess/domain/position/Position.java index cd97f9c5980..faba015f502 100644 --- a/src/main/java/chess/domain/position/Position.java +++ b/src/main/java/chess/domain/position/Position.java @@ -1,5 +1,7 @@ package chess.domain.position; +import chess.domain.piece.Direction; + import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -58,16 +60,11 @@ private int getGcdGap(int fileGap, int rankGap) { } public boolean isUpsideDiagonalPosition(Position target) { - int fileGap = target.file - this.file; - int rankGap = target.rank - this.rank; - return Math.abs(fileGap) == 1 && rankGap == 1; + return Direction.isUpsideDiagonal(target.file - this.file, target.rank - this.rank); } public boolean isDownSideDiagonalPosition(Position target) { - int fileGap = target.file - this.file; - int rankGap = target.rank - this.rank; - return Math.abs(fileGap) == 1 && rankGap == -1; - + return Direction.isDownsideDiagonal(target.file - this.file, target.rank - this.rank); } @Override diff --git a/src/main/java/chess/domain/result/Score.java b/src/main/java/chess/domain/result/Score.java new file mode 100644 index 00000000000..8e79e9e678a --- /dev/null +++ b/src/main/java/chess/domain/result/Score.java @@ -0,0 +1,60 @@ +package chess.domain.result; + +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import chess.domain.piece.TeamColor; +import chess.domain.position.Position; + +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class Score { + private static final int START_INDEX = 0; + private static final int END_INDEX = 8; + + private final Map score; + + private Score(Map score) { + this.score = score; + } + + public static Score calculate(Map board) { + Map score = Arrays.stream(TeamColor.values()) + .collect(Collectors.toMap(Function.identity(), + color -> sumScoreExceptPawn(color, board) + sumScorePawn(color, board))); + return new Score(score); + } + + private static Double sumScoreExceptPawn(TeamColor color, Map board) { + return board.values() + .stream() + .filter(piece -> piece.isSameTeam(color)) + .filter(piece -> !piece.isPawn()) + .mapToDouble(Piece::getScoreValue) + .sum(); + } + + private static Double sumScorePawn(TeamColor color, Map board) { + return IntStream.range(START_INDEX, END_INDEX) + .map(file -> countPawnByEachFile(file, color, board)) + .mapToDouble(Pawn::calculateScore) + .sum(); + } + + private static int countPawnByEachFile(int file, TeamColor color, Map board) { + return (int) IntStream.range(START_INDEX, END_INDEX) + .mapToObj(rank -> board.get(Position.of(rank, file))) + .filter(piece -> !Objects.isNull(piece)) + .filter(Piece::isPawn) + .filter(piece -> piece.isSameTeam(color)) + .count(); + } + + public Double getScore(TeamColor teamColor) { + return score.get(teamColor); + } +} diff --git a/src/main/java/chess/view/OutputView.java b/src/main/java/chess/view/OutputView.java index fb0321c71aa..0951d2a130d 100644 --- a/src/main/java/chess/view/OutputView.java +++ b/src/main/java/chess/view/OutputView.java @@ -1,6 +1,8 @@ package chess.view; -import static chess.controller.status.CommandType.*; +import chess.controller.ScoreDto; + +import static chess.controller.state.CommandType.*; public final class OutputView { @@ -17,4 +19,10 @@ public static void printStartMessage() { print(String.format(START_MESSAGE, START.name().toLowerCase(), END.name().toLowerCase(), MOVE.name().toLowerCase())); } + + public static void printStatus(ScoreDto scoreDto) { + print(String.format("Black : %f, White : %f", + scoreDto.getBlackScore(), scoreDto.getWhiteScore())); + System.out.println(System.lineSeparator()); + } } diff --git a/src/test/java/chess/controller/status/EndTest.java b/src/test/java/chess/controller/state/EndTest.java similarity index 97% rename from src/test/java/chess/controller/status/EndTest.java rename to src/test/java/chess/controller/state/EndTest.java index b782e1cd8e2..12aa030de62 100644 --- a/src/test/java/chess/controller/status/EndTest.java +++ b/src/test/java/chess/controller/state/EndTest.java @@ -1,4 +1,4 @@ -package chess.controller.status; +package chess.controller.state; import chess.controller.Command; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/chess/controller/status/MoveTest.java b/src/test/java/chess/controller/state/MoveTest.java similarity index 94% rename from src/test/java/chess/controller/status/MoveTest.java rename to src/test/java/chess/controller/state/MoveTest.java index 0a1db15bb9a..53e5d4bf090 100644 --- a/src/test/java/chess/controller/status/MoveTest.java +++ b/src/test/java/chess/controller/state/MoveTest.java @@ -1,7 +1,7 @@ -package chess.controller.status; +package chess.controller.state; import chess.controller.Command; -import chess.domain.chess.ChessGame; +import chess.domain.game.ChessGame; import chess.domain.piece.TeamColor; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -37,10 +37,10 @@ void checkCommandEnd() { final Command command = new Command(CommandType.END, List.of("end")); // when - Status status = move.checkCommand(command); + State state = move.checkCommand(command); // then - assertThat(status) + assertThat(state) .isInstanceOf(End.class); } diff --git a/src/test/java/chess/controller/status/StartTest.java b/src/test/java/chess/controller/state/StartTest.java similarity index 91% rename from src/test/java/chess/controller/status/StartTest.java rename to src/test/java/chess/controller/state/StartTest.java index 0dab071bf68..dfdbb82d264 100644 --- a/src/test/java/chess/controller/status/StartTest.java +++ b/src/test/java/chess/controller/state/StartTest.java @@ -1,7 +1,7 @@ -package chess.controller.status; +package chess.controller.state; import chess.controller.Command; -import chess.domain.chess.ChessGame; +import chess.domain.game.ChessGame; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -33,10 +33,10 @@ void checkCommand() { final Command command = new Command(CommandType.END, List.of("end")); // when - Status status = start.checkCommand(command); + State state = start.checkCommand(command); // then - assertThat(status) + assertThat(state) .isInstanceOf(End.class); } diff --git a/src/test/java/chess/dao/DBConnectionTest.java b/src/test/java/chess/dao/DBConnectionTest.java new file mode 100644 index 00000000000..d9f518ef7e2 --- /dev/null +++ b/src/test/java/chess/dao/DBConnectionTest.java @@ -0,0 +1,18 @@ +package chess.dao; + +import org.junit.jupiter.api.Test; + +import java.sql.SQLException; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +class DBConnectionTest { + private final DBConnection dbConnection = new DBConnection(); + + @Test + public void connection() throws SQLException { + try (final var connection = dbConnection.getConnection()) { + assertThat(connection).isNotNull(); + } + } +} diff --git a/src/test/java/chess/domain/board/ChessBoardTest.java b/src/test/java/chess/domain/board/ChessBoardTest.java index 08a8fd5129d..112a5f9b09d 100644 --- a/src/test/java/chess/domain/board/ChessBoardTest.java +++ b/src/test/java/chess/domain/board/ChessBoardTest.java @@ -1,6 +1,6 @@ package chess.domain.board; -import chess.domain.chess.ChessGame; +import chess.domain.game.ChessGame; import chess.domain.piece.*; import chess.domain.position.Position; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/chess/domain/chess/ChessGameTest.java b/src/test/java/chess/domain/game/ChessGameTest.java similarity index 76% rename from src/test/java/chess/domain/chess/ChessGameTest.java rename to src/test/java/chess/domain/game/ChessGameTest.java index 21a8ed31ff5..3f22db8dd5d 100644 --- a/src/test/java/chess/domain/chess/ChessGameTest.java +++ b/src/test/java/chess/domain/game/ChessGameTest.java @@ -1,26 +1,25 @@ -package chess.domain.chess; +package chess.domain.game; import chess.domain.position.Position; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatNoException; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; class ChessGameTest { @ParameterizedTest(name = "첫 턴은 백진영의 폰과 나이트만 움직이는지 검증한다.") - @CsvSource(value = {"6:0", "0:3"}, delimiter = ':') + @CsvSource(value = {"1:0", "0:1"}, delimiter = ':') void playMovableFail(final int rank, final int file) { // given final ChessGame chessGame = new ChessGame(); final Position source = Position.of(rank, file); - final Position target = Position.of(5, 0); + final Position target = Position.of(2, 0); // when, then - assertThatThrownBy(() -> chessGame.play(source, target)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("현재 차례가 아닙니다."); + assertThatNoException().isThrownBy(() -> chessGame.play(source, target)); } @ParameterizedTest(name = "첫 턴의 폰은 2칸 이내만 전진할 수 있는지 검증한다.") @@ -33,8 +32,7 @@ void playFirstTurnMovePawnFail(final int rank) { // when, then assertThatThrownBy(() -> chessGame.play(source, target)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("현재 차례가 아닙니다."); + .isInstanceOf(IllegalArgumentException.class); } @ParameterizedTest(name = "첫 턴의 나이트는 L자로 전진할 수 있는지 검증한다.") @@ -48,6 +46,6 @@ void playFirstTurnMoveKnightFail(final int rank, final int file) { // when, then assertThatThrownBy(() -> chessGame.play(source, target)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("현재 차례가 아닙니다."); + .hasMessage("기물 규칙 상 움직일 수 없는 위치입니다."); } } diff --git a/src/test/java/chess/domain/piece/CampTypeTest.java b/src/test/java/chess/domain/piece/CampTypeTest.java index 680c25ca8d1..8e914dee7e3 100644 --- a/src/test/java/chess/domain/piece/CampTypeTest.java +++ b/src/test/java/chess/domain/piece/CampTypeTest.java @@ -5,7 +5,7 @@ import static org.assertj.core.api.Assertions.assertThat; -class CampTypeTest { +class TeamColorTest { @ParameterizedTest(name = "입력받은 위치에 따라 진영을 나눈다.") @CsvSource(value = {"a:WHITE", "A:BLACK"}, delimiter = ':') diff --git a/src/test/java/chess/domain/piece/PieceTest.java b/src/test/java/chess/domain/piece/PieceTest.java index 823ecf1a369..e5dd5e915f1 100644 --- a/src/test/java/chess/domain/piece/PieceTest.java +++ b/src/test/java/chess/domain/piece/PieceTest.java @@ -1,8 +1,6 @@ package chess.domain.piece; import chess.domain.position.Position; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.CsvSource; @@ -24,30 +22,14 @@ private static Stream makePosition() { ); } - @Test - @DisplayName("두 개의 체스말이 동일한 진영에 속하는지 판단한다.") - void compareCamp() { - // given - final Piece piece = new WhitePawn(PieceType.PAWN); - final Piece other = new Queen(PieceType.QUEEN, TeamColor.WHITE); - final Piece otherCamp = new King(PieceType.KING, TeamColor.BLACK); - - // when, then - assertThat(piece.compareCamp(other)) - .isTrue(); - - assertThat(piece.compareCamp(otherCamp)) - .isFalse(); - } - @ParameterizedTest(name = "체스말이 입력받은 체스말과 동일한 진영인지 판단한다.") @CsvSource(value = {"WHITE:true", "BLACK:false"}, delimiter = ':') - void isSameCamp(final TeamColor campType, final boolean expected) { + void isSameTeamColor(final TeamColor teamColor, final boolean expected) { // given final Piece piece = new Knight(PieceType.KNIGHT, TeamColor.WHITE); // when, then - assertThat(piece.isSameTeam(campType)) + assertThat(piece.isSameTeam(teamColor)) .isSameAs(expected); } diff --git a/src/test/java/chess/domain/result/ScoreTest.java b/src/test/java/chess/domain/result/ScoreTest.java new file mode 100644 index 00000000000..bf377dd6b04 --- /dev/null +++ b/src/test/java/chess/domain/result/ScoreTest.java @@ -0,0 +1,65 @@ +package chess.domain.result; + +import chess.domain.piece.*; +import chess.domain.position.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +class ScoreTest { + @DisplayName("Pawn 이 한 File 에 하나 있으면 1점으로 계산된다") + @Test + void onePawninFile() { + Map board = new HashMap<>(); + board.put(Position.of(0, 1), new BlackPawn(PieceType.PAWN)); + board.put(Position.of(0, 2), new BlackPawn(PieceType.PAWN)); + Score score = Score.calculate(board); + + assertThat(score.getScore(TeamColor.BLACK)).isEqualTo(2.0); + } + + @DisplayName("Pawn 이 한 File 에 두개 이상 있으면 각각 0.5점으로 계산된다.") + @Test + void TwoPawninSameFile() { + Map board = new HashMap<>(); + board.put(Position.of(0, 1), new BlackPawn(PieceType.PAWN)); + board.put(Position.of(1, 1), new BlackPawn(PieceType.PAWN)); + Score score = Score.calculate(board); + + assertThat(score.getScore(TeamColor.BLACK)).isEqualTo(1.0); + } + + @DisplayName("Queen, Rook, Bishop, Knight 점수 계산 테스트(Pawn 제외)") + @Test + void allPieceExceptPawn() { + Map board = new HashMap<>(); + board.put(Position.of(1, 1), new Queen(PieceType.QUEEN, TeamColor.BLACK)); + board.put(Position.of(3, 4), new Rook(PieceType.ROOK, TeamColor.BLACK)); + board.put(Position.of(2, 6), new Bishop(PieceType.BISHOP, TeamColor.BLACK)); + board.put(Position.of(7, 7), new Knight(PieceType.KNIGHT, TeamColor.BLACK)); + + Score score = Score.calculate(board); + + assertThat(score.getScore(TeamColor.BLACK)).isEqualTo(19.5); + } + + @DisplayName("Queen, Rook, Bishop, Knight 점수 계산 테스트(같은 File 에 Pawn 2개 있을 때)") + @Test + void allPieceWithTwoPawnInSameFile() { + Map board = new HashMap<>(); + board.put(Position.of(1, 1), new Queen(PieceType.QUEEN, TeamColor.BLACK)); + board.put(Position.of(3, 4), new Rook(PieceType.ROOK, TeamColor.BLACK)); + board.put(Position.of(2, 6), new Bishop(PieceType.BISHOP, TeamColor.BLACK)); + board.put(Position.of(7, 7), new Knight(PieceType.KNIGHT, TeamColor.BLACK)); + board.put(Position.of(5, 5), new BlackPawn(PieceType.PAWN)); + board.put(Position.of(4, 5), new BlackPawn(PieceType.PAWN)); + + Score score = Score.calculate(board); + + assertThat(score.getScore(TeamColor.BLACK)).isEqualTo(20.5); + } +}