diff --git a/README.md b/README.md index 1925cd54b6e..a043f85db90 100644 --- a/README.md +++ b/README.md @@ -33,13 +33,52 @@ - [x] 체스 말이 반환한 경로 안에 다른 체스 말이 있는지 검사 - [x] 경로 안에 다른 체스 말이 있다면 이동할 수 없다 <- Path - **입력** -- [x] move source위치 target위치을 실행해 이동한다 +- [x] move source 위치 target 위치을 실행해 이동한다 ## 3단계 - 승패 및 점수 ### 기능 요구 사항 -- [x] King이 잡혔을 때 게임을 종료해야 한다 +- [x] King 이 잡혔을 때 게임을 종료해야 한다 - [x] 체스판에서 잡힌 말을 반환 - [x] 체스 말에서 해당 말의 타입을 반환 - [x] status 명령어를 입력하면 점수를 출력 - [x] 점수를 계산하는 ScoreCalculator 클래스 - [x] 같은 File 에 있는 Pawn 은 0.5점을 준다 + +## 4단계 - DB 적용 +### 기능 요구 사항 +- **DB 연결 전 기능** +- [x] end 명령어를 입력하면 각자의 점수와 승리 팀을 출력한다 + - [x] 점수를 계산해서 점수가 높은 팀이 승리한다 +- **DB 연결** +- [x] 애플리케이션을 재시작하더라도 이전에 하던 체스 게임을 다시 시작할 수 있어야 한다 + - [x] 이어하기 + - 이전에 진행하던 게임이 있다면 이어서 게임을 시작한다 + - 이전에 진행하던 게임이 없다면 새로운 게임을 시작한다 + - [x] 새로하기 + - 이전에 진행하던 게임이 있어도 새로운 게임을 시작한다 + +- chessGame 을 따로 저장, Turn 을 저장할 필요가 있음, 게임의 진행 상태를 저장할 필요가 있음 + +- [x] 피스를 이동할 때 마다 피스 테이블을 업데이트한다 +- [x] 체스 게임을 저장할 수 있다 +- [x] 체스 게임을 찾을 수 있다 +- [x] 체스 게임을 삭제할 수 있다 + +``` +CREATE TABLE chess_game ( + id BIGINT NOT NULL AUTO_INCREMENT, + turn VARCHAR(16) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE piece ( + id BIGINT NOT NULL AUTO_INCREMENT, + chess_game_id BIGINT NOT NULL, + piece_file INT NOT NULL, + piece_rank INT NOT NULL, + color VARCHAR(16), + type VARCHAR(16), + PRIMARY KEY (id) +); + +``` diff --git a/build.gradle b/build.gradle index 9d9f1e41f23..daa2e95f526 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,8 @@ repositories { dependencies { testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2' + + runtimeOnly 'mysql:mysql-connector-java:8.0.28' } java { diff --git a/gradlew.bat b/gradlew.bat index 6689b85beec..563ba037e22 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -44,7 +44,7 @@ set JAVA_EXE=java.exe if %ERRORLEVEL% equ 0 goto execute echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo ERROR: JAVA_HOME is not set and no 'java' commandType could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. @@ -66,7 +66,7 @@ echo location of your Java installation. goto fail :execute -@rem Setup the command line +@rem Setup the commandType line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar diff --git a/src/main/java/chess/ChessApplication.java b/src/main/java/chess/ChessApplication.java index e7a0206c7b8..3a2a036a1ab 100644 --- a/src/main/java/chess/ChessApplication.java +++ b/src/main/java/chess/ChessApplication.java @@ -1,22 +1,20 @@ package chess; import chess.controller.ChessController; -import chess.model.command.CommandFactory; -import chess.model.board.InitialBoard; -import chess.view.InputView; -import chess.view.OutputView; +import chess.dao.chessGame.JdbcChessGameDao; +import chess.dao.piece.JdbcPieceDao; +import chess.service.ChessGameService; +import java.sql.SQLException; public class ChessApplication { - public static void main(String[] args) { + public static void main(String[] args) throws SQLException { - InputView inputView = new InputView(); - OutputView outputView = new OutputView(); - CommandFactory commandFactory = new CommandFactory(); - InitialBoard initialBoard = new InitialBoard(); + final JdbcChessGameDao chessGameDao = new JdbcChessGameDao(); + final JdbcPieceDao PieceDao = new JdbcPieceDao(); + final ChessGameService chessGameService = new ChessGameService(chessGameDao, PieceDao); + final ChessController chessController = new ChessController(chessGameService); - ChessController chessController = new ChessController(inputView, outputView, commandFactory, - initialBoard); - chessController.runChess(); + chessController.run(); } } diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java index a6fa065f022..e1eed2419e9 100644 --- a/src/main/java/chess/controller/ChessController.java +++ b/src/main/java/chess/controller/ChessController.java @@ -1,90 +1,52 @@ package chess.controller; -import chess.model.command.CommandFactory; -import chess.model.command.CommandLauncher; -import chess.model.ErrorMessage; -import chess.model.board.Board; -import chess.model.board.InitialBoard; -import chess.model.piece.Piece; -import chess.model.piece.PieceInfo; -import chess.model.position.Color; -import chess.model.position.Position; -import chess.model.score.ScoreCalculator; +import chess.controller.command.CommandFactory; +import chess.controller.command.commands.Command; +import chess.domain.game.ChessGame; +import chess.domain.game.State; +import chess.service.ChessGameService; import chess.view.InputView; import chess.view.OutputView; +import java.sql.SQLException; public class ChessController { - private final InputView inputView; - private final OutputView outputView; - private final CommandFactory commandFactory; - private final Board board; - private Color currentTurn; - private boolean isRunning; + private final ChessGameService chessGameService; - public ChessController(InputView inputView, OutputView outputView, CommandFactory commandFactory, InitialBoard initialBoard) { - this.inputView = inputView; - this.outputView = outputView; - this.commandFactory = commandFactory; - this.board = initialBoard.createInitialBoard(); - this.currentTurn = Color.WHITE; - this.isRunning = true; + public ChessController(ChessGameService chessGameService) { + this.chessGameService = chessGameService; } - public void runChess() { - outputView.printStartMessage(); - CommandLauncher receivedCommand = null; + public void run() throws SQLException { + ChessGame chessGame = executeInitialCommandAndFetchChessGame(); - while (receivedCommand == null) { - try { - String initialCommandInput = inputView.receiveCommand(); - receivedCommand = commandFactory.createCommand(initialCommandInput); - if (receivedCommand.validateInitialCommandType()) { - break; - } - System.out.println(ErrorMessage.INVALID_INITIAL_COMMAND.getMessage()); - receivedCommand = null; - } catch (IllegalArgumentException exception) { - System.out.println(exception.getMessage()); - } - } - - receivedCommand.execute(this); - - while (isRunning) { - try { - String commandInput = inputView.receiveCommand(); - receivedCommand = commandFactory.createCommand(commandInput); - if (receivedCommand.validateStatusCommandType()) { - calculateAndPrintCurrentTurnScore(); - } else { - receivedCommand.execute(this); - currentTurn = currentTurn.changeTurn(currentTurn); - } - } catch (IllegalArgumentException exception) { - System.out.println(exception.getMessage()); - } + while (isRunnable(chessGame)) { + executeCommand(chessGame); } } - public void startGame() { - outputView.printBoard(board.getMap()); - } - - public void endGame() { - isRunning = false; + private void executeCommand(ChessGame chessGame) { + try { + Command command = CommandFactory.createCommand(chessGame, InputView.receiveCommand()); + command.execute(chessGameService); + } catch (IllegalArgumentException | UnsupportedOperationException e) { + OutputView.printErrorMessage(e); + executeCommand(chessGame); + } } - public void movePiece(Position source, Position target) { - Piece capturedPiece = board.move(source, target, currentTurn); - if (capturedPiece != null && capturedPiece.pieceType() == PieceInfo.KING) { - endGame(); + private ChessGame executeInitialCommandAndFetchChessGame() throws SQLException { + try { + Command command = CommandFactory.createInitialCommand(InputView.receiveInitialCommand()); + return command.initializeChessGame(chessGameService); + } catch (IllegalArgumentException | UnsupportedOperationException e) { + OutputView.printErrorMessage(e); + return executeInitialCommandAndFetchChessGame(); } - outputView.printBoard(board.getMap()); } - public void calculateAndPrintCurrentTurnScore() { - double score = ScoreCalculator.calculate(board.getMap(), currentTurn); - outputView.printCurrentTurnScore(currentTurn, score); + private boolean isRunnable(ChessGame chessGame) { + return chessGame.getState().equals(State.RUNNING) + || chessGame.getState().equals(State.WAITING); } } diff --git a/src/main/java/chess/controller/command/CommandFactory.java b/src/main/java/chess/controller/command/CommandFactory.java new file mode 100644 index 00000000000..f7279d538e7 --- /dev/null +++ b/src/main/java/chess/controller/command/CommandFactory.java @@ -0,0 +1,46 @@ +package chess.controller.command; + +import chess.controller.command.commands.Command; +import chess.controller.command.commands.ContinueCommand; +import chess.controller.command.commands.EndCommand; +import chess.controller.command.commands.MoveCommand; +import chess.controller.command.commands.NewCommand; +import chess.controller.command.commands.StartCommand; +import chess.controller.command.commands.StatusCommand; +import chess.domain.game.ChessGame; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; + +public class CommandFactory { + + private static final int COMMAND_INDEX = 0; + + private static final Map, Command>> COMMAND_TYPES = + new EnumMap<>(CommandType.class); + + private static final Map INITIAL_COMMAND_TYPES = new EnumMap<>(CommandType.class); + + static { + COMMAND_TYPES.put(CommandType.START, (chessGame, ignored) -> new StartCommand(chessGame)); + COMMAND_TYPES.put(CommandType.END, (chessGame, ignored) -> new EndCommand(chessGame)); + COMMAND_TYPES.put(CommandType.STATUS, (chessGame, ignored) -> new StatusCommand(chessGame)); + COMMAND_TYPES.put(CommandType.MOVE, MoveCommand::new); + + INITIAL_COMMAND_TYPES.put(CommandType.NEW, new NewCommand()); + INITIAL_COMMAND_TYPES.put(CommandType.CONTINUE, new ContinueCommand()); + } + + public static Command createCommand(ChessGame chessGame, String command) { + final List commandParts = List.of(command.split(" ")); + final CommandType commandType = CommandType.findCommand(commandParts.get(COMMAND_INDEX)); + return COMMAND_TYPES.get(commandType).apply(chessGame, commandParts); + } + + public static Command createInitialCommand(String command) { + final List commandParts = List.of(command.split(" ")); + final CommandType commandType = CommandType.findCommand(commandParts.get(COMMAND_INDEX)); + return INITIAL_COMMAND_TYPES.get(commandType); + } +} diff --git a/src/main/java/chess/controller/command/CommandType.java b/src/main/java/chess/controller/command/CommandType.java new file mode 100644 index 00000000000..82e0f10c460 --- /dev/null +++ b/src/main/java/chess/controller/command/CommandType.java @@ -0,0 +1,26 @@ +package chess.controller.command; + +import chess.domain.ErrorMessage; +import java.util.Arrays; + +public enum CommandType { + START("start"), + END("end"), + STATUS("status"), + MOVE("move"), + NEW("new"), + CONTINUE("continue"); + + private final String command; + + CommandType(final String command) { + this.command = command; + } + + public static CommandType findCommand(final String value) { + return Arrays.stream(values()) + .filter(commandType -> commandType.command.equals(value)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.INVALID_COMMAND.getMessage())); + } +} diff --git a/src/main/java/chess/controller/command/InitialCommandType.java b/src/main/java/chess/controller/command/InitialCommandType.java new file mode 100644 index 00000000000..729a053590a --- /dev/null +++ b/src/main/java/chess/controller/command/InitialCommandType.java @@ -0,0 +1,22 @@ +package chess.controller.command; + +import chess.domain.ErrorMessage; +import java.util.Arrays; + +public enum InitialCommandType { + NEW("new"), + CONTINUE("continue"); + + private final String command; + + InitialCommandType(String command) { + this.command = command; + } + + public static InitialCommandType findCommand(final String value) { + return Arrays.stream(values()) + .filter(command -> command.command.equals(value)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.INVALID_INITIAL_COMMAND.getMessage())); + } +} diff --git a/src/main/java/chess/controller/command/commands/Command.java b/src/main/java/chess/controller/command/commands/Command.java new file mode 100644 index 00000000000..7c8ef2a18e9 --- /dev/null +++ b/src/main/java/chess/controller/command/commands/Command.java @@ -0,0 +1,10 @@ +package chess.controller.command.commands; + +import chess.domain.game.ChessGame; +import chess.service.ChessGameService; +import java.sql.SQLException; + +public interface Command { + void execute(final ChessGameService chessGameService); + ChessGame initializeChessGame(final ChessGameService chessGameService) throws SQLException; +} diff --git a/src/main/java/chess/controller/command/commands/ContinueCommand.java b/src/main/java/chess/controller/command/commands/ContinueCommand.java new file mode 100644 index 00000000000..4898c7692da --- /dev/null +++ b/src/main/java/chess/controller/command/commands/ContinueCommand.java @@ -0,0 +1,25 @@ +package chess.controller.command.commands; + +import chess.domain.game.ChessGame; +import chess.service.ChessGameService; +import chess.view.OutputView; +import java.sql.SQLException; + +public class ContinueCommand implements Command { + + @Override + public ChessGame initializeChessGame(final ChessGameService chessGameService) + throws SQLException { + ChessGame chessGame = chessGameService.findChessGame(); + if (chessGame == null) { + OutputView.printNoExistsRunningGameMessage(); + } + OutputView.printContinueMessage(); + return chessGame; + } + + @Override + public void execute(ChessGameService chessGameService) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/chess/controller/command/commands/EndCommand.java b/src/main/java/chess/controller/command/commands/EndCommand.java new file mode 100644 index 00000000000..51631f295b7 --- /dev/null +++ b/src/main/java/chess/controller/command/commands/EndCommand.java @@ -0,0 +1,23 @@ +package chess.controller.command.commands; + +import chess.domain.game.ChessGame; +import chess.service.ChessGameService; + +public class EndCommand implements Command { + + private final ChessGame chessGame; + + public EndCommand(ChessGame chessGame) { + this.chessGame = chessGame; + } + + @Override + public void execute(final ChessGameService chessGameService) { + chessGame.end(); + } + + @Override + public ChessGame initializeChessGame(final ChessGameService chessGameService) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/chess/controller/command/commands/MoveCommand.java b/src/main/java/chess/controller/command/commands/MoveCommand.java new file mode 100644 index 00000000000..bbcf6bcf287 --- /dev/null +++ b/src/main/java/chess/controller/command/commands/MoveCommand.java @@ -0,0 +1,51 @@ +package chess.controller.command.commands; + +import chess.domain.game.ChessGame; +import chess.domain.game.State; +import chess.domain.position.Color; +import chess.domain.position.Position; +import chess.service.ChessGameService; +import chess.view.OutputView; +import java.util.List; + +public class MoveCommand implements Command { + + private static final int SOURCE_POSITION_INDEX = 1; + private static final int TARGET_POSITION_INDEX = 2; + + private final ChessGame chessGame; + private final Position source; + private final Position target; + + public MoveCommand(ChessGame chessGame, List commandParts) { + this.chessGame = chessGame; + this.source = parsePosition(commandParts.get(SOURCE_POSITION_INDEX)); + this.target = parsePosition(commandParts.get(TARGET_POSITION_INDEX)); + } + + @Override + public void execute(final ChessGameService chessGameService) { + chessGame.movePiece(source, target); + chessGameService.updatePiece(chessGame, source, target); + OutputView.printBoard(chessGame.getBoard().getMap()); + processIfKingCaptured(chessGame); + } + + private static Position parsePosition(String position) { + int file = position.charAt(0) - 'a' + 1; + int rank = position.charAt(1) - '0'; + return new Position(file, rank); + } + + private void processIfKingCaptured(final ChessGame chessGame) { + if (chessGame.getState().equals(State.KING_IS_CAPTURED)) { + final Color winner = chessGame.getTurn(); + OutputView.printWinningColor(winner); + } + } + + @Override + public ChessGame initializeChessGame(final ChessGameService chessGameService) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/chess/controller/command/commands/NewCommand.java b/src/main/java/chess/controller/command/commands/NewCommand.java new file mode 100644 index 00000000000..737a0ecdf1d --- /dev/null +++ b/src/main/java/chess/controller/command/commands/NewCommand.java @@ -0,0 +1,23 @@ +package chess.controller.command.commands; + +import chess.domain.game.ChessGame; +import chess.service.ChessGameService; +import chess.view.OutputView; + +public class NewCommand implements Command { + + public NewCommand() { + } + + @Override + public ChessGame initializeChessGame(final ChessGameService chessGameService) { + OutputView.printNewGameMessage(); + chessGameService.deleteChessGame(); + return chessGameService.initializeChessGame(); + } + + @Override + public void execute(ChessGameService chessGameService) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/chess/controller/command/commands/StartCommand.java b/src/main/java/chess/controller/command/commands/StartCommand.java new file mode 100644 index 00000000000..82f50f486c7 --- /dev/null +++ b/src/main/java/chess/controller/command/commands/StartCommand.java @@ -0,0 +1,25 @@ +package chess.controller.command.commands; + +import chess.domain.game.ChessGame; +import chess.service.ChessGameService; +import chess.view.OutputView; + +public class StartCommand implements Command { + + private final ChessGame chessGame; + + public StartCommand(ChessGame chessGame) { + this.chessGame = chessGame; + } + + @Override + public void execute(final ChessGameService chessGameService) { + chessGame.start(); + OutputView.printBoard(chessGame.getBoard().getMap()); + } + + @Override + public ChessGame initializeChessGame(final ChessGameService chessGameService) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/chess/controller/command/commands/StatusCommand.java b/src/main/java/chess/controller/command/commands/StatusCommand.java new file mode 100644 index 00000000000..cfd210c777c --- /dev/null +++ b/src/main/java/chess/controller/command/commands/StatusCommand.java @@ -0,0 +1,40 @@ +package chess.controller.command.commands; + +import chess.domain.game.ChessGame; +import chess.domain.position.Color; +import chess.service.ChessGameService; +import chess.view.OutputView; + +public class StatusCommand implements Command { + + private final ChessGame chessGame; + + public StatusCommand(ChessGame chessGame) { + this.chessGame = chessGame; + } + + @Override + public void execute(final ChessGameService chessGameService) { + final Double whiteScore = chessGame.calculateScore(Color.WHITE); + final Double blackScore = chessGame.calculateScore(Color.BLACK); + printScoreAndWinningColor(whiteScore, blackScore); + } + + private void printScoreAndWinningColor(Double whiteScore, Double blackScore) { + OutputView.printScore(whiteScore, blackScore); + if (whiteScore > blackScore) { + OutputView.printWinningColor(Color.WHITE); + } + if (whiteScore < blackScore) { + OutputView.printWinningColor(Color.BLACK); + } + if (whiteScore.equals(blackScore)) { + OutputView.printDraw(); + } + } + + @Override + public ChessGame initializeChessGame(final ChessGameService chessGameService) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/chess/dao/JdbcConnection.java b/src/main/java/chess/dao/JdbcConnection.java new file mode 100644 index 00000000000..fa902f53922 --- /dev/null +++ b/src/main/java/chess/dao/JdbcConnection.java @@ -0,0 +1,29 @@ +package chess.dao; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class JdbcConnection { + + private static Connection connection; + + private static final String SERVER = "localhost:3306"; + private static final String DATABASE = "chess"; + private static final String OPTION = "?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true"; + private static final String USERNAME = "root"; + private static final String PASSWORD = "root"; + + static { + try { + connection = DriverManager.getConnection("jdbc:mysql://" + SERVER + "/" + DATABASE + OPTION, USERNAME, PASSWORD); + } catch (final SQLException e) { + System.err.println("DB 연결 오류:" + e.getMessage()); + e.printStackTrace(); + } + } + + public static Connection getConnection() { + return connection; + } +} diff --git a/src/main/java/chess/dao/JdbcTemplate.java b/src/main/java/chess/dao/JdbcTemplate.java new file mode 100644 index 00000000000..a0ddaf9b184 --- /dev/null +++ b/src/main/java/chess/dao/JdbcTemplate.java @@ -0,0 +1,49 @@ +package chess.dao; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class JdbcTemplate { + + private static final int FIRST_INDEX = 1; + private final Connection connection; + + public JdbcTemplate(final Connection connection) { + this.connection = connection; + } + + public void executeUpdate(final String query, final Object... parameters) { + try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) { + for (int index = FIRST_INDEX; index <= parameters.length; index++) { + preparedStatement.setObject(index, parameters[index - FIRST_INDEX]); + } + preparedStatement.executeUpdate(); + } catch (final SQLException e) { + throw new RuntimeException(e); + } + } + + public List executeQuery(final String query, final List resultParameters, + final Object... sqlParameters) { + try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) { + final List result = new ArrayList<>(); + for (int index = FIRST_INDEX; index <= sqlParameters.length; index++) { + preparedStatement.setObject(index, sqlParameters[index - FIRST_INDEX]); + } + + final ResultSet resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + for (String parameter : resultParameters) { + result.add(resultSet.getObject(parameter)); + } + } + return result; + } catch (final SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/chess/dao/chessGame/ChessGameDao.java b/src/main/java/chess/dao/chessGame/ChessGameDao.java new file mode 100644 index 00000000000..b1def5861d0 --- /dev/null +++ b/src/main/java/chess/dao/chessGame/ChessGameDao.java @@ -0,0 +1,13 @@ +package chess.dao.chessGame; + +import chess.dao.chessGame.dto.FindResponseDto; +import chess.dao.chessGame.dto.SaveRequestDto; +import chess.domain.position.Color; + +public interface ChessGameDao { + + Long save(final SaveRequestDto saveRequestDto); + FindResponseDto findChessGameById(final long id); + void updateTurn(final long id, final Color turn); + void delete(); +} diff --git a/src/main/java/chess/dao/chessGame/JdbcChessGameDao.java b/src/main/java/chess/dao/chessGame/JdbcChessGameDao.java new file mode 100644 index 00000000000..cfb7a3c8681 --- /dev/null +++ b/src/main/java/chess/dao/chessGame/JdbcChessGameDao.java @@ -0,0 +1,69 @@ +package chess.dao.chessGame; + +import chess.dao.JdbcConnection; +import chess.dao.JdbcTemplate; +import chess.dao.chessGame.dto.FindResponseDto; +import chess.dao.chessGame.dto.SaveRequestDto; +import chess.domain.position.Color; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; + +public class JdbcChessGameDao implements ChessGameDao { + + private static final long TEMPORARY_ID = 1; + + private final Connection connection; + private final JdbcTemplate jdbcTemplate; + + public JdbcChessGameDao() { + this.connection = JdbcConnection.getConnection(); + this.jdbcTemplate = new JdbcTemplate(connection); + } + + @Override + public Long save(final SaveRequestDto saveRequestDto) { + final String query = "INSERT INTO chess_game VALUES (?, ?)"; + try (final PreparedStatement preparedStatement = + connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) { + preparedStatement.setLong(1, TEMPORARY_ID); + preparedStatement.setString(2, saveRequestDto.getTurn().name()); + preparedStatement.executeUpdate(); + return retrieveGeneratedKey(preparedStatement); + } catch (final SQLException e) { + throw new RuntimeException(e); + } + } + + private long retrieveGeneratedKey(final PreparedStatement preparedStatement) throws SQLException { + final ResultSet resultSet = preparedStatement.getGeneratedKeys(); + resultSet.next(); + return resultSet.getLong(1); + } + + @Override + public FindResponseDto findChessGameById(final long id) { + final String query = "SELECT * FROM chess_game WHERE id = ?"; + final List resultParameters = List.of("id", "turn"); + final List result = jdbcTemplate.executeQuery(query, resultParameters, id); + return new FindResponseDto( + (Long) result.get(0), + Color.valueOf((String) result.get(1)) + ); + } + + @Override + public void updateTurn(final long id, final Color turn) { + final String query = "UPDATE chess_game SET turn = ? WHERE id = ?"; + jdbcTemplate.executeUpdate(query, turn.name(), id); + } + + @Override + public void delete() { + final String query = "DELETE FROM chess_game"; + jdbcTemplate.executeUpdate(query); + } +} diff --git a/src/main/java/chess/dao/chessGame/dto/FindResponseDto.java b/src/main/java/chess/dao/chessGame/dto/FindResponseDto.java new file mode 100644 index 00000000000..a34aecaff34 --- /dev/null +++ b/src/main/java/chess/dao/chessGame/dto/FindResponseDto.java @@ -0,0 +1,22 @@ +package chess.dao.chessGame.dto; + +import chess.domain.position.Color; + +public class FindResponseDto { + + private final Long id; + private final Color turn; + + public FindResponseDto(Long id, Color turn) { + this.id = id; + this.turn = turn; + } + + public Long getId() { + return id; + } + + public Color getTurn() { + return turn; + } +} diff --git a/src/main/java/chess/dao/chessGame/dto/SaveRequestDto.java b/src/main/java/chess/dao/chessGame/dto/SaveRequestDto.java new file mode 100644 index 00000000000..878a55a83c6 --- /dev/null +++ b/src/main/java/chess/dao/chessGame/dto/SaveRequestDto.java @@ -0,0 +1,16 @@ +package chess.dao.chessGame.dto; + +import chess.domain.position.Color; + +public class SaveRequestDto { + + private final Color turn; + + public SaveRequestDto(Color turn) { + this.turn = turn; + } + + public Color getTurn() { + return turn; + } +} diff --git a/src/main/java/chess/dao/piece/JdbcPieceDao.java b/src/main/java/chess/dao/piece/JdbcPieceDao.java new file mode 100644 index 00000000000..2fb2b97ce2f --- /dev/null +++ b/src/main/java/chess/dao/piece/JdbcPieceDao.java @@ -0,0 +1,64 @@ +package chess.dao.piece; + +import chess.dao.JdbcConnection; +import chess.dao.JdbcTemplate; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceFactory; +import chess.domain.position.Position; +import java.sql.Connection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class JdbcPieceDao implements PieceDao { + + private final Connection connection; + private final JdbcTemplate jdbcTemplate; + + public JdbcPieceDao() { + this.connection = JdbcConnection.getConnection(); + this.jdbcTemplate = new JdbcTemplate(connection); + } + + @Override + public void insert(final long chessGameId, final Position position, final Piece piece) { + final String query = "INSERT INTO piece (chess_game_id, piece_file, piece_rank, color, type) " + + "VALUES (?, ?, ?, ?, ?)"; + jdbcTemplate.executeUpdate(query, chessGameId, + position.getFile().getValue(), + position.getRank().getValue(), + piece.getColor().name(), + piece.pieceType().name()); + } + + @Override + public void delete(final long chessGameId, final Position position) { + final String query = "DELETE FROM piece WHERE piece_file = ? AND piece_rank = ?"; + jdbcTemplate.executeUpdate(query, position.getFile().getValue(), position.getRank().getValue()); + } + + @Override + public Map putPiecesById(final long chessGameId) { + Map board = new HashMap<>(); + + final String query = "SELECT * FROM piece WHERE chess_game_id = ?"; + final List resultParameters = List.of("type", "color", "piece_file", "piece_rank"); + final List result = jdbcTemplate.executeQuery(query, resultParameters, chessGameId); + + int index = 0; + while (index < result.size()) { + final String type = (String) result.get(index++); + final String color = (String) result.get(index++); + final int file = (int) result.get(index++); + final int rank = (int) result.get(index++); + board.put(new Position(file, rank), PieceFactory.getPiece(type, color)); + } + return board; + } + + @Override + public void deleteAll() { + final String query = "DELETE FROM piece"; + jdbcTemplate.executeUpdate(query); + } +} diff --git a/src/main/java/chess/dao/piece/PieceDao.java b/src/main/java/chess/dao/piece/PieceDao.java new file mode 100644 index 00000000000..841318b3b77 --- /dev/null +++ b/src/main/java/chess/dao/piece/PieceDao.java @@ -0,0 +1,14 @@ +package chess.dao.piece; + +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import java.sql.SQLException; +import java.util.Map; + +public interface PieceDao { + + void insert(long chessGameId, Position position, Piece piece); + void delete(long chessGameId, Position position); + Map putPiecesById(long chessGameId) throws SQLException; + void deleteAll(); +} diff --git a/src/main/java/chess/model/ErrorMessage.java b/src/main/java/chess/domain/ErrorMessage.java similarity index 80% rename from src/main/java/chess/model/ErrorMessage.java rename to src/main/java/chess/domain/ErrorMessage.java index 7f28d8e5d8d..95437609756 100644 --- a/src/main/java/chess/model/ErrorMessage.java +++ b/src/main/java/chess/domain/ErrorMessage.java @@ -1,7 +1,7 @@ -package chess.model; +package chess.domain; public enum ErrorMessage { - INVALID_INITIAL_COMMAND("[ERROR] 게임 시작 명령은 start, 게임 종료 명령은 end 여야 합니다."), + INVALID_INITIAL_COMMAND("[ERROR] 이어하기는 continue, 새로하기는 new 입니다."), MISMATCH_FILE_ARGUMENT("[ERROR] 잘못된 파일 규격입니다."), MISMATCH_RANK_ARGUMENT("[ERROR] 잘못된 랭크 규격입니다."), MISMATCH_SYMBOL("[ERROR] 잘못된 피스 타입입니다."), @@ -13,7 +13,10 @@ public enum ErrorMessage { SAME_COLOR_PIECE("[ERROR] 같은 색의 말로는 이동할 수 없습니다."), SAME_POSITION("[ERROR] 출발지와 목적지가 동일하여 이동할 수 없습니다."), INVALID_MOVE_COMMAND("[ERROR] 이동은 move source 위치 target 위치여야 합니다."), - INVALID_COMMAND("[ERROR] 유효하지 않은 명령입니다."); + INVALID_COMMAND("[ERROR] 유효하지 않은 명령입니다."), + + ALREADY_START("[ERROR] 이미 진행 중인 게임입니다."), + NOT_RUNNING("[ERROR] 진행 중인 게임이 아닙니다."); private final String message; diff --git a/src/main/java/chess/model/board/Board.java b/src/main/java/chess/domain/board/Board.java similarity index 74% rename from src/main/java/chess/model/board/Board.java rename to src/main/java/chess/domain/board/Board.java index dba3f4ac175..4823a44888b 100644 --- a/src/main/java/chess/model/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -1,10 +1,10 @@ -package chess.model.board; +package chess.domain.board; -import chess.model.movement.Path; -import chess.model.piece.Piece; -import chess.model.position.Color; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.movement.Path; +import chess.domain.piece.Piece; +import chess.domain.position.Color; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -17,7 +17,7 @@ public Board(final Map board) { this.board = board; } - public Piece move(final Position from, final Position to, final Color turn) { + public Piece moveAndReturnPiece(final Position from, final Position to, final Color turn) { validateMove(from, to, turn); Piece piece = board.remove(from); return board.put(to, piece); @@ -47,11 +47,15 @@ private void validatePath(Position from, Position to) { Set positionsWithOutTarget = new HashSet<>(board.keySet()); positionsWithOutTarget.remove(to); - Path path = source.findPath(from, to); + Path path = source.findPath(from, to, board); path.validateObstacle(positionsWithOutTarget); } public Map getMap() { return Map.copyOf(board); } + + public Piece getPiece(Position position) { + return board.get(position); + } } diff --git a/src/main/java/chess/model/board/InitialBoard.java b/src/main/java/chess/domain/board/BoardFactory.java similarity index 75% rename from src/main/java/chess/model/board/InitialBoard.java rename to src/main/java/chess/domain/board/BoardFactory.java index a9052e9f240..ba089e7c05b 100644 --- a/src/main/java/chess/model/board/InitialBoard.java +++ b/src/main/java/chess/domain/board/BoardFactory.java @@ -1,21 +1,21 @@ -package chess.model.board; +package chess.domain.board; -import chess.model.piece.Piece; -import chess.model.piece.pieces.Bishop; -import chess.model.piece.pieces.King; -import chess.model.piece.pieces.Knight; -import chess.model.piece.pieces.Pawn; -import chess.model.piece.pieces.Queen; -import chess.model.piece.pieces.Rook; -import chess.model.position.Color; -import chess.model.position.Position; -import chess.model.position.InitialPosition; +import chess.domain.piece.Piece; +import chess.domain.piece.pieces.Bishop; +import chess.domain.piece.pieces.King; +import chess.domain.piece.pieces.Knight; +import chess.domain.piece.pieces.Pawn; +import chess.domain.piece.pieces.Queen; +import chess.domain.piece.pieces.Rook; +import chess.domain.position.Color; +import chess.domain.position.Position; +import chess.domain.position.InitialPosition; import java.util.HashMap; import java.util.Map; -public class InitialBoard { +public class BoardFactory { - public Board createInitialBoard() { + public static Board createInitialBoard() { Map board = new HashMap<>(); board.putAll(createPawn()); @@ -28,7 +28,7 @@ public Board createInitialBoard() { return new Board(board); } - private Map createPawn() { + private static Map createPawn() { Map pieceMap = new HashMap<>(); for (InitialPosition position : InitialPosition.values()) { if (position.name().startsWith("WHITE_PAWN")) { @@ -41,21 +41,21 @@ private Map createPawn() { return pieceMap; } - private Map createKing() { + private static Map createKing() { Map pieceMap = new HashMap<>(); pieceMap.put(InitialPosition.WHITE_KING.getPosition(), new King(Color.WHITE)); pieceMap.put(InitialPosition.BLACK_KING.getPosition(), new King(Color.BLACK)); return pieceMap; } - private Map createQueen() { + private static Map createQueen() { Map pieceMap = new HashMap<>(); pieceMap.put(InitialPosition.WHITE_QUEEN.getPosition(), new Queen(Color.WHITE)); pieceMap.put(InitialPosition.BLACK_QUEEN.getPosition(), new Queen(Color.BLACK)); return pieceMap; } - private Map createBishop() { + private static Map createBishop() { Map pieceMap = new HashMap<>(); pieceMap.put(InitialPosition.WHITE_BISHOP_LEFT.getPosition(), new Bishop(Color.WHITE)); pieceMap.put(InitialPosition.WHITE_BISHOP_RIGHT.getPosition(), new Bishop(Color.WHITE)); @@ -64,7 +64,7 @@ private Map createBishop() { return pieceMap; } - private Map createKnight() { + private static Map createKnight() { Map pieceMap = new HashMap<>(); pieceMap.put(InitialPosition.WHITE_KNIGHT_LEFT.getPosition(), new Knight(Color.WHITE)); pieceMap.put(InitialPosition.WHITE_KNIGHT_RIGHT.getPosition(), new Knight(Color.WHITE)); @@ -73,7 +73,7 @@ private Map createKnight() { return pieceMap; } - private Map createRook() { + private static Map createRook() { Map pieceMap = new HashMap<>(); pieceMap.put(InitialPosition.WHITE_ROOK_LEFT.getPosition(), new Rook(Color.WHITE)); pieceMap.put(InitialPosition.WHITE_ROOK_RIGHT.getPosition(), new Rook(Color.WHITE)); diff --git a/src/main/java/chess/domain/game/ChessGame.java b/src/main/java/chess/domain/game/ChessGame.java new file mode 100644 index 00000000000..54b3fdde59a --- /dev/null +++ b/src/main/java/chess/domain/game/ChessGame.java @@ -0,0 +1,83 @@ +package chess.domain.game; + +import chess.domain.ErrorMessage; +import chess.domain.board.Board; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceInfo; +import chess.domain.position.Color; +import chess.domain.position.Position; + +public class ChessGame { + + private final long id; + private State state; + private Color turn; + private final Board board; + + public ChessGame(long id, Board board, Color turn) { + this.id = id; + this.state = State.WAITING; + this.board = board; + this.turn = turn; + } + + public void start() { + if (state == State.RUNNING) { + throw new IllegalArgumentException(ErrorMessage.ALREADY_START.getMessage()); + } + this.state = State.RUNNING; + } + + public void end() { + this.state = State.ENDED; + } + + public void kingIsCaptured() { + this.state = State.KING_IS_CAPTURED; + } + + public void movePiece(Position source, Position target) { + checkRunning(); + Piece capturedPiece = board.moveAndReturnPiece(source, target, turn); + checkKingCaptured(capturedPiece); + changeTurn(); + } + + public Double calculateScore(Color color) { + checkRunning(); + return ScoreCalculator.calculate(board.getMap(), color); + } + + private void checkRunning() { + if (state == State.RUNNING) { + return; + } + throw new IllegalArgumentException(ErrorMessage.NOT_RUNNING.getMessage()); + } + + private void checkKingCaptured(Piece capturedPiece) { + if (capturedPiece != null && capturedPiece.pieceType() == PieceInfo.KING) { + kingIsCaptured(); + } + } + + private void changeTurn() { + turn = turn.changeTurn(); + } + + public Color getTurn() { + return turn; + } + + public long getId() { + return id; + } + + public Board getBoard() { + return board; + } + + public State getState() { + return state; + } +} diff --git a/src/main/java/chess/model/score/ScoreCalculator.java b/src/main/java/chess/domain/game/ScoreCalculator.java similarity index 87% rename from src/main/java/chess/model/score/ScoreCalculator.java rename to src/main/java/chess/domain/game/ScoreCalculator.java index 38d2462f539..ea8144358bb 100644 --- a/src/main/java/chess/model/score/ScoreCalculator.java +++ b/src/main/java/chess/domain/game/ScoreCalculator.java @@ -1,9 +1,9 @@ -package chess.model.score; +package chess.domain.game; -import chess.model.piece.Piece; -import chess.model.piece.PieceInfo; -import chess.model.position.Color; -import chess.model.position.Position; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceInfo; +import chess.domain.position.Color; +import chess.domain.position.Position; import java.util.List; import java.util.Map; diff --git a/src/main/java/chess/domain/game/State.java b/src/main/java/chess/domain/game/State.java new file mode 100644 index 00000000000..51b84149f0d --- /dev/null +++ b/src/main/java/chess/domain/game/State.java @@ -0,0 +1,8 @@ +package chess.domain.game; + +public enum State { + WAITING, + RUNNING, + ENDED, + KING_IS_CAPTURED +} diff --git a/src/main/java/chess/model/movement/Movement.java b/src/main/java/chess/domain/movement/Movement.java similarity index 94% rename from src/main/java/chess/model/movement/Movement.java rename to src/main/java/chess/domain/movement/Movement.java index c35ab9262f3..e4072f54501 100644 --- a/src/main/java/chess/model/movement/Movement.java +++ b/src/main/java/chess/domain/movement/Movement.java @@ -1,6 +1,6 @@ -package chess.model.movement; +package chess.domain.movement; -import chess.model.ErrorMessage; +import chess.domain.ErrorMessage; import java.util.Arrays; public enum Movement { diff --git a/src/main/java/chess/model/movement/MovementConverter.java b/src/main/java/chess/domain/movement/MovementConverter.java similarity index 87% rename from src/main/java/chess/model/movement/MovementConverter.java rename to src/main/java/chess/domain/movement/MovementConverter.java index 7944c55aa45..46a12bbea75 100644 --- a/src/main/java/chess/model/movement/MovementConverter.java +++ b/src/main/java/chess/domain/movement/MovementConverter.java @@ -1,10 +1,11 @@ -package chess.model.movement; +package chess.domain.movement; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; import java.math.BigInteger; public class MovementConverter { + public static Movement convertMovement(Position from, Position to) { int fileDiff = to.calculateFileDiff(from); int rankDiff = to.calculateRankDiff(from); diff --git a/src/main/java/chess/model/movement/Path.java b/src/main/java/chess/domain/movement/Path.java similarity index 83% rename from src/main/java/chess/model/movement/Path.java rename to src/main/java/chess/domain/movement/Path.java index b603a2ecbb1..0615d0418b5 100644 --- a/src/main/java/chess/model/movement/Path.java +++ b/src/main/java/chess/domain/movement/Path.java @@ -1,7 +1,7 @@ -package chess.model.movement; +package chess.domain.movement; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; import java.util.ArrayList; import java.util.List; import java.util.Set; diff --git a/src/main/java/chess/model/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java similarity index 69% rename from src/main/java/chess/model/piece/Piece.java rename to src/main/java/chess/domain/piece/Piece.java index 2bf9e807b50..acda0baede2 100644 --- a/src/main/java/chess/model/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -1,11 +1,12 @@ -package chess.model.piece; +package chess.domain.piece; -import chess.model.movement.Movement; -import chess.model.movement.Path; -import chess.model.position.Color; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.movement.Movement; +import chess.domain.movement.Path; +import chess.domain.position.Color; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; import java.util.List; +import java.util.Map; import java.util.Objects; public abstract class Piece { @@ -16,7 +17,7 @@ public Piece(final Color color) { this.color = color; } - public abstract Path findPath(Position from, Position to); + public abstract Path findPath(Position from, Position to, Map board); public abstract PieceInfo pieceType(); @@ -43,10 +44,13 @@ public void validateTurn(Color turn) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Piece piece = (Piece) o; - return color == piece.color && this.getClass().equals(piece.getClass()); + if (this == o) { + return true; + } + if (!(o instanceof Piece piece)) { + return false; + } + return color == piece.color; } @Override diff --git a/src/main/java/chess/domain/piece/PieceFactory.java b/src/main/java/chess/domain/piece/PieceFactory.java new file mode 100644 index 00000000000..f5c37adb4e3 --- /dev/null +++ b/src/main/java/chess/domain/piece/PieceFactory.java @@ -0,0 +1,59 @@ +package chess.domain.piece; + +import chess.domain.piece.pieces.Bishop; +import chess.domain.piece.pieces.King; +import chess.domain.piece.pieces.Knight; +import chess.domain.piece.pieces.Pawn; +import chess.domain.piece.pieces.Queen; +import chess.domain.piece.pieces.Rook; +import chess.domain.position.Color; + +public enum PieceFactory { + PAWN { + @Override + public Piece createPiece(Color color) { + return new Pawn(color); + } + }, + ROOK { + @Override + public Piece createPiece(Color color) { + return new Rook(color); + } + }, + KNIGHT { + @Override + public Piece createPiece(Color color) { + return new Knight(color); + } + }, + BISHOP { + @Override + public Piece createPiece(Color color) { + return new Bishop(color); + } + }, + QUEEN { + @Override + public Piece createPiece(Color color) { + return new Queen(color); + } + }, + KING { + @Override + public Piece createPiece(Color color) { + return new King(color); + } + }; + + public abstract Piece createPiece(Color color); + + public static Piece getPiece(String type, String color) { + for (PieceFactory pieceFactory : PieceFactory.values()) { + if (pieceFactory.name().equalsIgnoreCase(type)) { + return pieceFactory.createPiece(Color.valueOf(color.toUpperCase())); + } + } + return null; + } +} diff --git a/src/main/java/chess/model/piece/PieceInfo.java b/src/main/java/chess/domain/piece/PieceInfo.java similarity index 90% rename from src/main/java/chess/model/piece/PieceInfo.java rename to src/main/java/chess/domain/piece/PieceInfo.java index c981a90f888..9d5e62edfb0 100644 --- a/src/main/java/chess/model/piece/PieceInfo.java +++ b/src/main/java/chess/domain/piece/PieceInfo.java @@ -1,4 +1,4 @@ -package chess.model.piece; +package chess.domain.piece; public enum PieceInfo { KING(0), diff --git a/src/main/java/chess/model/piece/pieces/Bishop.java b/src/main/java/chess/domain/piece/pieces/Bishop.java similarity index 68% rename from src/main/java/chess/model/piece/pieces/Bishop.java rename to src/main/java/chess/domain/piece/pieces/Bishop.java index 5594f9c7ddb..7a1790889c7 100644 --- a/src/main/java/chess/model/piece/pieces/Bishop.java +++ b/src/main/java/chess/domain/piece/pieces/Bishop.java @@ -1,15 +1,16 @@ -package chess.model.piece.pieces; +package chess.domain.piece.pieces; -import static chess.model.movement.MovementConverter.convertMovement; +import static chess.domain.movement.MovementConverter.convertMovement; -import chess.model.movement.Movement; -import chess.model.movement.Path; -import chess.model.piece.Piece; -import chess.model.piece.PieceInfo; -import chess.model.position.Color; -import chess.model.position.Position; +import chess.domain.movement.Movement; +import chess.domain.movement.Path; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceInfo; +import chess.domain.position.Color; +import chess.domain.position.Position; import java.util.ArrayList; import java.util.List; +import java.util.Map; public class Bishop extends Piece { @@ -21,7 +22,7 @@ public Bishop(final Color color) { } @Override - public Path findPath(Position from, Position to) { + public Path findPath(Position from, Position to, final Map board) { Movement movement = convertMovement(from, to); validateMovement(movement, availableMovements); diff --git a/src/main/java/chess/model/piece/pieces/King.java b/src/main/java/chess/domain/piece/pieces/King.java similarity index 67% rename from src/main/java/chess/model/piece/pieces/King.java rename to src/main/java/chess/domain/piece/pieces/King.java index 63dfff1a2fa..4fcd2bf7a9f 100644 --- a/src/main/java/chess/model/piece/pieces/King.java +++ b/src/main/java/chess/domain/piece/pieces/King.java @@ -1,15 +1,16 @@ -package chess.model.piece.pieces; +package chess.domain.piece.pieces; -import static chess.model.movement.MovementConverter.convertMovement; +import static chess.domain.movement.MovementConverter.convertMovement; -import chess.model.movement.Movement; -import chess.model.movement.Path; -import chess.model.piece.Piece; -import chess.model.piece.PieceInfo; -import chess.model.position.Color; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.movement.Movement; +import chess.domain.movement.Path; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceInfo; +import chess.domain.position.Color; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; import java.util.List; +import java.util.Map; public class King extends Piece { @@ -22,7 +23,7 @@ public King(final Color color) { } @Override - public Path findPath(Position from, Position to) { + public Path findPath(Position from, Position to, final Map board) { Movement movement = convertMovement(from, to); validateMovement(movement, availableMovements); diff --git a/src/main/java/chess/model/piece/pieces/Knight.java b/src/main/java/chess/domain/piece/pieces/Knight.java similarity index 68% rename from src/main/java/chess/model/piece/pieces/Knight.java rename to src/main/java/chess/domain/piece/pieces/Knight.java index 433cbae3d40..ff0a51fc782 100644 --- a/src/main/java/chess/model/piece/pieces/Knight.java +++ b/src/main/java/chess/domain/piece/pieces/Knight.java @@ -1,16 +1,17 @@ -package chess.model.piece.pieces; +package chess.domain.piece.pieces; -import static chess.model.movement.MovementConverter.convertMovement; +import static chess.domain.movement.MovementConverter.convertMovement; -import chess.model.movement.Movement; -import chess.model.movement.Path; -import chess.model.piece.Piece; -import chess.model.piece.PieceInfo; -import chess.model.position.Color; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.movement.Movement; +import chess.domain.movement.Path; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceInfo; +import chess.domain.position.Color; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; import java.util.List; +import java.util.Map; public class Knight extends Piece { @@ -25,7 +26,7 @@ public Knight(final Color color) { } @Override - public Path findPath(Position from, Position to) { + public Path findPath(Position from, Position to, final Map board) { Movement movement = convertMovement(from, to); validateMovement(movement, availableMovements); diff --git a/src/main/java/chess/model/piece/pieces/Pawn.java b/src/main/java/chess/domain/piece/pieces/Pawn.java similarity index 69% rename from src/main/java/chess/model/piece/pieces/Pawn.java rename to src/main/java/chess/domain/piece/pieces/Pawn.java index 153b48874aa..e5cc5f1b479 100644 --- a/src/main/java/chess/model/piece/pieces/Pawn.java +++ b/src/main/java/chess/domain/piece/pieces/Pawn.java @@ -1,18 +1,18 @@ -package chess.model.piece.pieces; +package chess.domain.piece.pieces; -import chess.model.movement.Movement; -import chess.model.movement.Path; -import chess.model.piece.Piece; -import chess.model.piece.PieceInfo; -import chess.model.position.Color; -import chess.model.position.Position; +import chess.domain.movement.Movement; +import chess.domain.movement.Path; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceInfo; +import chess.domain.position.Color; +import chess.domain.position.Position; -import chess.model.ErrorMessage; +import chess.domain.ErrorMessage; import java.util.List; import java.util.Map; -import static chess.model.movement.Movement.*; -import static chess.model.movement.MovementConverter.convertMovement; +import static chess.domain.movement.Movement.*; +import static chess.domain.movement.MovementConverter.convertMovement; public class Pawn extends Piece { @@ -41,25 +41,29 @@ public Pawn(final Color color) { } @Override - public Path findPath(final Position from, final Position to) { + public Path findPath(final Position from, final Position to, final Map board) { Movement movement = convertMovement(from, to); - - if (isAttackMovement(movement)) { // 공격 움직임인 경우 - return findPathForAttackMovement(to, movement); + if (isAttackMovement(movement)) { + return findPathForAttackMovement(to, movement, board); } - - return findPathForEmptyPosition(from, to, movement); // 빈 칸으로 이동하는 경우 + return findPathForEmptyPosition(from, to, movement); } private boolean isAttackMovement(final Movement movement) { return AVAILABLE_ATTACK_MOVEMENTS.get(getColor()).contains(movement); } - private Path findPathForAttackMovement(final Position to, final Movement movement) { + private Path findPathForAttackMovement(final Position to, final Movement movement, final Map board) { validateMovement(movement, AVAILABLE_ATTACK_MOVEMENTS.get(getColor())); - + validateAttackMovement(to, board); return new Path(List.of(to)); } + private void validateAttackMovement(final Position to, final Map board) { + Piece targetPiece = board.get(to); + if (targetPiece == null || targetPiece.getColor() == this.getColor()) { + throw new IllegalArgumentException(ErrorMessage.INVALID_DIRECTION.getMessage()); + } + } private Path findPathForEmptyPosition(final Position from, final Position to, final Movement movement) { validateMovement(movement, List.of(AVAILABLE_MOVEMENTS.get(getColor()))); diff --git a/src/main/java/chess/model/piece/pieces/Queen.java b/src/main/java/chess/domain/piece/pieces/Queen.java similarity index 69% rename from src/main/java/chess/model/piece/pieces/Queen.java rename to src/main/java/chess/domain/piece/pieces/Queen.java index cdc7b892758..6fa7ec6d7e8 100644 --- a/src/main/java/chess/model/piece/pieces/Queen.java +++ b/src/main/java/chess/domain/piece/pieces/Queen.java @@ -1,17 +1,18 @@ -package chess.model.piece.pieces; +package chess.domain.piece.pieces; -import static chess.model.movement.MovementConverter.convertMovement; +import static chess.domain.movement.MovementConverter.convertMovement; -import chess.model.movement.Movement; -import chess.model.movement.Path; -import chess.model.piece.Piece; -import chess.model.piece.PieceInfo; -import chess.model.position.Color; -import chess.model.position.Position; +import chess.domain.movement.Movement; +import chess.domain.movement.Path; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceInfo; +import chess.domain.position.Color; +import chess.domain.position.Position; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; public class Queen extends Piece { @@ -25,7 +26,7 @@ public Queen(final Color color) { } @Override - public Path findPath(final Position from, final Position to) { + public Path findPath(final Position from, final Position to, final Map board) { Movement movement = convertMovement(from, to); validateMovement(movement, availableMovements); diff --git a/src/main/java/chess/model/piece/pieces/Rook.java b/src/main/java/chess/domain/piece/pieces/Rook.java similarity index 66% rename from src/main/java/chess/model/piece/pieces/Rook.java rename to src/main/java/chess/domain/piece/pieces/Rook.java index db9e086f431..c116a4c9c99 100644 --- a/src/main/java/chess/model/piece/pieces/Rook.java +++ b/src/main/java/chess/domain/piece/pieces/Rook.java @@ -1,17 +1,18 @@ -package chess.model.piece.pieces; +package chess.domain.piece.pieces; -import static chess.model.movement.MovementConverter.convertMovement; +import static chess.domain.movement.MovementConverter.convertMovement; -import chess.model.movement.Movement; -import chess.model.movement.Path; -import chess.model.piece.Piece; -import chess.model.piece.PieceInfo; -import chess.model.position.Color; -import chess.model.position.Position; +import chess.domain.movement.Movement; +import chess.domain.movement.Path; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceInfo; +import chess.domain.position.Color; +import chess.domain.position.Position; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; public class Rook extends Piece { @@ -24,7 +25,7 @@ public Rook(final Color color) { } @Override - public Path findPath(final Position from, final Position to) { + public Path findPath(final Position from, final Position to, final Map board) { Movement movement = convertMovement(from, to); validateMovement(movement, availableMovements); diff --git a/src/main/java/chess/model/position/Color.java b/src/main/java/chess/domain/position/Color.java similarity index 60% rename from src/main/java/chess/model/position/Color.java rename to src/main/java/chess/domain/position/Color.java index 331da6b40c6..cd0312c6582 100644 --- a/src/main/java/chess/model/position/Color.java +++ b/src/main/java/chess/domain/position/Color.java @@ -1,7 +1,6 @@ -package chess.model.position; +package chess.domain.position; public enum Color { - BLACK, WHITE; @@ -9,8 +8,8 @@ public boolean isSameColor(Color color) { return this == color; } - public Color changeTurn(Color currentTurn) { - if (currentTurn == WHITE) { + public Color changeTurn() { + if (this == WHITE) { return BLACK; } return WHITE; diff --git a/src/main/java/chess/model/position/File.java b/src/main/java/chess/domain/position/File.java similarity index 90% rename from src/main/java/chess/model/position/File.java rename to src/main/java/chess/domain/position/File.java index 621758ecfb7..662d03b7824 100644 --- a/src/main/java/chess/model/position/File.java +++ b/src/main/java/chess/domain/position/File.java @@ -1,6 +1,6 @@ -package chess.model.position; +package chess.domain.position; -import chess.model.ErrorMessage; +import chess.domain.ErrorMessage; import java.util.Arrays; public enum File { diff --git a/src/main/java/chess/model/position/InitialPosition.java b/src/main/java/chess/domain/position/InitialPosition.java similarity index 97% rename from src/main/java/chess/model/position/InitialPosition.java rename to src/main/java/chess/domain/position/InitialPosition.java index 892b59bbfb4..9034643004f 100644 --- a/src/main/java/chess/model/position/InitialPosition.java +++ b/src/main/java/chess/domain/position/InitialPosition.java @@ -1,4 +1,4 @@ -package chess.model.position; +package chess.domain.position; public enum InitialPosition { WHITE_ROOK_LEFT(1, 1), diff --git a/src/main/java/chess/model/position/Position.java b/src/main/java/chess/domain/position/Position.java similarity index 93% rename from src/main/java/chess/model/position/Position.java rename to src/main/java/chess/domain/position/Position.java index 276e5b596c8..66ddd33f898 100644 --- a/src/main/java/chess/model/position/Position.java +++ b/src/main/java/chess/domain/position/Position.java @@ -1,9 +1,10 @@ -package chess.model.position; +package chess.domain.position; -import chess.model.movement.Movement; +import chess.domain.movement.Movement; import java.util.Objects; public class Position { + private final File file; private final Rank rank; diff --git a/src/main/java/chess/model/position/Rank.java b/src/main/java/chess/domain/position/Rank.java similarity index 90% rename from src/main/java/chess/model/position/Rank.java rename to src/main/java/chess/domain/position/Rank.java index c8877ce568b..c67982f1ae9 100644 --- a/src/main/java/chess/model/position/Rank.java +++ b/src/main/java/chess/domain/position/Rank.java @@ -1,6 +1,6 @@ -package chess.model.position; +package chess.domain.position; -import chess.model.ErrorMessage; +import chess.domain.ErrorMessage; import java.util.Arrays; public enum Rank { diff --git a/src/main/java/chess/model/command/CommandFactory.java b/src/main/java/chess/model/command/CommandFactory.java deleted file mode 100644 index f33fc03b5a8..00000000000 --- a/src/main/java/chess/model/command/CommandFactory.java +++ /dev/null @@ -1,58 +0,0 @@ -package chess.model.command; - -import chess.model.command.commands.EndCommand; -import chess.model.command.commands.MoveCommand; -import chess.model.command.commands.StartCommand; -import chess.model.ErrorMessage; -import chess.model.command.commands.StatusCommand; -import chess.model.position.Position; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -public class CommandFactory { - - public static final String START_COMMAND = "start"; - public static final String END_COMMAND = "end"; - public static final String MOVE_COMMAND = "move"; - public static final String STATUS_COMMAND = "status"; - private static final int COMMAND_INDEX = 0; - private static final int SOURCE_POSITION_INDEX = 1; - private static final int TARGET_POSITION_INDEX = 2; - private static final int MOVE_COMMAND_PARTS_COUNT = 3; - - private static final Map, CommandLauncher>> - commandMap = new HashMap<>(); - - static { - commandMap.put(START_COMMAND, parts -> new StartCommand()); - commandMap.put(END_COMMAND, parts -> new EndCommand()); - commandMap.put(MOVE_COMMAND, parts -> { - if (parts.size() != MOVE_COMMAND_PARTS_COUNT) { - throw new IllegalArgumentException(ErrorMessage.INVALID_MOVE_COMMAND.getMessage()); - } - Position source = parsePosition(parts.get(SOURCE_POSITION_INDEX)); - Position target = parsePosition(parts.get(TARGET_POSITION_INDEX)); - return new MoveCommand(source, target); - }); - commandMap.put(STATUS_COMMAND, parts -> new StatusCommand()); - } - - public CommandLauncher createCommand(String inputCommand) { - List commandParts = List.of(inputCommand.split(" ")); - String command = commandParts.get(COMMAND_INDEX); - - Function, CommandLauncher> commandCreator = commandMap.get(command); - if (commandCreator == null) { - throw new IllegalArgumentException(ErrorMessage.INVALID_COMMAND.getMessage()); - } - return commandCreator.apply(commandParts); - } - - private static Position parsePosition(String position) { - int file = position.charAt(0) - 'a' + 1; - int rank = position.charAt(1) - '0'; - return new Position(file, rank); // 생성자에 1~8 유효성 검사가 있다 - } -} diff --git a/src/main/java/chess/model/command/CommandLauncher.java b/src/main/java/chess/model/command/CommandLauncher.java deleted file mode 100644 index 986e46aa096..00000000000 --- a/src/main/java/chess/model/command/CommandLauncher.java +++ /dev/null @@ -1,9 +0,0 @@ -package chess.model.command; - -import chess.controller.ChessController; - -public interface CommandLauncher { - void execute(ChessController controller); - boolean validateInitialCommandType(); - boolean validateStatusCommandType(); -} diff --git a/src/main/java/chess/model/command/commands/EndCommand.java b/src/main/java/chess/model/command/commands/EndCommand.java deleted file mode 100644 index a64a657b262..00000000000 --- a/src/main/java/chess/model/command/commands/EndCommand.java +++ /dev/null @@ -1,21 +0,0 @@ -package chess.model.command.commands; - -import chess.controller.ChessController; -import chess.model.command.CommandLauncher; - -public class EndCommand implements CommandLauncher { - @Override - public void execute(ChessController controller) { - controller.endGame(); - } - - @Override - public boolean validateInitialCommandType() { - return true; - } - - @Override - public boolean validateStatusCommandType() { - return false; - } -} diff --git a/src/main/java/chess/model/command/commands/MoveCommand.java b/src/main/java/chess/model/command/commands/MoveCommand.java deleted file mode 100644 index 40eeafca1c3..00000000000 --- a/src/main/java/chess/model/command/commands/MoveCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -package chess.model.command.commands; - -import chess.controller.ChessController; -import chess.model.command.CommandLauncher; -import chess.model.position.Position; - -public class MoveCommand implements CommandLauncher { - private final Position source; - private final Position target; - - public MoveCommand(Position source, Position target) { - this.source = source; - this.target = target; - } - - @Override - public void execute(ChessController controller) { - controller.movePiece(source, target); - } - - @Override - public boolean validateInitialCommandType() { - return false; - } - - @Override - public boolean validateStatusCommandType() { - return false; - } -} diff --git a/src/main/java/chess/model/command/commands/StartCommand.java b/src/main/java/chess/model/command/commands/StartCommand.java deleted file mode 100644 index 7361d882445..00000000000 --- a/src/main/java/chess/model/command/commands/StartCommand.java +++ /dev/null @@ -1,21 +0,0 @@ -package chess.model.command.commands; - -import chess.controller.ChessController; -import chess.model.command.CommandLauncher; - -public class StartCommand implements CommandLauncher { - @Override - public void execute(ChessController controller) { - controller.startGame(); - } - - @Override - public boolean validateInitialCommandType() { - return true; - } - - @Override - public boolean validateStatusCommandType() { - return false; - } -} diff --git a/src/main/java/chess/model/command/commands/StatusCommand.java b/src/main/java/chess/model/command/commands/StatusCommand.java deleted file mode 100644 index a995bce0542..00000000000 --- a/src/main/java/chess/model/command/commands/StatusCommand.java +++ /dev/null @@ -1,21 +0,0 @@ -package chess.model.command.commands; - -import chess.controller.ChessController; -import chess.model.command.CommandLauncher; - -public class StatusCommand implements CommandLauncher { - @Override - public void execute(ChessController controller) { - controller.calculateAndPrintCurrentTurnScore(); - } - - @Override - public boolean validateInitialCommandType() { - return false; - } - - @Override - public boolean validateStatusCommandType() { - return true; - } -} diff --git a/src/main/java/chess/service/ChessGameService.java b/src/main/java/chess/service/ChessGameService.java new file mode 100644 index 00000000000..7504fca025d --- /dev/null +++ b/src/main/java/chess/service/ChessGameService.java @@ -0,0 +1,59 @@ +package chess.service; + +import chess.dao.chessGame.ChessGameDao; +import chess.dao.chessGame.dto.FindResponseDto; +import chess.dao.chessGame.dto.SaveRequestDto; +import chess.dao.piece.PieceDao; +import chess.domain.board.Board; +import chess.domain.board.BoardFactory; +import chess.domain.game.ChessGame; +import chess.domain.piece.Piece; +import chess.domain.position.Color; +import chess.domain.position.Position; +import java.sql.SQLException; +import java.util.Map; + +public class ChessGameService { + + private static final long TEMPORAL_ID = 1; + + private final ChessGameDao chessGameDao; + private final PieceDao pieceDao; + + public ChessGameService(final ChessGameDao chessGameDao, final PieceDao pieceDao) { + this.chessGameDao = chessGameDao; + this.pieceDao = pieceDao; + } + + public ChessGame findChessGame() throws SQLException { + final FindResponseDto findResponseDto = chessGameDao.findChessGameById(TEMPORAL_ID); + return new ChessGame(findResponseDto.getId(), + new Board(pieceDao.putPiecesById(TEMPORAL_ID)), findResponseDto.getTurn()); + } + + public ChessGame initializeChessGame() { + Color turn = Color.WHITE; + final Long id = chessGameDao.save(new SaveRequestDto(turn)); + final ChessGame chessGame = new ChessGame(id, BoardFactory.createInitialBoard(), turn); + final Map board = chessGame.getBoard().getMap(); + + for (final Map.Entry entry : board.entrySet()) { + pieceDao.insert(id, entry.getKey(), entry.getValue()); + } + return chessGame; + } + + public void updatePiece(final ChessGame chessGame, final Position fromPosition, final Position toPosition) { + final Piece piece = chessGame.getBoard().getPiece(toPosition); + + pieceDao.delete(chessGame.getId(), toPosition); + pieceDao.insert(chessGame.getId(), toPosition, piece); + pieceDao.delete(chessGame.getId(), fromPosition); + chessGameDao.updateTurn(chessGame.getId(), chessGame.getTurn()); + } + + public void deleteChessGame() { + chessGameDao.delete(); + pieceDao.deleteAll(); + } +} diff --git a/src/main/java/chess/view/InputView.java b/src/main/java/chess/view/InputView.java index 16dfa075524..8f4c11b44bb 100644 --- a/src/main/java/chess/view/InputView.java +++ b/src/main/java/chess/view/InputView.java @@ -6,7 +6,18 @@ public class InputView { private static final Scanner scanner = new Scanner(System.in); - public String receiveCommand() { + public static String receiveInitialCommand() { + System.out.println("> 체스 게임을 시작합니다."); + System.out.println("> 이어하기 : continue"); + System.out.println("> 새로하기 : new"); + return scanner.nextLine(); + } + + public static String receiveCommand() { + System.out.println("> 게임 시작 : start"); + System.out.println("> 게임 종료 : end"); + System.out.println("> 게임 이동 : move source 위치 target 위치 - 예. move b2 b3"); + System.out.println("> 점수 출력 : status"); return scanner.nextLine(); } } diff --git a/src/main/java/chess/view/OutputView.java b/src/main/java/chess/view/OutputView.java index 0226556f594..1804cc55854 100644 --- a/src/main/java/chess/view/OutputView.java +++ b/src/main/java/chess/view/OutputView.java @@ -1,10 +1,10 @@ package chess.view; -import chess.model.piece.Piece; -import chess.model.position.Color; -import chess.model.position.File; -import chess.model.position.Position; -import chess.model.position.Rank; +import chess.domain.piece.Piece; +import chess.domain.position.Color; +import chess.domain.position.File; +import chess.domain.position.Position; +import chess.domain.position.Rank; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -17,29 +17,47 @@ public class OutputView { private static final int BOARD_SIZE = 8; private static final int INDEX_OFFSET = 1; - public void printStartMessage() { - System.out.println("> 체스 게임을 시작합니다."); - System.out.println("> 게임 시작 : start"); - System.out.println("> 게임 종료 : end"); - System.out.println("> 게임 이동 : move source위치 target위치 - 예. move b2 b3"); - } - - public void printBoard(final Map boardMap) { + public static void printBoard(final Map boardMap) { List> chessBoard = createEmptyBoard(); assignSymbols(boardMap, chessBoard); chessBoard.stream() .map(board -> String.join("", board)) .forEach(System.out::println); + } + + public static void printScore(final Double whiteScore, final Double blackScore) { + System.out.println("White Score: " + whiteScore); + System.out.println("Black Score: " + blackScore); + } + + public static void printWinningColor(Color winningColor) { + System.out.println("Winning Color: " + winningColor); + } + + public static void printDraw() { + System.out.println("Draw"); + } + + public static void printErrorMessage(final Exception e) { + System.out.println(e.getMessage()); System.out.print(System.lineSeparator()); } - public void printCurrentTurnScore(Color currentTurn, double score) { - System.out.println("Current turn: " + currentTurn); - System.out.println("Score: " + score); + public static void printNewGameMessage() { + System.out.println("새로운 게임을 시작합니다."); + } + + public static void printContinueMessage() { + System.out.println("진행 중인 게임을 이어합니다."); + } + + public static void printNoExistsRunningGameMessage() { + System.out.println("진행 중인 게임이 없습니다."); } - private void assignSymbols(final Map boardMap, final List> chessBoard) { + private static void assignSymbols(final Map boardMap, + final List> chessBoard) { for (final Map.Entry positionPieceEntry : boardMap.entrySet()) { final Position position = positionPieceEntry.getKey(); final Piece piece = positionPieceEntry.getValue(); @@ -53,7 +71,7 @@ private void assignSymbols(final Map boardMap, final List> createEmptyBoard() { + private static List> createEmptyBoard() { return IntStream.range(0, BOARD_SIZE) .mapToObj(it -> new ArrayList<>(Collections.nCopies(BOARD_SIZE, Symbol.EMPTY.getSymbol()))) .collect(Collectors.toList()); diff --git a/src/main/java/chess/view/Symbol.java b/src/main/java/chess/view/Symbol.java index 9c797ab125e..4e6797a0665 100644 --- a/src/main/java/chess/view/Symbol.java +++ b/src/main/java/chess/view/Symbol.java @@ -1,9 +1,9 @@ package chess.view; -import chess.model.ErrorMessage; -import chess.model.piece.Piece; -import chess.model.piece.pieces.*; -import chess.model.position.Color; +import chess.domain.ErrorMessage; +import chess.domain.piece.Piece; +import chess.domain.piece.pieces.*; +import chess.domain.position.Color; public enum Symbol { KING("k", King.class), diff --git a/src/test/java/chess/model/board/InitialBoardTest.java b/src/test/java/chess/domain/board/BoardFactoryTest.java similarity index 76% rename from src/test/java/chess/model/board/InitialBoardTest.java rename to src/test/java/chess/domain/board/BoardFactoryTest.java index 074a9e3bd58..be268ac643d 100644 --- a/src/test/java/chess/model/board/InitialBoardTest.java +++ b/src/test/java/chess/domain/board/BoardFactoryTest.java @@ -1,9 +1,9 @@ -package chess.model.board; +package chess.domain.board; -import chess.model.piece.Piece; -import chess.model.piece.pieces.*; -import chess.model.position.Color; -import chess.model.position.Position; +import chess.domain.piece.Piece; +import chess.domain.piece.pieces.*; +import chess.domain.position.Color; +import chess.domain.position.Position; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -11,13 +11,13 @@ import java.util.Map; -public class InitialBoardTest { +public class BoardFactoryTest { @Test @DisplayName("초기 보드에는 정확히 32개의 말이 있어야 한다") public void 초기_보드에는_정확히_32개의_말이_있어야_한다() { - InitialBoard initialBoard = new InitialBoard(); - Board board = initialBoard.createInitialBoard(); + BoardFactory boardFactory = new BoardFactory(); + Board board = boardFactory.createInitialBoard(); Map boardMap = board.getMap(); assertEquals(32, boardMap.size()); @@ -26,8 +26,8 @@ public class InitialBoardTest { @Test @DisplayName("초기 보드에는 각 말의 정확한 수가 있어야 한다") public void 초기_보드에는_각_말의_정확한_수가_있어야_한다() { - InitialBoard initialBoard = new InitialBoard(); - Board board = initialBoard.createInitialBoard(); + BoardFactory boardFactory = new BoardFactory(); + Board board = boardFactory.createInitialBoard(); Map boardMap = board.getMap(); long whitePawns = boardMap.values().stream().filter(piece -> piece instanceof Pawn && piece.getColor() == Color.WHITE).count(); diff --git a/src/test/java/chess/model/board/BoardTest.java b/src/test/java/chess/domain/board/BoardTest.java similarity index 84% rename from src/test/java/chess/model/board/BoardTest.java rename to src/test/java/chess/domain/board/BoardTest.java index 9c9e5b112d8..feb2f4c1558 100644 --- a/src/test/java/chess/model/board/BoardTest.java +++ b/src/test/java/chess/domain/board/BoardTest.java @@ -1,17 +1,17 @@ -package chess.model.board; +package chess.domain.board; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import chess.model.piece.Piece; -import chess.model.piece.PieceInfo; -import chess.model.piece.pieces.King; -import chess.model.piece.pieces.Pawn; -import chess.model.piece.pieces.Rook; -import chess.model.position.Color; -import chess.model.position.InitialPosition; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceInfo; +import chess.domain.piece.pieces.King; +import chess.domain.piece.pieces.Pawn; +import chess.domain.piece.pieces.Rook; +import chess.domain.position.Color; +import chess.domain.position.InitialPosition; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.BeforeEach; @@ -52,7 +52,7 @@ void setUp() { Position to = new Position(1, 2); // when & then - assertThatThrownBy(() -> board.move(from, to, Color.WHITE)) + assertThatThrownBy(() -> board.moveAndReturnPiece(from, to, Color.WHITE)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(ErrorMessage.NO_PIECE_AT_SOURCE.getMessage()); } @@ -66,7 +66,7 @@ void setUp() { initialBoard.put(from, new Pawn(Color.BLACK)); // when & then - assertThatThrownBy(() -> board.move(from, to, Color.WHITE)) + assertThatThrownBy(() -> board.moveAndReturnPiece(from, to, Color.WHITE)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(ErrorMessage.INVALID_TURN.getMessage()); } @@ -81,7 +81,7 @@ void setUp() { initialBoard.put(to, new Pawn(Color.WHITE)); // when & then - assertThatThrownBy(() -> board.move(from, to, Color.WHITE)) + assertThatThrownBy(() -> board.moveAndReturnPiece(from, to, Color.WHITE)) .isInstanceOf(IllegalArgumentException.class); } @@ -94,7 +94,7 @@ void setUp() { initialBoard.put(from, new King(Color.WHITE)); // when - board.move(from, to, Color.WHITE); + board.moveAndReturnPiece(from, to, Color.WHITE); Map retrievedMap = board.getMap(); // then @@ -112,7 +112,7 @@ void setUp() { initialBoard.put(new Position(1, 2), new Pawn(Color.WHITE)); // when & then - assertThatThrownBy(() -> board.move(from, to, Color.WHITE)) + assertThatThrownBy(() -> board.moveAndReturnPiece(from, to, Color.WHITE)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(ErrorMessage.HAS_OBSTACLE.getMessage()); } @@ -126,7 +126,7 @@ void setUp() { initialBoard.put(from, new Pawn(Color.WHITE)); // when - board.move(from, to, Color.WHITE); + board.moveAndReturnPiece(from, to, Color.WHITE); Map retrievedMap = board.getMap(); // then @@ -144,7 +144,7 @@ void setUp() { initialBoard.put(to, new Pawn(Color.BLACK)); //when - board.move(from, to, Color.WHITE); + board.moveAndReturnPiece(from, to, Color.WHITE); Map retrievedMap = board.getMap(); //then @@ -163,7 +163,7 @@ void setUp() { initialBoard.put(to, new King(Color.BLACK)); // when - Piece capturedPiece = board.move(from, to, Color.WHITE); + Piece capturedPiece = board.moveAndReturnPiece(from, to, Color.WHITE); // then assertThat(capturedPiece.pieceType()).isEqualTo(PieceInfo.KING); diff --git a/src/test/java/chess/model/movement/MovementConverterTest.java b/src/test/java/chess/domain/movement/MovementConverterTest.java similarity index 92% rename from src/test/java/chess/model/movement/MovementConverterTest.java rename to src/test/java/chess/domain/movement/MovementConverterTest.java index d295b165fab..6c66487442b 100644 --- a/src/test/java/chess/model/movement/MovementConverterTest.java +++ b/src/test/java/chess/domain/movement/MovementConverterTest.java @@ -1,7 +1,7 @@ -package chess.model.movement; +package chess.domain.movement; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/chess/model/movement/PathTest.java b/src/test/java/chess/domain/movement/PathTest.java similarity index 94% rename from src/test/java/chess/model/movement/PathTest.java rename to src/test/java/chess/domain/movement/PathTest.java index 9a6516b2af8..37465ff2d89 100644 --- a/src/test/java/chess/model/movement/PathTest.java +++ b/src/test/java/chess/domain/movement/PathTest.java @@ -1,8 +1,8 @@ -package chess.model.movement; +package chess.domain.movement; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import chess.model.position.Position; +import chess.domain.position.Position; import java.util.List; import java.util.Set; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/chess/model/piece/PieceTest.java b/src/test/java/chess/domain/piece/PieceTest.java similarity index 86% rename from src/test/java/chess/model/piece/PieceTest.java rename to src/test/java/chess/domain/piece/PieceTest.java index 192210d36d2..f20568b3ff4 100644 --- a/src/test/java/chess/model/piece/PieceTest.java +++ b/src/test/java/chess/domain/piece/PieceTest.java @@ -1,14 +1,15 @@ -package chess.model.piece; +package chess.domain.piece; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import chess.model.movement.Movement; -import chess.model.movement.Path; -import chess.model.position.Color; -import chess.model.position.Position; +import chess.domain.movement.Movement; +import chess.domain.movement.Path; +import chess.domain.position.Color; +import chess.domain.position.Position; import java.util.Arrays; import java.util.List; +import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -58,7 +59,7 @@ public TestPiece(Color color) { } @Override - public Path findPath(Position from, Position to) { + public Path findPath(Position from, Position to, Map board) { // 테스트 목적으로 간단히 작성 return new Path(List.of(to)); } diff --git a/src/test/java/chess/model/piece/pieces/BishopTest.java b/src/test/java/chess/domain/piece/pieces/BishopTest.java similarity index 73% rename from src/test/java/chess/model/piece/pieces/BishopTest.java rename to src/test/java/chess/domain/piece/pieces/BishopTest.java index 84ce79a4da0..d728f410de9 100644 --- a/src/test/java/chess/model/piece/pieces/BishopTest.java +++ b/src/test/java/chess/domain/piece/pieces/BishopTest.java @@ -1,11 +1,13 @@ -package chess.model.piece.pieces; +package chess.domain.piece.pieces; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import chess.model.position.Color; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.board.Board; +import chess.domain.board.BoardFactory; +import chess.domain.position.Color; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -31,9 +33,10 @@ class BishopTest { Bishop bishop = new Bishop(Color.WHITE); Position from = new Position(4, 4); Position to = new Position(4, 6); + Board board = BoardFactory.createInitialBoard(); // when & then - assertThatThrownBy(() -> bishop.findPath(from, to)) + assertThatThrownBy(() -> bishop.findPath(from, to, board.getMap())) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(ErrorMessage.INVALID_DIRECTION.getMessage()); } diff --git a/src/test/java/chess/model/piece/pieces/KingTest.java b/src/test/java/chess/domain/piece/pieces/KingTest.java similarity index 63% rename from src/test/java/chess/model/piece/pieces/KingTest.java rename to src/test/java/chess/domain/piece/pieces/KingTest.java index ca5fe04d1f0..7f7e36d0387 100644 --- a/src/test/java/chess/model/piece/pieces/KingTest.java +++ b/src/test/java/chess/domain/piece/pieces/KingTest.java @@ -1,11 +1,14 @@ -package chess.model.piece.pieces; +package chess.domain.piece.pieces; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import chess.model.position.Color; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.board.BoardFactory; +import chess.domain.piece.Piece; +import chess.domain.position.Color; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; +import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -18,7 +21,7 @@ class KingTest { Color color = Color.WHITE; //when - King king =new King(color); + King king = new King(color); //then assertThat(king.getColor()).isEqualTo(color); @@ -31,10 +34,11 @@ class KingTest { King king = new King(Color.WHITE); Position from = new Position(4, 4); Position to = new Position(6, 6); + Map board = BoardFactory.createInitialBoard().getMap(); // when & then - assertThatThrownBy(() -> king.findPath(from, to)) - .isInstanceOf(IllegalStateException.class) + assertThatThrownBy(() -> king.findPath(from, to, board)) + .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(ErrorMessage.INVALID_DIRECTION.getMessage()); } } diff --git a/src/test/java/chess/model/piece/pieces/KnightTest.java b/src/test/java/chess/domain/piece/pieces/KnightTest.java similarity index 71% rename from src/test/java/chess/model/piece/pieces/KnightTest.java rename to src/test/java/chess/domain/piece/pieces/KnightTest.java index 58b780a5280..91d599a477c 100644 --- a/src/test/java/chess/model/piece/pieces/KnightTest.java +++ b/src/test/java/chess/domain/piece/pieces/KnightTest.java @@ -1,11 +1,14 @@ -package chess.model.piece.pieces; +package chess.domain.piece.pieces; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import chess.model.position.Color; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.board.BoardFactory; +import chess.domain.piece.Piece; +import chess.domain.position.Color; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; +import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -31,9 +34,10 @@ class KnightTest { Knight knight = new Knight(Color.WHITE); Position from = new Position(4, 4); Position to = new Position(6, 6); + Map board = BoardFactory.createInitialBoard().getMap(); // when & then - assertThatThrownBy(() -> knight.findPath(from, to)) + assertThatThrownBy(() -> knight.findPath(from, to, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(ErrorMessage.INVALID_DIRECTION.getMessage()); } diff --git a/src/test/java/chess/model/piece/pieces/PawnTest.java b/src/test/java/chess/domain/piece/pieces/PawnTest.java similarity index 70% rename from src/test/java/chess/model/piece/pieces/PawnTest.java rename to src/test/java/chess/domain/piece/pieces/PawnTest.java index b7bfb4167de..24a7149f082 100644 --- a/src/test/java/chess/model/piece/pieces/PawnTest.java +++ b/src/test/java/chess/domain/piece/pieces/PawnTest.java @@ -1,12 +1,15 @@ -package chess.model.piece.pieces; +package chess.domain.piece.pieces; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import chess.model.movement.Path; -import chess.model.position.Color; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.board.BoardFactory; +import chess.domain.movement.Path; +import chess.domain.piece.Piece; +import chess.domain.position.Color; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; +import java.util.Map; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -38,9 +41,10 @@ class PawnTest { Pawn pawn = new Pawn(color); Position from = new Position(fromFile, fromRank); Position to = new Position(toFile, toRank); + Map board = BoardFactory.createInitialBoard().getMap(); // when - Path path = pawn.findPath(from, to); + Path path = pawn.findPath(from, to, board); // then assertThat(path) @@ -59,9 +63,10 @@ class PawnTest { Pawn pawn = new Pawn(color); Position from = new Position(fromFile, fromRank); Position to = new Position(toFile, toRank); + Map board = BoardFactory.createInitialBoard().getMap(); // when - Path path = pawn.findPath(from, to); + Path path = pawn.findPath(from, to, board); // then assertThat(path) @@ -71,29 +76,6 @@ class PawnTest { to); } - @ParameterizedTest - @CsvSource({ - "2, 2, 3, 3, WHITE", - "2, 2, 1, 3, WHITE", - "2, 7, 3, 6, BLACK", - "2, 7, 1, 6, BLACK" - }) - @DisplayName("폰은_대각선으로_한_칸_움직여_적을_공격할_수_있다") - void 폰은_대각선으로_한_칸_움직여_적을_공격할_수_있다(int fromFile, int fromRank, int toFile, int toRank, Color color) { - // given - Pawn pawn = new Pawn(color); - Position from = new Position(fromFile, fromRank); - Position to = new Position(toFile, toRank); - - // when - Path path = pawn.findPath(from, to); - - // then - assertThat(path) - .extracting("positions", InstanceOfAssertFactories.list(Position.class)) - .containsExactly(to); - } - @Test @DisplayName("폰이_뒤로_움직인다면_예외_처리한다") void 폰이_뒤로_움직인다면_예외_처리한다() { @@ -101,9 +83,10 @@ class PawnTest { Pawn pawn = new Pawn(Color.WHITE); Position from = new Position(2, 3); Position to = new Position(2, 2); + Map board = BoardFactory.createInitialBoard().getMap(); // when & then - assertThatThrownBy(() -> pawn.findPath(from, to)) + assertThatThrownBy(() -> pawn.findPath(from, to, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(ErrorMessage.INVALID_DIRECTION.getMessage()); } @@ -115,9 +98,31 @@ class PawnTest { Pawn pawn = new Pawn(Color.WHITE); Position from = new Position(2, 3); Position to = new Position(2, 5); + Map board = BoardFactory.createInitialBoard().getMap(); + + // when & then + assertThatThrownBy(() -> pawn.findPath(from, to, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ErrorMessage.INVALID_DIRECTION.getMessage()); + } + + @ParameterizedTest + @CsvSource({ + "2, 2, 3, 3, WHITE", + "2, 2, 1, 3, WHITE", + "2, 7, 3, 6, BLACK", + "2, 7, 1, 6, BLACK" + }) + @DisplayName("폰이_대각선으로_움직이려할_때_적이_없으면_예외_처리한다") + void 폰이_대각선으로_움직이려할_때_적이_없으면_예외_처리한다(int fromFile, int fromRank, int toFile, int toRank, Color color) { + // given + Pawn pawn = new Pawn(color); + Position from = new Position(fromFile, fromRank); + Position to = new Position(toFile, toRank); + Map board = BoardFactory.createInitialBoard().getMap(); // when & then - assertThatThrownBy(() -> pawn.findPath(from, to)) + assertThatThrownBy(() -> pawn.findPath(from, to, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(ErrorMessage.INVALID_DIRECTION.getMessage()); } diff --git a/src/test/java/chess/model/piece/pieces/QueenTest.java b/src/test/java/chess/domain/piece/pieces/QueenTest.java similarity index 70% rename from src/test/java/chess/model/piece/pieces/QueenTest.java rename to src/test/java/chess/domain/piece/pieces/QueenTest.java index 58e7827393e..120436f79d7 100644 --- a/src/test/java/chess/model/piece/pieces/QueenTest.java +++ b/src/test/java/chess/domain/piece/pieces/QueenTest.java @@ -1,11 +1,14 @@ -package chess.model.piece.pieces; +package chess.domain.piece.pieces; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import chess.model.position.Color; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.board.BoardFactory; +import chess.domain.piece.Piece; +import chess.domain.position.Color; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; +import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -31,8 +34,10 @@ class QueenTest { Queen queen = new Queen(Color.WHITE); Position from = new Position(4, 4); Position to = new Position(6, 5); + Map board = BoardFactory.createInitialBoard().getMap(); + // when & then - assertThatThrownBy(() -> queen.findPath(from, to)) + assertThatThrownBy(() -> queen.findPath(from, to, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(ErrorMessage.INVALID_DIRECTION.getMessage()); } diff --git a/src/test/java/chess/model/piece/pieces/RookTest.java b/src/test/java/chess/domain/piece/pieces/RookTest.java similarity index 70% rename from src/test/java/chess/model/piece/pieces/RookTest.java rename to src/test/java/chess/domain/piece/pieces/RookTest.java index 64cd4b76f16..8c108e31869 100644 --- a/src/test/java/chess/model/piece/pieces/RookTest.java +++ b/src/test/java/chess/domain/piece/pieces/RookTest.java @@ -1,11 +1,14 @@ -package chess.model.piece.pieces; +package chess.domain.piece.pieces; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import chess.model.position.Color; -import chess.model.position.Position; -import chess.model.ErrorMessage; +import chess.domain.board.BoardFactory; +import chess.domain.piece.Piece; +import chess.domain.position.Color; +import chess.domain.position.Position; +import chess.domain.ErrorMessage; +import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -31,9 +34,10 @@ class RookTest { Rook rook = new Rook(Color.WHITE); Position from = new Position(4, 4); Position to = new Position(6, 5); + Map board = BoardFactory.createInitialBoard().getMap(); //when & then - assertThatThrownBy(() -> rook.findPath(from, to)) + assertThatThrownBy(() -> rook.findPath(from, to, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(ErrorMessage.INVALID_DIRECTION.getMessage()); } diff --git a/src/test/java/chess/model/position/FileTest.java b/src/test/java/chess/domain/position/FileTest.java similarity index 95% rename from src/test/java/chess/model/position/FileTest.java rename to src/test/java/chess/domain/position/FileTest.java index e72be0f560b..a5a3f708469 100644 --- a/src/test/java/chess/model/position/FileTest.java +++ b/src/test/java/chess/domain/position/FileTest.java @@ -1,4 +1,4 @@ -package chess.model.position; +package chess.domain.position; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/src/test/java/chess/model/position/PositionTest.java b/src/test/java/chess/domain/position/PositionTest.java similarity index 94% rename from src/test/java/chess/model/position/PositionTest.java rename to src/test/java/chess/domain/position/PositionTest.java index 39691fc8130..5e46750b4e2 100644 --- a/src/test/java/chess/model/position/PositionTest.java +++ b/src/test/java/chess/domain/position/PositionTest.java @@ -1,10 +1,10 @@ -package chess.model.position; +package chess.domain.position; -import static chess.model.movement.MovementConverter.convertMovement; +import static chess.domain.movement.MovementConverter.convertMovement; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import chess.model.movement.Movement; +import chess.domain.movement.Movement; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/chess/model/position/RankTest.java b/src/test/java/chess/domain/position/RankTest.java similarity index 84% rename from src/test/java/chess/model/position/RankTest.java rename to src/test/java/chess/domain/position/RankTest.java index 282c2e2aa12..3ab9ebbec6d 100644 --- a/src/test/java/chess/model/position/RankTest.java +++ b/src/test/java/chess/domain/position/RankTest.java @@ -1,7 +1,6 @@ -package chess.model.position; +package chess.domain.position; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/chess/model/score/ScoreCalculatorTest.java b/src/test/java/chess/domain/score/ScoreCalculatorTest.java similarity index 93% rename from src/test/java/chess/model/score/ScoreCalculatorTest.java rename to src/test/java/chess/domain/score/ScoreCalculatorTest.java index 9b73f44e446..8c5c52992dd 100644 --- a/src/test/java/chess/model/score/ScoreCalculatorTest.java +++ b/src/test/java/chess/domain/score/ScoreCalculatorTest.java @@ -1,10 +1,11 @@ -package chess.model.score; - -import chess.model.board.Board; -import chess.model.piece.Piece; -import chess.model.piece.pieces.*; -import chess.model.position.Color; -import chess.model.position.Position; +package chess.domain.score; + +import chess.domain.board.Board; +import chess.domain.game.ScoreCalculator; +import chess.domain.piece.Piece; +import chess.domain.piece.pieces.*; +import chess.domain.position.Color; +import chess.domain.position.Position; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test;