Skip to content
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 <hw0603@naver.com>

Comment on lines +1 to +4
Copy link
Copy Markdown

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
Copy Markdown
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
Copy Markdown

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
Copy Markdown

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