diff --git a/.gitignore b/.gitignore
index 6c018781387..96711b991d8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,6 @@ out/
### VS Code ###
.vscode/
+
+### dockder ###
+docker/
diff --git a/README.md b/README.md
index 8102f91c870..0371d567c28 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,18 @@
체스 미션 저장소
+
+### [기능 요구 사항 바로가기](https://github.com/reddevilmidzy/java-chess/blob/62e632aa09435db4f69502e663a0cc7ef78a31bb/docs/README.md)
+
+
+
+### [product db 스키마](https://github.com/reddevilmidzy/java-chess/blob/0f6324e83c0fef95c84bd95c5dba8df742a8b613/src/main/resources0)
+
+
+
+### [test db 스키마](https://github.com/reddevilmidzy/java-chess/blob/0f6324e83c0fef95c84bd95c5dba8df742a8b613/src/test/resources)
+
+
## 우아한테크코스 코드리뷰
- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
diff --git a/build.gradle b/build.gradle
index 3697236c6fb..8c605072e94 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,6 +13,7 @@ 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/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 ee8506d8cf0..af5fa595726 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,5 +1,48 @@
# 체스 미션
+## DB 연동
+
+### Docker 실행하기
+
+```bash
+docker-compose -p chess up -d
+```
+
+### DB 스키마
+
+```sql
+use chess;
+
+drop table if exists board;
+
+create table board
+(
+ position varchar(2) not null,
+ piece_type varchar(6) not null,
+ camp varchar(5) not null
+
+);
+
+drop table if exists moving;
+
+create table moving
+(
+ movement_id INT primary key auto_increment,
+ camp varchar(5) not null,
+ start varchar(2) not null,
+ destination varchar(2) not null
+);
+
+drop table if exists turn;
+
+create table turn
+(
+ camp varchar(5) not null,
+ count int not null
+);
+```
+
+
## 기능 요구 사항
* [x] 8 * 8 의 체스 판 생성
@@ -16,7 +59,22 @@
* [x] 기물의 이동 규칙을 벗어나는 경우 예외 발생
* [x] 현재 위치로 빈 칸을 입력받은 경우 예외 발생
* [x] 현재 턴이 아닌 기물을 이동하는 경우 예외 발생
+* [x] status 기능 구현
+ * [x] 남아있는 기물의 점수 계산
+ * queen: 9
+ * rook: 5
+ * bishop 3
+ * knight: 2.5
+ * pawn: 1
+ * [x] 같은 세로줄에 있는 같은 pawn인 경우 0.5
+ * [x] 진영의 점수를 출력
+ * [x] 승리한 진영의 결과 확인
+* [x] 킹이 잡히면 게임 종료
* [x] end 가 입력되면 프로그램 종료
+* [x] 예외 시 재입력
+* [x] 진행중인 게임 end 입력 후 재시작시 이전 게임 실행
+* [x] quit 입력시 저장하지 않고 게임 종료
+* [x] db 예외 처리
## 기물의 이동 기능
@@ -37,18 +95,12 @@
* start: 게임을 시작하는 명령어
* 게임 시작은 start 명령어만 가능하다
-* end: 게임을 종료하는 명령어
+* end: 게임을 저장 후 종료하는 명령어
+* quit: 게임을 저장하지 않고 종료하는 명령어
* move: 게임 중 기물을 움직이는 명령어
* 게임이 시작되지 않았는데 move 명령어를 입력한다면 예외가 발생한다.
* 형식은 move [a-h1-8] [a-h1-8] 형태이고 아닐시 예외가 발생한다.
-
-
-## 리팩터링
-
-* [x] 테스트 코드 보충
-* [x] 프로그래밍 요구 사항 지키기
-* [x] 예외 메시지를 커스텀 예외로 만들기
-* [x] 예외 시 재입력 구현하기
+* status: 현재 점수를 확인하는 명령어
## 우선순위가 낮지만 구현해보고 싶은 기능
@@ -57,5 +109,5 @@
* [ ] 프로모션
* [ ] 캐슬링
* 체크 상황
- * [ ] 체크 메이트면 자동으로 게임 종료
* [ ] 체크 상황이면 체크를 피하는 방향으로만 이동 가능
+* [ ] log 입력시 현재까지 기보 출력
diff --git a/src/main/java/application/Application.java b/src/main/java/application/Application.java
index 4bf227f5dfb..baf4c8e727d 100644
--- a/src/main/java/application/Application.java
+++ b/src/main/java/application/Application.java
@@ -1,6 +1,7 @@
package application;
import controller.ChessController;
+import db.exception.DBException;
import java.util.Scanner;
import view.InputView;
import view.OutputView;
@@ -12,6 +13,10 @@ public static void main(String[] args) {
final OutputView outputView = new OutputView();
final ChessController chessController = new ChessController(inputView, outputView);
- chessController.run();
+ try {
+ chessController.run();
+ } catch (DBException exception) {
+ outputView.printException(exception.getErrorCode());
+ }
}
}
diff --git a/src/main/java/constant/ErrorCode.java b/src/main/java/constant/ErrorCode.java
index a1892cc1243..60a3d44b9f4 100644
--- a/src/main/java/constant/ErrorCode.java
+++ b/src/main/java/constant/ErrorCode.java
@@ -1,15 +1,24 @@
package constant;
public enum ErrorCode {
-
INVALID_INPUT,
INVALID_STATUS,
INVALID_COMMAND,
INVALID_POSITION,
- INVALID_MOVEMENT_RULE,
+ INVALID_PAWN_MOVEMENT,
+ INVALID_KING_MOVEMENT,
+ INVALID_BISHOP_MOVEMENT,
+ INVALID_KNIGHT_MOVEMENT,
+ INVALID_QUEEN_MOVEMENT,
+ INVALID_ROOK_MOVEMENT,
PIECE_EXIST_IN_ROUTE,
+ OWN_PIECE_EXIST_POSITION,
PIECE_DOES_NOT_EXIST_POSITION,
INVALID_CAMP_PIECE,
- NO_MESSAGE;
-
+ KING_DEAD,
+ CONNECTION,
+ FAIL_SAVE,
+ FAIL_FIND,
+ FAIL_DELETE,
+ NO_MESSAGE
}
diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java
index 61ed937e0e5..4d235ba49b9 100644
--- a/src/main/java/controller/ChessController.java
+++ b/src/main/java/controller/ChessController.java
@@ -1,13 +1,20 @@
package controller;
+import db.Repository;
+import db.dto.BoardDto;
+import db.dto.MovingDto;
+import db.dto.TurnDto;
import dto.ChessBoardDto;
+import dto.ScoreDto;
import exception.CustomException;
-import java.util.Collections;
import java.util.List;
-import model.ChessBoard;
-import model.Command;
+import model.Board;
+import model.Camp;
+import model.ChessGame;
+import model.Turn;
+import model.command.CommandLine;
import model.status.GameStatus;
-import model.status.Initialization;
+import model.status.StatusFactory;
import view.InputView;
import view.OutputView;
@@ -15,6 +22,7 @@ public class ChessController {
private final InputView inputView;
private final OutputView outputView;
+ private final Repository repository = new Repository("chess");
public ChessController(final InputView inputView, final OutputView outputView) {
this.inputView = inputView;
@@ -22,55 +30,89 @@ public ChessController(final InputView inputView, final OutputView outputView) {
}
public void run() {
- final ChessBoard chessBoard = ChessBoard.setupStartingPosition();
outputView.printStartMessage();
GameStatus gameStatus = initGame();
-
+ final ChessGame chessGame = create();
+ if (gameStatus.isRunning()) {
+ outputView.printChessBoard(ChessBoardDto.from(chessGame));
+ }
while (gameStatus.isRunning()) {
- printCurrentStatus(chessBoard);
- gameStatus = play(gameStatus, chessBoard);
+ gameStatus = play(gameStatus, chessGame);
}
+ save(gameStatus, chessGame);
+ }
+
+ private void save(final GameStatus gameStatus, final ChessGame chessGame) {
+ if (gameStatus.isCheck() || gameStatus.isQuit()) {
+ repository.removeAll();
+ return;
+ }
+ final Board board = chessGame.getBoard();
+ final Camp camp = chessGame.getCamp();
+ final Turn turn = chessGame.getTurn();
+ final BoardDto boardDto = BoardDto.from(board);
+ final TurnDto turnDto = TurnDto.from(camp, turn);
+ repository.save(boardDto, turnDto);
+ }
+
+ private ChessGame create() {
+ if (repository.hasGame()) {
+ return repository.findGame();
+ }
+ return ChessGame.setupStartingPosition();
}
private GameStatus initGame() {
try {
- return Initialization.gameSetting(getCommand());
+ return StatusFactory.create(readCommandLine());
} catch (final CustomException exception) {
outputView.printException(exception.getErrorCode());
return initGame();
}
}
- private GameStatus play(final GameStatus gameStatus, final ChessBoard chessBoard) {
+ private GameStatus play(final GameStatus preStatus, final ChessGame chessGame) {
try {
- return gameStatus.play(getCommand(), chessBoard);
- } catch (CustomException exception) {
+ final CommandLine commandLine = readCommandLine();
+ final GameStatus status = preStatus.play(commandLine, chessGame);
+ saveMoving(chessGame, commandLine);
+ print(status, commandLine, chessGame);
+ return status;
+ } catch (final CustomException exception) {
outputView.printException(exception.getErrorCode());
- return play(gameStatus, chessBoard);
+ return play(preStatus, chessGame);
}
}
- private void printCurrentStatus(final ChessBoard chessBoard) {
- outputView.printChessBoard(ChessBoardDto.from(chessBoard));
- outputView.printCamp(chessBoard.getCamp());
+ private void saveMoving(final ChessGame chessGame, final CommandLine commandLine) {
+ if (commandLine.isMove()) {
+ final List body = commandLine.getBody();
+ final Camp camp = chessGame.getCamp().toggle();
+ final MovingDto movingDto = MovingDto.from(body, camp);
+ repository.saveMoving(movingDto);
+ }
}
- private List getCommand() {
- List command = Collections.emptyList();
- while (command.isEmpty()) {
- command = readCommand();
+ private void print(final GameStatus gameStatus, final CommandLine commandLine, final ChessGame chessGame) {
+ if (gameStatus.isCheck()) {
+ outputView.printWinner(chessGame.getCamp().toString());
+ return;
+ }
+ if (commandLine.isStatus()) {
+ outputView.printScore(ScoreDto.from(chessGame));
+ }
+ if (commandLine.isStart() || commandLine.isMove()) {
+ outputView.printChessBoard(ChessBoardDto.from(chessGame));
}
- return command;
}
- private List readCommand() {
+ private CommandLine readCommandLine() {
try {
- List command = inputView.readCommandList();
- Command.validate(command);
- return command;
- } catch (CustomException exception) {
+ final List command = inputView.readCommandList();
+ return CommandLine.from(command);
+ } catch (final CustomException exception) {
outputView.printException(exception.getErrorCode());
}
- return Collections.emptyList();
+ return readCommandLine();
}
}
diff --git a/src/main/java/db/BoardDao.java b/src/main/java/db/BoardDao.java
new file mode 100644
index 00000000000..a84c9183bbd
--- /dev/null
+++ b/src/main/java/db/BoardDao.java
@@ -0,0 +1,92 @@
+package db;
+
+import constant.ErrorCode;
+import db.connection.DBConnectionUtil;
+import db.dto.BoardDto;
+import db.dto.PieceDto;
+import db.dto.PositionDto;
+import db.exception.DaoException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import model.Board;
+
+public class BoardDao {
+
+ private final String database;
+
+ public BoardDao(final String database) {
+ this.database = database;
+ }
+
+ public void saveBoard(final BoardDto board) {
+ final Map pieces = board.pieces();
+
+ for (final Entry entry : pieces.entrySet()) {
+ savePosition(entry.getKey(), entry.getValue());
+ }
+ }
+
+ private void savePosition(final PositionDto position, final PieceDto piece) {
+ final String query = "INSERT INTO board VALUES(?, ?, ?)";
+ try (final Connection connection = DBConnectionUtil.getConnection(database);
+ final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
+ preparedStatement.setString(1, position.value());
+ preparedStatement.setString(2, piece.type());
+ preparedStatement.setString(3, piece.camp());
+ preparedStatement.executeUpdate();
+ } catch (final SQLException exception) {
+ throw new DaoException(ErrorCode.FAIL_SAVE);
+ }
+ }
+
+ public BoardDto find() {
+ final String query = "SELECT * FROM board";
+ try (final Connection connection = DBConnectionUtil.getConnection(database);
+ final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
+ final ResultSet resultSet = preparedStatement.executeQuery();
+ return convert(resultSet);
+ } catch (final SQLException exception) {
+ throw new DaoException(ErrorCode.FAIL_FIND);
+ }
+ }
+
+ private BoardDto convert(final ResultSet resultSet) throws SQLException {
+ final Map result = new HashMap<>();
+ while (resultSet.next()) {
+ final PieceDto piece = convertToPiece(resultSet);
+ final PositionDto positionDto = convertToPosition(resultSet);
+ result.put(positionDto, piece);
+ }
+ if (result.isEmpty()) {
+ return BoardDto.from(Board.create());
+ }
+ return new BoardDto(result);
+ }
+
+ private PieceDto convertToPiece(final ResultSet resultSet) throws SQLException {
+ final String type = resultSet.getString("piece_type");
+ final String camp = resultSet.getString("camp");
+ return new PieceDto(type, camp);
+ }
+
+ private PositionDto convertToPosition(final ResultSet resultSet) throws SQLException {
+ final String position = resultSet.getString("position");
+ return new PositionDto(position);
+ }
+
+ public void remove() {
+ final String query = "TRUNCATE TABLE board";
+ try (final Connection connection = DBConnectionUtil.getConnection(database);
+ final PreparedStatement preparedStatement = connection.prepareStatement(query)
+ ) {
+ preparedStatement.executeUpdate();
+ } catch (final SQLException exception) {
+ throw new DaoException(ErrorCode.FAIL_DELETE);
+ }
+ }
+}
diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java
new file mode 100644
index 00000000000..edb3551b6ea
--- /dev/null
+++ b/src/main/java/db/MovingDao.java
@@ -0,0 +1,123 @@
+package db;
+
+import constant.ErrorCode;
+import db.connection.DBConnectionUtil;
+import db.dto.MovingDto;
+import db.exception.DaoException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MovingDao {
+
+ private final String database;
+
+ public MovingDao(final String database) {
+ this.database = database;
+ }
+
+ public long addMoving(final MovingDto moving) {
+ final String query = "INSERT INTO moving VALUES(?, ?, ?, ?)";
+ try (final Connection connection = DBConnectionUtil.getConnection(database);
+ final PreparedStatement preparedStatement = connection.prepareStatement(query,
+ Statement.RETURN_GENERATED_KEYS)) {
+ preparedStatementSet(moving, preparedStatement);
+ preparedStatement.executeUpdate();
+ return increaseKey(preparedStatement.getGeneratedKeys());
+ } catch (final SQLException exception) {
+ throw new DaoException(ErrorCode.FAIL_SAVE);
+ }
+ }
+
+ private long increaseKey(final ResultSet generatedKeys) throws SQLException {
+ if (generatedKeys.next()) {
+ return generatedKeys.getLong(1);
+ }
+ return 0;
+ }
+
+ private void preparedStatementSet(final MovingDto moving, final PreparedStatement preparedStatement)
+ throws SQLException {
+ preparedStatement.setNull(1, 0);
+ preparedStatement.setString(2, moving.camp());
+ preparedStatement.setString(3, moving.current());
+ preparedStatement.setString(4, moving.next());
+ }
+
+ public List findAll() {
+ final String query = "SELECT * FROM moving";
+ try (final Connection connection = DBConnectionUtil.getConnection(database);
+ final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
+ final ResultSet resultSet = preparedStatement.executeQuery();
+ return convert(resultSet);
+ } catch (SQLException exception) {
+ throw new DaoException(ErrorCode.FAIL_FIND);
+ }
+ }
+
+ private List convert(final ResultSet resultSet) throws SQLException {
+ final List moving = new ArrayList<>();
+ while (resultSet.next()) {
+ String camp = resultSet.getString("camp");
+ String current = resultSet.getString("start");
+ String next = resultSet.getString("destination");
+ moving.add(new MovingDto(camp, current, next));
+ }
+ return moving;
+ }
+
+ public int countMoving() {
+ final String query = "SELECT count(*) AS count FROM moving";
+ try (final Connection connection = DBConnectionUtil.getConnection(database);
+ final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
+ final ResultSet resultSet = preparedStatement.executeQuery();
+ return convertToCount(resultSet);
+ } catch (final SQLException exception) {
+ throw new DaoException(ErrorCode.FAIL_FIND);
+ }
+ }
+
+ private int convertToCount(final ResultSet resultSet) throws SQLException {
+ if (resultSet.next()) {
+ return resultSet.getInt("count");
+ }
+ throw new DaoException(ErrorCode.FAIL_FIND);
+ }
+
+ public MovingDto findByMovementId(final long movementId) {
+ final String query = "SELECT * FROM moving WHERE movement_id = ?";
+ try (final Connection connection = DBConnectionUtil.getConnection(database);
+ final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
+ preparedStatement.setLong(1, movementId);
+ final ResultSet resultSet = preparedStatement.executeQuery();
+ return convertToMoving(resultSet);
+ } catch (final SQLException exception) {
+ throw new DaoException(ErrorCode.FAIL_FIND);
+ }
+ }
+
+ private MovingDto convertToMoving(final ResultSet resultSet) throws SQLException {
+ if (resultSet.next()) {
+ return new MovingDto(
+ resultSet.getString("camp"),
+ resultSet.getString("start"),
+ resultSet.getString("destination")
+ );
+ }
+ throw new DaoException(ErrorCode.FAIL_FIND);
+ }
+
+ public void remove() {
+ final String query = "TRUNCATE TABLE moving";
+ try (final Connection connection = DBConnectionUtil.getConnection(database);
+ final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
+ preparedStatement.executeUpdate();
+ } catch (final SQLException exception) {
+ throw new DaoException(ErrorCode.FAIL_DELETE);
+ }
+ }
+}
diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java
new file mode 100644
index 00000000000..64dd8b46ba6
--- /dev/null
+++ b/src/main/java/db/Repository.java
@@ -0,0 +1,87 @@
+package db;
+
+import db.dto.BoardDto;
+import db.dto.MovingDto;
+import db.dto.TurnDto;
+import java.util.List;
+import model.Board;
+import model.Camp;
+import model.ChessGame;
+import model.GameTurn;
+import model.Turn;
+import model.position.Moving;
+import model.position.Position;
+
+public class Repository {
+
+ private final MovingDao movingDao;
+ private final TurnDao turnDao;
+ private final BoardDao boardDao;
+
+ public Repository(final String database) {
+ this.movingDao = new MovingDao(database);
+ this.turnDao = new TurnDao(database);
+ this.boardDao = new BoardDao(database);
+ }
+
+ public void removeAll() {
+ turnDao.remove();
+ boardDao.remove();
+ movingDao.remove();
+ }
+
+ public boolean hasGame() {
+ return movingDao.countMoving() > 0;
+ }
+
+ public void save(final BoardDto board, final TurnDto turnDto) {
+ boardDao.remove();
+ boardDao.saveBoard(board);
+ turnDao.remove();
+ turnDao.saveTurn(turnDto);
+ }
+
+ public void saveMoving(final MovingDto moving) {
+ movingDao.addMoving(moving);
+ }
+
+ public ChessGame findGame() {
+ final BoardDto findBoard = findBoard();
+ final TurnDto findTurn = findTurn();
+ final List findMoving = movingDao.findAll();
+ final Board board = findBoard.toBoard();
+ if (unsaved(findTurn, findMoving)) {
+ restore(findTurn, findMoving, board);
+ }
+ final Camp camp = findLastTurnCamp(findMoving);
+ final GameTurn gameTurn = new GameTurn(camp, new Turn(findMoving.size()));
+ return new ChessGame(board, gameTurn);
+ }
+
+ private BoardDto findBoard() {
+ return boardDao.find();
+ }
+
+ private TurnDto findTurn() {
+ return turnDao.findTurn();
+ }
+
+ private boolean unsaved(final TurnDto findTurn, final List findMoving) {
+ return findTurn.count() < findMoving.size();
+ }
+
+ private Camp findLastTurnCamp(final List findMoving) {
+ if (findMoving.size() % 2 == 0) {
+ return Camp.WHITE;
+ }
+ return Camp.BLACK;
+ }
+
+ private void restore(final TurnDto findTurn, final List findMoving, final Board board) {
+ for (int i = findTurn.count(); i < findMoving.size(); i++) {
+ final MovingDto movingDto = findMoving.get(i);
+ final Moving moving = new Moving(Position.from(movingDto.current()), Position.from(movingDto.next()));
+ board.move(moving);
+ }
+ }
+}
diff --git a/src/main/java/db/TurnDao.java b/src/main/java/db/TurnDao.java
new file mode 100644
index 00000000000..99542af9855
--- /dev/null
+++ b/src/main/java/db/TurnDao.java
@@ -0,0 +1,59 @@
+package db;
+
+import constant.ErrorCode;
+import db.connection.DBConnectionUtil;
+import db.dto.TurnDto;
+import db.exception.DaoException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class TurnDao {
+
+ private final String database;
+
+ public TurnDao(final String database) {
+ this.database = database;
+ }
+
+ public void saveTurn(final TurnDto turnDto) {
+ final String query = "INSERT INTO turn values(?, ?)";
+ try (final Connection connection = DBConnectionUtil.getConnection(database);
+ final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
+ preparedStatement.setString(1, turnDto.currentCamp());
+ preparedStatement.setInt(2, turnDto.count());
+ preparedStatement.executeUpdate();
+ } catch (final SQLException exception) {
+ throw new DaoException(ErrorCode.FAIL_SAVE);
+ }
+ }
+
+ public TurnDto findTurn() {
+ final String query = "SELECT * FROM turn";
+ try (final Connection connection = DBConnectionUtil.getConnection(database);
+ final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
+ final ResultSet resultSet = preparedStatement.executeQuery();
+ return convertToTurn(resultSet);
+ } catch (SQLException exception) {
+ throw new DaoException(ErrorCode.FAIL_FIND);
+ }
+ }
+
+ private TurnDto convertToTurn(final ResultSet resultSet) throws SQLException {
+ if (resultSet.next()) {
+ return new TurnDto(resultSet.getString("camp"), resultSet.getInt("count"));
+ }
+ return new TurnDto("WHITE", 0);
+ }
+
+ public void remove() {
+ final String query = "TRUNCATE TABLE turn";
+ try (final Connection connection = DBConnectionUtil.getConnection(database);
+ final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
+ preparedStatement.executeUpdate();
+ } catch (final SQLException exception) {
+ throw new DaoException(ErrorCode.FAIL_DELETE);
+ }
+ }
+}
diff --git a/src/main/java/db/connection/DBConnectionUtil.java b/src/main/java/db/connection/DBConnectionUtil.java
new file mode 100644
index 00000000000..a3d607fdeb0
--- /dev/null
+++ b/src/main/java/db/connection/DBConnectionUtil.java
@@ -0,0 +1,27 @@
+package db.connection;
+
+import constant.ErrorCode;
+import db.exception.ConnectionException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+public class DBConnectionUtil {
+
+ private static final String SERVER = "localhost:13306";
+ private static final String OPTION = "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
+ private static final String USERNAME = "root";
+ private static final String PASSWORD = "root";
+
+ private DBConnectionUtil() {
+ }
+
+ public static Connection getConnection(final String database) {
+ try {
+ final String url = "jdbc:mysql://" + SERVER + "/" + database + OPTION;
+ return DriverManager.getConnection(url, USERNAME, PASSWORD);
+ } catch (final SQLException exception) {
+ throw new ConnectionException(ErrorCode.CONNECTION);
+ }
+ }
+}
diff --git a/src/main/java/db/dto/BoardDto.java b/src/main/java/db/dto/BoardDto.java
new file mode 100644
index 00000000000..70a96506204
--- /dev/null
+++ b/src/main/java/db/dto/BoardDto.java
@@ -0,0 +1,31 @@
+package db.dto;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import model.Board;
+import model.piece.Piece;
+import model.position.Position;
+
+public record BoardDto(Map pieces) {
+
+ public static BoardDto from(final Board board) {
+ final Map result = new HashMap<>();
+ final Map pieces = board.getPieces();
+
+ for (Entry entry : pieces.entrySet()) {
+ final PositionDto position = PositionDto.from(entry.getKey());
+ final PieceDto piece = PieceDto.from(entry.getValue());
+ result.put(position, piece);
+ }
+ return new BoardDto(result);
+ }
+
+ public Board toBoard() {
+ final Map result = new HashMap<>();
+ for (Entry entry : pieces.entrySet()) {
+ result.put(entry.getKey().toPosition(), entry.getValue().toPiece());
+ }
+ return new Board(result);
+ }
+}
diff --git a/src/main/java/db/dto/CampType.java b/src/main/java/db/dto/CampType.java
new file mode 100644
index 00000000000..fd243d84196
--- /dev/null
+++ b/src/main/java/db/dto/CampType.java
@@ -0,0 +1,40 @@
+package db.dto;
+
+import java.util.Arrays;
+import model.Camp;
+
+public enum CampType {
+
+ WHITE(Camp.WHITE, "WHITE"),
+ BLACK(Camp.BLACK, "BLACK");
+
+ private final Camp camp;
+ private final String colorName;
+
+ CampType(final Camp camp, final String colorName) {
+ this.camp = camp;
+ this.colorName = colorName;
+ }
+
+ public static CampType findByColorName(final String color) {
+ return Arrays.stream(values())
+ .filter(campType -> campType.colorName.equals(color))
+ .findFirst()
+ .orElseThrow();
+ }
+
+ public static CampType findByCamp(final Camp camp) {
+ return Arrays.stream(values())
+ .filter(campType -> campType.camp == camp)
+ .findFirst()
+ .orElseThrow();
+ }
+
+ public String getColorName() {
+ return colorName;
+ }
+
+ public Camp getCamp() {
+ return camp;
+ }
+}
diff --git a/src/main/java/db/dto/MovingDto.java b/src/main/java/db/dto/MovingDto.java
new file mode 100644
index 00000000000..b9f0f67e044
--- /dev/null
+++ b/src/main/java/db/dto/MovingDto.java
@@ -0,0 +1,13 @@
+package db.dto;
+
+import java.util.List;
+import model.Camp;
+
+public record MovingDto(String camp, String current, String next) {
+
+ public static MovingDto from(final List moving, final Camp camp) {
+ final String current = moving.get(0);
+ final String next = moving.get(1);
+ return new MovingDto(camp.toString(), current, next);
+ }
+}
diff --git a/src/main/java/db/dto/PieceDto.java b/src/main/java/db/dto/PieceDto.java
new file mode 100644
index 00000000000..9b6b01c18be
--- /dev/null
+++ b/src/main/java/db/dto/PieceDto.java
@@ -0,0 +1,18 @@
+package db.dto;
+
+import model.piece.Piece;
+
+public record PieceDto(String type, String camp) {
+
+ public static PieceDto from(final Piece piece) {
+ final PieceType pieceType = PieceType.findValue(piece);
+ final CampType campType = CampType.findByCamp(piece.getCamp());
+ return new PieceDto(pieceType.getPieceName(), campType.getColorName());
+ }
+
+ public Piece toPiece() {
+ final CampType army = CampType.findByColorName(camp);
+ final PieceType pieceType = PieceType.findValue(army.getCamp(), type);
+ return pieceType.getPiece();
+ }
+}
diff --git a/src/main/java/db/dto/PieceType.java b/src/main/java/db/dto/PieceType.java
new file mode 100644
index 00000000000..2817b4bf109
--- /dev/null
+++ b/src/main/java/db/dto/PieceType.java
@@ -0,0 +1,62 @@
+package db.dto;
+
+import java.util.Arrays;
+import java.util.List;
+import model.Camp;
+import model.piece.Bishop;
+import model.piece.BlackPawn;
+import model.piece.King;
+import model.piece.Knight;
+import model.piece.Piece;
+import model.piece.Queen;
+import model.piece.Rook;
+import model.piece.WhitePawn;
+
+public enum PieceType {
+
+ WHITE_KING(new King(Camp.WHITE), "King"),
+ WHITE_QUEEN(new Queen(Camp.WHITE), "Queen"),
+ WHITE_ROOK(new Rook(Camp.WHITE), "Rook"),
+ WHITE_BISHOP(new Bishop(Camp.WHITE), "Bishop"),
+ WHITE_KNIGHT(new Knight(Camp.WHITE), "Knight"),
+ WHITE_PAWN(new WhitePawn(), "Pawn"),
+ BLACK_KING(new King(Camp.BLACK), "King"),
+ BLACK_QUEEN(new Queen(Camp.BLACK), "Queen"),
+ BLACK_ROOK(new Rook(Camp.BLACK), "Rook"),
+ BLACK_BISHOP(new Bishop(Camp.BLACK), "Bishop"),
+ BLACK_KNIGHT(new Knight(Camp.BLACK), "Knight"),
+ BLACK_PAWN(new BlackPawn(), "Pawn");
+
+ private final Piece piece;
+ private final String pieceName;
+
+ PieceType(final Piece piece, final String pieceName) {
+ this.piece = piece;
+ this.pieceName = pieceName;
+ }
+
+ public static PieceType findValue(final Piece piece) {
+ return Arrays.stream(values())
+ .filter(pieceType1 -> pieceType1.piece.getClass().isInstance(piece))
+ .findFirst()
+ .orElseThrow();
+ }
+
+ public static PieceType findValue(final Camp camp, final String type) {
+ final List pieceTypes = Arrays.stream(values())
+ .filter(pieceType -> pieceType.pieceName.equals(type))
+ .toList();
+ return pieceTypes.stream()
+ .filter(pieceType -> pieceType.piece.isSameCamp(camp))
+ .findFirst()
+ .orElseThrow();
+ }
+
+ public String getPieceName() {
+ return pieceName;
+ }
+
+ public Piece getPiece() {
+ return piece;
+ }
+}
diff --git a/src/main/java/db/dto/PositionDto.java b/src/main/java/db/dto/PositionDto.java
new file mode 100644
index 00000000000..5c451e94e67
--- /dev/null
+++ b/src/main/java/db/dto/PositionDto.java
@@ -0,0 +1,16 @@
+package db.dto;
+
+import model.position.File;
+import model.position.Position;
+import model.position.Rank;
+
+public record PositionDto(String value) {
+
+ public static PositionDto from(final Position position) {
+ return new PositionDto(position.toString());
+ }
+
+ public Position toPosition() {
+ return new Position(File.from(value.charAt(0)), Rank.from(value.charAt(1)));
+ }
+}
diff --git a/src/main/java/db/dto/TurnDto.java b/src/main/java/db/dto/TurnDto.java
new file mode 100644
index 00000000000..339aaa070c1
--- /dev/null
+++ b/src/main/java/db/dto/TurnDto.java
@@ -0,0 +1,12 @@
+package db.dto;
+
+import model.Camp;
+import model.Turn;
+
+public record TurnDto(String currentCamp, int count) {
+
+ public static TurnDto from(final Camp camp, final Turn turn) {
+ final CampType campType = CampType.findByCamp(camp);
+ return new TurnDto(campType.getColorName(), turn.count());
+ }
+}
diff --git a/src/main/java/db/exception/ConnectionException.java b/src/main/java/db/exception/ConnectionException.java
new file mode 100644
index 00000000000..65a6022feb3
--- /dev/null
+++ b/src/main/java/db/exception/ConnectionException.java
@@ -0,0 +1,10 @@
+package db.exception;
+
+import constant.ErrorCode;
+
+public class ConnectionException extends DBException {
+
+ public ConnectionException(final ErrorCode errorCode) {
+ super(errorCode);
+ }
+}
diff --git a/src/main/java/db/exception/DBException.java b/src/main/java/db/exception/DBException.java
new file mode 100644
index 00000000000..34bc84e2ae9
--- /dev/null
+++ b/src/main/java/db/exception/DBException.java
@@ -0,0 +1,16 @@
+package db.exception;
+
+import constant.ErrorCode;
+
+public class DBException extends RuntimeException {
+
+ private final ErrorCode errorCode;
+
+ public DBException(final ErrorCode errorCode) {
+ this.errorCode = errorCode;
+ }
+
+ public ErrorCode getErrorCode() {
+ return errorCode;
+ }
+}
diff --git a/src/main/java/db/exception/DaoException.java b/src/main/java/db/exception/DaoException.java
new file mode 100644
index 00000000000..0834c5d54fd
--- /dev/null
+++ b/src/main/java/db/exception/DaoException.java
@@ -0,0 +1,10 @@
+package db.exception;
+
+import constant.ErrorCode;
+
+public class DaoException extends DBException {
+
+ public DaoException(final ErrorCode errorCode) {
+ super(errorCode);
+ }
+}
diff --git a/src/main/java/dto/ChessBoardDto.java b/src/main/java/dto/ChessBoardDto.java
index fd74ac65bca..e8f7c1b22c8 100644
--- a/src/main/java/dto/ChessBoardDto.java
+++ b/src/main/java/dto/ChessBoardDto.java
@@ -1,9 +1,7 @@
package dto;
import java.util.Map;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import model.ChessBoard;
+import model.ChessGame;
import model.piece.Piece;
import model.position.File;
import model.position.Position;
@@ -13,32 +11,43 @@
public class ChessBoardDto {
private static final String FILE_GUIDE_LINE = "abcdefgh";
+ private static final String RANK_GUIDE_LINE_FORM = " %s";
private static final String EMPTY_POINT = ".";
- private static final String RANK_GUIDE_LINE = " %s";
private static final int BOARD_SIZE = 8;
- private final String value;
+ private final String board;
+ private final String currentTurn;
- private ChessBoardDto(final String value) {
- this.value = value;
+ private ChessBoardDto(final String board, final String currentTurn) {
+ this.board = board;
+ this.currentTurn = currentTurn;
}
- public static ChessBoardDto from(final ChessBoard chessBoard) {
- final Map pieceOfPosition = chessBoard.getBoard();
- final String result = IntStream.range(0, BOARD_SIZE)
- .mapToObj(Rank::from)
- .map(rank -> IntStream.range(0, BOARD_SIZE)
- .mapToObj(File::from)
- .map(file -> convertToString(pieceOfPosition, file, rank))
- .collect(Collectors.joining()))
- .collect(Collectors.joining(System.lineSeparator()))
- .concat(String.format("%n%n%s%n", FILE_GUIDE_LINE));
- return new ChessBoardDto(result);
+ public static ChessBoardDto from(final ChessGame chessGame) {
+ final Map pieceOfPosition = chessGame.getPieces();
+
+ final StringBuilder stringBuilder = new StringBuilder();
+ for (int i = 0; i < BOARD_SIZE; i++) {
+ stringBuilder.append(generateBoardLine(pieceOfPosition, Rank.from(i)));
+ stringBuilder.append(System.lineSeparator());
+ }
+ stringBuilder.append(String.format("%n%s%n", FILE_GUIDE_LINE));
+
+ return new ChessBoardDto(stringBuilder.toString(), chessGame.getCamp().toString());
+ }
+
+ private static String generateBoardLine(final Map pieceOfPosition, final Rank rank) {
+ final StringBuilder stringBuilder = new StringBuilder();
+ for (int j = 0; j < BOARD_SIZE; j++) {
+ final File file = File.from(j);
+ final Position position = new Position(file, rank);
+ final Piece piece = pieceOfPosition.get(position);
+ stringBuilder.append(convertToString(piece, file, rank));
+ }
+ return stringBuilder.toString();
}
- private static String convertToString(final Map board, final File file, final Rank rank) {
- final Position position = new Position(file, rank);
- final Piece piece = board.get(position);
+ private static String convertToString(final Piece piece, final File file, final Rank rank) {
if (piece != null) {
return PieceType.from(piece).getValue() + paddedRankGuidLine(file, rank);
}
@@ -47,12 +56,16 @@ private static String convertToString(final Map board, final Fi
private static String paddedRankGuidLine(final File file, final Rank rank) {
if (file.isLast()) {
- return String.format(RANK_GUIDE_LINE, rank.getValue());
+ return String.format(RANK_GUIDE_LINE_FORM, rank.getValue());
}
return "";
}
- public String getValue() {
- return value;
+ public String getBoard() {
+ return board;
+ }
+
+ public String getCurrentTurn() {
+ return currentTurn;
}
}
diff --git a/src/main/java/dto/ScoreDto.java b/src/main/java/dto/ScoreDto.java
new file mode 100644
index 00000000000..24f9466308d
--- /dev/null
+++ b/src/main/java/dto/ScoreDto.java
@@ -0,0 +1,30 @@
+package dto;
+
+import model.Camp;
+import model.ChessGame;
+import model.Score;
+
+public class ScoreDto {
+
+ private final float blackScore;
+ private final float whiteScore;
+
+ private ScoreDto(final float blackScore, final float whiteScore) {
+ this.blackScore = blackScore;
+ this.whiteScore = whiteScore;
+ }
+
+ public static ScoreDto from(final ChessGame chessGame) {
+ final Score black = chessGame.calculateScore(Camp.BLACK);
+ final Score white = chessGame.calculateScore(Camp.WHITE);
+ return new ScoreDto(black.value(), white.value());
+ }
+
+ public float getBlackScore() {
+ return blackScore;
+ }
+
+ public float getWhiteScore() {
+ return whiteScore;
+ }
+}
diff --git a/src/main/java/exception/KingDeadException.java b/src/main/java/exception/KingDeadException.java
new file mode 100644
index 00000000000..c37549aaff0
--- /dev/null
+++ b/src/main/java/exception/KingDeadException.java
@@ -0,0 +1,10 @@
+package exception;
+
+import constant.ErrorCode;
+
+public class KingDeadException extends CustomException {
+
+ public KingDeadException(final ErrorCode errorCode) {
+ super(errorCode);
+ }
+}
diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java
new file mode 100644
index 00000000000..8b6032ce042
--- /dev/null
+++ b/src/main/java/model/Board.java
@@ -0,0 +1,183 @@
+package model;
+
+import constant.ErrorCode;
+import exception.InvalidTurnException;
+import exception.KingDeadException;
+import exception.PieceDoesNotExistException;
+import exception.PieceExistInRouteException;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Function;
+import model.piece.Bishop;
+import model.piece.BlackPawn;
+import model.piece.King;
+import model.piece.Knight;
+import model.piece.Pawn;
+import model.piece.Piece;
+import model.piece.Queen;
+import model.piece.Rook;
+import model.piece.WhitePawn;
+import model.position.File;
+import model.position.Moving;
+import model.position.Position;
+import model.position.Rank;
+
+public class Board {
+
+ private static final Map> startingPosition = Map.of(
+ File.A, Rook::new,
+ File.B, Knight::new,
+ File.C, Bishop::new,
+ File.D, Queen::new,
+ File.E, King::new,
+ File.F, Bishop::new,
+ File.G, Knight::new,
+ File.H, Rook::new
+ );
+ private static final Set KINGS = Set.of(new King(Camp.WHITE), new King(Camp.BLACK));
+ private static final Set PAWNS = Set.of(new WhitePawn(), new BlackPawn());
+ private static final Score SAME_FILE_PAWN_SCORE = new Score(0.5F);
+
+ private final Map pieces;
+
+ public Board(final Map pieces) {
+ this.pieces = pieces;
+ }
+
+ public static Board create() {
+ final Map result = new HashMap<>();
+ settingExceptPawn(result, Camp.BLACK, Rank.EIGHT);
+ settingPawn(result, Rank.SEVEN, new BlackPawn());
+ settingPawn(result, Rank.TWO, new WhitePawn());
+ settingExceptPawn(result, Camp.WHITE, Rank.ONE);
+ return new Board(result);
+ }
+
+ private static void settingExceptPawn(final Map board, final Camp camp, final Rank rank) {
+ for (File file : File.values()) {
+ final Piece piece = startingPosition.get(file).apply(camp);
+ board.put(new Position(file, rank), piece);
+ }
+ }
+
+ private static void settingPawn(final Map board, final Rank rank, final Pawn pawn) {
+ for (File file : File.values()) {
+ board.put(new Position(file, rank), pawn);
+ }
+ }
+
+ public void validate(final Moving moving, final Camp currentCamp) {
+ final Position currentPosition = moving.getCurrentPosition();
+ validateExistPiece(currentPosition);
+ final Piece piece = pieces.get(currentPosition);
+ validateOwnPiece(currentCamp, piece);
+ final Set route = getRoute(moving, piece);
+ validateUnBlocked(route);
+ final Position nextPosition = moving.getNextPosition();
+ validateTargetEnemy(currentCamp, nextPosition);
+ validateIsKing(nextPosition);
+ }
+
+ private void validateIsKing(final Position nextPosition) {
+ if (!pieces.containsKey(nextPosition)) {
+ return;
+ }
+ final Piece piece = pieces.get(nextPosition);
+ if (KINGS.contains(piece)) {
+ throw new KingDeadException(ErrorCode.KING_DEAD);
+ }
+ }
+
+ private void validateExistPiece(final Position currentPosition) {
+ if (!pieces.containsKey(currentPosition)) {
+ throw new PieceDoesNotExistException(ErrorCode.PIECE_DOES_NOT_EXIST_POSITION);
+ }
+ }
+
+ private void validateOwnPiece(final Camp currentCamp, final Piece piece) {
+ if (!piece.isSameCamp(currentCamp)) {
+ throw new InvalidTurnException(ErrorCode.INVALID_CAMP_PIECE);
+ }
+ }
+
+ private void validateUnBlocked(final Set route) {
+ boolean blocked = route.stream()
+ .anyMatch(pieces::containsKey);
+ if (blocked) {
+ throw new PieceExistInRouteException(ErrorCode.PIECE_EXIST_IN_ROUTE);
+ }
+ }
+
+ private void validateTargetEnemy(final Camp currentCamp, final Position nextPosition) {
+ if (pieces.containsKey(nextPosition) && pieces.get(nextPosition).isSameCamp(currentCamp)) {
+ throw new PieceExistInRouteException(ErrorCode.OWN_PIECE_EXIST_POSITION);
+ }
+ }
+
+ private Set getRoute(final Moving moving, final Piece piece) {
+ if (pieces.containsKey(moving.getNextPosition())) {
+ return piece.getAttackRoute(moving);
+ }
+ return piece.getMoveRoute(moving);
+ }
+
+ public void move(final Moving moving) {
+ final Piece piece = pieces.get(moving.getCurrentPosition());
+ pieces.put(moving.getNextPosition(), piece);
+ pieces.remove(moving.getCurrentPosition());
+ }
+
+ public Score calculateScore(final Camp camp) {
+ final Map pawnCount = countSameFilePawn(camp);
+ final List scores = collectScore(camp);
+ final Score result = scores.stream()
+ .reduce(Score::plus)
+ .orElse(new Score(0));
+
+ return result.minus(duplicateFilePawns(pawnCount));
+ }
+
+ private List collectScore(final Camp camp) {
+ return pieces.values()
+ .stream()
+ .filter(piece -> piece.isSameCamp(camp))
+ .map(PieceScore::getScore)
+ .toList();
+ }
+
+ private Map countSameFilePawn(final Camp camp) {
+ final Map pawnCount = new EnumMap<>(File.class);
+ pieces.entrySet()
+ .stream()
+ .filter(entry -> entry.getValue().isSameCamp(camp))
+ .forEach(entry -> checkPawn(entry, pawnCount));
+ return pawnCount;
+ }
+
+ private Score duplicateFilePawns(final Map count) {
+ return count.values()
+ .stream()
+ .filter(sameFilePawnCount -> sameFilePawnCount > 1)
+ .map(sameFilePawnCount -> new Score(sameFilePawnCount * SAME_FILE_PAWN_SCORE.value()))
+ .reduce(Score::plus)
+ .orElse(new Score(0));
+ }
+
+ private void checkPawn(final Entry entry, final Map count) {
+ final Piece piece = entry.getValue();
+ if (PAWNS.contains(piece)) {
+ final Position position = entry.getKey();
+ final File file = position.getFile();
+ count.put(file, count.getOrDefault(file, 0) + 1);
+ }
+ }
+
+ public Map getPieces() {
+ return Collections.unmodifiableMap(pieces);
+ }
+}
diff --git a/src/main/java/model/ChessGame.java b/src/main/java/model/ChessGame.java
new file mode 100644
index 00000000000..94954dc9482
--- /dev/null
+++ b/src/main/java/model/ChessGame.java
@@ -0,0 +1,47 @@
+package model;
+
+import java.util.Map;
+import model.piece.Piece;
+import model.position.Moving;
+import model.position.Position;
+
+public class ChessGame {
+
+ private final Board board;
+ private final GameTurn gameTurn;
+
+ public ChessGame(final Board board, final GameTurn gameTurn) {
+ this.board = board;
+ this.gameTurn = gameTurn;
+ }
+
+ public static ChessGame setupStartingPosition() {
+ return new ChessGame(Board.create(), GameTurn.create());
+ }
+
+ public void move(final Moving moving) {
+ board.validate(moving, getCamp());
+ board.move(moving);
+ gameTurn.progress();
+ }
+
+ public Score calculateScore(final Camp camp) {
+ return board.calculateScore(camp);
+ }
+
+ public Map getPieces() {
+ return board.getPieces();
+ }
+
+ public Camp getCamp() {
+ return gameTurn.getCamp();
+ }
+
+ public Turn getTurn() {
+ return gameTurn.getTurn();
+ }
+
+ public Board getBoard() {
+ return board;
+ }
+}
diff --git a/src/main/java/model/GameTurn.java b/src/main/java/model/GameTurn.java
new file mode 100644
index 00000000000..32928eae461
--- /dev/null
+++ b/src/main/java/model/GameTurn.java
@@ -0,0 +1,36 @@
+package model;
+
+public class GameTurn {
+
+ private static final Camp STARTING_CAMP = Camp.WHITE;
+
+ private Camp camp;
+ private Turn turn;
+
+ private GameTurn(final Camp camp) {
+ this.camp = camp;
+ this.turn = new Turn(0);
+ }
+
+ public GameTurn(final Camp camp, final Turn turn) {
+ this.camp = camp;
+ this.turn = turn;
+ }
+
+ public static GameTurn create() {
+ return new GameTurn(STARTING_CAMP);
+ }
+
+ public void progress() {
+ camp = camp.toggle();
+ turn = turn.take();
+ }
+
+ public Camp getCamp() {
+ return camp;
+ }
+
+ public Turn getTurn() {
+ return turn;
+ }
+}
diff --git a/src/main/java/model/PieceScore.java b/src/main/java/model/PieceScore.java
new file mode 100644
index 00000000000..5fa3a51e0b0
--- /dev/null
+++ b/src/main/java/model/PieceScore.java
@@ -0,0 +1,36 @@
+package model;
+
+import java.util.Arrays;
+import model.piece.Bishop;
+import model.piece.King;
+import model.piece.Knight;
+import model.piece.Pawn;
+import model.piece.Piece;
+import model.piece.Queen;
+import model.piece.Rook;
+
+public enum PieceScore {
+
+ KING(King.class, new Score(0)),
+ QUEEN(Queen.class, new Score(9)),
+ ROOK(Rook.class, new Score(5)),
+ BISHOP(Bishop.class, new Score(3)),
+ KNIGHT(Knight.class, new Score(2.5F)),
+ PAWN(Pawn.class, new Score(1));
+
+ private final Class extends Piece> clazz;
+ private final Score score;
+
+ PieceScore(final Class extends Piece> clazz, final Score score) {
+ this.clazz = clazz;
+ this.score = score;
+ }
+
+ public static Score getScore(final Piece piece) {
+ return Arrays.stream(values())
+ .filter(pieceScore -> (pieceScore.clazz.isInstance(piece)))
+ .map(pieceScore -> pieceScore.score)
+ .findFirst()
+ .orElseThrow();
+ }
+}
diff --git a/src/main/java/model/Score.java b/src/main/java/model/Score.java
new file mode 100644
index 00000000000..8ea41d6dbd1
--- /dev/null
+++ b/src/main/java/model/Score.java
@@ -0,0 +1,12 @@
+package model;
+
+public record Score(float value) {
+
+ public Score plus(final Score target) {
+ return new Score(value + target.value);
+ }
+
+ public Score minus(final Score target) {
+ return new Score(value - target.value);
+ }
+}
diff --git a/src/main/java/model/Turn.java b/src/main/java/model/Turn.java
new file mode 100644
index 00000000000..179a2a85031
--- /dev/null
+++ b/src/main/java/model/Turn.java
@@ -0,0 +1,7 @@
+package model;
+
+public record Turn(int count) {
+ public Turn take() {
+ return new Turn(count + 1);
+ }
+}
diff --git a/src/main/java/model/command/Command.java b/src/main/java/model/command/Command.java
new file mode 100644
index 00000000000..c12e5e29ece
--- /dev/null
+++ b/src/main/java/model/command/Command.java
@@ -0,0 +1,35 @@
+package model.command;
+
+import constant.ErrorCode;
+import exception.InvalidCommandException;
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+public enum Command {
+
+ START(Pattern.compile("start"), 0),
+ MOVE(Pattern.compile("move"), 2),
+ POSITION(Pattern.compile("[a-hA-H][1-8]"), 0),
+ STATUS(Pattern.compile("status"), 0),
+ QUIT(Pattern.compile("quit"), 0),
+ END(Pattern.compile("end"), 0);
+
+ private final Pattern pattern;
+ private final int bodySize;
+
+ Command(final Pattern pattern, final int bodySize) {
+ this.pattern = pattern;
+ this.bodySize = bodySize;
+ }
+
+ public static Command from(String value) {
+ return Arrays.stream(values())
+ .filter(command -> command.pattern.matcher(value).matches())
+ .findFirst()
+ .orElseThrow(() -> new InvalidCommandException(ErrorCode.INVALID_COMMAND));
+ }
+
+ public boolean isEqualToBodySize(int targetSize) {
+ return bodySize == targetSize;
+ }
+}
diff --git a/src/main/java/model/command/CommandLine.java b/src/main/java/model/command/CommandLine.java
new file mode 100644
index 00000000000..b4e91556b53
--- /dev/null
+++ b/src/main/java/model/command/CommandLine.java
@@ -0,0 +1,69 @@
+package model.command;
+
+import constant.ErrorCode;
+import exception.InvalidCommandException;
+import java.util.Collections;
+import java.util.List;
+
+public class CommandLine {
+
+ public static final int HEAD_INDEX = 0;
+ public static final int CURRENT_POSITION_INDEX = 0;
+ public static final int NEXT_POSITION_INDEX = 1;
+
+ private final Command head;
+ private final List body;
+
+ private CommandLine(final Command head, final List body) {
+ this.head = head;
+ this.body = body;
+ }
+
+ public static CommandLine from(final List input) {
+ validateEmpty(input);
+ validateCommand(input);
+ Command command = Command.from(input.get(HEAD_INDEX));
+ validateSize(command, input);
+ return new CommandLine(command, input.subList(1, input.size()));
+ }
+
+ private static void validateEmpty(final List input) {
+ if (input == null || input.isEmpty()) {
+ throw new InvalidCommandException(ErrorCode.INVALID_COMMAND);
+ }
+ }
+
+ private static void validateCommand(final List input) {
+ input.forEach(Command::from);
+ }
+
+ private static void validateSize(final Command command, final List input) {
+ if (!command.isEqualToBodySize(input.size() - 1)) {
+ throw new InvalidCommandException(ErrorCode.INVALID_COMMAND);
+ }
+ }
+
+ public boolean isStart() {
+ return head == Command.START;
+ }
+
+ public boolean isEnd() {
+ return head == Command.END;
+ }
+
+ public boolean isMove() {
+ return head == Command.MOVE;
+ }
+
+ public boolean isStatus() {
+ return head == Command.STATUS;
+ }
+
+ public boolean isQuit() {
+ return head == Command.QUIT;
+ }
+
+ public List getBody() {
+ return Collections.unmodifiableList(body);
+ }
+}
diff --git a/src/main/java/model/piece/Bishop.java b/src/main/java/model/piece/Bishop.java
index 5f27aa3d84c..8532a32ed5f 100644
--- a/src/main/java/model/piece/Bishop.java
+++ b/src/main/java/model/piece/Bishop.java
@@ -6,7 +6,6 @@
import model.Camp;
import model.position.Moving;
import model.position.Position;
-import view.message.PieceType;
public class Bishop extends Piece {
@@ -19,7 +18,7 @@ public Set getMoveRoute(final Moving moving) {
if (canMovable(moving)) {
return moving.route();
}
- throw new InvalidMovingException(ErrorCode.INVALID_MOVEMENT_RULE);
+ throw new InvalidMovingException(ErrorCode.INVALID_BISHOP_MOVEMENT);
}
@Override
@@ -29,9 +28,4 @@ protected boolean canMovable(final Moving moving) {
}
return moving.isDiagonal();
}
-
- @Override
- public String toString() {
- return PieceType.from(this).getValue();
- }
}
diff --git a/src/main/java/model/piece/BlackPawn.java b/src/main/java/model/piece/BlackPawn.java
new file mode 100644
index 00000000000..b9d7014f8f1
--- /dev/null
+++ b/src/main/java/model/piece/BlackPawn.java
@@ -0,0 +1,42 @@
+package model.piece;
+
+import java.util.Set;
+import model.Camp;
+import model.position.Position;
+import model.position.Rank;
+
+public class BlackPawn extends Pawn {
+
+ public BlackPawn() {
+ super(Camp.BLACK);
+ }
+
+ @Override
+ protected Set twoMovedRoute(final Position currentPosition) {
+ return Set.of(new Position(currentPosition.getFile(), Rank.SIX));
+ }
+
+ @Override
+ protected boolean isStraight(final Position currentPosition, final int differenceRank, final int differenceFile) {
+ if (differenceFile != 0) {
+ return false;
+ }
+ if (Rank.SEVEN.getIndex() == currentPosition.getRankIndex() && differenceRank == -2) {
+ return true;
+ }
+ return differenceRank == -1;
+ }
+
+ @Override
+ protected boolean isDiagonal(final int differenceRank, final int differenceFile) {
+ if (Math.abs(differenceFile) != 1) {
+ return false;
+ }
+ return differenceRank == -1;
+ }
+
+ @Override
+ public Camp getCamp() {
+ return Camp.BLACK;
+ }
+}
diff --git a/src/main/java/model/piece/King.java b/src/main/java/model/piece/King.java
index 417937bb8b5..1d39e16b546 100644
--- a/src/main/java/model/piece/King.java
+++ b/src/main/java/model/piece/King.java
@@ -6,7 +6,6 @@
import model.Camp;
import model.position.Moving;
import model.position.Position;
-import view.message.PieceType;
public class King extends Piece {
@@ -19,7 +18,7 @@ public Set getMoveRoute(final Moving moving) {
if (canMovable(moving)) {
return Set.of();
}
- throw new InvalidMovingException(ErrorCode.INVALID_MOVEMENT_RULE);
+ throw new InvalidMovingException(ErrorCode.INVALID_KING_MOVEMENT);
}
@Override
@@ -29,9 +28,4 @@ protected boolean canMovable(final Moving moving) {
}
return moving.isAdjacent();
}
-
- @Override
- public String toString() {
- return PieceType.from(this).getValue();
- }
}
diff --git a/src/main/java/model/piece/Knight.java b/src/main/java/model/piece/Knight.java
index 5f21116885c..c4ef48c33ec 100644
--- a/src/main/java/model/piece/Knight.java
+++ b/src/main/java/model/piece/Knight.java
@@ -6,7 +6,6 @@
import model.Camp;
import model.position.Moving;
import model.position.Position;
-import view.message.PieceType;
public class Knight extends Piece {
@@ -19,7 +18,7 @@ public Set getMoveRoute(final Moving moving) {
if (canMovable(moving)) {
return Set.of();
}
- throw new InvalidMovingException(ErrorCode.INVALID_MOVEMENT_RULE);
+ throw new InvalidMovingException(ErrorCode.INVALID_KNIGHT_MOVEMENT);
}
@Override
@@ -29,9 +28,4 @@ protected boolean canMovable(final Moving moving) {
}
return moving.isShapeCapitalL();
}
-
- @Override
- public String toString() {
- return PieceType.from(this).getValue();
- }
}
diff --git a/src/main/java/model/piece/Pawn.java b/src/main/java/model/piece/Pawn.java
index 3644f4fbf22..d1a5a5ce2a4 100644
--- a/src/main/java/model/piece/Pawn.java
+++ b/src/main/java/model/piece/Pawn.java
@@ -6,40 +6,24 @@
import model.Camp;
import model.position.Moving;
import model.position.Position;
-import model.position.Rank;
-import view.message.PieceType;
-public class Pawn extends Piece {
+public abstract class Pawn extends Piece {
- public Pawn(final Camp camp) {
+ protected Pawn(final Camp camp) {
super(camp);
}
- @Override
- public Set getMoveRoute(final Moving moving) {
- if (!canMovable(moving)) {
- throw new InvalidMovingException(ErrorCode.INVALID_MOVEMENT_RULE);
- }
- final Position currentPosition = moving.getCurrentPosition();
- final Position nextPosition = moving.getNextPosition();
+ protected abstract boolean isDiagonal(final int differenceRank, final int differenceFile);
- if (Math.abs(nextPosition.getRankIndex() - currentPosition.getRankIndex()) == 1) {
- return Set.of();
- }
- return getTwoStraightRoute(currentPosition);
- }
+ protected abstract boolean isStraight(final Position currentPosition,
+ final int differenceRank,
+ final int differenceFile);
- private Set getTwoStraightRoute(final Position currentPosition) {
- if (Camp.BLACK == camp) {
- return Set.of(new Position(currentPosition.getFile(), Rank.SIX));
- }
- return Set.of(new Position(currentPosition.getFile(), Rank.THREE));
- }
+ protected abstract Set twoMovedRoute(final Position currentPosition);
- @Override
- protected boolean canMovable(final Moving moving) {
+ protected boolean canAttack(final Moving moving) {
if (moving.isNotMoved()) {
- return false;
+ return true;
}
final Position currentPosition = moving.getCurrentPosition();
final Position nextPosition = moving.getNextPosition();
@@ -47,38 +31,11 @@ protected boolean canMovable(final Moving moving) {
final int differenceRank = currentPosition.getRankIndex() - nextPosition.getRankIndex();
final int differenceFile = currentPosition.getFileIndex() - nextPosition.getFileIndex();
- return isStraight(currentPosition, differenceRank, differenceFile);
- }
-
- private boolean isStraight(final Position currentPosition, final int differenceRank, final int differenceFile) {
- if (differenceFile != 0) {
- return false;
- }
- if (Camp.BLACK == camp) {
- return isBlackTwoStraight(currentPosition, differenceRank);
- }
- if (Rank.TWO.getIndex() == currentPosition.getRankIndex() && differenceRank == 2) {
- return true;
- }
- return differenceRank == 1;
- }
-
- private boolean isBlackTwoStraight(final Position currentPosition, final int differenceRank) {
- if (Rank.SEVEN.getIndex() == currentPosition.getRankIndex() && differenceRank == -2) {
- return true;
- }
- return differenceRank == -1;
+ return !isDiagonal(differenceRank, differenceFile);
}
@Override
- public Set getAttackRoute(final Moving moving) {
- if (!canAttack(moving)) {
- throw new InvalidMovingException(ErrorCode.INVALID_MOVEMENT_RULE);
- }
- return Set.of();
- }
-
- private boolean canAttack(final Moving moving) {
+ protected boolean canMovable(final Moving moving) {
if (moving.isNotMoved()) {
return false;
}
@@ -88,21 +45,28 @@ private boolean canAttack(final Moving moving) {
final int differenceRank = currentPosition.getRankIndex() - nextPosition.getRankIndex();
final int differenceFile = currentPosition.getFileIndex() - nextPosition.getFileIndex();
- return isDiagonal(differenceRank, differenceFile);
+ return isStraight(currentPosition, differenceRank, differenceFile);
}
- private boolean isDiagonal(final int differenceRank, final int differenceFile) {
- if (Math.abs(differenceFile) != 1) {
- return false;
+ @Override
+ public Set getMoveRoute(final Moving moving) {
+ if (!canMovable(moving)) {
+ throw new InvalidMovingException(ErrorCode.INVALID_PAWN_MOVEMENT);
}
- if (Camp.BLACK == camp) {
- return differenceRank == -1;
+ final Position currentPosition = moving.getCurrentPosition();
+ final Position nextPosition = moving.getNextPosition();
+
+ if (Math.abs(nextPosition.getRankIndex() - currentPosition.getRankIndex()) == 1) {
+ return Set.of();
}
- return differenceRank == 1;
+ return twoMovedRoute(currentPosition);
}
@Override
- public String toString() {
- return PieceType.from(this).getValue();
+ public Set getAttackRoute(final Moving moving) {
+ if (canAttack(moving)) {
+ throw new InvalidMovingException(ErrorCode.INVALID_PAWN_MOVEMENT);
+ }
+ return Set.of();
}
}
diff --git a/src/main/java/model/piece/Piece.java b/src/main/java/model/piece/Piece.java
index 65faa851034..d12622068ff 100644
--- a/src/main/java/model/piece/Piece.java
+++ b/src/main/java/model/piece/Piece.java
@@ -8,7 +8,7 @@
public abstract class Piece {
- protected final Camp camp;
+ private final Camp camp;
protected Piece(final Camp camp) {
this.camp = camp;
diff --git a/src/main/java/model/piece/Queen.java b/src/main/java/model/piece/Queen.java
index 5a329fd31e0..62c95b20b26 100644
--- a/src/main/java/model/piece/Queen.java
+++ b/src/main/java/model/piece/Queen.java
@@ -6,7 +6,6 @@
import model.Camp;
import model.position.Moving;
import model.position.Position;
-import view.message.PieceType;
public class Queen extends Piece {
@@ -19,7 +18,7 @@ public Set getMoveRoute(final Moving moving) {
if (canMovable(moving)) {
return moving.route();
}
- throw new InvalidMovingException(ErrorCode.INVALID_MOVEMENT_RULE);
+ throw new InvalidMovingException(ErrorCode.INVALID_QUEEN_MOVEMENT);
}
@Override
@@ -29,9 +28,4 @@ protected boolean canMovable(final Moving moving) {
}
return moving.isDiagonal() || moving.isVertical() || moving.isHorizontal();
}
-
- @Override
- public String toString() {
- return PieceType.from(this).getValue();
- }
}
diff --git a/src/main/java/model/piece/Rook.java b/src/main/java/model/piece/Rook.java
index 2f47c751fd8..67fc56dbe08 100644
--- a/src/main/java/model/piece/Rook.java
+++ b/src/main/java/model/piece/Rook.java
@@ -6,7 +6,6 @@
import model.Camp;
import model.position.Moving;
import model.position.Position;
-import view.message.PieceType;
public class Rook extends Piece {
@@ -19,7 +18,7 @@ public Set getMoveRoute(final Moving moving) {
if (canMovable(moving)) {
return moving.route();
}
- throw new InvalidMovingException(ErrorCode.INVALID_MOVEMENT_RULE);
+ throw new InvalidMovingException(ErrorCode.INVALID_ROOK_MOVEMENT);
}
@Override
@@ -29,9 +28,4 @@ protected boolean canMovable(final Moving moving) {
}
return moving.isHorizontal() || moving.isVertical();
}
-
- @Override
- public String toString() {
- return PieceType.from(this).getValue();
- }
}
diff --git a/src/main/java/model/piece/WhitePawn.java b/src/main/java/model/piece/WhitePawn.java
new file mode 100644
index 00000000000..941206ea317
--- /dev/null
+++ b/src/main/java/model/piece/WhitePawn.java
@@ -0,0 +1,42 @@
+package model.piece;
+
+import java.util.Set;
+import model.Camp;
+import model.position.Position;
+import model.position.Rank;
+
+public class WhitePawn extends Pawn {
+
+ public WhitePawn() {
+ super(Camp.WHITE);
+ }
+
+ @Override
+ protected Set twoMovedRoute(final Position currentPosition) {
+ return Set.of(new Position(currentPosition.getFile(), Rank.THREE));
+ }
+
+ @Override
+ protected boolean isStraight(final Position currentPosition, final int differenceRank, final int differenceFile) {
+ if (differenceFile != 0) {
+ return false;
+ }
+ if (Rank.TWO.getIndex() == currentPosition.getRankIndex() && differenceRank == 2) {
+ return true;
+ }
+ return differenceRank == 1;
+ }
+
+ @Override
+ protected boolean isDiagonal(final int differenceRank, final int differenceFile) {
+ if (Math.abs(differenceFile) != 1) {
+ return false;
+ }
+ return differenceRank == 1;
+ }
+
+ @Override
+ public Camp getCamp() {
+ return Camp.WHITE;
+ }
+}
diff --git a/src/main/java/model/position/Position.java b/src/main/java/model/position/Position.java
index 5b753986aaf..2d2ba7026f1 100644
--- a/src/main/java/model/position/Position.java
+++ b/src/main/java/model/position/Position.java
@@ -60,4 +60,5 @@ public boolean equals(final Object target) {
public String toString() {
return file.getValue() + rank.getValue();
}
+
}
diff --git a/src/main/java/model/status/Checkmate.java b/src/main/java/model/status/Checkmate.java
new file mode 100644
index 00000000000..e9e2e69a9f0
--- /dev/null
+++ b/src/main/java/model/status/Checkmate.java
@@ -0,0 +1,24 @@
+package model.status;
+
+import constant.ErrorCode;
+import exception.InvalidStatusException;
+import model.ChessGame;
+import model.command.CommandLine;
+
+public class Checkmate implements GameStatus {
+
+ @Override
+ public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) {
+ throw new InvalidStatusException(ErrorCode.INVALID_STATUS);
+ }
+
+ @Override
+ public boolean isRunning() {
+ return false;
+ }
+
+ @Override
+ public boolean isCheck() {
+ return true;
+ }
+}
diff --git a/src/main/java/model/status/End.java b/src/main/java/model/status/End.java
index 3c8f7287deb..db30851e915 100644
--- a/src/main/java/model/status/End.java
+++ b/src/main/java/model/status/End.java
@@ -2,13 +2,13 @@
import constant.ErrorCode;
import exception.InvalidStatusException;
-import java.util.List;
-import model.ChessBoard;
+import model.ChessGame;
+import model.command.CommandLine;
public class End implements GameStatus {
@Override
- public GameStatus play(final List command, final ChessBoard chessBoard) {
+ public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) {
throw new InvalidStatusException(ErrorCode.INVALID_STATUS);
}
@@ -16,4 +16,9 @@ public GameStatus play(final List command, final ChessBoard chessBoard)
public boolean isRunning() {
return false;
}
+
+ @Override
+ public boolean isCheck() {
+ return false;
+ }
}
diff --git a/src/main/java/model/status/GameStatus.java b/src/main/java/model/status/GameStatus.java
index 3d29b87cd1f..5697a39517f 100644
--- a/src/main/java/model/status/GameStatus.java
+++ b/src/main/java/model/status/GameStatus.java
@@ -1,11 +1,17 @@
package model.status;
-import java.util.List;
-import model.ChessBoard;
+import model.ChessGame;
+import model.command.CommandLine;
public interface GameStatus {
- GameStatus play(List command, ChessBoard chessBoard);
+ GameStatus play(final CommandLine commandLine, final ChessGame chessGame);
boolean isRunning();
+
+ boolean isCheck();
+
+ default boolean isQuit() {
+ return false;
+ }
}
diff --git a/src/main/java/model/status/Quit.java b/src/main/java/model/status/Quit.java
new file mode 100644
index 00000000000..bfc3601d26b
--- /dev/null
+++ b/src/main/java/model/status/Quit.java
@@ -0,0 +1,29 @@
+package model.status;
+
+import constant.ErrorCode;
+import exception.InvalidStatusException;
+import model.ChessGame;
+import model.command.CommandLine;
+
+public class Quit implements GameStatus {
+
+ @Override
+ public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) {
+ throw new InvalidStatusException(ErrorCode.INVALID_STATUS);
+ }
+
+ @Override
+ public boolean isRunning() {
+ return false;
+ }
+
+ @Override
+ public boolean isCheck() {
+ return false;
+ }
+
+ @Override
+ public boolean isQuit() {
+ return true;
+ }
+}
diff --git a/src/main/java/model/status/Running.java b/src/main/java/model/status/Running.java
index f0c2a88fa59..4a29fd9a3f3 100644
--- a/src/main/java/model/status/Running.java
+++ b/src/main/java/model/status/Running.java
@@ -2,35 +2,56 @@
import constant.ErrorCode;
import exception.InvalidStatusException;
+import exception.KingDeadException;
import java.util.List;
-import model.ChessBoard;
-import model.Command;
+import model.ChessGame;
+import model.command.CommandLine;
import model.position.Moving;
import model.position.Position;
public class Running implements GameStatus {
@Override
- public GameStatus play(final List command, final ChessBoard chessBoard) {
- final Command cmd = Command.from(command.get(Command.HEAD_INDEX));
- if (cmd == Command.END && cmd.isAvailableSize(command.size())) {
+ public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) {
+ if (commandLine.isEnd()) {
return new End();
}
- if (cmd == Command.MOVE && cmd.isAvailableSize(command.size())) {
- final Moving moving = convert(command);
- chessBoard.move(moving);
+ if (commandLine.isMove()) {
+ return status(commandLine, chessGame);
+ }
+ if (commandLine.isStatus()) {
return new Running();
}
+ if (commandLine.isQuit()) {
+ return new Quit();
+ }
throw new InvalidStatusException(ErrorCode.INVALID_STATUS);
}
+ private GameStatus status(final CommandLine commandLine, final ChessGame chessGame) {
+ final Moving moving = convert(commandLine.getBody());
+ try {
+ chessGame.move(moving);
+ return new Running();
+ } catch (KingDeadException exception) {
+ return new Checkmate();
+ }
+ }
+
private Moving convert(final List command) {
- return new Moving(Position.from(command.get(Command.CURRENT_INDEX)),
- Position.from(command.get(Command.NEXT_INDEX)));
+ final String currentPosition = command.get(CommandLine.CURRENT_POSITION_INDEX);
+ final String nextPosition = command.get(CommandLine.NEXT_POSITION_INDEX);
+
+ return new Moving(Position.from(currentPosition), Position.from(nextPosition));
}
@Override
public boolean isRunning() {
return true;
}
+
+ @Override
+ public boolean isCheck() {
+ return false;
+ }
}
diff --git a/src/main/java/model/status/StatusFactory.java b/src/main/java/model/status/StatusFactory.java
new file mode 100644
index 00000000000..a11f624b57e
--- /dev/null
+++ b/src/main/java/model/status/StatusFactory.java
@@ -0,0 +1,24 @@
+package model.status;
+
+import constant.ErrorCode;
+import exception.InvalidStatusException;
+import model.command.CommandLine;
+
+public class StatusFactory {
+
+ private StatusFactory() {
+ }
+
+ public static GameStatus create(final CommandLine commandLine) {
+ if (commandLine.isStart()) {
+ return new Running();
+ }
+ if (commandLine.isEnd()) {
+ return new End();
+ }
+ if (commandLine.isQuit()) {
+ return new Quit();
+ }
+ throw new InvalidStatusException(ErrorCode.INVALID_STATUS);
+ }
+}
diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java
index f5884cad739..d09ad38c2ff 100644
--- a/src/main/java/view/OutputView.java
+++ b/src/main/java/view/OutputView.java
@@ -2,7 +2,7 @@
import constant.ErrorCode;
import dto.ChessBoardDto;
-import model.Camp;
+import dto.ScoreDto;
import view.message.ErrorCodeMessage;
public class OutputView {
@@ -10,19 +10,27 @@ public class OutputView {
public void printStartMessage() {
System.out.println("> 체스 게임을 시작합니다.");
System.out.println("> 게임 시작 : start");
- System.out.println("> 게임 종료 : end");
+ System.out.println("> 게임 저장 후 종료 : end");
+ System.out.println("> 게임 저장 하지 않고 종료 : quit");
System.out.println("> 게임 이동 : move source위치 target위치 - 예. move b2 b3");
+ System.out.println("> 게임 현황 : status");
}
- public void printChessBoard(ChessBoardDto chessBoardDto) {
- System.out.printf(chessBoardDto.getValue());
+ public void printChessBoard(final ChessBoardDto chessBoardDto) {
+ System.out.printf(chessBoardDto.getBoard());
+ System.out.printf("현재 턴: %s%n%n", chessBoardDto.getCurrentTurn());
+ }
+
+ public void printScore(final ScoreDto scoreDto) {
+ System.out.printf("BLACK 점수: %s%n", scoreDto.getBlackScore());
+ System.out.printf("WHITE 점수: %s%n", scoreDto.getWhiteScore());
}
public void printException(final ErrorCode errorCode) {
System.out.printf("[ERROR] %s%n", ErrorCodeMessage.from(errorCode).getMessage());
}
- public void printCamp(final Camp camp) {
- System.out.printf("현재 턴: %s%n%n", camp.toString());
+ public void printWinner(final String camp) {
+ System.out.printf("%s 승리%n", camp);
}
}
diff --git a/src/main/java/view/message/ErrorCodeMessage.java b/src/main/java/view/message/ErrorCodeMessage.java
index 3844ba29233..44d5d3934ff 100644
--- a/src/main/java/view/message/ErrorCodeMessage.java
+++ b/src/main/java/view/message/ErrorCodeMessage.java
@@ -13,10 +13,21 @@ public enum ErrorCodeMessage {
INVALID_STATUS(ErrorCode.INVALID_STATUS, "유효하지 않은 상태입니다."),
INVALID_COMMAND(ErrorCode.INVALID_COMMAND, "유효하지 않은 명령입니다."),
INVALID_POSITION(ErrorCode.INVALID_POSITION, "유효하지 않은 위치입니다."),
- INVALID_MOVEMENT_RULE(ErrorCode.INVALID_MOVEMENT_RULE, "이동할 수 없는 위치입니다."),
+ INVALID_PAWN_MOVEMENT(ErrorCode.INVALID_PAWN_MOVEMENT, "유효하지 않은 폰의 이동 경로입니다."),
+ INVALID_KING_MOVEMENT(ErrorCode.INVALID_KING_MOVEMENT, "유효하지 않은 킹의 이동 경로입니다."),
+ INVALID_BISHOP_MOVEMENT(ErrorCode.INVALID_BISHOP_MOVEMENT, "유효하지 않은 비숍의 이동 경로입니다."),
+ INVALID_KNIGHT_MOVEMENT(ErrorCode.INVALID_KNIGHT_MOVEMENT, "유효하지 않은 나이트의 이동 경로입니다."),
+ INVALID_QUEEN_MOVEMENT(ErrorCode.INVALID_QUEEN_MOVEMENT, "유효하지 않은 퀸의 이동 경로입니다."),
+ INVALID_ROOK_MOVEMENT(ErrorCode.INVALID_ROOK_MOVEMENT, "유효하지 않은 룩의 이동 경로입니다."),
PIECE_EXIST_IN_ROUTE(ErrorCode.PIECE_EXIST_IN_ROUTE, "이동 경로에 기물이 있습니다."),
+ OWN_PIECE_EXIST_POSITION(ErrorCode.OWN_PIECE_EXIST_POSITION, "도착 위치에 자신의 기물이 있습니다."),
PIECE_DOES_NOT_EXIST_POSITION(ErrorCode.PIECE_DOES_NOT_EXIST_POSITION, "해당 위치에 기물이 없습니다."),
INVALID_CAMP_PIECE(ErrorCode.INVALID_CAMP_PIECE, "자신의 기물만 움직일 수 있습니다."),
+ CONNECTION(ErrorCode.CONNECTION, "DB 연결 오류 입니다."),
+ FAIL_SAVE(ErrorCode.FAIL_SAVE, "저장에 실패하였습니다."),
+ FAIL_FIND(ErrorCode.FAIL_FIND, "게임을 불러오는데 실패하였습니다."),
+ FAIL_DELETE(ErrorCode.FAIL_DELETE, "게임을 삭제하는데 실패하였습니다."),
+
NO_MESSAGE(ErrorCode.NO_MESSAGE, "해당 메시지가 없습니다.");
private static final Map SUIT_MESSAGE = Arrays.stream(values())
diff --git a/src/main/java/view/message/PieceType.java b/src/main/java/view/message/PieceType.java
index 2d0b602c226..dd2ee1bc4dc 100644
--- a/src/main/java/view/message/PieceType.java
+++ b/src/main/java/view/message/PieceType.java
@@ -8,12 +8,13 @@
import java.util.stream.Collectors;
import model.Camp;
import model.piece.Bishop;
+import model.piece.BlackPawn;
import model.piece.King;
import model.piece.Knight;
-import model.piece.Pawn;
import model.piece.Piece;
import model.piece.Queen;
import model.piece.Rook;
+import model.piece.WhitePawn;
public enum PieceType {
@@ -23,8 +24,8 @@ public enum PieceType {
KING_WHITE(new King(Camp.WHITE), "k"),
KNIGHT_BLACK(new Knight(Camp.BLACK), "N"),
KNIGHT_WHITE(new Knight(Camp.WHITE), "n"),
- PAWN_BLACK(new Pawn(Camp.BLACK), "P"),
- PAWN_WHITE(new Pawn(Camp.WHITE), "p"),
+ PAWN_BLACK(new BlackPawn(), "P"),
+ PAWN_WHITE(new WhitePawn(), "p"),
QUEEN_BLACK(new Queen(Camp.BLACK), "Q"),
QUEEN_WHITE(new Queen(Camp.WHITE), "q"),
ROOK_BLACK(new Rook(Camp.BLACK), "R"),
diff --git a/src/main/resources/board.sql b/src/main/resources/board.sql
new file mode 100644
index 00000000000..7f6154ab713
--- /dev/null
+++ b/src/main/resources/board.sql
@@ -0,0 +1,9 @@
+use chess;
+
+create table board
+(
+ position varchar(2) not null,
+ piece_type varchar(6) not null,
+ camp varchar(5) not null
+
+);
diff --git a/src/main/resources/moving.sql b/src/main/resources/moving.sql
new file mode 100644
index 00000000000..d092cdbd5f8
--- /dev/null
+++ b/src/main/resources/moving.sql
@@ -0,0 +1,9 @@
+use chess;
+
+create table moving
+(
+ movement_id INT primary key auto_increment,
+ camp varchar(5) not null,
+ start varchar(2) not null,
+ destination varchar(2) not null
+);
diff --git a/src/main/resources/turn.sql b/src/main/resources/turn.sql
new file mode 100644
index 00000000000..00e339248b7
--- /dev/null
+++ b/src/main/resources/turn.sql
@@ -0,0 +1,7 @@
+use chess;
+
+create table turn
+(
+ camp varchar(5) not null,
+ count int not null
+);
diff --git a/src/test/java/db/BoardDaoTest.java b/src/test/java/db/BoardDaoTest.java
new file mode 100644
index 00000000000..0793e5b0052
--- /dev/null
+++ b/src/test/java/db/BoardDaoTest.java
@@ -0,0 +1,47 @@
+package db;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import db.dto.BoardDto;
+import model.Board;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class BoardDaoTest {
+
+ private final BoardDao boardDao = new BoardDao("chess_test");
+
+ @BeforeEach
+ void beforeEach() {
+ boardDao.remove();
+ }
+
+ @Test
+ @DisplayName("보드 저장 확인")
+ void addBoard() {
+ //given
+ final BoardDto board = BoardDto.from(Board.create());
+
+ //when
+ boardDao.saveBoard(board);
+ final BoardDto findBoard = boardDao.find();
+
+ //then
+ assertThat(board).isEqualTo(findBoard);
+ }
+
+ @DisplayName("테이블이 비어있다면 새로운 보드를 만든다.")
+ @Test
+ void findBoard() {
+ //given
+ boardDao.remove();
+ final BoardDto expected = BoardDto.from(Board.create());
+
+ //when
+ final BoardDto boardDto = boardDao.find();
+
+ //then
+ assertThat(boardDto).isEqualTo(expected);
+ }
+}
diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java
new file mode 100644
index 00000000000..1e4637fde17
--- /dev/null
+++ b/src/test/java/db/MovingDaoTest.java
@@ -0,0 +1,79 @@
+package db;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import db.connection.DBConnectionUtil;
+import db.dto.MovingDto;
+import db.exception.DaoException;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class MovingDaoTest {
+
+ private final MovingDao movingDao = new MovingDao("chess_test");
+
+ @BeforeEach
+ void beforeEach() {
+ movingDao.remove();
+ }
+
+ @DisplayName("데이터베이스 접속 확인")
+ @Test
+ void connection() throws SQLException {
+ try (final Connection connection = DBConnectionUtil.getConnection("chess_test")) {
+ assertThat(connection).isNotNull();
+ }
+ }
+
+ @Test
+ @DisplayName("이동 저장 확인")
+ void addMoving() {
+ final MovingDto moving = new MovingDto("WHITE", "a2", "a3");
+ final long id = movingDao.addMoving(moving);
+
+ assertThat(movingDao.findByMovementId(id)).isEqualTo(moving);
+ }
+
+ @Test
+ @DisplayName("찾고자 하는 기보가 없으면 예외가 발생한다.")
+ void failFindMoving() {
+ //given
+ movingDao.addMoving(new MovingDto("WHITE", "a2", "a3"));
+ movingDao.addMoving(new MovingDto("BLACK", "h7", "h6"));
+
+ //when then
+ assertThatThrownBy(() -> movingDao.findByMovementId(3))
+ .isInstanceOf(DaoException.class);
+ }
+
+ @Test
+ @DisplayName("행의 개수를 파악한다.")
+ void count() {
+ assertThat(movingDao.countMoving()).isZero();
+ }
+
+ @Test
+ @DisplayName("저장된 기보를 확인한다.")
+ void findAll() {
+ //given
+ final List movingDtos = List.of(
+ new MovingDto("WHITE", "a2", "a3"),
+ new MovingDto("BLACK", "h7", "h6"),
+ new MovingDto("WHITE", "a3", "a4"),
+ new MovingDto("BLACK", "g7", "g6")
+ );
+
+ //when
+ for (MovingDto movingDto : movingDtos) {
+ movingDao.addMoving(movingDto);
+ }
+
+ //then
+ assertThat(movingDao.findAll()).isEqualTo(movingDtos);
+ }
+}
diff --git a/src/test/java/db/RepositoryTest.java b/src/test/java/db/RepositoryTest.java
new file mode 100644
index 00000000000..b313c5968f3
--- /dev/null
+++ b/src/test/java/db/RepositoryTest.java
@@ -0,0 +1,96 @@
+package db;
+
+import static model.Fixtures.A2;
+import static model.Fixtures.A3;
+import static model.Fixtures.G6;
+import static model.Fixtures.G7;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import db.dto.BoardDto;
+import db.dto.MovingDto;
+import db.dto.TurnDto;
+import model.ChessGame;
+import model.position.Moving;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class RepositoryTest {
+
+ private final Repository repository = new Repository("chess_test");
+
+ @BeforeEach
+ void beforeEach() {
+ repository.removeAll();
+ }
+
+ @DisplayName("기보를 저장한다.")
+ @Test
+ void saveMoving() {
+ repository.saveMoving(new MovingDto("WHITE", "a2", "a3"));
+ assertThat(repository.hasGame()).isTrue();
+ }
+
+ @DisplayName("진행된 게입이 없으면 false를 반환한다.")
+ @Test
+ void hasNoGame() {
+ assertThat(repository.hasGame()).isFalse();
+ }
+
+ @DisplayName("저장된 게임이 없을 때 새로운 게임을 만든다.")
+ @Test
+ void createNewChessGame() {
+ final ChessGame game = repository.findGame();
+ final ChessGame expected = ChessGame.setupStartingPosition();
+ assertAll(
+ () -> assertThat(game.getPieces()).isEqualTo(expected.getPieces()),
+ () -> assertThat(game.getCamp()).isEqualTo(expected.getCamp()),
+ () -> assertThat(game.getTurn()).isEqualTo(expected.getTurn())
+ );
+ }
+
+ @Test
+ @DisplayName("기보만 저장됐을 때 기보를 바탕으로 복구한다.")
+ void restore() {
+ //given
+ final ChessGame expected = ChessGame.setupStartingPosition();
+ expected.move(new Moving(A2, A3));
+
+ //when
+ repository.saveMoving(new MovingDto("WHITE", "a2", "a3"));
+ final ChessGame game = repository.findGame();
+
+ //then
+ assertAll(
+ () -> assertThat(game.getPieces()).isEqualTo(expected.getPieces()),
+ () -> assertThat(game.getCamp()).isEqualTo(expected.getCamp()),
+ () -> assertThat(game.getTurn()).isEqualTo(expected.getTurn())
+ );
+ }
+
+ @Test
+ @DisplayName("보드와 턴을 저장한다.")
+ void saveBoardAndTurn() {
+ //given
+ final ChessGame expected = ChessGame.setupStartingPosition();
+ expected.move(new Moving(A2, A3));
+ repository.saveMoving(new MovingDto("WHITE", "a2", "a3"));
+ expected.move(new Moving(G7, G6));
+ repository.saveMoving(new MovingDto("BLACK", "g7", "g6"));
+
+ final BoardDto boardDto = BoardDto.from(expected.getBoard());
+ final TurnDto turnDto = TurnDto.from(expected.getCamp(), expected.getTurn());
+
+ //when
+ repository.save(boardDto, turnDto);
+
+ //then
+ final ChessGame game = repository.findGame();
+ assertAll(
+ () -> assertThat(game.getPieces()).isEqualTo(expected.getPieces()),
+ () -> assertThat(game.getCamp()).isEqualTo(expected.getCamp()),
+ () -> assertThat(game.getTurn()).isEqualTo(expected.getTurn())
+ );
+ }
+}
diff --git a/src/test/java/db/TurnDaoTest.java b/src/test/java/db/TurnDaoTest.java
new file mode 100644
index 00000000000..fb6c8752e25
--- /dev/null
+++ b/src/test/java/db/TurnDaoTest.java
@@ -0,0 +1,33 @@
+package db;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import db.dto.TurnDto;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class TurnDaoTest {
+
+ private final TurnDao turnDao = new TurnDao("chess_test");
+
+ @BeforeEach
+ void beforeEach() {
+ turnDao.remove();
+ }
+
+ @DisplayName("턴 저장 확인")
+ @Test
+ void saveTurn() {
+ //given
+ final TurnDto turnDto = new TurnDto("WHITE", 1);
+ turnDao.saveTurn(turnDto);
+ final TurnDto expected = new TurnDto("WHITE", 1);
+
+ //when
+ final TurnDto turn = turnDao.findTurn();
+
+ //then
+ assertThat(turn).isEqualTo(expected);
+ }
+}
diff --git a/src/test/java/db/dto/CampTypeTest.java b/src/test/java/db/dto/CampTypeTest.java
new file mode 100644
index 00000000000..89ded3b227d
--- /dev/null
+++ b/src/test/java/db/dto/CampTypeTest.java
@@ -0,0 +1,19 @@
+package db.dto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class CampTypeTest {
+
+ @DisplayName("문자로 Camp를 찾는다.")
+ @Test
+ void findByColor() {
+ assertAll(
+ () -> assertThat(CampType.findByColorName("WHITE")).isEqualTo(CampType.WHITE),
+ () -> assertThat(CampType.findByColorName("BLACK")).isEqualTo(CampType.BLACK)
+ );
+ }
+}
diff --git a/src/test/java/model/BoardTest.java b/src/test/java/model/BoardTest.java
new file mode 100644
index 00000000000..f56a16ec92b
--- /dev/null
+++ b/src/test/java/model/BoardTest.java
@@ -0,0 +1,112 @@
+package model;
+
+import static model.Fixtures.A2;
+import static model.Fixtures.A4;
+import static model.Fixtures.B4;
+import static model.Fixtures.B5;
+import static model.Fixtures.B7;
+import static model.Fixtures.B8;
+import static model.Fixtures.C2;
+import static model.Fixtures.C3;
+import static model.Fixtures.C6;
+import static model.Fixtures.F4;
+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 java.util.Map;
+import model.piece.Piece;
+import model.piece.WhitePawn;
+import model.position.Moving;
+import model.position.Position;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class BoardTest {
+
+ @DisplayName("초기 상태의 기물 점수를 계산한다.")
+ @Test
+ void calculateInitBoard() {
+ //given
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+ final Score expected = new Score(38.0F);
+
+ //when then
+ assertAll(
+ () -> assertThat(chessGame.calculateScore(Camp.WHITE)).isEqualTo(expected),
+ () -> assertThat(chessGame.calculateScore(Camp.BLACK)).isEqualTo(expected)
+ );
+ }
+
+ @Test
+ @DisplayName("같은 세로줄에 폰이 있으면 0.5점이다.")
+ void calculateBoardWhenPawnSameFile() {
+ //given
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+ final Score blackExpected = new Score(37.0F);
+ final Score whiteExpected = new Score(37.0F);
+
+ //when
+ chessGame.move(new Moving(A2, A4));
+ chessGame.move(new Moving(B7, B5));
+ chessGame.move(new Moving(A4, B5));
+
+ //when then
+ assertAll(
+ () -> assertThat(chessGame.calculateScore(Camp.WHITE)).isEqualTo(whiteExpected),
+ () -> assertThat(chessGame.calculateScore(Camp.BLACK)).isEqualTo(blackExpected)
+ );
+ }
+
+ @Test
+ @DisplayName("세로 줄에 3개의 폰이 있는 경우")
+ void threePawn() {
+ //given
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+ final Score blackExpected = new Score(34.5F);
+ final Score whiteExpected = new Score(36.5F);
+
+ //when
+ chessGame.move(new Moving(A2, A4));
+ chessGame.move(new Moving(B7, B5));
+ chessGame.move(new Moving(A4, B5));
+ chessGame.move(new Moving(B8, C6));
+ chessGame.move(new Moving(C2, C3));
+ chessGame.move(new Moving(C6, B4));
+ chessGame.move(new Moving(C3, B4));
+
+ /*
+ R.BQKBNR 8
+ P.PPPPPP 7
+ ........ 6
+ .p...... 5
+ .p...... 4
+ ........ 3
+ .p.ppppp 2
+ rnbqkbnr 1
+
+ abcdefgh
+ */
+
+ //then
+ assertAll(
+ () -> assertThat(chessGame.calculateScore(Camp.WHITE)).isEqualTo(whiteExpected),
+ () -> assertThat(chessGame.calculateScore(Camp.BLACK)).isEqualTo(blackExpected)
+ );
+ }
+
+ @Test
+ @DisplayName("getter로 가져온 값을 수정하려고 하면 예외가 발생한다.")
+ void doNotUpdate() {
+ //given
+ final Board board = Board.create();
+ final Piece piece = new WhitePawn();
+
+ //when
+ final Map pieces = board.getPieces();
+
+ //then
+ assertThatThrownBy(() -> pieces.put(F4, piece))
+ .isInstanceOf(UnsupportedOperationException.class);
+ }
+}
diff --git a/src/test/java/model/ChessGameTest.java b/src/test/java/model/ChessGameTest.java
new file mode 100644
index 00000000000..81e0d43b5e0
--- /dev/null
+++ b/src/test/java/model/ChessGameTest.java
@@ -0,0 +1,266 @@
+package model;
+
+import static model.Fixtures.A1;
+import static model.Fixtures.A2;
+import static model.Fixtures.A3;
+import static model.Fixtures.A4;
+import static model.Fixtures.A5;
+import static model.Fixtures.A6;
+import static model.Fixtures.A7;
+import static model.Fixtures.A8;
+import static model.Fixtures.B1;
+import static model.Fixtures.B2;
+import static model.Fixtures.B5;
+import static model.Fixtures.B7;
+import static model.Fixtures.B8;
+import static model.Fixtures.C1;
+import static model.Fixtures.C2;
+import static model.Fixtures.C7;
+import static model.Fixtures.C8;
+import static model.Fixtures.D1;
+import static model.Fixtures.D2;
+import static model.Fixtures.D7;
+import static model.Fixtures.D8;
+import static model.Fixtures.E1;
+import static model.Fixtures.E2;
+import static model.Fixtures.E3;
+import static model.Fixtures.E5;
+import static model.Fixtures.E7;
+import static model.Fixtures.E8;
+import static model.Fixtures.F1;
+import static model.Fixtures.F2;
+import static model.Fixtures.F3;
+import static model.Fixtures.F6;
+import static model.Fixtures.F7;
+import static model.Fixtures.F8;
+import static model.Fixtures.G1;
+import static model.Fixtures.G2;
+import static model.Fixtures.G4;
+import static model.Fixtures.G7;
+import static model.Fixtures.G8;
+import static model.Fixtures.H1;
+import static model.Fixtures.H2;
+import static model.Fixtures.H3;
+import static model.Fixtures.H4;
+import static model.Fixtures.H5;
+import static model.Fixtures.H6;
+import static model.Fixtures.H7;
+import static model.Fixtures.H8;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import exception.InvalidTurnException;
+import exception.KingDeadException;
+import exception.PieceDoesNotExistException;
+import exception.PieceExistInRouteException;
+import java.util.HashMap;
+import java.util.Map;
+import model.piece.Bishop;
+import model.piece.BlackPawn;
+import model.piece.King;
+import model.piece.Knight;
+import model.piece.Piece;
+import model.piece.Queen;
+import model.piece.Rook;
+import model.piece.WhitePawn;
+import model.position.Moving;
+import model.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.ValueSource;
+
+class ChessGameTest {
+
+ @DisplayName("초기에는 32개의 기물이 생성된다.")
+ @Test
+ void checkPiecesCount() {
+ //given && when
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+
+ //then
+ final Map board = chessGame.getPieces();
+ assertThat(board.keySet()).hasSize(32);
+ }
+
+ @DisplayName("기물들의 시작 위치를 확인한다.")
+ @Test
+ void checkStartingPosition() {
+ //given && when
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+
+ final Map board = chessGame.getPieces();
+
+ final Map expected = new HashMap<>();
+
+ // black
+ expected.put(A8, new Rook(Camp.BLACK));
+ expected.put(B8, new Knight(Camp.BLACK));
+ expected.put(C8, new Bishop(Camp.BLACK));
+ expected.put(D8, new Queen(Camp.BLACK));
+ expected.put(E8, new King(Camp.BLACK));
+ expected.put(F8, new Bishop(Camp.BLACK));
+ expected.put(G8, new Knight(Camp.BLACK));
+ expected.put(H8, new Rook(Camp.BLACK));
+ expected.put(A7, new BlackPawn());
+ expected.put(B7, new BlackPawn());
+ expected.put(C7, new BlackPawn());
+ expected.put(D7, new BlackPawn());
+ expected.put(E7, new BlackPawn());
+ expected.put(F7, new BlackPawn());
+ expected.put(G7, new BlackPawn());
+ expected.put(H7, new BlackPawn());
+
+ //white
+ expected.put(A1, new Rook(Camp.WHITE));
+ expected.put(B1, new Knight(Camp.WHITE));
+ expected.put(C1, new Bishop(Camp.WHITE));
+ expected.put(D1, new Queen(Camp.WHITE));
+ expected.put(E1, new King(Camp.WHITE));
+ expected.put(F1, new Bishop(Camp.WHITE));
+ expected.put(G1, new Knight(Camp.WHITE));
+ expected.put(H1, new Rook(Camp.WHITE));
+ expected.put(A2, new WhitePawn());
+ expected.put(B2, new WhitePawn());
+ expected.put(C2, new WhitePawn());
+ expected.put(D2, new WhitePawn());
+ expected.put(E2, new WhitePawn());
+ expected.put(F2, new WhitePawn());
+ expected.put(G2, new WhitePawn());
+ expected.put(H2, new WhitePawn());
+
+ //then
+ assertThat(board).isEqualTo(expected);
+ }
+
+ @DisplayName("해당 위치에 기물이 없는 경우 예외가 발생한다.")
+ @ParameterizedTest
+ @ValueSource(strings = {"a3", "b3", "c3", "d4", "e4", "f5", "g6", "h6"})
+ void failToMoveIfNoPiece(final String currentPosition) {
+ //given
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+
+ final Position position = Position.from(currentPosition);
+
+ final Moving moving = new Moving(position, E5);
+
+ //when & then
+ assertThatThrownBy(() -> chessGame.move(moving))
+ .isInstanceOf(PieceDoesNotExistException.class);
+ }
+
+ @Test
+ @DisplayName("자신의 기물이 아니면 예외가 발생한다.")
+ void failToMoveIfDifferentCamp() {
+ //given
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+
+ //when && then
+ final Moving moving = new Moving(A7, A6);
+
+ assertThatThrownBy(() -> chessGame.move(moving))
+ .isInstanceOf(InvalidTurnException.class);
+ }
+
+ @DisplayName("이동 경로에 기물이 있으면 예외를 발생시킨다.")
+ @Test
+ void failToMoveIfContainPieceInRoute() {
+ //given
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+
+ //when && then
+ final Moving moving = new Moving(A1, A3);
+
+ assertThatThrownBy(() -> chessGame.move(moving))
+ .isInstanceOf(PieceExistInRouteException.class);
+ }
+
+ @DisplayName("도착 지점에 같은 진영의 기물이 있으면 예외를 발생시킨다.")
+ @Test
+ void failToMoveIfContainsPieceInTargetPosition() {
+ //given
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+
+ //when
+ chessGame.move(new Moving(A2, A4)); // WHITE
+ chessGame.move(new Moving(A7, A5)); // BLACK
+
+ //then
+ final Moving moving = new Moving(A1, A4);
+
+ assertThatThrownBy(() -> chessGame.move(moving))
+ .isInstanceOf(PieceExistInRouteException.class);
+ }
+
+ @DisplayName("기물이 잡히면 체스보드에서 제거된다.")
+ @Test
+ void checkRemovePiece() {
+ //given
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+
+ //when
+ chessGame.move(new Moving(A2, A4));
+ chessGame.move(new Moving(B7, B5));
+ chessGame.move(new Moving(A4, B5));
+
+ //then
+ assertThat(chessGame.getPieces()).hasSize(31);
+ }
+
+ @DisplayName("선공은 WHITE이다.")
+ @Test
+ void checkFirstAttack() {
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+
+ assertThat(chessGame.getCamp()).isEqualTo(Camp.WHITE);
+ }
+
+ @DisplayName("후공은 BLACK이다.")
+ @Test
+ void checkSecondAttack() {
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+
+ chessGame.move(new Moving(A2, A3));
+
+ assertThat(chessGame.getCamp()).isEqualTo(Camp.BLACK);
+ }
+
+ @DisplayName("킹을 잡으면 예외가 발생한다. 블랙 이기는 경우")
+ @Test
+ void failToCatchBlackKing() {
+ //given
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+
+ //when
+ chessGame.move(new Moving(F2, F3));
+ chessGame.move(new Moving(E7, E5));
+ chessGame.move(new Moving(G2, G4));
+ chessGame.move(new Moving(D8, H4));
+ chessGame.move(new Moving(H2, H3));
+
+ final Moving catchKingMove = new Moving(H4, E1);
+
+ //then
+ assertThatThrownBy(() -> chessGame.move(catchKingMove))
+ .isInstanceOf(KingDeadException.class);
+ }
+
+ @DisplayName("킹을 잡으면 예외가 발생한다. 화이트 이기는 경우")
+ @Test
+ void failToCatchWhiteKing() {
+ //given
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+
+ //when
+ chessGame.move(new Moving(E2, E3));
+ chessGame.move(new Moving(F7, F6));
+ chessGame.move(new Moving(D1, H5));
+ chessGame.move(new Moving(G8, H6));
+
+ final Moving catchKingMove = new Moving(H5, E8);
+
+ //then
+ assertThatThrownBy(() -> chessGame.move(catchKingMove))
+ .isInstanceOf(KingDeadException.class);
+ }
+}
diff --git a/src/test/java/model/PieceScoreTest.java b/src/test/java/model/PieceScoreTest.java
new file mode 100644
index 00000000000..bf45fdd2607
--- /dev/null
+++ b/src/test/java/model/PieceScoreTest.java
@@ -0,0 +1,39 @@
+package model;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.stream.Stream;
+import model.piece.Bishop;
+import model.piece.BlackPawn;
+import model.piece.King;
+import model.piece.Knight;
+import model.piece.Piece;
+import model.piece.Queen;
+import model.piece.Rook;
+import model.piece.WhitePawn;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class PieceScoreTest {
+
+ @DisplayName("해당하는 기물의 점수를 반환한다.")
+ @ParameterizedTest
+ @MethodSource("getScoreParameterProvider")
+ void getScore(final Piece piece, final Score expected) {
+ assertThat(PieceScore.getScore(piece)).isEqualTo(expected);
+ }
+
+ static Stream getScoreParameterProvider() {
+ return Stream.of(
+ Arguments.of(new King(Camp.BLACK), new Score(0)),
+ Arguments.of(new Queen(Camp.WHITE), new Score(9)),
+ Arguments.of(new Rook(Camp.BLACK), new Score(5)),
+ Arguments.of(new Bishop(Camp.BLACK), new Score(3)),
+ Arguments.of(new Knight(Camp.WHITE), new Score(2.5F)),
+ Arguments.of(new WhitePawn(), new Score(1)),
+ Arguments.of(new BlackPawn(), new Score(1))
+ );
+ }
+}
diff --git a/src/test/java/model/ScoreTest.java b/src/test/java/model/ScoreTest.java
new file mode 100644
index 00000000000..e4ddf56bf66
--- /dev/null
+++ b/src/test/java/model/ScoreTest.java
@@ -0,0 +1,31 @@
+package model;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class ScoreTest {
+
+ @DisplayName("두개의 score를 더한다.")
+ @Test
+ void add() {
+ final Score one = new Score(1);
+ final Score two = new Score(2);
+
+ final Score expected = new Score(3);
+
+ assertThat(one.plus(two)).isEqualTo(expected);
+ }
+
+ @DisplayName("두개의 score를 뺸다.")
+ @Test
+ void minus() {
+ final Score three = new Score(3);
+ final Score two = new Score(2);
+
+ final Score expected = new Score(1);
+
+ assertThat(three.minus(two)).isEqualTo(expected);
+ }
+}
diff --git a/src/test/java/model/command/CommandLineTest.java b/src/test/java/model/command/CommandLineTest.java
new file mode 100644
index 00000000000..c5efd8c887d
--- /dev/null
+++ b/src/test/java/model/command/CommandLineTest.java
@@ -0,0 +1,49 @@
+package model.command;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import exception.InvalidCommandException;
+import java.util.List;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class CommandLineTest {
+
+ @DisplayName("유효하지 않은 커맨드가 입력되면 예외가 발생한다.")
+ @ParameterizedTest
+ @MethodSource("invalidInputParameterProvider")
+ void invalidInput(List input) {
+ assertThatThrownBy(() -> CommandLine.from(input))
+ .isInstanceOf(InvalidCommandException.class);
+ }
+
+ static Stream invalidInputParameterProvider() {
+ return Stream.of(
+ Arguments.of(List.of()),
+ Arguments.of(List.of("start", "a2", "a3")),
+ Arguments.of(List.of("start", "end")),
+ Arguments.of(List.of("move", "a2")),
+ Arguments.of(List.of("move", "a2", "a3", "a4")),
+ Arguments.of(List.of("end", "a2", "a3")),
+ Arguments.of(List.of("end", "end"))
+ );
+ }
+
+ @Test
+ @DisplayName("getter로 가져온 값을 수정하려고 하면 예외가 발생한다.")
+ void doNotUpdate() {
+ //given
+ final CommandLine commandLine = CommandLine.from(List.of("move", "a1", "a3"));
+
+ //when
+ final List body = commandLine.getBody();
+
+ //then
+ assertThatThrownBy(() -> body.add("b4"))
+ .isInstanceOf(UnsupportedOperationException.class);
+ }
+}
diff --git a/src/test/java/model/command/CommandTest.java b/src/test/java/model/command/CommandTest.java
new file mode 100644
index 00000000000..6403fa9478e
--- /dev/null
+++ b/src/test/java/model/command/CommandTest.java
@@ -0,0 +1,43 @@
+package model.command;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import java.util.List;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class CommandTest {
+
+ @DisplayName("올바른 command가 입력되면 예외가 발생하지 않는다.")
+ @Test
+ void checkCommand() {
+ assertAll(
+ () -> assertThat(Command.from("start")).isEqualTo(Command.START),
+ () -> assertThat(Command.from("end")).isEqualTo(Command.END),
+ () -> assertThat(Command.from("move")).isEqualTo(Command.MOVE),
+ () -> assertThat(Command.from("a1")).isEqualTo(Command.POSITION),
+ () -> assertThat(Command.from("h8")).isEqualTo(Command.POSITION),
+ () -> assertThat(Command.from("D8")).isEqualTo(Command.POSITION)
+ );
+ }
+
+ @DisplayName("각 command 의 body 사이즈를 확인한다.")
+ @ParameterizedTest
+ @MethodSource("checkBodySizeParameterProvider")
+ void checkBodySize(Command command, List body) {
+ assertThat(command.isEqualToBodySize(body.size())).isTrue();
+ }
+
+ static Stream checkBodySizeParameterProvider() {
+ return Stream.of(
+ Arguments.of(Command.START, List.of()),
+ Arguments.of(Command.END, List.of()),
+ Arguments.of(Command.MOVE, List.of("a1", "a2"))
+ );
+ }
+}
diff --git a/src/test/java/model/piece/PawnTest.java b/src/test/java/model/piece/PawnTest.java
index 9530d160953..296e4aacd38 100644
--- a/src/test/java/model/piece/PawnTest.java
+++ b/src/test/java/model/piece/PawnTest.java
@@ -31,8 +31,7 @@
import exception.InvalidMovingException;
import java.util.Set;
import java.util.stream.Stream;
-import model.Camp;
-import model.ChessBoard;
+import model.ChessGame;
import model.position.Moving;
import model.position.Position;
import org.junit.jupiter.api.DisplayName;
@@ -46,9 +45,7 @@ class PawnTest {
@DisplayName("이동할 수 없는 경로면 예외가 발생한다.")
@ParameterizedTest
@MethodSource("invalidMovingParameterProvider")
- void invalidMoving(final Camp camp, final Moving moving) {
- final Pawn pawn = new Pawn(camp);
-
+ void invalidMoving(final Pawn pawn, final Moving moving) {
assertAll(
() -> assertThat(pawn.canMovable(moving)).isFalse(),
() -> assertThatThrownBy(() -> pawn.getMoveRoute(moving))
@@ -58,18 +55,16 @@ void invalidMoving(final Camp camp, final Moving moving) {
static Stream invalidMovingParameterProvider() {
return Stream.of(
- Arguments.of(Camp.BLACK, new Moving(A6, A4)),
- Arguments.of(Camp.BLACK, new Moving(A7, A4)),
- Arguments.of(Camp.WHITE, new Moving(A8, A7))
+ Arguments.of(new BlackPawn(), new Moving(A6, A4)),
+ Arguments.of(new BlackPawn(), new Moving(A7, A4)),
+ Arguments.of(new WhitePawn(), new Moving(A8, A7))
);
}
@DisplayName("이동 경로를 반환한다. 출발지와 도착지는 포함하지 않는다.")
@ParameterizedTest
@MethodSource("checkRouteParameterProvider")
- void checkRoute(final Camp camp, final Moving moving, final Set expected) {
- final Pawn pawn = new Pawn(camp);
-
+ void checkRoute(final Pawn pawn, final Moving moving, final Set expected) {
assertAll(
() -> assertThat(pawn.canMovable(moving)).isTrue(),
() -> assertThat(pawn.getMoveRoute(moving)).isEqualTo(expected)
@@ -79,10 +74,10 @@ void checkRoute(final Camp camp, final Moving moving, final Set expect
static Stream checkRouteParameterProvider() {
return Stream.of(
- Arguments.of(Camp.BLACK, new Moving(A7, A5), Set.of(A6)),
- Arguments.of(Camp.BLACK, new Moving(A6, A5), Set.of()),
- Arguments.of(Camp.WHITE, new Moving(B2, B4), Set.of(B3)),
- Arguments.of(Camp.WHITE, new Moving(C2, C3), Set.of())
+ Arguments.of(new BlackPawn(), new Moving(A7, A5), Set.of(A6)),
+ Arguments.of(new BlackPawn(), new Moving(A6, A5), Set.of()),
+ Arguments.of(new WhitePawn(), new Moving(B2, B4), Set.of(B3)),
+ Arguments.of(new WhitePawn(), new Moving(C2, C3), Set.of())
);
}
@@ -90,7 +85,7 @@ static Stream checkRouteParameterProvider() {
@DisplayName("앞에 기물이 있을때 전진이 불가하다.")
void whenPieceInFrontCanNotMoveForward() {
//given
- final ChessBoard chessBoard = ChessBoard.setupStartingPosition();
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
/*
RNBQKBNR 8
@@ -106,13 +101,13 @@ void whenPieceInFrontCanNotMoveForward() {
*/
//when
- chessBoard.move(new Moving(A2, A4));
- chessBoard.move(new Moving(A7, A5));
+ chessGame.move(new Moving(A2, A4));
+ chessGame.move(new Moving(A7, A5));
final Moving forwadMoving = new Moving(A4, A5);
//then
- assertThatThrownBy(() -> chessBoard.move(forwadMoving))
+ assertThatThrownBy(() -> chessGame.move(forwadMoving))
.isInstanceOf(InvalidMovingException.class);
}
@@ -120,7 +115,7 @@ void whenPieceInFrontCanNotMoveForward() {
@DisplayName("대각선에 기물이 있을 때 대각선 이동이 가능하다. WHITE (위 오른쪽)")
void whenPieceInDiagonalCanMove1() {
//given
- final ChessBoard chessBoard = ChessBoard.setupStartingPosition();
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
/*
RNBQKBNR 8
@@ -136,11 +131,11 @@ void whenPieceInDiagonalCanMove1() {
*/
//when
- chessBoard.move(new Moving(A2, A4));
- chessBoard.move(new Moving(B7, B5));
+ chessGame.move(new Moving(A2, A4));
+ chessGame.move(new Moving(B7, B5));
//then
- assertThatCode(() -> chessBoard.move(new Moving(A4, B5)))
+ assertThatCode(() -> chessGame.move(new Moving(A4, B5)))
.doesNotThrowAnyException();
}
@@ -148,7 +143,7 @@ void whenPieceInDiagonalCanMove1() {
@DisplayName("대각선에 기물이 있다면 이동이 가능하다. BLACK (아래 오른쪽)")
void whenPieceInDiagonalCanMove2() {
//given
- final ChessBoard chessBoard = ChessBoard.setupStartingPosition();
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
/*
RNBQKBNR 8
@@ -164,12 +159,12 @@ void whenPieceInDiagonalCanMove2() {
*/
//when
- chessBoard.move(new Moving(C2, C4));
- chessBoard.move(new Moving(B7, B5));
- chessBoard.move(new Moving(B2, B3));
+ chessGame.move(new Moving(C2, C4));
+ chessGame.move(new Moving(B7, B5));
+ chessGame.move(new Moving(B2, B3));
//then
- assertThatCode(() -> chessBoard.move(new Moving(B5, C4)))
+ assertThatCode(() -> chessGame.move(new Moving(B5, C4)))
.doesNotThrowAnyException();
}
@@ -177,7 +172,7 @@ void whenPieceInDiagonalCanMove2() {
@DisplayName("대각선에 기물이 없다면 대각선 이동이 불가하다.")
void whenPieceNotInDiagonalCanNotMove() {
//given
- final ChessBoard chessBoard = ChessBoard.setupStartingPosition();
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
/*
RNBQKB.R 8
@@ -193,13 +188,13 @@ void whenPieceNotInDiagonalCanNotMove() {
*/
//when
- chessBoard.move(new Moving(A2, A4));
- chessBoard.move(new Moving(G8, H6));
+ chessGame.move(new Moving(A2, A4));
+ chessGame.move(new Moving(G8, H6));
final Moving diagonalMoving = new Moving(A4, B5);
//then
- assertThatThrownBy(() -> chessBoard.move(diagonalMoving))
+ assertThatThrownBy(() -> chessGame.move(diagonalMoving))
.isInstanceOf(InvalidMovingException.class);
}
@@ -207,16 +202,16 @@ void whenPieceNotInDiagonalCanNotMove() {
@DisplayName("폰은 후진할 수 없다. WHITE")
void failToMoveBackWhitePawn() {
//given
- final ChessBoard chessBoard = ChessBoard.setupStartingPosition();
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
//when
- chessBoard.move(new Moving(H2, H4));
- chessBoard.move(new Moving(D7, D5));
+ chessGame.move(new Moving(H2, H4));
+ chessGame.move(new Moving(D7, D5));
final Moving whiteBackMoving = new Moving(H4, H3);
//then
- assertThatThrownBy(() -> chessBoard.move(whiteBackMoving))
+ assertThatThrownBy(() -> chessGame.move(whiteBackMoving))
.isInstanceOf(InvalidMovingException.class);
}
@@ -224,17 +219,17 @@ void failToMoveBackWhitePawn() {
@DisplayName("폰은 후진할 수 없다. BLACK")
void failToMoveBackBlackPawn() {
//given
- final ChessBoard chessBoard = ChessBoard.setupStartingPosition();
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
//when
- chessBoard.move(new Moving(H2, H4));
- chessBoard.move(new Moving(D7, D5));
- chessBoard.move(new Moving(H4, H5));
+ chessGame.move(new Moving(H2, H4));
+ chessGame.move(new Moving(D7, D5));
+ chessGame.move(new Moving(H4, H5));
final Moving blackBackMoving = new Moving(D5, D6);
//then
- assertThatThrownBy(() -> chessBoard.move(blackBackMoving))
+ assertThatThrownBy(() -> chessGame.move(blackBackMoving))
.isInstanceOf(InvalidMovingException.class);
}
}
diff --git a/src/test/java/model/piece/PieceTest.java b/src/test/java/model/piece/PieceTest.java
index 4950cd63529..bd1fa20faa5 100644
--- a/src/test/java/model/piece/PieceTest.java
+++ b/src/test/java/model/piece/PieceTest.java
@@ -28,7 +28,7 @@ static Stream invalidMovingParameterProvider() {
Arguments.of(new Bishop(Camp.BLACK)),
Arguments.of(new King(Camp.BLACK)),
Arguments.of(new Knight(Camp.WHITE)),
- Arguments.of(new Pawn(Camp.WHITE)),
+ Arguments.of(new WhitePawn()),
Arguments.of(new Queen(Camp.WHITE)),
Arguments.of(new Rook(Camp.WHITE))
);
diff --git a/src/test/java/model/status/EndTest.java b/src/test/java/model/status/EndTest.java
index f7adb776910..6758223ee69 100644
--- a/src/test/java/model/status/EndTest.java
+++ b/src/test/java/model/status/EndTest.java
@@ -5,7 +5,8 @@
import exception.InvalidStatusException;
import java.util.List;
-import model.ChessBoard;
+import model.ChessGame;
+import model.command.CommandLine;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -15,22 +16,24 @@ class EndTest {
@Test
void failToPlayIfStatusEnd() {
//given
- final ChessBoard chessBoard = ChessBoard.setupStartingPosition();
- final GameStatus gameStatus = Initialization.gameSetting(List.of("start"));
- final List endCommand = List.of("end");
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+ final CommandLine startCommand = CommandLine.from(List.of("start"));
+ final GameStatus gameStatus = StatusFactory.create(startCommand);
+ final CommandLine endCommand = CommandLine.from(List.of("end"));
//when
- final GameStatus play = gameStatus.play(endCommand, chessBoard);
+ final GameStatus play = gameStatus.play(endCommand, chessGame);
//then
- assertThatThrownBy(() -> play.play(endCommand, chessBoard))
+ assertThatThrownBy(() -> play.play(endCommand, chessGame))
.isInstanceOf(InvalidStatusException.class);
}
@DisplayName("종료 상태일 때 상태를 확인한다.")
@Test
void checkRunning() {
- final GameStatus gameStatus = Initialization.gameSetting(List.of("end"));
+ final CommandLine endCommand = CommandLine.from(List.of("end"));
+ final GameStatus gameStatus = StatusFactory.create(endCommand);
assertThat(gameStatus.isRunning()).isFalse();
}
diff --git a/src/test/java/model/status/RunningTest.java b/src/test/java/model/status/RunningTest.java
index 844e05cf669..0f72f87acb5 100644
--- a/src/test/java/model/status/RunningTest.java
+++ b/src/test/java/model/status/RunningTest.java
@@ -5,7 +5,8 @@
import exception.InvalidStatusException;
import java.util.List;
-import model.ChessBoard;
+import model.ChessGame;
+import model.command.CommandLine;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -14,7 +15,8 @@ class RunningTest {
@DisplayName("러닝 상태일때 상태를 확인한다.")
@Test
void checkRunning() {
- final GameStatus gameStatus = Initialization.gameSetting(List.of("start"));
+ final CommandLine startCommand = CommandLine.from(List.of("start"));
+ final GameStatus gameStatus = StatusFactory.create(startCommand);
assertThat(gameStatus.isRunning()).isTrue();
}
@@ -22,35 +24,49 @@ void checkRunning() {
@DisplayName("러닝 상태일 때 play 후 상태를 확인한다.")
@Test
void checkRunningAfterPlay() {
- final GameStatus gameStatus = Initialization.gameSetting(List.of("start"));
- final ChessBoard chessBoard = ChessBoard.setupStartingPosition();
+ final CommandLine startCommand = CommandLine.from(List.of("start"));
+ final GameStatus gameStatus = StatusFactory.create(startCommand);
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
- final List moveCommand = List.of("move", "a2", "a3");
- assertThat(gameStatus.play(moveCommand, chessBoard))
+ final CommandLine moveCommand = CommandLine.from(List.of("move", "a2", "a3"));
+ assertThat(gameStatus.play(moveCommand, chessGame))
.isInstanceOf(Running.class);
}
@DisplayName("러닝 상태에서 end 후 상태를 학인한다.")
@Test
void checkRunningAfterEnd() {
- final GameStatus gameStatus = Initialization.gameSetting(List.of("start"));
- final ChessBoard chessBoard = ChessBoard.setupStartingPosition();
+ final CommandLine startCommand = CommandLine.from(List.of("start"));
+ final GameStatus gameStatus = StatusFactory.create(startCommand);
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
- final List endCommand = List.of("end");
- assertThat(gameStatus.play(endCommand, chessBoard))
+ final CommandLine endCommand = CommandLine.from(List.of("end"));
+ assertThat(gameStatus.play(endCommand, chessGame))
.isInstanceOf(End.class);
}
+ @DisplayName("러닝 상태에서 quit 후 상태를 학인한다.")
+ @Test
+ void checkRunningAfterQuit() {
+ final CommandLine startCommand = CommandLine.from(List.of("start"));
+ final GameStatus gameStatus = StatusFactory.create(startCommand);
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
+
+ final CommandLine quitCommand = CommandLine.from(List.of("quit"));
+ assertThat(gameStatus.play(quitCommand, chessGame))
+ .isInstanceOf(Quit.class);
+ }
+
@DisplayName("러닝 상태에서 start 하면 예외가 발생한다.")
@Test
void failToStartIfAlreadyRunning() {
//given
- final GameStatus gameStatus = Initialization.gameSetting(List.of("start"));
- final ChessBoard chessBoard = ChessBoard.setupStartingPosition();
+ final CommandLine startCommand = CommandLine.from(List.of("start"));
+ final GameStatus gameStatus = StatusFactory.create(startCommand);
+ final ChessGame chessGame = ChessGame.setupStartingPosition();
//when && then
- final List startCommand = List.of("start");
- assertThatThrownBy(() -> gameStatus.play(startCommand, chessBoard))
+ assertThatThrownBy(() -> gameStatus.play(startCommand, chessGame))
.isInstanceOf(InvalidStatusException.class);
}
}
diff --git a/src/test/java/model/status/StatusFactoryTest.java b/src/test/java/model/status/StatusFactoryTest.java
new file mode 100644
index 00000000000..439ded748c9
--- /dev/null
+++ b/src/test/java/model/status/StatusFactoryTest.java
@@ -0,0 +1,37 @@
+package model.status;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import exception.InvalidStatusException;
+import java.util.List;
+import model.command.CommandLine;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class StatusFactoryTest {
+
+ @DisplayName("start를 입력하면 게임이 시작된다.")
+ @Test
+ void gameStartWhenCommandIsStart() {
+ final CommandLine startCommand = CommandLine.from(List.of("start"));
+ final GameStatus gameStatus = StatusFactory.create(startCommand);
+ assertThat(gameStatus).isInstanceOf(Running.class);
+ }
+
+ @DisplayName("end를 입력하면 게임이 종료된다.")
+ @Test
+ void gameEndWhenCommandIsEnd() {
+ final CommandLine endCommand = CommandLine.from(List.of("end"));
+ final GameStatus gameStatus = StatusFactory.create(endCommand);
+ assertThat(gameStatus).isInstanceOf(End.class);
+ }
+
+ @DisplayName("시작시 유효하지 않은 명령어가 오면 예외가 발생한다.")
+ @Test
+ void invalidCommand() {
+ CommandLine moveCommand = CommandLine.from(List.of("move", "a2", "d3"));
+ assertThatThrownBy(() -> StatusFactory.create(moveCommand))
+ .isInstanceOf(InvalidStatusException.class);
+ }
+}
diff --git a/src/test/resources/board.sql b/src/test/resources/board.sql
new file mode 100644
index 00000000000..4692c4edd38
--- /dev/null
+++ b/src/test/resources/board.sql
@@ -0,0 +1,9 @@
+use chess_test;
+
+create table board
+(
+ position varchar(2) not null,
+ piece_type varchar(6) not null,
+ camp varchar(5) not null
+
+);
diff --git a/src/test/resources/moving.sql b/src/test/resources/moving.sql
new file mode 100644
index 00000000000..b1fbcefe0af
--- /dev/null
+++ b/src/test/resources/moving.sql
@@ -0,0 +1,9 @@
+use chess_test;
+
+create table moving
+(
+ movement_id INT primary key auto_increment,
+ camp varchar(5) not null,
+ start varchar(2) not null,
+ destination varchar(2) not null
+);
diff --git a/src/test/resources/turn.sql b/src/test/resources/turn.sql
new file mode 100644
index 00000000000..ed9675d1736
--- /dev/null
+++ b/src/test/resources/turn.sql
@@ -0,0 +1,7 @@
+use chess_test;
+
+create table turn
+(
+ camp varchar(5) not null,
+ count int not null
+);