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

박스터의 허브의 어제 #14

Open
wants to merge 43 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
ff4f19d
feat: remove web dependency
woowahan-neo Mar 13, 2023
1bab9ff
feat: add example for functional programming api example
woowahan-neo Mar 13, 2023
98598c0
chore: change package
woowahan-neo Mar 13, 2023
9524490
[1, 2단계 - 체스] 박스터(한우석) 미션 제출합니다. (#501)
drunkenhw Mar 21, 2023
757581a
style: 불필요한 개행 제거
drunkenhw Mar 22, 2023
1e42ee9
refactor: 네이밍 변경
drunkenhw Mar 22, 2023
f040e2b
refactor: 매직넘버 제거
drunkenhw Mar 22, 2023
3fd392c
refactor: pawn의 방향을 결정하는 로직 이동
drunkenhw Mar 22, 2023
691240e
refactor: 메서드 네이밍 변경
drunkenhw Mar 22, 2023
50292db
refactor: Turn의 생성 로직 변경
drunkenhw Mar 22, 2023
28ce53f
docs: 기능 구현 목록 추가
drunkenhw Mar 23, 2023
a1db5f3
refactor: 불필요한 메소드 제거
drunkenhw Mar 23, 2023
1463bd9
feat: RoleType 추가
drunkenhw Mar 23, 2023
184de2a
refactor: 빈 Piece인지 확인하는 로직 변경
drunkenhw Mar 23, 2023
c9436a7
feat: 체스 게임의 점수를 계산하는 기능 추가
drunkenhw Mar 23, 2023
d0240e6
feat: 왕이 죽었는지 확인하는 기능 추가
drunkenhw Mar 24, 2023
fb8b3d5
refactor: 컨트롤러 처리 방법 변경
drunkenhw Mar 24, 2023
9819456
feat: GameResult class 추가
drunkenhw Mar 25, 2023
da8e6af
feat: DB 연결 추가
drunkenhw Mar 25, 2023
3e764b8
refactor: 컨트롤러 분기처리 방법 변경
drunkenhw Mar 25, 2023
118ec1d
feat: status 출력 메세지 추가
drunkenhw Mar 26, 2023
40058d0
refactor: turn을 불변 객체로 변경
drunkenhw Mar 26, 2023
013480c
refactor: 상수로 변경
drunkenhw Mar 26, 2023
2bbaff8
style: 주석 제거
drunkenhw Mar 26, 2023
5124787
refactor: 컨트롤러 로직 변경
drunkenhw Mar 26, 2023
5218b49
refactor: ChessRequest를 dto로 이동
drunkenhw Mar 26, 2023
7ef3d2c
refactor: position을 변환하는 로직 이동
drunkenhw Mar 26, 2023
b425ecb
refactor: move history 도메인으로 이동
drunkenhw Mar 26, 2023
b114eab
feat: Db connection generator 추가
drunkenhw Mar 26, 2023
f3a696a
feat: 게임이 종료되면 삭제하지 않고 다른 게임을 시작하도록 변경
drunkenhw Mar 27, 2023
7c8b09e
refactor: 컨트롤러 로직 변경
drunkenhw Mar 27, 2023
046b858
refactor: 매직넘버 상수화
drunkenhw Mar 27, 2023
3b0e6dc
refactor: 불필요한 클래스 제거
drunkenhw Mar 27, 2023
963c502
docs: 체크 추가
drunkenhw Mar 27, 2023
457e508
docs: table 쿼리 추가
drunkenhw Mar 27, 2023
04e913c
feat: move history에 시간 칼럼 추가
drunkenhw Mar 27, 2023
c259962
feat: exist로 조회하는 기능 추가
drunkenhw Mar 29, 2023
99d1719
style: 메소드 선언 순서 변경
drunkenhw Mar 29, 2023
a2dcf8e
refactor: 사용되지 않는 메소드 제거
drunkenhw Mar 29, 2023
9f369f8
refactor: Command 변환 로직 위치 이동
drunkenhw Mar 30, 2023
e0a1009
refactor: Move 객체 생성
drunkenhw Mar 30, 2023
2c96765
fix: black 턴에서 clear할 때 black 턴으로 고정되는 에러 수정
drunkenhw Mar 30, 2023
7249d52
feat: jdbc 템플릿 적용
drunkenhw Mar 30, 2023
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
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,71 @@
## 우아한테크코스 코드리뷰

- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)

```mermaid
graph TD
ChessController --> OutputView
ChessController --> InputView
ChessController --> ChessGame

ChessGame --> Board
ChessGame --> Color

Board --> Piece
Board --> Position

Position --> File
Position --> Rank

Piece --> Rook
Piece --> King
Piece --> Queen
Piece --> Bishop
Piece --> Knight
Piece --> Pawn
```

## 기능 구현 목록

### 도메인

