Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3, 4단계 - 체스] 커찬(이충안) 미션 제출합니다. #21

Open
wants to merge 38 commits into
base: leegwichan
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
91a3e25
refactor(Team): 수정이 용이하도록 메서드 재사용
leegwichan Mar 26, 2024
607649f
refactor(PawnMovement) : 매직 넘버에 의미 부여 및 가시성 개선
leegwichan Mar 26, 2024
8ac528f
feat(Piece): 왕 여부 판단
leegwichan Mar 26, 2024
9abcbf8
feat(Board): 해당 게임의 계속 진행할 수 있는지 판단
leegwichan Mar 26, 2024
edd54fa
feat(ChessGame): 왕이 잡히면 게임이 종료됨
leegwichan Mar 26, 2024
e05d029
feat(Point): 기물 점수 더하기
leegwichan Mar 27, 2024
3625575
feat(Piece): 자신의 기물 점수 판단
leegwichan Mar 27, 2024
eff640d
refactor(Point): 패키지 이동
leegwichan Mar 27, 2024
e20e78c
test(Piece): 각 기물별 점수 테스트 추가
leegwichan Mar 27, 2024
2057f00
feat(Board): 팀별 기물 점수 계산
leegwichan Mar 27, 2024
e7f9c78
feat(ChessGame): 현재 팀별 말 점수 출력 기능
leegwichan Mar 27, 2024
23716a6
refactor: 메서드 길이를 줄이기 위해 리팩터링 실시
leegwichan Mar 28, 2024
cbc4b37
test(BoardTest): Optional assert 구문 수정
leegwichan Mar 29, 2024
a0db668
docs: 4단계 추가 요구사항 맟 db 설정 정리
leegwichan Mar 29, 2024
2d395f8
refactor(OutputView): 가독성을 위해 메서드 분리
leegwichan Mar 30, 2024
e955a9e
feat(Type): 데이터베이스 관리의 편의성을 위해 null 객체 추가
leegwichan Mar 30, 2024
ba2012b
feat(ChessDao): 데이터베이스 저장을 위한 Dao 계층 생성
leegwichan Mar 30, 2024
710bf81
feat(TurnType) : TeamType괴의 구분을 위해 구현
leegwichan Mar 30, 2024
3cf4dc0
test(BoardTest): entry 순서에 영향을 받지 않도록 수정
leegwichan Mar 30, 2024
8c7e15b
feat(ChessGameRepository): 체크 게임의 턴 상황 CRUD
leegwichan Mar 30, 2024
8fd21fe
feat(MysqlPieceRepository): 기물 상황 CRUD
leegwichan Mar 30, 2024
56f5451
feat(BoardService): 보드 초기화 구현
leegwichan Mar 31, 2024
4e47860
feat(ChessService) : 서비스 레이어 구성
leegwichan Mar 31, 2024
e5e006c
feat(ChessGame): ChessService와 연결되도록 구현
leegwichan Mar 31, 2024
da3bbf1
refactor(OutputView): printf 기능을 사용하도록 리팩터링
leegwichan Mar 31, 2024
db1b6c3
feat(ChessGame): 시작 시에 현재 누구의 차례인지 안내하는 문구 출력
leegwichan Mar 31, 2024
f7cf3ff
fix(OutputView): 추가 기능 출력
leegwichan Apr 1, 2024
e50ea69
refactor (ChessService) : null 객체를 이용하여 출력하도록 수정
leegwichan Apr 1, 2024
2df6c31
refactor(ChessService): Optional의 편의 메서드를 이용하여 if문 제거
leegwichan Apr 1, 2024
bcf5d26
style(ChessService): 현재 턴을 찾는다는 내용을 강조하기 위해 메서드 이름 변경
leegwichan Apr 1, 2024
2317cc3
refactor(ChessService): 가독성을 위해 PieceEntity의 메서드 구현
leegwichan Apr 1, 2024
4564d64
refactor(PawnMovement): 폰 움직임 추상화
leegwichan Apr 1, 2024
7d6b083
refactor(MysqlPieceRepository): 인덴트 조건을 지키기 위해 메서드 분리
leegwichan Apr 1, 2024
bffe03f
style(sql): 테이블 이름 복수형으로 변경
leegwichan Apr 1, 2024
c3bbcf1
refactor(Repository): 숫자가 아닌 필드의 이름으로 조회하도록 변경
leegwichan Apr 1, 2024
b241845
refactor(Piece) : getter로 Team을 제공하기 때문에, isBlack() 메서드 제거
leegwichan Apr 1, 2024
00e8d61
docs(README): 실행 방법 서술
leegwichan Apr 1, 2024
a2f0d0c
style(Piece): 조건을 명확하게 하기 위해 변수명 변경
leegwichan Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,4 @@ out/

