From e8a296a92160817a38d7cc659b68af22d5194fc1 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 14:58:22 +0900 Subject: [PATCH 01/75] =?UTF-8?q?test:=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EB=B2=A0=EC=9D=B4=EC=8A=A4=20init=20=ED=98=B8=EC=B6=9C=20?= =?UTF-8?q?=EC=8B=9C=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/DatabaseInitializerTest.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/test/java/janggi/persistence/DatabaseInitializerTest.java diff --git a/src/test/java/janggi/persistence/DatabaseInitializerTest.java b/src/test/java/janggi/persistence/DatabaseInitializerTest.java new file mode 100644 index 0000000000..abf76a7cd2 --- /dev/null +++ b/src/test/java/janggi/persistence/DatabaseInitializerTest.java @@ -0,0 +1,36 @@ +package janggi.persistence; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.InputStream; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class DatabaseInitializerTest { + + @Test + @DisplayName("init 호출 시 테이블 생성") + void init() throws SQLException { + // given + JdbcConnectionManager connectionManager = new JdbcConnectionManager(); + DatabaseInitializer databaseInitializer = new DatabaseInitializer(connectionManager); + + // when + databaseInitializer.init(); + + //then + assertThat(existsTable(connectionManager, "GAMES")).isTrue(); + assertThat(existsTable(connectionManager, "GAME_PIECES")).isTrue(); + } + + private boolean existsTable(JdbcConnectionManager connectionManager, String tableName) throws SQLException { + try (Connection connection = connectionManager.getConnection()){ + ResultSet resultSet = connection.getMetaData() + .getTables(null, null, tableName, null); + return resultSet.next(); + } + } +} From 1ddd06f9f4f75152352267e1923c5468a805b250 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 14:58:50 +0900 Subject: [PATCH 02/75] =?UTF-8?q?feat:=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EB=B2=A0=EC=9D=B4=EC=8A=A4=20init=20=ED=98=B8=EC=B6=9C=20?= =?UTF-8?q?=EC=8B=9C=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sql 스키마 생성 - 데이터베이스 연동 - 의존성 추가 --- build.gradle | 1 + .../persistence/DatabaseInitializer.java | 45 +++++++++++++++++++ .../persistence/JdbcConnectionManager.java | 16 +++++++ src/main/resources/schema.sql | 16 +++++++ 4 files changed, 78 insertions(+) create mode 100644 src/main/java/janggi/persistence/DatabaseInitializer.java create mode 100644 src/main/java/janggi/persistence/JdbcConnectionManager.java create mode 100644 src/main/resources/schema.sql diff --git a/build.gradle b/build.gradle index ce846f70cc..9326c6225c 100644 --- a/build.gradle +++ b/build.gradle @@ -9,6 +9,7 @@ repositories { } dependencies { + implementation 'com.h2database:h2:2.3.232' testImplementation platform('org.junit:junit-bom:5.11.4') testImplementation platform('org.assertj:assertj-bom:3.27.3') testImplementation('org.junit.jupiter:junit-jupiter') diff --git a/src/main/java/janggi/persistence/DatabaseInitializer.java b/src/main/java/janggi/persistence/DatabaseInitializer.java new file mode 100644 index 0000000000..588ed2864d --- /dev/null +++ b/src/main/java/janggi/persistence/DatabaseInitializer.java @@ -0,0 +1,45 @@ +package janggi.persistence; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +public class DatabaseInitializer { + + private final JdbcConnectionManager connectionManager; + + public DatabaseInitializer(JdbcConnectionManager connectionManager) { + this.connectionManager = connectionManager; + } + + public void init() { + try ( + Connection connection = connectionManager.getConnection(); + Statement statement = connection.createStatement() + ) { + statement.execute(readSchema()); + } catch (SQLException | IOException exception) { + throw new RuntimeException(exception); + } + } + + private String readSchema() throws IOException { + BufferedReader reader = new BufferedReader( + new InputStreamReader(schemaStream(), StandardCharsets.UTF_8)); + return reader.lines() + .reduce("", (first, second) -> first + second + "\n"); + } + + private InputStream schemaStream() { + InputStream stream = getClass().getResourceAsStream("/schema.sql"); + if (stream == null) { + throw new IllegalArgumentException("schema.sql 파일을 찾을 수 없습니다."); + } + return stream; + } +} diff --git a/src/main/java/janggi/persistence/JdbcConnectionManager.java b/src/main/java/janggi/persistence/JdbcConnectionManager.java new file mode 100644 index 0000000000..e58434c795 --- /dev/null +++ b/src/main/java/janggi/persistence/JdbcConnectionManager.java @@ -0,0 +1,16 @@ +package janggi.persistence; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class JdbcConnectionManager { + + private static final String URL = "jdbc:h2:file:./storage/janggi;AUTO_SERVER=TRUE"; + private static final String USER = "sa"; + private static final String PASSWORD = ""; + + public Connection getConnection() throws SQLException { + return DriverManager.getConnection(URL, USER, PASSWORD); + } +} diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000000..2a1bd917f2 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,16 @@ +CREATE TABLE games ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + current_turn VARCHAR(10) NOT NULL, + finished BOOLEAN NOT NULL, + winner VARCHAR(10) +); + +CREATE TABLE game_pieces ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + game_id BIGINT NOT NULL, + team VARCHAR(10) NOT NULL, + piece_type VARCHAR(10) NOT NULL, + x_value INT NOT NULL, + y_value INT NOT NULL, + FOREIGN KEY (game_id) REFERENCES games(id) +); From 3c0d4937edc91f87214b3be5c63b504e28a33e19 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 15:25:28 +0900 Subject: [PATCH 03/75] =?UTF-8?q?refactor:=20=ED=85=8C=EC=9D=B4=EB=B8=94?= =?UTF-8?q?=EC=9D=B4=20=EC=9E=88=EC=9D=84=20=EC=8B=9C=20=EB=8B=A4=EC=8B=9C?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/schema.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 2a1bd917f2..9df06562d9 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -1,11 +1,11 @@ -CREATE TABLE games ( +CREATE TABLE IF NOT EXISTS games ( id BIGINT AUTO_INCREMENT PRIMARY KEY, current_turn VARCHAR(10) NOT NULL, finished BOOLEAN NOT NULL, winner VARCHAR(10) ); -CREATE TABLE game_pieces ( +CREATE TABLE IF NOT EXISTS game_pieces ( id BIGINT AUTO_INCREMENT PRIMARY KEY, game_id BIGINT NOT NULL, team VARCHAR(10) NOT NULL, From e6725b98e1ad87735a24b133a6318d011bd45743 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 15:30:19 +0900 Subject: [PATCH 04/75] =?UTF-8?q?refactor:=20url=EA=B3=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90,=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8?= =?UTF-8?q?=EB=A5=BC=20=EC=A3=BC=EC=9E=85=EB=B0=9B=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../janggi/persistence/JdbcConnectionManager.java | 12 +++++++++--- .../janggi/persistence/DatabaseInitializerTest.java | 7 +++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/janggi/persistence/JdbcConnectionManager.java b/src/main/java/janggi/persistence/JdbcConnectionManager.java index e58434c795..170ee6b00b 100644 --- a/src/main/java/janggi/persistence/JdbcConnectionManager.java +++ b/src/main/java/janggi/persistence/JdbcConnectionManager.java @@ -6,9 +6,15 @@ public class JdbcConnectionManager { - private static final String URL = "jdbc:h2:file:./storage/janggi;AUTO_SERVER=TRUE"; - private static final String USER = "sa"; - private static final String PASSWORD = ""; + private final String URL; + private final String USER; + private final String PASSWORD; + + public JdbcConnectionManager(String url, String user, String password) { + this.URL = url; + this.USER = user; + this.PASSWORD = password; + } public Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); diff --git a/src/test/java/janggi/persistence/DatabaseInitializerTest.java b/src/test/java/janggi/persistence/DatabaseInitializerTest.java index abf76a7cd2..bab0208903 100644 --- a/src/test/java/janggi/persistence/DatabaseInitializerTest.java +++ b/src/test/java/janggi/persistence/DatabaseInitializerTest.java @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; @@ -15,7 +14,11 @@ public class DatabaseInitializerTest { @DisplayName("init 호출 시 테이블 생성") void init() throws SQLException { // given - JdbcConnectionManager connectionManager = new JdbcConnectionManager(); + JdbcConnectionManager connectionManager = new JdbcConnectionManager( + "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", + "sa", + "" + ); DatabaseInitializer databaseInitializer = new DatabaseInitializer(connectionManager); // when From ff07bc8fa86bbcdf0b47bee48f6afe79565cb23a Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 15:37:14 +0900 Subject: [PATCH 05/75] =?UTF-8?q?docs:=20=EC=B6=94=EA=B0=80=EB=90=9C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f069a20711..3ffffbb2b0 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ ### 장기 게임 (JanggiGame) - [X] 장기 게임 흐름 관리 - [X] 게임 턴 관리 - - [X] 초기 장기판 셋업 (각 기물들을 정해진 초기 Point에 배치) ### ✉️ 도메인 (Domain) @@ -59,3 +58,56 @@ #### 에러 출력 - [ ] 예외 상황 발생 시 에러 문구 출력 (반드시 [ERROR]로 시작) + +## 추가 기능 목록 +### 궁성 규칙 +- [ ] 장과 사는 궁성 안에서만 이동할 수 있다. +- [ ] 장은 궁성 대각선 이동 규칙을 따른다. +- [ ] 사는 궁성 대각선 이동 규칙을 따른다. +- [ ] 차는 궁성 대각선 이동 규칙을 따른다. +- [ ] 포는 궁성 대각선 이동 규칙을 따른다. +- [ ] 졸은 궁성 안에서 대각선 이동 규칙을 따른다. + +### 게임 종료 +- [X] 상대 왕이 잡히면 게임을 종료한다. +- [X] 게임 종료 시 승자를 반환한다. + +### 점수 계산 +- [ ] 현재 남아 있는 기물의 점수를 계산한다. +- [ ] 팀별 점수를 계산할 수 있다. +- [ ] 게임 종료 여부와 관계없이 현재 점수를 조회할 수 있다. + +### 데이터베이스 초기화 +- [X] H2 데이터베이스에 연결한다. +- [X] 애플리케이션 시작 시 스키마를 초기화한다. +- [X] games 테이블을 생성한다. +- [X] game_pieces 테이블을 생성한다. + +### 게임 저장소 +- [ ] 게임 목록을 조회할 수 있다. +- [ ] 게임 id로 게임을 조회할 수 있다. +- [ ] 새 게임을 저장할 수 있다. +- [ ] 진행 중인 게임 상태를 수정할 수 있다. +- [ ] 게임의 현재 턴 정보를 저장한다. +- [ ] 게임의 종료 여부와 승자를 저장한다. +- [ ] 현재 살아있는 기물의 위치를 저장한다. + +### 게임 복구 +- [ ] 저장된 게임 목록을 출력한다. +- [ ] 사용자가 이어서 할 게임을 선택할 수 있다. +- [ ] 저장된 기물 위치로 장기판을 복구한다. +- [ ] 저장된 현재 턴으로 게임을 복구한다. +- [ ] 저장된 종료 상태로 게임을 복구한다. +- [ ] 저장된 게임이 없으면 새 게임을 시작한다. + +### 게임 진행 흐름 +- [ ] 새 게임을 생성할 수 있다. +- [ ] 수를 둘 때마다 게임 상태를 저장한다. +- [ ] 게임 종료 시 최종 상태를 저장한다. + +### 입/출력 +- [ ] 게임 시작 시 새 게임과 이어하기 메뉴를 출력한다. +- [ ] 저장된 게임 목록을 사용자에게 출력한다. +- [ ] 사용자가 선택한 게임을 불러온다. +- [ ] 턴이 넘어갈 때 마다 게임을 그만할지 물어본다. +- [ ] 현재 턴 정보를 출력한다. From 696902fd56c945b44a6513caa06f95907cffc9bf Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 15:52:48 +0900 Subject: [PATCH 06/75] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EC=8A=A4?= =?UTF-8?q?=EB=83=85=EC=83=B7=20=EB=B0=8F=20=EC=9A=94=EC=95=BD=20dto=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/janggi/application/dto/GameSnapshot.java | 14 ++++++++++++++ .../java/janggi/application/dto/GameSummary.java | 7 +++++++ 2 files changed, 21 insertions(+) create mode 100644 src/main/java/janggi/application/dto/GameSnapshot.java create mode 100644 src/main/java/janggi/application/dto/GameSummary.java diff --git a/src/main/java/janggi/application/dto/GameSnapshot.java b/src/main/java/janggi/application/dto/GameSnapshot.java new file mode 100644 index 0000000000..1b8e178125 --- /dev/null +++ b/src/main/java/janggi/application/dto/GameSnapshot.java @@ -0,0 +1,14 @@ +package janggi.application.dto; + +import janggi.domain.status.Team; +import janggi.dto.PositionInfo; +import java.util.List; + +public record GameSnapshot( + Long id, + Team currentTurn, + boolean finished, + Team winner, + List positions +) { +} diff --git a/src/main/java/janggi/application/dto/GameSummary.java b/src/main/java/janggi/application/dto/GameSummary.java new file mode 100644 index 0000000000..f5953a23bb --- /dev/null +++ b/src/main/java/janggi/application/dto/GameSummary.java @@ -0,0 +1,7 @@ +package janggi.application.dto; + +public record GameSummary( + Long id, + boolean finished +) { +} From a9b161e833731fc64529127fcccdcb24621b1701 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 15:53:01 +0900 Subject: [PATCH 07/75] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=EC=86=8C=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/janggi/application/GameRepository.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/janggi/application/GameRepository.java diff --git a/src/main/java/janggi/application/GameRepository.java b/src/main/java/janggi/application/GameRepository.java new file mode 100644 index 0000000000..ea9ef4f62b --- /dev/null +++ b/src/main/java/janggi/application/GameRepository.java @@ -0,0 +1,13 @@ +package janggi.application; + +import janggi.application.dto.GameSnapshot; +import janggi.application.dto.GameSummary; +import java.util.List; +import java.util.Optional; + +public interface GameRepository { + List findAll(); + Optional findById(Long gameId); + void save(GameSnapshot gameSnapshot); + void update(GameSnapshot gameSnapshot); +} From 4b5d194b6a89bfb8a5c4e4eaefa98d10f7c38bbb Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 16:24:13 +0900 Subject: [PATCH 08/75] =?UTF-8?q?test:=20=EA=B2=8C=EC=9E=84=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EB=B0=8F=20=EB=AA=A8=EB=93=A0=20=EA=B2=8C=EC=9E=84?= =?UTF-8?q?=EC=9D=84=20=EC=B0=BE=EC=95=84=EC=98=A4=EB=8A=94=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/JdbcGameRepositoryTest.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/test/java/janggi/persistence/JdbcGameRepositoryTest.java diff --git a/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java b/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java new file mode 100644 index 0000000000..37f47e5055 --- /dev/null +++ b/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java @@ -0,0 +1,88 @@ +package janggi.persistence; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.application.GameRepository; +import janggi.application.dto.GameSnapshot; +import janggi.application.dto.GameSummary; +import janggi.domain.status.Team; +import janggi.dto.PositionInfo; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class JdbcGameRepositoryTest { + + private GameRepository repository; + private JdbcConnectionManager connectionManager; + + @BeforeEach + void setUp() { + String databaseName = "test" + System.nanoTime(); + connectionManager = new JdbcConnectionManager( + "jdbc:h2:mem:" + databaseName + ";DB_CLOSE_DELAY=-1", + "sa", + "" + ); + DatabaseInitializer databaseInitializer = new DatabaseInitializer(connectionManager); + databaseInitializer.init(); + repository = new JdbcGameRepository(connectionManager); + } + + @Test + @DisplayName("게임 저장 기능") + void save() { + // given + GameSnapshot gameSnapshot = new GameSnapshot( + null, + Team.CHO, + false, + null, + List.of( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ) + ); + + // when + repository.save(gameSnapshot); + + // then + assertThat(repository.findAll()).hasSize(1); + } + + @Test + @DisplayName("모든 게임 목록 조회") + void findAll() { + GameSnapshot firstGame = new GameSnapshot( + null, + Team.CHO, + false, + null, + List.of( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ) + ); + + GameSnapshot secondGame = new GameSnapshot( + null, + Team.HAN, + true, + Team.HAN, + List.of( + PositionInfo.from(Team.CHO, "JANG", 4, 1) + ) + ); + + repository.save(firstGame); + repository.save(secondGame); + + List gameSummaries = repository.findAll(); + + assertThat(gameSummaries).hasSize(2); + assertThat(gameSummaries.get(0).finished()).isFalse(); + assertThat(gameSummaries.get(1).finished()).isTrue(); + } +} From 00b9850397a13975379f9e9844e9b3db9e366644 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 16:24:25 +0900 Subject: [PATCH 09/75] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EB=B0=8F=20=EB=AA=A8=EB=93=A0=20=EA=B2=8C=EC=9E=84?= =?UTF-8?q?=EC=9D=84=20=EC=B0=BE=EC=95=84=EC=98=A4=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/JdbcGameRepository.java | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/main/java/janggi/persistence/JdbcGameRepository.java diff --git a/src/main/java/janggi/persistence/JdbcGameRepository.java b/src/main/java/janggi/persistence/JdbcGameRepository.java new file mode 100644 index 0000000000..e5b40cdd1f --- /dev/null +++ b/src/main/java/janggi/persistence/JdbcGameRepository.java @@ -0,0 +1,134 @@ +package janggi.persistence; + +import janggi.application.GameRepository; +import janggi.application.dto.GameSnapshot; +import janggi.application.dto.GameSummary; +import janggi.domain.status.Team; +import janggi.dto.PositionInfo; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class JdbcGameRepository implements GameRepository { + + private JdbcConnectionManager connectionManager; + + public JdbcGameRepository(JdbcConnectionManager connectionManager) { + this.connectionManager = connectionManager; + } + + @Override + public List findAll() { + try ( + Connection connection = connectionManager.getConnection(); + PreparedStatement statement = connection.prepareStatement( + "SELECT id, current_turn, finished FROM games ORDER BY id" + ); + ResultSet resultSet = statement.executeQuery() + ) { + return toGameSummaries(resultSet); + } catch (SQLException exception) { + throw new RuntimeException(exception); + } + } + + @Override + public Optional findById(Long gameId) { + return Optional.empty(); + } + + @Override + public void save(GameSnapshot gameSnapshot) { + try (Connection connection = connectionManager.getConnection()) { + Long gameId = insertGame(connection, gameSnapshot); + insertPieces(connection, gameId, gameSnapshot.positions()); + } catch (SQLException exception) { + throw new RuntimeException(exception); + } + } + + @Override + public void update(GameSnapshot gameSnapshot) { + + } + + private Long insertGame(Connection connection, GameSnapshot gameSnapshot) throws SQLException { + try ( + PreparedStatement statement = connection.prepareStatement( + "INSERT INTO games(current_turn, finished, winner) VALUES (?, ?, ?)", + PreparedStatement.RETURN_GENERATED_KEYS + ) + ) { + statement.setString(1, gameSnapshot.currentTurn().name()); + statement.setBoolean(2, gameSnapshot.finished()); + statement.setString(3, winnerName(gameSnapshot.winner())); + statement.executeUpdate(); + return generatedId(statement); + } + } + + private Long generatedId(PreparedStatement statement) throws SQLException { + ResultSet resultSet = statement.getGeneratedKeys(); + resultSet.next(); + return resultSet.getLong(1); + } + + private void insertPieces( + Connection connection, + Long gameId, + List positions + ) throws SQLException { + for (PositionInfo positionInfo : positions) { + insertPiece(connection, gameId, positionInfo); + } + } + + private void insertPiece( + Connection connection, + Long gameId, + PositionInfo position + ) throws SQLException { + try ( + PreparedStatement statement = connection.prepareStatement( + "INSERT INTO game_pieces(game_id, team, piece_type, x_value, y_value) " + + "VALUES (?, ?, ?, ?, ?)" + ) + ) { + statement.setLong(1, gameId); + statement.setString(2, teamName(position)); + statement.setString(3, position.piece().getType().name()); + statement.setInt(4, position.point().getX()); + statement.setInt(5, position.point().getY()); + statement.executeUpdate(); + } + } + + private String teamName(PositionInfo position) { + if (position.piece().isSameTeam(Team.HAN)) { + return Team.HAN.name(); + } + return Team.CHO.name(); + } + + private String winnerName(Team winner) { + if (winner == null) { + return null; + } + return winner.name(); + } + + private List toGameSummaries(ResultSet resultSet) throws SQLException { + List gameSummaries = new ArrayList<>(); + while (resultSet.next()) { + gameSummaries.add(new GameSummary( + resultSet.getLong("id"), + resultSet.getBoolean("finished") + )); + } + return gameSummaries; + } +} From 4727d5b6d8fe2af0d9149571c370ffceba0a5776 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 16:44:43 +0900 Subject: [PATCH 10/75] =?UTF-8?q?test:=20=EA=B2=8C=EC=9E=84=20id=EB=A1=9C?= =?UTF-8?q?=20=EC=A0=80=EC=9E=A5=EB=90=9C=20=EA=B2=8C=EC=9E=84=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/JdbcGameRepositoryTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java b/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java index 37f47e5055..0c2dfb0165 100644 --- a/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java +++ b/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java @@ -5,6 +5,8 @@ import janggi.application.GameRepository; import janggi.application.dto.GameSnapshot; import janggi.application.dto.GameSummary; +import janggi.domain.Point; +import janggi.domain.piece.PieceType; import janggi.domain.status.Team; import janggi.dto.PositionInfo; import java.util.List; @@ -85,4 +87,33 @@ void findAll() { assertThat(gameSummaries.get(0).finished()).isFalse(); assertThat(gameSummaries.get(1).finished()).isTrue(); } + + @Test + @DisplayName("게임 id로 저장된 게임 조회") + void findById() { + // given + GameSnapshot gameSnapshot = new GameSnapshot( + 1L, + Team.CHO, + false, + null, + List.of( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ) + ); + repository.save(gameSnapshot); + + // when + Long gameId = repository.findAll().get(0).id(); + GameSnapshot foundGame = repository.findById(gameId).orElseThrow(); + + + // then + assertThat(foundGame.id()).isEqualTo(gameId); + assertThat(foundGame.currentTurn()).isEqualTo(Team.CHO); + assertThat(foundGame.finished()).isFalse(); + assertThat(foundGame.winner()).isNull(); + assertThat(foundGame.positions()).hasSize(2); + } } From db807d116d333e4318fce54cb8029e53dbe0dd22 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 16:44:50 +0900 Subject: [PATCH 11/75] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20id=EB=A1=9C?= =?UTF-8?q?=20=EC=A0=80=EC=9E=A5=EB=90=9C=20=EA=B2=8C=EC=9E=84=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/JdbcGameRepository.java | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/main/java/janggi/persistence/JdbcGameRepository.java b/src/main/java/janggi/persistence/JdbcGameRepository.java index e5b40cdd1f..83d4d88c2f 100644 --- a/src/main/java/janggi/persistence/JdbcGameRepository.java +++ b/src/main/java/janggi/persistence/JdbcGameRepository.java @@ -38,7 +38,22 @@ public List findAll() { @Override public Optional findById(Long gameId) { - return Optional.empty(); + try ( + Connection connection = connectionManager.getConnection(); + PreparedStatement statement = connection.prepareStatement( + "SELECT id, current_turn, finished, winner FROM games WHERE id = ?" + ) + ) { + statement.setLong(1, gameId); + ResultSet resultSet = statement.executeQuery(); + if (!resultSet.next()) { + return Optional.empty(); + } + return Optional.of(toGameSnapshot(connection, resultSet)); + } catch (SQLException exception) { + throw new RuntimeException(exception); + } + } @Override @@ -121,6 +136,13 @@ private String winnerName(Team winner) { return winner.name(); } + private Team winner(String winner) { + if (winner == null) { + return null; + } + return Team.valueOf(winner); + } + private List toGameSummaries(ResultSet resultSet) throws SQLException { List gameSummaries = new ArrayList<>(); while (resultSet.next()) { @@ -131,4 +153,46 @@ private List toGameSummaries(ResultSet resultSet) throws SQLExcepti } return gameSummaries; } + + private List findPositions( + Connection connection, + Long gameId + ) throws SQLException { + try ( + PreparedStatement statement = connection.prepareStatement( + "SELECT team, piece_type, x_value, y_value FROM game_pieces WHERE game_id = ? ORDER BY id" + ) + ) { + statement.setLong(1, gameId); + ResultSet resultSet = statement.executeQuery(); + return toPositions(resultSet); + } + } + + private List toPositions(ResultSet resultSet) throws SQLException { + List positions = new ArrayList<>(); + while (resultSet.next()) { + positions.add(PositionInfo.from( + Team.valueOf(resultSet.getString("team")), + resultSet.getString("piece_type"), + resultSet.getInt("x_value"), + resultSet.getInt("y_value") + )); + } + return positions; + } + + private GameSnapshot toGameSnapshot( + Connection connection, + ResultSet resultSet + ) throws SQLException { + Long gameId = resultSet.getLong("id"); + return new GameSnapshot( + gameId, + Team.valueOf(resultSet.getString("current_turn")), + resultSet.getBoolean("finished"), + winner(resultSet.getString("winner")), + findPositions(connection, gameId) + ); + } } From 32a4ab07bc9692d0295cb9bf73e39efd481797f4 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 17:07:47 +0900 Subject: [PATCH 12/75] =?UTF-8?q?test:=20=EA=B2=8C=EC=9E=84=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=88=98=EC=A0=95=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/JdbcGameRepositoryTest.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java b/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java index 0c2dfb0165..cb63f4e0e9 100644 --- a/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java +++ b/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java @@ -116,4 +116,44 @@ void findById() { assertThat(foundGame.winner()).isNull(); assertThat(foundGame.positions()).hasSize(2); } + + @Test + @DisplayName("게임 상태 수정") + void update() { + // given + GameSnapshot savedGame = new GameSnapshot( + 1L, + Team.CHO, + false, + null, + List.of( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ) + ); + repository.save(savedGame); + Long gameId = repository.findAll().get(0).id(); + + // when + GameSnapshot updatedGame = new GameSnapshot( + gameId, + Team.HAN, + true, + Team.HAN, + List.of( + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ) + ); + repository.update(updatedGame); + + // then + GameSnapshot foundGame = repository.findById(gameId).orElseThrow(); + + assertThat(foundGame.id()).isEqualTo(gameId); + assertThat(foundGame.currentTurn()).isEqualTo(Team.HAN); + assertThat(foundGame.finished()).isTrue(); + assertThat(foundGame.winner()).isEqualTo(Team.HAN); + assertThat(foundGame.positions()).hasSize(1); + + } } From 23c085eee6a6b4844035384e4afe0e2e12b6e50a Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 17:07:54 +0900 Subject: [PATCH 13/75] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/JdbcGameRepository.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/main/java/janggi/persistence/JdbcGameRepository.java b/src/main/java/janggi/persistence/JdbcGameRepository.java index 83d4d88c2f..3863ca1169 100644 --- a/src/main/java/janggi/persistence/JdbcGameRepository.java +++ b/src/main/java/janggi/persistence/JdbcGameRepository.java @@ -68,7 +68,44 @@ public void save(GameSnapshot gameSnapshot) { @Override public void update(GameSnapshot gameSnapshot) { + try (Connection connection = connectionManager.getConnection()) { + updateGame(connection, gameSnapshot); + deletePieces(connection, gameSnapshot.id()); + insertPieces(connection, gameSnapshot.id(), gameSnapshot.positions()); + } catch (SQLException exception) { + throw new RuntimeException(exception); + } + } + + private void updateGame( + Connection connection, + GameSnapshot gameSnapshot + ) throws SQLException { + try ( + PreparedStatement statement = connection.prepareStatement( + "UPDATE games SET current_turn = ?, finished = ?, winner = ? WHERE id = ?" + ) + ) { + statement.setString(1, gameSnapshot.currentTurn().name()); + statement.setBoolean(2, gameSnapshot.finished()); + statement.setString(3, winnerName(gameSnapshot.winner())); + statement.setLong(4, gameSnapshot.id()); + statement.executeUpdate(); + } + } + private void deletePieces( + Connection connection, + Long gameId + ) throws SQLException { + try ( + PreparedStatement statement = connection.prepareStatement( + "DELETE FROM game_pieces WHERE game_id = ?" + ) + ) { + statement.setLong(1, gameId); + statement.executeUpdate(); + } } private Long insertGame(Connection connection, GameSnapshot gameSnapshot) throws SQLException { From 5a49376bef8fff6121379ae7ff6c89b62d64c763 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 17:30:21 +0900 Subject: [PATCH 14/75] =?UTF-8?q?refactor:=20state=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EA=B8=B0=EB=AC=BC?= =?UTF-8?q?/=EC=9C=84=EC=B9=98=20=EB=B0=98=ED=99=98=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Board.java | 28 +++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 0aa4372e9f..edd6743196 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -14,34 +14,34 @@ public class Board { private static final int BOARD_HEIGHT = 10; - private final Map state; + private final Map piecesByPoint; public Board() { - this.state = new LinkedHashMap<>(); + this.piecesByPoint = new LinkedHashMap<>(); } public void init(List positionInfos) { - positionInfos.forEach(info -> state.put(info.point(), info.piece())); + positionInfos.forEach(info -> piecesByPoint.put(info.point(), info.piece())); } public void move(Point from, Point to, Team team) { validateFromPoint(from, team); validateToPoint(to, team); - Piece piece = state.get(from); + Piece piece = piecesByPoint.get(from); List route = piece.getRoute(from, to); - Piece targetPiece = state.get(to); + Piece targetPiece = piecesByPoint.get(to); if (targetPiece != null && !piece.canCapture(targetPiece)) { throw new IllegalArgumentException("이 기물은 해당 타겟을 잡을 수 없습니다."); } if (!piece.canMove(getPieces(route))) { throw new IllegalArgumentException("해당 기물의 이동 경로에 장애물이 있거나 규칙에 어긋납니다."); } - state.remove(from); - state.put(to, piece); + piecesByPoint.remove(from); + piecesByPoint.put(to, piece); } public boolean isKingDie(Team team) { - return state.values().stream() + return piecesByPoint.values().stream() .noneMatch(piece -> piece.isSameType(PieceType.JANG) && piece.isSameTeam(team)); } @@ -51,7 +51,7 @@ public List> getPoints() { for (int i = 0; i < BOARD_HEIGHT; i++) { pieces.add( Point.getRow(i).stream() - .map(state::get) + .map(piecesByPoint::get) .toList() ); } @@ -60,11 +60,15 @@ public List> getPoints() { public List getPieces(List point) { return point.stream() - .map(state::get) + .map(piecesByPoint::get) .filter(Objects::nonNull) .toList(); } + public Map getPiecesByPoint() { + return piecesByPoint; + } + private void validateFromPoint(Point from, Team team) { if (isEmptyPoint(from)) { throw new IllegalArgumentException("출발지에 이동할 기물이 없습니다."); @@ -81,10 +85,10 @@ private void validateToPoint(Point to, Team team) { } private boolean isSameTeam(Point point, Team team) { - return state.get(point).isSameTeam(team); + return piecesByPoint.get(point).isSameTeam(team); } private boolean isEmptyPoint(Point point) { - return state.get(point) == null; + return piecesByPoint.get(point) == null; } } From 6bd441575052d263a91e76cf10663d8ebeea0a09 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 17:34:52 +0900 Subject: [PATCH 15/75] =?UTF-8?q?test:=20Board=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/BoardTest.java | 153 +++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/test/java/janggi/domain/BoardTest.java diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java new file mode 100644 index 0000000000..3be948170a --- /dev/null +++ b/src/test/java/janggi/domain/BoardTest.java @@ -0,0 +1,153 @@ +package janggi.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import janggi.domain.piece.Piece; +import janggi.domain.piece.PieceType; +import janggi.domain.status.Team; +import janggi.dto.PositionInfo; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class BoardTest { + + @Test + @DisplayName("출발지에 기물이 없으면 예외가 발생한다") + void moveFromEmptyPoint() { + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ); + + assertThatThrownBy(() -> board.move(Point.of(0, 0), Point.of(0, 1), Team.CHO)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("출발지에 이동할 기물이 없습니다."); + } + + @Test + @DisplayName("상대 기물은 움직일 수 없다") + void moveOtherTeamPiece() { + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8), + PositionInfo.from(Team.HAN, "CHA", 0, 0) + ); + + assertThatThrownBy(() -> board.move(Point.of(0, 0), Point.of(0, 1), Team.CHO)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("상대방의 기물은 움직일 수 없습니다."); + } + + @Test + @DisplayName("도착지에 아군 기물이 있으면 예외가 발생한다") + void moveToSameTeamPiece() { + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8), + PositionInfo.from(Team.CHO, "CHA", 0, 0), + PositionInfo.from(Team.CHO, "JOL", 0, 1) + ); + + assertThatThrownBy(() -> board.move(Point.of(0, 0), Point.of(0, 1), Team.CHO)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("도착지에 본인의 기물이 있습니다."); + } + + @Test + @DisplayName("이동 경로에 장애물이 있으면 예외가 발생한다") + void moveBlockedRoute() { + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8), + PositionInfo.from(Team.CHO, "CHA", 0, 0), + PositionInfo.from(Team.CHO, "JOL", 0, 1) + ); + + assertThatThrownBy(() -> board.move(Point.of(0, 0), Point.of(0, 3), Team.CHO)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("이동 경로에 장애물이 있거나 규칙에 어긋납니다."); + } + + @Test + @DisplayName("포는 상대 포를 잡을 수 없다") + void phoCanNotCapturePho() { + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8), + PositionInfo.from(Team.CHO, "PHO", 1, 2), + PositionInfo.from(Team.HAN, "CHA", 1, 4), + PositionInfo.from(Team.HAN, "PHO", 1, 6) + ); + + assertThatThrownBy(() -> board.move(Point.of(1, 2), Point.of(1, 6), Team.CHO)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("해당 타겟을 잡을 수 없습니다."); + } + + @Test + @DisplayName("기물을 이동하면 출발지는 비고 도착지에 기물이 위치한다") + void movePiece() { + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8), + PositionInfo.from(Team.CHO, "CHA", 0, 0) + ); + + board.move(Point.of(0, 0), Point.of(0, 3), Team.CHO); + + assertThat(pieceAt(board, 0, 0)).isNull(); + assertThat(pieceAt(board, 0, 3).getType()).isEqualTo(PieceType.CHA); + assertThat(pieceAt(board, 0, 3).isSameTeam(Team.CHO)).isTrue(); + } + + @Test + @DisplayName("적 기물이 있는 위치로 이동하면 포획한다") + void capturePiece() { + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8), + PositionInfo.from(Team.CHO, "JANG", 4, 2), + PositionInfo.from(Team.HAN, "JOL", 4, 3) + ); + + board.move(Point.of(4, 2), Point.of(4, 3), Team.CHO); + + assertThat(pieceAt(board, 4, 2)).isNull(); + assertThat(pieceAt(board, 4, 3).getType()).isEqualTo(PieceType.JANG); + assertThat(pieceAt(board, 4, 3).isSameTeam(Team.CHO)).isTrue(); + } + + @Test + @DisplayName("팀의 왕이 살아있으면 죽지 않은 상태다") + void kingAlive() { + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ); + + assertThat(board.isKingDie(Team.CHO)).isFalse(); + assertThat(board.isKingDie(Team.HAN)).isFalse(); + } + + @Test + @DisplayName("팀의 왕이 없으면 죽은 상태다") + void kingDie() { + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1) + ); + + assertThat(board.isKingDie(Team.HAN)).isTrue(); + } + + private Board initBoard(PositionInfo... positionInfos) { + Board board = new Board(); + board.init(List.of(positionInfos)); + return board; + } + + private Piece pieceAt(Board board, int x, int y) { + return board.getPoints().get(y).get(x); + } +} From dd042235ab3e0cf8562b6134e421bfd2371f677a Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 17:52:28 +0900 Subject: [PATCH 16/75] =?UTF-8?q?test:=20Board=20=ED=98=84=EC=9E=AC=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EB=B0=98=ED=99=98=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/BoardTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java index 3be948170a..27a421eab6 100644 --- a/src/test/java/janggi/domain/BoardTest.java +++ b/src/test/java/janggi/domain/BoardTest.java @@ -141,6 +141,22 @@ void kingDie() { assertThat(board.isKingDie(Team.HAN)).isTrue(); } + @Test + @DisplayName("보드의 현재 상태 반환") + void boardStatus() { + // given + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ); + + // when + List boardStatus = board.getBoardStatus(); + + // then + assertThat(boardStatus.size()).isEqualTo(2); + } + private Board initBoard(PositionInfo... positionInfos) { Board board = new Board(); board.init(List.of(positionInfos)); From 1af2477ead183f07863b6231871507102935721b Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 17:52:36 +0900 Subject: [PATCH 17/75] =?UTF-8?q?feat:=20Board=20=ED=98=84=EC=9E=AC=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EB=B0=98=ED=99=98=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Board.java | 4 ++++ src/main/java/janggi/dto/PositionInfo.java | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index edd6743196..2e7f9d792a 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -65,6 +65,10 @@ public List getPieces(List point) { .toList(); } + public List getBoardStatus() { + return PositionInfo.from(piecesByPoint); + } + public Map getPiecesByPoint() { return piecesByPoint; } diff --git a/src/main/java/janggi/dto/PositionInfo.java b/src/main/java/janggi/dto/PositionInfo.java index 9e26f7ee3b..d2477b1176 100644 --- a/src/main/java/janggi/dto/PositionInfo.java +++ b/src/main/java/janggi/dto/PositionInfo.java @@ -6,6 +6,8 @@ import janggi.domain.piece.PieceType; import janggi.domain.status.Team; import java.util.List; +import java.util.Map; +import java.util.Set; public record PositionInfo( Piece piece, @@ -26,4 +28,11 @@ public static PositionInfo from(Team team, String pieceName, int x, int y) { Point.of(x, y) ); } + + public static List from(Map boardStatus) { + return boardStatus.entrySet().stream() + .filter(entry -> entry.getValue() != null) + .map(entry -> new PositionInfo(entry.getValue(), entry.getKey())) + .toList(); + } } From 2ab4efcd72155d35dffe1f97f219242deb775ac0 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 21:04:18 +0900 Subject: [PATCH 18/75] =?UTF-8?q?test:=20=EC=9E=A5=EA=B8=B0=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B3=B4=EA=B0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/janggi/domain/JanggiGameTest.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/test/java/janggi/domain/JanggiGameTest.java diff --git a/src/test/java/janggi/domain/JanggiGameTest.java b/src/test/java/janggi/domain/JanggiGameTest.java new file mode 100644 index 0000000000..c4f7cea747 --- /dev/null +++ b/src/test/java/janggi/domain/JanggiGameTest.java @@ -0,0 +1,70 @@ +package janggi.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.status.HanTurn; +import janggi.domain.status.Team; +import janggi.dto.PositionInfo; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class JanggiGameTest { + + @Test + @DisplayName("새 게임은 초나라 턴으로 시작") + void startChoTurn() { + // given + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ); + + // when + JanggiGame janggiGame = new JanggiGame(board); + + // then + assertThat(janggiGame.currentTurn()).isEqualTo(Team.CHO); + } + + @Test + @DisplayName("현재 보드 상태를 저장용 목록으로 조회") + void getBoardStatus() { + // given + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ); + + // when + JanggiGame janggiGame = new JanggiGame(board); + List boardStatus = janggiGame.boardStatus(); + + // then + assertThat(boardStatus).hasSize(2); + assertThat(boardStatus.get(0).point()).isEqualTo(Point.of(4, 1)); + assertThat(boardStatus.get(1).point()).isEqualTo(Point.of(4, 8)); + } + + @Test + @DisplayName("저장된 게임은 해당 턴 차례로 시작") + void restoreTurn() { + // given + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ); + + // when + JanggiGame janggiGame = new JanggiGame(board, new HanTurn()); + + // then + assertThat(janggiGame.currentTurn()).isEqualTo(Team.HAN); + } + + private Board initBoard(PositionInfo... positionInfos) { + Board board = new Board(); + board.init(List.of(positionInfos)); + return board; + } +} From 42b8183d66bca2d4a175ca4e33688f29212189c3 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 21:05:10 +0900 Subject: [PATCH 19/75] =?UTF-8?q?feat:=20=EC=9E=A5=EA=B8=B0=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 현재 차례 반환 기능 구현 - 보드 상태 반환 기능 구현 - 재시작 장기게임 생성자 생성 --- src/main/java/janggi/domain/JanggiGame.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index 0c4dfb8afc..0f96f19e25 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -4,6 +4,7 @@ import janggi.domain.status.ChoTurn; import janggi.domain.status.GameStatus; import janggi.domain.status.Team; +import janggi.dto.PositionInfo; import java.util.List; public class JanggiGame { @@ -16,6 +17,11 @@ public JanggiGame(Board board) { this.gameStatus = new ChoTurn(); } + public JanggiGame(Board board, GameStatus gameStatus) { + this.board = board; + this.gameStatus = gameStatus; + } + public boolean isFinished() { return gameStatus.isFinished(); } @@ -34,4 +40,11 @@ public List> getBoardStatus() { public void play(Point from, Point to) { this.gameStatus = gameStatus.move(from, to, board); } + + public Team currentTurn() { + return gameStatus.getTeam(); + } + public List boardStatus() { + return board.getBoardStatus(); + } } From c5679d5c583c3d31b655e61cadd067fa56c54edb Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 21:23:39 +0900 Subject: [PATCH 20/75] =?UTF-8?q?test:=20=EC=9E=A5=EA=B8=B0=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=EC=A0=80=EC=9E=A5=20=EA=B2=8C=EC=9E=84=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/JanggiGameServiceTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/test/java/janggi/application/JanggiGameServiceTest.java diff --git a/src/test/java/janggi/application/JanggiGameServiceTest.java b/src/test/java/janggi/application/JanggiGameServiceTest.java new file mode 100644 index 0000000000..0a64429f3b --- /dev/null +++ b/src/test/java/janggi/application/JanggiGameServiceTest.java @@ -0,0 +1,33 @@ +package janggi.application; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.application.dto.GameSummary; +import janggi.persistence.JdbcGameRepository; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class JanggiGameServiceTest { + + @Test + @DisplayName("저장된 게임 목록 조회") + void find_all_games() { + // given + GameRepository gameRepository = new FakeGameRepository( + List.of( + new GameSummary(1L, false), + new GameSummary(2L, true) + ) + ); + JanggiGameService janggiGameService = new JanggiGameService(gameRepository); + + // when + List gameSummaries = janggiGameService.findAllGames(); + + // then + assertThat(gameSummaries).hasSize(2); + assertThat(gameSummaries.get(0).id()).isEqualTo(1L); + assertThat(gameSummaries.get(1).id()).isEqualTo(2L); + } +} From e9af6c70e5e7c2f889ca213dc506d5c6d297eb0e Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 21:27:57 +0900 Subject: [PATCH 21/75] =?UTF-8?q?test:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=EB=9F=AC=EB=A6=AC=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/application/JanggiGameServiceTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/janggi/application/JanggiGameServiceTest.java b/src/test/java/janggi/application/JanggiGameServiceTest.java index 0a64429f3b..31534dbd7c 100644 --- a/src/test/java/janggi/application/JanggiGameServiceTest.java +++ b/src/test/java/janggi/application/JanggiGameServiceTest.java @@ -3,7 +3,6 @@ import static org.assertj.core.api.Assertions.assertThat; import janggi.application.dto.GameSummary; -import janggi.persistence.JdbcGameRepository; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From 7fe74e2563289376a344a367f93da58fca29642e Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 21:28:13 +0900 Subject: [PATCH 22/75] =?UTF-8?q?feat:=20=EC=A0=80=EC=9E=A5=EB=90=9C=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../janggi/application/JanggiGameService.java | 17 +++++++++ .../application/FakeGameRepository.java | 35 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/main/java/janggi/application/JanggiGameService.java create mode 100644 src/test/java/janggi/application/FakeGameRepository.java diff --git a/src/main/java/janggi/application/JanggiGameService.java b/src/main/java/janggi/application/JanggiGameService.java new file mode 100644 index 0000000000..92ab30a007 --- /dev/null +++ b/src/main/java/janggi/application/JanggiGameService.java @@ -0,0 +1,17 @@ +package janggi.application; + +import janggi.application.dto.GameSummary; +import java.util.List; + +public class JanggiGameService { + + private final GameRepository gameRepository; + + public JanggiGameService(GameRepository gameRepository) { + this.gameRepository = gameRepository; + } + + public List findAllGames() { + return gameRepository.findAll(); + } +} diff --git a/src/test/java/janggi/application/FakeGameRepository.java b/src/test/java/janggi/application/FakeGameRepository.java new file mode 100644 index 0000000000..e556535f3f --- /dev/null +++ b/src/test/java/janggi/application/FakeGameRepository.java @@ -0,0 +1,35 @@ +package janggi.application; + +import janggi.application.dto.GameSnapshot; +import janggi.application.dto.GameSummary; +import java.util.List; +import java.util.Optional; + +public class FakeGameRepository implements GameRepository { + + private final List gameSummaries; + + public FakeGameRepository(List gameSummaries) { + this.gameSummaries = gameSummaries; + } + + @Override + public List findAll() { + return gameSummaries; + } + + @Override + public Optional findById(Long gameId) { + return Optional.empty(); + } + + @Override + public void save(GameSnapshot gameSnapshot) { + + } + + @Override + public void update(GameSnapshot gameSnapshot) { + + } +} From 53fe18d23ff486829785b36c6a7592d2827136ed Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 21:32:23 +0900 Subject: [PATCH 23/75] =?UTF-8?q?test:=20=EC=A0=80=EC=9E=A5=EB=90=9C=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=EC=9D=B4=20=EC=97=86=EC=9D=84=20=EC=8B=9C=20?= =?UTF-8?q?=EC=83=88=20=EA=B2=8C=EC=9E=84=20=EC=8B=9C=EC=9E=91=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/JanggiGameServiceTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/test/java/janggi/application/JanggiGameServiceTest.java b/src/test/java/janggi/application/JanggiGameServiceTest.java index 31534dbd7c..c24742054f 100644 --- a/src/test/java/janggi/application/JanggiGameServiceTest.java +++ b/src/test/java/janggi/application/JanggiGameServiceTest.java @@ -3,6 +3,9 @@ import static org.assertj.core.api.Assertions.assertThat; import janggi.application.dto.GameSummary; +import janggi.domain.JanggiGame; +import janggi.domain.status.Team; +import janggi.dto.PositionInfo; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -29,4 +32,25 @@ void find_all_games() { assertThat(gameSummaries.get(0).id()).isEqualTo(1L); assertThat(gameSummaries.get(1).id()).isEqualTo(2L); } + + @Test + @DisplayName("저장된 게임이 없을 시 새 게임 시작") + void no_save_game_new_game_start() { + // given + GameRepository gameRepository = new FakeGameRepository(List.of()); + InitialBoardProvider initialBoardProvider = new FakeInitialBoardProvider( + List.of( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ) + ) + JanggiGameService janggiGameService = new JanggiGameService(gameRepository, initialBoardProvider); + + // when + JanggiGame janggiGame = janggiGameService.startNewGame(); + + // then + assertThat(janggiGame.currentTurn()).isEqualTo(Team.CHO); + assertThat(janggiGame.boardStatus()).hasSize(2); + } } From cfadd1c8f46e2fae3f0c7db4eb215766b03346e7 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 21:38:24 +0900 Subject: [PATCH 24/75] =?UTF-8?q?test:=20=EC=A0=80=EC=9E=A5=EB=90=9C=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=EC=9D=B4=20=EC=97=86=EC=9D=84=20=EC=8B=9C=20?= =?UTF-8?q?=EC=83=88=20=EA=B2=8C=EC=9E=84=20=EC=8B=9C=EC=9E=91=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/application/JanggiGameServiceTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/janggi/application/JanggiGameServiceTest.java b/src/test/java/janggi/application/JanggiGameServiceTest.java index c24742054f..3deed618ac 100644 --- a/src/test/java/janggi/application/JanggiGameServiceTest.java +++ b/src/test/java/janggi/application/JanggiGameServiceTest.java @@ -22,7 +22,8 @@ void find_all_games() { new GameSummary(2L, true) ) ); - JanggiGameService janggiGameService = new JanggiGameService(gameRepository); + InitialBoardProvider initialBoardProvider = new FakeInitialBoardProvider(List.of()); + JanggiGameService janggiGameService = new JanggiGameService(gameRepository,initialBoardProvider); // when List gameSummaries = janggiGameService.findAllGames(); @@ -43,7 +44,7 @@ void no_save_game_new_game_start() { PositionInfo.from(Team.CHO, "JANG", 4, 1), PositionInfo.from(Team.HAN, "JANG", 4, 8) ) - ) + ); JanggiGameService janggiGameService = new JanggiGameService(gameRepository, initialBoardProvider); // when From b0d55fe8ff6df0442838e7cb967e5a806f1a9548 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 21:38:37 +0900 Subject: [PATCH 25/75] =?UTF-8?q?feat:=20=EC=9E=A5=EA=B8=B0=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=EC=8B=9C=EC=9E=91=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/InitialBoardProvider.java | 8 ++++++++ .../janggi/application/JanggiGameService.java | 12 +++++++++++- .../application/FakeInitialBoardProvider.java | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/main/java/janggi/application/InitialBoardProvider.java create mode 100644 src/test/java/janggi/application/FakeInitialBoardProvider.java diff --git a/src/main/java/janggi/application/InitialBoardProvider.java b/src/main/java/janggi/application/InitialBoardProvider.java new file mode 100644 index 0000000000..394c364507 --- /dev/null +++ b/src/main/java/janggi/application/InitialBoardProvider.java @@ -0,0 +1,8 @@ +package janggi.application; + +import janggi.dto.PositionInfo; +import java.util.List; + +public interface InitialBoardProvider { + List load(); +} diff --git a/src/main/java/janggi/application/JanggiGameService.java b/src/main/java/janggi/application/JanggiGameService.java index 92ab30a007..b4b114b588 100644 --- a/src/main/java/janggi/application/JanggiGameService.java +++ b/src/main/java/janggi/application/JanggiGameService.java @@ -1,17 +1,27 @@ package janggi.application; import janggi.application.dto.GameSummary; +import janggi.domain.Board; +import janggi.domain.JanggiGame; import java.util.List; public class JanggiGameService { private final GameRepository gameRepository; + private final InitialBoardProvider initialBoardProvider; - public JanggiGameService(GameRepository gameRepository) { + public JanggiGameService(GameRepository gameRepository, InitialBoardProvider initialBoardProvider) { this.gameRepository = gameRepository; + this.initialBoardProvider = initialBoardProvider; } public List findAllGames() { return gameRepository.findAll(); } + + public JanggiGame startNewGame() { + Board board = new Board(); + board.init(initialBoardProvider.load()); + return new JanggiGame(board); + } } diff --git a/src/test/java/janggi/application/FakeInitialBoardProvider.java b/src/test/java/janggi/application/FakeInitialBoardProvider.java new file mode 100644 index 0000000000..c393806c45 --- /dev/null +++ b/src/test/java/janggi/application/FakeInitialBoardProvider.java @@ -0,0 +1,18 @@ +package janggi.application; + +import janggi.dto.PositionInfo; +import java.util.List; + +public class FakeInitialBoardProvider implements InitialBoardProvider { + + private final List positionInfos; + + public FakeInitialBoardProvider(List positionInfos) { + this.positionInfos = positionInfos; + } + + @Override + public List load() { + return positionInfos; + } +} From 4a57b58ee1b7623494fe6494d39f5d1f4349fbd0 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 22:00:57 +0900 Subject: [PATCH 26/75] =?UTF-8?q?test:=20=EC=9E=A5=EA=B8=B0=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 선택한 게임 재시작 테스트 추가 - 선택한 게임이 없을 시 예외 발생 테스트 추가 --- .../application/JanggiGameServiceTest.java | 64 ++++++++++++++++++- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/src/test/java/janggi/application/JanggiGameServiceTest.java b/src/test/java/janggi/application/JanggiGameServiceTest.java index 3deed618ac..f8282eef71 100644 --- a/src/test/java/janggi/application/JanggiGameServiceTest.java +++ b/src/test/java/janggi/application/JanggiGameServiceTest.java @@ -1,7 +1,9 @@ package janggi.application; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import janggi.application.dto.GameSnapshot; import janggi.application.dto.GameSummary; import janggi.domain.JanggiGame; import janggi.domain.status.Team; @@ -20,7 +22,8 @@ void find_all_games() { List.of( new GameSummary(1L, false), new GameSummary(2L, true) - ) + ), + null ); InitialBoardProvider initialBoardProvider = new FakeInitialBoardProvider(List.of()); JanggiGameService janggiGameService = new JanggiGameService(gameRepository,initialBoardProvider); @@ -35,10 +38,10 @@ void find_all_games() { } @Test - @DisplayName("저장된 게임이 없을 시 새 게임 시작") + @DisplayName("새 게임 시작") void no_save_game_new_game_start() { // given - GameRepository gameRepository = new FakeGameRepository(List.of()); + GameRepository gameRepository = new FakeGameRepository(List.of(),null); InitialBoardProvider initialBoardProvider = new FakeInitialBoardProvider( List.of( PositionInfo.from(Team.CHO, "JANG", 4, 1), @@ -54,4 +57,59 @@ void no_save_game_new_game_start() { assertThat(janggiGame.currentTurn()).isEqualTo(Team.CHO); assertThat(janggiGame.boardStatus()).hasSize(2); } + + @Test + @DisplayName("선택한 게임 재시작") + void load_game() { + // given + GameSnapshot gameSnapshot = new GameSnapshot( + 1L, + Team.HAN, + false, + null, + List.of( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ) + ); + GameRepository gameRepository = new FakeGameRepository( + List.of(new GameSummary(1L, false)), + gameSnapshot + ); + InitialBoardProvider initialBoardProvider = new FakeInitialBoardProvider(List.of()); + JanggiGameService janggiGameService = new JanggiGameService(gameRepository, initialBoardProvider); + + // when + JanggiGame janggiGame = janggiGameService.loadGame(1L); + + assertThat(janggiGame.currentTurn()).isEqualTo(Team.HAN); + assertThat(janggiGame.boardStatus()).hasSize(2); + } + + @Test + @DisplayName("선택한 게임이 없을 시 예외 발생") + void no_game_error() { + // given + GameSnapshot gameSnapshot = new GameSnapshot( + 1L, + Team.HAN, + false, + null, + List.of( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ) + ); + GameRepository gameRepository = new FakeGameRepository( + List.of(new GameSummary(1L, false)), + gameSnapshot + ); + InitialBoardProvider initialBoardProvider = new FakeInitialBoardProvider(List.of()); + JanggiGameService janggiGameService = new JanggiGameService(gameRepository, initialBoardProvider); + + // when & then + assertThatThrownBy(() -> janggiGameService.loadGame(2L)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("존재하지"); + } } From c8a327f95657c7f01b12a132d2e98dd556444d33 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 22:01:11 +0900 Subject: [PATCH 27/75] =?UTF-8?q?feat:=20=EC=84=A0=ED=83=9D=ED=95=9C=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EC=9E=AC=EC=8B=9C=EC=9E=91=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../janggi/application/JanggiGameService.java | 24 +++++++++++++++++++ .../application/FakeGameRepository.java | 12 ++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/main/java/janggi/application/JanggiGameService.java b/src/main/java/janggi/application/JanggiGameService.java index b4b114b588..cfa5ca733c 100644 --- a/src/main/java/janggi/application/JanggiGameService.java +++ b/src/main/java/janggi/application/JanggiGameService.java @@ -1,8 +1,14 @@ package janggi.application; +import janggi.application.dto.GameSnapshot; import janggi.application.dto.GameSummary; import janggi.domain.Board; import janggi.domain.JanggiGame; +import janggi.domain.status.ChoTurn; +import janggi.domain.status.FinishedGame; +import janggi.domain.status.GameStatus; +import janggi.domain.status.HanTurn; +import janggi.domain.status.Team; import java.util.List; public class JanggiGameService { @@ -24,4 +30,22 @@ public JanggiGame startNewGame() { board.init(initialBoardProvider.load()); return new JanggiGame(board); } + + public JanggiGame loadGame(Long gameId) { + GameSnapshot gameSnapshot = gameRepository.findById(gameId) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 게임입니다.")); + Board board = new Board(); + board.init(gameSnapshot.positions()); + return new JanggiGame(board, gameStatus(gameSnapshot)); + } + + private GameStatus gameStatus(GameSnapshot gameSnapshot) { + if (gameSnapshot.finished()) { + return new FinishedGame(gameSnapshot.winner()); + } + if (gameSnapshot.currentTurn() == Team.HAN) { + return new HanTurn(); + } + return new ChoTurn(); + } } diff --git a/src/test/java/janggi/application/FakeGameRepository.java b/src/test/java/janggi/application/FakeGameRepository.java index e556535f3f..0f9df6ca1e 100644 --- a/src/test/java/janggi/application/FakeGameRepository.java +++ b/src/test/java/janggi/application/FakeGameRepository.java @@ -8,9 +8,11 @@ public class FakeGameRepository implements GameRepository { private final List gameSummaries; + private final GameSnapshot gameSnapshot; - public FakeGameRepository(List gameSummaries) { + public FakeGameRepository(List gameSummaries, GameSnapshot gameSnapshot) { this.gameSummaries = gameSummaries; + this.gameSnapshot = gameSnapshot; } @Override @@ -20,7 +22,13 @@ public List findAll() { @Override public Optional findById(Long gameId) { - return Optional.empty(); + if (gameSnapshot == null) { + return Optional.empty(); + } + if (!gameSnapshot.id().equals(gameId)) { + return Optional.empty(); + } + return Optional.of(gameSnapshot); } @Override From b4fdc9b53a1b54227457db1c7620d0689cf8657e Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 22:38:06 +0900 Subject: [PATCH 28/75] =?UTF-8?q?test:=20=ED=84=B4=EC=9D=B4=20=EB=84=98?= =?UTF-8?q?=EC=96=B4=EA=B0=80=EB=A9=B4=20=EA=B2=8C=EC=9E=84=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=A0=80=EC=9E=A5=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/JanggiGameServiceTest.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/test/java/janggi/application/JanggiGameServiceTest.java b/src/test/java/janggi/application/JanggiGameServiceTest.java index f8282eef71..a832536c9f 100644 --- a/src/test/java/janggi/application/JanggiGameServiceTest.java +++ b/src/test/java/janggi/application/JanggiGameServiceTest.java @@ -6,6 +6,7 @@ import janggi.application.dto.GameSnapshot; import janggi.application.dto.GameSummary; import janggi.domain.JanggiGame; +import janggi.domain.Point; import janggi.domain.status.Team; import janggi.dto.PositionInfo; import java.util.List; @@ -112,4 +113,35 @@ void no_game_error() { .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("존재하지"); } + + @Test + @DisplayName("턴이 넘어가면 게임 상태 저장") + void play_turn_and_save() { + // given + GameSnapshot gameSnapshot = new GameSnapshot( + 1L, + Team.CHO, + false, + null, + List.of( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ) + ); + FakeGameRepository gameRepository = new FakeGameRepository( + List.of(new GameSummary(1L, false)), + gameSnapshot + ); + InitialBoardProvider initialBoardProvider = new FakeInitialBoardProvider(List.of()); + JanggiGameService janggiGameService = + new JanggiGameService(gameRepository, initialBoardProvider); + + // when + janggiGameService.play(1L, Point.of(4, 1), Point.of(4, 2)); + + // then + assertThat(gameRepository.updatedGameSnapshot()).isNotNull(); + assertThat(gameRepository.updatedGameSnapshot().id()).isEqualTo(1L); + assertThat(gameRepository.updatedGameSnapshot().currentTurn()).isEqualTo(Team.HAN); + } } From cf3ea5bbd8d2e5fe9509446a9843fd4ade1ea3b0 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 22:38:18 +0900 Subject: [PATCH 29/75] =?UTF-8?q?feat:=20=ED=84=B4=EC=9D=B4=20=EB=84=98?= =?UTF-8?q?=EC=96=B4=EA=B0=80=EB=A9=B4=20=EA=B2=8C=EC=9E=84=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=A0=80=EC=9E=A5=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../janggi/application/JanggiGameService.java | 24 +++++++++++++++++++ .../application/FakeGameRepository.java | 7 +++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/janggi/application/JanggiGameService.java b/src/main/java/janggi/application/JanggiGameService.java index cfa5ca733c..9eeaa6d2ad 100644 --- a/src/main/java/janggi/application/JanggiGameService.java +++ b/src/main/java/janggi/application/JanggiGameService.java @@ -4,6 +4,7 @@ import janggi.application.dto.GameSummary; import janggi.domain.Board; import janggi.domain.JanggiGame; +import janggi.domain.Point; import janggi.domain.status.ChoTurn; import janggi.domain.status.FinishedGame; import janggi.domain.status.GameStatus; @@ -39,6 +40,12 @@ public JanggiGame loadGame(Long gameId) { return new JanggiGame(board, gameStatus(gameSnapshot)); } + public void play(Long gameId, Point from, Point to) { + JanggiGame janggiGame = loadGame(gameId); + janggiGame.play(from, to); + gameRepository.update(toSnapshot(gameId, janggiGame)); + } + private GameStatus gameStatus(GameSnapshot gameSnapshot) { if (gameSnapshot.finished()) { return new FinishedGame(gameSnapshot.winner()); @@ -48,4 +55,21 @@ private GameStatus gameStatus(GameSnapshot gameSnapshot) { } return new ChoTurn(); } + + private GameSnapshot toSnapshot(Long gameId, JanggiGame janggiGame) { + return new GameSnapshot( + gameId, + janggiGame.currentTurn(), + janggiGame.isFinished(), + winner(janggiGame), + janggiGame.boardStatus() + ); + } + + private Team winner(JanggiGame janggiGame) { + if (!janggiGame.isFinished()) { + return null; + } + return janggiGame.getWinner(); + } } diff --git a/src/test/java/janggi/application/FakeGameRepository.java b/src/test/java/janggi/application/FakeGameRepository.java index 0f9df6ca1e..2995e82a39 100644 --- a/src/test/java/janggi/application/FakeGameRepository.java +++ b/src/test/java/janggi/application/FakeGameRepository.java @@ -9,6 +9,7 @@ public class FakeGameRepository implements GameRepository { private final List gameSummaries; private final GameSnapshot gameSnapshot; + private GameSnapshot updatedGameSnapshot; public FakeGameRepository(List gameSummaries, GameSnapshot gameSnapshot) { this.gameSummaries = gameSummaries; @@ -38,6 +39,10 @@ public void save(GameSnapshot gameSnapshot) { @Override public void update(GameSnapshot gameSnapshot) { - + this.updatedGameSnapshot = gameSnapshot; } + + public GameSnapshot updatedGameSnapshot() { + return updatedGameSnapshot; + }; } From 8e7d362a483922c17a2ac6420fae18f11f1b32a0 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 22:48:09 +0900 Subject: [PATCH 30/75] =?UTF-8?q?refactor:=20save()=20=EB=A6=AC=ED=84=B4?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20Long=20=ED=83=80=EC=9E=85=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/application/GameRepository.java | 2 +- src/main/java/janggi/persistence/JdbcGameRepository.java | 3 ++- src/test/java/janggi/application/FakeGameRepository.java | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/janggi/application/GameRepository.java b/src/main/java/janggi/application/GameRepository.java index ea9ef4f62b..753f141874 100644 --- a/src/main/java/janggi/application/GameRepository.java +++ b/src/main/java/janggi/application/GameRepository.java @@ -8,6 +8,6 @@ public interface GameRepository { List findAll(); Optional findById(Long gameId); - void save(GameSnapshot gameSnapshot); + Long save(GameSnapshot gameSnapshot); void update(GameSnapshot gameSnapshot); } diff --git a/src/main/java/janggi/persistence/JdbcGameRepository.java b/src/main/java/janggi/persistence/JdbcGameRepository.java index 3863ca1169..98f5af9d71 100644 --- a/src/main/java/janggi/persistence/JdbcGameRepository.java +++ b/src/main/java/janggi/persistence/JdbcGameRepository.java @@ -57,10 +57,11 @@ public Optional findById(Long gameId) { } @Override - public void save(GameSnapshot gameSnapshot) { + public Long save(GameSnapshot gameSnapshot) { try (Connection connection = connectionManager.getConnection()) { Long gameId = insertGame(connection, gameSnapshot); insertPieces(connection, gameId, gameSnapshot.positions()); + return gameId; } catch (SQLException exception) { throw new RuntimeException(exception); } diff --git a/src/test/java/janggi/application/FakeGameRepository.java b/src/test/java/janggi/application/FakeGameRepository.java index 2995e82a39..42706b144e 100644 --- a/src/test/java/janggi/application/FakeGameRepository.java +++ b/src/test/java/janggi/application/FakeGameRepository.java @@ -33,8 +33,8 @@ public Optional findById(Long gameId) { } @Override - public void save(GameSnapshot gameSnapshot) { - + public Long save(GameSnapshot gameSnapshot) { + return gameSnapshot.id(); } @Override From 4dbbd4e78f58c06b771b8c280bf6d2113927334d Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 22:56:45 +0900 Subject: [PATCH 31/75] =?UTF-8?q?test:=20=EC=83=88=20=EA=B2=8C=EC=9E=84?= =?UTF-8?q?=EC=9D=84=20=EC=83=9D=EC=84=B1=ED=95=98=EA=B3=A0=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=ED=95=98=EB=8A=94=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/JanggiGameServiceTest.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/test/java/janggi/application/JanggiGameServiceTest.java b/src/test/java/janggi/application/JanggiGameServiceTest.java index a832536c9f..4dac14fdcf 100644 --- a/src/test/java/janggi/application/JanggiGameServiceTest.java +++ b/src/test/java/janggi/application/JanggiGameServiceTest.java @@ -144,4 +144,29 @@ void play_turn_and_save() { assertThat(gameRepository.updatedGameSnapshot().id()).isEqualTo(1L); assertThat(gameRepository.updatedGameSnapshot().currentTurn()).isEqualTo(Team.HAN); } + + @Test + @DisplayName("새 게임을 생성하고 저장") + void create_and_save_game() { + // given + FakeGameRepository gameRepository = new FakeGameRepository(List.of(),null); + InitialBoardProvider initialBoardProvider = new FakeInitialBoardProvider( + List.of( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ) + ); + JanggiGameService janggiGameService = new JanggiGameService(gameRepository, initialBoardProvider); + + // when + Long gameId = janggiGameService.createGame(); + + // then + assertThat(gameId).isEqualTo(1L); + assertThat(gameRepository.savedGameSnapshot()).isNotNull(); + assertThat(gameRepository.savedGameSnapshot().currentTurn()).isEqualTo(Team.CHO); + assertThat(gameRepository.savedGameSnapshot().finished()).isFalse(); + assertThat(gameRepository.savedGameSnapshot().winner()).isNull(); + assertThat(gameRepository.savedGameSnapshot().positions()).hasSize(2); + } } From 5682555372124cc77ef6b86d66ceef07f915b708 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 22:56:51 +0900 Subject: [PATCH 32/75] =?UTF-8?q?feat:=20=EC=83=88=20=EA=B2=8C=EC=9E=84?= =?UTF-8?q?=EC=9D=84=20=EC=83=9D=EC=84=B1=ED=95=98=EA=B3=A0=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/janggi/application/JanggiGameService.java | 5 +++++ .../java/janggi/application/FakeGameRepository.java | 10 ++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/janggi/application/JanggiGameService.java b/src/main/java/janggi/application/JanggiGameService.java index 9eeaa6d2ad..e02807be57 100644 --- a/src/main/java/janggi/application/JanggiGameService.java +++ b/src/main/java/janggi/application/JanggiGameService.java @@ -46,6 +46,11 @@ public void play(Long gameId, Point from, Point to) { gameRepository.update(toSnapshot(gameId, janggiGame)); } + public Long createGame() { + JanggiGame janggiGame = startNewGame(); + return gameRepository.save(toSnapshot(null, janggiGame)); + } + private GameStatus gameStatus(GameSnapshot gameSnapshot) { if (gameSnapshot.finished()) { return new FinishedGame(gameSnapshot.winner()); diff --git a/src/test/java/janggi/application/FakeGameRepository.java b/src/test/java/janggi/application/FakeGameRepository.java index 42706b144e..8a1257fcc4 100644 --- a/src/test/java/janggi/application/FakeGameRepository.java +++ b/src/test/java/janggi/application/FakeGameRepository.java @@ -10,6 +10,7 @@ public class FakeGameRepository implements GameRepository { private final List gameSummaries; private final GameSnapshot gameSnapshot; private GameSnapshot updatedGameSnapshot; + private GameSnapshot savedGameSnapshot; public FakeGameRepository(List gameSummaries, GameSnapshot gameSnapshot) { this.gameSummaries = gameSummaries; @@ -34,7 +35,8 @@ public Optional findById(Long gameId) { @Override public Long save(GameSnapshot gameSnapshot) { - return gameSnapshot.id(); + this.savedGameSnapshot = gameSnapshot; + return 1L; } @Override @@ -44,5 +46,9 @@ public void update(GameSnapshot gameSnapshot) { public GameSnapshot updatedGameSnapshot() { return updatedGameSnapshot; - }; + } + + public GameSnapshot savedGameSnapshot() { + return savedGameSnapshot; + } } From 1a122f7485bf2401622d02f9fdf1c511492bf1b7 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 23:03:06 +0900 Subject: [PATCH 33/75] =?UTF-8?q?feat:=20=EC=83=81=EC=B0=A8=EB=A6=BC=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../janggi/application/CsvInitialBoardProvider.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/janggi/application/CsvInitialBoardProvider.java diff --git a/src/main/java/janggi/application/CsvInitialBoardProvider.java b/src/main/java/janggi/application/CsvInitialBoardProvider.java new file mode 100644 index 0000000000..d1d00adaad --- /dev/null +++ b/src/main/java/janggi/application/CsvInitialBoardProvider.java @@ -0,0 +1,13 @@ +package janggi.application; + +import janggi.dto.PositionInfo; +import janggi.util.FileParser; +import java.util.List; + +public class CsvInitialBoardProvider implements InitialBoardProvider { + + @Override + public List load() { + return FileParser.readCsvFile("/janggi.csv"); + } +} From 67a37e8e0417e20405031f4470bca6559c2fb965 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 23:27:58 +0900 Subject: [PATCH 34/75] =?UTF-8?q?feat:=20InputView=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/ui/InputView.java | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/java/janggi/ui/InputView.java b/src/main/java/janggi/ui/InputView.java index ada1ce9c78..f407371a6d 100644 --- a/src/main/java/janggi/ui/InputView.java +++ b/src/main/java/janggi/ui/InputView.java @@ -7,15 +7,38 @@ public class InputView { + private static final String NEW_GAME_COMMAND = "1"; + private static final String LOAD_GAME_COMMAND = "2"; + private InputView() { } + public static String readGameCommand() { + System.out.println("장기 게임을 시작합니다."); + System.out.println("새 게임은 1, 이어하기는 2 입력해 주세요 : "); + return Console.readLine().trim(); + } + + public static Long readGameId() { + System.out.println("불러올 게임 id를 입력해 주세요 : "); + return Long.parseLong(Console.readLine().trim()); + } + public static List readPoints() { - return List.of(Parser.parsePoint(readFromPoint()), + return List.of( + Parser.parsePoint(readFromPoint()), Parser.parsePoint(readToPoint()) ); } + public static boolean isNewGameCommand(String command) { + return NEW_GAME_COMMAND.equals(command); + } + + public static boolean isLoadGameCommand(String command) { + return LOAD_GAME_COMMAND.equals(command); + } + private static String readFromPoint() { System.out.println("움직일 기물의 출발지를 입력해 주세요 (예 : 1,2) : "); return Console.readLine(); From cc717573b2dffe9aeee60111bbf9901022b3843d Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 23:28:09 +0900 Subject: [PATCH 35/75] =?UTF-8?q?feat:=20OutputView=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/ui/OutputView.java | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/main/java/janggi/ui/OutputView.java b/src/main/java/janggi/ui/OutputView.java index b52efef781..037f435ccf 100644 --- a/src/main/java/janggi/ui/OutputView.java +++ b/src/main/java/janggi/ui/OutputView.java @@ -1,8 +1,10 @@ package janggi.ui; +import janggi.application.dto.GameSummary; import janggi.domain.status.Team; import janggi.dto.GameStatusInfo; import janggi.dto.PieceInfo; +import java.util.List; public class OutputView { @@ -10,6 +12,17 @@ public class OutputView { private static final String GREEN = "\u001B[32m"; private static final String RED = "\u001B[31m"; + public static void printSavedGames(List gameSummaries) { + System.out.println(); + System.out.println("저장된 게임 목록입니다."); + gameSummaries.forEach(OutputView::printGameSummary); + System.out.println(); + } + + public static void printCurrentTurn(String teamName) { + System.out.println("현재 턴 : " + teamName); + } + public static void printWinner(Team winner) { System.out.println("승자는 " + winner.getName()); } @@ -24,6 +37,19 @@ public static void printGameStatus(GameStatusInfo status) { System.out.println(); } + private static void printGameSummary(GameSummary gameSummary) { + System.out.println( + gameSummary.id() + "번 게임 - " + gameStatus(gameSummary) + ); + } + + private static String gameStatus(GameSummary gameSummary) { + if (gameSummary.finished()) { + return "종료"; + } + return "진행 중"; + } + private static String formatPiece(PieceInfo piece) { if (piece.team() == null) { return piece.name(); From 1f89b60a94d5a6d5eb92c12cec2335eb29a5fc75 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Tue, 31 Mar 2026 23:44:01 +0900 Subject: [PATCH 36/75] =?UTF-8?q?feat:=20=EC=9E=A5=EA=B8=B0=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20DB=20=EC=97=B0=EB=8F=99=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/JanggiApplication.java | 64 +++++++++++++++++---- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/main/java/janggi/JanggiApplication.java b/src/main/java/janggi/JanggiApplication.java index a1e236e5f7..e3c2c1339d 100644 --- a/src/main/java/janggi/JanggiApplication.java +++ b/src/main/java/janggi/JanggiApplication.java @@ -1,28 +1,68 @@ package janggi; -import janggi.domain.Board; +import janggi.application.CsvInitialBoardProvider; +import janggi.application.GameRepository; +import janggi.application.InitialBoardProvider; +import janggi.application.JanggiGameService; +import janggi.application.dto.GameSummary; import janggi.domain.JanggiGame; import janggi.domain.Point; import janggi.dto.GameStatusInfo; -import janggi.dto.PositionInfo; +import janggi.persistence.DatabaseInitializer; +import janggi.persistence.JdbcConnectionManager; +import janggi.persistence.JdbcGameRepository; import janggi.ui.InputView; import janggi.ui.OutputView; -import janggi.util.FileParser; import java.util.List; public class JanggiApplication { + public static void main(String[] args) { - List positionInfos = FileParser.readCsvFile("/janggi.csv"); - Board board = new Board(); - board.init(positionInfos); - JanggiGame game = new JanggiGame(board); - OutputView.printGameStatus(GameStatusInfo.from(game.getBoardStatus())); + JdbcConnectionManager connectionManager = new JdbcConnectionManager( + "jdbc:h2:file:./storage/janggi;AUTO_SERVER=TRUE", + "sa", + "" + ); + DatabaseInitializer databaseInitializer = new DatabaseInitializer(connectionManager); + databaseInitializer.init(); + + GameRepository gameRepository = new JdbcGameRepository(connectionManager); + InitialBoardProvider initialBoardProvider = new CsvInitialBoardProvider(); + JanggiGameService janggiGameService = + new JanggiGameService(gameRepository, initialBoardProvider); + + Long gameId = selectGame(janggiGameService); + playGame(janggiGameService, gameId); + } + + private static Long selectGame(JanggiGameService janggiGameService) { + String command = InputView.readGameCommand(); + if (InputView.isNewGameCommand(command)) { + return janggiGameService.createGame(); + } + if (InputView.isLoadGameCommand(command)) { + return loadGame(janggiGameService); + } + throw new IllegalArgumentException("new 또는 load를 입력해 주세요."); + } + + private static Long loadGame(JanggiGameService janggiGameService) { + List gameSummaries = janggiGameService.findAllGames(); + OutputView.printSavedGames(gameSummaries); + return InputView.readGameId(); + } + + private static void playGame(JanggiGameService janggiGameService, Long gameId) { + JanggiGame janggiGame = janggiGameService.loadGame(gameId); + OutputView.printGameStatus(GameStatusInfo.from(janggiGame.getBoardStatus())); - while (!game.isFinished()) { + while (!janggiGame.isFinished()) { + OutputView.printCurrentTurn(janggiGame.currentTurn().getName()); List points = InputView.readPoints(); - game.play(points.get(0), points.get(1)); - OutputView.printGameStatus(GameStatusInfo.from(game.getBoardStatus())); + janggiGameService.play(gameId, points.get(0), points.get(1)); + janggiGame = janggiGameService.loadGame(gameId); + OutputView.printGameStatus(GameStatusInfo.from(janggiGame.getBoardStatus())); } - OutputView.printWinner(game.getWinner()); + OutputView.printWinner(janggiGame.getWinner()); } } From b7dad1af21b1f665b0bd12dd78af70f3380a7635 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Wed, 1 Apr 2026 00:03:26 +0900 Subject: [PATCH 37/75] =?UTF-8?q?refactor:=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/JanggiApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/janggi/JanggiApplication.java b/src/main/java/janggi/JanggiApplication.java index e3c2c1339d..1406907ac3 100644 --- a/src/main/java/janggi/JanggiApplication.java +++ b/src/main/java/janggi/JanggiApplication.java @@ -43,7 +43,7 @@ private static Long selectGame(JanggiGameService janggiGameService) { if (InputView.isLoadGameCommand(command)) { return loadGame(janggiGameService); } - throw new IllegalArgumentException("new 또는 load를 입력해 주세요."); + throw new IllegalArgumentException("1 또는 2를 입력해 주세요."); } private static Long loadGame(JanggiGameService janggiGameService) { From 348757537e036c52aaef046adfd163ea32e55e2c Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 11:22:01 +0900 Subject: [PATCH 38/75] =?UTF-8?q?refactor:=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EB=AA=85=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/JanggiApplication.java | 16 ++++++++-------- .../{application => }/dto/GameSnapshot.java | 3 +-- .../{application => }/dto/GameSummary.java | 2 +- .../JdbcConnectionManager.java | 2 +- .../JdbcGameRepository.java | 8 ++++---- .../CsvInitialBoardProvider.java | 2 +- .../{application => service}/GameRepository.java | 6 +++--- .../InitialBoardProvider.java | 2 +- .../JanggiGameService.java | 6 +++--- src/main/java/janggi/ui/OutputView.java | 2 +- .../DatabaseInitializer.java | 3 ++- .../DatabaseInitializerTest.java | 3 ++- .../JdbcGameRepositoryTest.java | 11 +++++------ .../FakeGameRepository.java | 6 +++--- .../FakeInitialBoardProvider.java | 2 +- .../JanggiGameServiceTest.java | 6 +++--- 16 files changed, 40 insertions(+), 40 deletions(-) rename src/main/java/janggi/{application => }/dto/GameSnapshot.java (77%) rename src/main/java/janggi/{application => }/dto/GameSummary.java (70%) rename src/main/java/janggi/{persistence => repository}/JdbcConnectionManager.java (94%) rename src/main/java/janggi/{persistence => repository}/JdbcGameRepository.java (98%) rename src/main/java/janggi/{application => service}/CsvInitialBoardProvider.java (90%) rename src/main/java/janggi/{application => service}/GameRepository.java (69%) rename src/main/java/janggi/{application => service}/InitialBoardProvider.java (82%) rename src/main/java/janggi/{application => service}/JanggiGameService.java (95%) rename src/main/java/janggi/{persistence => util}/DatabaseInitializer.java (95%) rename src/test/java/janggi/{persistence => repository}/DatabaseInitializerTest.java (94%) rename src/test/java/janggi/{persistence => repository}/JdbcGameRepositoryTest.java (95%) rename src/test/java/janggi/{application => service}/FakeGameRepository.java (91%) rename src/test/java/janggi/{application => service}/FakeInitialBoardProvider.java (93%) rename src/test/java/janggi/{application => service}/JanggiGameServiceTest.java (98%) diff --git a/src/main/java/janggi/JanggiApplication.java b/src/main/java/janggi/JanggiApplication.java index 1406907ac3..56ee17dd11 100644 --- a/src/main/java/janggi/JanggiApplication.java +++ b/src/main/java/janggi/JanggiApplication.java @@ -1,16 +1,16 @@ package janggi; -import janggi.application.CsvInitialBoardProvider; -import janggi.application.GameRepository; -import janggi.application.InitialBoardProvider; -import janggi.application.JanggiGameService; -import janggi.application.dto.GameSummary; +import janggi.service.CsvInitialBoardProvider; +import janggi.service.GameRepository; +import janggi.service.InitialBoardProvider; +import janggi.service.JanggiGameService; +import janggi.dto.GameSummary; import janggi.domain.JanggiGame; import janggi.domain.Point; import janggi.dto.GameStatusInfo; -import janggi.persistence.DatabaseInitializer; -import janggi.persistence.JdbcConnectionManager; -import janggi.persistence.JdbcGameRepository; +import janggi.util.DatabaseInitializer; +import janggi.repository.JdbcConnectionManager; +import janggi.repository.JdbcGameRepository; import janggi.ui.InputView; import janggi.ui.OutputView; import java.util.List; diff --git a/src/main/java/janggi/application/dto/GameSnapshot.java b/src/main/java/janggi/dto/GameSnapshot.java similarity index 77% rename from src/main/java/janggi/application/dto/GameSnapshot.java rename to src/main/java/janggi/dto/GameSnapshot.java index 1b8e178125..9d3ce86c47 100644 --- a/src/main/java/janggi/application/dto/GameSnapshot.java +++ b/src/main/java/janggi/dto/GameSnapshot.java @@ -1,7 +1,6 @@ -package janggi.application.dto; +package janggi.dto; import janggi.domain.status.Team; -import janggi.dto.PositionInfo; import java.util.List; public record GameSnapshot( diff --git a/src/main/java/janggi/application/dto/GameSummary.java b/src/main/java/janggi/dto/GameSummary.java similarity index 70% rename from src/main/java/janggi/application/dto/GameSummary.java rename to src/main/java/janggi/dto/GameSummary.java index f5953a23bb..0eec6722c8 100644 --- a/src/main/java/janggi/application/dto/GameSummary.java +++ b/src/main/java/janggi/dto/GameSummary.java @@ -1,4 +1,4 @@ -package janggi.application.dto; +package janggi.dto; public record GameSummary( Long id, diff --git a/src/main/java/janggi/persistence/JdbcConnectionManager.java b/src/main/java/janggi/repository/JdbcConnectionManager.java similarity index 94% rename from src/main/java/janggi/persistence/JdbcConnectionManager.java rename to src/main/java/janggi/repository/JdbcConnectionManager.java index 170ee6b00b..e5be9ba232 100644 --- a/src/main/java/janggi/persistence/JdbcConnectionManager.java +++ b/src/main/java/janggi/repository/JdbcConnectionManager.java @@ -1,4 +1,4 @@ -package janggi.persistence; +package janggi.repository; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/main/java/janggi/persistence/JdbcGameRepository.java b/src/main/java/janggi/repository/JdbcGameRepository.java similarity index 98% rename from src/main/java/janggi/persistence/JdbcGameRepository.java rename to src/main/java/janggi/repository/JdbcGameRepository.java index 98f5af9d71..5b599ae529 100644 --- a/src/main/java/janggi/persistence/JdbcGameRepository.java +++ b/src/main/java/janggi/repository/JdbcGameRepository.java @@ -1,8 +1,8 @@ -package janggi.persistence; +package janggi.repository; -import janggi.application.GameRepository; -import janggi.application.dto.GameSnapshot; -import janggi.application.dto.GameSummary; +import janggi.service.GameRepository; +import janggi.dto.GameSnapshot; +import janggi.dto.GameSummary; import janggi.domain.status.Team; import janggi.dto.PositionInfo; import java.sql.Connection; diff --git a/src/main/java/janggi/application/CsvInitialBoardProvider.java b/src/main/java/janggi/service/CsvInitialBoardProvider.java similarity index 90% rename from src/main/java/janggi/application/CsvInitialBoardProvider.java rename to src/main/java/janggi/service/CsvInitialBoardProvider.java index d1d00adaad..3d3c2af294 100644 --- a/src/main/java/janggi/application/CsvInitialBoardProvider.java +++ b/src/main/java/janggi/service/CsvInitialBoardProvider.java @@ -1,4 +1,4 @@ -package janggi.application; +package janggi.service; import janggi.dto.PositionInfo; import janggi.util.FileParser; diff --git a/src/main/java/janggi/application/GameRepository.java b/src/main/java/janggi/service/GameRepository.java similarity index 69% rename from src/main/java/janggi/application/GameRepository.java rename to src/main/java/janggi/service/GameRepository.java index 753f141874..a3a492f5d3 100644 --- a/src/main/java/janggi/application/GameRepository.java +++ b/src/main/java/janggi/service/GameRepository.java @@ -1,7 +1,7 @@ -package janggi.application; +package janggi.service; -import janggi.application.dto.GameSnapshot; -import janggi.application.dto.GameSummary; +import janggi.dto.GameSnapshot; +import janggi.dto.GameSummary; import java.util.List; import java.util.Optional; diff --git a/src/main/java/janggi/application/InitialBoardProvider.java b/src/main/java/janggi/service/InitialBoardProvider.java similarity index 82% rename from src/main/java/janggi/application/InitialBoardProvider.java rename to src/main/java/janggi/service/InitialBoardProvider.java index 394c364507..1e03514dc8 100644 --- a/src/main/java/janggi/application/InitialBoardProvider.java +++ b/src/main/java/janggi/service/InitialBoardProvider.java @@ -1,4 +1,4 @@ -package janggi.application; +package janggi.service; import janggi.dto.PositionInfo; import java.util.List; diff --git a/src/main/java/janggi/application/JanggiGameService.java b/src/main/java/janggi/service/JanggiGameService.java similarity index 95% rename from src/main/java/janggi/application/JanggiGameService.java rename to src/main/java/janggi/service/JanggiGameService.java index e02807be57..5fb7bba0a5 100644 --- a/src/main/java/janggi/application/JanggiGameService.java +++ b/src/main/java/janggi/service/JanggiGameService.java @@ -1,7 +1,7 @@ -package janggi.application; +package janggi.service; -import janggi.application.dto.GameSnapshot; -import janggi.application.dto.GameSummary; +import janggi.dto.GameSnapshot; +import janggi.dto.GameSummary; import janggi.domain.Board; import janggi.domain.JanggiGame; import janggi.domain.Point; diff --git a/src/main/java/janggi/ui/OutputView.java b/src/main/java/janggi/ui/OutputView.java index 037f435ccf..638f939c08 100644 --- a/src/main/java/janggi/ui/OutputView.java +++ b/src/main/java/janggi/ui/OutputView.java @@ -1,6 +1,6 @@ package janggi.ui; -import janggi.application.dto.GameSummary; +import janggi.dto.GameSummary; import janggi.domain.status.Team; import janggi.dto.GameStatusInfo; import janggi.dto.PieceInfo; diff --git a/src/main/java/janggi/persistence/DatabaseInitializer.java b/src/main/java/janggi/util/DatabaseInitializer.java similarity index 95% rename from src/main/java/janggi/persistence/DatabaseInitializer.java rename to src/main/java/janggi/util/DatabaseInitializer.java index 588ed2864d..34d42f6444 100644 --- a/src/main/java/janggi/persistence/DatabaseInitializer.java +++ b/src/main/java/janggi/util/DatabaseInitializer.java @@ -1,5 +1,6 @@ -package janggi.persistence; +package janggi.util; +import janggi.repository.JdbcConnectionManager; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; diff --git a/src/test/java/janggi/persistence/DatabaseInitializerTest.java b/src/test/java/janggi/repository/DatabaseInitializerTest.java similarity index 94% rename from src/test/java/janggi/persistence/DatabaseInitializerTest.java rename to src/test/java/janggi/repository/DatabaseInitializerTest.java index bab0208903..29f31711e8 100644 --- a/src/test/java/janggi/persistence/DatabaseInitializerTest.java +++ b/src/test/java/janggi/repository/DatabaseInitializerTest.java @@ -1,7 +1,8 @@ -package janggi.persistence; +package janggi.repository; import static org.assertj.core.api.Assertions.assertThat; +import janggi.util.DatabaseInitializer; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; diff --git a/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java b/src/test/java/janggi/repository/JdbcGameRepositoryTest.java similarity index 95% rename from src/test/java/janggi/persistence/JdbcGameRepositoryTest.java rename to src/test/java/janggi/repository/JdbcGameRepositoryTest.java index cb63f4e0e9..dc0996a5fd 100644 --- a/src/test/java/janggi/persistence/JdbcGameRepositoryTest.java +++ b/src/test/java/janggi/repository/JdbcGameRepositoryTest.java @@ -1,14 +1,13 @@ -package janggi.persistence; +package janggi.repository; import static org.assertj.core.api.Assertions.assertThat; -import janggi.application.GameRepository; -import janggi.application.dto.GameSnapshot; -import janggi.application.dto.GameSummary; -import janggi.domain.Point; -import janggi.domain.piece.PieceType; +import janggi.service.GameRepository; +import janggi.dto.GameSnapshot; +import janggi.dto.GameSummary; import janggi.domain.status.Team; import janggi.dto.PositionInfo; +import janggi.util.DatabaseInitializer; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/janggi/application/FakeGameRepository.java b/src/test/java/janggi/service/FakeGameRepository.java similarity index 91% rename from src/test/java/janggi/application/FakeGameRepository.java rename to src/test/java/janggi/service/FakeGameRepository.java index 8a1257fcc4..83e41e2624 100644 --- a/src/test/java/janggi/application/FakeGameRepository.java +++ b/src/test/java/janggi/service/FakeGameRepository.java @@ -1,7 +1,7 @@ -package janggi.application; +package janggi.service; -import janggi.application.dto.GameSnapshot; -import janggi.application.dto.GameSummary; +import janggi.dto.GameSnapshot; +import janggi.dto.GameSummary; import java.util.List; import java.util.Optional; diff --git a/src/test/java/janggi/application/FakeInitialBoardProvider.java b/src/test/java/janggi/service/FakeInitialBoardProvider.java similarity index 93% rename from src/test/java/janggi/application/FakeInitialBoardProvider.java rename to src/test/java/janggi/service/FakeInitialBoardProvider.java index c393806c45..cb786552f5 100644 --- a/src/test/java/janggi/application/FakeInitialBoardProvider.java +++ b/src/test/java/janggi/service/FakeInitialBoardProvider.java @@ -1,4 +1,4 @@ -package janggi.application; +package janggi.service; import janggi.dto.PositionInfo; import java.util.List; diff --git a/src/test/java/janggi/application/JanggiGameServiceTest.java b/src/test/java/janggi/service/JanggiGameServiceTest.java similarity index 98% rename from src/test/java/janggi/application/JanggiGameServiceTest.java rename to src/test/java/janggi/service/JanggiGameServiceTest.java index 4dac14fdcf..c4db7b1f12 100644 --- a/src/test/java/janggi/application/JanggiGameServiceTest.java +++ b/src/test/java/janggi/service/JanggiGameServiceTest.java @@ -1,10 +1,10 @@ -package janggi.application; +package janggi.service; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import janggi.application.dto.GameSnapshot; -import janggi.application.dto.GameSummary; +import janggi.dto.GameSnapshot; +import janggi.dto.GameSummary; import janggi.domain.JanggiGame; import janggi.domain.Point; import janggi.domain.status.Team; From c0f023d164a70bd76aad757d56e8dc24dd58d7dd Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 11:22:53 +0900 Subject: [PATCH 39/75] =?UTF-8?q?refactor:=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EB=AA=85=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/JanggiApplication.java | 8 ++++---- .../{service => repository}/CsvInitialBoardProvider.java | 2 +- .../janggi/{service => repository}/GameRepository.java | 2 +- .../{service => repository}/InitialBoardProvider.java | 2 +- src/main/java/janggi/repository/JdbcGameRepository.java | 2 +- src/main/java/janggi/service/JanggiGameService.java | 2 ++ src/main/java/janggi/util/DatabaseInitializer.java | 1 - .../{repository => util}/JdbcConnectionManager.java | 2 +- .../java/janggi/repository/DatabaseInitializerTest.java | 1 + .../java/janggi/repository/JdbcGameRepositoryTest.java | 2 +- src/test/java/janggi/service/FakeGameRepository.java | 1 + .../java/janggi/service/FakeInitialBoardProvider.java | 1 + src/test/java/janggi/service/JanggiGameServiceTest.java | 2 ++ 13 files changed, 17 insertions(+), 11 deletions(-) rename src/main/java/janggi/{service => repository}/CsvInitialBoardProvider.java (91%) rename src/main/java/janggi/{service => repository}/GameRepository.java (92%) rename src/main/java/janggi/{service => repository}/InitialBoardProvider.java (82%) rename src/main/java/janggi/{repository => util}/JdbcConnectionManager.java (95%) diff --git a/src/main/java/janggi/JanggiApplication.java b/src/main/java/janggi/JanggiApplication.java index 56ee17dd11..d898368edb 100644 --- a/src/main/java/janggi/JanggiApplication.java +++ b/src/main/java/janggi/JanggiApplication.java @@ -1,15 +1,15 @@ package janggi; -import janggi.service.CsvInitialBoardProvider; -import janggi.service.GameRepository; -import janggi.service.InitialBoardProvider; +import janggi.repository.CsvInitialBoardProvider; +import janggi.repository.GameRepository; +import janggi.repository.InitialBoardProvider; import janggi.service.JanggiGameService; import janggi.dto.GameSummary; import janggi.domain.JanggiGame; import janggi.domain.Point; import janggi.dto.GameStatusInfo; import janggi.util.DatabaseInitializer; -import janggi.repository.JdbcConnectionManager; +import janggi.util.JdbcConnectionManager; import janggi.repository.JdbcGameRepository; import janggi.ui.InputView; import janggi.ui.OutputView; diff --git a/src/main/java/janggi/service/CsvInitialBoardProvider.java b/src/main/java/janggi/repository/CsvInitialBoardProvider.java similarity index 91% rename from src/main/java/janggi/service/CsvInitialBoardProvider.java rename to src/main/java/janggi/repository/CsvInitialBoardProvider.java index 3d3c2af294..129dfddf1c 100644 --- a/src/main/java/janggi/service/CsvInitialBoardProvider.java +++ b/src/main/java/janggi/repository/CsvInitialBoardProvider.java @@ -1,4 +1,4 @@ -package janggi.service; +package janggi.repository; import janggi.dto.PositionInfo; import janggi.util.FileParser; diff --git a/src/main/java/janggi/service/GameRepository.java b/src/main/java/janggi/repository/GameRepository.java similarity index 92% rename from src/main/java/janggi/service/GameRepository.java rename to src/main/java/janggi/repository/GameRepository.java index a3a492f5d3..9205481edf 100644 --- a/src/main/java/janggi/service/GameRepository.java +++ b/src/main/java/janggi/repository/GameRepository.java @@ -1,4 +1,4 @@ -package janggi.service; +package janggi.repository; import janggi.dto.GameSnapshot; import janggi.dto.GameSummary; diff --git a/src/main/java/janggi/service/InitialBoardProvider.java b/src/main/java/janggi/repository/InitialBoardProvider.java similarity index 82% rename from src/main/java/janggi/service/InitialBoardProvider.java rename to src/main/java/janggi/repository/InitialBoardProvider.java index 1e03514dc8..8abe5faa07 100644 --- a/src/main/java/janggi/service/InitialBoardProvider.java +++ b/src/main/java/janggi/repository/InitialBoardProvider.java @@ -1,4 +1,4 @@ -package janggi.service; +package janggi.repository; import janggi.dto.PositionInfo; import java.util.List; diff --git a/src/main/java/janggi/repository/JdbcGameRepository.java b/src/main/java/janggi/repository/JdbcGameRepository.java index 5b599ae529..9c5848e61e 100644 --- a/src/main/java/janggi/repository/JdbcGameRepository.java +++ b/src/main/java/janggi/repository/JdbcGameRepository.java @@ -1,10 +1,10 @@ package janggi.repository; -import janggi.service.GameRepository; import janggi.dto.GameSnapshot; import janggi.dto.GameSummary; import janggi.domain.status.Team; import janggi.dto.PositionInfo; +import janggi.util.JdbcConnectionManager; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; diff --git a/src/main/java/janggi/service/JanggiGameService.java b/src/main/java/janggi/service/JanggiGameService.java index 5fb7bba0a5..84df81545c 100644 --- a/src/main/java/janggi/service/JanggiGameService.java +++ b/src/main/java/janggi/service/JanggiGameService.java @@ -10,6 +10,8 @@ import janggi.domain.status.GameStatus; import janggi.domain.status.HanTurn; import janggi.domain.status.Team; +import janggi.repository.GameRepository; +import janggi.repository.InitialBoardProvider; import java.util.List; public class JanggiGameService { diff --git a/src/main/java/janggi/util/DatabaseInitializer.java b/src/main/java/janggi/util/DatabaseInitializer.java index 34d42f6444..e2e6d9a423 100644 --- a/src/main/java/janggi/util/DatabaseInitializer.java +++ b/src/main/java/janggi/util/DatabaseInitializer.java @@ -1,6 +1,5 @@ package janggi.util; -import janggi.repository.JdbcConnectionManager; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/janggi/repository/JdbcConnectionManager.java b/src/main/java/janggi/util/JdbcConnectionManager.java similarity index 95% rename from src/main/java/janggi/repository/JdbcConnectionManager.java rename to src/main/java/janggi/util/JdbcConnectionManager.java index e5be9ba232..64c631d9ee 100644 --- a/src/main/java/janggi/repository/JdbcConnectionManager.java +++ b/src/main/java/janggi/util/JdbcConnectionManager.java @@ -1,4 +1,4 @@ -package janggi.repository; +package janggi.util; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/java/janggi/repository/DatabaseInitializerTest.java b/src/test/java/janggi/repository/DatabaseInitializerTest.java index 29f31711e8..7070d71e25 100644 --- a/src/test/java/janggi/repository/DatabaseInitializerTest.java +++ b/src/test/java/janggi/repository/DatabaseInitializerTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import janggi.util.DatabaseInitializer; +import janggi.util.JdbcConnectionManager; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; diff --git a/src/test/java/janggi/repository/JdbcGameRepositoryTest.java b/src/test/java/janggi/repository/JdbcGameRepositoryTest.java index dc0996a5fd..68af92821b 100644 --- a/src/test/java/janggi/repository/JdbcGameRepositoryTest.java +++ b/src/test/java/janggi/repository/JdbcGameRepositoryTest.java @@ -2,12 +2,12 @@ import static org.assertj.core.api.Assertions.assertThat; -import janggi.service.GameRepository; import janggi.dto.GameSnapshot; import janggi.dto.GameSummary; import janggi.domain.status.Team; import janggi.dto.PositionInfo; import janggi.util.DatabaseInitializer; +import janggi.util.JdbcConnectionManager; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/janggi/service/FakeGameRepository.java b/src/test/java/janggi/service/FakeGameRepository.java index 83e41e2624..61ebc05973 100644 --- a/src/test/java/janggi/service/FakeGameRepository.java +++ b/src/test/java/janggi/service/FakeGameRepository.java @@ -2,6 +2,7 @@ import janggi.dto.GameSnapshot; import janggi.dto.GameSummary; +import janggi.repository.GameRepository; import java.util.List; import java.util.Optional; diff --git a/src/test/java/janggi/service/FakeInitialBoardProvider.java b/src/test/java/janggi/service/FakeInitialBoardProvider.java index cb786552f5..cbc97bd962 100644 --- a/src/test/java/janggi/service/FakeInitialBoardProvider.java +++ b/src/test/java/janggi/service/FakeInitialBoardProvider.java @@ -1,6 +1,7 @@ package janggi.service; import janggi.dto.PositionInfo; +import janggi.repository.InitialBoardProvider; import java.util.List; public class FakeInitialBoardProvider implements InitialBoardProvider { diff --git a/src/test/java/janggi/service/JanggiGameServiceTest.java b/src/test/java/janggi/service/JanggiGameServiceTest.java index c4db7b1f12..97bb45d0a2 100644 --- a/src/test/java/janggi/service/JanggiGameServiceTest.java +++ b/src/test/java/janggi/service/JanggiGameServiceTest.java @@ -9,6 +9,8 @@ import janggi.domain.Point; import janggi.domain.status.Team; import janggi.dto.PositionInfo; +import janggi.repository.GameRepository; +import janggi.repository.InitialBoardProvider; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From 88461d4874beb292f4d1e105cadc509237101a84 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 15:41:05 +0900 Subject: [PATCH 40/75] =?UTF-8?q?test:=20=EC=B6=9C=EB=B0=9C=EA=B3=BC=20?= =?UTF-8?q?=EB=8F=84=EC=B0=A9=20=EC=9C=84=EC=B9=98=EA=B0=80=20=EA=B6=81?= =?UTF-8?q?=EC=84=B1=20=EC=95=88=EC=97=90=20=EC=9E=88=EB=8A=94=EC=A7=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/PointTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/janggi/domain/PointTest.java b/src/test/java/janggi/domain/PointTest.java index a9c721a888..5bfd14c86c 100644 --- a/src/test/java/janggi/domain/PointTest.java +++ b/src/test/java/janggi/domain/PointTest.java @@ -35,4 +35,18 @@ void validate_of() { .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("올바르지 않은 위치 범위입니다."); } + + @Test + @DisplayName("from, to의 위치가 궁성 내에 있는지 판단") + void is_in_palace() { + // given + Point from = Point.of(3, 1); + Point to = Point.of(4, 1); + + // when + boolean result = from.isInSamePalace(to); + + // then + assertThat(result).isTrue(); + } } From 0ea1aa86c54c1310420b2c70d42d5f895665d663 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 15:41:50 +0900 Subject: [PATCH 41/75] =?UTF-8?q?feat:=20=EC=B6=9C=EB=B0=9C=EA=B3=BC=20?= =?UTF-8?q?=EB=8F=84=EC=B0=A9=20=EC=9C=84=EC=B9=98=EA=B0=80=20=EA=B6=81?= =?UTF-8?q?=EC=84=B1=20=EC=95=88=EC=97=90=20=EC=9E=88=EB=8A=94=EC=A7=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Point.java | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/main/java/janggi/domain/Point.java b/src/main/java/janggi/domain/Point.java index 9006d9b3b6..c89b5f6baf 100644 --- a/src/main/java/janggi/domain/Point.java +++ b/src/main/java/janggi/domain/Point.java @@ -10,6 +10,15 @@ public class Point { private static final int BOARD_HEIGHT = 10; private static final List> CACHE; + private static final int PALACE_MIN_X = 3; + private static final int PALACE_MAX_X = 5; + + private static final int CHO_PALACE_MIN_Y = 0; + private static final int CHO_PALACE_MAX_Y = 2; + + private static final int HAN_PALACE_MIN_Y = 7; + private static final int HAN_PALACE_MAX_Y = 9; + private final int x; private final int y; @@ -54,6 +63,24 @@ public int getPathY(Point from) { return this.y - from.y; } + public boolean isInSamePalace(Point other) { + return (isInChoPalace() && other.isInChoPalace()) + || (isInHanPalace() && other.isInHanPalace()); + } + + private boolean isInChoPalace() { + return isInRange(CHO_PALACE_MIN_Y, CHO_PALACE_MAX_Y); + } + + private boolean isInHanPalace() { + return isInRange(HAN_PALACE_MIN_Y, HAN_PALACE_MAX_Y); + } + + private boolean isInRange(int minY, int maxY) { + return x >= PALACE_MIN_X && x <= PALACE_MAX_X + && y >= minY && y <= maxY; + } + private static void addX(List row, int y) { for(int i = 0; i < BOARD_WIDTH; i++) { row.add(new Point(i, y)); From e7ab8d205c94a8390be6e729061bdf627b29994f Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 16:05:35 +0900 Subject: [PATCH 42/75] =?UTF-8?q?test:=20=EC=B6=9C=EB=B0=9C=EA=B3=BC=20?= =?UTF-8?q?=EB=8F=84=EC=B0=A9=20=EC=9C=84=EC=B9=98=EA=B0=80=20=EA=B6=81?= =?UTF-8?q?=EC=84=B1=20=EC=95=88=EC=97=90=EC=84=9C=20=EB=8C=80=EA=B0=81?= =?UTF-8?q?=EC=84=A0=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=95=98?= =?UTF-8?q?=EB=8A=94=EC=A7=80=20=ED=8C=90=EB=8B=A8=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/PointTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/janggi/domain/PointTest.java b/src/test/java/janggi/domain/PointTest.java index 5bfd14c86c..a3afe689c5 100644 --- a/src/test/java/janggi/domain/PointTest.java +++ b/src/test/java/janggi/domain/PointTest.java @@ -49,4 +49,18 @@ void is_in_palace() { // then assertThat(result).isTrue(); } + + @Test + @DisplayName("궁성 내에서 대각선으로 이동하는지 판단") + void is_palace_diagonal_move() { + // given + Point from = Point.of(3, 0); + Point to = Point.of(5, 2); + + // when + boolean result = from.isPalaceDiagonalMove(to); + + // then + assertThat(result).isTrue(); + } } From a1134ad7181a4c535abc5cfe9032014869c03d63 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 16:05:44 +0900 Subject: [PATCH 43/75] =?UTF-8?q?feat:=20=EC=B6=9C=EB=B0=9C=EA=B3=BC=20?= =?UTF-8?q?=EB=8F=84=EC=B0=A9=20=EC=9C=84=EC=B9=98=EA=B0=80=20=EA=B6=81?= =?UTF-8?q?=EC=84=B1=20=EC=95=88=EC=97=90=EC=84=9C=20=EB=8C=80=EA=B0=81?= =?UTF-8?q?=EC=84=A0=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=95=98?= =?UTF-8?q?=EB=8A=94=EC=A7=80=20=ED=8C=90=EB=8B=A8=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Point.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/janggi/domain/Point.java b/src/main/java/janggi/domain/Point.java index c89b5f6baf..b7dab9c134 100644 --- a/src/main/java/janggi/domain/Point.java +++ b/src/main/java/janggi/domain/Point.java @@ -1,5 +1,7 @@ package janggi.domain; +import static java.lang.Math.abs; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -63,9 +65,19 @@ public int getPathY(Point from) { return this.y - from.y; } - public boolean isInSamePalace(Point other) { - return (isInChoPalace() && other.isInChoPalace()) - || (isInHanPalace() && other.isInHanPalace()); + public boolean isInSamePalace(Point to) { + return (isInChoPalace() && to.isInChoPalace()) + || (isInHanPalace() && to.isInHanPalace()); + } + + public boolean isPalaceDiagonalMove(Point to) { + if (!isInSamePalace(to)) { + return false; + } + int pathX = abs(to.getPathX(this)); + int pathY = abs(to.getPathY(this)); + return (pathX == 1 && pathY == 1) + || (pathX == 2 && pathY == 2); } private boolean isInChoPalace() { From 911e2be4293d75d1e84a4ac349e46d088a164ee5 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 16:08:20 +0900 Subject: [PATCH 44/75] =?UTF-8?q?test:=20=EA=B6=81=EC=84=B1=20=EC=95=88?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=8C=80=EA=B0=81=EC=84=A0=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/piece/ChaTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/java/janggi/domain/piece/ChaTest.java b/src/test/java/janggi/domain/piece/ChaTest.java index 8b5e7e6da3..2843c87279 100644 --- a/src/test/java/janggi/domain/piece/ChaTest.java +++ b/src/test/java/janggi/domain/piece/ChaTest.java @@ -29,6 +29,21 @@ void straight_back_route(int x, int y, int result) { assertThat(route.size()).isEqualTo(result); } + @Test + @DisplayName("궁성 안에서 대각선으로 이동하는 기능") + void palace_diagonal_move() { + // given + Piece cha = new Cha(Team.CHO); + Point from = Point.of(3, 0); + Point to = Point.of(5, 2); + + // when + List route = cha.getRoute(from, to); + + // then + assertThat(route.size()).isEqualTo(1); + } + @Test @DisplayName("대각선으로 목적지로 할 경우 예외 발생") void destination_exception() { From 382fe5e65cdb9a84333f3b504dbe7a6c43fb7559 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 16:08:28 +0900 Subject: [PATCH 45/75] =?UTF-8?q?feat:=20=EA=B6=81=EC=84=B1=20=EC=95=88?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=8C=80=EA=B0=81=EC=84=A0=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/Cha.java | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index eaf1478ecf..fb75ec2e92 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -15,6 +15,13 @@ public Cha(Team team) { @Override public List getRoute(Point from, Point to) { + if (from.isInSamePalace(to)) { + return getPalaceRoute(from, to); + } + return getNormalRoute(from, to); + } + + private List getNormalRoute(Point from, Point to) { List route = new ArrayList<>(); int pathX = to.getPathX(from); int pathY = to.getPathY(from); @@ -32,6 +39,19 @@ public List getRoute(Point from, Point to) { return route; } + private List getPalaceRoute(Point from, Point to) { + int pathX = abs(to.getPathX(from)); + int pathY = abs(to.getPathY(from)); + + if (pathX != 2 || pathY != 2) { + throw new IllegalArgumentException("궁성 경로가 아닙니다."); + } + + int middleX = (from.getX() + to.getX()) / 2; + int middleY = (from.getY() + to.getY()) / 2; + return List.of((Point.of(middleX, middleY))); + } + private void validateDiagonalMove(int pathX, int pathY) { if (pathX != 0 && pathY != 0) { throw new IllegalArgumentException("대각선 이동은 불가능합니다."); From b4f7691e396bbbf6bf25d331aae3a7b3f2dd0472 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 16:18:48 +0900 Subject: [PATCH 46/75] =?UTF-8?q?test:=20=EA=B6=81=EC=84=B1=20=EB=82=B4?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=9D=B4=EB=8F=99=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=9D=84=20=EC=8B=9C=20=EC=98=88=EC=99=B8=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/piece/JangTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/janggi/domain/piece/JangTest.java b/src/test/java/janggi/domain/piece/JangTest.java index c64b25acf3..1a27fecc52 100644 --- a/src/test/java/janggi/domain/piece/JangTest.java +++ b/src/test/java/janggi/domain/piece/JangTest.java @@ -21,4 +21,18 @@ void destination_exception() { assertThatThrownBy(() -> jang.getRoute(from, to)) .isInstanceOf(IllegalArgumentException.class); } + + @Test + @DisplayName("궁성 내에서 이동하지 않을 시 예외 발생") + void not_in_palace() { + // given + Piece jang = new Jang(Team.CHO); + Point from = Point.of(0,0); + Point to = Point.of(1, 1); + + // when & then + assertThatThrownBy(() -> jang.getRoute(from, to)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("궁성"); + } } From 2a9a889e711617e97abaca2d89b3d8bb18c449a7 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 16:19:06 +0900 Subject: [PATCH 47/75] =?UTF-8?q?feat:=20=EA=B6=81=EC=84=B1=20=EB=82=B4?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=9D=B4=EB=8F=99=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=9D=84=20=EC=8B=9C=20=EC=98=88=EC=99=B8=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/Jang.java | 31 ++++++++++++--------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Jang.java b/src/main/java/janggi/domain/piece/Jang.java index 1ff066b47c..c1198841ec 100644 --- a/src/main/java/janggi/domain/piece/Jang.java +++ b/src/main/java/janggi/domain/piece/Jang.java @@ -16,14 +16,25 @@ public Jang(Team team) { @Override public List getRoute(Point from, Point to) { - int pathX = to.getPathX(from); - int pathY = to.getPathY(from); - int distanceX = abs(pathX); - int distanceY = abs(pathY); + if (!from.isInSamePalace(to)) { + throw new IllegalArgumentException("장은 같은 궁성 안에서만 이동할 수 있습니다."); + } + if (isNormalMove(from, to) || isPalaceDiagonalMove(from, to)) { + return List.of(); + } + throw new IllegalArgumentException("장은 궁성 안에서 한 칸만 이동할 수 있습니다."); + } - validateOverMove(distanceX, distanceY); + private boolean isNormalMove(Point from, Point to) { + int pathX = abs(to.getPathX(from)); + int pathY = abs(to.getPathY(from)); + return pathX + pathY == MAX_DISTANCE; + } - return List.of(to); + private boolean isPalaceDiagonalMove(Point from, Point to) { + int pathX = abs(to.getPathX(from)); + int pathY = abs(to.getPathY(from)); + return pathX == 1 && pathY == 1 && from.isPalaceDiagonalMove(to); } @Override @@ -31,10 +42,4 @@ public boolean canMove(List route) { return route.stream() .noneMatch(piece -> piece.isSameTeam(team)); } - - private void validateOverMove(int distanceX, int distanceY) { - if (distanceX + distanceY != MAX_DISTANCE) { - throw new IllegalArgumentException("한 칸만 이동할 수 있습니다."); - } - } -} +} \ No newline at end of file From 152a82608a7559a77c0f04e5f4f8212c1ae5fc70 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 16:25:54 +0900 Subject: [PATCH 48/75] =?UTF-8?q?test:=20=EC=82=AC=EA=B0=80=20=EA=B6=81?= =?UTF-8?q?=EC=84=B1=20=EB=82=B4=EC=97=90=EC=84=9C=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9D=84=20=EC=8B=9C=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EB=B0=9C=EC=83=9D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/piece/SaTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/janggi/domain/piece/SaTest.java b/src/test/java/janggi/domain/piece/SaTest.java index f00f94c1c2..7683a00974 100644 --- a/src/test/java/janggi/domain/piece/SaTest.java +++ b/src/test/java/janggi/domain/piece/SaTest.java @@ -21,4 +21,18 @@ void destination_exception() { assertThatThrownBy(() -> sa.getRoute(from, to)) .isInstanceOf(IllegalArgumentException.class); } + + @Test + @DisplayName("궁성 내에서 이동하지 않을 시 예외 발생") + void not_in_palace() { + // given + Piece jang = new Jang(Team.CHO); + Point from = Point.of(0,0); + Point to = Point.of(1, 1); + + // when & then + assertThatThrownBy(() -> jang.getRoute(from, to)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("궁성"); + } } From aa8fa6d602b6bd8d53ad3e6b1ebd93b9a62ecc9c Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 16:26:02 +0900 Subject: [PATCH 49/75] =?UTF-8?q?feat:=20=EC=82=AC=EA=B0=80=20=EA=B6=81?= =?UTF-8?q?=EC=84=B1=20=EB=82=B4=EC=97=90=EC=84=9C=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9D=84=20=EC=8B=9C=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EB=B0=9C=EC=83=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/Jang.java | 6 ---- src/main/java/janggi/domain/piece/Sa.java | 36 +++++++++------------ 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Jang.java b/src/main/java/janggi/domain/piece/Jang.java index c1198841ec..cf3ba95372 100644 --- a/src/main/java/janggi/domain/piece/Jang.java +++ b/src/main/java/janggi/domain/piece/Jang.java @@ -36,10 +36,4 @@ private boolean isPalaceDiagonalMove(Point from, Point to) { int pathY = abs(to.getPathY(from)); return pathX == 1 && pathY == 1 && from.isPalaceDiagonalMove(to); } - - @Override - public boolean canMove(List route) { - return route.stream() - .noneMatch(piece -> piece.isSameTeam(team)); - } } \ No newline at end of file diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index 9d46638129..db673dff74 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -14,32 +14,26 @@ public Sa(Team team) { super(team, PieceType.SA); } - @Override - public boolean isSameTeam(Team team) { - return this.team.equals(team); - } - @Override public List getRoute(Point from, Point to) { - int pathX = to.getPathX(from); - int pathY = to.getPathY(from); - int distanceX = abs(pathX); - int distanceY = abs(pathY); - - validateOverMove(distanceX, distanceY); - - return List.of(to); + if (!from.isInSamePalace(to)) { + throw new IllegalArgumentException("사는 같은 궁성 안에서만 이동할 수 있습니다."); + } + if (isNormalMove(from, to) || isPalaceDiagonalMove(from, to)) { + return List.of(); + } + throw new IllegalArgumentException("사는 궁성 안에서 한 칸만 이동할 수 있습니다."); } - @Override - public boolean canMove(List route) { - return route.stream() - .noneMatch(piece -> piece.isSameTeam(team)); + private boolean isNormalMove(Point from, Point to) { + int pathX = abs(to.getPathX(from)); + int pathY = abs(to.getPathY(from)); + return pathX + pathY == MAX_DISTANCE; } - private void validateOverMove(int distanceX, int distanceY) { - if (distanceX + distanceY != MAX_DISTANCE) { - throw new IllegalArgumentException("한 칸만 이동할 수 있습니다."); - } + private boolean isPalaceDiagonalMove(Point from, Point to) { + int pathX = abs(to.getPathX(from)); + int pathY = abs(to.getPathY(from)); + return pathX == 1 && pathY == 1 && from.isPalaceDiagonalMove(to); } } From baf1b2b22f3f33e4afd888169f797b2cff76d9eb Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 16:33:55 +0900 Subject: [PATCH 50/75] =?UTF-8?q?test:=20=EC=A1=B8=EC=9D=98=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EB=8C=80=EA=B0=81=EC=84=A0=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B6=81=EC=84=B1=20=EB=82=B4=EC=9D=98=20=EB=8C=80=EA=B0=81?= =?UTF-8?q?=EC=84=A0=20=EC=9D=B4=EB=8F=99=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/janggi/domain/piece/JolTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/test/java/janggi/domain/piece/JolTest.java b/src/test/java/janggi/domain/piece/JolTest.java index 82696bce45..e56573df81 100644 --- a/src/test/java/janggi/domain/piece/JolTest.java +++ b/src/test/java/janggi/domain/piece/JolTest.java @@ -1,9 +1,11 @@ package janggi.domain.piece; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import janggi.domain.Point; import janggi.domain.status.Team; +import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -50,4 +52,33 @@ void destination_exception() { .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("한 칸만"); } + + @Test + @DisplayName("대각선 이동 시 에외 발생") + void not_diagonal() { + // given + Piece jol = new Jol(Team.CHO); + Point from = Point.of(0,0); + Point to = Point.of(1, 1); + + // when & then + assertThatThrownBy(() -> jol.getRoute(from, to)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("대각선"); + } + + @Test + @DisplayName("궁성 내에서 대각선 이동") + void palace_diagonal_move() { + // given + Piece jol = new Jol(Team.HAN); + Point from = Point.of(3, 2); + Point to = Point.of(4, 1); + + // when + List result = jol.getRoute(from, to); + + // then + assertThat(result).isEmpty(); + } } From cee52efbc2027d71320ea0e480aed2eb58bf74f7 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 16:34:06 +0900 Subject: [PATCH 51/75] =?UTF-8?q?feat:=20=EC=A1=B8=EC=9D=98=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EB=8C=80=EA=B0=81=EC=84=A0=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EB=B0=8F=20=EA=B6=81=EC=84=B1=20=EB=82=B4?= =?UTF-8?q?=EC=9D=98=20=EB=8C=80=EA=B0=81=EC=84=A0=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/Jol.java | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 85353c0aae..08ce40a2a5 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -21,22 +21,23 @@ public List getRoute(Point from, Point to) { int distanceX = abs(pathX); int distanceY = abs(pathY); - validateOverMove(distanceX, distanceY); validateBackMove(pathY); - return List.of(to); + if (isNormalMove(distanceX, distanceY) || isPalaceDiagonalMove(from, to, distanceX, distanceY)) { + return List.of(); + } + + throw new IllegalArgumentException("졸은 앞으로, 좌우 한 칸 또는 궁성 안에서 대각선 한 칸만 이동할 수 있습니다."); } - @Override - public boolean canMove(List route) { - return route.stream() - .noneMatch(piece -> piece.isSameTeam(team)); + private boolean isNormalMove(int distanceX, int distanceY) { + return distanceX + distanceY == MAX_DISTANCE; } - private void validateOverMove(int distanceX, int distanceY) { - if (distanceX + distanceY != MAX_DISTANCE) { - throw new IllegalArgumentException("한 칸만 이동할 수 있습니다."); - } + private boolean isPalaceDiagonalMove(Point from, Point to, int distanceX, int distanceY) { + return distanceX == 1 + && distanceY == 1 + && from.isPalaceDiagonalMove(to); } private void validateBackMove(int pathY) { @@ -44,5 +45,4 @@ private void validateBackMove(int pathY) { throw new IllegalArgumentException("뒤로 이동할 수 없습니다."); } } -} - +} \ No newline at end of file From 7e53d4dafa14a92fa1198d196d8febe056fd2bdc Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 16:43:38 +0900 Subject: [PATCH 52/75] =?UTF-8?q?test:=20=ED=8F=AC=EC=9D=98=20=EA=B6=81?= =?UTF-8?q?=EC=84=B1=20=EB=82=B4=EC=9D=98=20=EB=8C=80=EA=B0=81=EC=84=A0=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EA=B8=B0=EB=8A=A5=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/piece/PhoTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test/java/janggi/domain/piece/PhoTest.java b/src/test/java/janggi/domain/piece/PhoTest.java index 721c782485..6d0c553f3f 100644 --- a/src/test/java/janggi/domain/piece/PhoTest.java +++ b/src/test/java/janggi/domain/piece/PhoTest.java @@ -27,6 +27,8 @@ void setUp() { info.add(PositionInfo.from(List.of("HAN", "CHA", "1", "2"))); info.add(PositionInfo.from(List.of("HAN", "CHA", "1", "3"))); info.add(PositionInfo.from(List.of("CHO", "PHO", "1", "5"))); + info.add(PositionInfo.from(List.of("CHO", "PHO", "3", "0"))); + info.add(PositionInfo.from(List.of("CHO", "JANG", "4", "1"))); info.add(PositionInfo.from(List.of("CHO", "PHO", "1", "6"))); board.init(info); } @@ -107,4 +109,19 @@ void huddle_is_pho_can_not_move() { // then assertThat(pho.canMove(pieces)).isFalse(); } + + @Test + @DisplayName("궁성 안에서 대각선으로 이동하는 기능") + void palace_diagonal_move() { + // given + Piece cha = new Cha(Team.CHO); + Point from = Point.of(3, 0); + Point to = Point.of(5, 2); + + // when + List route = cha.getRoute(from, to); + + // then + assertThat(route.size()).isEqualTo(1); + } } From 27c7674b8f5bb96193604367e9cfd892ced082c3 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 16:43:45 +0900 Subject: [PATCH 53/75] =?UTF-8?q?feat:=20=ED=8F=AC=EC=9D=98=20=EA=B6=81?= =?UTF-8?q?=EC=84=B1=20=EB=82=B4=EC=9D=98=20=EB=8C=80=EA=B0=81=EC=84=A0=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/Pho.java | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/janggi/domain/piece/Pho.java b/src/main/java/janggi/domain/piece/Pho.java index 6e00abd738..4f785a03de 100644 --- a/src/main/java/janggi/domain/piece/Pho.java +++ b/src/main/java/janggi/domain/piece/Pho.java @@ -15,6 +15,13 @@ public Pho(Team team) { @Override public List getRoute(Point from, Point to) { + if (from.isInSamePalace(to)) { + return getPalaceRoute(from, to); + } + return getNormalRoute(from, to); + } + + private List getNormalRoute(Point from, Point to) { List route = new ArrayList<>(); int pathX = to.getPathX(from); int pathY = to.getPathY(from); @@ -32,6 +39,19 @@ public List getRoute(Point from, Point to) { return route; } + private List getPalaceRoute(Point from, Point to) { + int pathX = abs(to.getPathX(from)); + int pathY = abs(to.getPathY(from)); + + if (pathX != 2 || pathY != 2) { + throw new IllegalArgumentException("궁성 경로가 아닙니다."); + } + + int middleX = (from.getX() + to.getX()) / 2; + int middleY = (from.getY() + to.getY()) / 2; + return List.of((Point.of(middleX, middleY))); + } + @Override public boolean canMove(List route) { if (route.size() > 1) { From 3a65a838684d465521cbeaf635446c376b92d0fb Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 17:02:32 +0900 Subject: [PATCH 54/75] =?UTF-8?q?fix:=20=EA=B6=81=EC=84=B1=20=EB=82=B4?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=8C=80=EA=B0=81=EC=84=A0=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=EC=8B=9C=20=EC=98=AC=EB=B0=94=EB=A5=B4=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=9D=80=20=EB=8C=80=EA=B0=81=EC=84=A0=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=EB=8F=84=20=EC=A0=9C=ED=95=9C=20=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=A7=81=EC=A0=91=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Point.java | 28 ++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/janggi/domain/Point.java b/src/main/java/janggi/domain/Point.java index b7dab9c134..fb8a5b31f0 100644 --- a/src/main/java/janggi/domain/Point.java +++ b/src/main/java/janggi/domain/Point.java @@ -74,12 +74,32 @@ public boolean isPalaceDiagonalMove(Point to) { if (!isInSamePalace(to)) { return false; } - int pathX = abs(to.getPathX(this)); - int pathY = abs(to.getPathY(this)); - return (pathX == 1 && pathY == 1) - || (pathX == 2 && pathY == 2); + return isChoPalaceDiagonalPair(to) || isHanPalaceDiagonalPair(to); } + private boolean isChoPalaceDiagonalPair(Point to) { + return isSamePointPair(to, Point.of(3, 0), Point.of(4, 1)) + || isSamePointPair(to, Point.of(4, 1), Point.of(5, 2)) + || isSamePointPair(to, Point.of(5, 0), Point.of(4, 1)) + || isSamePointPair(to, Point.of(4, 1), Point.of(3, 2)) + || isSamePointPair(to, Point.of(3, 0), Point.of(5, 2)) + || isSamePointPair(to, Point.of(5, 0), Point.of(3, 2)); + } + + private boolean isHanPalaceDiagonalPair(Point to) { + return isSamePointPair(to, Point.of(3, 7), Point.of(4, 8)) + || isSamePointPair(to, Point.of(4, 8), Point.of(5, 9)) + || isSamePointPair(to, Point.of(5, 7), Point.of(4, 8)) + || isSamePointPair(to, Point.of(4, 8), Point.of(3, 9)) + || isSamePointPair(to, Point.of(3, 7), Point.of(5, 9)) + || isSamePointPair(to, Point.of(5, 7), Point.of(3, 9)); + } + + private boolean isSamePointPair(Point to, Point first, Point second) { + return (this == first && to == second) || (this == second && to == first); + } + + private boolean isInChoPalace() { return isInRange(CHO_PALACE_MIN_Y, CHO_PALACE_MAX_Y); } From c0a9f97e3ef7adb513fa04af02048c614531966c Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 17:03:02 +0900 Subject: [PATCH 55/75] =?UTF-8?q?test:=20=EA=B0=81=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=9D=98=20=EA=B8=B0=EB=AC=BC=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EB=A5=BC=20=EC=9E=98=EB=AA=BB=20=EC=83=9D=EC=84=B1=ED=95=9C=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/piece/PhoTest.java | 2 +- src/test/java/janggi/domain/piece/SaTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/janggi/domain/piece/PhoTest.java b/src/test/java/janggi/domain/piece/PhoTest.java index 6d0c553f3f..8ce36e0fb2 100644 --- a/src/test/java/janggi/domain/piece/PhoTest.java +++ b/src/test/java/janggi/domain/piece/PhoTest.java @@ -114,7 +114,7 @@ void huddle_is_pho_can_not_move() { @DisplayName("궁성 안에서 대각선으로 이동하는 기능") void palace_diagonal_move() { // given - Piece cha = new Cha(Team.CHO); + Piece cha = new Pho(Team.CHO); Point from = Point.of(3, 0); Point to = Point.of(5, 2); diff --git a/src/test/java/janggi/domain/piece/SaTest.java b/src/test/java/janggi/domain/piece/SaTest.java index 7683a00974..bbb0b55664 100644 --- a/src/test/java/janggi/domain/piece/SaTest.java +++ b/src/test/java/janggi/domain/piece/SaTest.java @@ -26,7 +26,7 @@ void destination_exception() { @DisplayName("궁성 내에서 이동하지 않을 시 예외 발생") void not_in_palace() { // given - Piece jang = new Jang(Team.CHO); + Piece jang = new Sa(Team.CHO); Point from = Point.of(0,0); Point to = Point.of(1, 1); From c26f4ee79eea7350f166c674780df239e4ddb18a Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 17:03:31 +0900 Subject: [PATCH 56/75] =?UTF-8?q?fix:=20=EA=B6=81=EC=84=B1=20=EB=82=B4?= =?UTF-8?q?=EC=97=90=EC=84=9C=202=EC=B9=B8=EB=A7=8C=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/Cha.java | 27 +++++++++++++++------- src/main/java/janggi/domain/piece/Pho.java | 27 +++++++++++++++------- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index fb75ec2e92..f63016c121 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -15,10 +15,19 @@ public Cha(Team team) { @Override public List getRoute(Point from, Point to) { - if (from.isInSamePalace(to)) { + if (isStraightMove(from, to)) { + return getNormalRoute(from, to); + } + if (from.isPalaceDiagonalMove(to)) { return getPalaceRoute(from, to); } - return getNormalRoute(from, to); + throw new IllegalArgumentException("차는 직선 또는 궁성 대각선으로만 이동할 수 있습니다."); + } + + private boolean isStraightMove(Point from, Point to) { + int pathX = to.getPathX(from); + int pathY = to.getPathY(from); + return pathX == 0 || pathY == 0; } private List getNormalRoute(Point from, Point to) { @@ -43,13 +52,15 @@ private List getPalaceRoute(Point from, Point to) { int pathX = abs(to.getPathX(from)); int pathY = abs(to.getPathY(from)); - if (pathX != 2 || pathY != 2) { - throw new IllegalArgumentException("궁성 경로가 아닙니다."); + if (pathX == 1 && pathY == 1) { + return List.of(); } - - int middleX = (from.getX() + to.getX()) / 2; - int middleY = (from.getY() + to.getY()) / 2; - return List.of((Point.of(middleX, middleY))); + if (pathX == 2 && pathY == 2) { + int middleX = (from.getX() + to.getX()) / 2; + int middleY = (from.getY() + to.getY()) / 2; + return List.of(Point.of(middleX, middleY)); + } + throw new IllegalArgumentException("궁성 대각선 경로가 아닙니다."); } private void validateDiagonalMove(int pathX, int pathY) { diff --git a/src/main/java/janggi/domain/piece/Pho.java b/src/main/java/janggi/domain/piece/Pho.java index 4f785a03de..ec8563f2a5 100644 --- a/src/main/java/janggi/domain/piece/Pho.java +++ b/src/main/java/janggi/domain/piece/Pho.java @@ -15,10 +15,19 @@ public Pho(Team team) { @Override public List getRoute(Point from, Point to) { - if (from.isInSamePalace(to)) { + if (isStraightMove(from, to)) { + return getNormalRoute(from, to); + } + if (from.isPalaceDiagonalMove(to)) { return getPalaceRoute(from, to); } - return getNormalRoute(from, to); + throw new IllegalArgumentException("차는 직선 또는 궁성 대각선으로만 이동할 수 있습니다."); + } + + private boolean isStraightMove(Point from, Point to) { + int pathX = to.getPathX(from); + int pathY = to.getPathY(from); + return pathX == 0 || pathY == 0; } private List getNormalRoute(Point from, Point to) { @@ -43,13 +52,15 @@ private List getPalaceRoute(Point from, Point to) { int pathX = abs(to.getPathX(from)); int pathY = abs(to.getPathY(from)); - if (pathX != 2 || pathY != 2) { - throw new IllegalArgumentException("궁성 경로가 아닙니다."); + if (pathX == 1 && pathY == 1) { + return List.of(); } - - int middleX = (from.getX() + to.getX()) / 2; - int middleY = (from.getY() + to.getY()) / 2; - return List.of((Point.of(middleX, middleY))); + if (pathX == 2 && pathY == 2) { + int middleX = (from.getX() + to.getX()) / 2; + int middleY = (from.getY() + to.getY()) / 2; + return List.of(Point.of(middleX, middleY)); + } + throw new IllegalArgumentException("궁성 대각선 경로가 아닙니다."); } @Override From fd2cfc361788f91695465554195e69563b103d98 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 17:04:02 +0900 Subject: [PATCH 57/75] =?UTF-8?q?test:=20=EC=9E=A5=EA=B3=BC=20=EC=A1=B8?= =?UTF-8?q?=EC=9D=98=20=EA=B6=81=EC=84=B1=20=EB=82=B4=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=9E=A1=EB=8A=94=20=EA=B2=83=EC=9D=B4=20=EC=95=84=EB=8B=88?= =?UTF-8?q?=EC=97=AC=EC=84=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/BoardTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java index 27a421eab6..23f1b59930 100644 --- a/src/test/java/janggi/domain/BoardTest.java +++ b/src/test/java/janggi/domain/BoardTest.java @@ -108,15 +108,15 @@ void capturePiece() { Board board = initBoard( PositionInfo.from(Team.CHO, "JANG", 4, 1), PositionInfo.from(Team.HAN, "JANG", 4, 8), - PositionInfo.from(Team.CHO, "JANG", 4, 2), - PositionInfo.from(Team.HAN, "JOL", 4, 3) + PositionInfo.from(Team.CHO, "CHA", 0, 0), + PositionInfo.from(Team.HAN, "JOL", 0, 3) ); - board.move(Point.of(4, 2), Point.of(4, 3), Team.CHO); + board.move(Point.of(0, 0), Point.of(0, 3), Team.CHO); - assertThat(pieceAt(board, 4, 2)).isNull(); - assertThat(pieceAt(board, 4, 3).getType()).isEqualTo(PieceType.JANG); - assertThat(pieceAt(board, 4, 3).isSameTeam(Team.CHO)).isTrue(); + assertThat(pieceAt(board, 0, 0)).isNull(); + assertThat(pieceAt(board, 0, 3).getType()).isEqualTo(PieceType.CHA); + assertThat(pieceAt(board, 0, 3).isSameTeam(Team.CHO)).isTrue(); } @Test From 15e6fb6f57797263e668b57a30819443c812cbc9 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 17:12:25 +0900 Subject: [PATCH 58/75] =?UTF-8?q?style:=20=EA=B0=9C=ED=96=89=20=EC=9D=BC?= =?UTF-8?q?=EA=B4=80=ED=99=94=20=EB=B0=8F=20=EC=97=90=EB=9F=AC=20=EC=A0=91?= =?UTF-8?q?=EB=91=90=EC=82=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/JanggiApplication.java | 2 +- src/main/java/janggi/domain/Board.java | 10 +++++----- src/main/java/janggi/domain/JanggiGame.java | 3 ++- src/main/java/janggi/domain/Point.java | 7 +++---- src/main/java/janggi/domain/piece/Cha.java | 6 +++--- src/main/java/janggi/domain/piece/Jang.java | 6 +++--- src/main/java/janggi/domain/piece/Jol.java | 6 +++--- src/main/java/janggi/domain/piece/Ma.java | 6 +++--- src/main/java/janggi/domain/piece/Pho.java | 6 +++--- src/main/java/janggi/domain/piece/PieceFactory.java | 2 +- src/main/java/janggi/domain/piece/Sa.java | 4 ++-- src/main/java/janggi/domain/piece/Sang.java | 10 +++++----- src/main/java/janggi/domain/status/FinishedGame.java | 2 +- .../java/janggi/repository/JdbcGameRepository.java | 8 ++++---- src/main/java/janggi/service/JanggiGameService.java | 2 +- src/main/java/janggi/util/DatabaseInitializer.java | 4 ++-- src/main/java/janggi/util/FileReader.java | 4 ++-- 17 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/main/java/janggi/JanggiApplication.java b/src/main/java/janggi/JanggiApplication.java index d898368edb..b1416220e9 100644 --- a/src/main/java/janggi/JanggiApplication.java +++ b/src/main/java/janggi/JanggiApplication.java @@ -43,7 +43,7 @@ private static Long selectGame(JanggiGameService janggiGameService) { if (InputView.isLoadGameCommand(command)) { return loadGame(janggiGameService); } - throw new IllegalArgumentException("1 또는 2를 입력해 주세요."); + throw new IllegalArgumentException("[ERROR] 1 또는 2를 입력해 주세요."); } private static Long loadGame(JanggiGameService janggiGameService) { diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 2e7f9d792a..3195361beb 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -31,10 +31,10 @@ public void move(Point from, Point to, Team team) { List route = piece.getRoute(from, to); Piece targetPiece = piecesByPoint.get(to); if (targetPiece != null && !piece.canCapture(targetPiece)) { - throw new IllegalArgumentException("이 기물은 해당 타겟을 잡을 수 없습니다."); + throw new IllegalArgumentException("[ERROR] 이 기물은 해당 타겟을 잡을 수 없습니다."); } if (!piece.canMove(getPieces(route))) { - throw new IllegalArgumentException("해당 기물의 이동 경로에 장애물이 있거나 규칙에 어긋납니다."); + throw new IllegalArgumentException("[ERROR] 해당 기물의 이동 경로에 장애물이 있거나 규칙에 어긋납니다."); } piecesByPoint.remove(from); piecesByPoint.put(to, piece); @@ -75,16 +75,16 @@ public Map getPiecesByPoint() { private void validateFromPoint(Point from, Team team) { if (isEmptyPoint(from)) { - throw new IllegalArgumentException("출발지에 이동할 기물이 없습니다."); + throw new IllegalArgumentException("[ERROR] 출발지에 이동할 기물이 없습니다."); } if (!isSameTeam(from, team)) { - throw new IllegalArgumentException("상대방의 기물은 움직일 수 없습니다."); + throw new IllegalArgumentException("[ERROR] 상대방의 기물은 움직일 수 없습니다."); } } private void validateToPoint(Point to, Team team) { if (!isEmptyPoint(to) && isSameTeam(to, team)) { - throw new IllegalArgumentException("도착지에 본인의 기물이 있습니다."); + throw new IllegalArgumentException("[ERROR] 도착지에 본인의 기물이 있습니다."); } } diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index 0f96f19e25..0e9fa64890 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -28,7 +28,7 @@ public boolean isFinished() { public Team getWinner() { if (!gameStatus.isFinished()) { - throw new RuntimeException("게임이 아직 끝나지 않았습니다."); + throw new RuntimeException("[ERROR] 게임이 아직 끝나지 않았습니다."); } return gameStatus.getTeam(); } @@ -44,6 +44,7 @@ public void play(Point from, Point to) { public Team currentTurn() { return gameStatus.getTeam(); } + public List boardStatus() { return board.getBoardStatus(); } diff --git a/src/main/java/janggi/domain/Point.java b/src/main/java/janggi/domain/Point.java index fb8a5b31f0..f2d6d69e4e 100644 --- a/src/main/java/janggi/domain/Point.java +++ b/src/main/java/janggi/domain/Point.java @@ -26,7 +26,7 @@ public class Point { static { List> points = new ArrayList<>(); - for(int i = 0; i < BOARD_HEIGHT; i++) { + for (int i = 0; i < BOARD_HEIGHT; i++) { List row = new ArrayList<>(); addX(row, i); points.add(row); @@ -99,7 +99,6 @@ private boolean isSamePointPair(Point to, Point first, Point second) { return (this == first && to == second) || (this == second && to == first); } - private boolean isInChoPalace() { return isInRange(CHO_PALACE_MIN_Y, CHO_PALACE_MAX_Y); } @@ -114,14 +113,14 @@ private boolean isInRange(int minY, int maxY) { } private static void addX(List row, int y) { - for(int i = 0; i < BOARD_WIDTH; i++) { + for (int i = 0; i < BOARD_WIDTH; i++) { row.add(new Point(i, y)); } } private static void validateRange(int x, int y) { if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) { - throw new IllegalArgumentException("올바르지 않은 위치 범위입니다."); + throw new IllegalArgumentException("[ERROR] 올바르지 않은 위치 범위입니다."); } } } diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index f63016c121..31415a7b48 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -21,7 +21,7 @@ public List getRoute(Point from, Point to) { if (from.isPalaceDiagonalMove(to)) { return getPalaceRoute(from, to); } - throw new IllegalArgumentException("차는 직선 또는 궁성 대각선으로만 이동할 수 있습니다."); + throw new IllegalArgumentException("[ERROR] 차는 직선 또는 궁성 대각선으로만 이동할 수 있습니다."); } private boolean isStraightMove(Point from, Point to) { @@ -60,12 +60,12 @@ private List getPalaceRoute(Point from, Point to) { int middleY = (from.getY() + to.getY()) / 2; return List.of(Point.of(middleX, middleY)); } - throw new IllegalArgumentException("궁성 대각선 경로가 아닙니다."); + throw new IllegalArgumentException("[ERROR] 궁성 대각선 경로가 아닙니다."); } private void validateDiagonalMove(int pathX, int pathY) { if (pathX != 0 && pathY != 0) { - throw new IllegalArgumentException("대각선 이동은 불가능합니다."); + throw new IllegalArgumentException("[ERROR] 대각선 이동은 불가능합니다."); } } } diff --git a/src/main/java/janggi/domain/piece/Jang.java b/src/main/java/janggi/domain/piece/Jang.java index cf3ba95372..859c952506 100644 --- a/src/main/java/janggi/domain/piece/Jang.java +++ b/src/main/java/janggi/domain/piece/Jang.java @@ -17,12 +17,12 @@ public Jang(Team team) { @Override public List getRoute(Point from, Point to) { if (!from.isInSamePalace(to)) { - throw new IllegalArgumentException("장은 같은 궁성 안에서만 이동할 수 있습니다."); + throw new IllegalArgumentException("[ERROR] 장은 같은 궁성 안에서만 이동할 수 있습니다."); } if (isNormalMove(from, to) || isPalaceDiagonalMove(from, to)) { return List.of(); } - throw new IllegalArgumentException("장은 궁성 안에서 한 칸만 이동할 수 있습니다."); + throw new IllegalArgumentException("[ERROR] 장은 궁성 안에서 한 칸만 이동할 수 있습니다."); } private boolean isNormalMove(Point from, Point to) { @@ -36,4 +36,4 @@ private boolean isPalaceDiagonalMove(Point from, Point to) { int pathY = abs(to.getPathY(from)); return pathX == 1 && pathY == 1 && from.isPalaceDiagonalMove(to); } -} \ No newline at end of file +} diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 08ce40a2a5..1914ea9e97 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -27,7 +27,7 @@ public List getRoute(Point from, Point to) { return List.of(); } - throw new IllegalArgumentException("졸은 앞으로, 좌우 한 칸 또는 궁성 안에서 대각선 한 칸만 이동할 수 있습니다."); + throw new IllegalArgumentException("[ERROR] 졸은 앞으로, 좌우 한 칸 또는 궁성 안에서 대각선 한 칸만 이동할 수 있습니다."); } private boolean isNormalMove(int distanceX, int distanceY) { @@ -42,7 +42,7 @@ private boolean isPalaceDiagonalMove(Point from, Point to, int distanceX, int di private void validateBackMove(int pathY) { if ((team == Team.CHO && pathY < 0) || (team == Team.HAN && pathY > 0)) { - throw new IllegalArgumentException("뒤로 이동할 수 없습니다."); + throw new IllegalArgumentException("[ERROR] 뒤로 이동할 수 없습니다."); } } -} \ No newline at end of file +} diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 3775155e3b..9dca246457 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -31,9 +31,9 @@ public List getRoute(Point from, Point to) { } private void validateMove(int distanceX, int distanceY) { - if (!((distanceX == DISTANCE_MAX && distanceY == DISTANCE_MIN) || - (distanceX == DISTANCE_MIN && distanceY == DISTANCE_MAX))) { - throw new IllegalArgumentException("해당 기물의 이동 경로의 규칙에 어긋납니다."); + if (!((distanceX == DISTANCE_MAX && distanceY == DISTANCE_MIN) + || (distanceX == DISTANCE_MIN && distanceY == DISTANCE_MAX))) { + throw new IllegalArgumentException("[ERROR] 해당 기물의 이동 경로의 규칙에 어긋납니다."); } } } diff --git a/src/main/java/janggi/domain/piece/Pho.java b/src/main/java/janggi/domain/piece/Pho.java index ec8563f2a5..741e2ca510 100644 --- a/src/main/java/janggi/domain/piece/Pho.java +++ b/src/main/java/janggi/domain/piece/Pho.java @@ -21,7 +21,7 @@ public List getRoute(Point from, Point to) { if (from.isPalaceDiagonalMove(to)) { return getPalaceRoute(from, to); } - throw new IllegalArgumentException("차는 직선 또는 궁성 대각선으로만 이동할 수 있습니다."); + throw new IllegalArgumentException("[ERROR] 포는 직선 또는 궁성 대각선으로만 이동할 수 있습니다."); } private boolean isStraightMove(Point from, Point to) { @@ -60,7 +60,7 @@ private List getPalaceRoute(Point from, Point to) { int middleY = (from.getY() + to.getY()) / 2; return List.of(Point.of(middleX, middleY)); } - throw new IllegalArgumentException("궁성 대각선 경로가 아닙니다."); + throw new IllegalArgumentException("[ERROR] 궁성 대각선 경로가 아닙니다."); } @Override @@ -79,7 +79,7 @@ public boolean canCapture(Piece target) { private void validateDiagonalMove(int pathX, int pathY) { if (pathX != 0 && pathY != 0) { - throw new IllegalArgumentException("대각선 이동은 불가능합니다."); + throw new IllegalArgumentException("[ERROR] 대각선 이동은 불가능합니다."); } } } diff --git a/src/main/java/janggi/domain/piece/PieceFactory.java b/src/main/java/janggi/domain/piece/PieceFactory.java index 23fd146b49..587103bc01 100644 --- a/src/main/java/janggi/domain/piece/PieceFactory.java +++ b/src/main/java/janggi/domain/piece/PieceFactory.java @@ -18,7 +18,7 @@ public class PieceFactory { public static Piece initPiece(Team team, PieceType pieceType) { if (!FACTORY.containsKey(pieceType)) { - throw new IllegalArgumentException("존재하지 않는 기물입니다."); + throw new IllegalArgumentException("[ERROR] 존재하지 않는 기물입니다."); } return FACTORY.get(pieceType).apply(team); } diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index db673dff74..d190d3db7f 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -17,12 +17,12 @@ public Sa(Team team) { @Override public List getRoute(Point from, Point to) { if (!from.isInSamePalace(to)) { - throw new IllegalArgumentException("사는 같은 궁성 안에서만 이동할 수 있습니다."); + throw new IllegalArgumentException("[ERROR] 사는 같은 궁성 안에서만 이동할 수 있습니다."); } if (isNormalMove(from, to) || isPalaceDiagonalMove(from, to)) { return List.of(); } - throw new IllegalArgumentException("사는 궁성 안에서 한 칸만 이동할 수 있습니다."); + throw new IllegalArgumentException("[ERROR] 사는 궁성 안에서 한 칸만 이동할 수 있습니다."); } private boolean isNormalMove(Point from, Point to) { diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index 08b35d8733..117c75b1a8 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -29,19 +29,19 @@ public List getRoute(Point from, Point to) { if (distanceX == DISTANCE_MAX) { return List.of( Point.of(from.getX() + (pathX / DISTANCE_MAX), from.getY()), - Point.of(from.getX() + signX * DISTANCE_MIN, from.getY() + signY) + Point.of(from.getX() + signX * DISTANCE_MIN, from.getY() + signY) ); } return List.of( Point.of(from.getX(), from.getY() + (pathY / DISTANCE_MAX)), - Point.of(from.getX() + signX, from.getY() + signY * DISTANCE_MIN) + Point.of(from.getX() + signX, from.getY() + signY * DISTANCE_MIN) ); } private void validateMove(int distanceX, int distanceY) { - if (!((distanceX == DISTANCE_MAX && distanceY == DISTANCE_MIN) || - (distanceX == DISTANCE_MIN && distanceY == DISTANCE_MAX))) { - throw new IllegalArgumentException("해당 기물의 이동 경로의 규칙에 어긋납니다."); + if (!((distanceX == DISTANCE_MAX && distanceY == DISTANCE_MIN) + || (distanceX == DISTANCE_MIN && distanceY == DISTANCE_MAX))) { + throw new IllegalArgumentException("[ERROR] 해당 기물의 이동 경로의 규칙에 어긋납니다."); } } } diff --git a/src/main/java/janggi/domain/status/FinishedGame.java b/src/main/java/janggi/domain/status/FinishedGame.java index a30cbc8d64..35b76713fe 100644 --- a/src/main/java/janggi/domain/status/FinishedGame.java +++ b/src/main/java/janggi/domain/status/FinishedGame.java @@ -23,6 +23,6 @@ public Team getTeam() { @Override public GameStatus move(Point from, Point to, Board board) { - throw new RuntimeException("게임이 종료되었습니다.\n 승자는 "+ winner.name()); + throw new RuntimeException("[ERROR] 게임이 종료되었습니다. 승자는 " + winner.name() + "입니다."); } } diff --git a/src/main/java/janggi/repository/JdbcGameRepository.java b/src/main/java/janggi/repository/JdbcGameRepository.java index 9c5848e61e..facf251ca5 100644 --- a/src/main/java/janggi/repository/JdbcGameRepository.java +++ b/src/main/java/janggi/repository/JdbcGameRepository.java @@ -32,7 +32,7 @@ public List findAll() { ) { return toGameSummaries(resultSet); } catch (SQLException exception) { - throw new RuntimeException(exception); + throw new RuntimeException("[ERROR] 게임 목록 조회 중 데이터베이스 오류가 발생했습니다.", exception); } } @@ -51,7 +51,7 @@ public Optional findById(Long gameId) { } return Optional.of(toGameSnapshot(connection, resultSet)); } catch (SQLException exception) { - throw new RuntimeException(exception); + throw new RuntimeException("[ERROR] 게임 조회 중 데이터베이스 오류가 발생했습니다.", exception); } } @@ -63,7 +63,7 @@ public Long save(GameSnapshot gameSnapshot) { insertPieces(connection, gameId, gameSnapshot.positions()); return gameId; } catch (SQLException exception) { - throw new RuntimeException(exception); + throw new RuntimeException("[ERROR] 게임 저장 중 데이터베이스 오류가 발생했습니다.", exception); } } @@ -74,7 +74,7 @@ public void update(GameSnapshot gameSnapshot) { deletePieces(connection, gameSnapshot.id()); insertPieces(connection, gameSnapshot.id(), gameSnapshot.positions()); } catch (SQLException exception) { - throw new RuntimeException(exception); + throw new RuntimeException("[ERROR] 게임 수정 중 데이터베이스 오류가 발생했습니다.", exception); } } diff --git a/src/main/java/janggi/service/JanggiGameService.java b/src/main/java/janggi/service/JanggiGameService.java index 84df81545c..b6f2dcf299 100644 --- a/src/main/java/janggi/service/JanggiGameService.java +++ b/src/main/java/janggi/service/JanggiGameService.java @@ -36,7 +36,7 @@ public JanggiGame startNewGame() { public JanggiGame loadGame(Long gameId) { GameSnapshot gameSnapshot = gameRepository.findById(gameId) - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 게임입니다.")); + .orElseThrow(() -> new IllegalArgumentException("[ERROR] 존재하지 않는 게임입니다.")); Board board = new Board(); board.init(gameSnapshot.positions()); return new JanggiGame(board, gameStatus(gameSnapshot)); diff --git a/src/main/java/janggi/util/DatabaseInitializer.java b/src/main/java/janggi/util/DatabaseInitializer.java index e2e6d9a423..f3a699b53c 100644 --- a/src/main/java/janggi/util/DatabaseInitializer.java +++ b/src/main/java/janggi/util/DatabaseInitializer.java @@ -24,7 +24,7 @@ public void init() { ) { statement.execute(readSchema()); } catch (SQLException | IOException exception) { - throw new RuntimeException(exception); + throw new RuntimeException("[ERROR] 데이터베이스 초기화 중 오류가 발생했습니다.", exception); } } @@ -38,7 +38,7 @@ private String readSchema() throws IOException { private InputStream schemaStream() { InputStream stream = getClass().getResourceAsStream("/schema.sql"); if (stream == null) { - throw new IllegalArgumentException("schema.sql 파일을 찾을 수 없습니다."); + throw new IllegalArgumentException("[ERROR] schema.sql 파일을 찾을 수 없습니다."); } return stream; } diff --git a/src/main/java/janggi/util/FileReader.java b/src/main/java/janggi/util/FileReader.java index 965397c4ea..65a5a6be62 100644 --- a/src/main/java/janggi/util/FileReader.java +++ b/src/main/java/janggi/util/FileReader.java @@ -16,7 +16,7 @@ public static List readFile(String resourcePath) { try (BufferedReader reader = createReader(resourcePath)) { return readLines(reader); } catch (IOException e) { - throw new IllegalStateException("[ERROR] 파일을 찾을 수 없습니다."); + throw new IllegalStateException("[ERROR] 파일을 읽을 수 없습니다.", e); } } @@ -36,4 +36,4 @@ private static List readLines(BufferedReader reader) throws IOException } return lines; } -} \ No newline at end of file +} From 7b85362279378b99937376c343abe607bd226531 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 20:58:36 +0900 Subject: [PATCH 59/75] =?UTF-8?q?feat:=20=EA=B8=B0=EB=AC=BC=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EB=B3=84=20=EC=A0=90=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/janggi/domain/piece/PieceType.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/janggi/domain/piece/PieceType.java b/src/main/java/janggi/domain/piece/PieceType.java index 0b878803c1..98aa729b26 100644 --- a/src/main/java/janggi/domain/piece/PieceType.java +++ b/src/main/java/janggi/domain/piece/PieceType.java @@ -1,21 +1,27 @@ package janggi.domain.piece; public enum PieceType { - CHA("차"), - PHO("포"), - MA("마"), - SANG("상"), - JANG("장"), - SA("사"), - JOL("졸"); + CHA("차", 3), + PHO("포", 3), + MA("마", 3), + SANG("상", 3), + JANG("장", 5), + SA("사", 1), + JOL("졸", 1); private final String name; + private final int score; - PieceType(String name) { + PieceType(String name, int score) { this.name = name; + this.score = score; } public String getName() { return name; } + + public int getScore() { + return score; + } } From ade537cca5418fc0e84413ab3be9874968409845 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 21:01:31 +0900 Subject: [PATCH 60/75] =?UTF-8?q?test:=20=ED=8C=80=EB=B3=84=20=ED=98=84?= =?UTF-8?q?=EC=9E=AC=20=EA=B8=B0=EB=AC=BC=20=EC=A0=90=EC=88=98=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/BoardTest.java | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java index 23f1b59930..96d298534f 100644 --- a/src/test/java/janggi/domain/BoardTest.java +++ b/src/test/java/janggi/domain/BoardTest.java @@ -157,6 +157,28 @@ void boardStatus() { assertThat(boardStatus.size()).isEqualTo(2); } + @Test + @DisplayName("팀별 현재 기물 점수 계산") + void score_of_team() { + // given + Board board = initBoard( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.CHO, "CHA", 0, 0), + PositionInfo.from(Team.CHO, "JOL", 0, 3), + PositionInfo.from(Team.HAN, "JANG", 4, 8), + PositionInfo.from(Team.HAN, "MA", 1, 9), + PositionInfo.from(Team.HAN, "SA", 3, 9) + ); + + // when + int choScore = board.scoreOf(Team.CHO); + int hanScore = board.scoreOf(Team.HAN); + + // then + assertThat(choScore).isEqualTo(9); + assertThat(hanScore).isEqualTo(9); + } + private Board initBoard(PositionInfo... positionInfos) { Board board = new Board(); board.init(List.of(positionInfos)); From fe56b44dd697cb8adb8ac1987b6e4dd002f85f53 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 21:01:49 +0900 Subject: [PATCH 61/75] =?UTF-8?q?feat:=20=ED=8C=80=EB=B3=84=20=ED=98=84?= =?UTF-8?q?=EC=9E=AC=20=EA=B8=B0=EB=AC=BC=20=EC=A0=90=EC=88=98=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Board.java | 7 +++++-- src/main/java/janggi/domain/JanggiGame.java | 4 ++++ src/main/java/janggi/domain/piece/Piece.java | 4 ++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 3195361beb..eb7f028436 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -69,8 +69,11 @@ public List getBoardStatus() { return PositionInfo.from(piecesByPoint); } - public Map getPiecesByPoint() { - return piecesByPoint; + public int scoreOf(Team team) { + return piecesByPoint.values().stream() + .filter(piece -> piece.isSameTeam(team)) + .mapToInt(Piece::getScore) + .sum(); } private void validateFromPoint(Point from, Team team) { diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index 0e9fa64890..97f3b132f0 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -48,4 +48,8 @@ public Team currentTurn() { public List boardStatus() { return board.getBoardStatus(); } + + public int scoreOf(Team team) { + return board.scoreOf(team); + } } diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index c4802f0d83..cb2ee7a8fb 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -17,4 +17,8 @@ default boolean isSameType(PieceType type) { default boolean canCapture(Piece targetPiece) { return true; } + + default int getScore() { + return getType().getScore(); + } } From ba9322f1bb571ec066c404fa23686792b5348e24 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 21:06:03 +0900 Subject: [PATCH 62/75] =?UTF-8?q?feat:=20=ED=8C=80=EB=B3=84=20=EC=B5=9C?= =?UTF-8?q?=EC=A2=85=20=EA=B2=8C=EC=9E=84=20=EC=A0=90=EC=88=98=20=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/JanggiApplication.java | 2 ++ src/main/java/janggi/ui/OutputView.java | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/main/java/janggi/JanggiApplication.java b/src/main/java/janggi/JanggiApplication.java index b1416220e9..364ef74865 100644 --- a/src/main/java/janggi/JanggiApplication.java +++ b/src/main/java/janggi/JanggiApplication.java @@ -1,5 +1,6 @@ package janggi; +import janggi.domain.status.Team; import janggi.repository.CsvInitialBoardProvider; import janggi.repository.GameRepository; import janggi.repository.InitialBoardProvider; @@ -64,5 +65,6 @@ private static void playGame(JanggiGameService janggiGameService, Long gameId) { OutputView.printGameStatus(GameStatusInfo.from(janggiGame.getBoardStatus())); } OutputView.printWinner(janggiGame.getWinner()); + OutputView.printScore(janggiGame.scoreOf(Team.CHO), janggiGame.scoreOf(Team.HAN)); } } diff --git a/src/main/java/janggi/ui/OutputView.java b/src/main/java/janggi/ui/OutputView.java index 638f939c08..0077b45692 100644 --- a/src/main/java/janggi/ui/OutputView.java +++ b/src/main/java/janggi/ui/OutputView.java @@ -37,6 +37,12 @@ public static void printGameStatus(GameStatusInfo status) { System.out.println(); } + public static void printScore(int choScore, int hanScore) { + System.out.println("최종 점수"); + System.out.println("초 : " + choScore); + System.out.println("한 : " + hanScore); + } + private static void printGameSummary(GameSummary gameSummary) { System.out.println( gameSummary.id() + "번 게임 - " + gameStatus(gameSummary) From 3c14041ebf20f889e54a85592ece41b31c42aeb0 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 21:07:46 +0900 Subject: [PATCH 63/75] =?UTF-8?q?docs:=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=B5=9C=EC=8B=A0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 64 +++++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 3ffffbb2b0..65d2b57927 100644 --- a/README.md +++ b/README.md @@ -49,33 +49,32 @@ ### 📤 출력 (OutputView) #### 게임 진행 상태 출력 -- [ ] 게임 시작 안내 메시지 출력 -- [ ] 매 턴마다 현재 누구의 턴인지 출력 +- [X] 게임 시작 안내 메시지 출력 +- [X] 매 턴마다 현재 누구의 턴인지 출력 - [X] 매 턴마다 이동 결과가 반영된 현재 장기판의 상태를 시각적으로 출력 #### 게임 종료 출력 - [X] 승리 팀 안내 및 게임 종료 메시지 출력 #### 에러 출력 -- [ ] 예외 상황 발생 시 에러 문구 출력 (반드시 [ERROR]로 시작) +- [X] 예외 상황 발생 시 에러 문구 출력 (반드시 [ERROR]로 시작) ## 추가 기능 목록 ### 궁성 규칙 -- [ ] 장과 사는 궁성 안에서만 이동할 수 있다. -- [ ] 장은 궁성 대각선 이동 규칙을 따른다. -- [ ] 사는 궁성 대각선 이동 규칙을 따른다. -- [ ] 차는 궁성 대각선 이동 규칙을 따른다. -- [ ] 포는 궁성 대각선 이동 규칙을 따른다. -- [ ] 졸은 궁성 안에서 대각선 이동 규칙을 따른다. +- [X] 장과 사는 궁성 안에서만 이동할 수 있다. +- [X] 장은 궁성 대각선 이동 규칙을 따른다. +- [X] 사는 궁성 대각선 이동 규칙을 따른다. +- [X] 차는 궁성 대각선 이동 규칙을 따른다. +- [X] 포는 궁성 대각선 이동 규칙을 따른다. +- [X] 졸은 궁성 안에서 대각선 이동 규칙을 따른다. ### 게임 종료 - [X] 상대 왕이 잡히면 게임을 종료한다. - [X] 게임 종료 시 승자를 반환한다. ### 점수 계산 -- [ ] 현재 남아 있는 기물의 점수를 계산한다. -- [ ] 팀별 점수를 계산할 수 있다. -- [ ] 게임 종료 여부와 관계없이 현재 점수를 조회할 수 있다. +- [X] 현재 남아 있는 기물의 점수를 계산한다. +- [X] 팀별 점수를 계산할 수 있다. ### 데이터베이스 초기화 - [X] H2 데이터베이스에 연결한다. @@ -84,30 +83,29 @@ - [X] game_pieces 테이블을 생성한다. ### 게임 저장소 -- [ ] 게임 목록을 조회할 수 있다. -- [ ] 게임 id로 게임을 조회할 수 있다. -- [ ] 새 게임을 저장할 수 있다. -- [ ] 진행 중인 게임 상태를 수정할 수 있다. -- [ ] 게임의 현재 턴 정보를 저장한다. -- [ ] 게임의 종료 여부와 승자를 저장한다. -- [ ] 현재 살아있는 기물의 위치를 저장한다. +- [X] 게임 목록을 조회할 수 있다. +- [X] 게임 id로 게임을 조회할 수 있다. +- [X] 새 게임을 저장할 수 있다. +- [X] 진행 중인 게임 상태를 수정할 수 있다. +- [X] 게임의 현재 턴 정보를 저장한다. +- [X] 게임의 종료 여부와 승자를 저장한다. +- [X] 현재 살아있는 기물의 위치를 저장한다. ### 게임 복구 -- [ ] 저장된 게임 목록을 출력한다. -- [ ] 사용자가 이어서 할 게임을 선택할 수 있다. -- [ ] 저장된 기물 위치로 장기판을 복구한다. -- [ ] 저장된 현재 턴으로 게임을 복구한다. -- [ ] 저장된 종료 상태로 게임을 복구한다. -- [ ] 저장된 게임이 없으면 새 게임을 시작한다. +- [X] 저장된 게임 목록을 출력한다. +- [X] 사용자가 이어서 할 게임을 선택할 수 있다. +- [X] 저장된 기물 위치로 장기판을 복구한다. +- [X] 저장된 현재 턴으로 게임을 복구한다. +- [X] 저장된 종료 상태로 게임을 복구한다. +- [X] 저장된 게임이 없으면 새 게임을 시작한다. ### 게임 진행 흐름 -- [ ] 새 게임을 생성할 수 있다. -- [ ] 수를 둘 때마다 게임 상태를 저장한다. -- [ ] 게임 종료 시 최종 상태를 저장한다. +- [X] 새 게임을 생성할 수 있다. +- [X] 수를 둘 때마다 게임 상태를 저장한다. +- [X] 게임 종료 시 최종 상태를 저장한다. ### 입/출력 -- [ ] 게임 시작 시 새 게임과 이어하기 메뉴를 출력한다. -- [ ] 저장된 게임 목록을 사용자에게 출력한다. -- [ ] 사용자가 선택한 게임을 불러온다. -- [ ] 턴이 넘어갈 때 마다 게임을 그만할지 물어본다. -- [ ] 현재 턴 정보를 출력한다. +- [X] 게임 시작 시 새 게임과 이어하기 메뉴를 출력한다. +- [X] 저장된 게임 목록을 사용자에게 출력한다. +- [X] 사용자가 선택한 게임을 불러온다. +- [X] 현재 턴 정보를 출력한다. From 514531d51a7513e6e52eca98f4201b12f973f859 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 21:12:51 +0900 Subject: [PATCH 64/75] =?UTF-8?q?refactor:=20=EC=9E=85/=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=20=EA=B0=9C=ED=96=89=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=B9=9C?= =?UTF-8?q?=ED=99=94=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/ui/OutputView.java | 28 ++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/main/java/janggi/ui/OutputView.java b/src/main/java/janggi/ui/OutputView.java index 0077b45692..a745a19298 100644 --- a/src/main/java/janggi/ui/OutputView.java +++ b/src/main/java/janggi/ui/OutputView.java @@ -1,9 +1,9 @@ package janggi.ui; -import janggi.dto.GameSummary; -import janggi.domain.status.Team; import janggi.dto.GameStatusInfo; +import janggi.dto.GameSummary; import janggi.dto.PieceInfo; +import janggi.domain.status.Team; import java.util.List; public class OutputView { @@ -29,11 +29,8 @@ public static void printWinner(Team winner) { public static void printGameStatus(GameStatusInfo status) { System.out.println(); - status.pieces().stream() - .map(row -> row.stream() - .map(OutputView::formatPiece) - .toList()) - .forEach(System.out::println); + printHeader(); + printRows(status.pieces()); System.out.println(); } @@ -43,6 +40,23 @@ public static void printScore(int choScore, int hanScore) { System.out.println("한 : " + hanScore); } + private static void printHeader() { + System.out.println(" " + List.of(0, 1, 2, 3, 4, 5, 6, 7, 8)); + } + + private static void printRows(List> pieces) { + for (int y = pieces.size() - 1; y >= 0; y--) { + System.out.println(y + " " + formatRow(pieces.get(y))); + } + } + + private static String formatRow(List row) { + return row.stream() + .map(OutputView::formatPiece) + .toList() + .toString(); + } + private static void printGameSummary(GameSummary gameSummary) { System.out.println( gameSummary.id() + "번 게임 - " + gameStatus(gameSummary) From ae4682d9b9de2bf449cfef26a5e9fcc8bc5f4fda Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 21:26:29 +0900 Subject: [PATCH 65/75] =?UTF-8?q?refactor:=20x,y=20=EB=A1=9C=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B0=8D=20=EC=9D=BC=EA=B4=80=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Board.java | 2 +- src/main/java/janggi/domain/Point.java | 12 ++++++------ src/main/java/janggi/dto/GameStatusInfo.java | 4 ++-- src/main/java/janggi/ui/OutputView.java | 10 +++++----- src/test/java/janggi/domain/PointTest.java | 16 ++++++++-------- src/test/java/janggi/domain/piece/SangTest.java | 4 ++-- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index eb7f028436..838cdaf299 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -50,7 +50,7 @@ public List> getPoints() { List> pieces = new ArrayList<>(); for (int i = 0; i < BOARD_HEIGHT; i++) { pieces.add( - Point.getRow(i).stream() + Point.getPointsAtY(i).stream() .map(piecesByPoint::get) .toList() ); diff --git a/src/main/java/janggi/domain/Point.java b/src/main/java/janggi/domain/Point.java index f2d6d69e4e..5712b51041 100644 --- a/src/main/java/janggi/domain/Point.java +++ b/src/main/java/janggi/domain/Point.java @@ -27,9 +27,9 @@ public class Point { static { List> points = new ArrayList<>(); for (int i = 0; i < BOARD_HEIGHT; i++) { - List row = new ArrayList<>(); - addX(row, i); - points.add(row); + List pointsAtY = new ArrayList<>(); + addPointsInX(pointsAtY, i); + points.add(pointsAtY); } CACHE = Collections.unmodifiableList(points); } @@ -45,7 +45,7 @@ public static Point of(int x, int y) { return CACHE.get(y).get(x); } - public static List getRow(int y) { + public static List getPointsAtY(int y) { return CACHE.get(y); } @@ -112,9 +112,9 @@ private boolean isInRange(int minY, int maxY) { && y >= minY && y <= maxY; } - private static void addX(List row, int y) { + private static void addPointsInX(List pointsAtY, int y) { for (int i = 0; i < BOARD_WIDTH; i++) { - row.add(new Point(i, y)); + pointsAtY.add(new Point(i, y)); } } diff --git a/src/main/java/janggi/dto/GameStatusInfo.java b/src/main/java/janggi/dto/GameStatusInfo.java index e2d74fdda3..43f8a83267 100644 --- a/src/main/java/janggi/dto/GameStatusInfo.java +++ b/src/main/java/janggi/dto/GameStatusInfo.java @@ -17,10 +17,10 @@ public static GameStatusInfo from(List> board) { ); } - private static List toPieceInfos(List row) { + private static List toPieceInfos(List piecesAtY) { List pieceInfos = new ArrayList<>(); - for (Piece piece : row) { + for (Piece piece : piecesAtY) { pieceInfos.add(createPieceInfo(piece)); } return pieceInfos; diff --git a/src/main/java/janggi/ui/OutputView.java b/src/main/java/janggi/ui/OutputView.java index a745a19298..4b8ca44f9d 100644 --- a/src/main/java/janggi/ui/OutputView.java +++ b/src/main/java/janggi/ui/OutputView.java @@ -30,7 +30,7 @@ public static void printWinner(Team winner) { public static void printGameStatus(GameStatusInfo status) { System.out.println(); printHeader(); - printRows(status.pieces()); + printLinesByY(status.pieces()); System.out.println(); } @@ -44,14 +44,14 @@ private static void printHeader() { System.out.println(" " + List.of(0, 1, 2, 3, 4, 5, 6, 7, 8)); } - private static void printRows(List> pieces) { + private static void printLinesByY(List> pieces) { for (int y = pieces.size() - 1; y >= 0; y--) { - System.out.println(y + " " + formatRow(pieces.get(y))); + System.out.println(y + " " + formatLineAtY(pieces.get(y))); } } - private static String formatRow(List row) { - return row.stream() + private static String formatLineAtY(List piecesAtY) { + return piecesAtY.stream() .map(OutputView::formatPiece) .toList() .toString(); diff --git a/src/test/java/janggi/domain/PointTest.java b/src/test/java/janggi/domain/PointTest.java index a3afe689c5..9b976d1202 100644 --- a/src/test/java/janggi/domain/PointTest.java +++ b/src/test/java/janggi/domain/PointTest.java @@ -12,26 +12,26 @@ public class PointTest { @DisplayName("조회 시 해당 포인트가 나온다") void of() { // given - int column = 1; - int row = 2; + int x = 1; + int y = 2; // when - Point point = Point.of(column, row); + Point point = Point.of(x, y); // then - assertThat(point.getX()).isEqualTo(column); - assertThat(point.getY()).isEqualTo(row); + assertThat(point.getX()).isEqualTo(x); + assertThat(point.getY()).isEqualTo(y); } @Test @DisplayName("보드의 범위가 벗어난 곳에서 Point 생성시 예외 발생") void validate_of() { // given - int column = 10; - int row = 10; + int x = 10; + int y = 10; // when & then - assertThatThrownBy(() -> Point.of(column, row)) + assertThatThrownBy(() -> Point.of(x, y)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("올바르지 않은 위치 범위입니다."); } diff --git a/src/test/java/janggi/domain/piece/SangTest.java b/src/test/java/janggi/domain/piece/SangTest.java index 280375c683..37384fb686 100644 --- a/src/test/java/janggi/domain/piece/SangTest.java +++ b/src/test/java/janggi/domain/piece/SangTest.java @@ -16,11 +16,11 @@ public class SangTest { @ParameterizedTest @CsvSource(value = {"8:7", "8:3", "2:7", "2:3", "7:8", "3:8", "7:2", "3:2"}, delimiter = ':') @DisplayName("상이 움직일 때, 경유지는 두 곳이다.") - void straight_back_route(int column, int row) { + void straight_back_route(int x, int y) { // given Piece sang = new Sang(Team.CHO); Point from = Point.of(5,5); - Point to = Point.of(column, row); + Point to = Point.of(x, y); // when List route = sang.getRoute(from, to); From 8a55cc8135b6224425e622b41efe36e178be4b1b Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 21:53:02 +0900 Subject: [PATCH 66/75] =?UTF-8?q?refactor:=20piece=EC=97=90=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=EB=A5=BC=20=EB=8D=98=EC=A0=B8=EC=84=9C=20?= =?UTF-8?q?=ED=8C=80=EC=9D=84=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/BasePiece.java | 5 +++++ src/main/java/janggi/domain/piece/Piece.java | 1 + src/main/java/janggi/dto/GameStatusInfo.java | 9 +-------- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/janggi/domain/piece/BasePiece.java b/src/main/java/janggi/domain/piece/BasePiece.java index d1d85602f8..05a53e9da2 100644 --- a/src/main/java/janggi/domain/piece/BasePiece.java +++ b/src/main/java/janggi/domain/piece/BasePiece.java @@ -27,4 +27,9 @@ public boolean isSameTeam(Team team) { public PieceType getType() { return type; } + + @Override + public Team getTeam() { + return team; + } } diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index cb2ee7a8fb..0699059d8f 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -9,6 +9,7 @@ public interface Piece { boolean canMove(List route); boolean isSameTeam(Team team); PieceType getType(); + Team getTeam(); default boolean isSameType(PieceType type) { return getType() == type; diff --git a/src/main/java/janggi/dto/GameStatusInfo.java b/src/main/java/janggi/dto/GameStatusInfo.java index 43f8a83267..a5a8067d8b 100644 --- a/src/main/java/janggi/dto/GameStatusInfo.java +++ b/src/main/java/janggi/dto/GameStatusInfo.java @@ -30,13 +30,6 @@ private static PieceInfo createPieceInfo(Piece piece) { if (piece == null) { return new PieceInfo("+", null); } - return new PieceInfo(piece.getType().getName(), extractTeam(piece)); - } - - private static Team extractTeam(Piece piece) { - if (piece.isSameTeam(Team.HAN)) { - return Team.HAN; - } - return Team.CHO; + return new PieceInfo(piece.getType().getName(), piece.getTeam()); } } From 2539b16dc327c53c1fdaf548baf369b29a6ec25d Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 21:53:25 +0900 Subject: [PATCH 67/75] =?UTF-8?q?test:=20=ED=94=BD=EC=8A=A4=EC=B3=90=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A5=BC=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=EC=9A=A9=EC=9D=B4=ED=95=98=EA=B2=8C=20=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/dto/PositionInfo.java | 8 -------- .../java/janggi/domain/piece/PhoTest.java | 15 +++++++------- .../janggi/domain/status/ChoTurnTest.java | 9 +++++---- .../janggi/domain/status/HanTurnTest.java | 12 +++++------ .../janggi/fixture/PositionInfoFixture.java | 20 +++++++++++++++++++ 5 files changed, 39 insertions(+), 25 deletions(-) create mode 100644 src/test/java/janggi/fixture/PositionInfoFixture.java diff --git a/src/main/java/janggi/dto/PositionInfo.java b/src/main/java/janggi/dto/PositionInfo.java index d2477b1176..c3a73f533f 100644 --- a/src/main/java/janggi/dto/PositionInfo.java +++ b/src/main/java/janggi/dto/PositionInfo.java @@ -13,14 +13,6 @@ public record PositionInfo( Piece piece, Point point ) { - public static PositionInfo from(List data) { - return from( - Team.valueOf(data.get(0)), - data.get(1), - Integer.parseInt(data.get(2)), - Integer.parseInt(data.get(3)) - ); - } public static PositionInfo from(Team team, String pieceName, int x, int y) { return new PositionInfo( diff --git a/src/test/java/janggi/domain/piece/PhoTest.java b/src/test/java/janggi/domain/piece/PhoTest.java index 8ce36e0fb2..9ce0c2628b 100644 --- a/src/test/java/janggi/domain/piece/PhoTest.java +++ b/src/test/java/janggi/domain/piece/PhoTest.java @@ -7,6 +7,7 @@ import janggi.domain.Point; import janggi.domain.status.Team; import janggi.dto.PositionInfo; +import janggi.fixture.PositionInfoFixture; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -23,13 +24,13 @@ public class PhoTest { void setUp() { board = new Board(); List info = new ArrayList<>(); - info.add(PositionInfo.from(List.of("HAN", "PHO", "1", "1"))); - info.add(PositionInfo.from(List.of("HAN", "CHA", "1", "2"))); - info.add(PositionInfo.from(List.of("HAN", "CHA", "1", "3"))); - info.add(PositionInfo.from(List.of("CHO", "PHO", "1", "5"))); - info.add(PositionInfo.from(List.of("CHO", "PHO", "3", "0"))); - info.add(PositionInfo.from(List.of("CHO", "JANG", "4", "1"))); - info.add(PositionInfo.from(List.of("CHO", "PHO", "1", "6"))); + info.add(PositionInfoFixture.from(List.of("HAN", "PHO", "1", "1"))); + info.add(PositionInfoFixture.from(List.of("HAN", "CHA", "1", "2"))); + info.add(PositionInfoFixture.from(List.of("HAN", "CHA", "1", "3"))); + info.add(PositionInfoFixture.from(List.of("CHO", "PHO", "1", "5"))); + info.add(PositionInfoFixture.from(List.of("CHO", "PHO", "3", "0"))); + info.add(PositionInfoFixture.from(List.of("CHO", "JANG", "4", "1"))); + info.add(PositionInfoFixture.from(List.of("CHO", "PHO", "1", "6"))); board.init(info); } diff --git a/src/test/java/janggi/domain/status/ChoTurnTest.java b/src/test/java/janggi/domain/status/ChoTurnTest.java index d8a3dad9eb..c9cd5ee371 100644 --- a/src/test/java/janggi/domain/status/ChoTurnTest.java +++ b/src/test/java/janggi/domain/status/ChoTurnTest.java @@ -6,6 +6,7 @@ import janggi.domain.Board; import janggi.domain.Point; import janggi.dto.PositionInfo; +import janggi.fixture.PositionInfoFixture; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -20,10 +21,10 @@ public class ChoTurnTest { void setUp() { board = new Board(); List info = new ArrayList<>(); - info.add(PositionInfo.from(List.of("HAN","JANG", "4", "1"))); - info.add(PositionInfo.from(List.of("CHO","JANG", "4", "8"))); - info.add(PositionInfo.from(List.of("HAN", "CHA", "1", "1"))); - info.add(PositionInfo.from(List.of("CHO", "CHA", "2", "3"))); + info.add(PositionInfoFixture.from(List.of("HAN","JANG", "4", "1"))); + info.add(PositionInfoFixture.from(List.of("CHO","JANG", "4", "8"))); + info.add(PositionInfoFixture.from(List.of("HAN", "CHA", "1", "1"))); + info.add(PositionInfoFixture.from(List.of("CHO", "CHA", "2", "3"))); board.init(info); } diff --git a/src/test/java/janggi/domain/status/HanTurnTest.java b/src/test/java/janggi/domain/status/HanTurnTest.java index 0ed00df0fd..c019787bc6 100644 --- a/src/test/java/janggi/domain/status/HanTurnTest.java +++ b/src/test/java/janggi/domain/status/HanTurnTest.java @@ -5,8 +5,8 @@ import janggi.domain.Board; import janggi.domain.Point; -import janggi.domain.piece.Piece; import janggi.dto.PositionInfo; +import janggi.fixture.PositionInfoFixture; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -21,10 +21,10 @@ public class HanTurnTest { void setUp() { board = new Board(); List info = new ArrayList<>(); - info.add(PositionInfo.from(List.of("HAN","JANG", "4", "1"))); - info.add(PositionInfo.from(List.of("CHO","JANG", "4", "8"))); - info.add(PositionInfo.from(List.of("HAN", "CHA", "1", "1"))); - info.add(PositionInfo.from(List.of("CHO", "CHA", "2", "3"))); + info.add(PositionInfoFixture.from(List.of("HAN","JANG", "4", "1"))); + info.add(PositionInfoFixture.from(List.of("CHO","JANG", "4", "8"))); + info.add(PositionInfoFixture.from(List.of("HAN", "CHA", "1", "1"))); + info.add(PositionInfoFixture.from(List.of("CHO", "CHA", "2", "3"))); board.init(info); } @@ -49,7 +49,7 @@ void unavailable_move() { // given Board board = new Board(); List info = new ArrayList<>(); - info.add(PositionInfo.from(List.of("CHO", "CHA", "1", "1"))); + info.add(PositionInfoFixture.from(List.of("CHO", "CHA", "1", "1"))); board.init(info); Point from = Point.of(1, 1); Point to = Point.of(2, 3); diff --git a/src/test/java/janggi/fixture/PositionInfoFixture.java b/src/test/java/janggi/fixture/PositionInfoFixture.java new file mode 100644 index 0000000000..feec5dba68 --- /dev/null +++ b/src/test/java/janggi/fixture/PositionInfoFixture.java @@ -0,0 +1,20 @@ +package janggi.fixture; + +import janggi.domain.status.Team; +import janggi.dto.PositionInfo; +import java.util.List; + +public class PositionInfoFixture { + + private PositionInfoFixture() { + } + + public static PositionInfo from(List data) { + return PositionInfo.from( + Team.valueOf(data.get(0)), + data.get(1), + Integer.parseInt(data.get(2)), + Integer.parseInt(data.get(3)) + ); + } +} From ce01ab8aa6e29abfec85e57e48958f70f3a6b026 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Thu, 2 Apr 2026 22:19:29 +0900 Subject: [PATCH 68/75] =?UTF-8?q?refactor:=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Board.java | 10 +++++----- src/main/java/janggi/domain/JanggiGame.java | 2 +- src/main/java/janggi/domain/status/FinishedGame.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 838cdaf299..085898cfc7 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -31,10 +31,10 @@ public void move(Point from, Point to, Team team) { List route = piece.getRoute(from, to); Piece targetPiece = piecesByPoint.get(to); if (targetPiece != null && !piece.canCapture(targetPiece)) { - throw new IllegalArgumentException("[ERROR] 이 기물은 해당 타겟을 잡을 수 없습니다."); + throw new IllegalStateException("[ERROR] 이 기물은 해당 타겟을 잡을 수 없습니다."); } if (!piece.canMove(getPieces(route))) { - throw new IllegalArgumentException("[ERROR] 해당 기물의 이동 경로에 장애물이 있거나 규칙에 어긋납니다."); + throw new IllegalStateException("[ERROR] 해당 기물의 이동 경로에 장애물이 있거나 규칙에 어긋납니다."); } piecesByPoint.remove(from); piecesByPoint.put(to, piece); @@ -78,16 +78,16 @@ public int scoreOf(Team team) { private void validateFromPoint(Point from, Team team) { if (isEmptyPoint(from)) { - throw new IllegalArgumentException("[ERROR] 출발지에 이동할 기물이 없습니다."); + throw new IllegalStateException("[ERROR] 출발지에 이동할 기물이 없습니다."); } if (!isSameTeam(from, team)) { - throw new IllegalArgumentException("[ERROR] 상대방의 기물은 움직일 수 없습니다."); + throw new IllegalStateException("[ERROR] 상대방의 기물은 움직일 수 없습니다."); } } private void validateToPoint(Point to, Team team) { if (!isEmptyPoint(to) && isSameTeam(to, team)) { - throw new IllegalArgumentException("[ERROR] 도착지에 본인의 기물이 있습니다."); + throw new IllegalStateException("[ERROR] 도착지에 본인의 기물이 있습니다."); } } diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index 97f3b132f0..5c53de6e8f 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -28,7 +28,7 @@ public boolean isFinished() { public Team getWinner() { if (!gameStatus.isFinished()) { - throw new RuntimeException("[ERROR] 게임이 아직 끝나지 않았습니다."); + throw new IllegalStateException("[ERROR] 게임이 아직 끝나지 않았습니다."); } return gameStatus.getTeam(); } diff --git a/src/main/java/janggi/domain/status/FinishedGame.java b/src/main/java/janggi/domain/status/FinishedGame.java index 35b76713fe..296392feb8 100644 --- a/src/main/java/janggi/domain/status/FinishedGame.java +++ b/src/main/java/janggi/domain/status/FinishedGame.java @@ -23,6 +23,6 @@ public Team getTeam() { @Override public GameStatus move(Point from, Point to, Board board) { - throw new RuntimeException("[ERROR] 게임이 종료되었습니다. 승자는 " + winner.name() + "입니다."); + throw new IllegalStateException("[ERROR] 게임이 종료되었습니다. 승자는 " + winner.name() + "입니다."); } } From 7bfda5c1ccff0bdd33f89257c18074256624b9a5 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Sat, 4 Apr 2026 12:23:49 +0900 Subject: [PATCH 69/75] =?UTF-8?q?test:=20=ED=91=9C=EC=A4=80=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/BoardTest.java | 10 +- .../janggi/domain/status/ChoTurnTest.java | 2 +- .../janggi/domain/status/HanTurnTest.java | 2 +- storage/janggi.mv.db | Bin 0 -> 36864 bytes storage/janggi.trace.db | 114 ++++++++++++++++++ 5 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 storage/janggi.mv.db create mode 100644 storage/janggi.trace.db diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java index 96d298534f..8f68146f23 100644 --- a/src/test/java/janggi/domain/BoardTest.java +++ b/src/test/java/janggi/domain/BoardTest.java @@ -22,7 +22,7 @@ void moveFromEmptyPoint() { ); assertThatThrownBy(() -> board.move(Point.of(0, 0), Point.of(0, 1), Team.CHO)) - .isInstanceOf(IllegalArgumentException.class) + .isInstanceOf(IllegalStateException.class) .hasMessageContaining("출발지에 이동할 기물이 없습니다."); } @@ -36,7 +36,7 @@ void moveOtherTeamPiece() { ); assertThatThrownBy(() -> board.move(Point.of(0, 0), Point.of(0, 1), Team.CHO)) - .isInstanceOf(IllegalArgumentException.class) + .isInstanceOf(IllegalStateException.class) .hasMessageContaining("상대방의 기물은 움직일 수 없습니다."); } @@ -51,7 +51,7 @@ void moveToSameTeamPiece() { ); assertThatThrownBy(() -> board.move(Point.of(0, 0), Point.of(0, 1), Team.CHO)) - .isInstanceOf(IllegalArgumentException.class) + .isInstanceOf(IllegalStateException.class) .hasMessageContaining("도착지에 본인의 기물이 있습니다."); } @@ -66,7 +66,7 @@ void moveBlockedRoute() { ); assertThatThrownBy(() -> board.move(Point.of(0, 0), Point.of(0, 3), Team.CHO)) - .isInstanceOf(IllegalArgumentException.class) + .isInstanceOf(IllegalStateException.class) .hasMessageContaining("이동 경로에 장애물이 있거나 규칙에 어긋납니다."); } @@ -82,7 +82,7 @@ void phoCanNotCapturePho() { ); assertThatThrownBy(() -> board.move(Point.of(1, 2), Point.of(1, 6), Team.CHO)) - .isInstanceOf(IllegalArgumentException.class) + .isInstanceOf(IllegalStateException.class) .hasMessageContaining("해당 타겟을 잡을 수 없습니다."); } diff --git a/src/test/java/janggi/domain/status/ChoTurnTest.java b/src/test/java/janggi/domain/status/ChoTurnTest.java index c9cd5ee371..125308025e 100644 --- a/src/test/java/janggi/domain/status/ChoTurnTest.java +++ b/src/test/java/janggi/domain/status/ChoTurnTest.java @@ -55,6 +55,6 @@ void unavailable_move() { //then assertThatThrownBy(() -> status.move(from, to, board)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalStateException.class); } } diff --git a/src/test/java/janggi/domain/status/HanTurnTest.java b/src/test/java/janggi/domain/status/HanTurnTest.java index c019787bc6..4eebbdc20b 100644 --- a/src/test/java/janggi/domain/status/HanTurnTest.java +++ b/src/test/java/janggi/domain/status/HanTurnTest.java @@ -59,6 +59,6 @@ void unavailable_move() { //then assertThatThrownBy(() -> status.move(from, to, board)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalStateException.class); } } diff --git a/storage/janggi.mv.db b/storage/janggi.mv.db new file mode 100644 index 0000000000000000000000000000000000000000..aa552211a2a84a31729f3ffb90ec7322b4ff8d40 GIT binary patch literal 36864 zcmeHQOKcm*8Q!J6HX}<(ouq()0>O%sDAA^woqce3={&TOD2a(gxg_OS?SWWwmyQuh zG$h)!)1uA8c{lI(J7^C@(L+(7mjXTX5cHgLky8&nv}ln64N{~{(SK(4#fNMvkz+d= z9=Q8+XJ`KT=bzDh`+xguJ}0MZtIhfYIfEX{ryq24NRp&<{r#~3!*U7iDZ{1#VGu0t-IKA$;=Q9Wm5^1$ro3UH= z0bf|O&jjZsxAATAd-IIDUfHnMH|=^0*bkfZ)|Opcbu&^_P@;l<6S=wNuANzE)}Z1W ziF|h5X)ZKRWl*~)6D4z1(qgW&#U#Ep=e3xAz`c+&8n&#-lA1neujVvEQff*Ajgex| zCups^R6|vDNi_|TlnV6l7#}BmYDc}%UO?_HkfQ1YJ?m~uGwrM`>7hnoOj63VYpx{g zRBy#`ROm#FcF!A&tt-n$te|vi)#EnLD$(2Y%ipHRwXCZticVVd-KF<^HN$pg%^`!} z8g9dBWHnyq9p2=#yumBH#@F~dug9|WSk?jGewX_r&kb-lkSUo#juf1s;7s1X=C2X9?$plITPkR{%sM?J68a* z0N@oM8(9JBTKeqz*-c;u`3nLStybVUjvQGeB(gf_W;Wfd*Uz4@*XtK@lJb6_C<-V^ zcu|tQMX5%VqKeu?QKTrLrU-!m^FOj&saRW8Ry1fZSgwGx)U6$gXorY)NY>lbgNi~+ z`kbluRTD)Nq?S}JVM&9SQm(bHrSkSH3F|Pdvx+K3Es1zE=&#kdeh`QyBoI_zfc_!F z7i2f9N}=FLiZ3Xxt0MSgG|6#CZl-^f{(SR7dH^r$iP=K2P@d1vB*dwurG>mz6pKq0 zvADe87r3ubEapoIaeCe=S<{t#NjzbdRtm+r2_&TkQlsL?(2u4|d8?8a%lYpv=Zn*M zF>!o(YN0Tl$Rx@uR+j9Q^=Z~mZYj|P03YNK}yxKOjOmCDP+v3%Cw<@D&>k* zs)+X$D)S-|OZkYXh*SB6rQ%$;=TEmhz{PbRrs~4 zCd8#;%x!*KM}pnANJpT_Fap#z=8slpuAWG)Y51Vb<*POXSD z`B`gup(0vku`rV_RtlAseul=%DzgX~Mpe-)7#ee0t{SSTS7$7kl}Me<&dxxC9o~WM zSB)srak-MW7PoMn&YDQys+E=F`95zrSv_GbEJJB9KzW~*T@$sJ?xp-} zzJ%Aga%4tF7ZUJ9n8VZe!dm;CfpY9XjN@Y*nwz(Zlbk#^J--AiF~>~|EbbYM@f_Eh z;r2k77!f35l{s8NQb2)_ zNJ78=GxR?J`YkJ}memb2ckGTk@49>PVg3=G6Xc-iXi`uDA(<4^K$u7h8WphS<3ctN z#>WMn2=k%_`eSQYv15)+@t(O{xSY>{t5m`{we-x z{u%zK{ImRX{PX+^{EPfc{LB0+{Hy$H{OkN1{G0q+{M-CH{JZ>n{QLX|V53KAoxV$4 z^qMGJ;0KRIu7M*w9yAZ0@B|e+S9sDZ>G{G_-bLyRPlpnC!!w>l-QlO91pe@BD1k#f z7fRp}&xaDY#0wz7Ou;^pND4285{&dqp#($yawx%Azv4-BuwM-&812`nsI}l*Z-w6x)dvkv$8M5e=kVS91hVGb}x^r0CuW7(4jcjh@A*9TFvkss7%4`Vi5|Q++NTG;II$Bou1I7(UA1F0%a8s zJE?603aSSnj}9(=xZ{I73{%>X(^e09x{*`##SevyIz07>q7gZSO{%Ax)}~uGY{HJ` z>(-%}MgeGt3%c#F>9TgDkEaxP?hpZ>C6vO`v|C5I{)&5X!hsXKI$$t>hahc(N@(hdNfBM9% zx(o6DeOu%I0>%GlJ^T+xg8%Wc4gddhm*W4=cQ5|`gyH||4*&c3JH&c=i2ohi&|P#z z@jr!RA=1HdK!njv8!n_!|9=vDii_RE#Zj%+K!z*D zWd%aUhHb|E&}`ff9LN30ciay+#|3^(pHH2kE+iTfej zxF3~{`!Vu(BkR;`t*(zWH%?{lmosbUHe1aNH{;YY#j~rcC+yX;E@lo?E}U`uDUB%)=?N5Ej54OH|Lkyw;%8l`CKIh_6oH5PIX(*mCV$I zmTPTn*cW`YJ4R-N44cJt>1vLFgYyA)`}3 zvH&WEyWy_a-BIjwNBg;D5eNyS^tRl%ADeRAzAyItmKO#g1hQwZuy22y;s3s(u?+vS z_Em6G8$ehe?|DiCzhELIybltl=>UgdSok39k2nVK>b4s<_7Cwq$Ki*5pMFS=d-pzh zXu`yd&0{y3@u5T9FiM~iG>XPh5{;t?ltPEm5tK$pQ3l^zh-SbD#dmT) zJox(?h9)=@<lrXdjQ7+cpX6I00sxpHh`-E z1Px$i02KrH7eKlIrUlR|fKvfP3LsAaV*+>*z>NS#1n?lB@6coDCUg#3j=gishXYX( zbk6|)t_$WHZ2|LZE0}M#>9)NP^c4)|cX6QUv#YNXf_?~xpn+jE$?@Zdz*qmk#r`r0 z52fJr@9>3>n2Lu_F%=IVH$Q@}Np8AHNhVuWDEijs??>bHJ z_see`IuPZygefb+L>2n>xh-LS1wZ%0Rzq|KSc{ok-9yi%T|d~ zwq;e_PpZt*@|LPTu|97&u@$S1T{~siY z#-H}&Yo-*<2WHanze%^MlC)#de^JEm|5swe9Cskh|JM^j1zG=$T?M9S#y_9J5zUs) zz!dyzh8%LD5Vbd$3R3_YJyQVIBY6~SAxXxEsNESeD6s0mnCS-4Mg%|`GzB2gXQEh7 zk_`M>gIDKHp*>igj@)|9zW>kS|J%>T&f@ Date: Sat, 4 Apr 2026 12:45:40 +0900 Subject: [PATCH 70/75] =?UTF-8?q?test:=20=EC=A0=80=EC=9E=A5=EB=90=9C=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=EC=9D=B4=20=EC=97=86=EC=9D=84=20=EC=8B=9C=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EB=B0=9C=EC=83=9D=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/janggi/service/JanggiGameServiceTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/janggi/service/JanggiGameServiceTest.java b/src/test/java/janggi/service/JanggiGameServiceTest.java index 97bb45d0a2..e981cc1cef 100644 --- a/src/test/java/janggi/service/JanggiGameServiceTest.java +++ b/src/test/java/janggi/service/JanggiGameServiceTest.java @@ -171,4 +171,18 @@ void create_and_save_game() { assertThat(gameRepository.savedGameSnapshot().winner()).isNull(); assertThat(gameRepository.savedGameSnapshot().positions()).hasSize(2); } + + @Test + @DisplayName("저장된 게임 목록이 없을 때 예외 발생") + void no_saved_games_error() { + // given + FakeGameRepository gameRepository = new FakeGameRepository(List.of(),null); + InitialBoardProvider initialBoardProvider = new FakeInitialBoardProvider(List.of()); + JanggiGameService janggiGameService = new JanggiGameService(gameRepository, initialBoardProvider); + + // when & then + assertThatThrownBy(janggiGameService::findAllGames) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("저장된"); + } } From ca81857eb11f88435964ec6daafafac90f7f88b2 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Sat, 4 Apr 2026 12:45:48 +0900 Subject: [PATCH 71/75] =?UTF-8?q?feat:=20=EC=A0=80=EC=9E=A5=EB=90=9C=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=EC=9D=B4=20=EC=97=86=EC=9D=84=20=EC=8B=9C=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EB=B0=9C=EC=83=9D=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/service/JanggiGameService.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/janggi/service/JanggiGameService.java b/src/main/java/janggi/service/JanggiGameService.java index b6f2dcf299..daf4306a65 100644 --- a/src/main/java/janggi/service/JanggiGameService.java +++ b/src/main/java/janggi/service/JanggiGameService.java @@ -25,7 +25,7 @@ public JanggiGameService(GameRepository gameRepository, InitialBoardProvider ini } public List findAllGames() { - return gameRepository.findAll(); + return validateGameList(gameRepository.findAll()); } public JanggiGame startNewGame() { @@ -79,4 +79,11 @@ private Team winner(JanggiGame janggiGame) { } return janggiGame.getWinner(); } + + private List validateGameList(List gameSummaries) { + if (gameSummaries.isEmpty()) { + throw new IllegalStateException("[ERROR] 저장된 게임이 없습니다."); + } + return gameSummaries; + } } From 564d2ae7d97e74738bd5fe8e21378529722af31c Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Sat, 4 Apr 2026 13:10:40 +0900 Subject: [PATCH 72/75] =?UTF-8?q?test:=20=EB=B3=B4=EB=93=9C=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=89=20=EC=BB=AC=EB=A0=89=EC=85=98=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 팀의 왕 상태 테스트 추가 - 보드 상태 테스트 추가 - 팀별 기물 점수 테스트 추가 - 보드 현재 기물 배치 조회 --- src/test/java/janggi/domain/BoardsTest.java | 94 +++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/test/java/janggi/domain/BoardsTest.java diff --git a/src/test/java/janggi/domain/BoardsTest.java b/src/test/java/janggi/domain/BoardsTest.java new file mode 100644 index 0000000000..b0c2226fa4 --- /dev/null +++ b/src/test/java/janggi/domain/BoardsTest.java @@ -0,0 +1,94 @@ +package janggi.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import janggi.domain.piece.Piece; +import janggi.domain.piece.PieceType; +import janggi.domain.status.Team; +import janggi.dto.PositionInfo; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class BoardsTest { + + @Test + @DisplayName("팀의 왕이 살아있으면 죽지 않은 상태다") + void kingAlive() { + Boards boards = initBoards( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ); + + assertThat(boards.isKingDie(Team.CHO)).isFalse(); + assertThat(boards.isKingDie(Team.HAN)).isFalse(); + } + + @Test + @DisplayName("팀의 왕이 없으면 죽은 상태다") + void kingDie() { + Boards boards = initBoards( + PositionInfo.from(Team.CHO, "JANG", 4, 1) + ); + + assertThat(boards.isKingDie(Team.HAN)).isTrue(); + } + + @Test + @DisplayName("보드의 현재 상태를 저장용 목록으로 반환한다") + void boardStatus() { + Boards boards = initBoards( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ); + + List boardStatus = boards.getBoardStatus(); + + assertThat(boardStatus).hasSize(2); + assertThat(boardStatus.get(0).point()).isEqualTo(Point.of(4, 1)); + assertThat(boardStatus.get(1).point()).isEqualTo(Point.of(4, 8)); + } + + @Test + @DisplayName("팀별 현재 기물 점수를 계산한다") + void scoreOfTeam() { + Boards boards = initBoards( + PositionInfo.from(Team.CHO, "JANG", 4, 1), + PositionInfo.from(Team.CHO, "CHA", 0, 0), + PositionInfo.from(Team.CHO, "JOL", 0, 3), + PositionInfo.from(Team.HAN, "JANG", 4, 8), + PositionInfo.from(Team.HAN, "MA", 1, 9), + PositionInfo.from(Team.HAN, "SA", 3, 9) + ); + + int choScore = boards.scoreOf(Team.CHO); + int hanScore = boards.scoreOf(Team.HAN); + + assertThat(choScore).isEqualTo(9); + assertThat(hanScore).isEqualTo(9); + } + + @Test + @DisplayName("보드 현재 기물 배치를 조회한다") + void getPoints() { + Boards boards = initBoards( + PositionInfo.from(Team.CHO, "CHA", 0, 0), + PositionInfo.from(Team.HAN, "JANG", 4, 8) + ); + + List> points = boards.getPoints(); + + assertThat(points).hasSize(10); + assertThat(points.get(0)).hasSize(9); + assertThat(points.get(0).get(0).getType()).isEqualTo(PieceType.CHA); + assertThat(points.get(8).get(4).getType()).isEqualTo(PieceType.JANG); + assertThat(points.get(1).get(0)).isNull(); + } + + private Boards initBoards(PositionInfo... positionInfos) { + Board board = new Board(); + board.init(List.of(positionInfos)); + return new Boards(board); + } +} From 8c4bfbeb8b536cda2cfb1c41e1a915d7db07b3be Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Sat, 4 Apr 2026 13:11:30 +0900 Subject: [PATCH 73/75] =?UTF-8?q?feat:=20=EB=B3=B4=EB=93=9C=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=89=20=EC=BB=AC=EB=A0=89=EC=85=98=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 팀의 왕 상태 기능 구현 - 보드 상태 기능 구현 - 팀별 기물 점수 기능 구현 - 보드 현재 기물 배치 조회 기능 구현 --- src/main/java/janggi/domain/Board.java | 31 +-------------- src/main/java/janggi/domain/Boards.java | 52 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 29 deletions(-) create mode 100644 src/main/java/janggi/domain/Boards.java diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 085898cfc7..eb7bb4ea0e 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -12,8 +12,6 @@ public class Board { - private static final int BOARD_HEIGHT = 10; - private final Map piecesByPoint; public Board() { @@ -40,24 +38,6 @@ public void move(Point from, Point to, Team team) { piecesByPoint.put(to, piece); } - public boolean isKingDie(Team team) { - return piecesByPoint.values().stream() - .noneMatch(piece -> piece.isSameType(PieceType.JANG) && - piece.isSameTeam(team)); - } - - public List> getPoints() { - List> pieces = new ArrayList<>(); - for (int i = 0; i < BOARD_HEIGHT; i++) { - pieces.add( - Point.getPointsAtY(i).stream() - .map(piecesByPoint::get) - .toList() - ); - } - return pieces; - } - public List getPieces(List point) { return point.stream() .map(piecesByPoint::get) @@ -65,15 +45,8 @@ public List getPieces(List point) { .toList(); } - public List getBoardStatus() { - return PositionInfo.from(piecesByPoint); - } - - public int scoreOf(Team team) { - return piecesByPoint.values().stream() - .filter(piece -> piece.isSameTeam(team)) - .mapToInt(Piece::getScore) - .sum(); + public Map getPiecesByPoint() { + return piecesByPoint; } private void validateFromPoint(Point from, Team team) { diff --git a/src/main/java/janggi/domain/Boards.java b/src/main/java/janggi/domain/Boards.java new file mode 100644 index 0000000000..dceb56d853 --- /dev/null +++ b/src/main/java/janggi/domain/Boards.java @@ -0,0 +1,52 @@ +package janggi.domain; + +import janggi.domain.piece.Piece; +import janggi.domain.piece.PieceType; +import janggi.domain.status.Team; +import janggi.dto.PositionInfo; +import java.util.ArrayList; +import java.util.List; + +public class Boards { + + private static final int BOARD_HEIGHT = 10; + + private final Board board; + + public Boards(Board board) { + this.board = board; + } + + public void move(Point from, Point to, Team team) { + board.move(from, to, team); + } + + public int scoreOf(Team team) { + return board.getPiecesByPoint().values().stream() + .filter(piece -> piece.isSameTeam(team)) + .mapToInt(Piece::getScore) + .sum(); + } + + public boolean isKingDie(Team team) { + return board.getPiecesByPoint().values().stream() + .noneMatch(piece -> piece.isSameType(PieceType.JANG) && + piece.isSameTeam(team)); + } + + public List getBoardStatus() { + return PositionInfo.from(board.getPiecesByPoint()); + } + + public List> getPoints() { + List> pieces = new ArrayList<>(); + for (int i = 0; i < BOARD_HEIGHT; i++) { + pieces.add( + Point.getPointsAtY(i).stream() + .map(board.getPiecesByPoint()::get) + .toList() + ); + } + return pieces; + } +} From 65897b12ff194a4debeff9385a8fa19a365516f3 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Sat, 4 Apr 2026 13:11:50 +0900 Subject: [PATCH 74/75] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20Board=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/janggi/domain/BoardTest.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java index 8f68146f23..23c142ff5b 100644 --- a/src/test/java/janggi/domain/BoardTest.java +++ b/src/test/java/janggi/domain/BoardTest.java @@ -126,9 +126,10 @@ void kingAlive() { PositionInfo.from(Team.CHO, "JANG", 4, 1), PositionInfo.from(Team.HAN, "JANG", 4, 8) ); + Boards boards = new Boards(board); - assertThat(board.isKingDie(Team.CHO)).isFalse(); - assertThat(board.isKingDie(Team.HAN)).isFalse(); + assertThat(boards.isKingDie(Team.CHO)).isFalse(); + assertThat(boards.isKingDie(Team.HAN)).isFalse(); } @Test @@ -137,8 +138,9 @@ void kingDie() { Board board = initBoard( PositionInfo.from(Team.CHO, "JANG", 4, 1) ); + Boards boards = new Boards(board); - assertThat(board.isKingDie(Team.HAN)).isTrue(); + assertThat(boards.isKingDie(Team.HAN)).isTrue(); } @Test @@ -149,9 +151,10 @@ void boardStatus() { PositionInfo.from(Team.CHO, "JANG", 4, 1), PositionInfo.from(Team.HAN, "JANG", 4, 8) ); + Boards boards = new Boards(board); // when - List boardStatus = board.getBoardStatus(); + List boardStatus = boards.getBoardStatus(); // then assertThat(boardStatus.size()).isEqualTo(2); @@ -169,10 +172,11 @@ void score_of_team() { PositionInfo.from(Team.HAN, "MA", 1, 9), PositionInfo.from(Team.HAN, "SA", 3, 9) ); + Boards boards = new Boards(board); // when - int choScore = board.scoreOf(Team.CHO); - int hanScore = board.scoreOf(Team.HAN); + int choScore = boards.scoreOf(Team.CHO); + int hanScore = boards.scoreOf(Team.HAN); // then assertThat(choScore).isEqualTo(9); @@ -186,6 +190,6 @@ private Board initBoard(PositionInfo... positionInfos) { } private Piece pieceAt(Board board, int x, int y) { - return board.getPoints().get(y).get(x); + return board.getPiecesByPoint().get(Point.of(x, y)); } } From 4f9debfaf8a3ba62ed149a6a61b979cd804083b5 Mon Sep 17 00:00:00 2001 From: jyt6640 Date: Sat, 4 Apr 2026 13:12:12 +0900 Subject: [PATCH 75/75] =?UTF-8?q?refactor:=20Boards=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/JanggiGame.java | 14 +++++++------- src/main/java/janggi/domain/status/ChoTurn.java | 8 ++++---- .../java/janggi/domain/status/FinishedGame.java | 4 ++-- src/main/java/janggi/domain/status/GameStatus.java | 4 ++-- src/main/java/janggi/domain/status/HanTurn.java | 8 ++++---- .../java/janggi/domain/status/ChoTurnTest.java | 10 ++++++---- .../java/janggi/domain/status/HanTurnTest.java | 11 +++++++---- 7 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index 5c53de6e8f..0e2812c4d4 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -9,16 +9,16 @@ public class JanggiGame { - private final Board board; private GameStatus gameStatus; + private final Boards boards; public JanggiGame(Board board) { - this.board = board; this.gameStatus = new ChoTurn(); + this.boards = new Boards(board); } public JanggiGame(Board board, GameStatus gameStatus) { - this.board = board; + this.boards = new Boards(board); this.gameStatus = gameStatus; } @@ -34,11 +34,11 @@ public Team getWinner() { } public List> getBoardStatus() { - return board.getPoints(); + return boards.getPoints(); } public void play(Point from, Point to) { - this.gameStatus = gameStatus.move(from, to, board); + this.gameStatus = gameStatus.move(from, to, boards); } public Team currentTurn() { @@ -46,10 +46,10 @@ public Team currentTurn() { } public List boardStatus() { - return board.getBoardStatus(); + return boards.getBoardStatus(); } public int scoreOf(Team team) { - return board.scoreOf(team); + return boards.scoreOf(team); } } diff --git a/src/main/java/janggi/domain/status/ChoTurn.java b/src/main/java/janggi/domain/status/ChoTurn.java index b7b84d7221..1367f958e9 100644 --- a/src/main/java/janggi/domain/status/ChoTurn.java +++ b/src/main/java/janggi/domain/status/ChoTurn.java @@ -1,6 +1,6 @@ package janggi.domain.status; -import janggi.domain.Board; +import janggi.domain.Boards; import janggi.domain.Point; public class ChoTurn implements GameStatus { @@ -17,9 +17,9 @@ public Team getTeam() { } @Override - public GameStatus move(Point from, Point to, Board board) { - board.move(from, to, team); - if (board.isKingDie(Team.HAN)) { + public GameStatus move(Point from, Point to, Boards boards) { + boards.move(from, to, team); + if (boards.isKingDie(Team.HAN)) { return new FinishedGame(team); } return new HanTurn(); diff --git a/src/main/java/janggi/domain/status/FinishedGame.java b/src/main/java/janggi/domain/status/FinishedGame.java index 296392feb8..b1614fbb89 100644 --- a/src/main/java/janggi/domain/status/FinishedGame.java +++ b/src/main/java/janggi/domain/status/FinishedGame.java @@ -1,6 +1,6 @@ package janggi.domain.status; -import janggi.domain.Board; +import janggi.domain.Boards; import janggi.domain.Point; public class FinishedGame implements GameStatus { @@ -22,7 +22,7 @@ public Team getTeam() { } @Override - public GameStatus move(Point from, Point to, Board board) { + public GameStatus move(Point from, Point to, Boards boards) { throw new IllegalStateException("[ERROR] 게임이 종료되었습니다. 승자는 " + winner.name() + "입니다."); } } diff --git a/src/main/java/janggi/domain/status/GameStatus.java b/src/main/java/janggi/domain/status/GameStatus.java index 1661246ffc..b7632c42cb 100644 --- a/src/main/java/janggi/domain/status/GameStatus.java +++ b/src/main/java/janggi/domain/status/GameStatus.java @@ -1,11 +1,11 @@ package janggi.domain.status; -import janggi.domain.Board; +import janggi.domain.Boards; import janggi.domain.Point; public interface GameStatus { Team getTeam(); - GameStatus move(Point from, Point to, Board board); + GameStatus move(Point from, Point to, Boards boards); default boolean isFinished() { return false; diff --git a/src/main/java/janggi/domain/status/HanTurn.java b/src/main/java/janggi/domain/status/HanTurn.java index 9c11e612db..7aafa937c4 100644 --- a/src/main/java/janggi/domain/status/HanTurn.java +++ b/src/main/java/janggi/domain/status/HanTurn.java @@ -1,6 +1,6 @@ package janggi.domain.status; -import janggi.domain.Board; +import janggi.domain.Boards; import janggi.domain.Point; public class HanTurn implements GameStatus { @@ -17,9 +17,9 @@ public Team getTeam() { } @Override - public GameStatus move(Point from, Point to, Board board) { - board.move(from, to, team); - if (board.isKingDie(Team.CHO)) { + public GameStatus move(Point from, Point to, Boards boards) { + boards.move(from, to, team); + if (boards.isKingDie(Team.CHO)) { return new FinishedGame(team); } return new ChoTurn(); diff --git a/src/test/java/janggi/domain/status/ChoTurnTest.java b/src/test/java/janggi/domain/status/ChoTurnTest.java index 125308025e..c1a3b2816e 100644 --- a/src/test/java/janggi/domain/status/ChoTurnTest.java +++ b/src/test/java/janggi/domain/status/ChoTurnTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertInstanceOf; import janggi.domain.Board; +import janggi.domain.Boards; import janggi.domain.Point; import janggi.dto.PositionInfo; import janggi.fixture.PositionInfoFixture; @@ -15,17 +16,18 @@ public class ChoTurnTest { - private Board board; + private Boards boards; @BeforeEach void setUp() { - board = new Board(); + Board board = new Board(); List info = new ArrayList<>(); info.add(PositionInfoFixture.from(List.of("HAN","JANG", "4", "1"))); info.add(PositionInfoFixture.from(List.of("CHO","JANG", "4", "8"))); info.add(PositionInfoFixture.from(List.of("HAN", "CHA", "1", "1"))); info.add(PositionInfoFixture.from(List.of("CHO", "CHA", "2", "3"))); board.init(info); + boards = new Boards(board); } @Test @@ -37,7 +39,7 @@ void turn_change() { // when GameStatus status = new ChoTurn(); - GameStatus gameStatus = status.move(from, to, board); + GameStatus gameStatus = status.move(from, to, boards); //then assertInstanceOf(HanTurn.class, gameStatus); @@ -54,7 +56,7 @@ void unavailable_move() { GameStatus status = new ChoTurn(); //then - assertThatThrownBy(() -> status.move(from, to, board)) + assertThatThrownBy(() -> status.move(from, to, boards)) .isInstanceOf(IllegalStateException.class); } } diff --git a/src/test/java/janggi/domain/status/HanTurnTest.java b/src/test/java/janggi/domain/status/HanTurnTest.java index 4eebbdc20b..d3e1235951 100644 --- a/src/test/java/janggi/domain/status/HanTurnTest.java +++ b/src/test/java/janggi/domain/status/HanTurnTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertInstanceOf; import janggi.domain.Board; +import janggi.domain.Boards; import janggi.domain.Point; import janggi.dto.PositionInfo; import janggi.fixture.PositionInfoFixture; @@ -15,17 +16,18 @@ public class HanTurnTest { - private Board board; + private Boards boards; @BeforeEach void setUp() { - board = new Board(); + Board board = new Board(); List info = new ArrayList<>(); info.add(PositionInfoFixture.from(List.of("HAN","JANG", "4", "1"))); info.add(PositionInfoFixture.from(List.of("CHO","JANG", "4", "8"))); info.add(PositionInfoFixture.from(List.of("HAN", "CHA", "1", "1"))); info.add(PositionInfoFixture.from(List.of("CHO", "CHA", "2", "3"))); board.init(info); + boards = new Boards(board); } @Test @@ -37,7 +39,7 @@ void turn_change() { // when GameStatus status = new HanTurn(); - GameStatus gameStatus = status.move(from, to, board); + GameStatus gameStatus = status.move(from, to, boards); //then assertInstanceOf(ChoTurn.class, gameStatus); @@ -51,6 +53,7 @@ void unavailable_move() { List info = new ArrayList<>(); info.add(PositionInfoFixture.from(List.of("CHO", "CHA", "1", "1"))); board.init(info); + Boards boards = new Boards(board); Point from = Point.of(1, 1); Point to = Point.of(2, 3); @@ -58,7 +61,7 @@ void unavailable_move() { GameStatus status = new HanTurn(); //then - assertThatThrownBy(() -> status.move(from, to, board)) + assertThatThrownBy(() -> status.move(from, to, boards)) .isInstanceOf(IllegalStateException.class); } }