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 제출 합니다. #25

Open
wants to merge 18 commits into
base: kelly6bf
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions .gitmessage.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@


Co-authored-by: Hyunguk Ryu <[email protected]>

Comment on lines +1 to +4
Copy link
Member

Choose a reason for hiding this comment

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

진짜 사소한 의견이긴 한데 공백도 컨벤션이니 위의 두줄은 없어도 되지 않을까..?

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
63 changes: 63 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
## 프로그램 흐름

- 사용자로부터 게임 시작 여부를 입력받는다.
- `start`를 입력받으면 게임을 생성하고 초기 체스판의 상태를 출력한다.
- `end`를 입력받으면 프로그램을 종료한다.
- 기물 이동 여부를 입력받는다.
- `move b2 b4`와 같이 입력받으면 `b2`에 존재하는 기물을 `b4`로 이동시킨다.
- `end`를 입력받으면 프로그램을 종료한다.

## 도메인별 기능

### Piece

- [x] 출발지 & 목적지 정보를 토대로 기물이 이동할 수 있는지 검사한다.
- [x] 입력받은 색상을 토대로 자신의 적 여부를 반환한다.

#### Rook

- 상/하/좌/우 거리 제한 없이 이동 가능
- 비었거나 적 기물이 존재하는 칸으로만 이동 가능
- 이동 경로에 기물이 존재하면 이동 불가

#### Bishop

- 대각선 방향 거리 제한 없이 이동 가능
- 비었거나 적 기물이 존재하는 칸으로만 이동 가능
- 이동 경로에 기물이 존재하면 이동 불가

#### Queen

- 상/하/좌/우, 대각선 방향 거리 제한 없이 이동 가능
- 비었거나 적 기물이 존재하는 칸으로만 이동 가능
- 이동 경로에 기물이 존재하면 이동 불가

#### King

- 상/하/좌/우, 대각선 방향 중 한 칸만 이동 가능
- 비었거나 적 기물이 존재하는 칸으로만 이동 가능

#### Knight

- 상/하/좌/우 중 한 칸 이동 후 전진 방향의 대각석으로 한 칸 이동 가능
- 비었거나 적 기물이 존재하는 칸으로만 이동 가능
- 이동 경로에 기물이 존재해도 이동 가능

#### Pawn

- 특정 조건에 따라 이동 선택
- 기본적으로 한 칸 전진 가능
- 출발지가 초기 위치일 경우 두 칸 전진 가능
- 전진 경로 혹은 목적지에 기물이 존재하면 이동 불가
- 전진 방향 대각선 한 칸에 적 기물이 존재시 이동 가능

## Board

- [x] 특정 색상의 기물 이동
- `출발지` == `도착지`인 경우 예외 처리
- 출발지에 기물이 존재하지 않으면 예외 처리
- 상대 색상의 기물을 이동시키려 할 시 예외 처리

## ChessGame

- [x] `흰색` 플레이어와 `검은색` 플레이어의 게임 순서 관리 및 `Board`에 기물 이동 요청 전달
30 changes: 30 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import controller.GameController;
import dao.ProductionTurnColorDao;
import dao.ProductionPieceDao;
import database.JdbcConnectionPool;
import domain.board.Board;
import domain.board.BoardInitializer;
import domain.board.Position;
import domain.game.ChessGame;
import domain.piece.Piece;
import view.InputView;
import view.OutputView;

import java.util.Map;

public class Application {
public static void main(String[] args) {
JdbcConnectionPool connectionPool = JdbcConnectionPool.getInstance();
Map<Position, Piece> initialPiecePositions = BoardInitializer.initBoard();
ProductionPieceDao productionPieceDao = new ProductionPieceDao(connectionPool);
Board board = new Board(productionPieceDao, initialPiecePositions);

ProductionTurnColorDao productionPieceColorDao = new ProductionTurnColorDao(connectionPool);
ChessGame chessGame = new ChessGame(productionPieceColorDao, board);

GameController gameController = new GameController(new InputView(), new OutputView(), chessGame);
gameController.run();

connectionPool.close();
}
}
107 changes: 107 additions & 0 deletions src/main/java/controller/GameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package controller;

