Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ea2fcab
docs: 2.1 κΈ°λŠ₯μš”κ΅¬μ‚¬ν•­ μ—…λ°μ΄νŠΈ
MODUGGAGI Mar 31, 2026
b8963ee
feat: ꢁ/사 κΈ°λ¬Ό μ•„κ΅° ꢁ성 μ™ΈλΆ€ 이동 λΆˆκ°€ κΈ°λŠ₯ κ΅¬ν˜„
MODUGGAGI Mar 31, 2026
77c16a1
feat: ꢁ성 λ‚΄ λŒ€κ°μ„  1μΉΈ 이동 κΈ°λŠ₯ κ΅¬ν˜„
MODUGGAGI Mar 31, 2026
9b56d34
feat: ꢁ성 λ‚΄ μ—°κ²°λ˜μ§€ μ•Šμ€ λŒ€κ°μ„  이동 λΆˆκ°€ κΈ°λŠ₯ μΆ”κ°€
MODUGGAGI Mar 31, 2026
e8c3326
docs: κ΅¬ν˜„ν•œ κΈ°λŠ₯ μ™„λ£Œ 처리
MODUGGAGI Mar 31, 2026
1845fcd
feat: μ‘Έ/λ³‘μ˜ ꢁ성 λ‚΄ λŒ€κ°μ„  이동 κ΅¬ν˜„
MODUGGAGI Mar 31, 2026
f6bb47f
refactor: ν…ŒμŠ€νŠΈ μˆ˜μ •
MODUGGAGI Mar 31, 2026
83ed257
docs: κ΅¬ν˜„ν•œ κΈ°λŠ₯ μ™„λ£Œ 처리
MODUGGAGI Mar 31, 2026
92b1b9d
feat: μ°¨/포 ꢁ성 λ‚΄ λŒ€κ°μ„  이동 κ΅¬ν˜„
MODUGGAGI Mar 31, 2026
ad5c85e
refactor: λ©”μ„œλ“œ νŒŒλΌλ―Έν„° μˆ˜μ •
MODUGGAGI Mar 31, 2026
a2ec5e6
test: μ°¨/포의 μ‹€μ œ ν–‰λ§ˆλ²•μœΌλ‘œ ꢁ성 λ‚΄λΆ€ λŒ€κ°μ„  이동 ν…ŒμŠ€νŠΈ μΆ”κ°€
MODUGGAGI Mar 31, 2026
471f823
docs: κ΅¬ν˜„ν•œ κΈ°λŠ₯ μ™„λ£Œ 처리
MODUGGAGI Mar 31, 2026
15e452a
refactor: λ©”μ„œλ“œ 이름 μˆ˜μ •
MODUGGAGI Apr 1, 2026
9d9d5cd
feat: ꢁ 작힐 μ‹œ κ²Œμž„ μ’…λ£Œ κΈ°λŠ₯ κ΅¬ν˜„
MODUGGAGI Apr 1, 2026
78c3991
refactor: λ©”μ„œλ“œ μˆœμ„œ λ³€κ²½
MODUGGAGI Apr 1, 2026
ceaf082
feat: 점수 계산 κΈ°λŠ₯ μΆ”κ°€
MODUGGAGI Apr 1, 2026
dc95de0
refactor: νŒŒλΌλ―Έν„°λ₯Ό 객체에 μž¬ν• λ‹Ή
MODUGGAGI Apr 1, 2026
2d3237d
refactor: μƒμˆ˜ private 처리
MODUGGAGI Apr 1, 2026
4927a19
refactor: μ˜μ‘΄μ„± μ£Όμž… μ „μš© 클래슀 제거
MODUGGAGI Apr 1, 2026
c9c7579
refactor: dto 제거
MODUGGAGI Apr 1, 2026
d2c74e9
refactor: switch문 제거
MODUGGAGI Apr 1, 2026
9ff0ad0
refactor: Camp의 초기 상차림 μ±…μž„ 뢄리
MODUGGAGI Apr 1, 2026
7302c92
refactor: Parser의 νŒ¨ν‚€μ§€ 이동
MODUGGAGI Apr 1, 2026
87225ef
refactor: intν˜• double둜 λ³€κ²½
MODUGGAGI Apr 1, 2026
aadbbd1
refactor: λ§€μ§λ„˜λ²„ 제거
MODUGGAGI Apr 1, 2026
ad1a8c1
refactor: ꢁ성 μ±…μž„ 뢄리
MODUGGAGI Apr 2, 2026
77fa7be
feat: DB μ—°κ²° 및 초기 μ„€μ • μΆ”κ°€
MODUGGAGI Apr 3, 2026
ef41c47
feat: κ²Œμž„ μƒνƒœ μ €μž₯ κΈ°λŠ₯ μΆ”κ°€
MODUGGAGI Apr 3, 2026
027c848
refactor: Position νŒ¨ν‚€μ§€ 이동
MODUGGAGI Apr 3, 2026
6b91d0f
refactor: νŠΈλžœμž­μ…˜ 관리 클래슀 λ„μž…
MODUGGAGI Apr 3, 2026
13fd788
refactor: λ©”μ„œλ“œ 이름 λ³€κ²½
MODUGGAGI Apr 3, 2026
f26981f
refactor: μ „λž΅ 이름 λ³€κ²½
MODUGGAGI Apr 3, 2026
af0d541
feat: κ²Œμž„λ°© 생성 κΈ°λŠ₯ κ΅¬ν˜„
MODUGGAGI Apr 3, 2026
2e6c338
refactor: 클래슀 이름 μˆ˜μ •
MODUGGAGI Apr 3, 2026
8f87e7c
refactor: μ™„λ£Œν•œ TODO 제거
MODUGGAGI Apr 3, 2026
4c08195
docs: κ΅¬ν˜„ν•œ κΈ°λŠ₯ μ™„λ£Œ 처리
MODUGGAGI Apr 3, 2026
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 .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
HELP.md
.gradle
.gradle-local/
build/
data/
*.mv.db
*.trace.db
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**
!**/src/test/**
Expand Down
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,27 +99,53 @@
- [x] κΈ°λ¬Ό κ·œμΉ™ 및 μ œμ•½μ— 맞게 μ΄λ™ν•œλ‹€.
- [차(車)]
- [x] μƒν•˜μ’Œμš°λ‘œ μž₯애물을 λ§Œλ‚  λ•ŒκΉŒμ§€ μΉΈ 수 μ œν•œ 없이 μ΄λ™ν•œλ‹€.
- [x] ꢁ성 λ‚΄λΆ€(μ§„μ˜ 상관 X)μ—μ„œ κΆμ„±μ˜ λŒ€κ°μ„ μ„ μ΄μš©ν•΄ 이동할 수 μžˆλ‹€.
- [x] μ΄λ™ν•˜λ €λŠ” 경둜 쀑간에 λ‹€λ₯Έ 기물이 **있**λ‹€λ©΄ `IllegalArgumentException`을 λ°œμƒμ‹œν‚¨λ‹€.

- [포(εŒ…)]
- [x] μƒν•˜μ’Œμš°λ‘œ μ΄λ™ν•˜λ˜, λ°˜λ“œμ‹œ 쀑간에 λ‹€λ₯Έ 기물을 ν•˜λ‚˜ λ›°μ–΄λ„˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.
- [x] ꢁ성 λ‚΄λΆ€(μ§„μ˜ 상관 X)μ—μ„œ κΆμ„±μ˜ λŒ€κ°μ„ μ„ μ΄μš©ν•΄ 이동할 수 μžˆλ‹€.
- μ΄λ•Œμ—λ„ 경둜 상에 λ°˜λ“œμ‹œ 기물이 μžˆμ–΄μ•Ό ν•œλ‹€.
- [x] μ΄λ™ν•˜λ €λŠ” 경둜 쀑간에 λ‹€λ₯Έ 기물이 **μ—†**λ‹€λ©΄ `IllegalArgumentException`을 λ°œμƒμ‹œν‚¨λ‹€.
- [x] λ„˜μœΌλ €λŠ” 기물이 포(εŒ…) 일 경우 `IllegalArgumentException`을 λ°œμƒμ‹œν‚¨λ‹€.
- [x] λͺ©μ μ§€ μ’Œν‘œμ˜ 기물이 포(εŒ…)일 경우 `IllegalArgumentException`을 λ°œμƒμ‹œν‚¨λ‹€.

- [마(馬)]
- [x] 직선 1μΉΈ + λŒ€κ°μ„  1μΉΈ(ζ—₯) μ΄λ™ν•œλ‹€.
- ꢁ성 λ‚΄λΆ€ λŒ€κ°μ„  이용 λΆˆκ°€
- [x] λͺ©μ μ§€ μ’Œν‘œ(직선 1μΉΈ λ˜λŠ” λŒ€κ°μ„  1μΉΈ)에 λ‹€λ₯Έ 기물이 있으면 `IllegalArgumentException`을 λ°œμƒμ‹œν‚¨λ‹€.

- [상(豑)]
- [x] 직선 1μΉΈ + λŒ€κ°μ„  2μΉΈ(用) μ΄λ™ν•œλ‹€.
- ꢁ성 λ‚΄λΆ€ λŒ€κ°μ„  이용 λΆˆκ°€
- [x] λͺ©μ μ§€ μ’Œν‘œ(직선 1μΉΈ λ˜λŠ” λŒ€κ°μ„  1μΉΈ)에 λ‹€λ₯Έ 기물이 있으면 `IllegalArgumentException`을 λ°œμƒμ‹œν‚¨λ‹€.

- [ꢁ(ζ₯š/ζΌ’), 사(士)]
- [ꢁ(ζ₯š/ζΌ’)]
- [x] μƒν•˜μ’Œμš° 직선 1μΉΈ μ΄λ™ν•œλ‹€.
- [x] μ•„κ΅° ꢁ성 λ‚΄λΆ€μ—μ„œλ§Œ λŒ€κ°μ„ μœΌλ‘œ 1μΉΈ 이동할 수 μžˆλ‹€.
- [x] μƒλŒ€ κΈ°λ¬Όμ—κ²Œ 작힐 경우 κ²Œμž„μ΄ μ’…λ£Œλ˜λ©° μƒλŒ€λ°©μ΄ κ²Œμž„μ„ μŠΉλ¦¬ν•œλ‹€.

- [사(士)]
- [x] μƒν•˜μ’Œμš° 직선 1μΉΈ μ΄λ™ν•œλ‹€.
- [x] μ•„κ΅° ꢁ성 λ‚΄λΆ€μ—μ„œλ§Œ λŒ€κ°μ„ μœΌλ‘œ 1μΉΈ 이동할 수 μžˆλ‹€.

- [μ‘Έ/병(卒/ε…΅)]
- [x] μƒμ’Œμš° 직선 1μΉΈ μ΄λ™ν•œλ‹€. 후진은 λΆˆκ°€ 주의
- [x] μƒλŒ€ ꢁ성 λ‚΄λΆ€μ—μ„œλ§Œ λŒ€κ°μ„ μœΌλ‘œ 1μΉΈ 이동할 수 μžˆλ‹€.
- μ‘Έ/병은 κ·œμΉ™μ— μ˜ν•΄ μ• μ΄ˆμ— μ•„κ΅° κΆμ„±μœΌλ‘œ 이동쑰차 λΆˆκ°€λŠ₯ν•˜λ‹€.

### κΈ°λ¬Ό 별 점수

γ€Žμ΄ˆγ€ 총점 : 72.0점
γ€Žν•œγ€ 총점 : 73.5점 (ν›„μˆ˜μ΄κΈ° λ•Œλ¬Έμ— 점수 + 1.5)

- [차(車)] : 13점
- [포(εŒ…)] : 7점
- [마(馬)] : 5점
- [상(豑)] : 3점
- [사(士)] : 3점
- [μ‘Έ/병(卒/ε…΅)] : 2점
- [ꢁ(ζ₯š/ζΌ’)] : 점수 μ—†μŒ -> μ£½λŠ” μˆœκ°„ μƒλŒ€λ°© πŸ₯‡κ²Œμž„ 승리πŸ₯‡

---

Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ repositories {
}

dependencies {
runtimeOnly('com.h2database:h2:2.4.240')
testImplementation platform('org.junit:junit-bom:5.11.4')
testImplementation platform('org.assertj:assertj-bom:3.27.3')
testImplementation('org.junit.jupiter:junit-jupiter')
Expand Down
19 changes: 0 additions & 19 deletions src/main/java/janggi/AppConfig.java

This file was deleted.

110 changes: 110 additions & 0 deletions src/main/java/janggi/GameRunner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package janggi;

import janggi.domain.Game;
import janggi.domain.board.Board;
import janggi.domain.board.Position;
import janggi.domain.board.initializer.BoardInitializer;
import janggi.domain.board.initializer.ElephantSetUp;
import janggi.domain.board.initializer.StandardBoardInitializer;
import janggi.domain.piece.Camp;
import janggi.service.GameService;
import janggi.view.InputView;
import janggi.view.OutputView;
import janggi.view.dto.CampDto;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

public class GameRunner {
private static final String INVALID_GAME_ROOM = "[ERROR] μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” κ²Œμž„λ°© λ²ˆν˜Έμž…λ‹ˆλ‹€.";

private final InputView inputView;
private final OutputView outputView;
private final GameService gameService;

public GameRunner(InputView inputView, OutputView outputView, GameService gameService) {
this.inputView = inputView;
this.outputView = outputView;
this.gameService = gameService;
}

public void run() {
Game game = retryOnInvalidInput(this::loadOrCreateGame);
outputView.printBoard(game.boardSnapshot());
play(game);
}

private Game loadOrCreateGame() {
outputView.printExistGameRoom(gameService.findAllIds());
long gameId = inputView.readSelectedGameRoom();

if (gameId == 0L) {
return createNewGame();
}

return gameService.findById(gameId)
.orElseThrow(() -> new IllegalArgumentException(INVALID_GAME_ROOM));
}

private Game createNewGame() {
Board board = createBoard();
return gameService.create(board);
}

private Board createBoard() {
Map<Camp, ElephantSetUp> elephantSetUps = new HashMap<>();
readElephantSetUp(elephantSetUps, Camp.HAN);
readElephantSetUp(elephantSetUps, Camp.CHO);

BoardInitializer initializer = new StandardBoardInitializer(elephantSetUps);
return new Board(initializer);
}

private void readElephantSetUp(Map<Camp, ElephantSetUp> elephantSetUps, Camp camp) {
ElephantSetUp elephantSetUp = retryOnInvalidInput(
() -> inputView.readElephantSetting(CampDto.from(camp))
);
elephantSetUps.put(camp, elephantSetUp);
}

private void play(Game game) {
boolean continueGame = true;
while (continueGame) {
outputView.printScore(game.calculateScore());
continueGame = retryOnInvalidInput(() -> playTurn(game));
outputView.printBoard(game.boardSnapshot());
}
outputView.printWinner(game.currentTurn());
}

private boolean playTurn(Game game) {
Camp currentTurn = game.currentTurn();

Position source = retryOnInvalidInput(() -> readSource(game, currentTurn));
Position destination = retryOnInvalidInput(inputView::readDestination);

boolean gameEnded = game.play(source, destination);
if (gameEnded) {
gameService.deleteById(game.id());
return false;
}
gameService.save(game);
return true;
}

private Position readSource(Game game, Camp currentTurn) {
Position source = inputView.readSource(CampDto.from(currentTurn));
game.validateSourceForCurrentTurn(source);
return source;
}

private <T> T retryOnInvalidInput(Supplier<T> input) {
while (true) {
try {
return input.get();
} catch (IllegalArgumentException e) {
outputView.printError(e.getMessage());
}
}
}
}
21 changes: 19 additions & 2 deletions src/main/java/janggi/JanggiApplication.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
package janggi;

import janggi.db.DatabaseInitializer;
import janggi.db.H2ConnectionManager;
import janggi.db.TransactionManager;
import janggi.repository.GamePieceRepository;
import janggi.repository.GameStateRepository;
import janggi.service.GameService;
import janggi.view.InputView;
import janggi.view.OutputView;
import java.util.Scanner;

public class JanggiApplication {
public static void main(String[] args) {
AppConfig app = new AppConfig();
JanggiGame janggi = app.janggi();
H2ConnectionManager connectionManager = new H2ConnectionManager();
new DatabaseInitializer(connectionManager).initialize();
TransactionManager transactionManager = new TransactionManager(connectionManager);

GameRunner janggi = new GameRunner(
new InputView(new Scanner(System.in)),
new OutputView(),
new GameService(transactionManager, new GameStateRepository(), new GamePieceRepository())
);
janggi.run();
}
}
90 changes: 0 additions & 90 deletions src/main/java/janggi/JanggiGame.java

This file was deleted.

8 changes: 8 additions & 0 deletions src/main/java/janggi/db/ConnectionManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package janggi.db;

import java.sql.Connection;

public interface ConnectionManager {

Connection createConnection();
}
49 changes: 49 additions & 0 deletions src/main/java/janggi/db/DatabaseInitializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package janggi.db;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

public final class DatabaseInitializer {

private static final String SCHEMA_PATH = "/schema.sql";
private static final String SCHEMA_LOAD_FAILED = "[ERROR] DB μŠ€ν‚€λ§ˆλ₯Ό 읽을 수 μ—†μŠ΅λ‹ˆλ‹€.";
private static final String SCHEMA_INIT_FAILED = "[ERROR] DB μŠ€ν‚€λ§ˆλ₯Ό μ΄ˆκΈ°ν™”ν•  수 μ—†μŠ΅λ‹ˆλ‹€.";

private final ConnectionManager connectionManager;

public DatabaseInitializer(ConnectionManager connectionManager) {
this.connectionManager = connectionManager;
}

public void initialize() {
try (Connection connection = connectionManager.createConnection()) {
String schema = loadSchema();
for (String statement : schema.split(";")) {
String sql = statement.trim();
if (sql.isEmpty()) {
continue;
}
try (Statement jdbcStatement = connection.createStatement()) {
jdbcStatement.execute(sql);
}
}
} catch (SQLException e) {
throw new IllegalStateException(SCHEMA_INIT_FAILED, e);
}
}

private String loadSchema() {
try (InputStream inputStream = DatabaseInitializer.class.getResourceAsStream(SCHEMA_PATH)) {
if (inputStream == null) {
throw new IllegalStateException(SCHEMA_LOAD_FAILED);
}
return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new IllegalStateException(SCHEMA_LOAD_FAILED, e);
}
}
}
Loading