From d6a2ea542358e50ef17f4f8bdb9430e3f8ecf992 Mon Sep 17 00:00:00 2001 From: Redddy <78539407+reddevilmidzy@users.noreply.github.com> Date: Tue, 26 Mar 2024 23:39:49 +0900 Subject: [PATCH 01/60] =?UTF-8?q?[1,2=EB=8B=A8=EA=B3=84=20-=20=EC=B2=B4?= =?UTF-8?q?=EC=8A=A4]=20=EB=A0=88=EB=94=94(=EC=B5=9C=EB=8F=99=EA=B7=BC)=20?= =?UTF-8?q?=EB=AF=B8=EC=85=98=20=EC=A0=9C=EC=B6=9C=ED=95=A9=EB=8B=88?= =?UTF-8?q?=EB=8B=A4.=20(#678)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: 기능 요구 사항 작성 * feat : 8 * 8 의 체스 판 생성 * feat: 체스 말 객체 생성 * feat : 한 칸의 정보를 가진 클래스 생성 * refactor: 패키지 변경 * feat: point equals & hashCode 구현 * feat: 체스 말 배치 * feat : 검은색 말은 대문자, 흰색 말은 소문자로 표현 * refactor: message 패키지 생성 * feat: 체스 게임 시작 메시지 출력 * feat: 게임 시작(start)/종료(end) 입력 받기 * feat : end 가 입력되면 프로그램 종료 * feat: 예외 시 재입력 기능 구현 * refactor: gameBoard의 필드 객체를 Piece로 변경 * feat : 보드의 해당 위치의 기물 정보 반환 기능 구현 * refactor: 테스트 코드 메서드 명 및 디스플레이 네임 수정 * feat: move 시 기물이 없는 위치일 경우 예외 발생 기능 구현 * feat: 이동할 말의 현재 위치, 이동할 위치 입력 받기 및 말의 위치 이동 구현 * refactor: gameBoard 필드를 List> 대신 Map으로 변경 * refactor : piece에서 posotion 삭제 * feat : Pawn 움직임 가능 여부 기능 구현 * feat : Pawn 이동 기능 구현 대각선 제외 * feat : 기물 이동 GameBoard에 업데이트 * fix: 출력 시 NPE 발생하던 문제 수정 * refactor: 사용하지 않는 클래스 삭제 * feat: 시작 메시지 출력 구현 * fix: target 위치에 아무 기물이 없을 때 예외 발생 수정 * feat : Knight 이동 기능 구현 * feat: Bishop 이동 기능 구현 * feat : 게임 보드 출력 시 좌표 안내 기능 추가 * feat : Rook 이동 기능 구현 * feat: Queen 이동 기능 구현 * feat : King 이동 기능 구현 * refactor : 테스트 케이스 가독성 향상 * refactor: 추상 클래스 Piece의 불필요한 메서드 제거 * refactor: 패키지 구조 변경 * refactor: moving 클래스에서 레코드로 변경 * docs: 구현 기능 목록 추가 * feat: 현재 턴의 해당하는 진영만 이동 가능 * refactor : Position 생성자 가독성 향상을 위한 매개변수 순서 변경 * refactor : 인뎁스 1로 변환 * refactor: GameBoardDto 생성 * feat : Pawn 대각선 이동 구현 * feat : 처음에 start외 다른 명령어 입력시 예외 처리 구현 * refactor: turn 변경 camp 에 메서드 추가 * fix: 운영체제 별 줄바꿈 맞춤 * refactor: application 메서드 분리 * refactor: moving 레코드 안에 움직임 방향 확인 메서드 추가 * refactor : toString 사용 대체 * refactor : toString 재정의 * refactor : 방향 찾는 기능 Moving으로 이동 * test : Queen checkRoute Test * test: 퀸 이동 경로 확인 테스트 * test: 룩 이동 경로 확인 테스트 * test: 비숍 이동 경로 확인 테스트 * refactor : Pawn의 이동 검증 기능 GameBoard에서 Pawn으로 이동 * refactor: 테스트 하나로 합침 * refactor: getRoute 메서드 명 변경 및 board 메서드 분리 * refactor : 이동 경로 생성 메서드 중복 제거 * refactor: 이동 방향 인덱스 구하는 과정을 Direction enum 으로 이동 * refactor : PieceType 패키지 이동 * refactor : 인뎁스 감소 * refactor : Moving을 class로 변경 * feat: 재입력 기능 구현 * test : Knight 테스트 케이스 추가 * style : TODO 주석 제거 * refactor: controller 코드 메서드 분리 * style: todo 주석 제거 * refactor : 테스트 불변화 적용 * refactor: 메서드 및 변수에 final 추가 * docs: 기능 요구 사항 수정 * docs: 리팩터링 목록 추가 * test: Camp의 toggle 테스트 추가 * refactor: dto 메서드 분리 * test: moving의 방향 확인 테스트 추가 * feat: 유효하지 않은 포지션일 경우 예외 발생 기능 구현 * docs: 우선순위 낮은 구현 기능 목록 추가 * refactor: Position fixtures 생성 * feat: 커스텀 예외 추가하여 IllegalArgumentException 대체 * refactor: 게임 진행을 상태로 만들어 리팩터링 * refactor: row, column enum 명을 rank, file로 변경 * refactor: dto 변환 스트림 활용 * test: 상태 테스트 추가 * refactor: king 의 움직임 가능 확인 책임을 Moving으로 이동 * feat: 예외 시 재입력 기능 구현 * feat: 예외 메시지 출력 기능 구현 * docs: 게임 상태 및 명령어 설명 추가 * refactor: GameBoard 클래스명 ChessBoard로 변경 * refactor: Knight의 움직임 가능 확인 책임을 Moving으로 이동 * test: command 테스트 추가 * refactor: dto 변환 로직 스트림 활용 * feat: File 입력 시 대문자도 허용 * refactor: 컨트롤러의 메서드 책임 분리 * test: 기물들의 시작 위치 확인 * refactor: 파라미터, 변수 앞에 final 추가 * test: chessboard 테스트 보충 * test: pawn 움직임 테스트 추가 * test: running 예외 발생 테스트 추가 * style: 코드 포맷팅 수정 * refactor: ChessBoard 생성 시 바로 체스 기물 세팅 * refactor: 변수에 final 추가 * test: 움직이지 않을 경우 예외 테스트 추가 * refactor: 테스트 명 및 displayName 변경 * refactor: 명령어 인덱스 매직넘버 제거 * refactor: command 사이즈를 상수 대신 enum 필드로 변경 * refactor: dto에서 사용되는 매직넘버 상수화 * refactor: printCurrentCamp 메서드 명 변경 * docs: 완료한 리팩터링 목록 수정 * refactor: dto에 현재 턴 정보 포함 * refactor: List이었던 커맨드들을 일급 컬렉션으로 변경 * refactor: dto 변환 스트림 대신 메서드 분리 * refactor: 불필요한 emtpy 커맨드 제거 * refactor: board를 일급 컬렉션으로 변경 및 책임 분리 * refactor: 게임 상태 생성 클래스 이름 변경 * refactor: pawn을 추상클래스로 변경하고 색마다 클래스 상속받는 형태로 변경 * fix: toString 메서드 제거 * refactor: 각 기물마다 예외 코드 세분화 * test: command body 확인 테스트 추가 * feat: 도착 지점에 아군이 있는 경우 ErrorCode 추가 * test: 해당 위치에 기물이 없는 경우 예외 발생 테스트 추가 * refactor: 점연산자 한줄에 여럿 있는 부분 분리 --------- Co-authored-by: unifolio0 --- docs/README.md | 61 +++++ src/main/java/application/Application.java | 17 ++ src/main/java/constant/ErrorCode.java | 21 ++ src/main/java/controller/ChessController.java | 65 +++++ src/main/java/dto/ChessBoardDto.java | 71 ++++++ src/main/java/exception/CustomException.java | 16 ++ .../exception/InvalidCommandException.java | 10 + .../java/exception/InvalidInputException.java | 10 + .../exception/InvalidMovingException.java | 10 + .../exception/InvalidPositionException.java | 10 + .../exception/InvalidStatusException.java | 10 + .../java/exception/InvalidTurnException.java | 10 + .../MessageDoesNotExistException.java | 10 + .../exception/PieceDoesNotExistException.java | 10 + .../exception/PieceExistInRouteException.java | 10 + src/main/java/model/Board.java | 118 +++++++++ src/main/java/model/Camp.java | 14 + src/main/java/model/ChessGame.java | 37 +++ src/main/java/model/command/Command.java | 55 ++++ src/main/java/model/command/CommandLine.java | 60 +++++ src/main/java/model/piece/Bishop.java | 31 +++ src/main/java/model/piece/BlackPawn.java | 42 +++ src/main/java/model/piece/King.java | 31 +++ src/main/java/model/piece/Knight.java | 31 +++ src/main/java/model/piece/Pawn.java | 79 ++++++ src/main/java/model/piece/Piece.java | 49 ++++ src/main/java/model/piece/Queen.java | 31 +++ src/main/java/model/piece/Rook.java | 31 +++ src/main/java/model/piece/WhitePawn.java | 42 +++ src/main/java/model/position/Direction.java | 75 ++++++ src/main/java/model/position/File.java | 51 ++++ src/main/java/model/position/Moving.java | 117 +++++++++ src/main/java/model/position/Position.java | 63 +++++ src/main/java/model/position/Rank.java | 47 ++++ src/main/java/model/status/End.java | 19 ++ src/main/java/model/status/GameStatus.java | 11 + src/main/java/model/status/Running.java | 37 +++ src/main/java/model/status/StatusFactory.java | 21 ++ src/main/java/view/InputView.java | 27 ++ src/main/java/view/OutputView.java | 25 ++ .../java/view/message/ErrorCodeMessage.java | 53 ++++ src/main/java/view/message/PieceType.java | 59 +++++ src/test/java/model/CampTest.java | 19 ++ src/test/java/model/ChessGameTest.java | 218 ++++++++++++++++ src/test/java/model/Fixtures.java | 84 ++++++ .../java/model/command/CommandLineTest.java | 33 +++ src/test/java/model/command/CommandTest.java | 62 +++++ src/test/java/model/piece/BishopTest.java | 81 ++++++ src/test/java/model/piece/KingTest.java | 84 ++++++ src/test/java/model/piece/KnightTest.java | 79 ++++++ src/test/java/model/piece/PawnTest.java | 240 ++++++++++++++++++ src/test/java/model/piece/PieceTest.java | 36 +++ src/test/java/model/piece/QueenTest.java | 98 +++++++ src/test/java/model/piece/RookTest.java | 88 +++++++ src/test/java/model/position/MovingTest.java | 86 +++++++ .../java/model/position/PositionTest.java | 23 ++ src/test/java/model/status/EndTest.java | 40 +++ src/test/java/model/status/RunningTest.java | 60 +++++ .../java/model/status/StatusFactoryTest.java | 37 +++ 59 files changed, 2965 insertions(+) create mode 100644 docs/README.md create mode 100644 src/main/java/application/Application.java create mode 100644 src/main/java/constant/ErrorCode.java create mode 100644 src/main/java/controller/ChessController.java create mode 100644 src/main/java/dto/ChessBoardDto.java create mode 100644 src/main/java/exception/CustomException.java create mode 100644 src/main/java/exception/InvalidCommandException.java create mode 100644 src/main/java/exception/InvalidInputException.java create mode 100644 src/main/java/exception/InvalidMovingException.java create mode 100644 src/main/java/exception/InvalidPositionException.java create mode 100644 src/main/java/exception/InvalidStatusException.java create mode 100644 src/main/java/exception/InvalidTurnException.java create mode 100644 src/main/java/exception/MessageDoesNotExistException.java create mode 100644 src/main/java/exception/PieceDoesNotExistException.java create mode 100644 src/main/java/exception/PieceExistInRouteException.java create mode 100644 src/main/java/model/Board.java create mode 100644 src/main/java/model/Camp.java create mode 100644 src/main/java/model/ChessGame.java create mode 100644 src/main/java/model/command/Command.java create mode 100644 src/main/java/model/command/CommandLine.java create mode 100644 src/main/java/model/piece/Bishop.java create mode 100644 src/main/java/model/piece/BlackPawn.java create mode 100644 src/main/java/model/piece/King.java create mode 100644 src/main/java/model/piece/Knight.java create mode 100644 src/main/java/model/piece/Pawn.java create mode 100644 src/main/java/model/piece/Piece.java create mode 100644 src/main/java/model/piece/Queen.java create mode 100644 src/main/java/model/piece/Rook.java create mode 100644 src/main/java/model/piece/WhitePawn.java create mode 100644 src/main/java/model/position/Direction.java create mode 100644 src/main/java/model/position/File.java create mode 100644 src/main/java/model/position/Moving.java create mode 100644 src/main/java/model/position/Position.java create mode 100644 src/main/java/model/position/Rank.java create mode 100644 src/main/java/model/status/End.java create mode 100644 src/main/java/model/status/GameStatus.java create mode 100644 src/main/java/model/status/Running.java create mode 100644 src/main/java/model/status/StatusFactory.java create mode 100644 src/main/java/view/InputView.java create mode 100644 src/main/java/view/OutputView.java create mode 100644 src/main/java/view/message/ErrorCodeMessage.java create mode 100644 src/main/java/view/message/PieceType.java create mode 100644 src/test/java/model/CampTest.java create mode 100644 src/test/java/model/ChessGameTest.java create mode 100644 src/test/java/model/Fixtures.java create mode 100644 src/test/java/model/command/CommandLineTest.java create mode 100644 src/test/java/model/command/CommandTest.java create mode 100644 src/test/java/model/piece/BishopTest.java create mode 100644 src/test/java/model/piece/KingTest.java create mode 100644 src/test/java/model/piece/KnightTest.java create mode 100644 src/test/java/model/piece/PawnTest.java create mode 100644 src/test/java/model/piece/PieceTest.java create mode 100644 src/test/java/model/piece/QueenTest.java create mode 100644 src/test/java/model/piece/RookTest.java create mode 100644 src/test/java/model/position/MovingTest.java create mode 100644 src/test/java/model/position/PositionTest.java create mode 100644 src/test/java/model/status/EndTest.java create mode 100644 src/test/java/model/status/RunningTest.java create mode 100644 src/test/java/model/status/StatusFactoryTest.java diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000000..ee8506d8cf0 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,61 @@ +# 체스 미션 + +## 기능 요구 사항 + +* [x] 8 * 8 의 체스 판 생성 +* [x] 체스 말 배치 +* [x] 검은색 말은 대문자, 흰색 말은 소문자로 표현 +* [x] 체스 게임 시작 메시지 출력 +* [x] 게임 시작(start)/종료(end) 입력 받기 + * [x] start/end 가 아닐 시 예외 발생 + * [x] 예외시 재입력 받기 +* [x] move 기능 구현 + * [x] 이동할 말의 현재 위치, 이동할 위치 입력 받기 + * [x] 말의 위치 이동 + * [x] 유효하지 않은 위치를 입력한 경우 예외 발생 (ex) z1 + * [x] 기물의 이동 규칙을 벗어나는 경우 예외 발생 + * [x] 현재 위치로 빈 칸을 입력받은 경우 예외 발생 + * [x] 현재 턴이 아닌 기물을 이동하는 경우 예외 발생 +* [x] end 가 입력되면 프로그램 종료 + +## 기물의 이동 기능 + +* [x] 현재 턴의 해당하는 진영만 이동 가능 + * 시작은 WHITE + +
+ +* [x] Pawn + * [x] 대각선에 상대방 기물이 있다면 이동 가능 +* [x] Knight +* [x] King +* [x] Bishop +* [x] Queen +* [x] Rook + +## 명령어 + +* start: 게임을 시작하는 명령어 + * 게임 시작은 start 명령어만 가능하다 +* end: 게임을 종료하는 명령어 +* move: 게임 중 기물을 움직이는 명령어 + * 게임이 시작되지 않았는데 move 명령어를 입력한다면 예외가 발생한다. + * 형식은 move [a-h1-8] [a-h1-8] 형태이고 아닐시 예외가 발생한다. + + +## 리팩터링 + +* [x] 테스트 코드 보충 +* [x] 프로그래밍 요구 사항 지키기 +* [x] 예외 메시지를 커스텀 예외로 만들기 +* [x] 예외 시 재입력 구현하기 + +## 우선순위가 낮지만 구현해보고 싶은 기능 + +* 특수 행마법 + * [ ] 앙파상 + * [ ] 프로모션 + * [ ] 캐슬링 +* 체크 상황 + * [ ] 체크 메이트면 자동으로 게임 종료 + * [ ] 체크 상황이면 체크를 피하는 방향으로만 이동 가능 diff --git a/src/main/java/application/Application.java b/src/main/java/application/Application.java new file mode 100644 index 00000000000..4bf227f5dfb --- /dev/null +++ b/src/main/java/application/Application.java @@ -0,0 +1,17 @@ +package application; + +import controller.ChessController; +import java.util.Scanner; +import view.InputView; +import view.OutputView; + +public class Application { + + public static void main(String[] args) { + final InputView inputView = new InputView(new Scanner(System.in)); + final OutputView outputView = new OutputView(); + + final ChessController chessController = new ChessController(inputView, outputView); + chessController.run(); + } +} diff --git a/src/main/java/constant/ErrorCode.java b/src/main/java/constant/ErrorCode.java new file mode 100644 index 00000000000..282db5a284d --- /dev/null +++ b/src/main/java/constant/ErrorCode.java @@ -0,0 +1,21 @@ +package constant; + +public enum ErrorCode { + + INVALID_INPUT, + INVALID_STATUS, + INVALID_COMMAND, + INVALID_POSITION, + INVALID_PAWN_MOVEMENT, + INVALID_KING_MOVEMENT, + INVALID_BISHOP_MOVEMENT, + INVALID_KNIGHT_MOVEMENT, + INVALID_QUEEN_MOVEMENT, + INVALID_ROOK_MOVEMENT, + PIECE_EXIST_IN_ROUTE, + OWN_PIECE_EXIST_POSITION, + PIECE_DOES_NOT_EXIST_POSITION, + INVALID_CAMP_PIECE, + NO_MESSAGE + +} diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java new file mode 100644 index 00000000000..300e0cd8641 --- /dev/null +++ b/src/main/java/controller/ChessController.java @@ -0,0 +1,65 @@ +package controller; + +import dto.ChessBoardDto; +import exception.CustomException; +import java.util.List; +import model.ChessGame; +import model.command.CommandLine; +import model.status.GameStatus; +import model.status.StatusFactory; +import view.InputView; +import view.OutputView; + +public class ChessController { + + private final InputView inputView; + private final OutputView outputView; + + public ChessController(final InputView inputView, final OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + final ChessGame chessGame = ChessGame.setupStartingPosition(); + outputView.printStartMessage(); + GameStatus gameStatus = initGame(); + + while (gameStatus.isRunning()) { + printCurrentStatus(chessGame); + gameStatus = play(gameStatus, chessGame); + } + } + + private GameStatus initGame() { + try { + return StatusFactory.create(readCommandLine()); + } catch (final CustomException exception) { + outputView.printException(exception.getErrorCode()); + return initGame(); + } + } + + private GameStatus play(final GameStatus gameStatus, final ChessGame chessGame) { + try { + return gameStatus.play(readCommandLine(), chessGame); + } catch (final CustomException exception) { + outputView.printException(exception.getErrorCode()); + return play(gameStatus, chessGame); + } + } + + private void printCurrentStatus(final ChessGame chessGame) { + outputView.printChessBoard(ChessBoardDto.from(chessGame)); + } + + private CommandLine readCommandLine() { + try { + List command = inputView.readCommandList(); + return CommandLine.from(command); + } catch (final CustomException exception) { + outputView.printException(exception.getErrorCode()); + } + return readCommandLine(); + } +} diff --git a/src/main/java/dto/ChessBoardDto.java b/src/main/java/dto/ChessBoardDto.java new file mode 100644 index 00000000000..eab90922d85 --- /dev/null +++ b/src/main/java/dto/ChessBoardDto.java @@ -0,0 +1,71 @@ +package dto; + +import java.util.Map; +import model.ChessGame; +import model.piece.Piece; +import model.position.File; +import model.position.Position; +import model.position.Rank; +import view.message.PieceType; + +public class ChessBoardDto { + + private static final String FILE_GUIDE_LINE = "abcdefgh"; + private static final String RANK_GUIDE_LINE_FORM = " %s"; + private static final String EMPTY_POINT = "."; + private static final int BOARD_SIZE = 8; + + private final String board; + private final String currentTurn; + + private ChessBoardDto(final String board, final String currentTurn) { + this.board = board; + this.currentTurn = currentTurn; + } + + public static ChessBoardDto from(final ChessGame chessGame) { + final Map pieceOfPosition = chessGame.getBoard(); + + final StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < BOARD_SIZE; i++) { + stringBuilder.append(generateBoardLine(pieceOfPosition, Rank.from(i))); + stringBuilder.append(System.lineSeparator()); + } + stringBuilder.append(String.format("%n%s%n", FILE_GUIDE_LINE)); + + return new ChessBoardDto(stringBuilder.toString(), chessGame.getCamp().toString()); + } + + private static String generateBoardLine(final Map pieceOfPosition, final Rank rank) { + final StringBuilder stringBuilder = new StringBuilder(); + for (int j = 0; j < BOARD_SIZE; j++) { + final File file = File.from(j); + final Position position = new Position(file, rank); + final Piece piece = pieceOfPosition.get(position); + stringBuilder.append(convertToString(piece, file, rank)); + } + return stringBuilder.toString(); + } + + private static String convertToString(final Piece piece, final File file, final Rank rank) { + if (piece != null) { + return PieceType.from(piece).getValue() + paddedRankGuidLine(file, rank); + } + return EMPTY_POINT + paddedRankGuidLine(file, rank); + } + + private static String paddedRankGuidLine(final File file, final Rank rank) { + if (file.isLast()) { + return String.format(RANK_GUIDE_LINE_FORM, rank.getValue()); + } + return ""; + } + + public String getBoard() { + return board; + } + + public String getCurrentTurn() { + return currentTurn; + } +} diff --git a/src/main/java/exception/CustomException.java b/src/main/java/exception/CustomException.java new file mode 100644 index 00000000000..73c1f3b9c63 --- /dev/null +++ b/src/main/java/exception/CustomException.java @@ -0,0 +1,16 @@ +package exception; + +import constant.ErrorCode; + +public class CustomException extends RuntimeException { + + private final ErrorCode errorCode; + + public CustomException(final ErrorCode errorCode) { + this.errorCode = errorCode; + } + + public ErrorCode getErrorCode() { + return errorCode; + } +} diff --git a/src/main/java/exception/InvalidCommandException.java b/src/main/java/exception/InvalidCommandException.java new file mode 100644 index 00000000000..29ecd160846 --- /dev/null +++ b/src/main/java/exception/InvalidCommandException.java @@ -0,0 +1,10 @@ +package exception; + +import constant.ErrorCode; + +public class InvalidCommandException extends CustomException { + + public InvalidCommandException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/exception/InvalidInputException.java b/src/main/java/exception/InvalidInputException.java new file mode 100644 index 00000000000..eb61d7d5e8b --- /dev/null +++ b/src/main/java/exception/InvalidInputException.java @@ -0,0 +1,10 @@ +package exception; + +import constant.ErrorCode; + +public class InvalidInputException extends CustomException { + + public InvalidInputException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/exception/InvalidMovingException.java b/src/main/java/exception/InvalidMovingException.java new file mode 100644 index 00000000000..018afe6c3c9 --- /dev/null +++ b/src/main/java/exception/InvalidMovingException.java @@ -0,0 +1,10 @@ +package exception; + +import constant.ErrorCode; + +public class InvalidMovingException extends CustomException { + + public InvalidMovingException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/exception/InvalidPositionException.java b/src/main/java/exception/InvalidPositionException.java new file mode 100644 index 00000000000..89e78a9a620 --- /dev/null +++ b/src/main/java/exception/InvalidPositionException.java @@ -0,0 +1,10 @@ +package exception; + +import constant.ErrorCode; + +public class InvalidPositionException extends CustomException { + + public InvalidPositionException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/exception/InvalidStatusException.java b/src/main/java/exception/InvalidStatusException.java new file mode 100644 index 00000000000..b111c45dc5a --- /dev/null +++ b/src/main/java/exception/InvalidStatusException.java @@ -0,0 +1,10 @@ +package exception; + +import constant.ErrorCode; + +public class InvalidStatusException extends CustomException { + + public InvalidStatusException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/exception/InvalidTurnException.java b/src/main/java/exception/InvalidTurnException.java new file mode 100644 index 00000000000..f2799574076 --- /dev/null +++ b/src/main/java/exception/InvalidTurnException.java @@ -0,0 +1,10 @@ +package exception; + +import constant.ErrorCode; + +public class InvalidTurnException extends CustomException { + + public InvalidTurnException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/exception/MessageDoesNotExistException.java b/src/main/java/exception/MessageDoesNotExistException.java new file mode 100644 index 00000000000..3c0e8dd1eb9 --- /dev/null +++ b/src/main/java/exception/MessageDoesNotExistException.java @@ -0,0 +1,10 @@ +package exception; + +import constant.ErrorCode; + +public class MessageDoesNotExistException extends CustomException { + + public MessageDoesNotExistException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/exception/PieceDoesNotExistException.java b/src/main/java/exception/PieceDoesNotExistException.java new file mode 100644 index 00000000000..e7cb0319f99 --- /dev/null +++ b/src/main/java/exception/PieceDoesNotExistException.java @@ -0,0 +1,10 @@ +package exception; + +import constant.ErrorCode; + +public class PieceDoesNotExistException extends CustomException { + + public PieceDoesNotExistException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/exception/PieceExistInRouteException.java b/src/main/java/exception/PieceExistInRouteException.java new file mode 100644 index 00000000000..5771890b306 --- /dev/null +++ b/src/main/java/exception/PieceExistInRouteException.java @@ -0,0 +1,10 @@ +package exception; + +import constant.ErrorCode; + +public class PieceExistInRouteException extends CustomException { + + public PieceExistInRouteException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java new file mode 100644 index 00000000000..834fb1ceabe --- /dev/null +++ b/src/main/java/model/Board.java @@ -0,0 +1,118 @@ +package model; + +import constant.ErrorCode; +import exception.InvalidTurnException; +import exception.PieceDoesNotExistException; +import exception.PieceExistInRouteException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import model.piece.Bishop; +import model.piece.King; +import model.piece.Knight; +import model.piece.Pawn; +import model.piece.Piece; +import model.piece.Queen; +import model.piece.Rook; +import model.position.File; +import model.position.Moving; +import model.position.Position; +import model.position.Rank; + +public class Board { + + private static final Map> startingPosition = Map.of( + File.A, Rook::new, + File.B, Knight::new, + File.C, Bishop::new, + File.D, Queen::new, + File.E, King::new, + File.F, Bishop::new, + File.G, Knight::new, + File.H, Rook::new + ); + + private final Map pieces; + + private Board(final Map pieces) { + this.pieces = pieces; + } + + public static Board create() { + Map result = new HashMap<>(); + settingExceptPawn(result, Camp.BLACK, Rank.EIGHT); + settingPawn(result, Camp.BLACK, Rank.SEVEN); + settingPawn(result, Camp.WHITE, Rank.TWO); + settingExceptPawn(result, Camp.WHITE, Rank.ONE); + return new Board(result); + } + + private static void settingExceptPawn(final Map board, final Camp camp, final Rank rank) { + for (File file : File.values()) { + final Piece piece = startingPosition.get(file).apply(camp); + board.put(new Position(file, rank), piece); + } + } + + private static void settingPawn(final Map board, final Camp camp, final Rank rank) { + for (File file : File.values()) { + final Piece piece = Pawn.create(camp); + board.put(new Position(file, rank), piece); + } + } + + public void validate(final Moving moving, final Camp currentCamp) { + final Position currentPosition = moving.getCurrentPosition(); + validateExistPiece(currentPosition); + final Piece piece = pieces.get(currentPosition); + validateOwnPiece(currentCamp, piece); + final Set route = getRoute(moving, piece); + validateUnBlocked(route); + final Position nextPosition = moving.getNextPosition(); + validateTargetEnemy(currentCamp, nextPosition); + } + + private void validateExistPiece(final Position currentPosition) { + if (!pieces.containsKey(currentPosition)) { + throw new PieceDoesNotExistException(ErrorCode.PIECE_DOES_NOT_EXIST_POSITION); + } + } + + private void validateOwnPiece(final Camp currentCamp, final Piece piece) { + if (!piece.isSameCamp(currentCamp)) { + throw new InvalidTurnException(ErrorCode.INVALID_CAMP_PIECE); + } + } + + private void validateUnBlocked(final Set route) { + boolean blocked = route.stream() + .anyMatch(pieces::containsKey); + if (blocked) { + throw new PieceExistInRouteException(ErrorCode.PIECE_EXIST_IN_ROUTE); + } + } + + private void validateTargetEnemy(final Camp currentCamp, final Position nextPosition) { + if (pieces.containsKey(nextPosition) && pieces.get(nextPosition).isSameCamp(currentCamp)) { + throw new PieceExistInRouteException(ErrorCode.OWN_PIECE_EXIST_POSITION); + } + } + + private Set getRoute(final Moving moving, final Piece piece) { + if (pieces.containsKey(moving.getNextPosition())) { + return piece.getAttackRoute(moving); + } + return piece.getMoveRoute(moving); + } + + public void move(final Moving moving) { + final Piece piece = pieces.get(moving.getCurrentPosition()); + pieces.put(moving.getNextPosition(), piece); + pieces.remove(moving.getCurrentPosition()); + } + + public Map getPieces() { + return pieces; + } +} diff --git a/src/main/java/model/Camp.java b/src/main/java/model/Camp.java new file mode 100644 index 00000000000..e567bb1efe2 --- /dev/null +++ b/src/main/java/model/Camp.java @@ -0,0 +1,14 @@ +package model; + +public enum Camp { + + BLACK, + WHITE; + + public Camp toggle() { + if (this == BLACK) { + return WHITE; + } + return BLACK; + } +} diff --git a/src/main/java/model/ChessGame.java b/src/main/java/model/ChessGame.java new file mode 100644 index 00000000000..0d1b69d9a09 --- /dev/null +++ b/src/main/java/model/ChessGame.java @@ -0,0 +1,37 @@ +package model; + +import java.util.Map; +import model.piece.Piece; +import model.position.Moving; +import model.position.Position; + +public class ChessGame { + + private static final Camp STARTING_CAMP = Camp.WHITE; + + private final Board board; + private Camp camp; + + private ChessGame(final Board board) { + this.board = board; + this.camp = STARTING_CAMP; + } + + public static ChessGame setupStartingPosition() { + return new ChessGame(Board.create()); + } + + public void move(final Moving moving) { + board.validate(moving, camp); + board.move(moving); + camp = camp.toggle(); + } + + public Map getBoard() { + return board.getPieces(); + } + + public Camp getCamp() { + return camp; + } +} diff --git a/src/main/java/model/command/Command.java b/src/main/java/model/command/Command.java new file mode 100644 index 00000000000..e98f109c7ab --- /dev/null +++ b/src/main/java/model/command/Command.java @@ -0,0 +1,55 @@ +package model.command; + +import constant.ErrorCode; +import exception.InvalidCommandException; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +public enum Command { + + START(Pattern.compile("start"), 0), + MOVE(Pattern.compile("move"), 2), + POSITION(Pattern.compile("[a-hA-H][1-8]"), 0), + END(Pattern.compile("end"), 0); + + public static final int HEAD_INDEX = 0; + + private final Pattern pattern; + private final int bodySize; + + Command(final Pattern pattern, final int bodySize) { + this.pattern = pattern; + this.bodySize = bodySize; + } + + public static Command from(String value) { + return Arrays.stream(values()) + .filter(command -> command.pattern.matcher(value).matches()) + .findFirst() + .orElseThrow(() -> new InvalidCommandException(ErrorCode.INVALID_COMMAND)); + } + + public static void validate(List values) { + if (values == null || values.isEmpty()) { + throw new InvalidCommandException(ErrorCode.INVALID_COMMAND); + } + values.forEach(Command::validate); + Command command = Command.from(values.get(HEAD_INDEX)); + if (values.size() != command.bodySize) { + throw new InvalidCommandException(ErrorCode.INVALID_COMMAND); + } + } + + private static void validate(String input) { + boolean match = Arrays.stream(values()) + .anyMatch(command -> command.pattern.matcher(input).matches()); + if (!match) { + throw new InvalidCommandException(ErrorCode.INVALID_COMMAND); + } + } + + public boolean isEqualToBodySize(int targetSize) { + return bodySize == targetSize; + } +} diff --git a/src/main/java/model/command/CommandLine.java b/src/main/java/model/command/CommandLine.java new file mode 100644 index 00000000000..26ece9ed534 --- /dev/null +++ b/src/main/java/model/command/CommandLine.java @@ -0,0 +1,60 @@ +package model.command; + +import constant.ErrorCode; +import exception.InvalidCommandException; +import java.util.List; + +public class CommandLine { + + public static final int HEAD_INDEX = 0; + public static final int CURRENT_POSITION_INDEX = 0; + public static final int NEXT_POSITION_INDEX = 1; + + private final Command head; + private final List body; + + private CommandLine(final Command head, final List body) { + this.head = head; + this.body = body; + } + + public static CommandLine from(final List input) { + validateEmpty(input); + validateCommand(input); + Command command = Command.from(input.get(HEAD_INDEX)); + validateSize(command, input); + return new CommandLine(command, input.subList(1, input.size())); + } + + private static void validateEmpty(final List input) { + if (input == null || input.isEmpty()) { + throw new InvalidCommandException(ErrorCode.INVALID_COMMAND); + } + } + + private static void validateCommand(final List input) { + input.forEach(Command::from); + } + + private static void validateSize(final Command command, final List input) { + if (!command.isEqualToBodySize(input.size() - 1)) { + throw new InvalidCommandException(ErrorCode.INVALID_COMMAND); + } + } + + public boolean isStart() { + return head == Command.START; + } + + public boolean isEnd() { + return head == Command.END; + } + + public boolean isMove() { + return head == Command.MOVE; + } + + public List getBody() { + return body; + } +} diff --git a/src/main/java/model/piece/Bishop.java b/src/main/java/model/piece/Bishop.java new file mode 100644 index 00000000000..8532a32ed5f --- /dev/null +++ b/src/main/java/model/piece/Bishop.java @@ -0,0 +1,31 @@ +package model.piece; + +import constant.ErrorCode; +import exception.InvalidMovingException; +import java.util.Set; +import model.Camp; +import model.position.Moving; +import model.position.Position; + +public class Bishop extends Piece { + + public Bishop(final Camp camp) { + super(camp); + } + + @Override + public Set getMoveRoute(final Moving moving) { + if (canMovable(moving)) { + return moving.route(); + } + throw new InvalidMovingException(ErrorCode.INVALID_BISHOP_MOVEMENT); + } + + @Override + protected boolean canMovable(final Moving moving) { + if (moving.isNotMoved()) { + return false; + } + return moving.isDiagonal(); + } +} diff --git a/src/main/java/model/piece/BlackPawn.java b/src/main/java/model/piece/BlackPawn.java new file mode 100644 index 00000000000..b9d7014f8f1 --- /dev/null +++ b/src/main/java/model/piece/BlackPawn.java @@ -0,0 +1,42 @@ +package model.piece; + +import java.util.Set; +import model.Camp; +import model.position.Position; +import model.position.Rank; + +public class BlackPawn extends Pawn { + + public BlackPawn() { + super(Camp.BLACK); + } + + @Override + protected Set twoMovedRoute(final Position currentPosition) { + return Set.of(new Position(currentPosition.getFile(), Rank.SIX)); + } + + @Override + protected boolean isStraight(final Position currentPosition, final int differenceRank, final int differenceFile) { + if (differenceFile != 0) { + return false; + } + if (Rank.SEVEN.getIndex() == currentPosition.getRankIndex() && differenceRank == -2) { + return true; + } + return differenceRank == -1; + } + + @Override + protected boolean isDiagonal(final int differenceRank, final int differenceFile) { + if (Math.abs(differenceFile) != 1) { + return false; + } + return differenceRank == -1; + } + + @Override + public Camp getCamp() { + return Camp.BLACK; + } +} diff --git a/src/main/java/model/piece/King.java b/src/main/java/model/piece/King.java new file mode 100644 index 00000000000..1d39e16b546 --- /dev/null +++ b/src/main/java/model/piece/King.java @@ -0,0 +1,31 @@ +package model.piece; + +import constant.ErrorCode; +import exception.InvalidMovingException; +import java.util.Set; +import model.Camp; +import model.position.Moving; +import model.position.Position; + +public class King extends Piece { + + public King(final Camp camp) { + super(camp); + } + + @Override + public Set getMoveRoute(final Moving moving) { + if (canMovable(moving)) { + return Set.of(); + } + throw new InvalidMovingException(ErrorCode.INVALID_KING_MOVEMENT); + } + + @Override + protected boolean canMovable(final Moving moving) { + if (moving.isNotMoved()) { + return false; + } + return moving.isAdjacent(); + } +} diff --git a/src/main/java/model/piece/Knight.java b/src/main/java/model/piece/Knight.java new file mode 100644 index 00000000000..c4ef48c33ec --- /dev/null +++ b/src/main/java/model/piece/Knight.java @@ -0,0 +1,31 @@ +package model.piece; + +import constant.ErrorCode; +import exception.InvalidMovingException; +import java.util.Set; +import model.Camp; +import model.position.Moving; +import model.position.Position; + +public class Knight extends Piece { + + public Knight(final Camp camp) { + super(camp); + } + + @Override + public Set getMoveRoute(final Moving moving) { + if (canMovable(moving)) { + return Set.of(); + } + throw new InvalidMovingException(ErrorCode.INVALID_KNIGHT_MOVEMENT); + } + + @Override + protected boolean canMovable(final Moving moving) { + if (moving.isNotMoved()) { + return false; + } + return moving.isShapeCapitalL(); + } +} diff --git a/src/main/java/model/piece/Pawn.java b/src/main/java/model/piece/Pawn.java new file mode 100644 index 00000000000..09cfd0ca512 --- /dev/null +++ b/src/main/java/model/piece/Pawn.java @@ -0,0 +1,79 @@ +package model.piece; + +import constant.ErrorCode; +import exception.InvalidMovingException; +import java.util.Set; +import model.Camp; +import model.position.Moving; +import model.position.Position; + +public abstract class Pawn extends Piece { + + protected Pawn(final Camp camp) { + super(camp); + } + + public static Pawn create(Camp camp) { + if (camp == Camp.BLACK) { + return new BlackPawn(); + } + return new WhitePawn(); + } + + protected abstract boolean isDiagonal(final int differenceRank, final int differenceFile); + + protected abstract boolean isStraight(final Position currentPosition, + final int differenceRank, + final int differenceFile); + + protected abstract Set twoMovedRoute(final Position currentPosition); + + protected boolean canAttack(final Moving moving) { + if (moving.isNotMoved()) { + return true; + } + final Position currentPosition = moving.getCurrentPosition(); + final Position nextPosition = moving.getNextPosition(); + + final int differenceRank = currentPosition.getRankIndex() - nextPosition.getRankIndex(); + final int differenceFile = currentPosition.getFileIndex() - nextPosition.getFileIndex(); + + return !isDiagonal(differenceRank, differenceFile); + } + + @Override + protected boolean canMovable(final Moving moving) { + if (moving.isNotMoved()) { + return false; + } + final Position currentPosition = moving.getCurrentPosition(); + final Position nextPosition = moving.getNextPosition(); + + final int differenceRank = currentPosition.getRankIndex() - nextPosition.getRankIndex(); + final int differenceFile = currentPosition.getFileIndex() - nextPosition.getFileIndex(); + + return isStraight(currentPosition, differenceRank, differenceFile); + } + + @Override + public Set getMoveRoute(final Moving moving) { + if (!canMovable(moving)) { + throw new InvalidMovingException(ErrorCode.INVALID_PAWN_MOVEMENT); + } + final Position currentPosition = moving.getCurrentPosition(); + final Position nextPosition = moving.getNextPosition(); + + if (Math.abs(nextPosition.getRankIndex() - currentPosition.getRankIndex()) == 1) { + return Set.of(); + } + return twoMovedRoute(currentPosition); + } + + @Override + public Set getAttackRoute(final Moving moving) { + if (canAttack(moving)) { + throw new InvalidMovingException(ErrorCode.INVALID_PAWN_MOVEMENT); + } + return Set.of(); + } +} diff --git a/src/main/java/model/piece/Piece.java b/src/main/java/model/piece/Piece.java new file mode 100644 index 00000000000..d12622068ff --- /dev/null +++ b/src/main/java/model/piece/Piece.java @@ -0,0 +1,49 @@ +package model.piece; + +import java.util.Objects; +import java.util.Set; +import model.Camp; +import model.position.Moving; +import model.position.Position; + +public abstract class Piece { + + private final Camp camp; + + protected Piece(final Camp camp) { + this.camp = camp; + } + + public abstract Set getMoveRoute(final Moving moving); + + protected abstract boolean canMovable(final Moving moving); + + public Set getAttackRoute(final Moving moving) { + return getMoveRoute(moving); + } + + public Camp getCamp() { + return camp; + } + + public boolean isSameCamp(final Camp target) { + return camp == target; + } + + @Override + public boolean equals(final Object target) { + if (this == target) { + return true; + } + if (target == null || getClass() != target.getClass()) { + return false; + } + Piece piece = (Piece) target; + return camp == piece.camp; + } + + @Override + public int hashCode() { + return Objects.hash(camp); + } +} diff --git a/src/main/java/model/piece/Queen.java b/src/main/java/model/piece/Queen.java new file mode 100644 index 00000000000..62c95b20b26 --- /dev/null +++ b/src/main/java/model/piece/Queen.java @@ -0,0 +1,31 @@ +package model.piece; + +import constant.ErrorCode; +import exception.InvalidMovingException; +import java.util.Set; +import model.Camp; +import model.position.Moving; +import model.position.Position; + +public class Queen extends Piece { + + public Queen(final Camp camp) { + super(camp); + } + + @Override + public Set getMoveRoute(final Moving moving) { + if (canMovable(moving)) { + return moving.route(); + } + throw new InvalidMovingException(ErrorCode.INVALID_QUEEN_MOVEMENT); + } + + @Override + protected boolean canMovable(final Moving moving) { + if (moving.isNotMoved()) { + return false; + } + return moving.isDiagonal() || moving.isVertical() || moving.isHorizontal(); + } +} diff --git a/src/main/java/model/piece/Rook.java b/src/main/java/model/piece/Rook.java new file mode 100644 index 00000000000..67fc56dbe08 --- /dev/null +++ b/src/main/java/model/piece/Rook.java @@ -0,0 +1,31 @@ +package model.piece; + +import constant.ErrorCode; +import exception.InvalidMovingException; +import java.util.Set; +import model.Camp; +import model.position.Moving; +import model.position.Position; + +public class Rook extends Piece { + + public Rook(final Camp camp) { + super(camp); + } + + @Override + public Set getMoveRoute(final Moving moving) { + if (canMovable(moving)) { + return moving.route(); + } + throw new InvalidMovingException(ErrorCode.INVALID_ROOK_MOVEMENT); + } + + @Override + protected boolean canMovable(final Moving moving) { + if (moving.isNotMoved()) { + return false; + } + return moving.isHorizontal() || moving.isVertical(); + } +} diff --git a/src/main/java/model/piece/WhitePawn.java b/src/main/java/model/piece/WhitePawn.java new file mode 100644 index 00000000000..941206ea317 --- /dev/null +++ b/src/main/java/model/piece/WhitePawn.java @@ -0,0 +1,42 @@ +package model.piece; + +import java.util.Set; +import model.Camp; +import model.position.Position; +import model.position.Rank; + +public class WhitePawn extends Pawn { + + public WhitePawn() { + super(Camp.WHITE); + } + + @Override + protected Set twoMovedRoute(final Position currentPosition) { + return Set.of(new Position(currentPosition.getFile(), Rank.THREE)); + } + + @Override + protected boolean isStraight(final Position currentPosition, final int differenceRank, final int differenceFile) { + if (differenceFile != 0) { + return false; + } + if (Rank.TWO.getIndex() == currentPosition.getRankIndex() && differenceRank == 2) { + return true; + } + return differenceRank == 1; + } + + @Override + protected boolean isDiagonal(final int differenceRank, final int differenceFile) { + if (Math.abs(differenceFile) != 1) { + return false; + } + return differenceRank == 1; + } + + @Override + public Camp getCamp() { + return Camp.WHITE; + } +} diff --git a/src/main/java/model/position/Direction.java b/src/main/java/model/position/Direction.java new file mode 100644 index 00000000000..b934d6f6273 --- /dev/null +++ b/src/main/java/model/position/Direction.java @@ -0,0 +1,75 @@ +package model.position; + +import java.util.Arrays; +import java.util.function.BiPredicate; + +public enum Direction { + + SOUTH(Direction::isSouth, 1, 0), + EAST(Direction::isEast, 0, 1), + NORTH(Direction::isNorth, -1, 0), + WEST(Direction::isWest, 0, -1), + SOUTH_EAST(Direction::isSouthEast, 1, 1), + SOUTH_WEST(Direction::isSouthWest, 1, -1), + NORTH_EAST(Direction::isNorthEast, -1, 1), + NORTH_WEST(Direction::isNorthWest, -1, -1); + + private final BiPredicate predicate; + private final int deltaRank; + private final int deltaFile; + + Direction(final BiPredicate predicate, final int deltaRank, final int deltaFile) { + this.predicate = predicate; + this.deltaRank = deltaRank; + this.deltaFile = deltaFile; + } + + public static Direction from(final Position currentPosition, final Position nextPosition) { + return Arrays.stream(values()) + .filter(direction -> direction.predicate.test( + nextPosition.getRankIndex() - currentPosition.getRankIndex(), + nextPosition.getFileIndex() - currentPosition.getFileIndex())) + .findFirst() + .orElseThrow(); + } + + private static boolean isSouth(final int dRank, final int dFile) { + return (dRank > 0 && dFile == 0); + } + + private static boolean isEast(final int dRank, final int dFile) { + return (dRank == 0 && dFile > 0); + } + + private static boolean isNorth(final int dRank, final int dFile) { + return (dRank < 0 && dFile == 0); + } + + private static boolean isWest(final int dRank, final int dFile) { + return (dRank == 0 && dFile < 0); + } + + private static boolean isSouthEast(final int dRank, final int dFile) { + return (dRank > 0 && dFile > 0); + } + + private static boolean isSouthWest(final int dRank, final int dFile) { + return (dRank > 0 && dFile < 0); + } + + private static boolean isNorthEast(final int dRank, final int dFile) { + return (dRank < 0 && dFile > 0); + } + + private static boolean isNorthWest(final int dRank, final int dFile) { + return (dRank < 0 && dFile < 0); + } + + public int getDeltaRank() { + return deltaRank; + } + + public int getDeltaFile() { + return deltaFile; + } +} diff --git a/src/main/java/model/position/File.java b/src/main/java/model/position/File.java new file mode 100644 index 00000000000..f9951bbfcec --- /dev/null +++ b/src/main/java/model/position/File.java @@ -0,0 +1,51 @@ +package model.position; + +import constant.ErrorCode; +import exception.InvalidPositionException; +import java.util.Arrays; + +public enum File { + + A("a", 0), + B("b", 1), + C("c", 2), + D("d", 3), + E("e", 4), + F("f", 5), + G("g", 6), + H("h", 7); + + private final String value; + private final int index; + + File(final String value, final int index) { + this.value = value; + this.index = index; + } + + public static File from(final char input) { + return Arrays.stream(values()) + .filter(file -> file.value.equals(String.valueOf(input))) + .findFirst() + .orElseThrow(() -> new InvalidPositionException(ErrorCode.INVALID_POSITION)); + } + + public static File from(final int targetIndex) { + return Arrays.stream(values()) + .filter(file -> file.index == targetIndex) + .findFirst() + .orElseThrow(() -> new IndexOutOfBoundsException("인덱스 범위 초과")); + } + + public boolean isLast() { + return this == H; + } + + public String getValue() { + return value; + } + + public int getIndex() { + return index; + } +} diff --git a/src/main/java/model/position/Moving.java b/src/main/java/model/position/Moving.java new file mode 100644 index 00000000000..a1a2431cdfc --- /dev/null +++ b/src/main/java/model/position/Moving.java @@ -0,0 +1,117 @@ +package model.position; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public class Moving { + private final Position currentPosition; + private final Position nextPosition; + + public Moving(final Position currentPosition, final Position nextPosition) { + this.currentPosition = currentPosition; + this.nextPosition = nextPosition; + } + + public boolean isHorizontal() { + final int currentRank = currentPosition.getRankIndex(); + final int nextRank = nextPosition.getRankIndex(); + + return currentRank == nextRank; + } + + public boolean isVertical() { + final int currentFile = currentPosition.getFileIndex(); + final int nextFile = nextPosition.getFileIndex(); + + return currentFile == nextFile; + } + + public boolean isDiagonal() { + final int currentRank = currentPosition.getRankIndex(); + final int currentFile = currentPosition.getFileIndex(); + + final int nextRank = nextPosition.getRankIndex(); + final int nextFile = nextPosition.getFileIndex(); + + return Math.abs(currentRank - nextRank) == Math.abs(currentFile - nextFile); + } + + public boolean isNotMoved() { + return Objects.equals(currentPosition, nextPosition); + } + + public boolean isShapeCapitalL() { + final int differenceRank = Math.abs(currentPosition.getRankIndex() - nextPosition.getRankIndex()); + final int differenceFile = Math.abs(currentPosition.getFileIndex() - nextPosition.getFileIndex()); + + return differenceRank + differenceFile == 3 && differenceRank != 0 && differenceFile != 0; + } + + public boolean isAdjacent() { + final int currentRank = currentPosition.getRankIndex(); + final int currentFile = currentPosition.getFileIndex(); + + final int nextRank = nextPosition.getRankIndex(); + final int nextFile = nextPosition.getFileIndex(); + + return Math.abs(nextRank - currentRank) <= 1 && Math.abs(nextFile - currentFile) <= 1; + } + + public Set route() { + final int currentRank = currentPosition.getRankIndex(); + final int currentFile = currentPosition.getFileIndex(); + + final Direction direction = Direction.from(currentPosition, nextPosition); + + final Set result = new HashSet<>(); + for (int i = 1; i < distance(); i++) { + Rank rank = Rank.from(currentRank + (i * direction.getDeltaRank())); + File file = File.from(currentFile + (i * direction.getDeltaFile())); + result.add(new Position(file, rank)); + } + return result; + } + + private int distance() { + final int currentRank = currentPosition.getRankIndex(); + final int currentFile = currentPosition.getFileIndex(); + + final int nextRank = nextPosition.getRankIndex(); + final int nextFile = nextPosition.getFileIndex(); + + return Math.max(Math.abs(currentRank - nextRank), Math.abs(currentFile - nextFile)); + } + + public Position getCurrentPosition() { + return currentPosition; + } + + public Position getNextPosition() { + return nextPosition; + } + + @Override + public boolean equals(final Object target) { + if (target == this) { + return true; + } + if (!(target instanceof Moving moving)) { + return false; + } + return Objects.equals(currentPosition, moving.currentPosition) && Objects.equals(nextPosition, + moving.nextPosition); + } + + @Override + public int hashCode() { + return Objects.hash(currentPosition, nextPosition); + } + + @Override + public String toString() { + return "Moving[" + + "currentPosition=" + currentPosition + ", " + + "nextPosition=" + nextPosition + ']'; + } +} diff --git a/src/main/java/model/position/Position.java b/src/main/java/model/position/Position.java new file mode 100644 index 00000000000..5b753986aaf --- /dev/null +++ b/src/main/java/model/position/Position.java @@ -0,0 +1,63 @@ +package model.position; + +import constant.ErrorCode; +import exception.InvalidPositionException; +import java.util.Objects; + +public class Position { + + private final File file; + private final Rank rank; + + public Position(final File file, final Rank rank) { + this.file = file; + this.rank = rank; + } + + public static Position from(final String command) { + validate(command); + return new Position(File.from(command.charAt(0)), Rank.from(command.charAt(1))); + } + + private static void validate(final String command) { + if (command == null || command.isBlank()) { + throw new InvalidPositionException(ErrorCode.INVALID_POSITION); + } + if (command.length() != 2) { + throw new InvalidPositionException(ErrorCode.INVALID_POSITION); + } + } + + public int getRankIndex() { + return rank.getIndex(); + } + + public int getFileIndex() { + return file.getIndex(); + } + + public File getFile() { + return file; + } + + @Override + public int hashCode() { + return Objects.hash(rank, file); + } + + @Override + public boolean equals(final Object target) { + if (this == target) { + return true; + } + if (!(target instanceof Position position)) { + return false; + } + return Objects.equals(rank, position.rank) && Objects.equals(file, position.file); + } + + @Override + public String toString() { + return file.getValue() + rank.getValue(); + } +} diff --git a/src/main/java/model/position/Rank.java b/src/main/java/model/position/Rank.java new file mode 100644 index 00000000000..4562991cb5d --- /dev/null +++ b/src/main/java/model/position/Rank.java @@ -0,0 +1,47 @@ +package model.position; + +import constant.ErrorCode; +import exception.InvalidPositionException; +import java.util.Arrays; + +public enum Rank { + + ONE("1", 7), + TWO("2", 6), + THREE("3", 5), + FOUR("4", 4), + FIVE("5", 3), + SIX("6", 2), + SEVEN("7", 1), + EIGHT("8", 0); + + private final String value; + private final int index; + + Rank(final String value, final int index) { + this.value = value; + this.index = index; + } + + public static Rank from(final char input) { + return Arrays.stream(values()) + .filter(rank -> rank.value.equals(String.valueOf(input))) + .findFirst() + .orElseThrow(() -> new InvalidPositionException(ErrorCode.INVALID_POSITION)); + } + + public static Rank from(final int targetIndex) { + return Arrays.stream(values()) + .filter(rank -> rank.index == targetIndex) + .findFirst() + .orElseThrow(() -> new IndexOutOfBoundsException("인덱스 범위 초과")); + } + + public int getIndex() { + return index; + } + + public String getValue() { + return value; + } +} diff --git a/src/main/java/model/status/End.java b/src/main/java/model/status/End.java new file mode 100644 index 00000000000..e2983e11ee1 --- /dev/null +++ b/src/main/java/model/status/End.java @@ -0,0 +1,19 @@ +package model.status; + +import constant.ErrorCode; +import exception.InvalidStatusException; +import model.ChessGame; +import model.command.CommandLine; + +public class End implements GameStatus { + + @Override + public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) { + throw new InvalidStatusException(ErrorCode.INVALID_STATUS); + } + + @Override + public boolean isRunning() { + return false; + } +} diff --git a/src/main/java/model/status/GameStatus.java b/src/main/java/model/status/GameStatus.java new file mode 100644 index 00000000000..8f455796286 --- /dev/null +++ b/src/main/java/model/status/GameStatus.java @@ -0,0 +1,11 @@ +package model.status; + +import model.ChessGame; +import model.command.CommandLine; + +public interface GameStatus { + + GameStatus play(CommandLine commandLine, ChessGame chessGame); + + boolean isRunning(); +} diff --git a/src/main/java/model/status/Running.java b/src/main/java/model/status/Running.java new file mode 100644 index 00000000000..1366857f3d4 --- /dev/null +++ b/src/main/java/model/status/Running.java @@ -0,0 +1,37 @@ +package model.status; + +import constant.ErrorCode; +import exception.InvalidStatusException; +import java.util.List; +import model.ChessGame; +import model.command.CommandLine; +import model.position.Moving; +import model.position.Position; + +public class Running implements GameStatus { + + @Override + public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) { + if (commandLine.isEnd()) { + return new End(); + } + if (commandLine.isMove()) { + final Moving moving = convert(commandLine.getBody()); + chessGame.move(moving); + return new Running(); + } + throw new InvalidStatusException(ErrorCode.INVALID_STATUS); + } + + private Moving convert(final List command) { + final String currentPosition = command.get(CommandLine.CURRENT_POSITION_INDEX); + final String nextPosition = command.get(CommandLine.NEXT_POSITION_INDEX); + + return new Moving(Position.from(currentPosition), Position.from(nextPosition)); + } + + @Override + public boolean isRunning() { + return true; + } +} diff --git a/src/main/java/model/status/StatusFactory.java b/src/main/java/model/status/StatusFactory.java new file mode 100644 index 00000000000..55b017f46d9 --- /dev/null +++ b/src/main/java/model/status/StatusFactory.java @@ -0,0 +1,21 @@ +package model.status; + +import constant.ErrorCode; +import exception.InvalidStatusException; +import model.command.CommandLine; + +public class StatusFactory { + + private StatusFactory() { + } + + public static GameStatus create(CommandLine commandLine) { + if (commandLine.isStart()) { + return new Running(); + } + if (commandLine.isEnd()) { + return new End(); + } + throw new InvalidStatusException(ErrorCode.INVALID_STATUS); + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000000..a8081d8f399 --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,27 @@ +package view; + +import constant.ErrorCode; +import exception.InvalidInputException; +import java.util.List; +import java.util.Scanner; + +public class InputView { + + private final Scanner scanner; + + public InputView(final Scanner scanner) { + this.scanner = scanner; + } + + public List readCommandList() { + String rawCommand = scanner.nextLine(); + validate(rawCommand); + return List.of(rawCommand.split(" ")); + } + + private void validate(String value) { + if (value == null || value.isBlank()) { + throw new InvalidInputException(ErrorCode.INVALID_INPUT); + } + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 00000000000..3c8a6d26c0a --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,25 @@ +package view; + +import constant.ErrorCode; +import dto.ChessBoardDto; +import model.Camp; +import view.message.ErrorCodeMessage; + +public class OutputView { + + 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 printChessBoard(final ChessBoardDto chessBoardDto) { + System.out.printf(chessBoardDto.getBoard()); + System.out.printf("현재 턴: %s%n%n", chessBoardDto.getCurrentTurn()); + } + + public void printException(final ErrorCode errorCode) { + System.out.printf("[ERROR] %s%n", ErrorCodeMessage.from(errorCode).getMessage()); + } +} diff --git a/src/main/java/view/message/ErrorCodeMessage.java b/src/main/java/view/message/ErrorCodeMessage.java new file mode 100644 index 00000000000..55c2781b473 --- /dev/null +++ b/src/main/java/view/message/ErrorCodeMessage.java @@ -0,0 +1,53 @@ +package view.message; + +import constant.ErrorCode; +import exception.MessageDoesNotExistException; +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public enum ErrorCodeMessage { + + INVALID_INPUT(ErrorCode.INVALID_INPUT, "값을 입력해주세요"), + INVALID_STATUS(ErrorCode.INVALID_STATUS, "유효하지 않은 상태입니다."), + INVALID_COMMAND(ErrorCode.INVALID_COMMAND, "유효하지 않은 명령입니다."), + INVALID_POSITION(ErrorCode.INVALID_POSITION, "유효하지 않은 위치입니다."), + INVALID_PAWN_MOVEMENT(ErrorCode.INVALID_PAWN_MOVEMENT, "유효하지 않은 폰의 이동 경로입니다."), + INVALID_KING_MOVEMENT(ErrorCode.INVALID_KING_MOVEMENT, "유효하지 않은 킹의 이동 경로입니다."), + INVALID_BISHOP_MOVEMENT(ErrorCode.INVALID_BISHOP_MOVEMENT, "유효하지 않은 비숍의 이동 경로입니다."), + INVALID_KNIGHT_MOVEMENT(ErrorCode.INVALID_KNIGHT_MOVEMENT, "유효하지 않은 나이트의 이동 경로입니다."), + INVALID_QUEEN_MOVEMENT(ErrorCode.INVALID_QUEEN_MOVEMENT, "유효하지 않은 퀸의 이동 경로입니다."), + INVALID_ROOK_MOVEMENT(ErrorCode.INVALID_ROOK_MOVEMENT, "유효하지 않은 룩의 이동 경로입니다."), + PIECE_EXIST_IN_ROUTE(ErrorCode.PIECE_EXIST_IN_ROUTE, "이동 경로에 기물이 있습니다."), + OWN_PIECE_EXIST_POSITION(ErrorCode.OWN_PIECE_EXIST_POSITION, "도착 위치에 자신의 기물이 있습니다."), + PIECE_DOES_NOT_EXIST_POSITION(ErrorCode.PIECE_DOES_NOT_EXIST_POSITION, "해당 위치에 기물이 없습니다."), + INVALID_CAMP_PIECE(ErrorCode.INVALID_CAMP_PIECE, "자신의 기물만 움직일 수 있습니다."), + NO_MESSAGE(ErrorCode.NO_MESSAGE, "해당 메시지가 없습니다."); + + private static final Map SUIT_MESSAGE = Arrays.stream(values()) + .collect(Collectors.toMap(ErrorCodeMessage::getCode, Function.identity())); + + private final ErrorCode errorCode; + private final String message; + + ErrorCodeMessage(final ErrorCode errorCode, final String message) { + this.errorCode = errorCode; + this.message = message; + } + + public static ErrorCodeMessage from(final ErrorCode errorCode) { + if (SUIT_MESSAGE.containsKey(errorCode)) { + return SUIT_MESSAGE.get(errorCode); + } + throw new MessageDoesNotExistException(ErrorCode.NO_MESSAGE); + } + + public String getMessage() { + return message; + } + + private ErrorCode getCode() { + return errorCode; + } +} diff --git a/src/main/java/view/message/PieceType.java b/src/main/java/view/message/PieceType.java new file mode 100644 index 00000000000..dd2ee1bc4dc --- /dev/null +++ b/src/main/java/view/message/PieceType.java @@ -0,0 +1,59 @@ +package view.message; + +import constant.ErrorCode; +import exception.MessageDoesNotExistException; +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import model.Camp; +import model.piece.Bishop; +import model.piece.BlackPawn; +import model.piece.King; +import model.piece.Knight; +import model.piece.Piece; +import model.piece.Queen; +import model.piece.Rook; +import model.piece.WhitePawn; + +public enum PieceType { + + BISHOP_BLACK(new Bishop(Camp.BLACK), "B"), + BISHOP_WHITE(new Bishop(Camp.WHITE), "b"), + KING_BLACK(new King(Camp.BLACK), "K"), + KING_WHITE(new King(Camp.WHITE), "k"), + KNIGHT_BLACK(new Knight(Camp.BLACK), "N"), + KNIGHT_WHITE(new Knight(Camp.WHITE), "n"), + PAWN_BLACK(new BlackPawn(), "P"), + PAWN_WHITE(new WhitePawn(), "p"), + QUEEN_BLACK(new Queen(Camp.BLACK), "Q"), + QUEEN_WHITE(new Queen(Camp.WHITE), "q"), + ROOK_BLACK(new Rook(Camp.BLACK), "R"), + ROOK_WHITE(new Rook(Camp.WHITE), "r"); + + private static final Map SUIT_MESSAGE = Arrays.stream(values()) + .collect(Collectors.toMap(PieceType::getPiece, Function.identity())); + + private final Piece piece; + private final String value; + + PieceType(Piece piece, String value) { + this.piece = piece; + this.value = value; + } + + public static PieceType from(Piece target) { + if (SUIT_MESSAGE.containsKey(target)) { + return SUIT_MESSAGE.get(target); + } + throw new MessageDoesNotExistException(ErrorCode.NO_MESSAGE); + } + + public String getValue() { + return value; + } + + private Piece getPiece() { + return piece; + } +} diff --git a/src/test/java/model/CampTest.java b/src/test/java/model/CampTest.java new file mode 100644 index 00000000000..a6e61e2aef3 --- /dev/null +++ b/src/test/java/model/CampTest.java @@ -0,0 +1,19 @@ +package model; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CampTest { + + @DisplayName("현재 Camp가 WHITE 면 BLACK을 현재 Camp가 BLACK이면 WHITE를 반환한다.") + @Test + void checkToggle() { + assertAll( + () -> assertThat(Camp.BLACK.toggle()).isEqualTo(Camp.WHITE), + () -> assertThat(Camp.WHITE.toggle()).isEqualTo(Camp.BLACK) + ); + } +} diff --git a/src/test/java/model/ChessGameTest.java b/src/test/java/model/ChessGameTest.java new file mode 100644 index 00000000000..360157c9c4c --- /dev/null +++ b/src/test/java/model/ChessGameTest.java @@ -0,0 +1,218 @@ +package model; + +import static model.Fixtures.A1; +import static model.Fixtures.A2; +import static model.Fixtures.A3; +import static model.Fixtures.A4; +import static model.Fixtures.A5; +import static model.Fixtures.A6; +import static model.Fixtures.A7; +import static model.Fixtures.A8; +import static model.Fixtures.B1; +import static model.Fixtures.B2; +import static model.Fixtures.B5; +import static model.Fixtures.B7; +import static model.Fixtures.B8; +import static model.Fixtures.C1; +import static model.Fixtures.C2; +import static model.Fixtures.C7; +import static model.Fixtures.C8; +import static model.Fixtures.D1; +import static model.Fixtures.D2; +import static model.Fixtures.D7; +import static model.Fixtures.D8; +import static model.Fixtures.E1; +import static model.Fixtures.E2; +import static model.Fixtures.E5; +import static model.Fixtures.E7; +import static model.Fixtures.E8; +import static model.Fixtures.F1; +import static model.Fixtures.F2; +import static model.Fixtures.F7; +import static model.Fixtures.F8; +import static model.Fixtures.G1; +import static model.Fixtures.G2; +import static model.Fixtures.G7; +import static model.Fixtures.G8; +import static model.Fixtures.H1; +import static model.Fixtures.H2; +import static model.Fixtures.H7; +import static model.Fixtures.H8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import exception.InvalidTurnException; +import exception.PieceDoesNotExistException; +import exception.PieceExistInRouteException; +import java.util.HashMap; +import java.util.Map; +import model.piece.Bishop; +import model.piece.BlackPawn; +import model.piece.King; +import model.piece.Knight; +import model.piece.Piece; +import model.piece.Queen; +import model.piece.Rook; +import model.piece.WhitePawn; +import model.position.Moving; +import model.position.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class ChessGameTest { + + @DisplayName("초기에는 32개의 기물이 생성된다.") + @Test + void checkPiecesCount() { + //given && when + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + //then + final Map board = chessGame.getBoard(); + assertThat(board.keySet()).hasSize(32); + } + + @DisplayName("기물들의 시작 위치를 확인한다.") + @Test + void checkStartingPosition() { + //given && when + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + final Map board = chessGame.getBoard(); + + final Map expected = new HashMap<>(); + + // black + expected.put(A8, new Rook(Camp.BLACK)); + expected.put(B8, new Knight(Camp.BLACK)); + expected.put(C8, new Bishop(Camp.BLACK)); + expected.put(D8, new Queen(Camp.BLACK)); + expected.put(E8, new King(Camp.BLACK)); + expected.put(F8, new Bishop(Camp.BLACK)); + expected.put(G8, new Knight(Camp.BLACK)); + expected.put(H8, new Rook(Camp.BLACK)); + expected.put(A7, new BlackPawn()); + expected.put(B7, new BlackPawn()); + expected.put(C7, new BlackPawn()); + expected.put(D7, new BlackPawn()); + expected.put(E7, new BlackPawn()); + expected.put(F7, new BlackPawn()); + expected.put(G7, new BlackPawn()); + expected.put(H7, new BlackPawn()); + + //white + expected.put(A1, new Rook(Camp.WHITE)); + expected.put(B1, new Knight(Camp.WHITE)); + expected.put(C1, new Bishop(Camp.WHITE)); + expected.put(D1, new Queen(Camp.WHITE)); + expected.put(E1, new King(Camp.WHITE)); + expected.put(F1, new Bishop(Camp.WHITE)); + expected.put(G1, new Knight(Camp.WHITE)); + expected.put(H1, new Rook(Camp.WHITE)); + expected.put(A2, new WhitePawn()); + expected.put(B2, new WhitePawn()); + expected.put(C2, new WhitePawn()); + expected.put(D2, new WhitePawn()); + expected.put(E2, new WhitePawn()); + expected.put(F2, new WhitePawn()); + expected.put(G2, new WhitePawn()); + expected.put(H2, new WhitePawn()); + + //then + assertThat(board).isEqualTo(expected); + } + + @DisplayName("해당 위치에 기물이 없는 경우 예외가 발생한다.") + @ParameterizedTest + @ValueSource(strings = {"a3", "b3", "c3", "d4", "e4", "f5", "g6", "h6"}) + void failToMoveIfNoPiece(final String currentPosition) { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + final Position position = Position.from(currentPosition); + + final Moving moving = new Moving(position, E5); + + //when & then + assertThatThrownBy(() -> chessGame.move(moving)) + .isInstanceOf(PieceDoesNotExistException.class); + } + + @Test + @DisplayName("자신의 기물이 아니면 예외가 발생한다.") + void failToMoveIfDifferentCamp() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + //when && then + final Moving moving = new Moving(A7, A6); + + assertThatThrownBy(() -> chessGame.move(moving)) + .isInstanceOf(InvalidTurnException.class); + } + + @DisplayName("이동 경로에 기물이 있으면 예외를 발생시킨다.") + @Test + void failToMoveIfContainPieceInRoute() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + //when && then + final Moving moving = new Moving(A1, A3); + + assertThatThrownBy(() -> chessGame.move(moving)) + .isInstanceOf(PieceExistInRouteException.class); + } + + @DisplayName("도착 지점에 같은 진영의 기물이 있으면 예외를 발생시킨다.") + @Test + void failToMoveIfContainsPieceInTargetPosition() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + //when + chessGame.move(new Moving(A2, A4)); // WHITE + chessGame.move(new Moving(A7, A5)); // BLACK + + //then + final Moving moving = new Moving(A1, A4); + + assertThatThrownBy(() -> chessGame.move(moving)) + .isInstanceOf(PieceExistInRouteException.class); + } + + @DisplayName("기물이 잡히면 체스보드에서 제거된다.") + @Test + void checkRemovePiece() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + //when + chessGame.move(new Moving(A2, A4)); + chessGame.move(new Moving(B7, B5)); + chessGame.move(new Moving(A4, B5)); + + //then + assertThat(chessGame.getBoard()).hasSize(31); + } + + @DisplayName("선공은 WHITE이다.") + @Test + void checkFirstAttack() { + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + assertThat(chessGame.getCamp()).isEqualTo(Camp.WHITE); + } + + @DisplayName("후공은 BLACK이다.") + @Test + void checkSecondAttack() { + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + chessGame.move(new Moving(A2, A3)); + + assertThat(chessGame.getCamp()).isEqualTo(Camp.BLACK); + } +} diff --git a/src/test/java/model/Fixtures.java b/src/test/java/model/Fixtures.java new file mode 100644 index 00000000000..b59a21b025e --- /dev/null +++ b/src/test/java/model/Fixtures.java @@ -0,0 +1,84 @@ +package model; + +import model.position.File; +import model.position.Position; +import model.position.Rank; + +@SuppressWarnings("unused") +public class Fixtures { + + public static final Position A1 = new Position(File.A, Rank.ONE); + public static final Position A2 = new Position(File.A, Rank.TWO); + public static final Position A3 = new Position(File.A, Rank.THREE); + public static final Position A4 = new Position(File.A, Rank.FOUR); + public static final Position A5 = new Position(File.A, Rank.FIVE); + public static final Position A6 = new Position(File.A, Rank.SIX); + public static final Position A7 = new Position(File.A, Rank.SEVEN); + public static final Position A8 = new Position(File.A, Rank.EIGHT); + + public static final Position B1 = new Position(File.B, Rank.ONE); + public static final Position B2 = new Position(File.B, Rank.TWO); + public static final Position B3 = new Position(File.B, Rank.THREE); + public static final Position B4 = new Position(File.B, Rank.FOUR); + public static final Position B5 = new Position(File.B, Rank.FIVE); + public static final Position B6 = new Position(File.B, Rank.SIX); + public static final Position B7 = new Position(File.B, Rank.SEVEN); + public static final Position B8 = new Position(File.B, Rank.EIGHT); + + public static final Position C1 = new Position(File.C, Rank.ONE); + public static final Position C2 = new Position(File.C, Rank.TWO); + public static final Position C3 = new Position(File.C, Rank.THREE); + public static final Position C4 = new Position(File.C, Rank.FOUR); + public static final Position C5 = new Position(File.C, Rank.FIVE); + public static final Position C6 = new Position(File.C, Rank.SIX); + public static final Position C7 = new Position(File.C, Rank.SEVEN); + public static final Position C8 = new Position(File.C, Rank.EIGHT); + + public static final Position D1 = new Position(File.D, Rank.ONE); + public static final Position D2 = new Position(File.D, Rank.TWO); + public static final Position D3 = new Position(File.D, Rank.THREE); + public static final Position D4 = new Position(File.D, Rank.FOUR); + public static final Position D5 = new Position(File.D, Rank.FIVE); + public static final Position D6 = new Position(File.D, Rank.SIX); + public static final Position D7 = new Position(File.D, Rank.SEVEN); + public static final Position D8 = new Position(File.D, Rank.EIGHT); + + public static final Position E1 = new Position(File.E, Rank.ONE); + public static final Position E2 = new Position(File.E, Rank.TWO); + public static final Position E3 = new Position(File.E, Rank.THREE); + public static final Position E4 = new Position(File.E, Rank.FOUR); + public static final Position E5 = new Position(File.E, Rank.FIVE); + public static final Position E6 = new Position(File.E, Rank.SIX); + public static final Position E7 = new Position(File.E, Rank.SEVEN); + public static final Position E8 = new Position(File.E, Rank.EIGHT); + + public static final Position F1 = new Position(File.F, Rank.ONE); + public static final Position F2 = new Position(File.F, Rank.TWO); + public static final Position F3 = new Position(File.F, Rank.THREE); + public static final Position F4 = new Position(File.F, Rank.FOUR); + public static final Position F5 = new Position(File.F, Rank.FIVE); + public static final Position F6 = new Position(File.F, Rank.SIX); + public static final Position F7 = new Position(File.F, Rank.SEVEN); + public static final Position F8 = new Position(File.F, Rank.EIGHT); + + public static final Position G1 = new Position(File.G, Rank.ONE); + public static final Position G2 = new Position(File.G, Rank.TWO); + public static final Position G3 = new Position(File.G, Rank.THREE); + public static final Position G4 = new Position(File.G, Rank.FOUR); + public static final Position G5 = new Position(File.G, Rank.FIVE); + public static final Position G6 = new Position(File.G, Rank.SIX); + public static final Position G7 = new Position(File.G, Rank.SEVEN); + public static final Position G8 = new Position(File.G, Rank.EIGHT); + + public static final Position H1 = new Position(File.H, Rank.ONE); + public static final Position H2 = new Position(File.H, Rank.TWO); + public static final Position H3 = new Position(File.H, Rank.THREE); + public static final Position H4 = new Position(File.H, Rank.FOUR); + public static final Position H5 = new Position(File.H, Rank.FIVE); + public static final Position H6 = new Position(File.H, Rank.SIX); + public static final Position H7 = new Position(File.H, Rank.SEVEN); + public static final Position H8 = new Position(File.H, Rank.EIGHT); + + private Fixtures() { + } +} diff --git a/src/test/java/model/command/CommandLineTest.java b/src/test/java/model/command/CommandLineTest.java new file mode 100644 index 00000000000..1255e96e40a --- /dev/null +++ b/src/test/java/model/command/CommandLineTest.java @@ -0,0 +1,33 @@ +package model.command; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import exception.InvalidCommandException; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class CommandLineTest { + + @DisplayName("유효하지 않은 커맨드가 입력되면 예외가 발생한다.") + @ParameterizedTest + @MethodSource("invalidInputParameterProvider") + void invalidInput(List input) { + assertThatThrownBy(() -> CommandLine.from(input)) + .isInstanceOf(InvalidCommandException.class); + } + + static Stream invalidInputParameterProvider() { + return Stream.of( + Arguments.of(List.of("start", "a2", "a3")), + Arguments.of(List.of("start", "end")), + Arguments.of(List.of("move", "a2")), + Arguments.of(List.of("move", "a2", "a3", "a4")), + Arguments.of(List.of("end", "a2", "a3")), + Arguments.of(List.of("end", "end")) + ); + } +} diff --git a/src/test/java/model/command/CommandTest.java b/src/test/java/model/command/CommandTest.java new file mode 100644 index 00000000000..2079e9f1c1e --- /dev/null +++ b/src/test/java/model/command/CommandTest.java @@ -0,0 +1,62 @@ +package model.command; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import exception.InvalidCommandException; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class CommandTest { + + @DisplayName("올바른 command가 입력되면 예외가 발생하지 않는다.") + @Test + void checkCommand() { + assertAll( + () -> assertThat(Command.from("start")).isEqualTo(Command.START), + () -> assertThat(Command.from("end")).isEqualTo(Command.END), + () -> assertThat(Command.from("move")).isEqualTo(Command.MOVE), + () -> assertThat(Command.from("a1")).isEqualTo(Command.POSITION), + () -> assertThat(Command.from("h8")).isEqualTo(Command.POSITION), + () -> assertThat(Command.from("D8")).isEqualTo(Command.POSITION) + ); + } + + @ParameterizedTest + @DisplayName("유효하지 않은 command가 입력되면 예외가 발생한다.") + @MethodSource("invalidCommandParameterProvider") + void invalidCommand(List value) { + assertThatThrownBy(() -> Command.validate(value)) + .isInstanceOf(InvalidCommandException.class); + } + + static Stream invalidCommandParameterProvider() { + return Stream.of( + Arguments.of(List.of("start", "end")), + Arguments.of(List.of("sta")), + Arguments.of(List.of("end", "a1", "a2")), + Arguments.of(List.of("move")) + ); + } + + @DisplayName("각 command 의 body 사이즈를 확인한다.") + @ParameterizedTest + @MethodSource("checkBodySizeParameterProvider") + void checkBodySize(Command command, List body) { + assertThat(command.isEqualToBodySize(body.size())).isTrue(); + } + + static Stream checkBodySizeParameterProvider() { + return Stream.of( + Arguments.of(Command.START, List.of()), + Arguments.of(Command.END, List.of()), + Arguments.of(Command.MOVE, List.of("a1", "a2")) + ); + } +} diff --git a/src/test/java/model/piece/BishopTest.java b/src/test/java/model/piece/BishopTest.java new file mode 100644 index 00000000000..b654b70ff89 --- /dev/null +++ b/src/test/java/model/piece/BishopTest.java @@ -0,0 +1,81 @@ +package model.piece; + +import static model.Fixtures.A3; +import static model.Fixtures.A6; +import static model.Fixtures.A8; +import static model.Fixtures.B4; +import static model.Fixtures.B5; +import static model.Fixtures.C3; +import static model.Fixtures.C4; +import static model.Fixtures.C5; +import static model.Fixtures.C8; +import static model.Fixtures.D5; +import static model.Fixtures.D6; +import static model.Fixtures.D7; +import static model.Fixtures.E6; +import static model.Fixtures.E7; +import static model.Fixtures.E8; +import static model.Fixtures.F7; +import static model.Fixtures.F8; +import static model.Fixtures.G4; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import exception.InvalidMovingException; +import java.util.Set; +import java.util.stream.Stream; +import model.Camp; +import model.position.Moving; +import model.position.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class BishopTest { + + @DisplayName("이동할 수 없는 경로면 예외가 발생한다.") + @ParameterizedTest + @MethodSource("invalidMovingParameterProvider") + void invalidMoving(final Moving moving) { + final Bishop bishop = new Bishop(Camp.BLACK); + + assertAll( + () -> assertThat(bishop.canMovable(moving)).isFalse(), + () -> assertThatThrownBy(() -> bishop.getMoveRoute(moving)) + .isInstanceOf(InvalidMovingException.class) + ); + } + + static Stream invalidMovingParameterProvider() { + return Stream.of( + Arguments.of(new Moving(C8, A8)), + Arguments.of(new Moving(C8, C3)), + Arguments.of(new Moving(F8, E8)), + Arguments.of(new Moving(G4, C3)) + ); + } + + @DisplayName("이동 경로를 반환한다. 출발지와 도착지는 포함하지 않는다.") + @ParameterizedTest + @MethodSource("checkRouteParameterProvider") + void checkRoute(final Moving moving, final Set expected) { + final Bishop bishop = new Bishop(Camp.BLACK); + + assertAll( + () -> assertThat(bishop.canMovable(moving)).isTrue(), + () -> assertThat(bishop.getMoveRoute(moving)).isEqualTo(expected) + ); + + } + + static Stream checkRouteParameterProvider() { + return Stream.of( + Arguments.of(new Moving(C8, E6), Set.of(D7)), + Arguments.of(new Moving(F8, A3), Set.of(E7, D6, C5, B4)), + Arguments.of(new Moving(C4, A6), Set.of(B5)), + Arguments.of(new Moving(C4, F7), Set.of(D5, E6)) + ); + } +} diff --git a/src/test/java/model/piece/KingTest.java b/src/test/java/model/piece/KingTest.java new file mode 100644 index 00000000000..f9fc3767c9a --- /dev/null +++ b/src/test/java/model/piece/KingTest.java @@ -0,0 +1,84 @@ +package model.piece; + +import static model.Fixtures.A8; +import static model.Fixtures.B5; +import static model.Fixtures.D2; +import static model.Fixtures.D3; +import static model.Fixtures.D6; +import static model.Fixtures.D7; +import static model.Fixtures.D8; +import static model.Fixtures.E1; +import static model.Fixtures.E2; +import static model.Fixtures.E7; +import static model.Fixtures.E8; +import static model.Fixtures.F2; +import static model.Fixtures.F5; +import static model.Fixtures.F7; +import static model.Fixtures.F8; +import static model.Fixtures.G4; +import static model.Fixtures.H2; +import static model.Fixtures.H6; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import exception.InvalidMovingException; +import java.util.Set; +import java.util.stream.Stream; +import model.Camp; +import model.position.Moving; +import model.position.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class KingTest { + + @DisplayName("이동할 수 없는 경로면 예외가 발생한다.") + @ParameterizedTest + @MethodSource("invalidMovingParameterProvider") + void invalidMoving(final Moving moving) { + final King king = new King(Camp.BLACK); + + assertAll( + () -> assertThat(king.canMovable(moving)).isFalse(), + () -> assertThatThrownBy(() -> king.getMoveRoute(moving)) + .isInstanceOf(InvalidMovingException.class) + ); + } + + static Stream invalidMovingParameterProvider() { + return Stream.of( + Arguments.of(new Moving(D6, B5)), + Arguments.of(new Moving(F5, A8)), + Arguments.of(new Moving(D3, H2)), + Arguments.of(new Moving(H6, G4)) + ); + } + + @DisplayName("이동 경로를 반환한다. 출발지와 도착지는 포함하지 않는다.") + @ParameterizedTest + @MethodSource("checkRouteParameterProvider") + void checkRoute(final Moving moving, final Set expected) { + final King king = new King(Camp.BLACK); + + assertAll( + () -> assertThat(king.canMovable(moving)).isTrue(), + () -> assertThat(king.getMoveRoute(moving)).isEqualTo(expected) + ); + } + + static Stream checkRouteParameterProvider() { + return Stream.of( + Arguments.of(new Moving(E8, D8), Set.of()), + Arguments.of(new Moving(E8, E7), Set.of()), + Arguments.of(new Moving(E8, D7), Set.of()), + Arguments.of(new Moving(E8, F7), Set.of()), + Arguments.of(new Moving(E8, F8), Set.of()), + Arguments.of(new Moving(E1, D2), Set.of()), + Arguments.of(new Moving(E1, E2), Set.of()), + Arguments.of(new Moving(E1, F2), Set.of()) + ); + } +} diff --git a/src/test/java/model/piece/KnightTest.java b/src/test/java/model/piece/KnightTest.java new file mode 100644 index 00000000000..e975af2ef5b --- /dev/null +++ b/src/test/java/model/piece/KnightTest.java @@ -0,0 +1,79 @@ +package model.piece; + +import static model.Fixtures.A5; +import static model.Fixtures.A8; +import static model.Fixtures.B5; +import static model.Fixtures.B6; +import static model.Fixtures.B7; +import static model.Fixtures.C4; +import static model.Fixtures.C7; +import static model.Fixtures.C8; +import static model.Fixtures.D5; +import static model.Fixtures.D6; +import static model.Fixtures.E5; +import static model.Fixtures.E8; +import static model.Fixtures.F3; +import static model.Fixtures.F7; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import exception.InvalidMovingException; +import java.util.Set; +import java.util.stream.Stream; +import model.Camp; +import model.position.Moving; +import model.position.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class KnightTest { + + @DisplayName("이동할 수 없는 경로면 예외가 발생한다.") + @ParameterizedTest + @MethodSource("invalidMovingParameterProvider") + void invalidMoving(final Moving moving) { + final Knight knight = new Knight(Camp.BLACK); + + assertAll( + () -> assertThat(knight.canMovable(moving)).isFalse(), + () -> assertThatThrownBy(() -> knight.getMoveRoute(moving)) + .isInstanceOf(InvalidMovingException.class) + ); + } + + static Stream invalidMovingParameterProvider() { + return Stream.of( + Arguments.of(new Moving(D5, A5)), + Arguments.of(new Moving(A8, F3)), + Arguments.of(new Moving(A8, E5)) + ); + } + + @DisplayName("이동 경로를 반환한다. 출발지와 도착지는 포함하지 않는다.") + @ParameterizedTest + @MethodSource("checkRouteParameterProvider") + void checkRoute(final Moving moving, final Set expected) { + final Knight knight = new Knight(Camp.BLACK); + + assertAll( + () -> assertThat(knight.canMovable(moving)).isTrue(), + () -> assertThat(knight.getMoveRoute(moving)).isEqualTo(expected) + ); + } + + static Stream checkRouteParameterProvider() { + return Stream.of( + Arguments.of(new Moving(A8, C7), Set.of()), + Arguments.of(new Moving(A8, B6), Set.of()), + Arguments.of(new Moving(D6, C4), Set.of()), + Arguments.of(new Moving(D6, B5), Set.of()), + Arguments.of(new Moving(D6, B7), Set.of()), + Arguments.of(new Moving(D6, C8), Set.of()), + Arguments.of(new Moving(D6, E8), Set.of()), + Arguments.of(new Moving(D6, F7), Set.of()) + ); + } +} diff --git a/src/test/java/model/piece/PawnTest.java b/src/test/java/model/piece/PawnTest.java new file mode 100644 index 00000000000..4313e5263fa --- /dev/null +++ b/src/test/java/model/piece/PawnTest.java @@ -0,0 +1,240 @@ +package model.piece; + +import static model.Fixtures.A2; +import static model.Fixtures.A4; +import static model.Fixtures.A5; +import static model.Fixtures.A6; +import static model.Fixtures.A7; +import static model.Fixtures.A8; +import static model.Fixtures.B2; +import static model.Fixtures.B3; +import static model.Fixtures.B4; +import static model.Fixtures.B5; +import static model.Fixtures.B7; +import static model.Fixtures.C2; +import static model.Fixtures.C3; +import static model.Fixtures.C4; +import static model.Fixtures.D5; +import static model.Fixtures.D6; +import static model.Fixtures.D7; +import static model.Fixtures.G8; +import static model.Fixtures.H2; +import static model.Fixtures.H3; +import static model.Fixtures.H4; +import static model.Fixtures.H5; +import static model.Fixtures.H6; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import exception.InvalidMovingException; +import java.util.Set; +import java.util.stream.Stream; +import model.Camp; +import model.ChessGame; +import model.position.Moving; +import model.position.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class PawnTest { + + @DisplayName("이동할 수 없는 경로면 예외가 발생한다.") + @ParameterizedTest + @MethodSource("invalidMovingParameterProvider") + void invalidMoving(final Camp camp, final Moving moving) { + final Pawn pawn = Pawn.create(camp); + + assertAll( + () -> assertThat(pawn.canMovable(moving)).isFalse(), + () -> assertThatThrownBy(() -> pawn.getMoveRoute(moving)) + .isInstanceOf(InvalidMovingException.class) + ); + } + + static Stream invalidMovingParameterProvider() { + return Stream.of( + Arguments.of(Camp.BLACK, new Moving(A6, A4)), + Arguments.of(Camp.BLACK, new Moving(A7, A4)), + Arguments.of(Camp.WHITE, new Moving(A8, A7)) + ); + } + + @DisplayName("이동 경로를 반환한다. 출발지와 도착지는 포함하지 않는다.") + @ParameterizedTest + @MethodSource("checkRouteParameterProvider") + void checkRoute(final Camp camp, final Moving moving, final Set expected) { + final Pawn pawn = Pawn.create(camp); + + assertAll( + () -> assertThat(pawn.canMovable(moving)).isTrue(), + () -> assertThat(pawn.getMoveRoute(moving)).isEqualTo(expected) + ); + + } + + static Stream checkRouteParameterProvider() { + return Stream.of( + Arguments.of(Camp.BLACK, new Moving(A7, A5), Set.of(A6)), + Arguments.of(Camp.BLACK, new Moving(A6, A5), Set.of()), + Arguments.of(Camp.WHITE, new Moving(B2, B4), Set.of(B3)), + Arguments.of(Camp.WHITE, new Moving(C2, C3), Set.of()) + ); + } + + @Test + @DisplayName("앞에 기물이 있을때 전진이 불가하다.") + void whenPieceInFrontCanNotMoveForward() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + /* + RNBQKBNR 8 + .PPPPPPP 7 + ........ 6 + P....... 5 + p....... 4 + ........ 3 + .ppppppp 2 + rnbqkbnr 1 + + abcdefgh + */ + + //when + chessGame.move(new Moving(A2, A4)); + chessGame.move(new Moving(A7, A5)); + + final Moving forwadMoving = new Moving(A4, A5); + + //then + assertThatThrownBy(() -> chessGame.move(forwadMoving)) + .isInstanceOf(InvalidMovingException.class); + } + + @Test + @DisplayName("대각선에 기물이 있을 때 대각선 이동이 가능하다. WHITE (위 오른쪽)") + void whenPieceInDiagonalCanMove1() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + /* + RNBQKBNR 8 + P.PPPPPP 7 + ........ 6 + .P...... 5 + p....... 4 + ........ 3 + .ppppppp 2 + rnbqkbnr 1 + + abcdefgh + */ + + //when + chessGame.move(new Moving(A2, A4)); + chessGame.move(new Moving(B7, B5)); + + //then + assertThatCode(() -> chessGame.move(new Moving(A4, B5))) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("대각선에 기물이 있다면 이동이 가능하다. BLACK (아래 오른쪽)") + void whenPieceInDiagonalCanMove2() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + /* + RNBQKBNR 8 + P.PPPPPP 7 + ........ 6 + .P...... 5 + ..p..... 4 + .p...... 3 + p..ppppp 2 + rnbqkbnr 1 + + abcdefgh + */ + + //when + chessGame.move(new Moving(C2, C4)); + chessGame.move(new Moving(B7, B5)); + chessGame.move(new Moving(B2, B3)); + + //then + assertThatCode(() -> chessGame.move(new Moving(B5, C4))) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("대각선에 기물이 없다면 대각선 이동이 불가하다.") + void whenPieceNotInDiagonalCanNotMove() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + /* + RNBQKB.R 8 + PPPPPPPP 7 + .......N 6 + ........ 5 + p....... 4 + ........ 3 + .ppppppp 2 + rnbqkbnr 1 + + abcdefgh + */ + + //when + chessGame.move(new Moving(A2, A4)); + chessGame.move(new Moving(G8, H6)); + + final Moving diagonalMoving = new Moving(A4, B5); + + //then + assertThatThrownBy(() -> chessGame.move(diagonalMoving)) + .isInstanceOf(InvalidMovingException.class); + } + + @Test + @DisplayName("폰은 후진할 수 없다. WHITE") + void failToMoveBackWhitePawn() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + //when + chessGame.move(new Moving(H2, H4)); + chessGame.move(new Moving(D7, D5)); + + final Moving whiteBackMoving = new Moving(H4, H3); + + //then + assertThatThrownBy(() -> chessGame.move(whiteBackMoving)) + .isInstanceOf(InvalidMovingException.class); + } + + @Test + @DisplayName("폰은 후진할 수 없다. BLACK") + void failToMoveBackBlackPawn() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + //when + chessGame.move(new Moving(H2, H4)); + chessGame.move(new Moving(D7, D5)); + chessGame.move(new Moving(H4, H5)); + + final Moving blackBackMoving = new Moving(D5, D6); + + //then + assertThatThrownBy(() -> chessGame.move(blackBackMoving)) + .isInstanceOf(InvalidMovingException.class); + } +} diff --git a/src/test/java/model/piece/PieceTest.java b/src/test/java/model/piece/PieceTest.java new file mode 100644 index 00000000000..bd1fa20faa5 --- /dev/null +++ b/src/test/java/model/piece/PieceTest.java @@ -0,0 +1,36 @@ +package model.piece; + +import static model.Fixtures.G7; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import exception.InvalidMovingException; +import java.util.stream.Stream; +import model.Camp; +import model.position.Moving; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class PieceTest { + + @ParameterizedTest + @DisplayName("위치가 동일하다면 예외가 발생한다.") + @MethodSource("invalidMovingParameterProvider") + void invalidMoving(Piece piece) { + final Moving notMovedMoving = new Moving(G7, G7); + assertThatThrownBy(() -> piece.getMoveRoute(notMovedMoving)) + .isInstanceOf(InvalidMovingException.class); + } + + static Stream invalidMovingParameterProvider() { + return Stream.of( + Arguments.of(new Bishop(Camp.BLACK)), + Arguments.of(new King(Camp.BLACK)), + Arguments.of(new Knight(Camp.WHITE)), + Arguments.of(new WhitePawn()), + Arguments.of(new Queen(Camp.WHITE)), + Arguments.of(new Rook(Camp.WHITE)) + ); + } +} diff --git a/src/test/java/model/piece/QueenTest.java b/src/test/java/model/piece/QueenTest.java new file mode 100644 index 00000000000..fad48a6a3b8 --- /dev/null +++ b/src/test/java/model/piece/QueenTest.java @@ -0,0 +1,98 @@ +package model.piece; + +import static model.Fixtures.A2; +import static model.Fixtures.A8; +import static model.Fixtures.B3; +import static model.Fixtures.B5; +import static model.Fixtures.B6; +import static model.Fixtures.B8; +import static model.Fixtures.C5; +import static model.Fixtures.C6; +import static model.Fixtures.C7; +import static model.Fixtures.C8; +import static model.Fixtures.D1; +import static model.Fixtures.D2; +import static model.Fixtures.D3; +import static model.Fixtures.D4; +import static model.Fixtures.D5; +import static model.Fixtures.D6; +import static model.Fixtures.D7; +import static model.Fixtures.D8; +import static model.Fixtures.E4; +import static model.Fixtures.E5; +import static model.Fixtures.E6; +import static model.Fixtures.E7; +import static model.Fixtures.E8; +import static model.Fixtures.F5; +import static model.Fixtures.F6; +import static model.Fixtures.F8; +import static model.Fixtures.G6; +import static model.Fixtures.G8; +import static model.Fixtures.H1; +import static model.Fixtures.H7; +import static model.Fixtures.H8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import exception.InvalidMovingException; +import java.util.Set; +import java.util.stream.Stream; +import model.Camp; +import model.position.Moving; +import model.position.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class QueenTest { + + @DisplayName("이동할 수 없는 경로면 예외가 발생한다.") + @ParameterizedTest + @MethodSource("invalidMovingParameterProvider") + void invalidMoving(final Moving moving) { + final Queen queen = new Queen(Camp.BLACK); + + assertAll( + () -> assertThat(queen.canMovable(moving)).isFalse(), + () -> assertThatThrownBy(() -> queen.getMoveRoute(moving)) + .isInstanceOf(InvalidMovingException.class) + ); + } + + static Stream invalidMovingParameterProvider() { + return Stream.of( + Arguments.of(new Moving(B3, B3)), + Arguments.of(new Moving(D8, E5)), + Arguments.of(new Moving(D6, A2)), + Arguments.of(new Moving(B5, A8)), + Arguments.of(new Moving(H1, C5)) + ); + } + + @DisplayName("이동 경로를 반환한다. 출발지와 도착지는 포함하지 않는다.") + @ParameterizedTest + @MethodSource("checkRouteParameterProvider") + void checkRoute(final Moving moving, final Set expected) { + final Queen queen = new Queen(Camp.BLACK); + + assertAll( + () -> assertThat(queen.canMovable(moving)).isTrue(), + () -> assertThat(queen.getMoveRoute(moving)).isEqualTo(expected) + ); + } + + static Stream checkRouteParameterProvider() { + return Stream.of( + Arguments.of(new Moving(D8, D1), Set.of(D2, D3, D4, D5, D6, D7)), + Arguments.of(new Moving(D8, A8), Set.of(B8, C8)), + Arguments.of(new Moving(D8, H8), Set.of(E8, F8, G8)), + Arguments.of(new Moving(D8, B6), Set.of(C7)), + Arguments.of(new Moving(D8, F6), Set.of(E7)), + Arguments.of(new Moving(E4, E8), Set.of(E5, E6, E7)), + Arguments.of(new Moving(E4, C6), Set.of(D5)), + Arguments.of(new Moving(E4, H7), Set.of(F5, G6))); + + } +} diff --git a/src/test/java/model/piece/RookTest.java b/src/test/java/model/piece/RookTest.java new file mode 100644 index 00000000000..801171b1937 --- /dev/null +++ b/src/test/java/model/piece/RookTest.java @@ -0,0 +1,88 @@ +package model.piece; + +import static model.Fixtures.A2; +import static model.Fixtures.A3; +import static model.Fixtures.A4; +import static model.Fixtures.A5; +import static model.Fixtures.A6; +import static model.Fixtures.A7; +import static model.Fixtures.A8; +import static model.Fixtures.B3; +import static model.Fixtures.B7; +import static model.Fixtures.C1; +import static model.Fixtures.C2; +import static model.Fixtures.C3; +import static model.Fixtures.C4; +import static model.Fixtures.D8; +import static model.Fixtures.E3; +import static model.Fixtures.E8; +import static model.Fixtures.F7; +import static model.Fixtures.F8; +import static model.Fixtures.G2; +import static model.Fixtures.G3; +import static model.Fixtures.G4; +import static model.Fixtures.G5; +import static model.Fixtures.G6; +import static model.Fixtures.G8; +import static model.Fixtures.H5; +import static model.Fixtures.H8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import exception.InvalidMovingException; +import java.util.Set; +import java.util.stream.Stream; +import model.Camp; +import model.position.Moving; +import model.position.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class RookTest { + + @DisplayName("이동할 수 없는 경로면 예외가 발생한다.") + @ParameterizedTest + @MethodSource("invalidMovingParameterProvider") + void invalidMoving(final Moving moving) { + final Rook rook = new Rook(Camp.BLACK); + + assertAll( + () -> assertThat(rook.canMovable(moving)).isFalse(), + () -> assertThatThrownBy(() -> rook.getMoveRoute(moving)) + .isInstanceOf(InvalidMovingException.class) + ); + } + + static Stream invalidMovingParameterProvider() { + return Stream.of( + Arguments.of(new Moving(A8, B7)), + Arguments.of(new Moving(A8, C4)), + Arguments.of(new Moving(B3, F7)), + Arguments.of(new Moving(E3, H5)) + ); + } + + @DisplayName("이동 경로를 반환한다. 출발지와 도착지는 포함하지 않는다.") + @ParameterizedTest + @MethodSource("checkRouteParameterProvider") + void checkRoute(final Moving moving, final Set expected) { + final Rook rook = new Rook(Camp.BLACK); + + assertAll( + () -> assertThat(rook.canMovable(moving)).isTrue(), + () -> assertThat(rook.getMoveRoute(moving)).isEqualTo(expected) + ); + } + + static Stream checkRouteParameterProvider() { + return Stream.of( + Arguments.of(new Moving(A8, A2), Set.of(A3, A4, A5, A6, A7)), + Arguments.of(new Moving(H8, D8), Set.of(G8, F8, E8)), + Arguments.of(new Moving(C3, C1), Set.of(C2)), + Arguments.of(new Moving(G2, G6), Set.of(G3, G4, G5)) + ); + } +} diff --git a/src/test/java/model/position/MovingTest.java b/src/test/java/model/position/MovingTest.java new file mode 100644 index 00000000000..9b1d74e4fe1 --- /dev/null +++ b/src/test/java/model/position/MovingTest.java @@ -0,0 +1,86 @@ +package model.position; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +class MovingTest { + + @DisplayName("같은 위치인지 확인한다.") + @ParameterizedTest + @ValueSource(strings = {"a2", "b4", "h8"}) + void checkIsNotMovedTrue(String value) { + //given + final Position current = Position.from(value); + final Position next = Position.from(value); + + //when + final Moving moving = new Moving(current, next); + + //then + assertThat(moving.isNotMoved()).isTrue(); + } + + @DisplayName("다른 위치인지 확인한다.") + @ParameterizedTest + @CsvSource(value = {"a1,a2", "b5,b3", "d8,g4"}) + void checkIsNotMovedFalse(String current, String next) { + //given + final Position currentPosition = Position.from(current); + final Position nextPosition = Position.from(next); + + //when + final Moving moving = new Moving(currentPosition, nextPosition); + + //then + assertThat(moving.isNotMoved()).isFalse(); + } + + @DisplayName("수평인지 확인한다.") + @ParameterizedTest + @CsvSource(value = {"b2,f2", "h6,a6", "d3,c3"}) + void checkHorizontal(String current, String next) { + //given + final Position currentPosition = Position.from(current); + final Position nextPosition = Position.from(next); + + //when + final Moving moving = new Moving(currentPosition, nextPosition); + + //then + assertThat(moving.isHorizontal()).isTrue(); + } + + @DisplayName("수직인지 확인한다.") + @ParameterizedTest + @CsvSource(value = {"a7,a1", "e2,e5", "g1,g8"}) + void checkVertical(String current, String next) { + //given + final Position currentPosition = Position.from(current); + final Position nextPosition = Position.from(next); + + //when + final Moving moving = new Moving(currentPosition, nextPosition); + + //then + assertThat(moving.isVertical()).isTrue(); + } + + @DisplayName("대각선인지 확인한다.") + @ParameterizedTest + @CsvSource(value = {"b8,g3", "h7,f5", "e1,d2", "a1,h8"}) + void checkDiagonal(String current, String next) { + //given + final Position currentPosition = Position.from(current); + final Position nextPosition = Position.from(next); + + //when + final Moving moving = new Moving(currentPosition, nextPosition); + + //then + assertThat(moving.isDiagonal()).isTrue(); + } +} diff --git a/src/test/java/model/position/PositionTest.java b/src/test/java/model/position/PositionTest.java new file mode 100644 index 00000000000..b727d7f42a3 --- /dev/null +++ b/src/test/java/model/position/PositionTest.java @@ -0,0 +1,23 @@ +package model.position; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import exception.InvalidPositionException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EmptySource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +class PositionTest { + + @DisplayName("유효하지 않은 위치인 경우 예외가 발생한다.") + @ParameterizedTest + @NullSource + @EmptySource + @ValueSource(strings = {"aa1", "abb", "h", "11", "aa33", "a1.", ".a1"}) + void invalidPosition(String value) { + assertThatThrownBy(() -> Position.from(value)) + .isInstanceOf(InvalidPositionException.class); + } +} diff --git a/src/test/java/model/status/EndTest.java b/src/test/java/model/status/EndTest.java new file mode 100644 index 00000000000..6758223ee69 --- /dev/null +++ b/src/test/java/model/status/EndTest.java @@ -0,0 +1,40 @@ +package model.status; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import exception.InvalidStatusException; +import java.util.List; +import model.ChessGame; +import model.command.CommandLine; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class EndTest { + + @DisplayName("종료 상태에서 play하면 예외가 발생한다.") + @Test + void failToPlayIfStatusEnd() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + final CommandLine startCommand = CommandLine.from(List.of("start")); + final GameStatus gameStatus = StatusFactory.create(startCommand); + final CommandLine endCommand = CommandLine.from(List.of("end")); + + //when + final GameStatus play = gameStatus.play(endCommand, chessGame); + + //then + assertThatThrownBy(() -> play.play(endCommand, chessGame)) + .isInstanceOf(InvalidStatusException.class); + } + + @DisplayName("종료 상태일 때 상태를 확인한다.") + @Test + void checkRunning() { + final CommandLine endCommand = CommandLine.from(List.of("end")); + final GameStatus gameStatus = StatusFactory.create(endCommand); + + assertThat(gameStatus.isRunning()).isFalse(); + } +} diff --git a/src/test/java/model/status/RunningTest.java b/src/test/java/model/status/RunningTest.java new file mode 100644 index 00000000000..7bfef73b516 --- /dev/null +++ b/src/test/java/model/status/RunningTest.java @@ -0,0 +1,60 @@ +package model.status; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import exception.InvalidStatusException; +import java.util.List; +import model.ChessGame; +import model.command.CommandLine; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RunningTest { + + @DisplayName("러닝 상태일때 상태를 확인한다.") + @Test + void checkRunning() { + final CommandLine startCommand = CommandLine.from(List.of("start")); + final GameStatus gameStatus = StatusFactory.create(startCommand); + + assertThat(gameStatus.isRunning()).isTrue(); + } + + @DisplayName("러닝 상태일 때 play 후 상태를 확인한다.") + @Test + void checkRunningAfterPlay() { + final CommandLine startCommand = CommandLine.from(List.of("start")); + final GameStatus gameStatus = StatusFactory.create(startCommand); + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + final CommandLine moveCommand = CommandLine.from(List.of("move", "a2", "a3")); + assertThat(gameStatus.play(moveCommand, chessGame)) + .isInstanceOf(Running.class); + } + + @DisplayName("러닝 상태에서 end 후 상태를 학인한다.") + @Test + void checkRunningAfterEnd() { + final CommandLine startCommand = CommandLine.from(List.of("start")); + final GameStatus gameStatus = StatusFactory.create(startCommand); + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + final CommandLine endCommand = CommandLine.from(List.of("end")); + assertThat(gameStatus.play(endCommand, chessGame)) + .isInstanceOf(End.class); + } + + @DisplayName("러닝 상태에서 start 하면 예외가 발생한다.") + @Test + void failToStartIfAlreadyRunning() { + //given + final CommandLine startCommand = CommandLine.from(List.of("start")); + final GameStatus gameStatus = StatusFactory.create(startCommand); + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + //when && then + assertThatThrownBy(() -> gameStatus.play(startCommand, chessGame)) + .isInstanceOf(InvalidStatusException.class); + } +} diff --git a/src/test/java/model/status/StatusFactoryTest.java b/src/test/java/model/status/StatusFactoryTest.java new file mode 100644 index 00000000000..439ded748c9 --- /dev/null +++ b/src/test/java/model/status/StatusFactoryTest.java @@ -0,0 +1,37 @@ +package model.status; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import exception.InvalidStatusException; +import java.util.List; +import model.command.CommandLine; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class StatusFactoryTest { + + @DisplayName("start를 입력하면 게임이 시작된다.") + @Test + void gameStartWhenCommandIsStart() { + final CommandLine startCommand = CommandLine.from(List.of("start")); + final GameStatus gameStatus = StatusFactory.create(startCommand); + assertThat(gameStatus).isInstanceOf(Running.class); + } + + @DisplayName("end를 입력하면 게임이 종료된다.") + @Test + void gameEndWhenCommandIsEnd() { + final CommandLine endCommand = CommandLine.from(List.of("end")); + final GameStatus gameStatus = StatusFactory.create(endCommand); + assertThat(gameStatus).isInstanceOf(End.class); + } + + @DisplayName("시작시 유효하지 않은 명령어가 오면 예외가 발생한다.") + @Test + void invalidCommand() { + CommandLine moveCommand = CommandLine.from(List.of("move", "a2", "d3")); + assertThatThrownBy(() -> StatusFactory.create(moveCommand)) + .isInstanceOf(InvalidStatusException.class); + } +} From 4a0d4789d352bcc78d80613f130af7f893c58583 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Wed, 27 Mar 2024 10:42:39 +0900 Subject: [PATCH 02/60] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=20=EC=82=AC=ED=95=AD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 21 ++++++++++++--------- src/main/java/constant/ErrorCode.java | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/README.md b/docs/README.md index ee8506d8cf0..5de7c36138a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,7 +16,19 @@ * [x] 기물의 이동 규칙을 벗어나는 경우 예외 발생 * [x] 현재 위치로 빈 칸을 입력받은 경우 예외 발생 * [x] 현재 턴이 아닌 기물을 이동하는 경우 예외 발생 +* [ ] status 기능 구현 + * [ ] 남아있는 기물의 점수 계산 + * queen: 9 + * rook: 5 + * bishop 3 + * knight: 2.5 + * pawn: 1 + * 같은 세로줄에 있는 같은 pawn인 경우 0.5 + * [ ] 진영의 점수를 출력 + * [ ] 승리한 진영의 결과 확인 +* [ ] 체크 메이트면 게임 종료 * [x] end 가 입력되면 프로그램 종료 +* [x] 예외 시 재입력 ## 기물의 이동 기능 @@ -42,14 +54,6 @@ * 게임이 시작되지 않았는데 move 명령어를 입력한다면 예외가 발생한다. * 형식은 move [a-h1-8] [a-h1-8] 형태이고 아닐시 예외가 발생한다. - -## 리팩터링 - -* [x] 테스트 코드 보충 -* [x] 프로그래밍 요구 사항 지키기 -* [x] 예외 메시지를 커스텀 예외로 만들기 -* [x] 예외 시 재입력 구현하기 - ## 우선순위가 낮지만 구현해보고 싶은 기능 * 특수 행마법 @@ -57,5 +61,4 @@ * [ ] 프로모션 * [ ] 캐슬링 * 체크 상황 - * [ ] 체크 메이트면 자동으로 게임 종료 * [ ] 체크 상황이면 체크를 피하는 방향으로만 이동 가능 diff --git a/src/main/java/constant/ErrorCode.java b/src/main/java/constant/ErrorCode.java index 282db5a284d..66d885e3e6b 100644 --- a/src/main/java/constant/ErrorCode.java +++ b/src/main/java/constant/ErrorCode.java @@ -1,7 +1,7 @@ package constant; public enum ErrorCode { - + //TODO: 같은 곳으로 이동할 때 예외 다르게 errorCode INVALID_INPUT, INVALID_STATUS, INVALID_COMMAND, From c7d94a666882c7d9266282e5e66a0a0f76db22e5 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Wed, 27 Mar 2024 11:40:31 +0900 Subject: [PATCH 03/60] =?UTF-8?q?feat:=20=EB=82=A8=EC=95=84=EC=9E=88?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=AC=BC=EC=9D=98=20=EC=A0=90=EC=88=98=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 4 +- src/main/java/model/Board.java | 18 +++++++++ src/main/java/model/ChessGame.java | 4 ++ src/main/java/model/PieceScore.java | 39 ++++++++++++++++++++ src/main/java/model/Score.java | 8 ++++ src/main/java/model/command/CommandLine.java | 1 + src/test/java/model/BoardTest.java | 25 +++++++++++++ src/test/java/model/PieceScoreTest.java | 39 ++++++++++++++++++++ src/test/java/model/ScoreTest.java | 20 ++++++++++ 9 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 src/main/java/model/PieceScore.java create mode 100644 src/main/java/model/Score.java create mode 100644 src/test/java/model/BoardTest.java create mode 100644 src/test/java/model/PieceScoreTest.java create mode 100644 src/test/java/model/ScoreTest.java diff --git a/docs/README.md b/docs/README.md index 5de7c36138a..c01367bb60c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -17,13 +17,13 @@ * [x] 현재 위치로 빈 칸을 입력받은 경우 예외 발생 * [x] 현재 턴이 아닌 기물을 이동하는 경우 예외 발생 * [ ] status 기능 구현 - * [ ] 남아있는 기물의 점수 계산 + * [x] 남아있는 기물의 점수 계산 * queen: 9 * rook: 5 * bishop 3 * knight: 2.5 * pawn: 1 - * 같은 세로줄에 있는 같은 pawn인 경우 0.5 + * [ ] 같은 세로줄에 있는 같은 pawn인 경우 0.5 * [ ] 진영의 점수를 출력 * [ ] 승리한 진영의 결과 확인 * [ ] 체크 메이트면 게임 종료 diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java index 834fb1ceabe..2bb702a016e 100644 --- a/src/main/java/model/Board.java +++ b/src/main/java/model/Board.java @@ -4,8 +4,11 @@ import exception.InvalidTurnException; import exception.PieceDoesNotExistException; import exception.PieceExistInRouteException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.function.Function; import model.piece.Bishop; @@ -112,6 +115,21 @@ public void move(final Moving moving) { pieces.remove(moving.getCurrentPosition()); } + public Score calculateScore(final Camp camp) { //TODO 람다 스트림으로 빼보기 + //TODO 폰 특수 처리 + final List sameCamp = new ArrayList<>(); + Score result = new Score(0); + for (final Entry entry : pieces.entrySet()) { + final Piece piece = entry.getValue(); + if (piece.isSameCamp(camp)) { + sameCamp.add(piece); + result = result.add(PieceScore.getScore(piece)); + } + } + return result; + } + + //TODO 방어적 복사 public Map getPieces() { return pieces; } diff --git a/src/main/java/model/ChessGame.java b/src/main/java/model/ChessGame.java index 0d1b69d9a09..2d405823bed 100644 --- a/src/main/java/model/ChessGame.java +++ b/src/main/java/model/ChessGame.java @@ -34,4 +34,8 @@ public Map getBoard() { public Camp getCamp() { return camp; } + + public Score calculateScore(final Camp camp) { + return board.calculateScore(camp); + } } diff --git a/src/main/java/model/PieceScore.java b/src/main/java/model/PieceScore.java new file mode 100644 index 00000000000..5d9963e380f --- /dev/null +++ b/src/main/java/model/PieceScore.java @@ -0,0 +1,39 @@ +package model; + +import java.util.Arrays; +import java.util.Objects; +import model.piece.Bishop; +import model.piece.BlackPawn; +import model.piece.King; +import model.piece.Knight; +import model.piece.Piece; +import model.piece.Queen; +import model.piece.Rook; +import model.piece.WhitePawn; + +public enum PieceScore { + + KING(King.class, new Score(0)), + QUEEN(Queen.class, new Score(9)), + ROOK(Rook.class, new Score(5)), + BISHOP(Bishop.class, new Score(3)), + KNIGHT(Knight.class, new Score(2.5F)), + WHITE_PAWN(WhitePawn.class, new Score(1)), //TODO whitePawn, blackPawn 나누지 않는 방법고민 + BLACK_PAWN(BlackPawn.class, new Score(1)); + + private final Class clazz; + private final Score score; + + PieceScore(final Class clazz, final Score score) { + this.clazz = clazz; + this.score = score; + } + + public static Score getScore(final Piece piece) { + return Arrays.stream(values()) + .filter(pieceScore -> Objects.equals(pieceScore.clazz, piece.getClass())) + .map(pieceScore -> pieceScore.score) + .findFirst() + .orElseThrow(); + } +} diff --git a/src/main/java/model/Score.java b/src/main/java/model/Score.java new file mode 100644 index 00000000000..5c75f0a1912 --- /dev/null +++ b/src/main/java/model/Score.java @@ -0,0 +1,8 @@ +package model; + +public record Score(float value) { + + public Score add(final Score target) { + return new Score(value + target.value); + } +} diff --git a/src/main/java/model/command/CommandLine.java b/src/main/java/model/command/CommandLine.java index 26ece9ed534..88b41c33815 100644 --- a/src/main/java/model/command/CommandLine.java +++ b/src/main/java/model/command/CommandLine.java @@ -54,6 +54,7 @@ public boolean isMove() { return head == Command.MOVE; } + //TODO: 방어적 복사 public List getBody() { return body; } diff --git a/src/test/java/model/BoardTest.java b/src/test/java/model/BoardTest.java new file mode 100644 index 00000000000..38f73c0dd0d --- /dev/null +++ b/src/test/java/model/BoardTest.java @@ -0,0 +1,25 @@ +package model; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class BoardTest { + + //TODO 다른 테스트도 추가하기 + @DisplayName("초기 상태의 기물 점수를 계산한다.") + @Test + void calculateInitBoard() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + final Score expected = new Score(38.0F); + + //when then + assertAll( + () -> assertThat(chessGame.calculateScore(Camp.WHITE)).isEqualTo(expected), + () -> assertThat(chessGame.calculateScore(Camp.BLACK)).isEqualTo(expected) + ); + } +} diff --git a/src/test/java/model/PieceScoreTest.java b/src/test/java/model/PieceScoreTest.java new file mode 100644 index 00000000000..bf45fdd2607 --- /dev/null +++ b/src/test/java/model/PieceScoreTest.java @@ -0,0 +1,39 @@ +package model; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.stream.Stream; +import model.piece.Bishop; +import model.piece.BlackPawn; +import model.piece.King; +import model.piece.Knight; +import model.piece.Piece; +import model.piece.Queen; +import model.piece.Rook; +import model.piece.WhitePawn; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class PieceScoreTest { + + @DisplayName("해당하는 기물의 점수를 반환한다.") + @ParameterizedTest + @MethodSource("getScoreParameterProvider") + void getScore(final Piece piece, final Score expected) { + assertThat(PieceScore.getScore(piece)).isEqualTo(expected); + } + + static Stream getScoreParameterProvider() { + return Stream.of( + Arguments.of(new King(Camp.BLACK), new Score(0)), + Arguments.of(new Queen(Camp.WHITE), new Score(9)), + Arguments.of(new Rook(Camp.BLACK), new Score(5)), + Arguments.of(new Bishop(Camp.BLACK), new Score(3)), + Arguments.of(new Knight(Camp.WHITE), new Score(2.5F)), + Arguments.of(new WhitePawn(), new Score(1)), + Arguments.of(new BlackPawn(), new Score(1)) + ); + } +} diff --git a/src/test/java/model/ScoreTest.java b/src/test/java/model/ScoreTest.java new file mode 100644 index 00000000000..85f0d38a552 --- /dev/null +++ b/src/test/java/model/ScoreTest.java @@ -0,0 +1,20 @@ +package model; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ScoreTest { + + @Test + @DisplayName("두개의 score를 더한다.") + void add() { + final Score one = new Score(1); + final Score two = new Score(2); + + final Score expected = new Score(3); + + assertThat(one.add(two)).isEqualTo(expected); + } +} From 6959778aa986f01c0065240591b68dac828bb7d6 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Wed, 27 Mar 2024 12:54:50 +0900 Subject: [PATCH 04/60] =?UTF-8?q?feat:=20=EA=B0=99=EC=9D=80=20=EC=84=B8?= =?UTF-8?q?=EB=A1=9C=EC=A4=84=EC=97=90=20=EC=9E=88=EB=8A=94=20=ED=8F=B0?= =?UTF-8?q?=EC=9D=80=200.5=EB=A1=9C=20=EA=B3=84=EC=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 2 +- src/main/java/model/Board.java | 17 +++++-- src/main/java/model/Score.java | 4 ++ src/main/java/model/piece/Piece.java | 4 ++ src/test/java/model/BoardTest.java | 69 ++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 6 deletions(-) diff --git a/docs/README.md b/docs/README.md index c01367bb60c..318cfb9f770 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,7 +23,7 @@ * bishop 3 * knight: 2.5 * pawn: 1 - * [ ] 같은 세로줄에 있는 같은 pawn인 경우 0.5 + * [x] 같은 세로줄에 있는 같은 pawn인 경우 0.5 * [ ] 진영의 점수를 출력 * [ ] 승리한 진영의 결과 확인 * [ ] 체크 메이트면 게임 종료 diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java index 2bb702a016e..c806001c6b8 100644 --- a/src/main/java/model/Board.java +++ b/src/main/java/model/Board.java @@ -4,9 +4,8 @@ import exception.InvalidTurnException; import exception.PieceDoesNotExistException; import exception.PieceExistInRouteException; -import java.util.ArrayList; +import java.util.EnumMap; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -116,16 +115,24 @@ public void move(final Moving moving) { } public Score calculateScore(final Camp camp) { //TODO 람다 스트림으로 빼보기 - //TODO 폰 특수 처리 - final List sameCamp = new ArrayList<>(); + final Map count = new EnumMap<>(File.class); Score result = new Score(0); for (final Entry entry : pieces.entrySet()) { final Piece piece = entry.getValue(); if (piece.isSameCamp(camp)) { - sameCamp.add(piece); + if (piece.isPawn()) { + final File file = entry.getKey().getFile(); + count.put(file, count.getOrDefault(file, 0) + 1); + } result = result.add(PieceScore.getScore(piece)); } } + final Score score = new Score(0.5F); + for (Integer sameFilePawnCount : count.values()) { + for (int i = 1; i < sameFilePawnCount; i++) { + result = result.minus(score); + } + } return result; } diff --git a/src/main/java/model/Score.java b/src/main/java/model/Score.java index 5c75f0a1912..29cedbbc161 100644 --- a/src/main/java/model/Score.java +++ b/src/main/java/model/Score.java @@ -5,4 +5,8 @@ public record Score(float value) { public Score add(final Score target) { return new Score(value + target.value); } + + public Score minus(final Score target) { + return new Score(value - target.value); + } } diff --git a/src/main/java/model/piece/Piece.java b/src/main/java/model/piece/Piece.java index d12622068ff..7d46ec4bde7 100644 --- a/src/main/java/model/piece/Piece.java +++ b/src/main/java/model/piece/Piece.java @@ -30,6 +30,10 @@ public boolean isSameCamp(final Camp target) { return camp == target; } + public boolean isPawn() { + return Objects.equals(getClass(), WhitePawn.class) || Objects.equals(getClass(), BlackPawn.class); + } + @Override public boolean equals(final Object target) { if (this == target) { diff --git a/src/test/java/model/BoardTest.java b/src/test/java/model/BoardTest.java index 38f73c0dd0d..85400366af1 100644 --- a/src/test/java/model/BoardTest.java +++ b/src/test/java/model/BoardTest.java @@ -1,8 +1,18 @@ package model; +import static model.Fixtures.A2; +import static model.Fixtures.A4; +import static model.Fixtures.B4; +import static model.Fixtures.B5; +import static model.Fixtures.B7; +import static model.Fixtures.B8; +import static model.Fixtures.C2; +import static model.Fixtures.C3; +import static model.Fixtures.C6; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import model.position.Moving; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -22,4 +32,63 @@ void calculateInitBoard() { () -> assertThat(chessGame.calculateScore(Camp.BLACK)).isEqualTo(expected) ); } + + @Test + @DisplayName("같은 세로줄에 폰이 있으면 0.5점이다.") + void calculateBoardWhenPawnSameFile() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + final Score blackExpected = new Score(37.0F); + final Score whiteExpected = new Score(37.5F); + + //when + chessGame.move(new Moving(A2, A4)); + chessGame.move(new Moving(B7, B5)); + chessGame.move(new Moving(A4, B5)); + + //when then + assertAll( + () -> assertThat(chessGame.calculateScore(Camp.WHITE)).isEqualTo(whiteExpected), + () -> assertThat(chessGame.calculateScore(Camp.BLACK)).isEqualTo(blackExpected) + ); + } + + @Test + @DisplayName("세로 줄에 3개의 폰이 있는 경우") + void threePawn() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + final Score blackExpected = new Score(34.5F); + final Score whiteExpected = new Score(37.0F); + + //when + chessGame.move(new Moving(A2, A4)); + chessGame.move(new Moving(B7, B5)); + chessGame.move(new Moving(A4, B5)); + + chessGame.move(new Moving(B8, C6)); + chessGame.move(new Moving(C2, C3)); + chessGame.move(new Moving(C6, B4)); + + chessGame.move(new Moving(C3, B4)); + + /* + R.BQKBNR 8 + P.PPPPPP 7 + ........ 6 + .p...... 5 + .p...... 4 + ........ 3 + .p.ppppp 2 + rnbqkbnr 1 + + abcdefgh + */ + + //then + assertAll( + () -> assertThat(chessGame.calculateScore(Camp.WHITE)).isEqualTo(whiteExpected), + () -> assertThat(chessGame.calculateScore(Camp.BLACK)).isEqualTo(blackExpected) + ); + } } From f710580f74e652f75cc678f0c26d98e559842d33 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Wed, 27 Mar 2024 15:26:16 +0900 Subject: [PATCH 05/60] =?UTF-8?q?feat:=20=EC=A7=84=EC=98=81=EC=9D=98=20?= =?UTF-8?q?=EC=A0=90=EC=88=98=EB=A5=BC=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 2 +- src/main/java/controller/ChessController.java | 16 ++++++++-- src/main/java/dto/ScoreDto.java | 30 +++++++++++++++++++ src/main/java/model/command/Command.java | 1 + src/main/java/model/command/CommandLine.java | 4 +++ src/main/java/model/status/Running.java | 3 ++ src/main/java/view/OutputView.java | 8 ++++- 7 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 src/main/java/dto/ScoreDto.java diff --git a/docs/README.md b/docs/README.md index 318cfb9f770..b319f677ab4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,7 +24,7 @@ * knight: 2.5 * pawn: 1 * [x] 같은 세로줄에 있는 같은 pawn인 경우 0.5 - * [ ] 진영의 점수를 출력 + * [x] 진영의 점수를 출력 * [ ] 승리한 진영의 결과 확인 * [ ] 체크 메이트면 게임 종료 * [x] end 가 입력되면 프로그램 종료 diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 300e0cd8641..f7063bdacbd 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -1,6 +1,7 @@ package controller; import dto.ChessBoardDto; +import dto.ScoreDto; import exception.CustomException; import java.util.List; import model.ChessGame; @@ -26,7 +27,7 @@ public void run() { GameStatus gameStatus = initGame(); while (gameStatus.isRunning()) { - printCurrentStatus(chessGame); + printChessBoard(chessGame); gameStatus = play(gameStatus, chessGame); } } @@ -42,14 +43,23 @@ private GameStatus initGame() { private GameStatus play(final GameStatus gameStatus, final ChessGame chessGame) { try { - return gameStatus.play(readCommandLine(), chessGame); + final CommandLine commandLine = readCommandLine(); + printStatusOrNot(chessGame, commandLine); + return gameStatus.play(commandLine, chessGame); } catch (final CustomException exception) { outputView.printException(exception.getErrorCode()); return play(gameStatus, chessGame); } } - private void printCurrentStatus(final ChessGame chessGame) { + //TODO 메서드 이름 이상해ㅐㅐㅐㅐ 형태도 이상해 + private void printStatusOrNot(final ChessGame chessGame, final CommandLine commandLine) { + if (commandLine.isStatus()) { + outputView.printScore(ScoreDto.from(chessGame)); + } + } + + private void printChessBoard(final ChessGame chessGame) { outputView.printChessBoard(ChessBoardDto.from(chessGame)); } diff --git a/src/main/java/dto/ScoreDto.java b/src/main/java/dto/ScoreDto.java new file mode 100644 index 00000000000..24f9466308d --- /dev/null +++ b/src/main/java/dto/ScoreDto.java @@ -0,0 +1,30 @@ +package dto; + +import model.Camp; +import model.ChessGame; +import model.Score; + +public class ScoreDto { + + private final float blackScore; + private final float whiteScore; + + private ScoreDto(final float blackScore, final float whiteScore) { + this.blackScore = blackScore; + this.whiteScore = whiteScore; + } + + public static ScoreDto from(final ChessGame chessGame) { + final Score black = chessGame.calculateScore(Camp.BLACK); + final Score white = chessGame.calculateScore(Camp.WHITE); + return new ScoreDto(black.value(), white.value()); + } + + public float getBlackScore() { + return blackScore; + } + + public float getWhiteScore() { + return whiteScore; + } +} diff --git a/src/main/java/model/command/Command.java b/src/main/java/model/command/Command.java index e98f109c7ab..5b284f4dc6f 100644 --- a/src/main/java/model/command/Command.java +++ b/src/main/java/model/command/Command.java @@ -11,6 +11,7 @@ public enum Command { START(Pattern.compile("start"), 0), MOVE(Pattern.compile("move"), 2), POSITION(Pattern.compile("[a-hA-H][1-8]"), 0), + STATUS(Pattern.compile("status"), 0), END(Pattern.compile("end"), 0); public static final int HEAD_INDEX = 0; diff --git a/src/main/java/model/command/CommandLine.java b/src/main/java/model/command/CommandLine.java index 88b41c33815..79b4bc3a222 100644 --- a/src/main/java/model/command/CommandLine.java +++ b/src/main/java/model/command/CommandLine.java @@ -54,6 +54,10 @@ public boolean isMove() { return head == Command.MOVE; } + public boolean isStatus() { + return head == Command.STATUS; + } + //TODO: 방어적 복사 public List getBody() { return body; diff --git a/src/main/java/model/status/Running.java b/src/main/java/model/status/Running.java index 1366857f3d4..a4a7396b353 100644 --- a/src/main/java/model/status/Running.java +++ b/src/main/java/model/status/Running.java @@ -20,6 +20,9 @@ public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) chessGame.move(moving); return new Running(); } + if (commandLine.isStatus()) { + return new Running(); + } throw new InvalidStatusException(ErrorCode.INVALID_STATUS); } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 3c8a6d26c0a..b7bd277d417 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -2,7 +2,7 @@ import constant.ErrorCode; import dto.ChessBoardDto; -import model.Camp; +import dto.ScoreDto; import view.message.ErrorCodeMessage; public class OutputView { @@ -12,6 +12,7 @@ public void printStartMessage() { System.out.println("> 게임 시작 : start"); System.out.println("> 게임 종료 : end"); System.out.println("> 게임 이동 : move source위치 target위치 - 예. move b2 b3"); + System.out.println("> 게임 현황 : status"); } public void printChessBoard(final ChessBoardDto chessBoardDto) { @@ -19,6 +20,11 @@ public void printChessBoard(final ChessBoardDto chessBoardDto) { System.out.printf("현재 턴: %s%n%n", chessBoardDto.getCurrentTurn()); } + public void printScore(final ScoreDto scoreDto) { + System.out.printf("흑 점수: %s%n", scoreDto.getBlackScore()); + System.out.printf("백 점수: %s%n", scoreDto.getWhiteScore()); + } + public void printException(final ErrorCode errorCode) { System.out.printf("[ERROR] %s%n", ErrorCodeMessage.from(errorCode).getMessage()); } From 7ede4eb39c61c281542a1395779121fe0dcf8881 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Wed, 27 Mar 2024 15:55:36 +0900 Subject: [PATCH 06/60] =?UTF-8?q?refactor:=20controller=EC=97=90=EC=84=9C?= =?UTF-8?q?=20=EC=B6=9C=EB=A0=A5=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index f7063bdacbd..66c27539cd2 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -25,9 +25,9 @@ public void run() { final ChessGame chessGame = ChessGame.setupStartingPosition(); outputView.printStartMessage(); GameStatus gameStatus = initGame(); + outputView.printChessBoard(ChessBoardDto.from(chessGame)); while (gameStatus.isRunning()) { - printChessBoard(chessGame); gameStatus = play(gameStatus, chessGame); } } @@ -44,28 +44,28 @@ private GameStatus initGame() { private GameStatus play(final GameStatus gameStatus, final ChessGame chessGame) { try { final CommandLine commandLine = readCommandLine(); - printStatusOrNot(chessGame, commandLine); - return gameStatus.play(commandLine, chessGame); + final GameStatus tmp = gameStatus.play(commandLine, chessGame); // TODO tmp 말고 딴거 + print(commandLine, chessGame); + return tmp; } catch (final CustomException exception) { outputView.printException(exception.getErrorCode()); return play(gameStatus, chessGame); } } - //TODO 메서드 이름 이상해ㅐㅐㅐㅐ 형태도 이상해 - private void printStatusOrNot(final ChessGame chessGame, final CommandLine commandLine) { + //TODO 뭔가 이녀석도 옮겨주기 + private void print(final CommandLine commandLine, final ChessGame chessGame) { if (commandLine.isStatus()) { outputView.printScore(ScoreDto.from(chessGame)); } - } - - private void printChessBoard(final ChessGame chessGame) { - outputView.printChessBoard(ChessBoardDto.from(chessGame)); + if (commandLine.isStart() || commandLine.isMove()) { + outputView.printChessBoard(ChessBoardDto.from(chessGame)); + } } private CommandLine readCommandLine() { try { - List command = inputView.readCommandList(); + final List command = inputView.readCommandList(); return CommandLine.from(command); } catch (final CustomException exception) { outputView.printException(exception.getErrorCode()); From df92d83535aa788960396a98f7e320e75abc5ddd Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Wed, 27 Mar 2024 16:16:34 +0900 Subject: [PATCH 07/60] =?UTF-8?q?refactor:=20board=EC=99=80=20commandLine?= =?UTF-8?q?=20=EB=B0=A9=EC=96=B4=EC=A0=81=20=EB=B3=B5=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/Board.java | 6 +++--- src/main/java/model/command/CommandLine.java | 4 ++-- src/test/java/model/BoardTest.java | 21 +++++++++++++++++++ .../java/model/command/CommandLineTest.java | 15 +++++++++++++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java index c806001c6b8..fabae617214 100644 --- a/src/main/java/model/Board.java +++ b/src/main/java/model/Board.java @@ -4,6 +4,7 @@ import exception.InvalidTurnException; import exception.PieceDoesNotExistException; import exception.PieceExistInRouteException; +import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; @@ -42,7 +43,7 @@ private Board(final Map pieces) { } public static Board create() { - Map result = new HashMap<>(); + final Map result = new HashMap<>(); settingExceptPawn(result, Camp.BLACK, Rank.EIGHT); settingPawn(result, Camp.BLACK, Rank.SEVEN); settingPawn(result, Camp.WHITE, Rank.TWO); @@ -136,8 +137,7 @@ public Score calculateScore(final Camp camp) { //TODO 람다 스트림으로 빼 return result; } - //TODO 방어적 복사 public Map getPieces() { - return pieces; + return Collections.unmodifiableMap(pieces); } } diff --git a/src/main/java/model/command/CommandLine.java b/src/main/java/model/command/CommandLine.java index 79b4bc3a222..febb021e84d 100644 --- a/src/main/java/model/command/CommandLine.java +++ b/src/main/java/model/command/CommandLine.java @@ -2,6 +2,7 @@ import constant.ErrorCode; import exception.InvalidCommandException; +import java.util.Collections; import java.util.List; public class CommandLine { @@ -58,8 +59,7 @@ public boolean isStatus() { return head == Command.STATUS; } - //TODO: 방어적 복사 public List getBody() { - return body; + return Collections.unmodifiableList(body); } } diff --git a/src/test/java/model/BoardTest.java b/src/test/java/model/BoardTest.java index 85400366af1..6a1a9afeed4 100644 --- a/src/test/java/model/BoardTest.java +++ b/src/test/java/model/BoardTest.java @@ -9,10 +9,16 @@ import static model.Fixtures.C2; import static model.Fixtures.C3; import static model.Fixtures.C6; +import static model.Fixtures.F4; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; +import java.util.Map; +import model.piece.Piece; +import model.piece.WhitePawn; import model.position.Moving; +import model.position.Position; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -91,4 +97,19 @@ void threePawn() { () -> assertThat(chessGame.calculateScore(Camp.BLACK)).isEqualTo(blackExpected) ); } + + @Test + @DisplayName("getter로 가져온 값을 수정하려고 하면 예외가 발생한다..") + void doNotUpdate() { + //given + final Board board = Board.create(); + final Piece piece = new WhitePawn(); + + //when + final Map pieces = board.getPieces(); + + //then + assertThatThrownBy(() -> pieces.put(F4, piece)) + .isInstanceOf(UnsupportedOperationException.class); + } } diff --git a/src/test/java/model/command/CommandLineTest.java b/src/test/java/model/command/CommandLineTest.java index 1255e96e40a..fa81a315fb5 100644 --- a/src/test/java/model/command/CommandLineTest.java +++ b/src/test/java/model/command/CommandLineTest.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -30,4 +31,18 @@ static Stream invalidInputParameterProvider() { Arguments.of(List.of("end", "end")) ); } + + @Test + @DisplayName("getter로 가져온 값을 수정하려고 하면 예외가 발생한다.") + void doNotUpdate() { + //given + final CommandLine commandLine = CommandLine.from(List.of("move", "a1", "a3")); + + //when + final List body = commandLine.getBody(); + + //then + assertThatThrownBy(() -> body.add("b4")) + .isInstanceOf(UnsupportedOperationException.class); + } } From 1c33f96c3e676189d0222e4878beea36da093a00 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Wed, 27 Mar 2024 16:21:41 +0900 Subject: [PATCH 08/60] =?UTF-8?q?refactor:=20=ED=8C=8C=EB=9D=BC=EB=AF=B8?= =?UTF-8?q?=ED=84=B0=EC=97=90=20final=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/status/GameStatus.java | 2 +- src/main/java/model/status/StatusFactory.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/model/status/GameStatus.java b/src/main/java/model/status/GameStatus.java index 8f455796286..bb1c7a41699 100644 --- a/src/main/java/model/status/GameStatus.java +++ b/src/main/java/model/status/GameStatus.java @@ -5,7 +5,7 @@ public interface GameStatus { - GameStatus play(CommandLine commandLine, ChessGame chessGame); + GameStatus play(final CommandLine commandLine, final ChessGame chessGame); boolean isRunning(); } diff --git a/src/main/java/model/status/StatusFactory.java b/src/main/java/model/status/StatusFactory.java index 55b017f46d9..23f3bfca49b 100644 --- a/src/main/java/model/status/StatusFactory.java +++ b/src/main/java/model/status/StatusFactory.java @@ -9,7 +9,7 @@ public class StatusFactory { private StatusFactory() { } - public static GameStatus create(CommandLine commandLine) { + public static GameStatus create(final CommandLine commandLine) { if (commandLine.isStart()) { return new Running(); } From df7b29aa7d904da3818709ec5526e2d43673eed8 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Wed, 27 Mar 2024 20:01:04 +0900 Subject: [PATCH 09/60] =?UTF-8?q?feat:=20king=EC=9D=84=20=EC=9E=A1?= =?UTF-8?q?=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=EC=98=88=EC=99=B8=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/constant/ErrorCode.java | 1 + .../java/exception/KingDeadException.java | 10 ++++ src/main/java/model/Board.java | 8 ++++ src/main/java/model/piece/Piece.java | 4 ++ src/test/java/model/ChessGameTest.java | 48 +++++++++++++++++++ 5 files changed, 71 insertions(+) create mode 100644 src/main/java/exception/KingDeadException.java diff --git a/src/main/java/constant/ErrorCode.java b/src/main/java/constant/ErrorCode.java index 66d885e3e6b..20c19a3485c 100644 --- a/src/main/java/constant/ErrorCode.java +++ b/src/main/java/constant/ErrorCode.java @@ -16,6 +16,7 @@ public enum ErrorCode { OWN_PIECE_EXIST_POSITION, PIECE_DOES_NOT_EXIST_POSITION, INVALID_CAMP_PIECE, + KING_DEAD, NO_MESSAGE } diff --git a/src/main/java/exception/KingDeadException.java b/src/main/java/exception/KingDeadException.java new file mode 100644 index 00000000000..c37549aaff0 --- /dev/null +++ b/src/main/java/exception/KingDeadException.java @@ -0,0 +1,10 @@ +package exception; + +import constant.ErrorCode; + +public class KingDeadException extends CustomException { + + public KingDeadException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java index fabae617214..34e5708ae95 100644 --- a/src/main/java/model/Board.java +++ b/src/main/java/model/Board.java @@ -2,6 +2,7 @@ import constant.ErrorCode; import exception.InvalidTurnException; +import exception.KingDeadException; import exception.PieceDoesNotExistException; import exception.PieceExistInRouteException; import java.util.Collections; @@ -74,6 +75,13 @@ public void validate(final Moving moving, final Camp currentCamp) { validateUnBlocked(route); final Position nextPosition = moving.getNextPosition(); validateTargetEnemy(currentCamp, nextPosition); + validateIsKing(nextPosition); + } + + private void validateIsKing(final Position nextPosition) { + if (pieces.containsKey(nextPosition) && pieces.get(nextPosition).isKing()) { + throw new KingDeadException(ErrorCode.KING_DEAD); + } } private void validateExistPiece(final Position currentPosition) { diff --git a/src/main/java/model/piece/Piece.java b/src/main/java/model/piece/Piece.java index 7d46ec4bde7..4f905c2a13f 100644 --- a/src/main/java/model/piece/Piece.java +++ b/src/main/java/model/piece/Piece.java @@ -34,6 +34,10 @@ public boolean isPawn() { return Objects.equals(getClass(), WhitePawn.class) || Objects.equals(getClass(), BlackPawn.class); } + public boolean isKing() { + return Objects.equals(getClass(), King.class); + } + @Override public boolean equals(final Object target) { if (this == target) { diff --git a/src/test/java/model/ChessGameTest.java b/src/test/java/model/ChessGameTest.java index 360157c9c4c..cec8ef2b140 100644 --- a/src/test/java/model/ChessGameTest.java +++ b/src/test/java/model/ChessGameTest.java @@ -23,25 +23,34 @@ import static model.Fixtures.D8; import static model.Fixtures.E1; import static model.Fixtures.E2; +import static model.Fixtures.E3; import static model.Fixtures.E5; import static model.Fixtures.E7; import static model.Fixtures.E8; import static model.Fixtures.F1; import static model.Fixtures.F2; +import static model.Fixtures.F3; +import static model.Fixtures.F6; import static model.Fixtures.F7; import static model.Fixtures.F8; import static model.Fixtures.G1; import static model.Fixtures.G2; +import static model.Fixtures.G4; import static model.Fixtures.G7; import static model.Fixtures.G8; import static model.Fixtures.H1; import static model.Fixtures.H2; +import static model.Fixtures.H3; +import static model.Fixtures.H4; +import static model.Fixtures.H5; +import static model.Fixtures.H6; import static model.Fixtures.H7; import static model.Fixtures.H8; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import exception.InvalidTurnException; +import exception.KingDeadException; import exception.PieceDoesNotExistException; import exception.PieceExistInRouteException; import java.util.HashMap; @@ -215,4 +224,43 @@ void checkSecondAttack() { assertThat(chessGame.getCamp()).isEqualTo(Camp.BLACK); } + + @DisplayName("킹을 잡으면 예외가 발생한다. 블랙 이기는 경우") + @Test + void failToCatchBlackKing() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + //when + chessGame.move(new Moving(F2, F3)); + chessGame.move(new Moving(E7, E5)); + chessGame.move(new Moving(G2, G4)); + chessGame.move(new Moving(D8, H4)); + chessGame.move(new Moving(H2, H3)); + + final Moving catchKingMove = new Moving(H4, E1); + + //then + assertThatThrownBy(() -> chessGame.move(catchKingMove)) + .isInstanceOf(KingDeadException.class); + } + + @DisplayName("킹을 잡으면 예외가 발생한다. 화이트 이기는 경우") + @Test + void failToCatchWhiteKing() { + //given + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + //when + chessGame.move(new Moving(E2, E3)); + chessGame.move(new Moving(F7, F6)); + chessGame.move(new Moving(D1, H5)); + chessGame.move(new Moving(G8, H6)); + + final Moving catchKingMove = new Moving(H5, E8); + + //then + assertThatThrownBy(() -> chessGame.move(catchKingMove)) + .isInstanceOf(KingDeadException.class); + } } From 18337a857ecbd66eac01708c6b51aaf03f618f2c Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Thu, 28 Mar 2024 14:57:29 +0900 Subject: [PATCH 10/60] =?UTF-8?q?feat:=20=EC=8A=B9=EB=A6=AC=ED=95=9C=20?= =?UTF-8?q?=EC=A7=84=EC=98=81=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 8 +++++-- src/main/java/model/status/Checkmate.java | 24 +++++++++++++++++++ src/main/java/model/status/End.java | 5 ++++ src/main/java/model/status/GameStatus.java | 2 ++ src/main/java/model/status/Running.java | 20 +++++++++++++--- src/main/java/view/OutputView.java | 8 +++++-- 6 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 src/main/java/model/status/Checkmate.java diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 66c27539cd2..b6a8b5e9520 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -45,7 +45,7 @@ private GameStatus play(final GameStatus gameStatus, final ChessGame chessGame) try { final CommandLine commandLine = readCommandLine(); final GameStatus tmp = gameStatus.play(commandLine, chessGame); // TODO tmp 말고 딴거 - print(commandLine, chessGame); + print(tmp, commandLine, chessGame); return tmp; } catch (final CustomException exception) { outputView.printException(exception.getErrorCode()); @@ -54,7 +54,11 @@ private GameStatus play(final GameStatus gameStatus, final ChessGame chessGame) } //TODO 뭔가 이녀석도 옮겨주기 - private void print(final CommandLine commandLine, final ChessGame chessGame) { + private void print(final GameStatus gameStatus, final CommandLine commandLine, final ChessGame chessGame) { + if (gameStatus.isCheck()) { + outputView.printWinner(chessGame.getCamp().toString()); + return; + } if (commandLine.isStatus()) { outputView.printScore(ScoreDto.from(chessGame)); } diff --git a/src/main/java/model/status/Checkmate.java b/src/main/java/model/status/Checkmate.java new file mode 100644 index 00000000000..e9e2e69a9f0 --- /dev/null +++ b/src/main/java/model/status/Checkmate.java @@ -0,0 +1,24 @@ +package model.status; + +import constant.ErrorCode; +import exception.InvalidStatusException; +import model.ChessGame; +import model.command.CommandLine; + +public class Checkmate implements GameStatus { + + @Override + public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) { + throw new InvalidStatusException(ErrorCode.INVALID_STATUS); + } + + @Override + public boolean isRunning() { + return false; + } + + @Override + public boolean isCheck() { + return true; + } +} diff --git a/src/main/java/model/status/End.java b/src/main/java/model/status/End.java index e2983e11ee1..db30851e915 100644 --- a/src/main/java/model/status/End.java +++ b/src/main/java/model/status/End.java @@ -16,4 +16,9 @@ public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) public boolean isRunning() { return false; } + + @Override + public boolean isCheck() { + return false; + } } diff --git a/src/main/java/model/status/GameStatus.java b/src/main/java/model/status/GameStatus.java index bb1c7a41699..a387268504a 100644 --- a/src/main/java/model/status/GameStatus.java +++ b/src/main/java/model/status/GameStatus.java @@ -8,4 +8,6 @@ public interface GameStatus { GameStatus play(final CommandLine commandLine, final ChessGame chessGame); boolean isRunning(); + + boolean isCheck(); } diff --git a/src/main/java/model/status/Running.java b/src/main/java/model/status/Running.java index a4a7396b353..12aced3eb1a 100644 --- a/src/main/java/model/status/Running.java +++ b/src/main/java/model/status/Running.java @@ -2,6 +2,7 @@ import constant.ErrorCode; import exception.InvalidStatusException; +import exception.KingDeadException; import java.util.List; import model.ChessGame; import model.command.CommandLine; @@ -16,9 +17,7 @@ public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) return new End(); } if (commandLine.isMove()) { - final Moving moving = convert(commandLine.getBody()); - chessGame.move(moving); - return new Running(); + return status(commandLine, chessGame); } if (commandLine.isStatus()) { return new Running(); @@ -26,6 +25,16 @@ public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) throw new InvalidStatusException(ErrorCode.INVALID_STATUS); } + private GameStatus status(final CommandLine commandLine, final ChessGame chessGame) { + final Moving moving = convert(commandLine.getBody()); + try { + chessGame.move(moving); + return new Running(); + } catch (KingDeadException exception) { + return new Checkmate(); + } + } + private Moving convert(final List command) { final String currentPosition = command.get(CommandLine.CURRENT_POSITION_INDEX); final String nextPosition = command.get(CommandLine.NEXT_POSITION_INDEX); @@ -37,4 +46,9 @@ private Moving convert(final List command) { public boolean isRunning() { return true; } + + @Override + public boolean isCheck() { + return false; + } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index b7bd277d417..4c4ad1d6924 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -21,11 +21,15 @@ public void printChessBoard(final ChessBoardDto chessBoardDto) { } public void printScore(final ScoreDto scoreDto) { - System.out.printf("흑 점수: %s%n", scoreDto.getBlackScore()); - System.out.printf("백 점수: %s%n", scoreDto.getWhiteScore()); + System.out.printf("BLACK 점수: %s%n", scoreDto.getBlackScore()); + System.out.printf("WHITE 점수: %s%n", scoreDto.getWhiteScore()); } public void printException(final ErrorCode errorCode) { System.out.printf("[ERROR] %s%n", ErrorCodeMessage.from(errorCode).getMessage()); } + + public void printWinner(final String camp) { + System.out.printf("%s 승리%n", camp); + } } From 0735435a62332971999c59e86e35fccf8f695a69 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Thu, 28 Mar 2024 15:03:29 +0900 Subject: [PATCH 11/60] =?UTF-8?q?docs:=20=EC=99=84=EB=A3=8C=ED=95=9C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EA=B8=B0=EB=8A=A5=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index b319f677ab4..d95127ebd45 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,7 +16,7 @@ * [x] 기물의 이동 규칙을 벗어나는 경우 예외 발생 * [x] 현재 위치로 빈 칸을 입력받은 경우 예외 발생 * [x] 현재 턴이 아닌 기물을 이동하는 경우 예외 발생 -* [ ] status 기능 구현 +* [x] status 기능 구현 * [x] 남아있는 기물의 점수 계산 * queen: 9 * rook: 5 @@ -25,8 +25,9 @@ * pawn: 1 * [x] 같은 세로줄에 있는 같은 pawn인 경우 0.5 * [x] 진영의 점수를 출력 - * [ ] 승리한 진영의 결과 확인 + * [x] 승리한 진영의 결과 확인 * [ ] 체크 메이트면 게임 종료 + * 킹을 잡는 move 명령이 있어야 게임 종료 * [x] end 가 입력되면 프로그램 종료 * [x] 예외 시 재입력 From 14300617f3403431d25ce0816ead5f63f6a3ffca Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Thu, 28 Mar 2024 17:00:16 +0900 Subject: [PATCH 12/60] =?UTF-8?q?chore:=20db=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +++ build.gradle | 1 + docker/docker-compose.yml | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 docker/docker-compose.yml diff --git a/.gitignore b/.gitignore index 6c018781387..96711b991d8 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ out/ ### VS Code ### .vscode/ + +### dockder ### +docker/ diff --git a/build.gradle b/build.gradle index 3697236c6fb..8c605072e94 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,7 @@ dependencies { testImplementation platform('org.assertj:assertj-bom:3.25.1') testImplementation('org.junit.jupiter:junit-jupiter') testImplementation('org.assertj:assertj-core') + runtimeOnly("com.mysql:mysql-connector-j:8.3.0") } java { diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 00000000000..558a1d5a53f --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,18 @@ +version: "3.9" +services: + db: + image: mysql:8.0.28 + platform: linux/x86_64 + restart: always + ports: + - "13306:3306" + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: chess + MYSQL_USER: user + MYSQL_PASSWORD: password + TZ: Asia/Seoul + volumes: + - ./db/mysql/data:/var/lib/mysql + - ./db/mysql/config:/etc/mysql/conf.d + - ./db/mysql/init:/docker-entrypoint-initdb.d From 525bdbbc1442713c4e983c2a354132a2c3008041 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Thu, 28 Mar 2024 20:08:01 +0900 Subject: [PATCH 13/60] =?UTF-8?q?test:=20=EC=9D=B4=EB=8F=99=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=ED=99=95=EC=9D=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/MovingDao.java | 77 +++++++++++++++++++++++++++++ src/main/java/db/dto/MovingDto.java | 15 ++++++ src/test/java/db/MovingDaoTest.java | 30 +++++++++++ 3 files changed, 122 insertions(+) create mode 100644 src/main/java/db/MovingDao.java create mode 100644 src/main/java/db/dto/MovingDto.java create mode 100644 src/test/java/db/MovingDaoTest.java diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java new file mode 100644 index 00000000000..0598b5378da --- /dev/null +++ b/src/main/java/db/MovingDao.java @@ -0,0 +1,77 @@ +package db; + +import db.dto.MovingDto; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +public class MovingDao { + + private static final String SERVER = "localhost:13306"; // MySQL 서버 주소 + private static final String OPTION = "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; + private static final String USERNAME = "root"; // MySQL 서버 아이디 + private static final String PASSWORD = "root"; // MySQL 서버 비밀번호 + + private final String database; // MySQL DATABASE 이름 + + public MovingDao(final String database) { + this.database = database; + } + + public Connection getConnection() { + // 드라이버 연결 + try { + return DriverManager.getConnection("jdbc:mysql://" + SERVER + "/" + database + OPTION, USERNAME, PASSWORD); + } catch (final SQLException e) { + System.err.println("DB 연결 오류:" + e.getMessage()); + e.printStackTrace(); + return null; + } + } + + public long addMoving(final MovingDto moving) { + final var query = "INSERT INTO moving VALUES(?, ?, ?, ?)"; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + ) { + long autoIncrement = 0; + preparedStatement.setNull(1, 0); + preparedStatement.setString(2, moving.camp()); + preparedStatement.setString(3, moving.current()); + preparedStatement.setString(4, moving.next()); + + preparedStatement.executeUpdate(); + final var generatedKeys = preparedStatement.getGeneratedKeys(); + + if (generatedKeys.next()) { + autoIncrement = generatedKeys.getLong(1); + } + return autoIncrement; + } catch (final SQLException e) { + throw new RuntimeException(e); + } + } + + public MovingDto findByMovementId(final long movementId) { + final var query = "SELECT * FROM moving WHERE movement_id = ?"; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + preparedStatement.setLong(1, movementId); + + final var resultSet = preparedStatement.executeQuery(); + + if (resultSet.next()) { + return new MovingDto( + resultSet.getString("camp"), + resultSet.getString("current"), + resultSet.getString("next") + ); + } + } catch (final SQLException e) { + throw new RuntimeException(e); + } + + return null; + } +} diff --git a/src/main/java/db/dto/MovingDto.java b/src/main/java/db/dto/MovingDto.java new file mode 100644 index 00000000000..5daa67862d8 --- /dev/null +++ b/src/main/java/db/dto/MovingDto.java @@ -0,0 +1,15 @@ +package db.dto; + +import model.Camp; +import model.position.Moving; +import model.position.Position; + +public record MovingDto(String camp, String current, String next) { + + public static MovingDto from(final Moving moving, final Camp camp) { + final Position currentPosition = moving.getCurrentPosition(); + final Position nextPosition = moving.getNextPosition(); + + return new MovingDto(camp.toString(), currentPosition.toString(), nextPosition.toString()); + } +} diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java new file mode 100644 index 00000000000..22c71a39ee8 --- /dev/null +++ b/src/test/java/db/MovingDaoTest.java @@ -0,0 +1,30 @@ +package db; + +import static org.assertj.core.api.Assertions.assertThat; + +import db.dto.MovingDto; +import java.sql.SQLException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class MovingDaoTest { + + private final MovingDao movingDao = new MovingDao("chess_test"); + + @DisplayName("데이터베이스 접속 확인") + @Test + void connection() throws SQLException { + try (final var connection = movingDao.getConnection()) { + assertThat(connection).isNotNull(); + } + } + + @Test + @DisplayName("이동 저장 확인") + void addMoving() { + final var moving = new MovingDto("WHITE", "a2", "a3"); + final var id = movingDao.addMoving(moving); + + assertThat(movingDao.findByMovementId(id)).isEqualTo(moving); + } +} From 3f5078e893f963a15a430d71dfa54ae2a6da09f5 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Fri, 29 Mar 2024 13:27:24 +0900 Subject: [PATCH 14/60] =?UTF-8?q?refactor:=20moving=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/MovingDao.java | 14 +++++++++----- src/main/java/db/dto/MovingDto.java | 8 ++++++-- src/main/java/model/position/Position.java | 5 +++++ src/test/java/db/MovingDaoTest.java | 2 +- src/test/resources/moving.sql | 11 +++++++++++ 5 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 src/test/resources/moving.sql diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 0598b5378da..9ff02ae2b62 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -31,15 +31,17 @@ public Connection getConnection() { } public long addMoving(final MovingDto moving) { - final var query = "INSERT INTO moving VALUES(?, ?, ?, ?)"; + final var query = "INSERT INTO moving VALUES(?, ?, ?, ?, ?, ?)"; try (final var connection = getConnection(); final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); ) { long autoIncrement = 0; preparedStatement.setNull(1, 0); preparedStatement.setString(2, moving.camp()); - preparedStatement.setString(3, moving.current()); - preparedStatement.setString(4, moving.next()); + preparedStatement.setString(3, moving.currentFile()); + preparedStatement.setString(4, moving.currentRank()); + preparedStatement.setString(5, moving.nextFile()); + preparedStatement.setString(6, moving.nextRank()); preparedStatement.executeUpdate(); final var generatedKeys = preparedStatement.getGeneratedKeys(); @@ -64,8 +66,10 @@ public MovingDto findByMovementId(final long movementId) { if (resultSet.next()) { return new MovingDto( resultSet.getString("camp"), - resultSet.getString("current"), - resultSet.getString("next") + resultSet.getString("start_rank"), + resultSet.getString("start_file"), + resultSet.getString("destination_rank"), + resultSet.getString("destination_file") ); } } catch (final SQLException e) { diff --git a/src/main/java/db/dto/MovingDto.java b/src/main/java/db/dto/MovingDto.java index 5daa67862d8..d339db4d127 100644 --- a/src/main/java/db/dto/MovingDto.java +++ b/src/main/java/db/dto/MovingDto.java @@ -4,12 +4,16 @@ import model.position.Moving; import model.position.Position; -public record MovingDto(String camp, String current, String next) { +public record MovingDto(String camp, String currentFile, String currentRank, String nextFile, String nextRank) { public static MovingDto from(final Moving moving, final Camp camp) { final Position currentPosition = moving.getCurrentPosition(); final Position nextPosition = moving.getNextPosition(); - return new MovingDto(camp.toString(), currentPosition.toString(), nextPosition.toString()); + final String current = currentPosition.toString(); + final String next = nextPosition.toString(); + + return new MovingDto(camp.toString(), String.valueOf(current.charAt(0)), String.valueOf(current.charAt(1)), + String.valueOf(next.charAt(0)), String.valueOf(next.charAt(1))); } } diff --git a/src/main/java/model/position/Position.java b/src/main/java/model/position/Position.java index 5b753986aaf..fc34b8778b5 100644 --- a/src/main/java/model/position/Position.java +++ b/src/main/java/model/position/Position.java @@ -40,6 +40,10 @@ public File getFile() { return file; } + public Rank getRank() { + return rank; + } + @Override public int hashCode() { return Objects.hash(rank, file); @@ -60,4 +64,5 @@ public boolean equals(final Object target) { public String toString() { return file.getValue() + rank.getValue(); } + } diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java index 22c71a39ee8..8be22964d19 100644 --- a/src/test/java/db/MovingDaoTest.java +++ b/src/test/java/db/MovingDaoTest.java @@ -22,7 +22,7 @@ void connection() throws SQLException { @Test @DisplayName("이동 저장 확인") void addMoving() { - final var moving = new MovingDto("WHITE", "a2", "a3"); + final var moving = new MovingDto("WHITE", "a", "2", "a", "3"); final var id = movingDao.addMoving(moving); assertThat(movingDao.findByMovementId(id)).isEqualTo(moving); diff --git a/src/test/resources/moving.sql b/src/test/resources/moving.sql new file mode 100644 index 00000000000..954ae4f3059 --- /dev/null +++ b/src/test/resources/moving.sql @@ -0,0 +1,11 @@ +use chess_test; + +create table moving +( + movement_id INT primary key auto_increment, + camp varchar(5) not null, + start_rank varchar(12) not null, + start_file varchar(12) not null, + destination_rank varchar(12) not null, + destination_file varchar(12) not null +); From ae567607e2b3aa504eb47d31f4534467661e7f2d Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Fri, 29 Mar 2024 15:23:52 +0900 Subject: [PATCH 15/60] =?UTF-8?q?feat:=20board=20=EC=A0=80=EC=9E=A5=20(?= =?UTF-8?q?=EC=9C=84=EC=B9=98,=20=EA=B8=B0=EB=AC=BC,=20=EC=A7=84=EC=98=81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/MovingDao.java | 56 +++++++++++++++++++++++++++ src/main/java/db/dto/BoardDto.java | 29 ++++++++++++++ src/main/java/db/dto/PieceDto.java | 10 +++++ src/main/java/db/dto/PieceType.java | 38 ++++++++++++++++++ src/main/java/db/dto/PositionDto.java | 10 +++++ src/test/java/db/MovingDaoTest.java | 16 ++++++++ src/test/resources/board.sql | 11 ++++++ 7 files changed, 170 insertions(+) create mode 100644 src/main/java/db/dto/BoardDto.java create mode 100644 src/main/java/db/dto/PieceDto.java create mode 100644 src/main/java/db/dto/PieceType.java create mode 100644 src/main/java/db/dto/PositionDto.java create mode 100644 src/test/resources/board.sql diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 9ff02ae2b62..86025e9ea63 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -1,10 +1,16 @@ package db; +import db.dto.BoardDto; import db.dto.MovingDto; +import db.dto.PieceDto; +import db.dto.PositionDto; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; public class MovingDao { @@ -30,6 +36,31 @@ public Connection getConnection() { } } + public void addBoard(final BoardDto board) { + final Map pieces = board.pieces(); + + for (final Entry entry : pieces.entrySet()) { + addPosition(entry.getKey(), entry.getValue()); + } + } + + private void addPosition(final PositionDto position, final PieceDto piece) { + final var query = "INSERT INTO board VALUES(?, ?, ?)"; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + ) { + preparedStatement.setString(1, position.value()); + preparedStatement.setString(2, piece.type()); + preparedStatement.setString(3, piece.camp()); + + preparedStatement.executeUpdate(); + + } catch (final SQLException e) { + throw new RuntimeException(e); + } + + } + public long addMoving(final MovingDto moving) { final var query = "INSERT INTO moving VALUES(?, ?, ?, ?, ?, ?)"; try (final var connection = getConnection(); @@ -78,4 +109,29 @@ public MovingDto findByMovementId(final long movementId) { return null; } + + public BoardDto findBoard() { + final var query = "SELECT * FROM board"; + + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + + final var resultSet = preparedStatement.executeQuery(); + + final Map result = new HashMap<>(); + + while (resultSet.next()) { + final var position = new PositionDto(resultSet.getString("position")); + final var type = resultSet.getString("piece_type"); + final var camp = resultSet.getString("camp"); + final var piece = new PieceDto(type, camp); + + result.put(position, piece); + + } + return new BoardDto(result); + } catch (final SQLException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/db/dto/BoardDto.java b/src/main/java/db/dto/BoardDto.java new file mode 100644 index 00000000000..10f6a0809e6 --- /dev/null +++ b/src/main/java/db/dto/BoardDto.java @@ -0,0 +1,29 @@ +package db.dto; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import model.Board; +import model.piece.Piece; +import model.position.Position; + +public record BoardDto(Map pieces) { + + public BoardDto(final Map pieces) { + this.pieces = pieces; + } + + public static BoardDto from(final Board board) { + final Map result = new HashMap<>(); + + final Map pieces = board.getPieces(); + + for (Entry entry : pieces.entrySet()) { + final PositionDto position = PositionDto.from(entry.getKey()); + final PieceDto piece = PieceDto.from(entry.getValue()); + result.put(position, piece); + } + + return new BoardDto(result); + } +} diff --git a/src/main/java/db/dto/PieceDto.java b/src/main/java/db/dto/PieceDto.java new file mode 100644 index 00000000000..55713ff5b2e --- /dev/null +++ b/src/main/java/db/dto/PieceDto.java @@ -0,0 +1,10 @@ +package db.dto; + +import model.piece.Piece; + +public record PieceDto(String type, String camp) { + + public static PieceDto from(final Piece piece) { + return new PieceDto(PieceType.findValue(piece), piece.getCamp().toString()); + } +} diff --git a/src/main/java/db/dto/PieceType.java b/src/main/java/db/dto/PieceType.java new file mode 100644 index 00000000000..0f2a4bb2b4a --- /dev/null +++ b/src/main/java/db/dto/PieceType.java @@ -0,0 +1,38 @@ +package db.dto; + +import java.util.Arrays; +import java.util.Objects; +import model.piece.Bishop; +import model.piece.BlackPawn; +import model.piece.King; +import model.piece.Knight; +import model.piece.Piece; +import model.piece.Queen; +import model.piece.Rook; +import model.piece.WhitePawn; + +public enum PieceType { + KING(King.class, "King"), + QUEEN(Queen.class, "Queen"), + ROOK(Rook.class, "Rook"), + BISHOP(Bishop.class, "Bishop"), + KNIGHT(Knight.class, "Knight"), + WHITE_PAWN(WhitePawn.class, "Pawn"), + BLACK_PAWN(BlackPawn.class, "Pawn"); + + private final Class clazz; + private final String value; + + PieceType(final Class clazz, final String value) { + this.clazz = clazz; + this.value = value; + } + + public static String findValue(final Piece piece) { + return Arrays.stream(values()) + .filter(pieceType -> Objects.equals(pieceType.clazz, piece.getClass())) + .findFirst() + .orElseThrow() + .value; + } +} diff --git a/src/main/java/db/dto/PositionDto.java b/src/main/java/db/dto/PositionDto.java new file mode 100644 index 00000000000..e795b20ff24 --- /dev/null +++ b/src/main/java/db/dto/PositionDto.java @@ -0,0 +1,10 @@ +package db.dto; + +import model.position.Position; + +public record PositionDto(String value) { + + public static PositionDto from(final Position position) { + return new PositionDto(position.toString()); + } +} diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java index 8be22964d19..39b3e6c34c4 100644 --- a/src/test/java/db/MovingDaoTest.java +++ b/src/test/java/db/MovingDaoTest.java @@ -2,8 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; +import db.dto.BoardDto; import db.dto.MovingDto; import java.sql.SQLException; +import model.Board; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -27,4 +29,18 @@ void addMoving() { assertThat(movingDao.findByMovementId(id)).isEqualTo(moving); } + + @Test + @DisplayName("보드 저장 확인") + void addBoard() { + //given + final var board = BoardDto.from(Board.create()); + + //when + movingDao.addBoard(board); + final BoardDto findBoard = movingDao.findBoard(); + + //then + assertThat(board).isEqualTo(findBoard); + } } diff --git a/src/test/resources/board.sql b/src/test/resources/board.sql new file mode 100644 index 00000000000..fc90bc133e2 --- /dev/null +++ b/src/test/resources/board.sql @@ -0,0 +1,11 @@ +use chess_test; + +# drop table board; + +create table board +( + position varchar(2) not null, + piece_type varchar(6) not null, + camp varchar(5) not null + +); From f0dbfbca89487851a4455fd1e542c2b91ae62a56 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Fri, 29 Mar 2024 15:41:22 +0900 Subject: [PATCH 16/60] =?UTF-8?q?refactor:=20=EB=A7=A4=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EB=A7=88=EB=8B=A4=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EC=83=9D=EC=84=B1=20=ED=9B=84=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/MovingDao.java | 52 +++++++++++++++++++++++++++++ src/test/java/db/MovingDaoTest.java | 14 ++++++++ 2 files changed, 66 insertions(+) diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 86025e9ea63..a673d047eda 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -134,4 +134,56 @@ public BoardDto findBoard() { throw new RuntimeException(e); } } + + public void createMoving() { + final var query = """ + create table moving + ( + movement_id INT primary key auto_increment, + camp varchar(5) not null, + start_rank varchar(12) not null, + start_file varchar(12) not null, + destination_rank varchar(12) not null, + destination_file varchar(12) not null + )"""; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + ) { + preparedStatement.executeUpdate(); + + } catch (final SQLException e) { + throw new RuntimeException(e); + } + } + + public void createBoard() { + final var query = """ + create table board + ( + position varchar(2) not null, + piece_type varchar(6) not null, + camp varchar(5) not null + + )"""; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + ) { + preparedStatement.executeUpdate(); + + } catch (final SQLException e) { + throw new RuntimeException(e); + } + } + + public void remove(String table) { + final var query = String.format("drop table %s", table); + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + ) { + preparedStatement.executeUpdate(); + + } catch (final SQLException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java index 39b3e6c34c4..85ae278f574 100644 --- a/src/test/java/db/MovingDaoTest.java +++ b/src/test/java/db/MovingDaoTest.java @@ -6,6 +6,8 @@ import db.dto.MovingDto; import java.sql.SQLException; import model.Board; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -13,6 +15,18 @@ class MovingDaoTest { private final MovingDao movingDao = new MovingDao("chess_test"); + @BeforeEach + void beforeEach() { + movingDao.createMoving(); + movingDao.createBoard(); + } + + @AfterEach + void afterEach() { + movingDao.remove("moving"); + movingDao.remove("board"); + } + @DisplayName("데이터베이스 접속 확인") @Test void connection() throws SQLException { From ccdf574f5e354bcf5321bc8db0357ecc48c1699f Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Fri, 29 Mar 2024 21:13:47 +0900 Subject: [PATCH 17/60] =?UTF-8?q?fix:=20=EA=B0=99=EC=9D=80=20=EC=A4=84=20?= =?UTF-8?q?=ED=8F=B0=20=EC=9E=88=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=EC=A0=90?= =?UTF-8?q?=EC=88=98=20=EA=B3=84=EC=82=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/Board.java | 14 +++++++++----- src/test/java/model/BoardTest.java | 6 ++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java index 34e5708ae95..ae204946bea 100644 --- a/src/main/java/model/Board.java +++ b/src/main/java/model/Board.java @@ -39,7 +39,7 @@ public class Board { private final Map pieces; - private Board(final Map pieces) { + public Board(final Map pieces) { this.pieces = pieces; } @@ -136,10 +136,14 @@ public Score calculateScore(final Camp camp) { //TODO 람다 스트림으로 빼 result = result.add(PieceScore.getScore(piece)); } } - final Score score = new Score(0.5F); - for (Integer sameFilePawnCount : count.values()) { - for (int i = 1; i < sameFilePawnCount; i++) { - result = result.minus(score); + final Score score = new Score(1); + for (int sameFilePawnCount : count.values()) { + if (sameFilePawnCount == 1) { + result.add(score); + } + if (sameFilePawnCount > 1) { + float value = sameFilePawnCount * 0.5f; + result = result.minus(new Score(value)); } } return result; diff --git a/src/test/java/model/BoardTest.java b/src/test/java/model/BoardTest.java index 6a1a9afeed4..298274dcbb1 100644 --- a/src/test/java/model/BoardTest.java +++ b/src/test/java/model/BoardTest.java @@ -45,7 +45,7 @@ void calculateBoardWhenPawnSameFile() { //given final ChessGame chessGame = ChessGame.setupStartingPosition(); final Score blackExpected = new Score(37.0F); - final Score whiteExpected = new Score(37.5F); + final Score whiteExpected = new Score(37.0F); //when chessGame.move(new Moving(A2, A4)); @@ -65,17 +65,15 @@ void threePawn() { //given final ChessGame chessGame = ChessGame.setupStartingPosition(); final Score blackExpected = new Score(34.5F); - final Score whiteExpected = new Score(37.0F); + final Score whiteExpected = new Score(36.5F); //when chessGame.move(new Moving(A2, A4)); chessGame.move(new Moving(B7, B5)); chessGame.move(new Moving(A4, B5)); - chessGame.move(new Moving(B8, C6)); chessGame.move(new Moving(C2, C3)); chessGame.move(new Moving(C6, B4)); - chessGame.move(new Moving(C3, B4)); /* From 1e7a3db25537a346aee547a788258595ff85fb44 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 30 Mar 2024 00:33:53 +0900 Subject: [PATCH 18/60] =?UTF-8?q?feat:=20=EC=A7=84=ED=96=89=EC=A4=91?= =?UTF-8?q?=EC=9D=B8=20=EA=B2=8C=EC=9E=84=20end=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=ED=9B=84=20=EC=9E=AC=EC=8B=9C=EC=9E=91=EC=8B=9C=20=EC=9D=B4?= =?UTF-8?q?=EC=A0=84=20=EA=B2=8C=EC=9E=84=20=EC=8B=A4=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 3 + src/main/java/controller/ChessController.java | 60 +++++++++++-- src/main/java/db/MovingDao.java | 85 +++++++++++++++---- src/main/java/db/dto/BoardDto.java | 12 ++- src/main/java/db/dto/PieceDto.java | 39 +++++++++ src/main/java/db/dto/PositionDto.java | 6 ++ src/main/java/db/dto/TurnDto.java | 13 +++ src/main/java/model/ChessGame.java | 6 +- src/main/resources/board.sql | 11 +++ src/main/resources/moving.sql | 13 +++ src/main/resources/turn.sql | 8 ++ src/test/java/db/MovingDaoTest.java | 8 ++ src/test/resources/turn.sql | 6 ++ 13 files changed, 239 insertions(+), 31 deletions(-) create mode 100644 src/main/java/db/dto/TurnDto.java create mode 100644 src/main/resources/board.sql create mode 100644 src/main/resources/moving.sql create mode 100644 src/main/resources/turn.sql create mode 100644 src/test/resources/turn.sql diff --git a/docs/README.md b/docs/README.md index d95127ebd45..28d87e590a7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -30,6 +30,9 @@ * 킹을 잡는 move 명령이 있어야 게임 종료 * [x] end 가 입력되면 프로그램 종료 * [x] 예외 시 재입력 +* [x] 진행중인 게임 end 입력 후 재시작시 이전 게임 실행 + + ## 기물의 이동 기능 diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index b6a8b5e9520..09b0d798b8c 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -1,9 +1,17 @@ package controller; +import db.MovingDao; +import db.dto.BoardDto; +import db.dto.MovingDto; +import db.dto.PieceDto; +import db.dto.PositionDto; +import db.dto.TurnDto; import dto.ChessBoardDto; import dto.ScoreDto; import exception.CustomException; import java.util.List; +import java.util.Map; +import model.Board; import model.ChessGame; import model.command.CommandLine; import model.status.GameStatus; @@ -15,6 +23,7 @@ public class ChessController { private final InputView inputView; private final OutputView outputView; + private final MovingDao movingDao = new MovingDao("chess"); public ChessController(final InputView inputView, final OutputView outputView) { this.inputView = inputView; @@ -22,7 +31,8 @@ public ChessController(final InputView inputView, final OutputView outputView) { } public void run() { - final ChessGame chessGame = ChessGame.setupStartingPosition(); + + final ChessGame chessGame = create(); outputView.printStartMessage(); GameStatus gameStatus = initGame(); outputView.printChessBoard(ChessBoardDto.from(chessGame)); @@ -30,6 +40,26 @@ public void run() { while (gameStatus.isRunning()) { gameStatus = play(gameStatus, chessGame); } + + final BoardDto boardDto = BoardDto.from(new Board(chessGame.getBoard())); // TODO 형태 너무 이상 변경 필요 + movingDao.remove("board"); + movingDao.remove("turn"); + movingDao.createBoard(); + movingDao.createTurn(); + + movingDao.addBoard(boardDto); + movingDao.addTurn(chessGame.getCamp()); + } + + private ChessGame create() { + final BoardDto board = movingDao.findBoard(); + final TurnDto turn = movingDao.findTurn(); + final Map pieces = board.pieces(); + + if (pieces.isEmpty() || turn == null) { + return ChessGame.setupStartingPosition(); + } + return new ChessGame(board.convert(), turn.convert()); } private GameStatus initGame() { @@ -41,18 +71,36 @@ private GameStatus initGame() { } } - private GameStatus play(final GameStatus gameStatus, final ChessGame chessGame) { + private GameStatus play(final GameStatus preStatus, final ChessGame chessGame) { try { final CommandLine commandLine = readCommandLine(); - final GameStatus tmp = gameStatus.play(commandLine, chessGame); // TODO tmp 말고 딴거 - print(tmp, commandLine, chessGame); - return tmp; + final GameStatus status = preStatus.play(commandLine, chessGame); + saveMoving(chessGame, commandLine); + print(status, commandLine, chessGame); + return status; } catch (final CustomException exception) { outputView.printException(exception.getErrorCode()); - return play(gameStatus, chessGame); + return play(preStatus, chessGame); } } + private void saveMoving(final ChessGame chessGame, final CommandLine commandLine) { + if (commandLine.isMove()) { + final MovingDto movingDto = getMovingDto(chessGame, commandLine); + movingDao.addMoving(movingDto); + } + } + + private MovingDto getMovingDto(final ChessGame chessGame, final CommandLine commandLine) { + final String camp = chessGame.getCamp().toString(); + final List body = commandLine.getBody(); + + final List current = List.of(body.get(0).split("")); + final List next = List.of(body.get(1).split("")); + + return new MovingDto(camp, current.get(0), current.get(1), next.get(0), next.get(1)); + } + //TODO 뭔가 이녀석도 옮겨주기 private void print(final GameStatus gameStatus, final CommandLine commandLine, final ChessGame chessGame) { if (gameStatus.isCheck()) { diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index a673d047eda..372731b72b1 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -4,6 +4,7 @@ import db.dto.MovingDto; import db.dto.PieceDto; import db.dto.PositionDto; +import db.dto.TurnDto; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; @@ -11,8 +12,9 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import model.Camp; -public class MovingDao { +public class MovingDao { // TODO 이름 수정 private static final String SERVER = "localhost:13306"; // MySQL 서버 주소 private static final String OPTION = "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; @@ -29,9 +31,9 @@ public Connection getConnection() { // 드라이버 연결 try { return DriverManager.getConnection("jdbc:mysql://" + SERVER + "/" + database + OPTION, USERNAME, PASSWORD); - } catch (final SQLException e) { - System.err.println("DB 연결 오류:" + e.getMessage()); - e.printStackTrace(); + } catch (final SQLException exception) { + System.err.println("DB 연결 오류:" + exception.getMessage()); + exception.printStackTrace(); return null; } } @@ -55,8 +57,8 @@ private void addPosition(final PositionDto position, final PieceDto piece) { preparedStatement.executeUpdate(); - } catch (final SQLException e) { - throw new RuntimeException(e); + } catch (final SQLException exception) { + throw new RuntimeException(exception); } } @@ -81,8 +83,8 @@ public long addMoving(final MovingDto moving) { autoIncrement = generatedKeys.getLong(1); } return autoIncrement; - } catch (final SQLException e) { - throw new RuntimeException(e); + } catch (final SQLException exception) { + throw new RuntimeException(exception); } } @@ -103,8 +105,8 @@ public MovingDto findByMovementId(final long movementId) { resultSet.getString("destination_file") ); } - } catch (final SQLException e) { - throw new RuntimeException(e); + } catch (final SQLException exception) { + throw new RuntimeException(exception); } return null; @@ -130,11 +132,12 @@ public BoardDto findBoard() { } return new BoardDto(result); - } catch (final SQLException e) { - throw new RuntimeException(e); + } catch (final SQLException exception) { + throw new RuntimeException(exception); } } + //TODO 접근제한자 변경 public void createMoving() { final var query = """ create table moving @@ -151,8 +154,8 @@ destination_file varchar(12) not null ) { preparedStatement.executeUpdate(); - } catch (final SQLException e) { - throw new RuntimeException(e); + } catch (final SQLException exception) { + throw new RuntimeException(exception); } } @@ -170,8 +173,8 @@ camp varchar(5) not null ) { preparedStatement.executeUpdate(); - } catch (final SQLException e) { - throw new RuntimeException(e); + } catch (final SQLException exception) { + throw new RuntimeException(exception); } } @@ -182,8 +185,54 @@ public void remove(String table) { ) { preparedStatement.executeUpdate(); - } catch (final SQLException e) { - throw new RuntimeException(e); + } catch (final SQLException exception) { + throw new RuntimeException(exception); + } + } + + public TurnDto findTurn() { + final var query = "SELECT * FROM turn"; + + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query)) { + final var resultSet = preparedStatement.executeQuery(); + + if (resultSet.next()) { + return new TurnDto(resultSet.getString("camp")); + } + return null; + + } catch (SQLException exception) { + throw new RuntimeException(exception); + } + } + + public void addTurn(final Camp camp) { + final var query = "INSERT INTO turn values(?)"; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query); + ) { + preparedStatement.setString(1, camp.toString()); + preparedStatement.executeUpdate(); + + } catch (final SQLException exception) { + throw new RuntimeException(exception); + } + } + + public void createTurn() { + final var query = """ + create table turn + ( + camp varchar(5) not null + )"""; + try (final var connection = getConnection(); + final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + ) { + preparedStatement.executeUpdate(); + + } catch (final SQLException exception) { + throw new RuntimeException(exception); } } } diff --git a/src/main/java/db/dto/BoardDto.java b/src/main/java/db/dto/BoardDto.java index 10f6a0809e6..37937a2e875 100644 --- a/src/main/java/db/dto/BoardDto.java +++ b/src/main/java/db/dto/BoardDto.java @@ -9,10 +9,6 @@ public record BoardDto(Map pieces) { - public BoardDto(final Map pieces) { - this.pieces = pieces; - } - public static BoardDto from(final Board board) { final Map result = new HashMap<>(); @@ -26,4 +22,12 @@ public static BoardDto from(final Board board) { return new BoardDto(result); } + + public Board convert() { + final Map result = new HashMap<>(); + for (Entry entry : pieces.entrySet()) { + result.put(entry.getKey().convert(), entry.getValue().convert()); + } + return new Board(result); + } } diff --git a/src/main/java/db/dto/PieceDto.java b/src/main/java/db/dto/PieceDto.java index 55713ff5b2e..fc81c1dd6af 100644 --- a/src/main/java/db/dto/PieceDto.java +++ b/src/main/java/db/dto/PieceDto.java @@ -1,10 +1,49 @@ package db.dto; +import model.Camp; +import model.piece.Bishop; +import model.piece.BlackPawn; +import model.piece.King; +import model.piece.Knight; import model.piece.Piece; +import model.piece.Queen; +import model.piece.Rook; +import model.piece.WhitePawn; public record PieceDto(String type, String camp) { public static PieceDto from(final Piece piece) { return new PieceDto(PieceType.findValue(piece), piece.getCamp().toString()); } + + //TODO 요고 리팩터링 + public Piece convert() { + final Camp army = find(); + if ("King".equals(type)) { + return new King(army); + } + if ("Queen".equals(type)) { + return new Queen(army); + } + if ("Knight".equals(type)) { + return new Knight(army); + } + if ("Bishop".equals(type)) { + return new Bishop(army); + } + if ("Rook".equals(type)) { + return new Rook(army); + } + if ("Pawn".equals(type) && army == Camp.WHITE) { + return new WhitePawn(); + } + return new BlackPawn(); + } + + private Camp find() { + if ("BLACK".equals(camp)) { + return Camp.BLACK; + } + return Camp.WHITE; + } } diff --git a/src/main/java/db/dto/PositionDto.java b/src/main/java/db/dto/PositionDto.java index e795b20ff24..8d79c58f676 100644 --- a/src/main/java/db/dto/PositionDto.java +++ b/src/main/java/db/dto/PositionDto.java @@ -1,10 +1,16 @@ package db.dto; +import model.position.File; import model.position.Position; +import model.position.Rank; public record PositionDto(String value) { public static PositionDto from(final Position position) { return new PositionDto(position.toString()); } + + public Position convert() { + return new Position(File.from(value.charAt(0)), Rank.from(value.charAt(1))); + } } diff --git a/src/main/java/db/dto/TurnDto.java b/src/main/java/db/dto/TurnDto.java new file mode 100644 index 00000000000..19e506eb509 --- /dev/null +++ b/src/main/java/db/dto/TurnDto.java @@ -0,0 +1,13 @@ +package db.dto; + +import model.Camp; + +public record TurnDto(String value) { + + public Camp convert() { + if ("WHITE".equals(value)) { + return Camp.WHITE; + } + return Camp.BLACK; + } +} diff --git a/src/main/java/model/ChessGame.java b/src/main/java/model/ChessGame.java index 2d405823bed..b18fe1cd84b 100644 --- a/src/main/java/model/ChessGame.java +++ b/src/main/java/model/ChessGame.java @@ -12,13 +12,13 @@ public class ChessGame { private final Board board; private Camp camp; - private ChessGame(final Board board) { + public ChessGame(final Board board, final Camp camp) { this.board = board; - this.camp = STARTING_CAMP; + this.camp = camp; } public static ChessGame setupStartingPosition() { - return new ChessGame(Board.create()); + return new ChessGame(Board.create(), STARTING_CAMP); } public void move(final Moving moving) { diff --git a/src/main/resources/board.sql b/src/main/resources/board.sql new file mode 100644 index 00000000000..4200ab475d8 --- /dev/null +++ b/src/main/resources/board.sql @@ -0,0 +1,11 @@ +use chess; + +drop table board; + +create table board +( + position varchar(2) not null, + piece_type varchar(6) not null, + camp varchar(5) not null + +); diff --git a/src/main/resources/moving.sql b/src/main/resources/moving.sql new file mode 100644 index 00000000000..b862069dd2b --- /dev/null +++ b/src/main/resources/moving.sql @@ -0,0 +1,13 @@ +use chess; +# +drop table moving; + +create table moving +( + movement_id INT primary key auto_increment, + camp varchar(5) not null, + start_rank varchar(12) not null, + start_file varchar(12) not null, + destination_rank varchar(12) not null, + destination_file varchar(12) not null +); diff --git a/src/main/resources/turn.sql b/src/main/resources/turn.sql new file mode 100644 index 00000000000..204a9c80767 --- /dev/null +++ b/src/main/resources/turn.sql @@ -0,0 +1,8 @@ +use chess; + +drop table turn; + +create table turn +( + camp varchar(5) not null +); diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java index 85ae278f574..06a43dcba7c 100644 --- a/src/test/java/db/MovingDaoTest.java +++ b/src/test/java/db/MovingDaoTest.java @@ -6,6 +6,7 @@ import db.dto.MovingDto; import java.sql.SQLException; import model.Board; +import model.Camp; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -57,4 +58,11 @@ void addBoard() { //then assertThat(board).isEqualTo(findBoard); } + + @Test + @DisplayName("턴 저장 확인") + void addTurn() { + movingDao.addTurn(Camp.BLACK); + + } } diff --git a/src/test/resources/turn.sql b/src/test/resources/turn.sql new file mode 100644 index 00000000000..3c41c04221b --- /dev/null +++ b/src/test/resources/turn.sql @@ -0,0 +1,6 @@ +use chess_test; + +create table turn +( + camp varchar(5) not null +); From fd0debd753eb3646846b1b54bfb8809a0cb85793 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 30 Mar 2024 09:26:52 +0900 Subject: [PATCH 19/60] =?UTF-8?q?chore:=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=BF=BC=EB=A6=AC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/board.sql | 2 +- src/main/resources/moving.sql | 4 ++-- src/main/resources/turn.sql | 2 +- src/test/resources/board.sql | 2 +- src/test/resources/moving.sql | 2 ++ src/test/resources/turn.sql | 2 ++ 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/resources/board.sql b/src/main/resources/board.sql index 4200ab475d8..91d5395421b 100644 --- a/src/main/resources/board.sql +++ b/src/main/resources/board.sql @@ -1,6 +1,6 @@ use chess; -drop table board; +drop table if exists board; create table board ( diff --git a/src/main/resources/moving.sql b/src/main/resources/moving.sql index b862069dd2b..5bc04711949 100644 --- a/src/main/resources/moving.sql +++ b/src/main/resources/moving.sql @@ -1,6 +1,6 @@ use chess; -# -drop table moving; + +drop table if exists moving; create table moving ( diff --git a/src/main/resources/turn.sql b/src/main/resources/turn.sql index 204a9c80767..7eb997d9974 100644 --- a/src/main/resources/turn.sql +++ b/src/main/resources/turn.sql @@ -1,6 +1,6 @@ use chess; -drop table turn; +drop table if exists turn; create table turn ( diff --git a/src/test/resources/board.sql b/src/test/resources/board.sql index fc90bc133e2..5ebbdf9bc54 100644 --- a/src/test/resources/board.sql +++ b/src/test/resources/board.sql @@ -1,6 +1,6 @@ use chess_test; -# drop table board; +drop table if exists board; create table board ( diff --git a/src/test/resources/moving.sql b/src/test/resources/moving.sql index 954ae4f3059..60737d06138 100644 --- a/src/test/resources/moving.sql +++ b/src/test/resources/moving.sql @@ -1,5 +1,7 @@ use chess_test; +drop table if exists moving; + create table moving ( movement_id INT primary key auto_increment, diff --git a/src/test/resources/turn.sql b/src/test/resources/turn.sql index 3c41c04221b..2359a475c43 100644 --- a/src/test/resources/turn.sql +++ b/src/test/resources/turn.sql @@ -1,5 +1,7 @@ use chess_test; +drop table if exists turn; + create table turn ( camp varchar(5) not null From fc1d118ef3e1cdc42e1cd217e466c98ddfcc54d9 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 30 Mar 2024 09:49:03 +0900 Subject: [PATCH 20/60] =?UTF-8?q?docs:=20=EA=B5=AC=ED=98=84=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EB=AA=A9=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 28d87e590a7..f7a0bfea08f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -31,8 +31,8 @@ * [x] end 가 입력되면 프로그램 종료 * [x] 예외 시 재입력 * [x] 진행중인 게임 end 입력 후 재시작시 이전 게임 실행 - - +* [ ] restart 입력시 새로운 게임 실행 +* [ ] log 입력시 현재까지 기보 출력 ## 기물의 이동 기능 @@ -58,6 +58,23 @@ * 게임이 시작되지 않았는데 move 명령어를 입력한다면 예외가 발생한다. * 형식은 move [a-h1-8] [a-h1-8] 형태이고 아닐시 예외가 발생한다. +## 고민해볼점 + +* 프로그램 실행 중 종료시 어떻게 저장할 것인가 + + * 현재 총 세개의 테이블이 있다. (board, moving, turn) + * board: 기물들의 위치 + * turn: 마지막 턴 + * moving: 기보 + * board, turn 테이블은 end 입력될 때 한번 업데이트를 한다. (문제, 프로그램 실행중 종료되면 저장안됨) + * moving 테이블은 move 입력될 때마다 insert 쿼리를 날린다. (프로그램이 도중에 종료되어도 최신것까지 저장됨) + * 만약 프로그램이 도중에 종료된다면 moving이랑 board가 불일치발생 + + * **해결방안** + * 현재까지 진행된 turn 횟수를 기록하고 turn 테이블에 같이 저장 + * turn 횟수와 moving이 일치하지 않는다면 moving을 토대로 board를 업데이트! + + ## 우선순위가 낮지만 구현해보고 싶은 기능 * 특수 행마법 From c879ac55a0f6652bf209a00fe6d5f474751d34c3 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 30 Mar 2024 17:00:15 +0900 Subject: [PATCH 21/60] =?UTF-8?q?refactor:=20=ED=98=84=EC=9E=AC=20?= =?UTF-8?q?=EB=B3=B4=EB=93=9C=20=EC=A0=90=EC=88=98=20=EA=B3=84=EC=82=B0=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=84=B0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/Board.java | 67 ++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java index ae204946bea..55508651217 100644 --- a/src/main/java/model/Board.java +++ b/src/main/java/model/Board.java @@ -8,6 +8,7 @@ import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -123,30 +124,50 @@ public void move(final Moving moving) { pieces.remove(moving.getCurrentPosition()); } - public Score calculateScore(final Camp camp) { //TODO 람다 스트림으로 빼보기 - final Map count = new EnumMap<>(File.class); - Score result = new Score(0); - for (final Entry entry : pieces.entrySet()) { - final Piece piece = entry.getValue(); - if (piece.isSameCamp(camp)) { - if (piece.isPawn()) { - final File file = entry.getKey().getFile(); - count.put(file, count.getOrDefault(file, 0) + 1); - } - result = result.add(PieceScore.getScore(piece)); - } - } - final Score score = new Score(1); - for (int sameFilePawnCount : count.values()) { - if (sameFilePawnCount == 1) { - result.add(score); - } - if (sameFilePawnCount > 1) { - float value = sameFilePawnCount * 0.5f; - result = result.minus(new Score(value)); - } + public Score calculateScore(final Camp camp) { + final Map pawnCount = countSameFilePawn(camp); + final List scores = collectScore(camp); + + final Score result = scores.stream() + .reduce(Score::add) + .orElse(new Score(0)); + + return result.minus(duplicateFilePawns(pawnCount)); + } + + private List collectScore(final Camp camp) { + return pieces.values() + .stream() + .filter(piece -> piece.isSameCamp(camp)) + .map(PieceScore::getScore) + .toList(); + } + + private Map countSameFilePawn(final Camp camp) { + final Map pawnCount = new EnumMap<>(File.class); + pieces.entrySet() + .stream() + .filter(entry -> entry.getValue().isSameCamp(camp)) + .forEach(entry -> checkPawn(entry, pawnCount)); + return pawnCount; + } + + private Score duplicateFilePawns(final Map count) { + return count.values() + .stream() + .filter(sameFilePawn -> sameFilePawn > 1) + .map(sameFilePawn -> new Score(sameFilePawn * 0.5F)) // TODO 0.5 상수화 + .reduce(Score::add) + .orElse(new Score(0)); + } + + private void checkPawn(final Entry entry, final Map count) { + final Piece piece = entry.getValue(); + if (piece.isPawn()) { + final Position position = entry.getKey(); + final File file = position.getFile(); + count.put(file, count.getOrDefault(file, 0) + 1); } - return result; } public Map getPieces() { From b3d1c8c84f00942372af15328c18e87ca74a7c8a Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 30 Mar 2024 20:09:36 +0900 Subject: [PATCH 22/60] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=ED=95=98=EC=A7=80=20=EC=95=8A=EA=B3=A0=20=EC=A2=85?= =?UTF-8?q?=EB=A3=8C=ED=95=98=EB=8A=94=20=EB=AA=85=EB=A0=B9=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 4 ++++ src/main/java/controller/ChessController.java | 4 ++++ src/main/java/model/command/Command.java | 1 + src/main/java/model/command/CommandLine.java | 4 ++++ src/main/java/model/status/Quit.java | 24 +++++++++++++++++++ src/main/java/model/status/Running.java | 3 +++ src/main/java/view/OutputView.java | 3 ++- src/test/java/model/status/RunningTest.java | 12 ++++++++++ 8 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/main/java/model/status/Quit.java diff --git a/docs/README.md b/docs/README.md index f7a0bfea08f..d9ca3e33139 100644 --- a/docs/README.md +++ b/docs/README.md @@ -32,7 +32,11 @@ * [x] 예외 시 재입력 * [x] 진행중인 게임 end 입력 후 재시작시 이전 게임 실행 * [ ] restart 입력시 새로운 게임 실행 + * quit 으로 현재 게임을 완전히 끝내고 시작은 start만 두는 게 나을지 + * 아니면 시작할때 restart, start로 구분하는게 나을지. + * quit 이 구현은 좀 더 쉬워보인다. * [ ] log 입력시 현재까지 기보 출력 +* [ ] db 예외 처리 ## 기물의 이동 기능 diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 09b0d798b8c..7a52b5fa1de 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -15,6 +15,7 @@ import model.ChessGame; import model.command.CommandLine; import model.status.GameStatus; +import model.status.Quit; import model.status.StatusFactory; import view.InputView; import view.OutputView; @@ -47,6 +48,9 @@ public void run() { movingDao.createBoard(); movingDao.createTurn(); + if (gameStatus instanceof Quit) { // TODO instanceof 괜춘? + return; + } movingDao.addBoard(boardDto); movingDao.addTurn(chessGame.getCamp()); } diff --git a/src/main/java/model/command/Command.java b/src/main/java/model/command/Command.java index 5b284f4dc6f..5ea841b4e3d 100644 --- a/src/main/java/model/command/Command.java +++ b/src/main/java/model/command/Command.java @@ -12,6 +12,7 @@ public enum Command { MOVE(Pattern.compile("move"), 2), POSITION(Pattern.compile("[a-hA-H][1-8]"), 0), STATUS(Pattern.compile("status"), 0), + QUIT(Pattern.compile("quit"), 0), END(Pattern.compile("end"), 0); public static final int HEAD_INDEX = 0; diff --git a/src/main/java/model/command/CommandLine.java b/src/main/java/model/command/CommandLine.java index febb021e84d..b4e91556b53 100644 --- a/src/main/java/model/command/CommandLine.java +++ b/src/main/java/model/command/CommandLine.java @@ -59,6 +59,10 @@ public boolean isStatus() { return head == Command.STATUS; } + public boolean isQuit() { + return head == Command.QUIT; + } + public List getBody() { return Collections.unmodifiableList(body); } diff --git a/src/main/java/model/status/Quit.java b/src/main/java/model/status/Quit.java new file mode 100644 index 00000000000..721849812d6 --- /dev/null +++ b/src/main/java/model/status/Quit.java @@ -0,0 +1,24 @@ +package model.status; + +import constant.ErrorCode; +import exception.InvalidStatusException; +import model.ChessGame; +import model.command.CommandLine; + +public class Quit implements GameStatus { + + @Override + public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) { + throw new InvalidStatusException(ErrorCode.INVALID_STATUS); + } + + @Override + public boolean isRunning() { + return false; + } + + @Override + public boolean isCheck() { + return false; + } +} diff --git a/src/main/java/model/status/Running.java b/src/main/java/model/status/Running.java index 12aced3eb1a..4a29fd9a3f3 100644 --- a/src/main/java/model/status/Running.java +++ b/src/main/java/model/status/Running.java @@ -22,6 +22,9 @@ public GameStatus play(final CommandLine commandLine, final ChessGame chessGame) if (commandLine.isStatus()) { return new Running(); } + if (commandLine.isQuit()) { + return new Quit(); + } throw new InvalidStatusException(ErrorCode.INVALID_STATUS); } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 4c4ad1d6924..d09ad38c2ff 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -10,7 +10,8 @@ public class OutputView { public void printStartMessage() { System.out.println("> 체스 게임을 시작합니다."); System.out.println("> 게임 시작 : start"); - System.out.println("> 게임 종료 : end"); + System.out.println("> 게임 저장 후 종료 : end"); + System.out.println("> 게임 저장 하지 않고 종료 : quit"); System.out.println("> 게임 이동 : move source위치 target위치 - 예. move b2 b3"); System.out.println("> 게임 현황 : status"); } diff --git a/src/test/java/model/status/RunningTest.java b/src/test/java/model/status/RunningTest.java index 7bfef73b516..0f72f87acb5 100644 --- a/src/test/java/model/status/RunningTest.java +++ b/src/test/java/model/status/RunningTest.java @@ -45,6 +45,18 @@ void checkRunningAfterEnd() { .isInstanceOf(End.class); } + @DisplayName("러닝 상태에서 quit 후 상태를 학인한다.") + @Test + void checkRunningAfterQuit() { + final CommandLine startCommand = CommandLine.from(List.of("start")); + final GameStatus gameStatus = StatusFactory.create(startCommand); + final ChessGame chessGame = ChessGame.setupStartingPosition(); + + final CommandLine quitCommand = CommandLine.from(List.of("quit")); + assertThat(gameStatus.play(quitCommand, chessGame)) + .isInstanceOf(Quit.class); + } + @DisplayName("러닝 상태에서 start 하면 예외가 발생한다.") @Test void failToStartIfAlreadyRunning() { From bbc01753bd93d64e75da8f07a1fc159f9f25b3f5 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 30 Mar 2024 20:31:27 +0900 Subject: [PATCH 23/60] =?UTF-8?q?refactor:=20=ED=85=8C=EC=9D=B4=EB=B8=94?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C=20=EB=8C=80=EC=8B=A0=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EB=A7=8C=20=EC=82=AD=EC=A0=9C(drop=20->=20truncate)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 2 - src/main/java/db/MovingDao.java | 79 +++---------------- src/test/java/db/MovingDaoTest.java | 8 +- 3 files changed, 12 insertions(+), 77 deletions(-) diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 7a52b5fa1de..61076b4661b 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -45,8 +45,6 @@ public void run() { final BoardDto boardDto = BoardDto.from(new Board(chessGame.getBoard())); // TODO 형태 너무 이상 변경 필요 movingDao.remove("board"); movingDao.remove("turn"); - movingDao.createBoard(); - movingDao.createTurn(); if (gameStatus instanceof Quit) { // TODO instanceof 괜춘? return; diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 372731b72b1..861c0086346 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -16,12 +16,12 @@ public class MovingDao { // TODO 이름 수정 - private static final String SERVER = "localhost:13306"; // MySQL 서버 주소 + private static final String SERVER = "localhost:13306"; private static final String OPTION = "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; - private static final String USERNAME = "root"; // MySQL 서버 아이디 - private static final String PASSWORD = "root"; // MySQL 서버 비밀번호 + private static final String USERNAME = "root"; + private static final String PASSWORD = "root"; - private final String database; // MySQL DATABASE 이름 + private final String database; public MovingDao(final String database) { this.database = database; @@ -49,7 +49,7 @@ public void addBoard(final BoardDto board) { private void addPosition(final PositionDto position, final PieceDto piece) { final var query = "INSERT INTO board VALUES(?, ?, ?)"; try (final var connection = getConnection(); - final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { preparedStatement.setString(1, position.value()); preparedStatement.setString(2, piece.type()); @@ -66,7 +66,7 @@ private void addPosition(final PositionDto position, final PieceDto piece) { public long addMoving(final MovingDto moving) { final var query = "INSERT INTO moving VALUES(?, ?, ?, ?, ?, ?)"; try (final var connection = getConnection(); - final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { long autoIncrement = 0; preparedStatement.setNull(1, 0); @@ -137,51 +137,10 @@ public BoardDto findBoard() { } } - //TODO 접근제한자 변경 - public void createMoving() { - final var query = """ - create table moving - ( - movement_id INT primary key auto_increment, - camp varchar(5) not null, - start_rank varchar(12) not null, - start_file varchar(12) not null, - destination_rank varchar(12) not null, - destination_file varchar(12) not null - )"""; - try (final var connection = getConnection(); - final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); - ) { - preparedStatement.executeUpdate(); - - } catch (final SQLException exception) { - throw new RuntimeException(exception); - } - } - - public void createBoard() { - final var query = """ - create table board - ( - position varchar(2) not null, - piece_type varchar(6) not null, - camp varchar(5) not null - - )"""; - try (final var connection = getConnection(); - final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); - ) { - preparedStatement.executeUpdate(); - - } catch (final SQLException exception) { - throw new RuntimeException(exception); - } - } - public void remove(String table) { - final var query = String.format("drop table %s", table); + final String query = String.format("truncate table %s", table); try (final var connection = getConnection(); - final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { preparedStatement.executeUpdate(); @@ -191,7 +150,7 @@ public void remove(String table) { } public TurnDto findTurn() { - final var query = "SELECT * FROM turn"; + final String query = "SELECT * FROM turn"; try (final var connection = getConnection(); final var preparedStatement = connection.prepareStatement(query)) { @@ -208,9 +167,9 @@ public TurnDto findTurn() { } public void addTurn(final Camp camp) { - final var query = "INSERT INTO turn values(?)"; + final String query = "INSERT INTO turn values(?)"; try (final var connection = getConnection(); - final var preparedStatement = connection.prepareStatement(query); + final var preparedStatement = connection.prepareStatement(query) ) { preparedStatement.setString(1, camp.toString()); preparedStatement.executeUpdate(); @@ -219,20 +178,4 @@ public void addTurn(final Camp camp) { throw new RuntimeException(exception); } } - - public void createTurn() { - final var query = """ - create table turn - ( - camp varchar(5) not null - )"""; - try (final var connection = getConnection(); - final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); - ) { - preparedStatement.executeUpdate(); - - } catch (final SQLException exception) { - throw new RuntimeException(exception); - } - } } diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java index 06a43dcba7c..8cf1a3a5923 100644 --- a/src/test/java/db/MovingDaoTest.java +++ b/src/test/java/db/MovingDaoTest.java @@ -7,7 +7,6 @@ import java.sql.SQLException; import model.Board; import model.Camp; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -18,13 +17,8 @@ class MovingDaoTest { @BeforeEach void beforeEach() { - movingDao.createMoving(); - movingDao.createBoard(); - } - - @AfterEach - void afterEach() { movingDao.remove("moving"); + movingDao.remove("turn"); movingDao.remove("board"); } From 702f306a72a708aa65adbb2fe4cc02f3b2d8030f Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 30 Mar 2024 21:41:58 +0900 Subject: [PATCH 24/60] =?UTF-8?q?refactor:=20pawn=EC=9D=B4=20whitePawn?= =?UTF-8?q?=EA=B3=BC=20blackPawn=EC=9D=84=20=EC=A7=81=EC=A0=91=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=95=8C=EA=B3=A0=20=EC=9E=88=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EA=B2=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/Board.java | 11 ++++++----- src/main/java/model/piece/Pawn.java | 7 ------- src/test/java/model/piece/PawnTest.java | 23 +++++++++-------------- 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java index 55508651217..64b483e45e8 100644 --- a/src/main/java/model/Board.java +++ b/src/main/java/model/Board.java @@ -14,12 +14,14 @@ import java.util.Set; import java.util.function.Function; import model.piece.Bishop; +import model.piece.BlackPawn; import model.piece.King; import model.piece.Knight; import model.piece.Pawn; import model.piece.Piece; import model.piece.Queen; import model.piece.Rook; +import model.piece.WhitePawn; import model.position.File; import model.position.Moving; import model.position.Position; @@ -47,8 +49,8 @@ public Board(final Map pieces) { public static Board create() { final Map result = new HashMap<>(); settingExceptPawn(result, Camp.BLACK, Rank.EIGHT); - settingPawn(result, Camp.BLACK, Rank.SEVEN); - settingPawn(result, Camp.WHITE, Rank.TWO); + settingPawn(result, Rank.SEVEN, new BlackPawn()); + settingPawn(result, Rank.TWO, new WhitePawn()); settingExceptPawn(result, Camp.WHITE, Rank.ONE); return new Board(result); } @@ -60,10 +62,9 @@ private static void settingExceptPawn(final Map board, final Ca } } - private static void settingPawn(final Map board, final Camp camp, final Rank rank) { + private static void settingPawn(final Map board, final Rank rank, final Pawn pawn) { for (File file : File.values()) { - final Piece piece = Pawn.create(camp); - board.put(new Position(file, rank), piece); + board.put(new Position(file, rank), pawn); } } diff --git a/src/main/java/model/piece/Pawn.java b/src/main/java/model/piece/Pawn.java index 09cfd0ca512..d1a5a5ce2a4 100644 --- a/src/main/java/model/piece/Pawn.java +++ b/src/main/java/model/piece/Pawn.java @@ -13,13 +13,6 @@ protected Pawn(final Camp camp) { super(camp); } - public static Pawn create(Camp camp) { - if (camp == Camp.BLACK) { - return new BlackPawn(); - } - return new WhitePawn(); - } - protected abstract boolean isDiagonal(final int differenceRank, final int differenceFile); protected abstract boolean isStraight(final Position currentPosition, diff --git a/src/test/java/model/piece/PawnTest.java b/src/test/java/model/piece/PawnTest.java index 4313e5263fa..296e4aacd38 100644 --- a/src/test/java/model/piece/PawnTest.java +++ b/src/test/java/model/piece/PawnTest.java @@ -31,7 +31,6 @@ import exception.InvalidMovingException; import java.util.Set; import java.util.stream.Stream; -import model.Camp; import model.ChessGame; import model.position.Moving; import model.position.Position; @@ -46,9 +45,7 @@ class PawnTest { @DisplayName("이동할 수 없는 경로면 예외가 발생한다.") @ParameterizedTest @MethodSource("invalidMovingParameterProvider") - void invalidMoving(final Camp camp, final Moving moving) { - final Pawn pawn = Pawn.create(camp); - + void invalidMoving(final Pawn pawn, final Moving moving) { assertAll( () -> assertThat(pawn.canMovable(moving)).isFalse(), () -> assertThatThrownBy(() -> pawn.getMoveRoute(moving)) @@ -58,18 +55,16 @@ void invalidMoving(final Camp camp, final Moving moving) { static Stream invalidMovingParameterProvider() { return Stream.of( - Arguments.of(Camp.BLACK, new Moving(A6, A4)), - Arguments.of(Camp.BLACK, new Moving(A7, A4)), - Arguments.of(Camp.WHITE, new Moving(A8, A7)) + Arguments.of(new BlackPawn(), new Moving(A6, A4)), + Arguments.of(new BlackPawn(), new Moving(A7, A4)), + Arguments.of(new WhitePawn(), new Moving(A8, A7)) ); } @DisplayName("이동 경로를 반환한다. 출발지와 도착지는 포함하지 않는다.") @ParameterizedTest @MethodSource("checkRouteParameterProvider") - void checkRoute(final Camp camp, final Moving moving, final Set expected) { - final Pawn pawn = Pawn.create(camp); - + void checkRoute(final Pawn pawn, final Moving moving, final Set expected) { assertAll( () -> assertThat(pawn.canMovable(moving)).isTrue(), () -> assertThat(pawn.getMoveRoute(moving)).isEqualTo(expected) @@ -79,10 +74,10 @@ void checkRoute(final Camp camp, final Moving moving, final Set expect static Stream checkRouteParameterProvider() { return Stream.of( - Arguments.of(Camp.BLACK, new Moving(A7, A5), Set.of(A6)), - Arguments.of(Camp.BLACK, new Moving(A6, A5), Set.of()), - Arguments.of(Camp.WHITE, new Moving(B2, B4), Set.of(B3)), - Arguments.of(Camp.WHITE, new Moving(C2, C3), Set.of()) + Arguments.of(new BlackPawn(), new Moving(A7, A5), Set.of(A6)), + Arguments.of(new BlackPawn(), new Moving(A6, A5), Set.of()), + Arguments.of(new WhitePawn(), new Moving(B2, B4), Set.of(B3)), + Arguments.of(new WhitePawn(), new Moving(C2, C3), Set.of()) ); } From 6f3e9feb76a5891cb90e55050ec0ef5b46ae052f Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 30 Mar 2024 22:51:18 +0900 Subject: [PATCH 25/60] =?UTF-8?q?refactor:=20db=20connection=20=ED=9A=8D?= =?UTF-8?q?=EB=93=9D=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 17 +++------- src/main/java/db/MovingDao.java | 33 +++++-------------- .../java/db/connection/ConnectionConst.java | 19 +++++++++++ .../java/db/connection/DBConnectionUtil.java | 28 ++++++++++++++++ src/main/java/db/dto/MovingDto.java | 12 +++---- src/test/java/db/MovingDaoTest.java | 3 +- 6 files changed, 66 insertions(+), 46 deletions(-) create mode 100644 src/main/java/db/connection/ConnectionConst.java create mode 100644 src/main/java/db/connection/DBConnectionUtil.java diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 61076b4661b..0457757954e 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; import model.Board; +import model.Camp; import model.ChessGame; import model.command.CommandLine; import model.status.GameStatus; @@ -42,13 +43,13 @@ public void run() { gameStatus = play(gameStatus, chessGame); } - final BoardDto boardDto = BoardDto.from(new Board(chessGame.getBoard())); // TODO 형태 너무 이상 변경 필요 movingDao.remove("board"); movingDao.remove("turn"); if (gameStatus instanceof Quit) { // TODO instanceof 괜춘? return; } + final BoardDto boardDto = BoardDto.from(new Board(chessGame.getBoard())); // TODO 형태 너무 이상 변경 필요 movingDao.addBoard(boardDto); movingDao.addTurn(chessGame.getCamp()); } @@ -88,21 +89,13 @@ private GameStatus play(final GameStatus preStatus, final ChessGame chessGame) { private void saveMoving(final ChessGame chessGame, final CommandLine commandLine) { if (commandLine.isMove()) { - final MovingDto movingDto = getMovingDto(chessGame, commandLine); + final List body = commandLine.getBody(); + final Camp camp = chessGame.getCamp(); + final MovingDto movingDto = MovingDto.from(body, camp); movingDao.addMoving(movingDto); } } - private MovingDto getMovingDto(final ChessGame chessGame, final CommandLine commandLine) { - final String camp = chessGame.getCamp().toString(); - final List body = commandLine.getBody(); - - final List current = List.of(body.get(0).split("")); - final List next = List.of(body.get(1).split("")); - - return new MovingDto(camp, current.get(0), current.get(1), next.get(0), next.get(1)); - } - //TODO 뭔가 이녀석도 옮겨주기 private void print(final GameStatus gameStatus, final CommandLine commandLine, final ChessGame chessGame) { if (gameStatus.isCheck()) { diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 861c0086346..5e3b0a6b4f0 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -1,12 +1,11 @@ package db; +import db.connection.DBConnectionUtil; import db.dto.BoardDto; import db.dto.MovingDto; import db.dto.PieceDto; import db.dto.PositionDto; import db.dto.TurnDto; -import java.sql.Connection; -import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.HashMap; @@ -16,28 +15,12 @@ public class MovingDao { // TODO 이름 수정 - private static final String SERVER = "localhost:13306"; - private static final String OPTION = "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; - private static final String USERNAME = "root"; - private static final String PASSWORD = "root"; - private final String database; public MovingDao(final String database) { this.database = database; } - public Connection getConnection() { - // 드라이버 연결 - try { - return DriverManager.getConnection("jdbc:mysql://" + SERVER + "/" + database + OPTION, USERNAME, PASSWORD); - } catch (final SQLException exception) { - System.err.println("DB 연결 오류:" + exception.getMessage()); - exception.printStackTrace(); - return null; - } - } - public void addBoard(final BoardDto board) { final Map pieces = board.pieces(); @@ -48,7 +31,7 @@ public void addBoard(final BoardDto board) { private void addPosition(final PositionDto position, final PieceDto piece) { final var query = "INSERT INTO board VALUES(?, ?, ?)"; - try (final var connection = getConnection(); + try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { preparedStatement.setString(1, position.value()); @@ -65,7 +48,7 @@ private void addPosition(final PositionDto position, final PieceDto piece) { public long addMoving(final MovingDto moving) { final var query = "INSERT INTO moving VALUES(?, ?, ?, ?, ?, ?)"; - try (final var connection = getConnection(); + try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { long autoIncrement = 0; @@ -90,7 +73,7 @@ public long addMoving(final MovingDto moving) { public MovingDto findByMovementId(final long movementId) { final var query = "SELECT * FROM moving WHERE movement_id = ?"; - try (final var connection = getConnection(); + try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query)) { preparedStatement.setLong(1, movementId); @@ -115,7 +98,7 @@ public MovingDto findByMovementId(final long movementId) { public BoardDto findBoard() { final var query = "SELECT * FROM board"; - try (final var connection = getConnection(); + try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query)) { final var resultSet = preparedStatement.executeQuery(); @@ -139,7 +122,7 @@ public BoardDto findBoard() { public void remove(String table) { final String query = String.format("truncate table %s", table); - try (final var connection = getConnection(); + try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { preparedStatement.executeUpdate(); @@ -152,7 +135,7 @@ public void remove(String table) { public TurnDto findTurn() { final String query = "SELECT * FROM turn"; - try (final var connection = getConnection(); + try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query)) { final var resultSet = preparedStatement.executeQuery(); @@ -168,7 +151,7 @@ public TurnDto findTurn() { public void addTurn(final Camp camp) { final String query = "INSERT INTO turn values(?)"; - try (final var connection = getConnection(); + try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query) ) { preparedStatement.setString(1, camp.toString()); diff --git a/src/main/java/db/connection/ConnectionConst.java b/src/main/java/db/connection/ConnectionConst.java new file mode 100644 index 00000000000..d7195396b0f --- /dev/null +++ b/src/main/java/db/connection/ConnectionConst.java @@ -0,0 +1,19 @@ +package db.connection; + +public enum ConnectionConst { + + SERVER("localhost:13306"), + OPTION("?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"), + USERNAME("root"), + PASSWORD("root"); + + private final String value; + + ConnectionConst(final String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/src/main/java/db/connection/DBConnectionUtil.java b/src/main/java/db/connection/DBConnectionUtil.java new file mode 100644 index 00000000000..abc585b438b --- /dev/null +++ b/src/main/java/db/connection/DBConnectionUtil.java @@ -0,0 +1,28 @@ +package db.connection; + +import static db.connection.ConnectionConst.OPTION; +import static db.connection.ConnectionConst.PASSWORD; +import static db.connection.ConnectionConst.SERVER; +import static db.connection.ConnectionConst.USERNAME; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class DBConnectionUtil { + + private DBConnectionUtil() { + } + + public static Connection getConnection(final String database) { + // 드라이버 연결 + try { + final String url = "jdbc:mysql://" + SERVER.getValue() + "/" + database + OPTION.getValue(); + return DriverManager.getConnection(url, USERNAME.getValue(), PASSWORD.getValue()); + } catch (final SQLException exception) { + System.err.println("DB 연결 오류:" + exception.getMessage()); + exception.printStackTrace(); + return null; // TODO null 을 리턴하는게 맞을지 예외 던지는게 맞을지 + } + } +} diff --git a/src/main/java/db/dto/MovingDto.java b/src/main/java/db/dto/MovingDto.java index d339db4d127..c02bb920bfe 100644 --- a/src/main/java/db/dto/MovingDto.java +++ b/src/main/java/db/dto/MovingDto.java @@ -1,17 +1,13 @@ package db.dto; +import java.util.List; import model.Camp; -import model.position.Moving; -import model.position.Position; public record MovingDto(String camp, String currentFile, String currentRank, String nextFile, String nextRank) { - public static MovingDto from(final Moving moving, final Camp camp) { - final Position currentPosition = moving.getCurrentPosition(); - final Position nextPosition = moving.getNextPosition(); - - final String current = currentPosition.toString(); - final String next = nextPosition.toString(); + public static MovingDto from(final List moving, final Camp camp) { + final String current = moving.get(0); + final String next = moving.get(1); return new MovingDto(camp.toString(), String.valueOf(current.charAt(0)), String.valueOf(current.charAt(1)), String.valueOf(next.charAt(0)), String.valueOf(next.charAt(1))); diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java index 8cf1a3a5923..5d744a76db5 100644 --- a/src/test/java/db/MovingDaoTest.java +++ b/src/test/java/db/MovingDaoTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import db.connection.DBConnectionUtil; import db.dto.BoardDto; import db.dto.MovingDto; import java.sql.SQLException; @@ -25,7 +26,7 @@ void beforeEach() { @DisplayName("데이터베이스 접속 확인") @Test void connection() throws SQLException { - try (final var connection = movingDao.getConnection()) { + try (final var connection = DBConnectionUtil.getConnection("chess_test")) { assertThat(connection).isNotNull(); } } From c5694c3dea4437eba234375b41df0c1c4aa71509 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 30 Mar 2024 23:53:25 +0900 Subject: [PATCH 26/60] =?UTF-8?q?refactor:=20dao=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 18 ++-- src/main/java/db/BoardDao.java | 80 ++++++++++++++++ src/main/java/db/MovingDao.java | 91 +------------------ src/main/java/db/TurnDao.java | 58 ++++++++++++ src/test/java/db/BoardDaoTest.java | 33 +++++++ src/test/java/db/MovingDaoTest.java | 26 ------ src/test/java/db/TurnDaoTest.java | 33 +++++++ 7 files changed, 217 insertions(+), 122 deletions(-) create mode 100644 src/main/java/db/BoardDao.java create mode 100644 src/main/java/db/TurnDao.java create mode 100644 src/test/java/db/BoardDaoTest.java create mode 100644 src/test/java/db/TurnDaoTest.java diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 0457757954e..64e5d107efe 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -1,6 +1,8 @@ package controller; +import db.BoardDao; import db.MovingDao; +import db.TurnDao; import db.dto.BoardDto; import db.dto.MovingDto; import db.dto.PieceDto; @@ -25,7 +27,10 @@ public class ChessController { private final InputView inputView; private final OutputView outputView; - private final MovingDao movingDao = new MovingDao("chess"); + private final MovingDao movingDao = new MovingDao("chess"); // TODO 중구난방쓰. dao 모으기 + private final TurnDao turnDao = new TurnDao("chess"); + private final BoardDao boardDao = new BoardDao("chess"); + public ChessController(final InputView inputView, final OutputView outputView) { this.inputView = inputView; @@ -43,20 +48,21 @@ public void run() { gameStatus = play(gameStatus, chessGame); } - movingDao.remove("board"); + boardDao.remove(); + turnDao.remove(); movingDao.remove("turn"); if (gameStatus instanceof Quit) { // TODO instanceof 괜춘? return; } final BoardDto boardDto = BoardDto.from(new Board(chessGame.getBoard())); // TODO 형태 너무 이상 변경 필요 - movingDao.addBoard(boardDto); - movingDao.addTurn(chessGame.getCamp()); + boardDao.saveBoard(boardDto); + turnDao.addTurn(chessGame.getCamp()); } private ChessGame create() { - final BoardDto board = movingDao.findBoard(); - final TurnDto turn = movingDao.findTurn(); + final BoardDto board = boardDao.find(); + final TurnDto turn = turnDao.findTurn(); final Map pieces = board.pieces(); if (pieces.isEmpty() || turn == null) { diff --git a/src/main/java/db/BoardDao.java b/src/main/java/db/BoardDao.java new file mode 100644 index 00000000000..864df6c4d03 --- /dev/null +++ b/src/main/java/db/BoardDao.java @@ -0,0 +1,80 @@ +package db; + +import db.connection.DBConnectionUtil; +import db.dto.BoardDto; +import db.dto.PieceDto; +import db.dto.PositionDto; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +public class BoardDao { + + private final String database; + + public BoardDao(final String database) { + this.database = database; + } + + public void saveBoard(final BoardDto board) { + final Map pieces = board.pieces(); + + for (final Entry entry : pieces.entrySet()) { + savePosition(entry.getKey(), entry.getValue()); + } + } + + private void savePosition(final PositionDto position, final PieceDto piece) { + final var query = "INSERT INTO board VALUES(?, ?, ?)"; + try (final var connection = DBConnectionUtil.getConnection(database); + final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) + ) { + preparedStatement.setString(1, position.value()); + preparedStatement.setString(2, piece.type()); + preparedStatement.setString(3, piece.camp()); + + preparedStatement.executeUpdate(); + + } catch (final SQLException exception) { + throw new RuntimeException(exception); + } + } + + public BoardDto find() { + final var query = "SELECT * FROM board"; + + try (final var connection = DBConnectionUtil.getConnection(database); + final var preparedStatement = connection.prepareStatement(query)) { + + final var resultSet = preparedStatement.executeQuery(); + + final Map result = new HashMap<>(); + + while (resultSet.next()) { + final var position = new PositionDto(resultSet.getString("position")); + final var type = resultSet.getString("piece_type"); + final var camp = resultSet.getString("camp"); + final var piece = new PieceDto(type, camp); + + result.put(position, piece); + } + return new BoardDto(result); + } catch (final SQLException exception) { + throw new RuntimeException(exception); + } + } + + public void remove() { + final String query = "truncate table board"; + try (final var connection = DBConnectionUtil.getConnection(database); + final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) + ) { + preparedStatement.executeUpdate(); + + } catch (final SQLException exception) { + throw new RuntimeException(exception); + } + } +} diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 5e3b0a6b4f0..852e1f9239b 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -1,19 +1,11 @@ package db; import db.connection.DBConnectionUtil; -import db.dto.BoardDto; import db.dto.MovingDto; -import db.dto.PieceDto; -import db.dto.PositionDto; -import db.dto.TurnDto; import java.sql.SQLException; import java.sql.Statement; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import model.Camp; -public class MovingDao { // TODO 이름 수정 +public class MovingDao { private final String database; @@ -21,31 +13,6 @@ public MovingDao(final String database) { this.database = database; } - public void addBoard(final BoardDto board) { - final Map pieces = board.pieces(); - - for (final Entry entry : pieces.entrySet()) { - addPosition(entry.getKey(), entry.getValue()); - } - } - - private void addPosition(final PositionDto position, final PieceDto piece) { - final var query = "INSERT INTO board VALUES(?, ?, ?)"; - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) - ) { - preparedStatement.setString(1, position.value()); - preparedStatement.setString(2, piece.type()); - preparedStatement.setString(3, piece.camp()); - - preparedStatement.executeUpdate(); - - } catch (final SQLException exception) { - throw new RuntimeException(exception); - } - - } - public long addMoving(final MovingDto moving) { final var query = "INSERT INTO moving VALUES(?, ?, ?, ?, ?, ?)"; try (final var connection = DBConnectionUtil.getConnection(database); @@ -91,35 +58,9 @@ public MovingDto findByMovementId(final long movementId) { } catch (final SQLException exception) { throw new RuntimeException(exception); } - return null; } - public BoardDto findBoard() { - final var query = "SELECT * FROM board"; - - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query)) { - - final var resultSet = preparedStatement.executeQuery(); - - final Map result = new HashMap<>(); - - while (resultSet.next()) { - final var position = new PositionDto(resultSet.getString("position")); - final var type = resultSet.getString("piece_type"); - final var camp = resultSet.getString("camp"); - final var piece = new PieceDto(type, camp); - - result.put(position, piece); - - } - return new BoardDto(result); - } catch (final SQLException exception) { - throw new RuntimeException(exception); - } - } - public void remove(String table) { final String query = String.format("truncate table %s", table); try (final var connection = DBConnectionUtil.getConnection(database); @@ -131,34 +72,4 @@ public void remove(String table) { throw new RuntimeException(exception); } } - - public TurnDto findTurn() { - final String query = "SELECT * FROM turn"; - - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query)) { - final var resultSet = preparedStatement.executeQuery(); - - if (resultSet.next()) { - return new TurnDto(resultSet.getString("camp")); - } - return null; - - } catch (SQLException exception) { - throw new RuntimeException(exception); - } - } - - public void addTurn(final Camp camp) { - final String query = "INSERT INTO turn values(?)"; - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query) - ) { - preparedStatement.setString(1, camp.toString()); - preparedStatement.executeUpdate(); - - } catch (final SQLException exception) { - throw new RuntimeException(exception); - } - } } diff --git a/src/main/java/db/TurnDao.java b/src/main/java/db/TurnDao.java new file mode 100644 index 00000000000..74c8f707703 --- /dev/null +++ b/src/main/java/db/TurnDao.java @@ -0,0 +1,58 @@ +package db; + +import db.connection.DBConnectionUtil; +import db.dto.TurnDto; +import java.sql.SQLException; +import java.sql.Statement; +import model.Camp; + +public class TurnDao { + + private final String database; + + public TurnDao(final String database) { + this.database = database; + } + + public void addTurn(final Camp camp) { + final String query = "INSERT INTO turn values(?)"; + try (final var connection = DBConnectionUtil.getConnection(database); + final var preparedStatement = connection.prepareStatement(query) + ) { + preparedStatement.setString(1, camp.toString()); + preparedStatement.executeUpdate(); + + } catch (final SQLException exception) { + throw new RuntimeException(exception); + } + } + + public TurnDto findTurn() { + final String query = "SELECT * FROM turn"; + + try (final var connection = DBConnectionUtil.getConnection(database); + final var preparedStatement = connection.prepareStatement(query)) { + final var resultSet = preparedStatement.executeQuery(); + + if (resultSet.next()) { + return new TurnDto(resultSet.getString("camp")); + } + return null; + + } catch (SQLException exception) { + throw new RuntimeException(exception); + } + } + + public void remove() { + final String query = "truncate table turn"; + try (final var connection = DBConnectionUtil.getConnection(database); + final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) + ) { + preparedStatement.executeUpdate(); + + } catch (final SQLException exception) { + throw new RuntimeException(exception); + } + } +} diff --git a/src/test/java/db/BoardDaoTest.java b/src/test/java/db/BoardDaoTest.java new file mode 100644 index 00000000000..7ca02c67dd1 --- /dev/null +++ b/src/test/java/db/BoardDaoTest.java @@ -0,0 +1,33 @@ +package db; + +import static org.assertj.core.api.Assertions.assertThat; + +import db.dto.BoardDto; +import model.Board; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class BoardDaoTest { + + private final BoardDao boardDao = new BoardDao("chess_test"); + + @BeforeEach + void beforeEach() { + boardDao.remove(); + } + + @Test + @DisplayName("보드 저장 확인") + void addBoard() { + //given + final var board = BoardDto.from(Board.create()); + + //when + boardDao.saveBoard(board); + final BoardDto findBoard = boardDao.find(); + + //then + assertThat(board).isEqualTo(findBoard); + } +} diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java index 5d744a76db5..598ef785eed 100644 --- a/src/test/java/db/MovingDaoTest.java +++ b/src/test/java/db/MovingDaoTest.java @@ -3,11 +3,8 @@ import static org.assertj.core.api.Assertions.assertThat; import db.connection.DBConnectionUtil; -import db.dto.BoardDto; import db.dto.MovingDto; import java.sql.SQLException; -import model.Board; -import model.Camp; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -19,8 +16,6 @@ class MovingDaoTest { @BeforeEach void beforeEach() { movingDao.remove("moving"); - movingDao.remove("turn"); - movingDao.remove("board"); } @DisplayName("데이터베이스 접속 확인") @@ -39,25 +34,4 @@ void addMoving() { assertThat(movingDao.findByMovementId(id)).isEqualTo(moving); } - - @Test - @DisplayName("보드 저장 확인") - void addBoard() { - //given - final var board = BoardDto.from(Board.create()); - - //when - movingDao.addBoard(board); - final BoardDto findBoard = movingDao.findBoard(); - - //then - assertThat(board).isEqualTo(findBoard); - } - - @Test - @DisplayName("턴 저장 확인") - void addTurn() { - movingDao.addTurn(Camp.BLACK); - - } } diff --git a/src/test/java/db/TurnDaoTest.java b/src/test/java/db/TurnDaoTest.java new file mode 100644 index 00000000000..e16d1c252a8 --- /dev/null +++ b/src/test/java/db/TurnDaoTest.java @@ -0,0 +1,33 @@ +package db; + +import static org.assertj.core.api.Assertions.assertThat; + +import db.dto.TurnDto; +import model.Camp; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TurnDaoTest { + + private final TurnDao turnDao = new TurnDao("chess_test"); + + @BeforeEach + void beforeEach() { + turnDao.remove(); + } + + @DisplayName("턴 저장 확인") + @Test + void saveTurn() { + //given + turnDao.addTurn(Camp.WHITE); + final TurnDto expected = new TurnDto("WHITE"); + + //when + final TurnDto turn = turnDao.findTurn(); + + //then + assertThat(turn).isEqualTo(expected); + } +} From 5e2041eb9e871663ff8b70864bb5ba3ac5cb5aff Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 30 Mar 2024 23:58:35 +0900 Subject: [PATCH 27/60] =?UTF-8?q?refactor:=20whitePawn,=20blackPawn=20?= =?UTF-8?q?=EB=82=98=EB=88=A0=20=EC=B2=98=EB=A6=AC=ED=95=98=EB=8D=98?= =?UTF-8?q?=EA=B1=B0=20Pawn=EC=9C=BC=EB=A1=9C=20=EB=AC=B6=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/dto/PieceType.java | 9 +++------ src/main/java/model/PieceScore.java | 9 +++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/main/java/db/dto/PieceType.java b/src/main/java/db/dto/PieceType.java index 0f2a4bb2b4a..dfa9aa25583 100644 --- a/src/main/java/db/dto/PieceType.java +++ b/src/main/java/db/dto/PieceType.java @@ -1,15 +1,13 @@ package db.dto; import java.util.Arrays; -import java.util.Objects; import model.piece.Bishop; -import model.piece.BlackPawn; import model.piece.King; import model.piece.Knight; +import model.piece.Pawn; import model.piece.Piece; import model.piece.Queen; import model.piece.Rook; -import model.piece.WhitePawn; public enum PieceType { KING(King.class, "King"), @@ -17,8 +15,7 @@ public enum PieceType { ROOK(Rook.class, "Rook"), BISHOP(Bishop.class, "Bishop"), KNIGHT(Knight.class, "Knight"), - WHITE_PAWN(WhitePawn.class, "Pawn"), - BLACK_PAWN(BlackPawn.class, "Pawn"); + PAWN(Pawn.class, "Pawn"); private final Class clazz; private final String value; @@ -30,7 +27,7 @@ public enum PieceType { public static String findValue(final Piece piece) { return Arrays.stream(values()) - .filter(pieceType -> Objects.equals(pieceType.clazz, piece.getClass())) + .filter(pieceType -> pieceType.clazz.isInstance(piece)) .findFirst() .orElseThrow() .value; diff --git a/src/main/java/model/PieceScore.java b/src/main/java/model/PieceScore.java index 5d9963e380f..5fa3a51e0b0 100644 --- a/src/main/java/model/PieceScore.java +++ b/src/main/java/model/PieceScore.java @@ -1,15 +1,13 @@ package model; import java.util.Arrays; -import java.util.Objects; import model.piece.Bishop; -import model.piece.BlackPawn; import model.piece.King; import model.piece.Knight; +import model.piece.Pawn; import model.piece.Piece; import model.piece.Queen; import model.piece.Rook; -import model.piece.WhitePawn; public enum PieceScore { @@ -18,8 +16,7 @@ public enum PieceScore { ROOK(Rook.class, new Score(5)), BISHOP(Bishop.class, new Score(3)), KNIGHT(Knight.class, new Score(2.5F)), - WHITE_PAWN(WhitePawn.class, new Score(1)), //TODO whitePawn, blackPawn 나누지 않는 방법고민 - BLACK_PAWN(BlackPawn.class, new Score(1)); + PAWN(Pawn.class, new Score(1)); private final Class clazz; private final Score score; @@ -31,7 +28,7 @@ public enum PieceScore { public static Score getScore(final Piece piece) { return Arrays.stream(values()) - .filter(pieceScore -> Objects.equals(pieceScore.clazz, piece.getClass())) + .filter(pieceScore -> (pieceScore.clazz.isInstance(piece))) .map(pieceScore -> pieceScore.score) .findFirst() .orElseThrow(); From bd740d221ed187b6b8727ea2ffe6f049e0190d12 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 01:01:04 +0900 Subject: [PATCH 28/60] =?UTF-8?q?refactor:=20dao=EB=93=A4=EC=9D=84=20?= =?UTF-8?q?=ED=95=98=EB=82=98=EC=9D=98=20Repository=EB=A1=9C=20=EB=AC=B6?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 39 ++++++--------- src/main/java/db/MovingDao.java | 4 +- src/main/java/db/Repository.java | 50 +++++++++++++++++++ src/main/java/db/TurnDao.java | 2 +- src/test/java/db/MovingDaoTest.java | 2 +- src/test/java/db/TurnDaoTest.java | 2 +- 6 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 src/main/java/db/Repository.java diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 64e5d107efe..13bb89bc7e7 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -1,18 +1,13 @@ package controller; -import db.BoardDao; -import db.MovingDao; -import db.TurnDao; +import db.Repository; import db.dto.BoardDto; import db.dto.MovingDto; -import db.dto.PieceDto; -import db.dto.PositionDto; import db.dto.TurnDto; import dto.ChessBoardDto; import dto.ScoreDto; import exception.CustomException; import java.util.List; -import java.util.Map; import model.Board; import model.Camp; import model.ChessGame; @@ -27,10 +22,7 @@ public class ChessController { private final InputView inputView; private final OutputView outputView; - private final MovingDao movingDao = new MovingDao("chess"); // TODO 중구난방쓰. dao 모으기 - private final TurnDao turnDao = new TurnDao("chess"); - private final BoardDao boardDao = new BoardDao("chess"); - + private final Repository repository = new Repository("chess"); public ChessController(final InputView inputView, final OutputView outputView) { this.inputView = inputView; @@ -48,27 +40,28 @@ public void run() { gameStatus = play(gameStatus, chessGame); } - boardDao.remove(); - turnDao.remove(); - movingDao.remove("turn"); + repository.remove(); if (gameStatus instanceof Quit) { // TODO instanceof 괜춘? + repository.removeMoving(); return; } + if (gameStatus.isCheck()) { + repository.removeMoving(); + return; // TODO 체크로 게임이 끝났을때 어떻게 처리할까 + } final BoardDto boardDto = BoardDto.from(new Board(chessGame.getBoard())); // TODO 형태 너무 이상 변경 필요 - boardDao.saveBoard(boardDto); - turnDao.addTurn(chessGame.getCamp()); + repository.save(boardDto, chessGame.getCamp()); } private ChessGame create() { - final BoardDto board = boardDao.find(); - final TurnDto turn = turnDao.findTurn(); - final Map pieces = board.pieces(); - - if (pieces.isEmpty() || turn == null) { - return ChessGame.setupStartingPosition(); + if (repository.hasGame()) { + final BoardDto board = repository.findBoard(); + final TurnDto turn = repository.findTurn(); + return new ChessGame(board.convert(), turn.convert()); } - return new ChessGame(board.convert(), turn.convert()); + return ChessGame.setupStartingPosition(); + } private GameStatus initGame() { @@ -98,7 +91,7 @@ private void saveMoving(final ChessGame chessGame, final CommandLine commandLine final List body = commandLine.getBody(); final Camp camp = chessGame.getCamp(); final MovingDto movingDto = MovingDto.from(body, camp); - movingDao.addMoving(movingDto); + repository.saveMoving(movingDto); } } diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 852e1f9239b..4e6687f94f7 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -61,8 +61,8 @@ public MovingDto findByMovementId(final long movementId) { return null; } - public void remove(String table) { - final String query = String.format("truncate table %s", table); + public void remove() { + final String query = "truncate table moving"; try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java new file mode 100644 index 00000000000..44d2766f69d --- /dev/null +++ b/src/main/java/db/Repository.java @@ -0,0 +1,50 @@ +package db; + +import db.dto.BoardDto; +import db.dto.MovingDto; +import db.dto.TurnDto; +import model.Camp; + +public class Repository { + + // TODO 인터페이스로 변경하기 + private final MovingDao movingDao; + private final TurnDao turnDao; + private final BoardDao boardDao; + + public Repository(String database) { + this.movingDao = new MovingDao(database); + this.turnDao = new TurnDao(database); + this.boardDao = new BoardDao(database); + } + + public void remove() { + turnDao.remove(); + boardDao.remove(); + } + + public void removeMoving() { + movingDao.remove(); + } + + public boolean hasGame() { + return turnDao.findTurn() != null; + } + + public void save(final BoardDto board, final Camp camp) { + boardDao.saveBoard(board); + turnDao.saveTurn(camp); + } + + public void saveMoving(final MovingDto moving) { + movingDao.addMoving(moving); + } + + public BoardDto findBoard() { + return boardDao.find(); + } + + public TurnDto findTurn() { + return turnDao.findTurn(); + } +} diff --git a/src/main/java/db/TurnDao.java b/src/main/java/db/TurnDao.java index 74c8f707703..ffb4a1e6aa1 100644 --- a/src/main/java/db/TurnDao.java +++ b/src/main/java/db/TurnDao.java @@ -14,7 +14,7 @@ public TurnDao(final String database) { this.database = database; } - public void addTurn(final Camp camp) { + public void saveTurn(final Camp camp) { final String query = "INSERT INTO turn values(?)"; try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query) diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java index 598ef785eed..5a2b8782769 100644 --- a/src/test/java/db/MovingDaoTest.java +++ b/src/test/java/db/MovingDaoTest.java @@ -15,7 +15,7 @@ class MovingDaoTest { @BeforeEach void beforeEach() { - movingDao.remove("moving"); + movingDao.remove(); } @DisplayName("데이터베이스 접속 확인") diff --git a/src/test/java/db/TurnDaoTest.java b/src/test/java/db/TurnDaoTest.java index e16d1c252a8..d5466835dd8 100644 --- a/src/test/java/db/TurnDaoTest.java +++ b/src/test/java/db/TurnDaoTest.java @@ -21,7 +21,7 @@ void beforeEach() { @Test void saveTurn() { //given - turnDao.addTurn(Camp.WHITE); + turnDao.saveTurn(Camp.WHITE); final TurnDto expected = new TurnDto("WHITE"); //when From cb14f6d94e2d99550e1b9fdedd11905621815f1d Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 14:34:12 +0900 Subject: [PATCH 29/60] =?UTF-8?q?feat:=20SQLException=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=EC=98=88=EC=99=B8=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/application/Application.java | 7 ++++++- src/main/java/constant/ErrorCode.java | 5 ++++- src/main/java/db/BoardDao.java | 10 ++++++---- src/main/java/db/MovingDao.java | 14 +++++++------- src/main/java/db/Repository.java | 2 +- src/main/java/db/TurnDao.java | 12 ++++++------ .../java/db/connection/DBConnectionUtil.java | 7 +++---- .../java/db/exception/ConnectionException.java | 10 ++++++++++ src/main/java/db/exception/DBException.java | 16 ++++++++++++++++ src/main/java/db/exception/DaoException.java | 10 ++++++++++ src/main/java/view/message/ErrorCodeMessage.java | 5 +++++ 11 files changed, 74 insertions(+), 24 deletions(-) create mode 100644 src/main/java/db/exception/ConnectionException.java create mode 100644 src/main/java/db/exception/DBException.java create mode 100644 src/main/java/db/exception/DaoException.java diff --git a/src/main/java/application/Application.java b/src/main/java/application/Application.java index 4bf227f5dfb..baf4c8e727d 100644 --- a/src/main/java/application/Application.java +++ b/src/main/java/application/Application.java @@ -1,6 +1,7 @@ package application; import controller.ChessController; +import db.exception.DBException; import java.util.Scanner; import view.InputView; import view.OutputView; @@ -12,6 +13,10 @@ public static void main(String[] args) { final OutputView outputView = new OutputView(); final ChessController chessController = new ChessController(inputView, outputView); - chessController.run(); + try { + chessController.run(); + } catch (DBException exception) { + outputView.printException(exception.getErrorCode()); + } } } diff --git a/src/main/java/constant/ErrorCode.java b/src/main/java/constant/ErrorCode.java index 20c19a3485c..532192307dd 100644 --- a/src/main/java/constant/ErrorCode.java +++ b/src/main/java/constant/ErrorCode.java @@ -17,6 +17,9 @@ public enum ErrorCode { PIECE_DOES_NOT_EXIST_POSITION, INVALID_CAMP_PIECE, KING_DEAD, + CONNECTION, + FAIL_SAVE, + FAIL_FIND, + FAIL_DELETE, NO_MESSAGE - } diff --git a/src/main/java/db/BoardDao.java b/src/main/java/db/BoardDao.java index 864df6c4d03..f40863a48e7 100644 --- a/src/main/java/db/BoardDao.java +++ b/src/main/java/db/BoardDao.java @@ -1,9 +1,11 @@ package db; +import constant.ErrorCode; import db.connection.DBConnectionUtil; import db.dto.BoardDto; import db.dto.PieceDto; import db.dto.PositionDto; +import db.exception.DaoException; import java.sql.SQLException; import java.sql.Statement; import java.util.HashMap; @@ -38,7 +40,7 @@ private void savePosition(final PositionDto position, final PieceDto piece) { preparedStatement.executeUpdate(); } catch (final SQLException exception) { - throw new RuntimeException(exception); + throw new DaoException(ErrorCode.FAIL_SAVE); } } @@ -62,19 +64,19 @@ public BoardDto find() { } return new BoardDto(result); } catch (final SQLException exception) { - throw new RuntimeException(exception); + throw new DaoException(ErrorCode.FAIL_FIND); } } public void remove() { - final String query = "truncate table board"; + final String query = "TRUNCATE TABLE board"; try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { preparedStatement.executeUpdate(); } catch (final SQLException exception) { - throw new RuntimeException(exception); + throw new DaoException(ErrorCode.FAIL_DELETE); } } } diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 4e6687f94f7..dfc9e5ee56a 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -1,7 +1,9 @@ package db; +import constant.ErrorCode; import db.connection.DBConnectionUtil; import db.dto.MovingDto; +import db.exception.DaoException; import java.sql.SQLException; import java.sql.Statement; @@ -34,7 +36,7 @@ public long addMoving(final MovingDto moving) { } return autoIncrement; } catch (final SQLException exception) { - throw new RuntimeException(exception); + throw new DaoException(ErrorCode.FAIL_SAVE); } } @@ -43,7 +45,6 @@ public MovingDto findByMovementId(final long movementId) { try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query)) { preparedStatement.setLong(1, movementId); - final var resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { @@ -55,21 +56,20 @@ public MovingDto findByMovementId(final long movementId) { resultSet.getString("destination_file") ); } + return null; // TODO 없으면 어카지? } catch (final SQLException exception) { - throw new RuntimeException(exception); + throw new DaoException(ErrorCode.FAIL_FIND); } - return null; } public void remove() { - final String query = "truncate table moving"; + final String query = "TRUNCATE TABLE moving"; try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { preparedStatement.executeUpdate(); - } catch (final SQLException exception) { - throw new RuntimeException(exception); + throw new DaoException(ErrorCode.FAIL_DELETE); } } } diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java index 44d2766f69d..607ad26fbbd 100644 --- a/src/main/java/db/Repository.java +++ b/src/main/java/db/Repository.java @@ -12,7 +12,7 @@ public class Repository { private final TurnDao turnDao; private final BoardDao boardDao; - public Repository(String database) { + public Repository(final String database) { this.movingDao = new MovingDao(database); this.turnDao = new TurnDao(database); this.boardDao = new BoardDao(database); diff --git a/src/main/java/db/TurnDao.java b/src/main/java/db/TurnDao.java index ffb4a1e6aa1..803a43d54e6 100644 --- a/src/main/java/db/TurnDao.java +++ b/src/main/java/db/TurnDao.java @@ -1,7 +1,9 @@ package db; +import constant.ErrorCode; import db.connection.DBConnectionUtil; import db.dto.TurnDto; +import db.exception.DaoException; import java.sql.SQLException; import java.sql.Statement; import model.Camp; @@ -21,9 +23,8 @@ public void saveTurn(final Camp camp) { ) { preparedStatement.setString(1, camp.toString()); preparedStatement.executeUpdate(); - } catch (final SQLException exception) { - throw new RuntimeException(exception); + throw new DaoException(ErrorCode.FAIL_SAVE); } } @@ -40,19 +41,18 @@ public TurnDto findTurn() { return null; } catch (SQLException exception) { - throw new RuntimeException(exception); + throw new DaoException(ErrorCode.FAIL_FIND); } } public void remove() { - final String query = "truncate table turn"; + final String query = "TRUNCATE TABLE turn"; try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { preparedStatement.executeUpdate(); - } catch (final SQLException exception) { - throw new RuntimeException(exception); + throw new DaoException(ErrorCode.FAIL_DELETE); } } } diff --git a/src/main/java/db/connection/DBConnectionUtil.java b/src/main/java/db/connection/DBConnectionUtil.java index abc585b438b..15af2d34b10 100644 --- a/src/main/java/db/connection/DBConnectionUtil.java +++ b/src/main/java/db/connection/DBConnectionUtil.java @@ -5,6 +5,8 @@ import static db.connection.ConnectionConst.SERVER; import static db.connection.ConnectionConst.USERNAME; +import constant.ErrorCode; +import db.exception.ConnectionException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; @@ -15,14 +17,11 @@ private DBConnectionUtil() { } public static Connection getConnection(final String database) { - // 드라이버 연결 try { final String url = "jdbc:mysql://" + SERVER.getValue() + "/" + database + OPTION.getValue(); return DriverManager.getConnection(url, USERNAME.getValue(), PASSWORD.getValue()); } catch (final SQLException exception) { - System.err.println("DB 연결 오류:" + exception.getMessage()); - exception.printStackTrace(); - return null; // TODO null 을 리턴하는게 맞을지 예외 던지는게 맞을지 + throw new ConnectionException(ErrorCode.CONNECTION); } } } diff --git a/src/main/java/db/exception/ConnectionException.java b/src/main/java/db/exception/ConnectionException.java new file mode 100644 index 00000000000..65a6022feb3 --- /dev/null +++ b/src/main/java/db/exception/ConnectionException.java @@ -0,0 +1,10 @@ +package db.exception; + +import constant.ErrorCode; + +public class ConnectionException extends DBException { + + public ConnectionException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/db/exception/DBException.java b/src/main/java/db/exception/DBException.java new file mode 100644 index 00000000000..34bc84e2ae9 --- /dev/null +++ b/src/main/java/db/exception/DBException.java @@ -0,0 +1,16 @@ +package db.exception; + +import constant.ErrorCode; + +public class DBException extends RuntimeException { + + private final ErrorCode errorCode; + + public DBException(final ErrorCode errorCode) { + this.errorCode = errorCode; + } + + public ErrorCode getErrorCode() { + return errorCode; + } +} diff --git a/src/main/java/db/exception/DaoException.java b/src/main/java/db/exception/DaoException.java new file mode 100644 index 00000000000..0834c5d54fd --- /dev/null +++ b/src/main/java/db/exception/DaoException.java @@ -0,0 +1,10 @@ +package db.exception; + +import constant.ErrorCode; + +public class DaoException extends DBException { + + public DaoException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/view/message/ErrorCodeMessage.java b/src/main/java/view/message/ErrorCodeMessage.java index 55c2781b473..44d5d3934ff 100644 --- a/src/main/java/view/message/ErrorCodeMessage.java +++ b/src/main/java/view/message/ErrorCodeMessage.java @@ -23,6 +23,11 @@ public enum ErrorCodeMessage { OWN_PIECE_EXIST_POSITION(ErrorCode.OWN_PIECE_EXIST_POSITION, "도착 위치에 자신의 기물이 있습니다."), PIECE_DOES_NOT_EXIST_POSITION(ErrorCode.PIECE_DOES_NOT_EXIST_POSITION, "해당 위치에 기물이 없습니다."), INVALID_CAMP_PIECE(ErrorCode.INVALID_CAMP_PIECE, "자신의 기물만 움직일 수 있습니다."), + CONNECTION(ErrorCode.CONNECTION, "DB 연결 오류 입니다."), + FAIL_SAVE(ErrorCode.FAIL_SAVE, "저장에 실패하였습니다."), + FAIL_FIND(ErrorCode.FAIL_FIND, "게임을 불러오는데 실패하였습니다."), + FAIL_DELETE(ErrorCode.FAIL_DELETE, "게임을 삭제하는데 실패하였습니다."), + NO_MESSAGE(ErrorCode.NO_MESSAGE, "해당 메시지가 없습니다."); private static final Map SUIT_MESSAGE = Arrays.stream(values()) From a306cb25e761d1498c581c02d532e9d6323339ad Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 14:53:57 +0900 Subject: [PATCH 30/60] =?UTF-8?q?refactor:=20moving=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/MovingDao.java | 14 +++++--------- src/main/java/db/dto/MovingDto.java | 5 ++--- src/main/resources/moving.sql | 10 ++++------ src/test/resources/moving.sql | 10 ++++------ 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index dfc9e5ee56a..ca87803d053 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -16,17 +16,15 @@ public MovingDao(final String database) { } public long addMoving(final MovingDto moving) { - final var query = "INSERT INTO moving VALUES(?, ?, ?, ?, ?, ?)"; + final var query = "INSERT INTO moving VALUES(?, ?, ?, ?)"; try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { long autoIncrement = 0; preparedStatement.setNull(1, 0); preparedStatement.setString(2, moving.camp()); - preparedStatement.setString(3, moving.currentFile()); - preparedStatement.setString(4, moving.currentRank()); - preparedStatement.setString(5, moving.nextFile()); - preparedStatement.setString(6, moving.nextRank()); + preparedStatement.setString(3, moving.current()); + preparedStatement.setString(4, moving.next()); preparedStatement.executeUpdate(); final var generatedKeys = preparedStatement.getGeneratedKeys(); @@ -50,10 +48,8 @@ public MovingDto findByMovementId(final long movementId) { if (resultSet.next()) { return new MovingDto( resultSet.getString("camp"), - resultSet.getString("start_rank"), - resultSet.getString("start_file"), - resultSet.getString("destination_rank"), - resultSet.getString("destination_file") + resultSet.getString("start"), + resultSet.getString("destination") ); } return null; // TODO 없으면 어카지? diff --git a/src/main/java/db/dto/MovingDto.java b/src/main/java/db/dto/MovingDto.java index c02bb920bfe..3fdf57606c9 100644 --- a/src/main/java/db/dto/MovingDto.java +++ b/src/main/java/db/dto/MovingDto.java @@ -3,13 +3,12 @@ import java.util.List; import model.Camp; -public record MovingDto(String camp, String currentFile, String currentRank, String nextFile, String nextRank) { +public record MovingDto(String camp, String current, String next) { public static MovingDto from(final List moving, final Camp camp) { final String current = moving.get(0); final String next = moving.get(1); - return new MovingDto(camp.toString(), String.valueOf(current.charAt(0)), String.valueOf(current.charAt(1)), - String.valueOf(next.charAt(0)), String.valueOf(next.charAt(1))); + return new MovingDto(camp.toString(), current, next); } } diff --git a/src/main/resources/moving.sql b/src/main/resources/moving.sql index 5bc04711949..2a17b4f8f3f 100644 --- a/src/main/resources/moving.sql +++ b/src/main/resources/moving.sql @@ -4,10 +4,8 @@ drop table if exists moving; create table moving ( - movement_id INT primary key auto_increment, - camp varchar(5) not null, - start_rank varchar(12) not null, - start_file varchar(12) not null, - destination_rank varchar(12) not null, - destination_file varchar(12) not null + movement_id INT primary key auto_increment, + camp varchar(5) not null, + start varchar(2) not null, + destination varchar(2) not null ); diff --git a/src/test/resources/moving.sql b/src/test/resources/moving.sql index 60737d06138..f0f01e270af 100644 --- a/src/test/resources/moving.sql +++ b/src/test/resources/moving.sql @@ -4,10 +4,8 @@ drop table if exists moving; create table moving ( - movement_id INT primary key auto_increment, - camp varchar(5) not null, - start_rank varchar(12) not null, - start_file varchar(12) not null, - destination_rank varchar(12) not null, - destination_file varchar(12) not null + movement_id INT primary key auto_increment, + camp varchar(5) not null, + start varchar(2) not null, + destination varchar(2) not null ); From a4de87d66092b0279762d94ffd5f4f485ec94609 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 14:54:29 +0900 Subject: [PATCH 31/60] =?UTF-8?q?fix:=20=EA=B8=B0=EB=B3=B4=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=EC=8B=9C=20=EC=88=9C=EC=84=9C=20=EB=B0=94=EB=80=8C?= =?UTF-8?q?=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 13bb89bc7e7..ab8b9633a71 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -89,7 +89,7 @@ private GameStatus play(final GameStatus preStatus, final ChessGame chessGame) { private void saveMoving(final ChessGame chessGame, final CommandLine commandLine) { if (commandLine.isMove()) { final List body = commandLine.getBody(); - final Camp camp = chessGame.getCamp(); + final Camp camp = chessGame.getCamp().toggle(); final MovingDto movingDto = MovingDto.from(body, camp); repository.saveMoving(movingDto); } From d75190f7778161068aee65fa5b3301ce1d8c4fd5 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 16:39:32 +0900 Subject: [PATCH 32/60] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EB=8F=84?= =?UTF-8?q?=EC=A4=91=EC=97=90=20=EC=A2=85=EB=A3=8C=EC=8B=9C=EC=97=90?= =?UTF-8?q?=EB=8F=84=20=EC=A7=84=ED=96=89=20=EC=83=81=ED=99=A9=20=EB=B3=B5?= =?UTF-8?q?=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 9 ++--- src/main/java/db/BoardDao.java | 5 ++- src/main/java/db/MovingDao.java | 23 ++++++++++- src/main/java/db/Repository.java | 39 +++++++++++++++++-- src/main/java/db/TurnDao.java | 11 ++++-- src/main/java/db/dto/TurnDto.java | 2 +- src/main/java/model/ChessGame.java | 20 +++++----- src/main/java/model/GameTurn.java | 36 +++++++++++++++++ src/main/java/model/Turn.java | 7 ++++ src/main/resources/turn.sql | 3 +- src/test/java/db/MovingDaoTest.java | 2 +- src/test/java/db/TurnDaoTest.java | 5 ++- src/test/resources/turn.sql | 3 +- 13 files changed, 135 insertions(+), 30 deletions(-) create mode 100644 src/main/java/model/GameTurn.java create mode 100644 src/main/java/model/Turn.java diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index ab8b9633a71..a7de87ad8ad 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -3,7 +3,6 @@ import db.Repository; import db.dto.BoardDto; import db.dto.MovingDto; -import db.dto.TurnDto; import dto.ChessBoardDto; import dto.ScoreDto; import exception.CustomException; @@ -31,8 +30,8 @@ public ChessController(final InputView inputView, final OutputView outputView) { public void run() { - final ChessGame chessGame = create(); outputView.printStartMessage(); + final ChessGame chessGame = create(); GameStatus gameStatus = initGame(); outputView.printChessBoard(ChessBoardDto.from(chessGame)); @@ -51,14 +50,12 @@ public void run() { return; // TODO 체크로 게임이 끝났을때 어떻게 처리할까 } final BoardDto boardDto = BoardDto.from(new Board(chessGame.getBoard())); // TODO 형태 너무 이상 변경 필요 - repository.save(boardDto, chessGame.getCamp()); + repository.save(boardDto, chessGame.getCamp(), chessGame.getTurn()); } private ChessGame create() { if (repository.hasGame()) { - final BoardDto board = repository.findBoard(); - final TurnDto turn = repository.findTurn(); - return new ChessGame(board.convert(), turn.convert()); + return repository.findGame(); } return ChessGame.setupStartingPosition(); diff --git a/src/main/java/db/BoardDao.java b/src/main/java/db/BoardDao.java index f40863a48e7..6b8fdd09cfd 100644 --- a/src/main/java/db/BoardDao.java +++ b/src/main/java/db/BoardDao.java @@ -11,6 +11,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import model.Board; public class BoardDao { @@ -59,9 +60,11 @@ public BoardDto find() { final var type = resultSet.getString("piece_type"); final var camp = resultSet.getString("camp"); final var piece = new PieceDto(type, camp); - result.put(position, piece); } + if (result.isEmpty()) { + return BoardDto.from(Board.create()); + } return new BoardDto(result); } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_FIND); diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index ca87803d053..7f237317563 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -6,6 +6,8 @@ import db.exception.DaoException; import java.sql.SQLException; import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; public class MovingDao { @@ -38,8 +40,27 @@ public long addMoving(final MovingDto moving) { } } + public List findAll() { + final String query = "SELECT * FROM moving"; + final List moving = new ArrayList<>(); + try (final var connection = DBConnectionUtil.getConnection(database); + final var preparedStatement = connection.prepareStatement(query)) { + + final var resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + var camp = resultSet.getString("camp"); + var current = resultSet.getString("start"); + var next = resultSet.getString("destination"); + moving.add(new MovingDto(camp, current, next)); + } + return moving; + } catch (SQLException exception) { + throw new DaoException(ErrorCode.FAIL_FIND); + } + } + public MovingDto findByMovementId(final long movementId) { - final var query = "SELECT * FROM moving WHERE movement_id = ?"; + final String query = "SELECT * FROM moving WHERE movement_id = ?"; try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query)) { preparedStatement.setLong(1, movementId); diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java index 607ad26fbbd..c338fe3655e 100644 --- a/src/main/java/db/Repository.java +++ b/src/main/java/db/Repository.java @@ -3,7 +3,14 @@ import db.dto.BoardDto; import db.dto.MovingDto; import db.dto.TurnDto; +import java.util.List; +import model.Board; import model.Camp; +import model.ChessGame; +import model.GameTurn; +import model.Turn; +import model.position.Moving; +import model.position.Position; public class Repository { @@ -28,12 +35,14 @@ public void removeMoving() { } public boolean hasGame() { - return turnDao.findTurn() != null; + return movingDao.findByMovementId(1) != null; } - public void save(final BoardDto board, final Camp camp) { + public void save(final BoardDto board, final Camp camp, final Turn turn) { + remove(); + boardDao.saveBoard(board); - turnDao.saveTurn(camp); + turnDao.saveTurn(camp, turn); } public void saveMoving(final MovingDto moving) { @@ -47,4 +56,28 @@ public BoardDto findBoard() { public TurnDto findTurn() { return turnDao.findTurn(); } + + public ChessGame findGame() { + System.out.println("찾자 "); + final BoardDto findBoard = findBoard(); + final TurnDto findTurn = findTurn(); + final List findMoving = movingDao.findAll(); + final Board board = findBoard.convert(); + + if (findTurn.count() < findMoving.size()) { + restore(findTurn, findMoving, board); + } + //TODO 리팩터링 + final Camp camp = findMoving.size() % 2 == 0 ? Camp.WHITE : Camp.BLACK; + final GameTurn gameTurn = new GameTurn(camp, new Turn(findMoving.size())); + return new ChessGame(board, gameTurn); + } + + private void restore(final TurnDto findTurn, final List findMoving, final Board board) { + for (int i = findTurn.count(); i < findMoving.size(); i++) { + final MovingDto movingDto = findMoving.get(i); + final Moving moving = new Moving(Position.from(movingDto.current()), Position.from(movingDto.next())); + board.move(moving); + } + } } diff --git a/src/main/java/db/TurnDao.java b/src/main/java/db/TurnDao.java index 803a43d54e6..25268e65ad0 100644 --- a/src/main/java/db/TurnDao.java +++ b/src/main/java/db/TurnDao.java @@ -7,6 +7,7 @@ import java.sql.SQLException; import java.sql.Statement; import model.Camp; +import model.Turn; public class TurnDao { @@ -16,12 +17,14 @@ public TurnDao(final String database) { this.database = database; } - public void saveTurn(final Camp camp) { - final String query = "INSERT INTO turn values(?)"; + public void saveTurn(final Camp camp, final Turn turn) { + final String query = "INSERT INTO turn values(?, ?)"; try (final var connection = DBConnectionUtil.getConnection(database); final var preparedStatement = connection.prepareStatement(query) ) { preparedStatement.setString(1, camp.toString()); + preparedStatement.setInt(2, turn.count()); + preparedStatement.executeUpdate(); } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_SAVE); @@ -36,9 +39,9 @@ public TurnDto findTurn() { final var resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { - return new TurnDto(resultSet.getString("camp")); + return new TurnDto(resultSet.getString("camp"), resultSet.getInt("count")); } - return null; + return new TurnDto("WHITE", 0); } catch (SQLException exception) { throw new DaoException(ErrorCode.FAIL_FIND); diff --git a/src/main/java/db/dto/TurnDto.java b/src/main/java/db/dto/TurnDto.java index 19e506eb509..d2ad9bd14f4 100644 --- a/src/main/java/db/dto/TurnDto.java +++ b/src/main/java/db/dto/TurnDto.java @@ -2,7 +2,7 @@ import model.Camp; -public record TurnDto(String value) { +public record TurnDto(String value, int count) { public Camp convert() { if ("WHITE".equals(value)) { diff --git a/src/main/java/model/ChessGame.java b/src/main/java/model/ChessGame.java index b18fe1cd84b..aea2647aa12 100644 --- a/src/main/java/model/ChessGame.java +++ b/src/main/java/model/ChessGame.java @@ -7,24 +7,22 @@ public class ChessGame { - private static final Camp STARTING_CAMP = Camp.WHITE; - private final Board board; - private Camp camp; + private final GameTurn gameTurn; - public ChessGame(final Board board, final Camp camp) { + public ChessGame(final Board board, final GameTurn gameTurn) { this.board = board; - this.camp = camp; + this.gameTurn = gameTurn; } public static ChessGame setupStartingPosition() { - return new ChessGame(Board.create(), STARTING_CAMP); + return new ChessGame(Board.create(), GameTurn.create()); } public void move(final Moving moving) { - board.validate(moving, camp); + board.validate(moving, getCamp()); board.move(moving); - camp = camp.toggle(); + gameTurn.progress(); } public Map getBoard() { @@ -32,7 +30,11 @@ public Map getBoard() { } public Camp getCamp() { - return camp; + return gameTurn.getCamp(); + } + + public Turn getTurn() { + return gameTurn.getTurn(); } public Score calculateScore(final Camp camp) { diff --git a/src/main/java/model/GameTurn.java b/src/main/java/model/GameTurn.java new file mode 100644 index 00000000000..32928eae461 --- /dev/null +++ b/src/main/java/model/GameTurn.java @@ -0,0 +1,36 @@ +package model; + +public class GameTurn { + + private static final Camp STARTING_CAMP = Camp.WHITE; + + private Camp camp; + private Turn turn; + + private GameTurn(final Camp camp) { + this.camp = camp; + this.turn = new Turn(0); + } + + public GameTurn(final Camp camp, final Turn turn) { + this.camp = camp; + this.turn = turn; + } + + public static GameTurn create() { + return new GameTurn(STARTING_CAMP); + } + + public void progress() { + camp = camp.toggle(); + turn = turn.take(); + } + + public Camp getCamp() { + return camp; + } + + public Turn getTurn() { + return turn; + } +} diff --git a/src/main/java/model/Turn.java b/src/main/java/model/Turn.java new file mode 100644 index 00000000000..179a2a85031 --- /dev/null +++ b/src/main/java/model/Turn.java @@ -0,0 +1,7 @@ +package model; + +public record Turn(int count) { + public Turn take() { + return new Turn(count + 1); + } +} diff --git a/src/main/resources/turn.sql b/src/main/resources/turn.sql index 7eb997d9974..cc2d391fd8e 100644 --- a/src/main/resources/turn.sql +++ b/src/main/resources/turn.sql @@ -4,5 +4,6 @@ drop table if exists turn; create table turn ( - camp varchar(5) not null + camp varchar(5) not null, + count int not null ); diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java index 5a2b8782769..409b4f53cf7 100644 --- a/src/test/java/db/MovingDaoTest.java +++ b/src/test/java/db/MovingDaoTest.java @@ -29,7 +29,7 @@ void connection() throws SQLException { @Test @DisplayName("이동 저장 확인") void addMoving() { - final var moving = new MovingDto("WHITE", "a", "2", "a", "3"); + final var moving = new MovingDto("WHITE", "a2", "a3"); final var id = movingDao.addMoving(moving); assertThat(movingDao.findByMovementId(id)).isEqualTo(moving); diff --git a/src/test/java/db/TurnDaoTest.java b/src/test/java/db/TurnDaoTest.java index d5466835dd8..903087c35e9 100644 --- a/src/test/java/db/TurnDaoTest.java +++ b/src/test/java/db/TurnDaoTest.java @@ -4,6 +4,7 @@ import db.dto.TurnDto; import model.Camp; +import model.Turn; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -21,8 +22,8 @@ void beforeEach() { @Test void saveTurn() { //given - turnDao.saveTurn(Camp.WHITE); - final TurnDto expected = new TurnDto("WHITE"); + turnDao.saveTurn(Camp.WHITE, new Turn(1)); + final TurnDto expected = new TurnDto("WHITE", 1); //when final TurnDto turn = turnDao.findTurn(); diff --git a/src/test/resources/turn.sql b/src/test/resources/turn.sql index 2359a475c43..02ed0d1b3c7 100644 --- a/src/test/resources/turn.sql +++ b/src/test/resources/turn.sql @@ -4,5 +4,6 @@ drop table if exists turn; create table turn ( - camp varchar(5) not null + camp varchar(5) not null, + count int not null ); From a3574f68925a4f61d57b8e7738bdae7dad71ad7f Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 17:04:13 +0900 Subject: [PATCH 33/60] =?UTF-8?q?feat:=20id=EB=A1=9C=20=EA=B8=B0=EB=B3=B4?= =?UTF-8?q?=EB=A5=BC=20=EC=B0=BE=EC=9D=84=20=EB=95=8C=20=EC=97=86=EC=9C=BC?= =?UTF-8?q?=EB=A9=B4=20=EC=98=88=EC=99=B8=EA=B0=80=20=EB=B0=9C=EC=83=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/constant/ErrorCode.java | 1 - src/main/java/db/MovingDao.java | 2 +- src/test/java/db/MovingDaoTest.java | 21 ++++++++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/java/constant/ErrorCode.java b/src/main/java/constant/ErrorCode.java index 532192307dd..60a3d44b9f4 100644 --- a/src/main/java/constant/ErrorCode.java +++ b/src/main/java/constant/ErrorCode.java @@ -1,7 +1,6 @@ package constant; public enum ErrorCode { - //TODO: 같은 곳으로 이동할 때 예외 다르게 errorCode INVALID_INPUT, INVALID_STATUS, INVALID_COMMAND, diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 7f237317563..691b2027d03 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -73,7 +73,7 @@ public MovingDto findByMovementId(final long movementId) { resultSet.getString("destination") ); } - return null; // TODO 없으면 어카지? + throw new DaoException(ErrorCode.FAIL_FIND); } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_FIND); } diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java index 409b4f53cf7..2d0ef26dbc9 100644 --- a/src/test/java/db/MovingDaoTest.java +++ b/src/test/java/db/MovingDaoTest.java @@ -1,9 +1,12 @@ package db; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import db.connection.DBConnectionUtil; import db.dto.MovingDto; +import db.exception.DaoException; +import java.sql.Connection; import java.sql.SQLException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -21,7 +24,7 @@ void beforeEach() { @DisplayName("데이터베이스 접속 확인") @Test void connection() throws SQLException { - try (final var connection = DBConnectionUtil.getConnection("chess_test")) { + try (final Connection connection = DBConnectionUtil.getConnection("chess_test")) { assertThat(connection).isNotNull(); } } @@ -29,9 +32,21 @@ void connection() throws SQLException { @Test @DisplayName("이동 저장 확인") void addMoving() { - final var moving = new MovingDto("WHITE", "a2", "a3"); - final var id = movingDao.addMoving(moving); + final MovingDto moving = new MovingDto("WHITE", "a2", "a3"); + final long id = movingDao.addMoving(moving); assertThat(movingDao.findByMovementId(id)).isEqualTo(moving); } + + @Test + @DisplayName("찾고자 하는 기보가 없으면 예외가 발생한다.") + void failFindMoving() { + //given + movingDao.addMoving(new MovingDto("WHITE", "a2", "a3")); + movingDao.addMoving(new MovingDto("BLACK", "h7", "h6")); + + //when then + assertThatThrownBy(() -> movingDao.findByMovementId(3)) + .isInstanceOf(DaoException.class); + } } From 6b89a4592c7dc529ca7e077bfc3e487200266a7c Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 18:49:11 +0900 Subject: [PATCH 34/60] =?UTF-8?q?refactor:=20=ED=8F=B0=20=EC=A0=90?= =?UTF-8?q?=EC=88=98=20=EA=B4=80=EB=A0=A8=20=EB=A7=A4=EC=A7=81=20=EB=84=98?= =?UTF-8?q?=EB=B2=84=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/Board.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java index 64b483e45e8..0c39791d728 100644 --- a/src/main/java/model/Board.java +++ b/src/main/java/model/Board.java @@ -39,6 +39,7 @@ public class Board { File.G, Knight::new, File.H, Rook::new ); + private static final Score SAME_FILE_PAWN_SCORE = new Score(0.5F); private final Map pieces; @@ -156,8 +157,8 @@ private Map countSameFilePawn(final Camp camp) { private Score duplicateFilePawns(final Map count) { return count.values() .stream() - .filter(sameFilePawn -> sameFilePawn > 1) - .map(sameFilePawn -> new Score(sameFilePawn * 0.5F)) // TODO 0.5 상수화 + .filter(sameFilePawnCount -> sameFilePawnCount > 1) + .map(sameFilePawnCount -> new Score(sameFilePawnCount * SAME_FILE_PAWN_SCORE.value())) .reduce(Score::add) .orElse(new Score(0)); } From c00b9b9b76ae080508617d97a4492df144160c89 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 18:57:18 +0900 Subject: [PATCH 35/60] =?UTF-8?q?refactor:=20=EC=82=BC=ED=95=AD=20?= =?UTF-8?q?=EC=97=B0=EC=82=B0=EC=9E=90=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/Repository.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java index c338fe3655e..d65fb61f48f 100644 --- a/src/main/java/db/Repository.java +++ b/src/main/java/db/Repository.java @@ -40,7 +40,6 @@ public boolean hasGame() { public void save(final BoardDto board, final Camp camp, final Turn turn) { remove(); - boardDao.saveBoard(board); turnDao.saveTurn(camp, turn); } @@ -58,7 +57,6 @@ public TurnDto findTurn() { } public ChessGame findGame() { - System.out.println("찾자 "); final BoardDto findBoard = findBoard(); final TurnDto findTurn = findTurn(); final List findMoving = movingDao.findAll(); @@ -67,12 +65,18 @@ public ChessGame findGame() { if (findTurn.count() < findMoving.size()) { restore(findTurn, findMoving, board); } - //TODO 리팩터링 - final Camp camp = findMoving.size() % 2 == 0 ? Camp.WHITE : Camp.BLACK; + final Camp camp = findLastTurnCamp(findMoving); final GameTurn gameTurn = new GameTurn(camp, new Turn(findMoving.size())); return new ChessGame(board, gameTurn); } + private Camp findLastTurnCamp(final List findMoving) { + if (findMoving.size() % 2 == 0) { + return Camp.WHITE; + } + return Camp.BLACK; + } + private void restore(final TurnDto findTurn, final List findMoving, final Board board) { for (int i = findTurn.count(); i < findMoving.size(); i++) { final MovingDto movingDto = findMoving.get(i); From 15265d0ba307746920176d7c2e0de1fd859e522d Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 20:06:05 +0900 Subject: [PATCH 36/60] =?UTF-8?q?refactor:=20piece=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=B0=BE=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/dto/PieceDto.java | 30 ++---------------- src/main/java/db/dto/PieceType.java | 48 ++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 42 deletions(-) diff --git a/src/main/java/db/dto/PieceDto.java b/src/main/java/db/dto/PieceDto.java index fc81c1dd6af..cc06789cda7 100644 --- a/src/main/java/db/dto/PieceDto.java +++ b/src/main/java/db/dto/PieceDto.java @@ -1,43 +1,17 @@ package db.dto; import model.Camp; -import model.piece.Bishop; -import model.piece.BlackPawn; -import model.piece.King; -import model.piece.Knight; import model.piece.Piece; -import model.piece.Queen; -import model.piece.Rook; -import model.piece.WhitePawn; public record PieceDto(String type, String camp) { public static PieceDto from(final Piece piece) { - return new PieceDto(PieceType.findValue(piece), piece.getCamp().toString()); + return new PieceDto(PieceType.findValue(piece), piece.getCamp().name()); } - //TODO 요고 리팩터링 public Piece convert() { final Camp army = find(); - if ("King".equals(type)) { - return new King(army); - } - if ("Queen".equals(type)) { - return new Queen(army); - } - if ("Knight".equals(type)) { - return new Knight(army); - } - if ("Bishop".equals(type)) { - return new Bishop(army); - } - if ("Rook".equals(type)) { - return new Rook(army); - } - if ("Pawn".equals(type) && army == Camp.WHITE) { - return new WhitePawn(); - } - return new BlackPawn(); + return PieceType.findValue(army, type); } private Camp find() { diff --git a/src/main/java/db/dto/PieceType.java b/src/main/java/db/dto/PieceType.java index dfa9aa25583..76dc3412e25 100644 --- a/src/main/java/db/dto/PieceType.java +++ b/src/main/java/db/dto/PieceType.java @@ -1,35 +1,55 @@ package db.dto; import java.util.Arrays; +import model.Camp; import model.piece.Bishop; +import model.piece.BlackPawn; import model.piece.King; import model.piece.Knight; -import model.piece.Pawn; import model.piece.Piece; import model.piece.Queen; import model.piece.Rook; +import model.piece.WhitePawn; public enum PieceType { - KING(King.class, "King"), - QUEEN(Queen.class, "Queen"), - ROOK(Rook.class, "Rook"), - BISHOP(Bishop.class, "Bishop"), - KNIGHT(Knight.class, "Knight"), - PAWN(Pawn.class, "Pawn"); - private final Class clazz; - private final String value; + WHITE_KING(new King(Camp.WHITE), "King", Camp.WHITE), + WHITE_QUEEN(new Queen(Camp.WHITE), "Queen", Camp.WHITE), + WHITE_ROOK(new Rook(Camp.WHITE), "Rook", Camp.WHITE), + WHITE_BISHOP(new Bishop(Camp.WHITE), "Bishop", Camp.WHITE), + WHITE_KNIGHT(new Knight(Camp.WHITE), "Knight", Camp.WHITE), + WHITE_PAWN(new WhitePawn(), "Pawn", Camp.WHITE), + BLACK_KING(new King(Camp.BLACK), "King", Camp.BLACK), + BLACK_QUEEN(new Queen(Camp.BLACK), "Queen", Camp.BLACK), + BLACK_ROOK(new Rook(Camp.BLACK), "Rook", Camp.BLACK), + BLACK_BISHOP(new Bishop(Camp.BLACK), "Bishop", Camp.BLACK), + BLACK_KNIGHT(new Knight(Camp.BLACK), "Knight", Camp.BLACK), + BLACK_PAWN(new BlackPawn(), "Pawn", Camp.BLACK); - PieceType(final Class clazz, final String value) { - this.clazz = clazz; - this.value = value; + private final Piece piece; + private final String pieceName; + private final Camp camp; + + PieceType(final Piece piece, final String pieceName, final Camp camp) { + this.piece = piece; + this.pieceName = pieceName; + this.camp = camp; } public static String findValue(final Piece piece) { return Arrays.stream(values()) - .filter(pieceType -> pieceType.clazz.isInstance(piece)) + .filter(pieceType1 -> pieceType1.piece.getClass().isInstance(piece)) + .findFirst() + .orElseThrow() + .pieceName; + } + + public static Piece findValue(final Camp camp, final String type) { + return Arrays.stream(values()) + .filter(pieceType -> pieceType.pieceName.equals(type)) + .filter(pieceType -> pieceType.camp == camp) .findFirst() .orElseThrow() - .value; + .piece; } } From 44df19a8e98bdca29e6426ab5b1954ab94132ad6 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 20:06:36 +0900 Subject: [PATCH 37/60] =?UTF-8?q?fix:=20=EC=9D=B4=EC=A0=84=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=EC=9D=B4=20=EC=9E=88=EB=8A=94=EC=A7=80=20=EC=B0=BE?= =?UTF-8?q?=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/MovingDao.java | 14 ++++++++++++++ src/main/java/db/Repository.java | 2 +- src/test/java/db/MovingDaoTest.java | 6 ++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 691b2027d03..52ac89a0c88 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -59,6 +59,20 @@ public List findAll() { } } + public int countMoving() { + final String query = "SELECT count(*) AS count FROM moving"; + try (final var connection = DBConnectionUtil.getConnection(database); + final var preparedStatement = connection.prepareStatement(query)) { + final var resultSet = preparedStatement.executeQuery(); + if (resultSet.next()) { + return resultSet.getInt("count"); + } + throw new DaoException(ErrorCode.FAIL_FIND); + } catch (final SQLException exception) { + throw new DaoException(ErrorCode.FAIL_FIND); + } + } + public MovingDto findByMovementId(final long movementId) { final String query = "SELECT * FROM moving WHERE movement_id = ?"; try (final var connection = DBConnectionUtil.getConnection(database); diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java index d65fb61f48f..8811322b8e7 100644 --- a/src/main/java/db/Repository.java +++ b/src/main/java/db/Repository.java @@ -35,7 +35,7 @@ public void removeMoving() { } public boolean hasGame() { - return movingDao.findByMovementId(1) != null; + return movingDao.countMoving() > 0; } public void save(final BoardDto board, final Camp camp, final Turn turn) { diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java index 2d0ef26dbc9..b1ba7bf4ca6 100644 --- a/src/test/java/db/MovingDaoTest.java +++ b/src/test/java/db/MovingDaoTest.java @@ -49,4 +49,10 @@ void failFindMoving() { assertThatThrownBy(() -> movingDao.findByMovementId(3)) .isInstanceOf(DaoException.class); } + + @Test + @DisplayName("행의 개수를 파악한다.") + void count() { + assertThat(movingDao.countMoving()).isZero(); + } } From 83ec2230ba3ec570a0638bc47383b143c883a8f7 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 20:13:54 +0900 Subject: [PATCH 38/60] =?UTF-8?q?refactor:=20=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=EC=A2=85=EB=A3=8C=20=ED=9B=84=20=EB=A1=9C=EC=A7=81=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 21 +++++++------------ src/main/java/db/Repository.java | 8 +++---- src/main/java/model/status/GameStatus.java | 4 ++++ src/main/java/model/status/Quit.java | 5 +++++ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index a7de87ad8ad..1f1af6f8bdc 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -12,7 +12,6 @@ import model.ChessGame; import model.command.CommandLine; import model.status.GameStatus; -import model.status.Quit; import model.status.StatusFactory; import view.InputView; import view.OutputView; @@ -29,27 +28,23 @@ public ChessController(final InputView inputView, final OutputView outputView) { } public void run() { - outputView.printStartMessage(); final ChessGame chessGame = create(); GameStatus gameStatus = initGame(); outputView.printChessBoard(ChessBoardDto.from(chessGame)); - while (gameStatus.isRunning()) { gameStatus = play(gameStatus, chessGame); } + save(gameStatus, chessGame); + } - repository.remove(); - - if (gameStatus instanceof Quit) { // TODO instanceof 괜춘? - repository.removeMoving(); + private void save(final GameStatus gameStatus, final ChessGame chessGame) { + if (gameStatus.isCheck() || gameStatus.isQuit()) { + repository.removeAll(); return; } - if (gameStatus.isCheck()) { - repository.removeMoving(); - return; // TODO 체크로 게임이 끝났을때 어떻게 처리할까 - } - final BoardDto boardDto = BoardDto.from(new Board(chessGame.getBoard())); // TODO 형태 너무 이상 변경 필요 + final Board board = new Board(chessGame.getBoard()); + final BoardDto boardDto = BoardDto.from(board); repository.save(boardDto, chessGame.getCamp(), chessGame.getTurn()); } @@ -58,7 +53,6 @@ private ChessGame create() { return repository.findGame(); } return ChessGame.setupStartingPosition(); - } private GameStatus initGame() { @@ -92,7 +86,6 @@ private void saveMoving(final ChessGame chessGame, final CommandLine commandLine } } - //TODO 뭔가 이녀석도 옮겨주기 private void print(final GameStatus gameStatus, final CommandLine commandLine, final ChessGame chessGame) { if (gameStatus.isCheck()) { outputView.printWinner(chessGame.getCamp().toString()); diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java index 8811322b8e7..8610e5ba380 100644 --- a/src/main/java/db/Repository.java +++ b/src/main/java/db/Repository.java @@ -25,12 +25,9 @@ public Repository(final String database) { this.boardDao = new BoardDao(database); } - public void remove() { + public void removeAll() { turnDao.remove(); boardDao.remove(); - } - - public void removeMoving() { movingDao.remove(); } @@ -39,8 +36,9 @@ public boolean hasGame() { } public void save(final BoardDto board, final Camp camp, final Turn turn) { - remove(); + boardDao.remove(); boardDao.saveBoard(board); + turnDao.remove(); turnDao.saveTurn(camp, turn); } diff --git a/src/main/java/model/status/GameStatus.java b/src/main/java/model/status/GameStatus.java index a387268504a..5697a39517f 100644 --- a/src/main/java/model/status/GameStatus.java +++ b/src/main/java/model/status/GameStatus.java @@ -10,4 +10,8 @@ public interface GameStatus { boolean isRunning(); boolean isCheck(); + + default boolean isQuit() { + return false; + } } diff --git a/src/main/java/model/status/Quit.java b/src/main/java/model/status/Quit.java index 721849812d6..bfc3601d26b 100644 --- a/src/main/java/model/status/Quit.java +++ b/src/main/java/model/status/Quit.java @@ -21,4 +21,9 @@ public boolean isRunning() { public boolean isCheck() { return false; } + + @Override + public boolean isQuit() { + return true; + } } From 90659cccfd37b13f26949c24a8dcc7ba1ac0ae7e Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 20:22:48 +0900 Subject: [PATCH 39/60] =?UTF-8?q?refactor:=20var=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/BoardDao.java | 32 +++++++++++++---------- src/main/java/db/MovingDao.java | 41 +++++++++++++++++------------- src/main/java/db/Repository.java | 1 - src/main/java/db/TurnDao.java | 18 ++++++++----- src/test/java/model/BoardTest.java | 3 +-- 5 files changed, 53 insertions(+), 42 deletions(-) diff --git a/src/main/java/db/BoardDao.java b/src/main/java/db/BoardDao.java index 6b8fdd09cfd..2ba34b263e4 100644 --- a/src/main/java/db/BoardDao.java +++ b/src/main/java/db/BoardDao.java @@ -6,6 +6,9 @@ import db.dto.PieceDto; import db.dto.PositionDto; import db.exception.DaoException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.HashMap; @@ -30,9 +33,10 @@ public void saveBoard(final BoardDto board) { } private void savePosition(final PositionDto position, final PieceDto piece) { - final var query = "INSERT INTO board VALUES(?, ?, ?)"; - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) + final String query = "INSERT INTO board VALUES(?, ?, ?)"; + try (final Connection connection = DBConnectionUtil.getConnection(database); + final PreparedStatement preparedStatement = connection.prepareStatement(query, + Statement.RETURN_GENERATED_KEYS) ) { preparedStatement.setString(1, position.value()); preparedStatement.setString(2, piece.type()); @@ -46,20 +50,20 @@ private void savePosition(final PositionDto position, final PieceDto piece) { } public BoardDto find() { - final var query = "SELECT * FROM board"; + final String query = "SELECT * FROM board"; - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query)) { + try (final Connection connection = DBConnectionUtil.getConnection(database); + final PreparedStatement preparedStatement = connection.prepareStatement(query)) { - final var resultSet = preparedStatement.executeQuery(); + final ResultSet resultSet = preparedStatement.executeQuery(); final Map result = new HashMap<>(); while (resultSet.next()) { - final var position = new PositionDto(resultSet.getString("position")); - final var type = resultSet.getString("piece_type"); - final var camp = resultSet.getString("camp"); - final var piece = new PieceDto(type, camp); + final PositionDto position = new PositionDto(resultSet.getString("position")); + final String type = resultSet.getString("piece_type"); + final String camp = resultSet.getString("camp"); + final PieceDto piece = new PieceDto(type, camp); result.put(position, piece); } if (result.isEmpty()) { @@ -73,11 +77,11 @@ public BoardDto find() { public void remove() { final String query = "TRUNCATE TABLE board"; - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) + try (final Connection connection = DBConnectionUtil.getConnection(database); + final PreparedStatement preparedStatement = connection.prepareStatement(query, + Statement.RETURN_GENERATED_KEYS) ) { preparedStatement.executeUpdate(); - } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_DELETE); } diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 52ac89a0c88..83f7ebdbbc4 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -4,6 +4,9 @@ import db.connection.DBConnectionUtil; import db.dto.MovingDto; import db.exception.DaoException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; @@ -18,9 +21,10 @@ public MovingDao(final String database) { } public long addMoving(final MovingDto moving) { - final var query = "INSERT INTO moving VALUES(?, ?, ?, ?)"; - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) + final String query = "INSERT INTO moving VALUES(?, ?, ?, ?)"; + try (final Connection connection = DBConnectionUtil.getConnection(database); + final PreparedStatement preparedStatement = connection.prepareStatement(query, + Statement.RETURN_GENERATED_KEYS) ) { long autoIncrement = 0; preparedStatement.setNull(1, 0); @@ -29,7 +33,7 @@ public long addMoving(final MovingDto moving) { preparedStatement.setString(4, moving.next()); preparedStatement.executeUpdate(); - final var generatedKeys = preparedStatement.getGeneratedKeys(); + final ResultSet generatedKeys = preparedStatement.getGeneratedKeys(); if (generatedKeys.next()) { autoIncrement = generatedKeys.getLong(1); @@ -43,14 +47,14 @@ public long addMoving(final MovingDto moving) { public List findAll() { final String query = "SELECT * FROM moving"; final List moving = new ArrayList<>(); - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query)) { + try (final Connection connection = DBConnectionUtil.getConnection(database); + final PreparedStatement preparedStatement = connection.prepareStatement(query)) { - final var resultSet = preparedStatement.executeQuery(); + final ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { - var camp = resultSet.getString("camp"); - var current = resultSet.getString("start"); - var next = resultSet.getString("destination"); + String camp = resultSet.getString("camp"); + String current = resultSet.getString("start"); + String next = resultSet.getString("destination"); moving.add(new MovingDto(camp, current, next)); } return moving; @@ -61,9 +65,9 @@ public List findAll() { public int countMoving() { final String query = "SELECT count(*) AS count FROM moving"; - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query)) { - final var resultSet = preparedStatement.executeQuery(); + try (final Connection connection = DBConnectionUtil.getConnection(database); + final PreparedStatement preparedStatement = connection.prepareStatement(query)) { + final ResultSet resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { return resultSet.getInt("count"); } @@ -75,10 +79,10 @@ public int countMoving() { public MovingDto findByMovementId(final long movementId) { final String query = "SELECT * FROM moving WHERE movement_id = ?"; - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query)) { + try (final Connection connection = DBConnectionUtil.getConnection(database); + final PreparedStatement preparedStatement = connection.prepareStatement(query)) { preparedStatement.setLong(1, movementId); - final var resultSet = preparedStatement.executeQuery(); + final ResultSet resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { return new MovingDto( @@ -95,8 +99,9 @@ public MovingDto findByMovementId(final long movementId) { public void remove() { final String query = "TRUNCATE TABLE moving"; - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) + try (final Connection connection = DBConnectionUtil.getConnection(database); + final PreparedStatement preparedStatement = connection.prepareStatement(query, + Statement.RETURN_GENERATED_KEYS) ) { preparedStatement.executeUpdate(); } catch (final SQLException exception) { diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java index 8610e5ba380..2a9af083cee 100644 --- a/src/main/java/db/Repository.java +++ b/src/main/java/db/Repository.java @@ -14,7 +14,6 @@ public class Repository { - // TODO 인터페이스로 변경하기 private final MovingDao movingDao; private final TurnDao turnDao; private final BoardDao boardDao; diff --git a/src/main/java/db/TurnDao.java b/src/main/java/db/TurnDao.java index 25268e65ad0..72eda0585c8 100644 --- a/src/main/java/db/TurnDao.java +++ b/src/main/java/db/TurnDao.java @@ -4,6 +4,9 @@ import db.connection.DBConnectionUtil; import db.dto.TurnDto; import db.exception.DaoException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import model.Camp; @@ -19,8 +22,8 @@ public TurnDao(final String database) { public void saveTurn(final Camp camp, final Turn turn) { final String query = "INSERT INTO turn values(?, ?)"; - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query) + try (final Connection connection = DBConnectionUtil.getConnection(database); + final PreparedStatement preparedStatement = connection.prepareStatement(query) ) { preparedStatement.setString(1, camp.toString()); preparedStatement.setInt(2, turn.count()); @@ -34,9 +37,9 @@ public void saveTurn(final Camp camp, final Turn turn) { public TurnDto findTurn() { final String query = "SELECT * FROM turn"; - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query)) { - final var resultSet = preparedStatement.executeQuery(); + try (final Connection connection = DBConnectionUtil.getConnection(database); + final PreparedStatement preparedStatement = connection.prepareStatement(query)) { + final ResultSet resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { return new TurnDto(resultSet.getString("camp"), resultSet.getInt("count")); @@ -50,8 +53,9 @@ public TurnDto findTurn() { public void remove() { final String query = "TRUNCATE TABLE turn"; - try (final var connection = DBConnectionUtil.getConnection(database); - final var preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) + try (final Connection connection = DBConnectionUtil.getConnection(database); + final PreparedStatement preparedStatement = connection.prepareStatement(query, + Statement.RETURN_GENERATED_KEYS) ) { preparedStatement.executeUpdate(); } catch (final SQLException exception) { diff --git a/src/test/java/model/BoardTest.java b/src/test/java/model/BoardTest.java index 298274dcbb1..f56a16ec92b 100644 --- a/src/test/java/model/BoardTest.java +++ b/src/test/java/model/BoardTest.java @@ -24,7 +24,6 @@ class BoardTest { - //TODO 다른 테스트도 추가하기 @DisplayName("초기 상태의 기물 점수를 계산한다.") @Test void calculateInitBoard() { @@ -97,7 +96,7 @@ void threePawn() { } @Test - @DisplayName("getter로 가져온 값을 수정하려고 하면 예외가 발생한다..") + @DisplayName("getter로 가져온 값을 수정하려고 하면 예외가 발생한다.") void doNotUpdate() { //given final Board board = Board.create(); From 61835603ef5edfc3b167f4b30b361481f98721d6 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 20:27:31 +0900 Subject: [PATCH 40/60] =?UTF-8?q?style:=20=EA=B0=9C=ED=96=89=20=EB=B0=8F?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/BoardDao.java | 2 -- src/main/java/db/Repository.java | 6 +++++- src/main/java/db/TurnDao.java | 3 --- src/main/java/db/dto/BoardDto.java | 2 -- src/main/java/db/dto/MovingDto.java | 1 - src/main/java/db/dto/TurnDto.java | 9 --------- 6 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/main/java/db/BoardDao.java b/src/main/java/db/BoardDao.java index 2ba34b263e4..4119faf52e0 100644 --- a/src/main/java/db/BoardDao.java +++ b/src/main/java/db/BoardDao.java @@ -54,9 +54,7 @@ public BoardDto find() { try (final Connection connection = DBConnectionUtil.getConnection(database); final PreparedStatement preparedStatement = connection.prepareStatement(query)) { - final ResultSet resultSet = preparedStatement.executeQuery(); - final Map result = new HashMap<>(); while (resultSet.next()) { diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java index 2a9af083cee..e1c51beeedf 100644 --- a/src/main/java/db/Repository.java +++ b/src/main/java/db/Repository.java @@ -59,7 +59,7 @@ public ChessGame findGame() { final List findMoving = movingDao.findAll(); final Board board = findBoard.convert(); - if (findTurn.count() < findMoving.size()) { + if (unsaved(findTurn, findMoving)) { restore(findTurn, findMoving, board); } final Camp camp = findLastTurnCamp(findMoving); @@ -67,6 +67,10 @@ public ChessGame findGame() { return new ChessGame(board, gameTurn); } + private boolean unsaved(final TurnDto findTurn, final List findMoving) { + return findTurn.count() < findMoving.size(); + } + private Camp findLastTurnCamp(final List findMoving) { if (findMoving.size() % 2 == 0) { return Camp.WHITE; diff --git a/src/main/java/db/TurnDao.java b/src/main/java/db/TurnDao.java index 72eda0585c8..11651025e77 100644 --- a/src/main/java/db/TurnDao.java +++ b/src/main/java/db/TurnDao.java @@ -27,7 +27,6 @@ public void saveTurn(final Camp camp, final Turn turn) { ) { preparedStatement.setString(1, camp.toString()); preparedStatement.setInt(2, turn.count()); - preparedStatement.executeUpdate(); } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_SAVE); @@ -40,12 +39,10 @@ public TurnDto findTurn() { try (final Connection connection = DBConnectionUtil.getConnection(database); final PreparedStatement preparedStatement = connection.prepareStatement(query)) { final ResultSet resultSet = preparedStatement.executeQuery(); - if (resultSet.next()) { return new TurnDto(resultSet.getString("camp"), resultSet.getInt("count")); } return new TurnDto("WHITE", 0); - } catch (SQLException exception) { throw new DaoException(ErrorCode.FAIL_FIND); } diff --git a/src/main/java/db/dto/BoardDto.java b/src/main/java/db/dto/BoardDto.java index 37937a2e875..ea28bce7e71 100644 --- a/src/main/java/db/dto/BoardDto.java +++ b/src/main/java/db/dto/BoardDto.java @@ -11,7 +11,6 @@ public record BoardDto(Map pieces) { public static BoardDto from(final Board board) { final Map result = new HashMap<>(); - final Map pieces = board.getPieces(); for (Entry entry : pieces.entrySet()) { @@ -19,7 +18,6 @@ public static BoardDto from(final Board board) { final PieceDto piece = PieceDto.from(entry.getValue()); result.put(position, piece); } - return new BoardDto(result); } diff --git a/src/main/java/db/dto/MovingDto.java b/src/main/java/db/dto/MovingDto.java index 3fdf57606c9..b9f0f67e044 100644 --- a/src/main/java/db/dto/MovingDto.java +++ b/src/main/java/db/dto/MovingDto.java @@ -8,7 +8,6 @@ public record MovingDto(String camp, String current, String next) { public static MovingDto from(final List moving, final Camp camp) { final String current = moving.get(0); final String next = moving.get(1); - return new MovingDto(camp.toString(), current, next); } } diff --git a/src/main/java/db/dto/TurnDto.java b/src/main/java/db/dto/TurnDto.java index d2ad9bd14f4..cd7af0e4456 100644 --- a/src/main/java/db/dto/TurnDto.java +++ b/src/main/java/db/dto/TurnDto.java @@ -1,13 +1,4 @@ package db.dto; -import model.Camp; - public record TurnDto(String value, int count) { - - public Camp convert() { - if ("WHITE".equals(value)) { - return Camp.WHITE; - } - return Camp.BLACK; - } } From 394b8284313220d48f835a6830e269ec33a69647 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 20:32:17 +0900 Subject: [PATCH 41/60] =?UTF-8?q?docs:=20=EA=B5=AC=ED=98=84=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EB=AA=A9=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/docs/README.md b/docs/README.md index d9ca3e33139..04d7d57ef7f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,17 +26,12 @@ * [x] 같은 세로줄에 있는 같은 pawn인 경우 0.5 * [x] 진영의 점수를 출력 * [x] 승리한 진영의 결과 확인 -* [ ] 체크 메이트면 게임 종료 - * 킹을 잡는 move 명령이 있어야 게임 종료 +* [x] 킹이 잡히면 게임 종료 * [x] end 가 입력되면 프로그램 종료 * [x] 예외 시 재입력 * [x] 진행중인 게임 end 입력 후 재시작시 이전 게임 실행 -* [ ] restart 입력시 새로운 게임 실행 - * quit 으로 현재 게임을 완전히 끝내고 시작은 start만 두는 게 나을지 - * 아니면 시작할때 restart, start로 구분하는게 나을지. - * quit 이 구현은 좀 더 쉬워보인다. -* [ ] log 입력시 현재까지 기보 출력 -* [ ] db 예외 처리 +* [x] quit 입력시 저장하지 않고 게임 종료 +* [x] db 예외 처리 ## 기물의 이동 기능 @@ -57,27 +52,12 @@ * start: 게임을 시작하는 명령어 * 게임 시작은 start 명령어만 가능하다 -* end: 게임을 종료하는 명령어 +* end: 게임을 저장 후 종료하는 명령어 +* quit: 게임을 저장하지 않고 종료하는 명령어 * move: 게임 중 기물을 움직이는 명령어 * 게임이 시작되지 않았는데 move 명령어를 입력한다면 예외가 발생한다. * 형식은 move [a-h1-8] [a-h1-8] 형태이고 아닐시 예외가 발생한다. - -## 고민해볼점 - -* 프로그램 실행 중 종료시 어떻게 저장할 것인가 - - * 현재 총 세개의 테이블이 있다. (board, moving, turn) - * board: 기물들의 위치 - * turn: 마지막 턴 - * moving: 기보 - * board, turn 테이블은 end 입력될 때 한번 업데이트를 한다. (문제, 프로그램 실행중 종료되면 저장안됨) - * moving 테이블은 move 입력될 때마다 insert 쿼리를 날린다. (프로그램이 도중에 종료되어도 최신것까지 저장됨) - * 만약 프로그램이 도중에 종료된다면 moving이랑 board가 불일치발생 - - * **해결방안** - * 현재까지 진행된 turn 횟수를 기록하고 turn 테이블에 같이 저장 - * turn 횟수와 moving이 일치하지 않는다면 moving을 토대로 board를 업데이트! - +* status: 현재 점수를 확인하는 명령어 ## 우선순위가 낮지만 구현해보고 싶은 기능 @@ -87,3 +67,4 @@ * [ ] 캐슬링 * 체크 상황 * [ ] 체크 상황이면 체크를 피하는 방향으로만 이동 가능 +* [ ] log 입력시 현재까지 기보 출력 From 62e632aa09435db4f69502e663a0cc7ef78a31bb Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sun, 31 Mar 2024 20:37:31 +0900 Subject: [PATCH 42/60] =?UTF-8?q?docs:=20=EB=8F=84=EC=BB=A4=20=EB=B0=8F=20?= =?UTF-8?q?db=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=EB=A6=AC=EB=93=9C=EB=AF=B8?= =?UTF-8?q?=EC=97=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/docs/README.md b/docs/README.md index 04d7d57ef7f..af5fa595726 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,48 @@ # 체스 미션 +## DB 연동 + +### Docker 실행하기 + +```bash +docker-compose -p chess up -d +``` + +### DB 스키마 + +```sql +use chess; + +drop table if exists board; + +create table board +( + position varchar(2) not null, + piece_type varchar(6) not null, + camp varchar(5) not null + +); + +drop table if exists moving; + +create table moving +( + movement_id INT primary key auto_increment, + camp varchar(5) not null, + start varchar(2) not null, + destination varchar(2) not null +); + +drop table if exists turn; + +create table turn +( + camp varchar(5) not null, + count int not null +); +``` + + ## 기능 요구 사항 * [x] 8 * 8 의 체스 판 생성 From b7881ece8f4a3910d19490b7ea158d93aa013baa Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 00:48:29 +0900 Subject: [PATCH 43/60] =?UTF-8?q?refactor:=20pieceDto=EA=B0=80=20=ED=95=98?= =?UTF-8?q?=EB=8D=98=20camp=EB=A5=BC=20=EC=B0=BE=EB=8A=94=20=EC=B1=85?= =?UTF-8?q?=EC=9E=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/dto/CampType.java | 26 ++++++++++++++++++++++++++ src/main/java/db/dto/PieceDto.java | 9 +-------- src/test/java/db/dto/CampTypeTest.java | 20 ++++++++++++++++++++ 3 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 src/main/java/db/dto/CampType.java create mode 100644 src/test/java/db/dto/CampTypeTest.java diff --git a/src/main/java/db/dto/CampType.java b/src/main/java/db/dto/CampType.java new file mode 100644 index 00000000000..ae11f703aab --- /dev/null +++ b/src/main/java/db/dto/CampType.java @@ -0,0 +1,26 @@ +package db.dto; + +import java.util.Arrays; +import model.Camp; + +public enum CampType { + + WHITE(Camp.WHITE, "WHITE"), + BLACK(Camp.BLACK, "BLACK"); + + private final Camp camp; + private final String colorName; + + CampType(final Camp camp, final String colorName) { + this.camp = camp; + this.colorName = colorName; + } + + public static Camp findByColorName(final String color) { + return Arrays.stream(values()) + .filter(campType -> campType.colorName.equals(color)) + .findFirst() + .orElseThrow() + .camp; + } +} diff --git a/src/main/java/db/dto/PieceDto.java b/src/main/java/db/dto/PieceDto.java index cc06789cda7..289c298f18e 100644 --- a/src/main/java/db/dto/PieceDto.java +++ b/src/main/java/db/dto/PieceDto.java @@ -10,14 +10,7 @@ public static PieceDto from(final Piece piece) { } public Piece convert() { - final Camp army = find(); + final Camp army = CampType.findByColorName(camp); return PieceType.findValue(army, type); } - - private Camp find() { - if ("BLACK".equals(camp)) { - return Camp.BLACK; - } - return Camp.WHITE; - } } diff --git a/src/test/java/db/dto/CampTypeTest.java b/src/test/java/db/dto/CampTypeTest.java new file mode 100644 index 00000000000..0f12c52865e --- /dev/null +++ b/src/test/java/db/dto/CampTypeTest.java @@ -0,0 +1,20 @@ +package db.dto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import model.Camp; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CampTypeTest { + + @DisplayName("문자로 Camp를 찾는다.") + @Test + void findByColor() { + assertAll( + () -> assertThat(CampType.findByColorName("WHITE")).isEqualTo(Camp.WHITE), + () -> assertThat(CampType.findByColorName("BLACK")).isEqualTo(Camp.BLACK) + ); + } +} From b6805126f314b8eb14f0f668c5bc52c2732e30c5 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 00:58:19 +0900 Subject: [PATCH 44/60] =?UTF-8?q?refactor:=20enum=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EA=B0=92=EC=9D=84=20=EC=B0=BE=EC=9D=84=20=EB=95=8C=20return=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/dto/CampType.java | 20 +++++++++++++++++--- src/main/java/db/dto/PieceDto.java | 10 ++++++---- src/main/java/db/dto/PieceType.java | 18 ++++++++++++------ src/test/java/db/dto/CampTypeTest.java | 5 ++--- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/main/java/db/dto/CampType.java b/src/main/java/db/dto/CampType.java index ae11f703aab..fd243d84196 100644 --- a/src/main/java/db/dto/CampType.java +++ b/src/main/java/db/dto/CampType.java @@ -16,11 +16,25 @@ public enum CampType { this.colorName = colorName; } - public static Camp findByColorName(final String color) { + public static CampType findByColorName(final String color) { return Arrays.stream(values()) .filter(campType -> campType.colorName.equals(color)) .findFirst() - .orElseThrow() - .camp; + .orElseThrow(); + } + + public static CampType findByCamp(final Camp camp) { + return Arrays.stream(values()) + .filter(campType -> campType.camp == camp) + .findFirst() + .orElseThrow(); + } + + public String getColorName() { + return colorName; + } + + public Camp getCamp() { + return camp; } } diff --git a/src/main/java/db/dto/PieceDto.java b/src/main/java/db/dto/PieceDto.java index 289c298f18e..d4538aa61c8 100644 --- a/src/main/java/db/dto/PieceDto.java +++ b/src/main/java/db/dto/PieceDto.java @@ -1,16 +1,18 @@ package db.dto; -import model.Camp; import model.piece.Piece; public record PieceDto(String type, String camp) { public static PieceDto from(final Piece piece) { - return new PieceDto(PieceType.findValue(piece), piece.getCamp().name()); + final PieceType pieceType = PieceType.findValue(piece); + final CampType campType = CampType.findByCamp(piece.getCamp()); + return new PieceDto(pieceType.getPieceName(), campType.getColorName()); } public Piece convert() { - final Camp army = CampType.findByColorName(camp); - return PieceType.findValue(army, type); + final CampType army = CampType.findByColorName(camp); + final PieceType pieceType = PieceType.findValue(army.getCamp(), type); + return pieceType.getPiece(); } } diff --git a/src/main/java/db/dto/PieceType.java b/src/main/java/db/dto/PieceType.java index 76dc3412e25..bbf38415013 100644 --- a/src/main/java/db/dto/PieceType.java +++ b/src/main/java/db/dto/PieceType.java @@ -36,20 +36,26 @@ public enum PieceType { this.camp = camp; } - public static String findValue(final Piece piece) { + public static PieceType findValue(final Piece piece) { return Arrays.stream(values()) .filter(pieceType1 -> pieceType1.piece.getClass().isInstance(piece)) .findFirst() - .orElseThrow() - .pieceName; + .orElseThrow(); } - public static Piece findValue(final Camp camp, final String type) { + public static PieceType findValue(final Camp camp, final String type) { return Arrays.stream(values()) .filter(pieceType -> pieceType.pieceName.equals(type)) .filter(pieceType -> pieceType.camp == camp) .findFirst() - .orElseThrow() - .piece; + .orElseThrow(); + } + + public String getPieceName() { + return pieceName; + } + + public Piece getPiece() { + return piece; } } diff --git a/src/test/java/db/dto/CampTypeTest.java b/src/test/java/db/dto/CampTypeTest.java index 0f12c52865e..89ded3b227d 100644 --- a/src/test/java/db/dto/CampTypeTest.java +++ b/src/test/java/db/dto/CampTypeTest.java @@ -3,7 +3,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; -import model.Camp; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -13,8 +12,8 @@ class CampTypeTest { @Test void findByColor() { assertAll( - () -> assertThat(CampType.findByColorName("WHITE")).isEqualTo(Camp.WHITE), - () -> assertThat(CampType.findByColorName("BLACK")).isEqualTo(Camp.BLACK) + () -> assertThat(CampType.findByColorName("WHITE")).isEqualTo(CampType.WHITE), + () -> assertThat(CampType.findByColorName("BLACK")).isEqualTo(CampType.BLACK) ); } } From c3200b390367b6929a5ad5f3852889f5e53b3ad1 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 01:46:36 +0900 Subject: [PATCH 45/60] =?UTF-8?q?test:=20dao=20=EB=B0=8F=20repository=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 1 + src/main/java/db/Repository.java | 18 ++-- src/test/java/db/BoardDaoTest.java | 16 +++- src/test/java/db/MovingDaoTest.java | 21 +++++ src/test/java/db/RepositoryTest.java | 87 +++++++++++++++++++ src/test/java/db/dto/CampTypeTest.java | 2 + 6 files changed, 135 insertions(+), 10 deletions(-) create mode 100644 src/test/java/db/RepositoryTest.java diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 1f1af6f8bdc..192c39679e9 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -49,6 +49,7 @@ private void save(final GameStatus gameStatus, final ChessGame chessGame) { } private ChessGame create() { + //TODO 여기서 hasGame 체크할까 아니면 그냥 리포지토리 안으로 책임 넘길까 if (repository.hasGame()) { return repository.findGame(); } diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java index e1c51beeedf..2f88f7d00cf 100644 --- a/src/main/java/db/Repository.java +++ b/src/main/java/db/Repository.java @@ -12,7 +12,7 @@ import model.position.Moving; import model.position.Position; -public class Repository { +public class Repository { //TODO 이름 변경 private final MovingDao movingDao; private final TurnDao turnDao; @@ -45,14 +45,6 @@ public void saveMoving(final MovingDto moving) { movingDao.addMoving(moving); } - public BoardDto findBoard() { - return boardDao.find(); - } - - public TurnDto findTurn() { - return turnDao.findTurn(); - } - public ChessGame findGame() { final BoardDto findBoard = findBoard(); final TurnDto findTurn = findTurn(); @@ -67,6 +59,14 @@ public ChessGame findGame() { return new ChessGame(board, gameTurn); } + private BoardDto findBoard() { + return boardDao.find(); + } + + private TurnDto findTurn() { + return turnDao.findTurn(); + } + private boolean unsaved(final TurnDto findTurn, final List findMoving) { return findTurn.count() < findMoving.size(); } diff --git a/src/test/java/db/BoardDaoTest.java b/src/test/java/db/BoardDaoTest.java index 7ca02c67dd1..0793e5b0052 100644 --- a/src/test/java/db/BoardDaoTest.java +++ b/src/test/java/db/BoardDaoTest.java @@ -21,7 +21,7 @@ void beforeEach() { @DisplayName("보드 저장 확인") void addBoard() { //given - final var board = BoardDto.from(Board.create()); + final BoardDto board = BoardDto.from(Board.create()); //when boardDao.saveBoard(board); @@ -30,4 +30,18 @@ void addBoard() { //then assertThat(board).isEqualTo(findBoard); } + + @DisplayName("테이블이 비어있다면 새로운 보드를 만든다.") + @Test + void findBoard() { + //given + boardDao.remove(); + final BoardDto expected = BoardDto.from(Board.create()); + + //when + final BoardDto boardDto = boardDao.find(); + + //then + assertThat(boardDto).isEqualTo(expected); + } } diff --git a/src/test/java/db/MovingDaoTest.java b/src/test/java/db/MovingDaoTest.java index b1ba7bf4ca6..1e4637fde17 100644 --- a/src/test/java/db/MovingDaoTest.java +++ b/src/test/java/db/MovingDaoTest.java @@ -8,6 +8,7 @@ import db.exception.DaoException; import java.sql.Connection; import java.sql.SQLException; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -55,4 +56,24 @@ void failFindMoving() { void count() { assertThat(movingDao.countMoving()).isZero(); } + + @Test + @DisplayName("저장된 기보를 확인한다.") + void findAll() { + //given + final List movingDtos = List.of( + new MovingDto("WHITE", "a2", "a3"), + new MovingDto("BLACK", "h7", "h6"), + new MovingDto("WHITE", "a3", "a4"), + new MovingDto("BLACK", "g7", "g6") + ); + + //when + for (MovingDto movingDto : movingDtos) { + movingDao.addMoving(movingDto); + } + + //then + assertThat(movingDao.findAll()).isEqualTo(movingDtos); + } } diff --git a/src/test/java/db/RepositoryTest.java b/src/test/java/db/RepositoryTest.java new file mode 100644 index 00000000000..b2626e4d460 --- /dev/null +++ b/src/test/java/db/RepositoryTest.java @@ -0,0 +1,87 @@ +package db; + +import static model.Fixtures.A2; +import static model.Fixtures.A3; +import static model.Fixtures.G6; +import static model.Fixtures.G7; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import db.dto.BoardDto; +import db.dto.MovingDto; +import model.ChessGame; +import model.position.Moving; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RepositoryTest { + + private final Repository repository = new Repository("chess_test"); + + @BeforeEach + void beforeEach() { + repository.removeAll(); + } + + @DisplayName("기보를 저장한다.") + @Test + void saveMoving() { + repository.saveMoving(new MovingDto("WHITE", "a2", "a3")); + assertThat(repository.hasGame()).isTrue(); + } + + @DisplayName("진행된 게입이 없으면 false를 반환한다.") + @Test + void hasNoGame() { + assertThat(repository.hasGame()).isFalse(); + } + + @DisplayName("저장된 게임이 없을 때 새로운 게임을 만든다.") + @Test + void createNewChessGame() { + final ChessGame game = repository.findGame(); + final ChessGame expected = ChessGame.setupStartingPosition(); + // TODO 이 때 chessgame객체 equal 재정의 안하고 이렇게 꺼내서 비교하는 방법 괜춘? + assertAll( + () -> assertThat(game.getBoard()).isEqualTo(expected.getBoard()), + () -> assertThat(game.getCamp()).isEqualTo(expected.getCamp()), + () -> assertThat(game.getTurn()).isEqualTo(expected.getTurn()) + ); + } + + @Test + @DisplayName("기보만 저장됐을 때 기보를 바탕으로 복구한다.") + void restore() { + //given + final ChessGame expected = ChessGame.setupStartingPosition(); + expected.move(new Moving(A2, A3)); + + //when + repository.saveMoving(new MovingDto("WHITE", "a2", "a3")); + final ChessGame game = repository.findGame(); + + //then + assertAll( + () -> assertThat(game.getBoard()).isEqualTo(expected.getBoard()), + () -> assertThat(game.getCamp()).isEqualTo(expected.getCamp()), + () -> assertThat(game.getTurn()).isEqualTo(expected.getTurn()) + ); + } + + @Test + @DisplayName("보드와 턴을 저장한다.") + void saveBoardAndTurn() { //TODO boardDto 형태 고치고 다시 돌아올것! + //given + final ChessGame expected = ChessGame.setupStartingPosition(); + expected.move(new Moving(A2, A3)); + expected.move(new Moving(G7, G6)); + +// final BoardDto boardDto = BoardDto.from(expected.getBoard()); + + //when +// repository.save(); + + //then + } +} diff --git a/src/test/java/db/dto/CampTypeTest.java b/src/test/java/db/dto/CampTypeTest.java index 89ded3b227d..490297ef663 100644 --- a/src/test/java/db/dto/CampTypeTest.java +++ b/src/test/java/db/dto/CampTypeTest.java @@ -5,12 +5,14 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import view.message.ErrorCodeMessage; class CampTypeTest { @DisplayName("문자로 Camp를 찾는다.") @Test void findByColor() { + System.out.println(ErrorCodeMessage.FAIL_FIND); assertAll( () -> assertThat(CampType.findByColorName("WHITE")).isEqualTo(CampType.WHITE), () -> assertThat(CampType.findByColorName("BLACK")).isEqualTo(CampType.BLACK) From 36871815b7dacadd9ea08456d6ab27bceeef43c0 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 02:10:32 +0900 Subject: [PATCH 46/60] =?UTF-8?q?refactor:=20save=ED=95=A0=20=EB=95=8C=20c?= =?UTF-8?q?amp=EC=99=80=20turn=20=ED=95=A8=EA=BB=98=20dto=EB=A1=9C=20?= =?UTF-8?q?=EB=AC=B6=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 9 +++++++-- src/main/java/db/Repository.java | 4 ++-- src/main/java/db/TurnDao.java | 8 +++----- src/main/java/db/dto/TurnDto.java | 10 +++++++++- src/main/java/dto/ChessBoardDto.java | 2 +- src/main/java/model/ChessGame.java | 10 +++++++--- src/test/java/db/TurnDaoTest.java | 5 ++--- src/test/java/model/ChessGameTest.java | 6 +++--- 8 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 192c39679e9..a399f627aab 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -3,6 +3,7 @@ import db.Repository; import db.dto.BoardDto; import db.dto.MovingDto; +import db.dto.TurnDto; import dto.ChessBoardDto; import dto.ScoreDto; import exception.CustomException; @@ -10,6 +11,7 @@ import model.Board; import model.Camp; import model.ChessGame; +import model.Turn; import model.command.CommandLine; import model.status.GameStatus; import model.status.StatusFactory; @@ -43,9 +45,12 @@ private void save(final GameStatus gameStatus, final ChessGame chessGame) { repository.removeAll(); return; } - final Board board = new Board(chessGame.getBoard()); + final Board board = chessGame.getBoard(); + final Camp camp = chessGame.getCamp(); + final Turn turn = chessGame.getTurn(); final BoardDto boardDto = BoardDto.from(board); - repository.save(boardDto, chessGame.getCamp(), chessGame.getTurn()); + final TurnDto turnDto = TurnDto.from(camp, turn); + repository.save(boardDto, turnDto); } private ChessGame create() { diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java index 2f88f7d00cf..dcd68c95887 100644 --- a/src/main/java/db/Repository.java +++ b/src/main/java/db/Repository.java @@ -34,11 +34,11 @@ public boolean hasGame() { return movingDao.countMoving() > 0; } - public void save(final BoardDto board, final Camp camp, final Turn turn) { + public void save(final BoardDto board, final TurnDto turnDto) { boardDao.remove(); boardDao.saveBoard(board); turnDao.remove(); - turnDao.saveTurn(camp, turn); + turnDao.saveTurn(turnDto); } public void saveMoving(final MovingDto moving) { diff --git a/src/main/java/db/TurnDao.java b/src/main/java/db/TurnDao.java index 11651025e77..1da3255a439 100644 --- a/src/main/java/db/TurnDao.java +++ b/src/main/java/db/TurnDao.java @@ -9,8 +9,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import model.Camp; -import model.Turn; public class TurnDao { @@ -20,13 +18,13 @@ public TurnDao(final String database) { this.database = database; } - public void saveTurn(final Camp camp, final Turn turn) { + public void saveTurn(final TurnDto turnDto) { final String query = "INSERT INTO turn values(?, ?)"; try (final Connection connection = DBConnectionUtil.getConnection(database); final PreparedStatement preparedStatement = connection.prepareStatement(query) ) { - preparedStatement.setString(1, camp.toString()); - preparedStatement.setInt(2, turn.count()); + preparedStatement.setString(1, turnDto.currentCamp()); + preparedStatement.setInt(2, turnDto.count()); preparedStatement.executeUpdate(); } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_SAVE); diff --git a/src/main/java/db/dto/TurnDto.java b/src/main/java/db/dto/TurnDto.java index cd7af0e4456..339aaa070c1 100644 --- a/src/main/java/db/dto/TurnDto.java +++ b/src/main/java/db/dto/TurnDto.java @@ -1,4 +1,12 @@ package db.dto; -public record TurnDto(String value, int count) { +import model.Camp; +import model.Turn; + +public record TurnDto(String currentCamp, int count) { + + public static TurnDto from(final Camp camp, final Turn turn) { + final CampType campType = CampType.findByCamp(camp); + return new TurnDto(campType.getColorName(), turn.count()); + } } diff --git a/src/main/java/dto/ChessBoardDto.java b/src/main/java/dto/ChessBoardDto.java index eab90922d85..e8f7c1b22c8 100644 --- a/src/main/java/dto/ChessBoardDto.java +++ b/src/main/java/dto/ChessBoardDto.java @@ -24,7 +24,7 @@ private ChessBoardDto(final String board, final String currentTurn) { } public static ChessBoardDto from(final ChessGame chessGame) { - final Map pieceOfPosition = chessGame.getBoard(); + final Map pieceOfPosition = chessGame.getPieces(); final StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < BOARD_SIZE; i++) { diff --git a/src/main/java/model/ChessGame.java b/src/main/java/model/ChessGame.java index aea2647aa12..94954dc9482 100644 --- a/src/main/java/model/ChessGame.java +++ b/src/main/java/model/ChessGame.java @@ -25,7 +25,11 @@ public void move(final Moving moving) { gameTurn.progress(); } - public Map getBoard() { + public Score calculateScore(final Camp camp) { + return board.calculateScore(camp); + } + + public Map getPieces() { return board.getPieces(); } @@ -37,7 +41,7 @@ public Turn getTurn() { return gameTurn.getTurn(); } - public Score calculateScore(final Camp camp) { - return board.calculateScore(camp); + public Board getBoard() { + return board; } } diff --git a/src/test/java/db/TurnDaoTest.java b/src/test/java/db/TurnDaoTest.java index 903087c35e9..fb6c8752e25 100644 --- a/src/test/java/db/TurnDaoTest.java +++ b/src/test/java/db/TurnDaoTest.java @@ -3,8 +3,6 @@ import static org.assertj.core.api.Assertions.assertThat; import db.dto.TurnDto; -import model.Camp; -import model.Turn; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -22,7 +20,8 @@ void beforeEach() { @Test void saveTurn() { //given - turnDao.saveTurn(Camp.WHITE, new Turn(1)); + final TurnDto turnDto = new TurnDto("WHITE", 1); + turnDao.saveTurn(turnDto); final TurnDto expected = new TurnDto("WHITE", 1); //when diff --git a/src/test/java/model/ChessGameTest.java b/src/test/java/model/ChessGameTest.java index cec8ef2b140..81e0d43b5e0 100644 --- a/src/test/java/model/ChessGameTest.java +++ b/src/test/java/model/ChessGameTest.java @@ -79,7 +79,7 @@ void checkPiecesCount() { final ChessGame chessGame = ChessGame.setupStartingPosition(); //then - final Map board = chessGame.getBoard(); + final Map board = chessGame.getPieces(); assertThat(board.keySet()).hasSize(32); } @@ -89,7 +89,7 @@ void checkStartingPosition() { //given && when final ChessGame chessGame = ChessGame.setupStartingPosition(); - final Map board = chessGame.getBoard(); + final Map board = chessGame.getPieces(); final Map expected = new HashMap<>(); @@ -204,7 +204,7 @@ void checkRemovePiece() { chessGame.move(new Moving(A4, B5)); //then - assertThat(chessGame.getBoard()).hasSize(31); + assertThat(chessGame.getPieces()).hasSize(31); } @DisplayName("선공은 WHITE이다.") From f21c94821e47d4a2baf5812790b2d0a4c3c091f7 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 02:10:41 +0900 Subject: [PATCH 47/60] =?UTF-8?q?test:=20=EC=A0=80=EC=9E=A5=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/db/RepositoryTest.java | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/test/java/db/RepositoryTest.java b/src/test/java/db/RepositoryTest.java index b2626e4d460..1ae28667088 100644 --- a/src/test/java/db/RepositoryTest.java +++ b/src/test/java/db/RepositoryTest.java @@ -9,6 +9,7 @@ import db.dto.BoardDto; import db.dto.MovingDto; +import db.dto.TurnDto; import model.ChessGame; import model.position.Moving; import org.junit.jupiter.api.BeforeEach; @@ -44,7 +45,7 @@ void createNewChessGame() { final ChessGame expected = ChessGame.setupStartingPosition(); // TODO 이 때 chessgame객체 equal 재정의 안하고 이렇게 꺼내서 비교하는 방법 괜춘? assertAll( - () -> assertThat(game.getBoard()).isEqualTo(expected.getBoard()), + () -> assertThat(game.getPieces()).isEqualTo(expected.getPieces()), () -> assertThat(game.getCamp()).isEqualTo(expected.getCamp()), () -> assertThat(game.getTurn()).isEqualTo(expected.getTurn()) ); @@ -63,7 +64,7 @@ void restore() { //then assertAll( - () -> assertThat(game.getBoard()).isEqualTo(expected.getBoard()), + () -> assertThat(game.getPieces()).isEqualTo(expected.getPieces()), () -> assertThat(game.getCamp()).isEqualTo(expected.getCamp()), () -> assertThat(game.getTurn()).isEqualTo(expected.getTurn()) ); @@ -71,17 +72,26 @@ void restore() { @Test @DisplayName("보드와 턴을 저장한다.") - void saveBoardAndTurn() { //TODO boardDto 형태 고치고 다시 돌아올것! + void saveBoardAndTurn() { //given final ChessGame expected = ChessGame.setupStartingPosition(); expected.move(new Moving(A2, A3)); + repository.saveMoving(new MovingDto("WHITE", "a2", "a3")); expected.move(new Moving(G7, G6)); + repository.saveMoving(new MovingDto("BLACK", "g7", "g6")); -// final BoardDto boardDto = BoardDto.from(expected.getBoard()); + final BoardDto boardDto = BoardDto.from(expected.getBoard()); + final TurnDto turnDto = TurnDto.from(expected.getCamp(), expected.getTurn()); //when -// repository.save(); + repository.save(boardDto, turnDto); //then + final ChessGame game = repository.findGame(); + assertAll( + () -> assertThat(game.getPieces()).isEqualTo(expected.getPieces()), + () -> assertThat(game.getCamp()).isEqualTo(expected.getCamp()), + () -> assertThat(game.getTurn()).isEqualTo(expected.getTurn()) + ); } } From 8e823634ca65576ff9b943192a9b6e2e01f6dae0 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 02:23:44 +0900 Subject: [PATCH 48/60] =?UTF-8?q?fix:=20=EC=A0=9C=EC=9D=BC=20=EC=B2=98?= =?UTF-8?q?=EC=9D=8C=20end=20=EC=9E=85=EB=A0=A5=20=EC=8B=9C=EC=97=90?= =?UTF-8?q?=EB=8F=84=20=EB=B3=B4=EB=93=9C=20=EC=B6=9C=EB=A0=A5=20=EC=95=88?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index a399f627aab..293953b9f75 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -22,6 +22,7 @@ public class ChessController { private final InputView inputView; private final OutputView outputView; + // TODO repository 분리 private final Repository repository = new Repository("chess"); public ChessController(final InputView inputView, final OutputView outputView) { @@ -31,9 +32,11 @@ public ChessController(final InputView inputView, final OutputView outputView) { public void run() { outputView.printStartMessage(); - final ChessGame chessGame = create(); GameStatus gameStatus = initGame(); - outputView.printChessBoard(ChessBoardDto.from(chessGame)); + final ChessGame chessGame = create(); + if (gameStatus.isRunning()) { + outputView.printChessBoard(ChessBoardDto.from(chessGame)); + } while (gameStatus.isRunning()) { gameStatus = play(gameStatus, chessGame); } From fc88a1457b27769d9a6867ed1bbc459c69eb5330 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 02:24:07 +0900 Subject: [PATCH 49/60] =?UTF-8?q?feat:=20=EC=A0=9C=EC=9D=BC=20=EC=B2=98?= =?UTF-8?q?=EC=9D=8C=20quit=20=EC=9E=85=EB=A0=A5=20=EA=B0=80=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/status/StatusFactory.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/model/status/StatusFactory.java b/src/main/java/model/status/StatusFactory.java index 23f3bfca49b..a11f624b57e 100644 --- a/src/main/java/model/status/StatusFactory.java +++ b/src/main/java/model/status/StatusFactory.java @@ -16,6 +16,9 @@ public static GameStatus create(final CommandLine commandLine) { if (commandLine.isEnd()) { return new End(); } + if (commandLine.isQuit()) { + return new Quit(); + } throw new InvalidStatusException(ErrorCode.INVALID_STATUS); } } From 60644ad1affa5e36bcf5b1e1971cb923ab990251 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 03:10:58 +0900 Subject: [PATCH 50/60] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/BoardDao.java | 39 +++++++++++------------ src/main/java/db/MovingDao.java | 53 ++++++++++++++++++-------------- src/main/java/db/Repository.java | 1 - src/main/java/db/TurnDao.java | 4 +-- 4 files changed, 49 insertions(+), 48 deletions(-) diff --git a/src/main/java/db/BoardDao.java b/src/main/java/db/BoardDao.java index 4119faf52e0..a9500ebde7c 100644 --- a/src/main/java/db/BoardDao.java +++ b/src/main/java/db/BoardDao.java @@ -10,7 +10,6 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -35,15 +34,12 @@ public void saveBoard(final BoardDto board) { private void savePosition(final PositionDto position, final PieceDto piece) { final String query = "INSERT INTO board VALUES(?, ?, ?)"; try (final Connection connection = DBConnectionUtil.getConnection(database); - final PreparedStatement preparedStatement = connection.prepareStatement(query, - Statement.RETURN_GENERATED_KEYS) + final PreparedStatement preparedStatement = connection.prepareStatement(query) ) { preparedStatement.setString(1, position.value()); preparedStatement.setString(2, piece.type()); preparedStatement.setString(3, piece.camp()); - preparedStatement.executeUpdate(); - } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_SAVE); } @@ -51,33 +47,34 @@ private void savePosition(final PositionDto position, final PieceDto piece) { public BoardDto find() { final String query = "SELECT * FROM board"; - try (final Connection connection = DBConnectionUtil.getConnection(database); final PreparedStatement preparedStatement = connection.prepareStatement(query)) { final ResultSet resultSet = preparedStatement.executeQuery(); - final Map result = new HashMap<>(); - - while (resultSet.next()) { - final PositionDto position = new PositionDto(resultSet.getString("position")); - final String type = resultSet.getString("piece_type"); - final String camp = resultSet.getString("camp"); - final PieceDto piece = new PieceDto(type, camp); - result.put(position, piece); - } - if (result.isEmpty()) { - return BoardDto.from(Board.create()); - } - return new BoardDto(result); + return convert(resultSet); } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_FIND); } } + private BoardDto convert(final ResultSet resultSet) throws SQLException { + final Map result = new HashMap<>(); + while (resultSet.next()) { + final PositionDto position = new PositionDto(resultSet.getString("position")); + final String type = resultSet.getString("piece_type"); + final String camp = resultSet.getString("camp"); + final PieceDto piece = new PieceDto(type, camp); + result.put(position, piece); + } + if (result.isEmpty()) { + return BoardDto.from(Board.create()); + } + return new BoardDto(result); + } + public void remove() { final String query = "TRUNCATE TABLE board"; try (final Connection connection = DBConnectionUtil.getConnection(database); - final PreparedStatement preparedStatement = connection.prepareStatement(query, - Statement.RETURN_GENERATED_KEYS) + final PreparedStatement preparedStatement = connection.prepareStatement(query) ) { preparedStatement.executeUpdate(); } catch (final SQLException exception) { diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 83f7ebdbbc4..7b0e6414f22 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -26,43 +26,51 @@ public long addMoving(final MovingDto moving) { final PreparedStatement preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS) ) { - long autoIncrement = 0; - preparedStatement.setNull(1, 0); - preparedStatement.setString(2, moving.camp()); - preparedStatement.setString(3, moving.current()); - preparedStatement.setString(4, moving.next()); - + preparedStatementSet(moving, preparedStatement); preparedStatement.executeUpdate(); - final ResultSet generatedKeys = preparedStatement.getGeneratedKeys(); - - if (generatedKeys.next()) { - autoIncrement = generatedKeys.getLong(1); - } - return autoIncrement; + return increaseKey(preparedStatement.getGeneratedKeys()); } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_SAVE); } } + private long increaseKey(final ResultSet generatedKeys) throws SQLException { + if (generatedKeys.next()) { + return generatedKeys.getLong(1); + } + return 0; + } + + private void preparedStatementSet(final MovingDto moving, final PreparedStatement preparedStatement) + throws SQLException { + preparedStatement.setNull(1, 0); + preparedStatement.setString(2, moving.camp()); + preparedStatement.setString(3, moving.current()); + preparedStatement.setString(4, moving.next()); + } + public List findAll() { final String query = "SELECT * FROM moving"; - final List moving = new ArrayList<>(); try (final Connection connection = DBConnectionUtil.getConnection(database); final PreparedStatement preparedStatement = connection.prepareStatement(query)) { - final ResultSet resultSet = preparedStatement.executeQuery(); - while (resultSet.next()) { - String camp = resultSet.getString("camp"); - String current = resultSet.getString("start"); - String next = resultSet.getString("destination"); - moving.add(new MovingDto(camp, current, next)); - } - return moving; + return convert(resultSet); } catch (SQLException exception) { throw new DaoException(ErrorCode.FAIL_FIND); } } + private List convert(final ResultSet resultSet) throws SQLException { + final List moving = new ArrayList<>(); + while (resultSet.next()) { + String camp = resultSet.getString("camp"); + String current = resultSet.getString("start"); + String next = resultSet.getString("destination"); + moving.add(new MovingDto(camp, current, next)); + } + return moving; + } + public int countMoving() { final String query = "SELECT count(*) AS count FROM moving"; try (final Connection connection = DBConnectionUtil.getConnection(database); @@ -100,8 +108,7 @@ public MovingDto findByMovementId(final long movementId) { public void remove() { final String query = "TRUNCATE TABLE moving"; try (final Connection connection = DBConnectionUtil.getConnection(database); - final PreparedStatement preparedStatement = connection.prepareStatement(query, - Statement.RETURN_GENERATED_KEYS) + final PreparedStatement preparedStatement = connection.prepareStatement(query) ) { preparedStatement.executeUpdate(); } catch (final SQLException exception) { diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java index dcd68c95887..2958fd5fe24 100644 --- a/src/main/java/db/Repository.java +++ b/src/main/java/db/Repository.java @@ -50,7 +50,6 @@ public ChessGame findGame() { final TurnDto findTurn = findTurn(); final List findMoving = movingDao.findAll(); final Board board = findBoard.convert(); - if (unsaved(findTurn, findMoving)) { restore(findTurn, findMoving, board); } diff --git a/src/main/java/db/TurnDao.java b/src/main/java/db/TurnDao.java index 1da3255a439..75be5db523b 100644 --- a/src/main/java/db/TurnDao.java +++ b/src/main/java/db/TurnDao.java @@ -8,7 +8,6 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; public class TurnDao { @@ -49,8 +48,7 @@ public TurnDto findTurn() { public void remove() { final String query = "TRUNCATE TABLE turn"; try (final Connection connection = DBConnectionUtil.getConnection(database); - final PreparedStatement preparedStatement = connection.prepareStatement(query, - Statement.RETURN_GENERATED_KEYS) + final PreparedStatement preparedStatement = connection.prepareStatement(query) ) { preparedStatement.executeUpdate(); } catch (final SQLException exception) { From 719d55a621f4367efab83df97b02e580db640ed6 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 03:17:07 +0900 Subject: [PATCH 51/60] =?UTF-8?q?refactor:=20dto=20=EB=8B=A4=EC=8B=9C=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EB=B3=80=ED=99=98=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=AA=85=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/Repository.java | 2 +- src/main/java/db/dto/BoardDto.java | 4 ++-- src/main/java/db/dto/PieceDto.java | 2 +- src/main/java/db/dto/PositionDto.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java index 2958fd5fe24..bb6f85d04b9 100644 --- a/src/main/java/db/Repository.java +++ b/src/main/java/db/Repository.java @@ -49,7 +49,7 @@ public ChessGame findGame() { final BoardDto findBoard = findBoard(); final TurnDto findTurn = findTurn(); final List findMoving = movingDao.findAll(); - final Board board = findBoard.convert(); + final Board board = findBoard.toBoard(); if (unsaved(findTurn, findMoving)) { restore(findTurn, findMoving, board); } diff --git a/src/main/java/db/dto/BoardDto.java b/src/main/java/db/dto/BoardDto.java index ea28bce7e71..70a96506204 100644 --- a/src/main/java/db/dto/BoardDto.java +++ b/src/main/java/db/dto/BoardDto.java @@ -21,10 +21,10 @@ public static BoardDto from(final Board board) { return new BoardDto(result); } - public Board convert() { + public Board toBoard() { final Map result = new HashMap<>(); for (Entry entry : pieces.entrySet()) { - result.put(entry.getKey().convert(), entry.getValue().convert()); + result.put(entry.getKey().toPosition(), entry.getValue().toPiece()); } return new Board(result); } diff --git a/src/main/java/db/dto/PieceDto.java b/src/main/java/db/dto/PieceDto.java index d4538aa61c8..9b6b01c18be 100644 --- a/src/main/java/db/dto/PieceDto.java +++ b/src/main/java/db/dto/PieceDto.java @@ -10,7 +10,7 @@ public static PieceDto from(final Piece piece) { return new PieceDto(pieceType.getPieceName(), campType.getColorName()); } - public Piece convert() { + public Piece toPiece() { final CampType army = CampType.findByColorName(camp); final PieceType pieceType = PieceType.findValue(army.getCamp(), type); return pieceType.getPiece(); diff --git a/src/main/java/db/dto/PositionDto.java b/src/main/java/db/dto/PositionDto.java index 8d79c58f676..5c451e94e67 100644 --- a/src/main/java/db/dto/PositionDto.java +++ b/src/main/java/db/dto/PositionDto.java @@ -10,7 +10,7 @@ public static PositionDto from(final Position position) { return new PositionDto(position.toString()); } - public Position convert() { + public Position toPosition() { return new Position(File.from(value.charAt(0)), Rank.from(value.charAt(1))); } } From a094e2f642861271adb9c65a412935fa85078e10 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 03:31:21 +0900 Subject: [PATCH 52/60] =?UTF-8?q?refactor:=20score=20add=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=9D=B4=EB=A6=84=20plus=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/Board.java | 5 ++--- src/main/java/model/Score.java | 2 +- src/test/java/model/ScoreTest.java | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java index 0c39791d728..d322457ff35 100644 --- a/src/main/java/model/Board.java +++ b/src/main/java/model/Board.java @@ -129,9 +129,8 @@ public void move(final Moving moving) { public Score calculateScore(final Camp camp) { final Map pawnCount = countSameFilePawn(camp); final List scores = collectScore(camp); - final Score result = scores.stream() - .reduce(Score::add) + .reduce(Score::plus) .orElse(new Score(0)); return result.minus(duplicateFilePawns(pawnCount)); @@ -159,7 +158,7 @@ private Score duplicateFilePawns(final Map count) { .stream() .filter(sameFilePawnCount -> sameFilePawnCount > 1) .map(sameFilePawnCount -> new Score(sameFilePawnCount * SAME_FILE_PAWN_SCORE.value())) - .reduce(Score::add) + .reduce(Score::plus) .orElse(new Score(0)); } diff --git a/src/main/java/model/Score.java b/src/main/java/model/Score.java index 29cedbbc161..8ea41d6dbd1 100644 --- a/src/main/java/model/Score.java +++ b/src/main/java/model/Score.java @@ -2,7 +2,7 @@ public record Score(float value) { - public Score add(final Score target) { + public Score plus(final Score target) { return new Score(value + target.value); } diff --git a/src/test/java/model/ScoreTest.java b/src/test/java/model/ScoreTest.java index 85f0d38a552..e4ddf56bf66 100644 --- a/src/test/java/model/ScoreTest.java +++ b/src/test/java/model/ScoreTest.java @@ -7,14 +7,25 @@ class ScoreTest { - @Test @DisplayName("두개의 score를 더한다.") + @Test void add() { final Score one = new Score(1); final Score two = new Score(2); final Score expected = new Score(3); - assertThat(one.add(two)).isEqualTo(expected); + assertThat(one.plus(two)).isEqualTo(expected); + } + + @DisplayName("두개의 score를 뺸다.") + @Test + void minus() { + final Score three = new Score(3); + final Score two = new Score(2); + + final Score expected = new Score(1); + + assertThat(three.minus(two)).isEqualTo(expected); } } From 548cfa1638f4273cec48235d137385cc66cd3cc6 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 15:53:17 +0900 Subject: [PATCH 53/60] =?UTF-8?q?refactor:=20db=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=EB=90=9C=20=EC=83=81=EC=88=98=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/db/connection/ConnectionConst.java | 19 ------------------- .../java/db/connection/DBConnectionUtil.java | 14 +++++++------- 2 files changed, 7 insertions(+), 26 deletions(-) delete mode 100644 src/main/java/db/connection/ConnectionConst.java diff --git a/src/main/java/db/connection/ConnectionConst.java b/src/main/java/db/connection/ConnectionConst.java deleted file mode 100644 index d7195396b0f..00000000000 --- a/src/main/java/db/connection/ConnectionConst.java +++ /dev/null @@ -1,19 +0,0 @@ -package db.connection; - -public enum ConnectionConst { - - SERVER("localhost:13306"), - OPTION("?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"), - USERNAME("root"), - PASSWORD("root"); - - private final String value; - - ConnectionConst(final String value) { - this.value = value; - } - - public String getValue() { - return value; - } -} diff --git a/src/main/java/db/connection/DBConnectionUtil.java b/src/main/java/db/connection/DBConnectionUtil.java index 15af2d34b10..a3d607fdeb0 100644 --- a/src/main/java/db/connection/DBConnectionUtil.java +++ b/src/main/java/db/connection/DBConnectionUtil.java @@ -1,10 +1,5 @@ package db.connection; -import static db.connection.ConnectionConst.OPTION; -import static db.connection.ConnectionConst.PASSWORD; -import static db.connection.ConnectionConst.SERVER; -import static db.connection.ConnectionConst.USERNAME; - import constant.ErrorCode; import db.exception.ConnectionException; import java.sql.Connection; @@ -13,13 +8,18 @@ public class DBConnectionUtil { + private static final String SERVER = "localhost:13306"; + private static final String OPTION = "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; + private static final String USERNAME = "root"; + private static final String PASSWORD = "root"; + private DBConnectionUtil() { } public static Connection getConnection(final String database) { try { - final String url = "jdbc:mysql://" + SERVER.getValue() + "/" + database + OPTION.getValue(); - return DriverManager.getConnection(url, USERNAME.getValue(), PASSWORD.getValue()); + final String url = "jdbc:mysql://" + SERVER + "/" + database + OPTION; + return DriverManager.getConnection(url, USERNAME, PASSWORD); } catch (final SQLException exception) { throw new ConnectionException(ErrorCode.CONNECTION); } From 616c59c7baf5d3cc8f4d04e1252e7fb52f3df83c Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 16:21:19 +0900 Subject: [PATCH 54/60] =?UTF-8?q?refactor:=20isPawn,=20isKing=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/Board.java | 10 ++++++++-- src/main/java/model/piece/Piece.java | 8 -------- src/test/java/db/dto/CampTypeTest.java | 2 -- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java index d322457ff35..8b6032ce042 100644 --- a/src/main/java/model/Board.java +++ b/src/main/java/model/Board.java @@ -39,6 +39,8 @@ public class Board { File.G, Knight::new, File.H, Rook::new ); + private static final Set KINGS = Set.of(new King(Camp.WHITE), new King(Camp.BLACK)); + private static final Set PAWNS = Set.of(new WhitePawn(), new BlackPawn()); private static final Score SAME_FILE_PAWN_SCORE = new Score(0.5F); private final Map pieces; @@ -82,7 +84,11 @@ public void validate(final Moving moving, final Camp currentCamp) { } private void validateIsKing(final Position nextPosition) { - if (pieces.containsKey(nextPosition) && pieces.get(nextPosition).isKing()) { + if (!pieces.containsKey(nextPosition)) { + return; + } + final Piece piece = pieces.get(nextPosition); + if (KINGS.contains(piece)) { throw new KingDeadException(ErrorCode.KING_DEAD); } } @@ -164,7 +170,7 @@ private Score duplicateFilePawns(final Map count) { private void checkPawn(final Entry entry, final Map count) { final Piece piece = entry.getValue(); - if (piece.isPawn()) { + if (PAWNS.contains(piece)) { final Position position = entry.getKey(); final File file = position.getFile(); count.put(file, count.getOrDefault(file, 0) + 1); diff --git a/src/main/java/model/piece/Piece.java b/src/main/java/model/piece/Piece.java index 4f905c2a13f..d12622068ff 100644 --- a/src/main/java/model/piece/Piece.java +++ b/src/main/java/model/piece/Piece.java @@ -30,14 +30,6 @@ public boolean isSameCamp(final Camp target) { return camp == target; } - public boolean isPawn() { - return Objects.equals(getClass(), WhitePawn.class) || Objects.equals(getClass(), BlackPawn.class); - } - - public boolean isKing() { - return Objects.equals(getClass(), King.class); - } - @Override public boolean equals(final Object target) { if (this == target) { diff --git a/src/test/java/db/dto/CampTypeTest.java b/src/test/java/db/dto/CampTypeTest.java index 490297ef663..89ded3b227d 100644 --- a/src/test/java/db/dto/CampTypeTest.java +++ b/src/test/java/db/dto/CampTypeTest.java @@ -5,14 +5,12 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import view.message.ErrorCodeMessage; class CampTypeTest { @DisplayName("문자로 Camp를 찾는다.") @Test void findByColor() { - System.out.println(ErrorCodeMessage.FAIL_FIND); assertAll( () -> assertThat(CampType.findByColorName("WHITE")).isEqualTo(CampType.WHITE), () -> assertThat(CampType.findByColorName("BLACK")).isEqualTo(CampType.BLACK) From 0f6324e83c0fef95c84bd95c5dba8df742a8b613 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 16:47:01 +0900 Subject: [PATCH 55/60] =?UTF-8?q?chore:=20drop=20table=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/board.sql | 2 -- src/main/resources/moving.sql | 2 -- src/main/resources/turn.sql | 2 -- src/test/resources/board.sql | 2 -- src/test/resources/moving.sql | 2 -- src/test/resources/turn.sql | 2 -- 6 files changed, 12 deletions(-) diff --git a/src/main/resources/board.sql b/src/main/resources/board.sql index 91d5395421b..7f6154ab713 100644 --- a/src/main/resources/board.sql +++ b/src/main/resources/board.sql @@ -1,7 +1,5 @@ use chess; -drop table if exists board; - create table board ( position varchar(2) not null, diff --git a/src/main/resources/moving.sql b/src/main/resources/moving.sql index 2a17b4f8f3f..d092cdbd5f8 100644 --- a/src/main/resources/moving.sql +++ b/src/main/resources/moving.sql @@ -1,7 +1,5 @@ use chess; -drop table if exists moving; - create table moving ( movement_id INT primary key auto_increment, diff --git a/src/main/resources/turn.sql b/src/main/resources/turn.sql index cc2d391fd8e..00e339248b7 100644 --- a/src/main/resources/turn.sql +++ b/src/main/resources/turn.sql @@ -1,7 +1,5 @@ use chess; -drop table if exists turn; - create table turn ( camp varchar(5) not null, diff --git a/src/test/resources/board.sql b/src/test/resources/board.sql index 5ebbdf9bc54..4692c4edd38 100644 --- a/src/test/resources/board.sql +++ b/src/test/resources/board.sql @@ -1,7 +1,5 @@ use chess_test; -drop table if exists board; - create table board ( position varchar(2) not null, diff --git a/src/test/resources/moving.sql b/src/test/resources/moving.sql index f0f01e270af..b1fbcefe0af 100644 --- a/src/test/resources/moving.sql +++ b/src/test/resources/moving.sql @@ -1,7 +1,5 @@ use chess_test; -drop table if exists moving; - create table moving ( movement_id INT primary key auto_increment, diff --git a/src/test/resources/turn.sql b/src/test/resources/turn.sql index 02ed0d1b3c7..ed9675d1736 100644 --- a/src/test/resources/turn.sql +++ b/src/test/resources/turn.sql @@ -1,7 +1,5 @@ use chess_test; -drop table if exists turn; - create table turn ( camp varchar(5) not null, From 7b879873b83dec123fc3be65f38a8267dfd118e1 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 16:59:21 +0900 Subject: [PATCH 56/60] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=ED=98=B9=EC=9D=80=20=EC=82=AC=EC=9A=A9=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8D=98=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/command/Command.java | 22 ------------------- src/main/java/model/position/Position.java | 4 ---- .../java/model/command/CommandLineTest.java | 1 + src/test/java/model/command/CommandTest.java | 19 ---------------- 4 files changed, 1 insertion(+), 45 deletions(-) diff --git a/src/main/java/model/command/Command.java b/src/main/java/model/command/Command.java index 5ea841b4e3d..c12e5e29ece 100644 --- a/src/main/java/model/command/Command.java +++ b/src/main/java/model/command/Command.java @@ -3,7 +3,6 @@ import constant.ErrorCode; import exception.InvalidCommandException; import java.util.Arrays; -import java.util.List; import java.util.regex.Pattern; public enum Command { @@ -15,8 +14,6 @@ public enum Command { QUIT(Pattern.compile("quit"), 0), END(Pattern.compile("end"), 0); - public static final int HEAD_INDEX = 0; - private final Pattern pattern; private final int bodySize; @@ -32,25 +29,6 @@ public static Command from(String value) { .orElseThrow(() -> new InvalidCommandException(ErrorCode.INVALID_COMMAND)); } - public static void validate(List values) { - if (values == null || values.isEmpty()) { - throw new InvalidCommandException(ErrorCode.INVALID_COMMAND); - } - values.forEach(Command::validate); - Command command = Command.from(values.get(HEAD_INDEX)); - if (values.size() != command.bodySize) { - throw new InvalidCommandException(ErrorCode.INVALID_COMMAND); - } - } - - private static void validate(String input) { - boolean match = Arrays.stream(values()) - .anyMatch(command -> command.pattern.matcher(input).matches()); - if (!match) { - throw new InvalidCommandException(ErrorCode.INVALID_COMMAND); - } - } - public boolean isEqualToBodySize(int targetSize) { return bodySize == targetSize; } diff --git a/src/main/java/model/position/Position.java b/src/main/java/model/position/Position.java index fc34b8778b5..2d2ba7026f1 100644 --- a/src/main/java/model/position/Position.java +++ b/src/main/java/model/position/Position.java @@ -40,10 +40,6 @@ public File getFile() { return file; } - public Rank getRank() { - return rank; - } - @Override public int hashCode() { return Objects.hash(rank, file); diff --git a/src/test/java/model/command/CommandLineTest.java b/src/test/java/model/command/CommandLineTest.java index fa81a315fb5..c5efd8c887d 100644 --- a/src/test/java/model/command/CommandLineTest.java +++ b/src/test/java/model/command/CommandLineTest.java @@ -23,6 +23,7 @@ void invalidInput(List input) { static Stream invalidInputParameterProvider() { return Stream.of( + Arguments.of(List.of()), Arguments.of(List.of("start", "a2", "a3")), Arguments.of(List.of("start", "end")), Arguments.of(List.of("move", "a2")), diff --git a/src/test/java/model/command/CommandTest.java b/src/test/java/model/command/CommandTest.java index 2079e9f1c1e..6403fa9478e 100644 --- a/src/test/java/model/command/CommandTest.java +++ b/src/test/java/model/command/CommandTest.java @@ -1,10 +1,8 @@ package model.command; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; -import exception.InvalidCommandException; import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; @@ -28,23 +26,6 @@ void checkCommand() { ); } - @ParameterizedTest - @DisplayName("유효하지 않은 command가 입력되면 예외가 발생한다.") - @MethodSource("invalidCommandParameterProvider") - void invalidCommand(List value) { - assertThatThrownBy(() -> Command.validate(value)) - .isInstanceOf(InvalidCommandException.class); - } - - static Stream invalidCommandParameterProvider() { - return Stream.of( - Arguments.of(List.of("start", "end")), - Arguments.of(List.of("sta")), - Arguments.of(List.of("end", "a1", "a2")), - Arguments.of(List.of("move")) - ); - } - @DisplayName("각 command 의 body 사이즈를 확인한다.") @ParameterizedTest @MethodSource("checkBodySizeParameterProvider") From bfd213972dcec69656029fad73e5a3ae320d3ecf Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 17:38:32 +0900 Subject: [PATCH 57/60] =?UTF-8?q?refactor:=20pieceType=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20camp=20=EC=9D=B8=EC=8A=A4=ED=84=B4=EC=8A=A4=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/dto/PieceType.java | 35 +++++++++++++++-------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/main/java/db/dto/PieceType.java b/src/main/java/db/dto/PieceType.java index bbf38415013..2817b4bf109 100644 --- a/src/main/java/db/dto/PieceType.java +++ b/src/main/java/db/dto/PieceType.java @@ -1,6 +1,7 @@ package db.dto; import java.util.Arrays; +import java.util.List; import model.Camp; import model.piece.Bishop; import model.piece.BlackPawn; @@ -13,27 +14,25 @@ public enum PieceType { - WHITE_KING(new King(Camp.WHITE), "King", Camp.WHITE), - WHITE_QUEEN(new Queen(Camp.WHITE), "Queen", Camp.WHITE), - WHITE_ROOK(new Rook(Camp.WHITE), "Rook", Camp.WHITE), - WHITE_BISHOP(new Bishop(Camp.WHITE), "Bishop", Camp.WHITE), - WHITE_KNIGHT(new Knight(Camp.WHITE), "Knight", Camp.WHITE), - WHITE_PAWN(new WhitePawn(), "Pawn", Camp.WHITE), - BLACK_KING(new King(Camp.BLACK), "King", Camp.BLACK), - BLACK_QUEEN(new Queen(Camp.BLACK), "Queen", Camp.BLACK), - BLACK_ROOK(new Rook(Camp.BLACK), "Rook", Camp.BLACK), - BLACK_BISHOP(new Bishop(Camp.BLACK), "Bishop", Camp.BLACK), - BLACK_KNIGHT(new Knight(Camp.BLACK), "Knight", Camp.BLACK), - BLACK_PAWN(new BlackPawn(), "Pawn", Camp.BLACK); + WHITE_KING(new King(Camp.WHITE), "King"), + WHITE_QUEEN(new Queen(Camp.WHITE), "Queen"), + WHITE_ROOK(new Rook(Camp.WHITE), "Rook"), + WHITE_BISHOP(new Bishop(Camp.WHITE), "Bishop"), + WHITE_KNIGHT(new Knight(Camp.WHITE), "Knight"), + WHITE_PAWN(new WhitePawn(), "Pawn"), + BLACK_KING(new King(Camp.BLACK), "King"), + BLACK_QUEEN(new Queen(Camp.BLACK), "Queen"), + BLACK_ROOK(new Rook(Camp.BLACK), "Rook"), + BLACK_BISHOP(new Bishop(Camp.BLACK), "Bishop"), + BLACK_KNIGHT(new Knight(Camp.BLACK), "Knight"), + BLACK_PAWN(new BlackPawn(), "Pawn"); private final Piece piece; private final String pieceName; - private final Camp camp; - PieceType(final Piece piece, final String pieceName, final Camp camp) { + PieceType(final Piece piece, final String pieceName) { this.piece = piece; this.pieceName = pieceName; - this.camp = camp; } public static PieceType findValue(final Piece piece) { @@ -44,9 +43,11 @@ public static PieceType findValue(final Piece piece) { } public static PieceType findValue(final Camp camp, final String type) { - return Arrays.stream(values()) + final List pieceTypes = Arrays.stream(values()) .filter(pieceType -> pieceType.pieceName.equals(type)) - .filter(pieceType -> pieceType.camp == camp) + .toList(); + return pieceTypes.stream() + .filter(pieceType -> pieceType.piece.isSameCamp(camp)) .findFirst() .orElseThrow(); } From 3ade17e3d24846fe0564ae69b480ac687a256324 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 17:48:15 +0900 Subject: [PATCH 58/60] =?UTF-8?q?refactor:=20dao=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/BoardDao.java | 22 +++++++++++++------ src/main/java/db/MovingDao.java | 39 +++++++++++++++++++-------------- src/main/java/db/TurnDao.java | 19 ++++++++-------- 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/src/main/java/db/BoardDao.java b/src/main/java/db/BoardDao.java index a9500ebde7c..a84c9183bbd 100644 --- a/src/main/java/db/BoardDao.java +++ b/src/main/java/db/BoardDao.java @@ -34,8 +34,7 @@ public void saveBoard(final BoardDto board) { private void savePosition(final PositionDto position, final PieceDto piece) { final String query = "INSERT INTO board VALUES(?, ?, ?)"; try (final Connection connection = DBConnectionUtil.getConnection(database); - final PreparedStatement preparedStatement = connection.prepareStatement(query) - ) { + final PreparedStatement preparedStatement = connection.prepareStatement(query)) { preparedStatement.setString(1, position.value()); preparedStatement.setString(2, piece.type()); preparedStatement.setString(3, piece.camp()); @@ -59,11 +58,9 @@ public BoardDto find() { private BoardDto convert(final ResultSet resultSet) throws SQLException { final Map result = new HashMap<>(); while (resultSet.next()) { - final PositionDto position = new PositionDto(resultSet.getString("position")); - final String type = resultSet.getString("piece_type"); - final String camp = resultSet.getString("camp"); - final PieceDto piece = new PieceDto(type, camp); - result.put(position, piece); + final PieceDto piece = convertToPiece(resultSet); + final PositionDto positionDto = convertToPosition(resultSet); + result.put(positionDto, piece); } if (result.isEmpty()) { return BoardDto.from(Board.create()); @@ -71,6 +68,17 @@ private BoardDto convert(final ResultSet resultSet) throws SQLException { return new BoardDto(result); } + private PieceDto convertToPiece(final ResultSet resultSet) throws SQLException { + final String type = resultSet.getString("piece_type"); + final String camp = resultSet.getString("camp"); + return new PieceDto(type, camp); + } + + private PositionDto convertToPosition(final ResultSet resultSet) throws SQLException { + final String position = resultSet.getString("position"); + return new PositionDto(position); + } + public void remove() { final String query = "TRUNCATE TABLE board"; try (final Connection connection = DBConnectionUtil.getConnection(database); diff --git a/src/main/java/db/MovingDao.java b/src/main/java/db/MovingDao.java index 7b0e6414f22..edb3551b6ea 100644 --- a/src/main/java/db/MovingDao.java +++ b/src/main/java/db/MovingDao.java @@ -24,8 +24,7 @@ public long addMoving(final MovingDto moving) { final String query = "INSERT INTO moving VALUES(?, ?, ?, ?)"; try (final Connection connection = DBConnectionUtil.getConnection(database); final PreparedStatement preparedStatement = connection.prepareStatement(query, - Statement.RETURN_GENERATED_KEYS) - ) { + Statement.RETURN_GENERATED_KEYS)) { preparedStatementSet(moving, preparedStatement); preparedStatement.executeUpdate(); return increaseKey(preparedStatement.getGeneratedKeys()); @@ -76,40 +75,46 @@ public int countMoving() { try (final Connection connection = DBConnectionUtil.getConnection(database); final PreparedStatement preparedStatement = connection.prepareStatement(query)) { final ResultSet resultSet = preparedStatement.executeQuery(); - if (resultSet.next()) { - return resultSet.getInt("count"); - } - throw new DaoException(ErrorCode.FAIL_FIND); + return convertToCount(resultSet); } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_FIND); } } + private int convertToCount(final ResultSet resultSet) throws SQLException { + if (resultSet.next()) { + return resultSet.getInt("count"); + } + throw new DaoException(ErrorCode.FAIL_FIND); + } + public MovingDto findByMovementId(final long movementId) { final String query = "SELECT * FROM moving WHERE movement_id = ?"; try (final Connection connection = DBConnectionUtil.getConnection(database); final PreparedStatement preparedStatement = connection.prepareStatement(query)) { preparedStatement.setLong(1, movementId); final ResultSet resultSet = preparedStatement.executeQuery(); - - if (resultSet.next()) { - return new MovingDto( - resultSet.getString("camp"), - resultSet.getString("start"), - resultSet.getString("destination") - ); - } - throw new DaoException(ErrorCode.FAIL_FIND); + return convertToMoving(resultSet); } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_FIND); } } + private MovingDto convertToMoving(final ResultSet resultSet) throws SQLException { + if (resultSet.next()) { + return new MovingDto( + resultSet.getString("camp"), + resultSet.getString("start"), + resultSet.getString("destination") + ); + } + throw new DaoException(ErrorCode.FAIL_FIND); + } + public void remove() { final String query = "TRUNCATE TABLE moving"; try (final Connection connection = DBConnectionUtil.getConnection(database); - final PreparedStatement preparedStatement = connection.prepareStatement(query) - ) { + final PreparedStatement preparedStatement = connection.prepareStatement(query)) { preparedStatement.executeUpdate(); } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_DELETE); diff --git a/src/main/java/db/TurnDao.java b/src/main/java/db/TurnDao.java index 75be5db523b..99542af9855 100644 --- a/src/main/java/db/TurnDao.java +++ b/src/main/java/db/TurnDao.java @@ -20,8 +20,7 @@ public TurnDao(final String database) { public void saveTurn(final TurnDto turnDto) { final String query = "INSERT INTO turn values(?, ?)"; try (final Connection connection = DBConnectionUtil.getConnection(database); - final PreparedStatement preparedStatement = connection.prepareStatement(query) - ) { + final PreparedStatement preparedStatement = connection.prepareStatement(query)) { preparedStatement.setString(1, turnDto.currentCamp()); preparedStatement.setInt(2, turnDto.count()); preparedStatement.executeUpdate(); @@ -32,24 +31,26 @@ public void saveTurn(final TurnDto turnDto) { public TurnDto findTurn() { final String query = "SELECT * FROM turn"; - try (final Connection connection = DBConnectionUtil.getConnection(database); final PreparedStatement preparedStatement = connection.prepareStatement(query)) { final ResultSet resultSet = preparedStatement.executeQuery(); - if (resultSet.next()) { - return new TurnDto(resultSet.getString("camp"), resultSet.getInt("count")); - } - return new TurnDto("WHITE", 0); + return convertToTurn(resultSet); } catch (SQLException exception) { throw new DaoException(ErrorCode.FAIL_FIND); } } + private TurnDto convertToTurn(final ResultSet resultSet) throws SQLException { + if (resultSet.next()) { + return new TurnDto(resultSet.getString("camp"), resultSet.getInt("count")); + } + return new TurnDto("WHITE", 0); + } + public void remove() { final String query = "TRUNCATE TABLE turn"; try (final Connection connection = DBConnectionUtil.getConnection(database); - final PreparedStatement preparedStatement = connection.prepareStatement(query) - ) { + final PreparedStatement preparedStatement = connection.prepareStatement(query)) { preparedStatement.executeUpdate(); } catch (final SQLException exception) { throw new DaoException(ErrorCode.FAIL_DELETE); From 54bd0e5b49418d3caf4c7bc3bde0ccbb831e0da6 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 17:52:37 +0900 Subject: [PATCH 59/60] =?UTF-8?q?docs:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=8F=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=A0=95=EB=8F=84=20=EB=B0=94=EB=A1=9C=EA=B0=80?= =?UTF-8?q?=EA=B8=B0=20=EB=A7=81=ED=81=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 ++++++++++++ src/main/java/db/Repository.java | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8102f91c870..0371d567c28 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,18 @@ 체스 미션 저장소 + +### [기능 요구 사항 바로가기](https://github.com/reddevilmidzy/java-chess/blob/62e632aa09435db4f69502e663a0cc7ef78a31bb/docs/README.md) + +
+ +### [product db 스키마](https://github.com/reddevilmidzy/java-chess/blob/0f6324e83c0fef95c84bd95c5dba8df742a8b613/src/main/resources0) + +
+ +### [test db 스키마](https://github.com/reddevilmidzy/java-chess/blob/0f6324e83c0fef95c84bd95c5dba8df742a8b613/src/test/resources) + + ## 우아한테크코스 코드리뷰 - [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) diff --git a/src/main/java/db/Repository.java b/src/main/java/db/Repository.java index bb6f85d04b9..64dd8b46ba6 100644 --- a/src/main/java/db/Repository.java +++ b/src/main/java/db/Repository.java @@ -12,7 +12,7 @@ import model.position.Moving; import model.position.Position; -public class Repository { //TODO 이름 변경 +public class Repository { private final MovingDao movingDao; private final TurnDao turnDao; From 2e17f4566f2c5f3677a2c4a842d605c6fdf290f5 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 1 Apr 2024 17:53:45 +0900 Subject: [PATCH 60/60] =?UTF-8?q?style:=20todo=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/ChessController.java | 2 -- src/test/java/db/RepositoryTest.java | 1 - 2 files changed, 3 deletions(-) diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 293953b9f75..4d235ba49b9 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -22,7 +22,6 @@ public class ChessController { private final InputView inputView; private final OutputView outputView; - // TODO repository 분리 private final Repository repository = new Repository("chess"); public ChessController(final InputView inputView, final OutputView outputView) { @@ -57,7 +56,6 @@ private void save(final GameStatus gameStatus, final ChessGame chessGame) { } private ChessGame create() { - //TODO 여기서 hasGame 체크할까 아니면 그냥 리포지토리 안으로 책임 넘길까 if (repository.hasGame()) { return repository.findGame(); } diff --git a/src/test/java/db/RepositoryTest.java b/src/test/java/db/RepositoryTest.java index 1ae28667088..b313c5968f3 100644 --- a/src/test/java/db/RepositoryTest.java +++ b/src/test/java/db/RepositoryTest.java @@ -43,7 +43,6 @@ void hasNoGame() { void createNewChessGame() { final ChessGame game = repository.findGame(); final ChessGame expected = ChessGame.setupStartingPosition(); - // TODO 이 때 chessgame객체 equal 재정의 안하고 이렇게 꺼내서 비교하는 방법 괜춘? assertAll( () -> assertThat(game.getPieces()).isEqualTo(expected.getPieces()), () -> assertThat(game.getCamp()).isEqualTo(expected.getCamp()),