import domain.board.Position;
import domain.game.ChessGame;
import domain.game.GameCommand;
import domain.game.GameCommandType;
import domain.game.GameScore;
import domain.game.GameStatus;
import domain.piece.PieceColor;
import dto.BoardDto;
import view.InputView;
import view.OutputView;

public class GameController {
private final InputView inputView;
private final OutputView outputView;
private final ChessGame chessGame;

public GameController(final InputView inputView, final OutputView outputView, final ChessGame chessGame) {
this.inputView = inputView;
this.outputView = outputView;
this.chessGame = chessGame;
}

public void run() {
initGame();

if (chessGame.isRunning()) {
start();
}
}

private void initGame() {
try {
GameCommand gameCommand = inputCommand();
gameCommand.execute(this);
} catch (Exception e) {
outputView.printErrorMessage(e.getMessage());
initGame();
}
}

private GameCommand inputCommand() {
String[] inputValues = inputView.inputCommand().split(" ");
return GameCommandType.of(inputValues);
}

public void buildGame() {
if (chessGame.existPrevGame()) {
outputView.printInputRoadGameMessage();
GameCommand gameCommand = inputCommand();
gameCommand.execute(this);
return;
}
createChessGame();
start();
}

public void createChessGame() {
chessGame.createChessGame();
}

public void roadPrevGame() {
chessGame.roadPrevGame();
}

public void start() {
chessGame.gameStart();
outputView.printWelcomeMessage();
while (chessGame.isRunning()) {
BoardDto boardDto = BoardDto.from(chessGame.piecePositions());
PieceColor currentPlayTeamColor = chessGame.currentPlayTeamColor();
outputView.printTurnStatus(boardDto, currentPlayTeamColor);
playTurn();
}
}

private void playTurn() {
try {
GameCommand gameCommand = inputCommand();
gameCommand.execute(this);
} catch (Exception e) {
outputView.printErrorMessage(e.getMessage());
playTurn();
}
}

public void movePiece(final Position source, final Position destination) {
chessGame.movePiece(source, destination);
}
Comment on lines +88 to +90
Copy link
Member

Choose a reason for hiding this comment

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

이건 비즈니스 로직이라고 봐도 될 것 같은데
요 로직이 왜 컨트롤러에 있을까용? 사용처가 안보입니다 😄


public GameStatus gameStatus() {
return chessGame.gameStatus();
}

public void end() {
chessGame.gameEnd();
}

public void printGameStatus() {
GameScore gameScore = chessGame.getGameResult();
outputView.printGameResult(
gameScore.whiteTeamScore(),
gameScore.blackTeamScore(),
gameScore.gameResult());
}
}
21 changes: 21 additions & 0 deletions src/main/java/dao/PieceDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package dao;

import domain.board.File;
import domain.board.Rank;

import java.util.List;

public interface PieceDao {

List<PieceEntity> findAll();

boolean existPiecePositions();

void save(PieceEntity piece);

void update(File sourceFile, Rank sourceRank, File destinationFile, Rank destinationRank);

void delete(File file, Rank rank);

void deleteAll();
}
9 changes: 9 additions & 0 deletions src/main/java/dao/PieceEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package dao;

import domain.board.File;
import domain.board.Rank;
import domain.piece.PieceColor;
import domain.piece.PieceType;

public record PieceEntity(PieceType pieceType, PieceColor pieceColor, File file, Rank rank) {
}
146 changes: 146 additions & 0 deletions src/main/java/dao/ProductionPieceDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package dao;