### VS Code ###
.vscode/

db/

docker-compose.yml
24 changes: 19 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# java-chess

## 페어와 지킬 컨벤션
1. 클래스 정의 다음 줄은 공백으로 한다.
2. test code에 사용하는 메서드는 `static import`한다.
3. `this`는 같은 클래스의 객체가 파라미터로 넘어왔을 때, 파라미터 변수 명이 필드의 변수 명과 겹칠 때 사용한다.
## 실행 방법
1. [database](./database) 폴더로 이동한다.
2. [docker-compose.yml](./database/docker-compose.yml)를 통하여 docker를 실행한다.
3. [init.sql](./database/init.sql)의 명령어를 docker 내부에서 차례로 실행한다.
4. IDE를 통해 ChessApplication를 실행한다.

## 기능 요구 사항

### 체스 말
- [x] 무슨 팀인지 알려준다.
- [x] 자신의 기물 점수를 알려준다.
- [x] queen 9점, rook 5점, bishop 3점, knight 2.5점, king 0점
- [x] pawn은 1점, 같은 세로줄에 같은 색의 폰이 있는 경우 0.5점

### 움직임
- [x] 이동 가능한지 판단한다.
Expand All @@ -17,6 +21,9 @@
### 팀
- [x] 흰색, 검은색을 구분한다.

### 말 점수
- [x] 말 점수를 서로 더할 수 있다.

### 체스 보드
- [x] 체스 말 위치 초기화를 한다.
- [x] 해당 위치에 어떤 말이 있는지 알려준다.
Expand All @@ -26,13 +33,20 @@
- [x] 이동 경로에 다른 말이 있을 경우 예외
- [x] 마지막 위치에 적 말이 있을 경우, 잡아먹는다.
- [x] 흰색부터 번갈아가며 플레이한다.

- [x] 상대편 왕을 잡으면, 해당 팀의 게임 승리로 게임을 종료한다.
- [x] 현재 각 팀별 기물 점수를 구한다.

### 위치
- [x] 가로 위치(왼쪽부터 a~h)를 저장한다.
- [x] 세로 위치(아래부터 1~8)를 저장한다.
- [x] 서로 같은 위치인지 판단한다.
- [x] 다음 동, 서, 남, 북쪽 위치를 알려준다.

### 저장소
- [x] 각 말을 한 번 움직임을 저장할 수 있다.
- [x] 말의 전체 위치를 저장할 수 있다.
- [x] 게임이 끝나, 저장소를 초기화할 수 있다.
- [x] 현재 진행중인 게임이 있는지 알 수 있다.

### 출력
- [x] 체스판에서 각 진영은 검은색(대문자)과 흰색(소문자) 편으로 구분한다.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ dependencies {
testImplementation platform('org.assertj:assertj-bom:3.25.1')
testImplementation('org.junit.jupiter:junit-jupiter')
testImplementation('org.assertj:assertj-core')

runtimeOnly("com.mysql:mysql-connector-j:8.3.0")
}