- [x] 체스 게임을 할 수 있다.
- [x] 체스판의 가로는 왼쪽부터 a ~ h이고, 세로는 아래부터 위로 1 ~ 8의 위치값을 가진다.
- [x] 체스를 시작할 때 말의 위치는 고정된 위치 값을 가진다.
- [x] R: a, h
- [x] N: b, g
- [x] B: c, f
- [x] Q: d
- [x] K: e
- [x] 체스 말이 없는 위치는 .으로 표현한다.
- [x] 체스 말이 있다.
- [x] 체스 말은 검은색은 대문자, 흰색은 소문자로 구분한다.
- [x] 체스 말을 이동할 수 있다.
- [x] 이동 경로에 상대방 말이 있다면 그 말을 잡고 해당 위치로 이동한다.
- [x] 폰
- [x] 움직이지 않았을 때 앞으로 1칸 혹은 2칸 움직일 수 있다.
- [x] 이미 움직였으면 앞으로 1칸 혹은 대각선에 상대 말이 있다면 그 방향으로 움직일 수 있다.
- [x] 킹
- [x] 상, 하, 좌, 우, 대각선으로 1칸 이동할 수 있다.
- [x] 퀸
- [x] 상, 하, 좌, 우, 대각선으로 원하는 만큼 이동할 수 있다.
- [x] 룩
- [x] 상, 하, 좌, 우로 원하는 만큼 이동할 수 있다.
- [x] 비숍
- [x] 대각선으로 원하는 만큼 이동할 수 있다.
- [x] 킹이 잡히면 게임을 끝낸다.
- [x] 체스 게임의 점수를 구한다
- [x] pawn은 기본은 1점 같은 세로줄에 있으면 0.5점으로 계산한다.
- [x] queen은 9점, rook은 5점, bishop은 3점, knight는 2.5점이다.

### 입력

- [x] 체스 게임을 시작/종료할 수 있다.
- [x] 게임 시작은 start로 입력받는다.
- [x] 게임 종료는 end로 입력받는다.

### 출력

- [x] 체스 말의 현재 위치를 출력할 수 있다.
- [x] `status`를 입력하면 점수를 출력한다.

4 changes: 1 addition & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ repositories {
}

dependencies {
implementation 'com.sparkjava:spark-core:2.9.3'
implementation 'com.sparkjava:spark-template-handlebars:2.7.1'
implementation 'ch.qos.logback:logback-classic:1.2.10'
testImplementation 'org.assertj:assertj-core:3.22.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'
runtimeOnly 'mysql:mysql-connector-java:8.0.28'
}