import database.JdbcConnectionPool;
import domain.board.File;
import domain.board.Rank;
import domain.piece.PieceColor;
import domain.piece.PieceType;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class ProductionPieceDao implements PieceDao {
private static final int PIECE_COUNT_INDEX = 1;
private static final int FILE_INDEX = 1;
private static final int RANK_INDEX = 2;
private static final int PIECE_TYPE_INDEX = 3;
private static final int PIECE_COLOR_INDEX = 4;
private static final int DESTINATION_FILE_INDEX = 3;
private static final int DESTINATION_RANK_INDEX = 4;

private final JdbcConnectionPool connectionPool;

public ProductionPieceDao(final JdbcConnectionPool connectionPool) {
this.connectionPool = connectionPool;
}

@Override
public List<PieceEntity> findAll() {
final String query = "SELECT * FROM piece";
Copy link
Member

Choose a reason for hiding this comment

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

final 이 붙은건 네오의 코드 복붙해오는 과정에서 사용했다고 생각하는데 final을 붙일거면 전부다 필사적으로 붙여야 한다고 생각!

Connection connection = connectionPool.getConnection();

try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
ResultSet resultSet = preparedStatement.executeQuery();
return createPieceEntities(resultSet);
} catch (SQLException e) {
throw new RuntimeException(e);
Copy link
Member

Choose a reason for hiding this comment

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

SQLException을 잡고 RuntimeExcpetion으로 넘기기 보다 IllegalStatementException을 던지는건 어때?

} finally {
connectionPool.releaseConnection(connection);
}
}

private List<PieceEntity> createPieceEntities(final ResultSet resultSet) throws SQLException {
List<PieceEntity> pieceEntities = new ArrayList<>();
while (resultSet.next()) {
File file = File.of(resultSet.getString("file"));
Rank rank = Rank.of(resultSet.getString("rank"));
PieceType type = PieceType.of(resultSet.getString("type"));
PieceColor color = PieceColor.of(resultSet.getString("color"));

pieceEntities.add(new PieceEntity(type, color, file, rank));
}

return pieceEntities;
}

@Override
public boolean existPiecePositions() {
final String query = "SELECT COUNT(*) FROM piece";
Connection connection = connectionPool.getConnection();

try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
int pieceCount = resultSet.getInt(PIECE_COUNT_INDEX);
return pieceCount > 0;
}

return false;
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
connectionPool.releaseConnection(connection);
}
}

@Override
public void save(final PieceEntity piece) {
final String query = "INSERT INTO piece (file, `rank`, type, color) VALUES (?, ?, ?, ?)";
Connection connection = connectionPool.getConnection();

try (final PreparedStatement preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) {
preparedStatement.setString(FILE_INDEX, piece.file().name());
preparedStatement.setInt(RANK_INDEX, piece.rank().value());
preparedStatement.setString(PIECE_TYPE_INDEX, piece.pieceType().name());
preparedStatement.setString(PIECE_COLOR_INDEX, piece.pieceColor().name());
preparedStatement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
connectionPool.releaseConnection(connection);
}
}

@Override
public void update(final File sourceFile, final Rank sourceRank, final File destinationFile, final Rank destinationRank) {
final String query = "UPDATE piece SET file=?, `rank`=? WHERE file=? AND `rank`=?";
Connection connection = connectionPool.getConnection();

try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
preparedStatement.setString(FILE_INDEX, destinationFile.name());
preparedStatement.setInt(RANK_INDEX, destinationRank.value());
preparedStatement.setString(DESTINATION_FILE_INDEX, sourceFile.name());
preparedStatement.setInt(DESTINATION_RANK_INDEX, sourceRank.value());
preparedStatement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
connectionPool.releaseConnection(connection);
}
}

@Override
public void delete(final File file, final Rank rank) {
final String query = "DELETE FROM piece WHERE file=? AND `rank`=?";
Connection connection = connectionPool.getConnection();

try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
preparedStatement.setString(FILE_INDEX, file.name());
preparedStatement.setInt(RANK_INDEX, rank.value());
preparedStatement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
connectionPool.releaseConnection(connection);
}
}

@Override
public void deleteAll() {
final String query = "DELETE FROM piece";
Connection connection = connectionPool.getConnection();

try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
preparedStatement.execute();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
connectionPool.releaseConnection(connection);
}
}
}
Loading