java {
Expand Down
18 changes: 18 additions & 0 deletions database/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions database/init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
CREATE DATABASE IF NOT EXISTS chess;

USE chess;

CREATE TABLE chess_game (
turn VARCHAR(10) CHECK (turn IN ('BLACK', 'WHITE')) not null,
PRIMARY KEY (turn)
);

CREATE TABLE pieces (
board_file VARCHAR(2) CHECK (board_file IN ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H')) not null,
board_rank VARCHAR(2) CHECK (board_rank IN ('1', '2', '3', '4', '5', '6', '7', '8')) not null,
type VARCHAR(10) CHECK (type IN ('KING', 'QUEEN', 'ROOK', 'BISHOP', 'KNIGHT', 'PAWN', 'EMPTY')) not null,
team VARCHAR(10) CHECK (team IN ('BLACK', 'WHITE', 'EMPTY')) not null,
PRIMARY KEY (board_rank, board_file)
Comment on lines +6 to +15
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테이블 명이나 컬럼명은 소문자로 하고 예약어는 대문자 컨벤션을 유지한거 같은데 not null은 소문자인 이유 궁금하군요!👀

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어 그러게요 ㅎ;
대문자로 통일하는게 좋겠네요 ㅎㅎ

);
26 changes: 21 additions & 5 deletions src/main/java/chess/ChessApplication.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
package chess;

import chess.dao.ChessDao;
import chess.dao.ChessGameRepository;
import chess.dao.ConnectionManager;
import chess.dao.MysqlChessGameRepository;
import chess.dao.MysqlPieceRepository;
import chess.dao.PieceRepository;
import chess.view.InputView;
import chess.view.OutputView;

public class ChessApplication {

private static final InputView INPUT_VIEW = new InputView();
private static final OutputView OUTPUT_VIEW = new OutputView();

public static void main(String[] args) {
Comment on lines +14 to 17
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

view 주입을 방식을 변경하게 된 이유가 궁금해요

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메서드 줄 수 줄일려고 했는데, 통일성이 없어서 수정했습니다.

    public static void main(String[] args) {
        ConnectionManager connectionManager = new ConnectionManager();
        ChessService chessService = createChessService(connectionManager);
        InputView inputView = new InputView();
        OutputView outputView = new OutputView();
        ChessController chessController = new ChessController(chessService, inputView, outputView);

        try {
            chessController.run();
        } finally {
            connectionManager.closeConnection();
        }
    }

    private static ChessService createChessService(ConnectionManager connectionManager) {
        ChessGameRepository chessGameRepository = new MysqlChessGameRepository(connectionManager);
        PieceRepository pieceRepository = new MysqlPieceRepository(connectionManager);
        ChessDao chessDao = new ChessDao(chessGameRepository, pieceRepository);
        return new ChessService(chessDao);
    }

InputView inputView = new InputView();
OutputView outputView = new OutputView();
ChessGame chessGame = new ChessGame(inputView, outputView);
ConnectionManager connectionManager = new ConnectionManager();
ChessGameRepository chessGameRepository = new MysqlChessGameRepository(connectionManager);
PieceRepository pieceRepository = new MysqlPieceRepository(connectionManager);
ChessDao chessDao = new ChessDao(chessGameRepository, pieceRepository);
ChessService chessService = new ChessService(chessDao);

ChessGame chessGame = new ChessGame(INPUT_VIEW, OUTPUT_VIEW, chessService);
run(chessGame);
}

private static void run(ChessGame chessGame) {
try {
chessGame.start();
chessGame.run();
} catch (IllegalArgumentException exception) {
outputView.printExceptionMessage(exception);
OUTPUT_VIEW.printExceptionMessage(exception);
}
}
}
90 changes: 53 additions & 37 deletions src/main/java/chess/ChessGame.java
Original file line number Diff line number Diff line change
@@ -1,81 +1,97 @@
package chess;

import chess.domain.Board;
import chess.domain.BoardFactory;
import chess.domain.piece.Piece;
import chess.domain.Team;
import chess.domain.position.Position;
import chess.dto.PieceDto;
import chess.dto.ProgressStatus;
import chess.view.GameCommand;
import chess.view.InputView;
import chess.view.OutputView;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class ChessGame {

private final InputView inputView;
private final OutputView outputView;
private final ChessService chessService;

public ChessGame(InputView inputView, OutputView outputView) {
public ChessGame(InputView inputView, OutputView outputView, ChessService chessService) {
this.inputView = inputView;
this.outputView = outputView;
this.chessService = chessService;
}

public void start() {
public void run() {
startGame();
play();
}

private void startGame() {
outputView.printStartGame();
GameCommand command = inputView.readCommand();
if (command.isStart()) {
Board board = BoardFactory.createInitBoard();
showBoard(board);
play(board);
}
if (command.isMove()) {
throw new IllegalArgumentException("아직 게임을 시작하지 않았습니다.");
initBoard();
showCurrentTeam();
showBoard();
return;
}
throw new IllegalArgumentException("아직 게임을 시작하지 않았습니다.");
}

private void play(Board board) {
while (true) {
processTurn(board);
}
private void initBoard() {
chessService.init();
}

private void showCurrentTeam() {
Team turn = chessService.findCurrentTurn();
outputView.printCurrentTurn(turn);
}

private void processTurn(Board board) {
private void play() {
ProgressStatus status;
do {
status = processTurn();
} while (status.isContinue());
showResult(status);
}

private ProgressStatus processTurn() {
GameCommand command = inputView.readCommand();
if (command.isStart()) {
throw new IllegalArgumentException("이미 게임을 시작했습니다.");
}
if (command.isEnd()) {
System.exit(0);
Comment on lines -50 to -51
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

System.exit(0) 를 사용하지 않은 건 좋군요!👍

if (command.isMove()) {
return executeMove();
}
if (command.isStatus()) {
return executeStatus();
}
executeMove(board);
Comment on lines +58 to -53
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if문으로 분기하니, 의도가 명확하게 보여서 좋은 것 같아요 👍
하지만 더 많은 커맨드가 추가된다면 어떨까요? 유지보수 관점에서 어떻게 생각하는지 궁금해요 💭

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

커멘드가 5~6개로 늘어난다면, 다른 대안을 생각해볼 것 같아.

지금과 같이 생각한 이유는 '특정 커멘드에 따라 행동하는 것은 하나의 파일로 같이 있어야 하지 않을까?'라는 생각이 있었어. 그런데 커멘드가 5~6개로 늘어난다면 지금과 같이 한 파일로 보는 것이 더 힘들것 같네...

return ProgressStatus.END_GAME;
}

private void executeMove(Board board) {
private ProgressStatus executeMove() {
Position start = inputView.readPosition();
Position end = inputView.readPosition();
board.move(start, end);
showBoard(board);
ProgressStatus status = chessService.moveTo(start, end);
showBoard();
return status;
}

private void showBoard(Board board) {
List<Position> positions = Position.ALL_POSITIONS;
Map<Position, PieceDto> boardDto = new HashMap<>();
positions.forEach(position -> addPiece(board, position, boardDto));
outputView.printBoard(boardDto);
private ProgressStatus executeStatus() {
Map<Team, Double> statusDto = chessService.calculatePiecePoints();
outputView.printStatus(statusDto);
return ProgressStatus.PROGRESS;
}

private void addPiece(Board board, Position position, Map<Position, PieceDto> boardDto) {
Optional<Piece> optionalPiece = board.find(position);

if (optionalPiece.isEmpty()) {
private void showResult(ProgressStatus status) {
if (status.isInputEndCommand()) {
return;
}
outputView.printWinnerMessage(status);
}

Piece piece = optionalPiece.get();
PieceDto pieceDto = PieceDto.from(piece);
boardDto.put(position, pieceDto);
private void showBoard() {
Map<Position, PieceDto> boardDto = chessService.findTotalBoard();
outputView.printBoard(boardDto);
}
}
Loading