java {
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/chess/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package chess;

import chess.controller.ChessController;
import chess.dao.ChessGameJdbcDao;
import chess.dao.JdbcTemplate;
import chess.domain.game.ChessGame;
import chess.service.ChessGameService;
import chess.view.InputView;
import chess.view.OutputView;

public class Application {

public static void main(String[] args) {
ChessController chessController = new ChessController(new OutputView(), new InputView(),
new ChessGameService(new ChessGame(), new ChessGameJdbcDao(new JdbcTemplate())));
chessController.run();
}
}
22 changes: 0 additions & 22 deletions src/main/java/chess/WebApplication.java

This file was deleted.

103 changes: 103 additions & 0 deletions src/main/java/chess/controller/ChessController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package chess.controller;

import static chess.controller.GameCommand.CLEAR;
import static chess.controller.GameCommand.END;
import static chess.controller.GameCommand.MOVE;
import static chess.controller.GameCommand.START;
import static chess.controller.GameCommand.STATUS;

import chess.dto.ChessRequest;
import chess.service.ChessGameService;
import chess.service.Move;
import chess.view.InputView;
import chess.view.OutputView;
import java.util.EnumMap;
import java.util.Map;

public class ChessController {

private final OutputView outputView;
private final InputView inputView;
private final ChessGameService chessGameService;
private final Map<GameCommand, GameAction> commandMapper = new EnumMap<>(GameCommand.class);

public ChessController(OutputView outputView, InputView inputView, ChessGameService chessGameService) {
this.outputView = outputView;
this.inputView = inputView;
this.chessGameService = chessGameService;
init();
}

private void init() {
commandMapper.put(START, this::start);
commandMapper.put(MOVE, this::move);
commandMapper.put(STATUS, this::status);
commandMapper.put(CLEAR, this::clear);
commandMapper.put(END, (chessGame, commands) -> END);
}

public void run() {
outputView.printStart();
GameCommand gameCommand = GameCommand.EMPTY;
while (gameCommand != END) {
gameCommand = play();
}
}

private GameCommand play() {
try {
ChessRequest chessRequest = inputView.readGameCommand();
GameCommand gameCommand = chessRequest.getCommand();
GameAction gameAction = commandMapper.get(gameCommand);
return gameAction.execute(chessGameService, chessRequest);
} catch (IllegalArgumentException e) {
outputView.printError(e.getMessage());
return play();
}
}

private GameCommand start(ChessGameService chessGameService, ChessRequest chessRequest) {
chessGameService.start();
outputView.printBoard(chessGameService.loadBoard());
return GameCommand.START;
}

private GameCommand move(ChessGameService chessGameService, ChessRequest chessRequest) {
validateStart(chessGameService);
Move move = Move.of(chessRequest.getSource(), chessRequest.getTarget());
chessGameService.move(move);
outputView.printBoard(chessGameService.getGameResult());
return checkGameEnd(chessGameService, outputView);
}

private GameCommand checkGameEnd(ChessGameService chessGameService, OutputView outputView) {
if (chessGameService.isGameEnd()) {
outputView.printStatus(chessGameService.getGameResult());
outputView.printWinner(chessGameService.getGameResult());
outputView.printEnd();
return GameCommand.END;
}
return GameCommand.MOVE;
}

private GameCommand status(ChessGameService chessGameService, ChessRequest chessRequest) {
validateStart(chessGameService);
outputView.printStatus(chessGameService.getGameResult());
return GameCommand.STATUS;
}

private void validateStart(ChessGameService chessGameService) {
if (chessGameService.isNotStart()) {
throw new IllegalArgumentException("start를 해주세요");
}
}

private GameCommand clear(ChessGameService chessGameService, ChessRequest chessRequest) {
validateStart(chessGameService);
chessGameService.finishedGame();
outputView.printStatus(chessGameService.getGameResult());
outputView.printClear();
outputView.printStart();
return GameCommand.CLEAR;
}
}
10 changes: 10 additions & 0 deletions src/main/java/chess/controller/GameAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package chess.controller;

import chess.dto.ChessRequest;
import chess.service.ChessGameService;

@FunctionalInterface
public interface GameAction {
GameCommand execute(ChessGameService chessGameService, ChessRequest chessRequest);

}
21 changes: 21 additions & 0 deletions src/main/java/chess/controller/GameCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package chess.controller;

public enum GameCommand {
START(0),
MOVE(2),
END(0),
STATUS(0),
EMPTY(0),
CLEAR(0),
;

private final int parameterLength;

GameCommand(int parameterLength) {
this.parameterLength = parameterLength;
}

public int getParameterLength() {
return parameterLength;
}
}
19 changes: 19 additions & 0 deletions src/main/java/chess/dao/ChessGameDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package chess.dao;

import chess.service.Move;
import java.util.List;

public interface ChessGameDao {

void saveMove(Move move, int gameId);

List<Move> findByGameId(int gameId);

int findGameIdByNotFinished();

void saveGame();

void finishedGame();

boolean isExistNotFinishedGame();
}
78 changes: 78 additions & 0 deletions src/main/java/chess/dao/ChessGameJdbcDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package chess.dao;

import chess.domain.position.Position;
import chess.service.Move;
import java.util.ArrayList;
import java.util.List;

public class ChessGameJdbcDao implements ChessGameDao {

private static final int NOT_EXIST_GAME = -1;
private final JdbcTemplate jdbcTemplate;

public ChessGameJdbcDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

@Override
public void saveMove(Move move, int gameId) {
var query = "INSERT INTO move_history(source, target, move_time, game_id) VALUES(?, ?, ?, ?)";
jdbcTemplate.executeUpdate(query, parsePosition(move.getSource()), parsePosition(move.getTarget()),
move.getMoveTime(), gameId);
}

private String parsePosition(Position position) {
return position.getFileCoordinate().name().toLowerCase() + position.getRankCoordinate().getRowNumber();
}

@Override
public List<Move> findByGameId(int gameId) {
var query = "SELECT * FROM move_history WHERE game_id = (?)";
return jdbcTemplate.executeQuery(query, resultSet -> {
List<Move> moveHistories = new ArrayList<>();
while (resultSet.next()) {
moveHistories.add(Move.of(
resultSet.getString("source"),
resultSet.getString("target"),
resultSet.getTimestamp("move_time").toLocalDateTime()
));
}
return moveHistories;
}, gameId);
}

@Override
public int findGameIdByNotFinished() {
var query = "SELECT id FROM game WHERE finished = false";
return jdbcTemplate.executeQuery(query, resultSet -> {
if (resultSet.next()) {
return resultSet.getInt("id");
}
return NOT_EXIST_GAME;
});
}

@Override
public void saveGame() {
var query = "INSERT INTO game(finished) VALUES (false)";
jdbcTemplate.executeUpdate(query);
}

@Override
public void finishedGame() {
var query = "UPDATE game SET finished = true WHERE finished = false";
jdbcTemplate.executeUpdate(query);
}

@Override
public boolean isExistNotFinishedGame() {
var query = "SELECT EXISTS(SELECT * FROM game WHERE finished = false)";
return jdbcTemplate.executeQuery(query, resultSet -> {
if (resultSet.next()) {
return resultSet.getBoolean(1);
}
return false;
});
}
}

26 changes: 26 additions & 0 deletions src/main/java/chess/dao/ConnectionGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package chess.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionGenerator {

private static final String SERVER = "localhost:13306";
private static final String DATABASE = "chess";
private static final String OPTION = "?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true";
private static final String USERNAME = "root";
private static final String PASSWORD = "root";
private static final String URL = "jdbc:mysql://" + SERVER + "/" + DATABASE + OPTION;

private ConnectionGenerator() {
}

public static Connection getConnection() {
try {
return DriverManager.getConnection(URL, USERNAME, PASSWORD);
} catch (SQLException e) {
throw new IllegalArgumentException();
}
}
}
Loading