From 187d1014a288ab1edc9d348933778b3d8eaa1c9b Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:16:26 +0900 Subject: [PATCH 01/99] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=EB=AA=85?= =?UTF-8?q?=EC=84=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 188 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9775dda0ae..5b4b75f4b9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,189 @@ # java-janggi -장기 미션 저장소 +## 🚀 사이클1: 미션 (보드 초기화 + 기물 이동) + +> 규칙을 적용하면서 장기를 구현한다. + +### 기능 요구 사항 + +> 장기(Janggi) 규칙을 참고하여 다음을 구현한다. + +**1.1단계 - 보드 초기화** + +- 게임 시작 시 장기판과 전체 기물을 올바른 위치에 초기화한다. +- 1.1단계에서는 기물의 이동은 구현하지 않는다. + +**1.2단계 - 기물 이동** + +- 각 기물의 이동 규칙을 구현한다. +- 기물의 이동 규칙은 직접 요구사항을 분석하여 정의한다. +- 궁성(宮城) 영역은 구현하지 않는다. (사이클2에서 다룬다) + +### 프로그래밍 요구 사항 + +- 자바 코드 컨벤션을 지키면서 프로그래밍한다. +- 기본적으로 Java Style Guide을 원칙으로 한다. +- indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다. + - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. + - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다. +- 3항 연산자를 쓰지 않는다. +- else 예약어를 쓰지 않는다. + - else 예약어를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다. + - 힌트: if문에서 값을 반환하는 방식으로 구현하면 else 예약어를 사용하지 않아도 된다. +- 모든 기능을 TDD로 구현해 단위 테스트가 존재해야 한다. 단, UI(System.out, System.in) 로직은 제외 + - 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다. + - UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다. +- 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다. + - 함수(또는 메소드)가 한 가지 일만 하도록 최대한 작게 만들어라. +- 배열 대신 컬렉션을 사용한다. +- 모든 원시 값과 문자열을 포장한다. +- 줄여 쓰지 않는다(축약 금지). +- 일급 컬렉션을 쓴다. +- 모든 엔티티를 작게 유지한다. +- 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다. + +### 추가된 요구 사항 + +- 도메인의 의존성을 최소한으로 구현한다. +- 한 줄에 점을 하나만 찍는다. +- 게터/세터/프로퍼티를 쓰지 않는다. +- 모든 객체지향 생활 체조 원칙을 잘 지키며 구현한다. +- 프로그래밍 체크리스트의 원칙을 지키면서 프로그래밍한다. + +## 과제 진행 요구 사항 + +- 구현을 시작하기 전에 기능 요구 사항을 분석하여 기능 목록을 정리한다. +- README.md 파일에 구현할 기능 목록을 정리해 추가한다. +- Git의 커밋 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가한다. + - AngularJS Git Commit Message Conventions 참고해 커밋 메시지를 작성한다. + +## 미션 중 할 일 + +1. 토론 활동에서 정한 규칙을 의식하며 코드 작성 +2. 규칙 때문에 코드를 변경한 곳 기록 +3. 막히는 순간 기록 + +## 미션 중 기록 + +필수 기록: + +- 상태 위치를 결정할 때 고민한 순간 1회 +- 불변/캡슐화를 적용한 코드 1곳 +- 규칙 적용으로 변경한 설계 1곳 +- 조건문을 다형성으로 대체한 코드 1곳 +- 인터페이스/추상 클래스를 도입한 이유 +- 새 기물 추가 시 변경 범위 테스트 (가상으로) + +## 출력예시 + +``` +장기 게임을 시작합니다. + + 1 2 3 4 5 6 7 8 9 +10 차 마 상 사 왕 사 상 마 차 + 9 . . . . . . . . . + 8 . 포 . . . . . 포 . + 7 졸 . 졸 . 졸 . 졸 . 졸 + 6 . . . . . . . . . + 5 . . . . . . . . . + 4 병 . 병 . 병 . 병 . 병 + 3 . 포 . . . . . 포 . + 2 . . . . . . . . . + 1 차 마 상 사 왕 사 상 마 차 + +현재 차례: 한 +이동할 기물의 출발 좌표를 입력하세요. 예: 1,4 +> 1,4 +이동할 기물의 도착 좌표를 입력하세요. 예: 1,4 +> 2,4 + +이동을 완료했습니다. + + 1 2 3 4 5 6 7 8 9 +10 차 마 상 사 왕 사 상 마 차 + 9 . . . . . . . . . + 8 . 포 . . . . . 포 . + 7 졸 . 졸 . 졸 . 졸 . 졸 + 6 . . . . . . . . . + 5 . . . . . . . . . + 4 차 . 병 . 병 . 병 . 병 + 3 . 포 . . . . . 포 . + 2 . . . . . . . . . + 1 . 마 상 사 왕 사 상 마 차 + +현재 차례: 초 +이동할 기물의 출발 좌표와 도착 좌표를 입력하세요. 예: a10 a9 +> b8 b5 + +오류: 포는 정확히 하나의 기물을 넘어야 합니다. +현재 차례: 초 +이동할 기물의 출발 좌표와 도착 좌표를 입력하세요. 예: a10 a9 +> a7 a6 + +이동을 완료했습니다. + +``` + +게임 종료 예시 + +``` +> e4 e1 + +왕을 포획했습니다. +승자: 한 + +게임을 종료합니다. +``` + +# 프로그램 흐름 +- [ ] 장기 게임 시작 문구 출력 (OutputView) +- [ ] 장기판 초기화 +- [ ] 장기판 출력(OutputView) +- [ ] 초, 한 번갈아가며 진행(Turn) + - [ ] 움직일 기물 좌표 입력(InputView - 콤마로 구분하여 좌표 입력) + - [ ] 입력값 검증 및 Position 객체 생성 + - [ ] 기물 반환 + - [ ] 장기판 내부 좌표인지 검증 + - [ ] 기물 반환 + - [ ] 반환된 기물의 목적 좌표 입력 + - [ ] 입력값 검증 및 Position 객체 생성 + - [ ] 기물 이동 + - [ ] 기물 이동 가능 여부 검증 + - [ ] 이동 경로 내에 아군 기물이 존재하는지 + - [ ] 이동 목적 좌표에 아군 기물이 존재하는지 + - [ ] 기물 Position Map 갱신(Pieces) + - [ ] 적 기물 잡았을 경우 score 갱신 + - [ ] 갱신된 장기판 출력(OutputView) +- [ ] 진행 후 Turns에 Board 상태 추가 + +# 도메인 + +- Piece(interface - move) + - gung + - sa + - jol(byeong) + - sang + - ma + - cha + - po + - List +- Pieces + - Map +- Position + - int x, int y +- Board + - Side han, Side cho +- JanggiRunner +- InputView +- OutputView +- Delta + - 칸 이동수 관리. +- MovePath + - List +- Side + - int score + - Pieces +- Turn + - Board +- Turns + - List From c9434e9924e9b632919d6bada3df6a5df91c937a Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:21:08 +0900 Subject: [PATCH 02/99] =?UTF-8?q?feat:=20Team=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=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/side/Team.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/janggi/domain/side/Team.java diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java new file mode 100644 index 0000000000..e82d75d040 --- /dev/null +++ b/src/main/java/janggi/domain/side/Team.java @@ -0,0 +1,8 @@ +package janggi.domain.side; + +public interface Team { + + boolean isPieceExists(int x, int y); + + Team move(int startX, int startY, int endX, int endY); +} From b6cb0830e33ee74f16e18907b721dab902d839f2 Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:21:26 +0900 Subject: [PATCH 03/99] =?UTF-8?q?feat:=20Team=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=EB=A5=BC=20=EC=83=81=EC=86=8D?= =?UTF-8?q?=EB=B0=9B=EB=8A=94=20=EC=B4=88=EB=82=98=EB=9D=BC,=20=ED=95=9C?= =?UTF-8?q?=EB=82=98=EB=9D=BC=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/side/Chu.java | 33 +++++++++++++++++++++++ src/main/java/janggi/domain/side/Han.java | 33 +++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/main/java/janggi/domain/side/Chu.java create mode 100644 src/main/java/janggi/domain/side/Han.java diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java new file mode 100644 index 0000000000..8d45b835ef --- /dev/null +++ b/src/main/java/janggi/domain/side/Chu.java @@ -0,0 +1,33 @@ +package janggi.domain.side; + +import janggi.domain.Pieces; + +public class Chu implements Team { + + private final Pieces pieces; + private final int score; + + private Chu(Pieces pieces, int score) { + this.pieces = pieces; + this.score = score; + } + + public static Chu createInitialChu() { + return new Chu(Pieces.createChu(), 0); + } + + @Override + public boolean isPieceExists(int x, int y) { + return pieces.isPieceExists(x, y); + } + + @Override + public Team move(int startX, int startY, int endX, int endY) { + return new Chu(pieces.move(startX, startY, endX, endY), calculateScore()); + } + + // TODO: 구현하기 + public int calculateScore() { + return 0; + } +} diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java new file mode 100644 index 0000000000..00eb51526f --- /dev/null +++ b/src/main/java/janggi/domain/side/Han.java @@ -0,0 +1,33 @@ +package janggi.domain.side; + +import janggi.domain.Pieces; + +public class Han implements Team { + + private final Pieces pieces; + private final int score; + + private Han(Pieces pieces, int score) { + this.pieces = pieces; + this.score = score; + } + + public static Han createInitialHan() { + return new Han(Pieces.createHan(), 0); + } + + @Override + public boolean isPieceExists(int x, int y) { + return pieces.isPieceExists(x, y); + } + + @Override + public Team move(int startX, int startY, int endX, int endY) { + return null; + } + + // TODO: 구현하기 + public int calculateScore() { + return 0; + } +} From 3ea93c443dcbe1ae58892f798db86b3f6df040db Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:21:53 +0900 Subject: [PATCH 04/99] =?UTF-8?q?feat:=20Board=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=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 | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/janggi/domain/Board.java diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java new file mode 100644 index 0000000000..2f5fbb7843 --- /dev/null +++ b/src/main/java/janggi/domain/Board.java @@ -0,0 +1,16 @@ +package janggi.domain; + +import janggi.domain.side.Chu; +import janggi.domain.side.Han; +import janggi.domain.side.Team; + +public class Board { + + private final Team chu; + private final Team han; + + public Board() { + this.chu = Chu.createInitialChu(); + this.han = Han.createInitialHan(); + } +} From 5542ff46a504217c890c41b1a0a94ed5228617e2 Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:22:09 +0900 Subject: [PATCH 05/99] =?UTF-8?q?feat:=20=EA=B8=B0=EB=AC=BC=EC=9D=84=20?= =?UTF-8?q?=ED=91=9C=ED=98=84=ED=95=98=EB=8A=94=20Piece=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=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/piece/Piece.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/java/janggi/domain/piece/Piece.java diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java new file mode 100644 index 0000000000..1dafa02def --- /dev/null +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -0,0 +1,6 @@ +package janggi.domain.piece; + +public interface Piece { + + boolean canMove(int startX, int startY, int endX, int endY); +} From e01fb6a0dc18c2b5bd698a210fb4445d4153088e Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:23:11 +0900 Subject: [PATCH 06/99] =?UTF-8?q?feat:=20=EB=B0=A9=ED=96=A5=EC=9D=84=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=95=98=EB=8A=94=20Delta=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=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/Delta.java | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/main/java/janggi/domain/Delta.java diff --git a/src/main/java/janggi/domain/Delta.java b/src/main/java/janggi/domain/Delta.java new file mode 100644 index 0000000000..db2eb0e783 --- /dev/null +++ b/src/main/java/janggi/domain/Delta.java @@ -0,0 +1,44 @@ +package janggi.domain; + +public class Delta { + + private final int dx; + private final int dy; + + private Delta(int dx, int dy) { + this.dx = dx; + this.dy = dy; + } + + public static Delta createUp() { + return new Delta(0, 1); + } + + public static Delta createDown() { + return new Delta(0, -1); + } + + public static Delta createRight() { + return new Delta(1, 0); + } + + public static Delta createLeft() { + return new Delta(-1, 0); + } + + public static Delta createLeftUp() { + return new Delta(-1, 1); + } + + public static Delta createRightUp() { + return new Delta(1, 1); + } + + public static Delta createLeftDown() { + return new Delta(-1, -1); + } + + public static Delta createRightDown() { + return new Delta(1, -1); + } +} From 9dab5565d6d25fc953bc374425a08cc4942966ee Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:23:26 +0900 Subject: [PATCH 07/99] =?UTF-8?q?feat:=20Delta=EC=9D=98=20=EC=A7=91?= =?UTF-8?q?=ED=95=A9=20=EB=8F=84=EB=A9=94=EC=9D=B8=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/MovePath.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/janggi/domain/MovePath.java diff --git a/src/main/java/janggi/domain/MovePath.java b/src/main/java/janggi/domain/MovePath.java new file mode 100644 index 0000000000..8268120406 --- /dev/null +++ b/src/main/java/janggi/domain/MovePath.java @@ -0,0 +1,12 @@ +package janggi.domain; + +import java.util.List; + +public class MovePath { + + private final List path; + + public MovePath(List path) { + this.path = path; + } +} From 911e4eaa2a13c91350ca31c49965fee3547ab3cf Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:24:22 +0900 Subject: [PATCH 08/99] =?UTF-8?q?feat:=20=EA=B8=B0=EB=AC=BC=EC=9D=98=20?= =?UTF-8?q?=EC=A0=90=EC=88=98=EC=99=80=20=ED=95=9C=EA=B8=80=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EC=9D=84=20=EA=B4=80=EB=A6=AC=ED=95=98=EB=8A=94=20enu?= =?UTF-8?q?m=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 | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/janggi/domain/piece/PieceType.java diff --git a/src/main/java/janggi/domain/piece/PieceType.java b/src/main/java/janggi/domain/piece/PieceType.java new file mode 100644 index 0000000000..1c25338efa --- /dev/null +++ b/src/main/java/janggi/domain/piece/PieceType.java @@ -0,0 +1,28 @@ +package janggi.domain.piece; + +public enum PieceType { + CHA("차", 13), + GUNG("궁", 0), + JOL("졸", 2), + MA("마", 5), + PO("포", 7), + SA("사", 3), + SANG("상", 3), + ; + + PieceType(String nickname, int score) { + this.nickname = nickname; + this.score = score; + } + + private final String nickname; + private final int score; + + public String getNickname() { + return nickname; + } + + public int getScore() { + return score; + } +} From 6b40cfdad603a77b1f21d25132354f5bc169931b Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:24:33 +0900 Subject: [PATCH 09/99] =?UTF-8?q?feat:=20=ED=95=9C=EB=82=98=EB=9D=BC?= =?UTF-8?q?=EC=99=80=20=EC=B4=88=EB=82=98=EB=9D=BC=EB=A5=BC=20=EB=AA=85?= =?UTF-8?q?=EC=8B=9C=ED=95=98=EB=8A=94=20enum=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/side/TeamType.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/janggi/domain/side/TeamType.java diff --git a/src/main/java/janggi/domain/side/TeamType.java b/src/main/java/janggi/domain/side/TeamType.java new file mode 100644 index 0000000000..194f7456f0 --- /dev/null +++ b/src/main/java/janggi/domain/side/TeamType.java @@ -0,0 +1,7 @@ +package janggi.domain.side; + +public enum TeamType { + CHU, + HAN, + ; +} From a969e128c067c61a78e0420487dcbdc17be7f0fd Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:24:47 +0900 Subject: [PATCH 10/99] =?UTF-8?q?feat:=20=EA=B8=B0=EB=AC=BC=20=EB=B3=84=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=ED=8C=90=EB=8B=A8=20=EB=A1=9C=EC=A7=81=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/piece/Cha.java | 38 ++++++++++++ src/main/java/janggi/domain/piece/Gung.java | 43 ++++++++++++++ src/main/java/janggi/domain/piece/Jol.java | 65 +++++++++++++++++++++ src/main/java/janggi/domain/piece/Ma.java | 34 +++++++++++ src/main/java/janggi/domain/piece/Po.java | 30 ++++++++++ src/main/java/janggi/domain/piece/Sa.java | 43 ++++++++++++++ src/main/java/janggi/domain/piece/Sang.java | 33 +++++++++++ 7 files changed, 286 insertions(+) create mode 100644 src/main/java/janggi/domain/piece/Cha.java create mode 100644 src/main/java/janggi/domain/piece/Gung.java create mode 100644 src/main/java/janggi/domain/piece/Jol.java create mode 100644 src/main/java/janggi/domain/piece/Ma.java create mode 100644 src/main/java/janggi/domain/piece/Po.java create mode 100644 src/main/java/janggi/domain/piece/Sa.java create mode 100644 src/main/java/janggi/domain/piece/Sang.java diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java new file mode 100644 index 0000000000..a4ab84a7eb --- /dev/null +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -0,0 +1,38 @@ +package janggi.domain.piece; + +import janggi.domain.Delta; +import janggi.domain.MovePath; +import java.util.List; + +public class Cha implements Piece { + + private final PieceType pieceType; + private final List paths; + + public Cha() { + pieceType = PieceType.CHA; + paths = List.of( + new MovePath(List.of(Delta.createUp())), + new MovePath(List.of(Delta.createDown())), + new MovePath(List.of(Delta.createLeft())), + new MovePath(List.of(Delta.createRight())) + ); + } + + // TODO: start 좌표는 유효한게 보장 되어 있는지? Pieces에서 Map 조회 후 반환하기. + @Override + public boolean canMove(int startX, int startY, int endX, int endY) { + if (isSamePosition(startX, startY, endX, endY)) { + return false; + } + return isStraightDirection(startX, startY, endX, endY); + } + + private boolean isSamePosition(int startX, int startY, int endX, int endY) { + return startX == endX && startY == endY; + } + + private boolean isStraightDirection(int startX, int startY, int endX, int endY) { + return startX == endX || startY == endY; + } +} diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java new file mode 100644 index 0000000000..d10cbe531a --- /dev/null +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -0,0 +1,43 @@ +package janggi.domain.piece; + +import janggi.domain.Delta; +import janggi.domain.MovePath; +import java.util.List; + +public class Gung implements Piece { + + private final PieceType pieceType; + private final List paths; + + public Gung() { + pieceType = PieceType.GUNG; + paths = List.of( + new MovePath(List.of(Delta.createUp())), + new MovePath(List.of(Delta.createDown())), + new MovePath(List.of(Delta.createLeft())), + new MovePath(List.of(Delta.createRight())), + new MovePath(List.of(Delta.createRightUp())), + new MovePath(List.of(Delta.createRightDown())), + new MovePath(List.of(Delta.createLeftUp())), + new MovePath(List.of(Delta.createLeftDown())) + ); + } + + @Override + public boolean canMove(int startX, int startY, int endX, int endY) { + int distanceX = Math.abs(endX - startX); + int distanceY = Math.abs(endY - startY); + if (isSamePosition(distanceX, distanceY)) { + return false; + } + return isOneStep(distanceX, distanceY); + } + + private boolean isSamePosition(int distanceX, int distanceY) { + return distanceX == 0 && distanceY == 0; + } + + private boolean isOneStep(int distanceX, int distanceY) { + return distanceX <= 1 && distanceY <= 1; + } +} diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java new file mode 100644 index 0000000000..08ba05040b --- /dev/null +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -0,0 +1,65 @@ +package janggi.domain.piece; + +import janggi.domain.Delta; +import janggi.domain.MovePath; +import janggi.domain.side.TeamType; +import java.util.List; + +public class Jol implements Piece { + + private final TeamType teamType; + private final PieceType pieceType; + private final List paths; + + public Jol(TeamType teamType) { + this.teamType = teamType; + pieceType = PieceType.JOL; + paths = createPaths(); + } + + @Override + public boolean canMove(int startX, int startY, int endX, int endY) { + int distanceX = endX - startX; + int distanceY = endY - startY; + if (isSamePosition(distanceX, distanceY)) { + return false; + } + if (isHorizontalMove(distanceX, distanceY)) { + return true; + } + return isForwardMove(distanceX, distanceY); + } + + private boolean isSamePosition(int distanceX, int distanceY) { + return distanceX == 0 && distanceY == 0; + } + + private boolean isHorizontalMove(int distanceX, int distanceY) { + return Math.abs(distanceX) == 1 && distanceY == 0; + } + + private boolean isForwardMove(int distanceX, int distanceY) { + if (distanceX != 0) { + return false; + } + if (teamType == TeamType.HAN) { + return distanceY == -1; + } + return distanceY == 1; + } + + private List createPaths() { + if (teamType == TeamType.HAN) { + return List.of( + new MovePath(List.of(Delta.createDown())), + new MovePath(List.of(Delta.createLeft())), + new MovePath(List.of(Delta.createRight())) + ); + } + return List.of( + new MovePath(List.of(Delta.createUp())), + new MovePath(List.of(Delta.createLeft())), + new MovePath(List.of(Delta.createRight())) + ); + } +} diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java new file mode 100644 index 0000000000..5de31b0fcf --- /dev/null +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -0,0 +1,34 @@ +package janggi.domain.piece; + +import janggi.domain.Delta; +import janggi.domain.MovePath; +import java.util.List; + +public class Ma implements Piece { + + private final PieceType pieceType; + private final List paths; + + public Ma() { + pieceType = PieceType.MA; + this.paths = List.of( + new MovePath(List.of(Delta.createUp(), Delta.createRightUp())), + new MovePath(List.of(Delta.createUp(), Delta.createLeftUp())), + new MovePath(List.of(Delta.createDown(), Delta.createRightDown())), + new MovePath(List.of(Delta.createDown(), Delta.createLeftDown())), + new MovePath(List.of(Delta.createLeft(), Delta.createLeftUp())), + new MovePath(List.of(Delta.createLeft(), Delta.createLeftDown())), + new MovePath(List.of(Delta.createRight(), Delta.createRightUp())), + new MovePath(List.of(Delta.createRight(), Delta.createRightDown())) + ); + } + + @Override + public boolean canMove(int startX, int startY, int endX, int endY) { + int distanceX = Math.abs(endX - startX); + int distanceY = Math.abs(endY - startY); + + // 마의 이동 거리 공식: (X가 1칸이면 Y는 2칸) 또는 (X가 2칸이면 Y는 1칸) + return (distanceX == 1 && distanceY == 2) || (distanceX == 2 && distanceY == 1); + } +} diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java new file mode 100644 index 0000000000..c220dac881 --- /dev/null +++ b/src/main/java/janggi/domain/piece/Po.java @@ -0,0 +1,30 @@ +package janggi.domain.piece; + +import janggi.domain.Delta; +import janggi.domain.MovePath; +import java.util.List; + +public class Po implements Piece { + + private final PieceType pieceType; + private final List paths; + + public Po() { + pieceType = PieceType.PO; + paths = List.of( + new MovePath(List.of(Delta.createUp())), + new MovePath(List.of(Delta.createDown())), + new MovePath(List.of(Delta.createLeft())), + new MovePath(List.of(Delta.createRight())) + ); + } + + // TODO: Pieces에서 사이에 기물이 없다면 애초에 호출하지 않음. Pieces에 Po를 움직일 경우 중간에 좌표가 있는지 확인하는 로직이 필요함. + @Override + public boolean canMove(int startX, int startY, int endX, int endY) { + if (startX != endX && startY != endY) { + return false; + } + return startX != endX || startY != endY; + } +} diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java new file mode 100644 index 0000000000..edfeccfabb --- /dev/null +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -0,0 +1,43 @@ +package janggi.domain.piece; + +import janggi.domain.Delta; +import janggi.domain.MovePath; +import java.util.List; + +public class Sa implements Piece{ + + private final PieceType pieceType; + private final List paths; + + public Sa() { + pieceType = PieceType.SA; + paths = List.of( + new MovePath(List.of(Delta.createUp())), + new MovePath(List.of(Delta.createDown())), + new MovePath(List.of(Delta.createLeft())), + new MovePath(List.of(Delta.createRight())), + new MovePath(List.of(Delta.createRightUp())), + new MovePath(List.of(Delta.createRightDown())), + new MovePath(List.of(Delta.createLeftUp())), + new MovePath(List.of(Delta.createLeftDown())) + ); + } + + @Override + public boolean canMove(int startX, int startY, int endX, int endY) { + int distanceX = Math.abs(endX - startX); + int distanceY = Math.abs(endY - startY); + if (isSamePosition(distanceX, distanceY)) { + return false; + } + return isOneStep(distanceX, distanceY); + } + + private boolean isSamePosition(int distanceX, int distanceY) { + return distanceX == 0 && distanceY == 0; + } + + private boolean isOneStep(int distanceX, int distanceY) { + return distanceX <= 1 && distanceY <= 1; + } +} diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java new file mode 100644 index 0000000000..5f3ae7fdd3 --- /dev/null +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -0,0 +1,33 @@ +package janggi.domain.piece; + +import janggi.domain.Delta; +import janggi.domain.MovePath; +import java.util.List; + +public class Sang implements Piece { + + private final PieceType pieceType; + private final List paths; + + public Sang() { + pieceType = PieceType.SANG; + paths = List.of( + new MovePath(List.of(Delta.createUp(), Delta.createRightUp(), Delta.createRightUp())), + new MovePath(List.of(Delta.createUp(), Delta.createLeftUp(), Delta.createLeftUp())), + new MovePath(List.of(Delta.createDown(), Delta.createRightDown(), Delta.createRightDown())), + new MovePath(List.of(Delta.createDown(), Delta.createLeftDown(), Delta.createLeftDown())), + new MovePath(List.of(Delta.createLeft(), Delta.createLeftUp(), Delta.createLeftUp())), + new MovePath(List.of(Delta.createLeft(), Delta.createLeftDown(), Delta.createLeftDown())), + new MovePath(List.of(Delta.createRight(), Delta.createRightUp(), Delta.createRightUp())), + new MovePath(List.of(Delta.createRight(), Delta.createRightDown(), Delta.createRightDown())) + ); + } + + @Override + public boolean canMove(int startX, int startY, int endX, int endY) { + int distanceX = Math.abs(endX - startX); + int distanceY = Math.abs(endY - startY); + + return (distanceX == 2 && distanceY == 3) || (distanceX == 3 && distanceY == 2); + } +} From f637cef4c7ea2c4b62844b071f38fa0501b69068 Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:33:12 +0900 Subject: [PATCH 11/99] =?UTF-8?q?feat:=20=EA=B8=B0=EB=AC=BC=20=EB=AC=B6?= =?UTF-8?q?=EC=9D=8C=20=EB=8F=84=EB=A9=94=EC=9D=B8=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/Pieces.java | 96 +++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/main/java/janggi/domain/Pieces.java diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java new file mode 100644 index 0000000000..6414b74150 --- /dev/null +++ b/src/main/java/janggi/domain/Pieces.java @@ -0,0 +1,96 @@ +package janggi.domain; + +import janggi.domain.piece.Cha; +import janggi.domain.piece.Gung; +import janggi.domain.piece.Jol; +import janggi.domain.piece.Ma; +import janggi.domain.piece.Piece; +import janggi.domain.piece.Po; +import janggi.domain.piece.Sa; +import janggi.domain.piece.Sang; +import janggi.domain.side.TeamType; +import java.util.HashMap; +import java.util.Map; + +public class Pieces { + + private final Map value; + + private Pieces(Map value) { + this.value = value; + } + + public Pieces move(int startX, int startY, int endX, int endY) { + Piece piece = value.get(new Position(startX, startY)); + if (piece.canMove(startX, startY, endX, endY)) { + // TODO: move하기 + // 기존 좌표 삭제 + 새 좌표 삽입 + } + return new Pieces(new HashMap<>(value)); + } + + public static Pieces createHan() { + Map pieces = new HashMap<>(); + createChas(pieces, 10); + createMas(pieces, 10); + createSangs(pieces, 10); + createSas(pieces, 10); + createGung(pieces, 9); + createPos(pieces, 8); + createJols(pieces, 7, TeamType.HAN); + return new Pieces(pieces); + } + + public static Pieces createChu() { + Map pieces = new HashMap<>(); + createChas(pieces, 1); + createMas(pieces, 1); + createSangs(pieces, 1); + createSas(pieces, 1); + createGung(pieces, 2); + createPos(pieces, 3); + createJols(pieces, 4, TeamType.CHU); + return new Pieces(pieces); + } + + + private static void createChas(Map pieces, int indexY) { + pieces.put(new Position(1, indexY), new Cha()); + pieces.put(new Position(9, indexY), new Cha()); + } + + private static void createMas(Map pieces, int indexY) { + pieces.put(new Position(2, indexY), new Ma()); + pieces.put(new Position(8, indexY), new Ma()); + } + + private static void createSangs(Map pieces, int indexY) { + pieces.put(new Position(7, indexY), new Sang()); + pieces.put(new Position(10 - 7, indexY), new Sang()); + } + + private static void createSas(Map pieces, int indexY) { + pieces.put(new Position(4, indexY), new Sa()); + pieces.put(new Position(10 - 6, indexY), new Sa()); + } + + private static void createGung(Map pieces, int indexY) { + pieces.put(new Position(5, indexY), new Gung()); + } + + private static void createPos(Map pieces, int indexY) { + pieces.put(new Position(2, indexY), new Po()); + pieces.put(new Position(10 - 2, indexY), new Po()); + } + + private static void createJols(Map pieces, int indexY, TeamType teamType) { + for (int i = 1; i < 10; i += 2) { + pieces.put(new Position(i, indexY), new Jol(teamType)); + } + } + + public boolean isPieceExists(int x, int y) { + Position position = new Position(x, y); + return value.containsKey(position); + } +} From 974e9ae7e10874dd7cfde0edb2f220979543edfb Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:33:21 +0900 Subject: [PATCH 12/99] =?UTF-8?q?feat:=20=EC=9C=84=EC=B9=98=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=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/Position.java | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/java/janggi/domain/Position.java diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java new file mode 100644 index 0000000000..ce33dccdf9 --- /dev/null +++ b/src/main/java/janggi/domain/Position.java @@ -0,0 +1,30 @@ +package janggi.domain; + +import java.util.Objects; + +public class Position { + + private final int x; + private final int y; + + public Position(int x, int y) { + this.x = x; + this.y = y; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof Position position)) { + return false; + } + return x == position.x && y == position.y; + } + + @Override + public int hashCode() { + return Objects.hash(x, y); + } +} From 4f8bd852f8a07b8136883a9979e9eeb6b04d90b7 Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 16:42:38 +0900 Subject: [PATCH 13/99] =?UTF-8?q?refactor:=20score=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EA=B0=92=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/side/Chu.java | 8 +++----- src/main/java/janggi/domain/side/Han.java | 6 ++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index 8d45b835ef..c6779d4df8 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -5,15 +5,13 @@ public class Chu implements Team { private final Pieces pieces; - private final int score; - private Chu(Pieces pieces, int score) { + private Chu(Pieces pieces) { this.pieces = pieces; - this.score = score; } public static Chu createInitialChu() { - return new Chu(Pieces.createChu(), 0); + return new Chu(Pieces.createChu()); } @Override @@ -23,7 +21,7 @@ public boolean isPieceExists(int x, int y) { @Override public Team move(int startX, int startY, int endX, int endY) { - return new Chu(pieces.move(startX, startY, endX, endY), calculateScore()); + return new Chu(pieces.move(startX, startY, endX, endY)); } // TODO: 구현하기 diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index 00eb51526f..763e2ecc2e 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -5,15 +5,13 @@ public class Han implements Team { private final Pieces pieces; - private final int score; - private Han(Pieces pieces, int score) { + private Han(Pieces pieces) { this.pieces = pieces; - this.score = score; } public static Han createInitialHan() { - return new Han(Pieces.createHan(), 0); + return new Han(Pieces.createHan()); } @Override From e0bc871e2c0ead666b71fd88adfc80e0a8715213 Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 19:21:29 +0900 Subject: [PATCH 14/99] =?UTF-8?q?feat:=20=EA=B2=BD=EA=B8=B0=EB=A5=BC=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=95=A0=20Turn=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=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/Turn.java | 14 ++++++++++++++ src/main/java/janggi/domain/Turns.java | 12 ++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/main/java/janggi/domain/Turn.java create mode 100644 src/main/java/janggi/domain/Turns.java diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java new file mode 100644 index 0000000000..d792b3bae1 --- /dev/null +++ b/src/main/java/janggi/domain/Turn.java @@ -0,0 +1,14 @@ +package janggi.domain; + +import janggi.domain.side.TeamType; + +public class Turn { + + private final TeamType movedTeam; + private final Board board; + + public Turn(TeamType movedTeam, Board board) { + this.movedTeam = movedTeam; + this.board = board; + } +} diff --git a/src/main/java/janggi/domain/Turns.java b/src/main/java/janggi/domain/Turns.java new file mode 100644 index 0000000000..8f068650ca --- /dev/null +++ b/src/main/java/janggi/domain/Turns.java @@ -0,0 +1,12 @@ +package janggi.domain; + +import java.util.List; + +public class Turns { + + private final List value; + + public Turns(List value) { + this.value = value; + } +} From 4d7077059c709b50bf5f33d3ec9f0b0c151de5f8 Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 19:21:58 +0900 Subject: [PATCH 15/99] =?UTF-8?q?feat:=20=EC=9C=84=EC=B9=98=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EB=B0=98=ED=99=98=20=EB=A9=94=EC=84=9C=EB=93=9C=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/main/java/janggi/domain/side/Chu.java | 7 +++++++ src/main/java/janggi/domain/side/Han.java | 7 +++++++ src/main/java/janggi/domain/side/Team.java | 5 +++++ 3 files changed, 19 insertions(+) diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index c6779d4df8..8ce126b172 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -1,6 +1,8 @@ package janggi.domain.side; import janggi.domain.Pieces; +import janggi.dto.BoardSpot; +import java.util.List; public class Chu implements Team { @@ -19,6 +21,11 @@ public boolean isPieceExists(int x, int y) { return pieces.isPieceExists(x, y); } + @Override + public List makeSpots() { + return pieces.makeSpots(); + } + @Override public Team move(int startX, int startY, int endX, int endY) { return new Chu(pieces.move(startX, startY, endX, endY)); diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index 763e2ecc2e..e8727b2262 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -1,6 +1,8 @@ package janggi.domain.side; import janggi.domain.Pieces; +import janggi.dto.BoardSpot; +import java.util.List; public class Han implements Team { @@ -19,6 +21,11 @@ public boolean isPieceExists(int x, int y) { return pieces.isPieceExists(x, y); } + @Override + public List makeSpots() { + return pieces.makeSpots(); + } + @Override public Team move(int startX, int startY, int endX, int endY) { return null; diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index e82d75d040..0c034917ad 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -1,8 +1,13 @@ package janggi.domain.side; +import janggi.dto.BoardSpot; +import java.util.List; + public interface Team { boolean isPieceExists(int x, int y); + List makeSpots(); + Team move(int startX, int startY, int endX, int endY); } From 3ea60463f3748bb5634e26a6996734bb4d6b2fb2 Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 19:23:23 +0900 Subject: [PATCH 16/99] =?UTF-8?q?feat:=20=EC=9E=A5=EA=B8=B0=ED=8C=90=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=ED=99=94=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/Application.java | 14 ++++ src/main/java/janggi/JanggiRunner.java | 22 ++++++ src/main/java/janggi/domain/Board.java | 21 +++++- src/main/java/janggi/domain/Pieces.java | 16 ++++- src/main/java/janggi/domain/Position.java | 4 ++ src/main/java/janggi/domain/piece/Cha.java | 5 ++ src/main/java/janggi/domain/piece/Gung.java | 5 ++ src/main/java/janggi/domain/piece/Jol.java | 5 ++ src/main/java/janggi/domain/piece/Ma.java | 5 ++ src/main/java/janggi/domain/piece/Piece.java | 2 + src/main/java/janggi/domain/piece/Po.java | 5 ++ src/main/java/janggi/domain/piece/Sa.java | 5 ++ src/main/java/janggi/domain/piece/Sang.java | 5 ++ src/main/java/janggi/dto/BoardSpot.java | 8 +++ src/main/java/janggi/view/InputView.java | 5 ++ src/main/java/janggi/view/OutputView.java | 71 ++++++++++++++++++++ 16 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 src/main/java/janggi/Application.java create mode 100644 src/main/java/janggi/JanggiRunner.java create mode 100644 src/main/java/janggi/dto/BoardSpot.java create mode 100644 src/main/java/janggi/view/InputView.java create mode 100644 src/main/java/janggi/view/OutputView.java diff --git a/src/main/java/janggi/Application.java b/src/main/java/janggi/Application.java new file mode 100644 index 0000000000..afb3e08782 --- /dev/null +++ b/src/main/java/janggi/Application.java @@ -0,0 +1,14 @@ +package janggi; + +import janggi.view.InputView; +import janggi.view.OutputView; + +public class Application { + + public static void main(String[] args) { + InputView inputView = new InputView(); + OutputView outputView = new OutputView(); + JanggiRunner janggiRunner = new JanggiRunner(inputView, outputView); + janggiRunner.execute(); + } +} diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java new file mode 100644 index 0000000000..1ccff1cd18 --- /dev/null +++ b/src/main/java/janggi/JanggiRunner.java @@ -0,0 +1,22 @@ +package janggi; + +import janggi.domain.Board; +import janggi.view.InputView; +import janggi.view.OutputView; + +public class JanggiRunner { + + private final InputView inputView; + private final OutputView outputView; + + public JanggiRunner(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void execute() { + Board board = Board.createInitialBoard(); + outputView.printStartMessage(); + outputView.printBoard(board.makeSpots()); + } +} diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 2f5fbb7843..77b15b24d3 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -3,14 +3,29 @@ import janggi.domain.side.Chu; import janggi.domain.side.Han; import janggi.domain.side.Team; +import janggi.dto.BoardSpot; +import java.util.ArrayList; +import java.util.List; public class Board { private final Team chu; private final Team han; - public Board() { - this.chu = Chu.createInitialChu(); - this.han = Han.createInitialHan(); + private Board(Team chu, Team han) { + this.chu = chu; + this.han = han; + } + + public static Board createInitialBoard() { + return new Board(Chu.createInitialChu(), Han.createInitialHan()); + } + + public List makeSpots() { + List chuBoardSpots = chu.makeSpots(); + List hanBoardSpots = han.makeSpots(); + List boardSpots = new ArrayList<>(chuBoardSpots); + boardSpots.addAll(hanBoardSpots); + return boardSpots; } } diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index 6414b74150..52613afd9c 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -9,7 +9,10 @@ import janggi.domain.piece.Sa; import janggi.domain.piece.Sang; import janggi.domain.side.TeamType; +import janggi.dto.BoardSpot; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; public class Pieces { @@ -71,7 +74,7 @@ private static void createSangs(Map pieces, int indexY) { private static void createSas(Map pieces, int indexY) { pieces.put(new Position(4, indexY), new Sa()); - pieces.put(new Position(10 - 6, indexY), new Sa()); + pieces.put(new Position(10 - 4, indexY), new Sa()); } private static void createGung(Map pieces, int indexY) { @@ -93,4 +96,15 @@ public boolean isPieceExists(int x, int y) { Position position = new Position(x, y); return value.containsKey(position); } + + public List makeSpots() { + List boardSpots = new ArrayList<>(); + for (Map.Entry entry : value.entrySet()) { + boardSpots.add(new BoardSpot( + entry.getKey().makePositionKey(), + entry.getValue().nickname() + )); + } + return boardSpots; + } } diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java index ce33dccdf9..c56be7d51f 100644 --- a/src/main/java/janggi/domain/Position.java +++ b/src/main/java/janggi/domain/Position.java @@ -12,6 +12,10 @@ public Position(int x, int y) { this.y = y; } + public String makePositionKey() { + return x + "," + y; + } + @Override public boolean equals(Object object) { if (this == object) { diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index a4ab84a7eb..42dc8fe36f 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -35,4 +35,9 @@ private boolean isSamePosition(int startX, int startY, int endX, int endY) { private boolean isStraightDirection(int startX, int startY, int endX, int endY) { return startX == endX || startY == endY; } + + @Override + public String nickname() { + return pieceType.getNickname(); + } } diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index d10cbe531a..ef1a6a0daf 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -40,4 +40,9 @@ private boolean isSamePosition(int distanceX, int distanceY) { private boolean isOneStep(int distanceX, int distanceY) { return distanceX <= 1 && distanceY <= 1; } + + @Override + public String nickname() { + return pieceType.getNickname(); + } } diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 08ba05040b..9a79cf08a8 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -62,4 +62,9 @@ private List createPaths() { new MovePath(List.of(Delta.createRight())) ); } + + @Override + public String nickname() { + return pieceType.getNickname(); + } } diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 5de31b0fcf..8e9eb4bf5c 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -31,4 +31,9 @@ public boolean canMove(int startX, int startY, int endX, int endY) { // 마의 이동 거리 공식: (X가 1칸이면 Y는 2칸) 또는 (X가 2칸이면 Y는 1칸) return (distanceX == 1 && distanceY == 2) || (distanceX == 2 && distanceY == 1); } + + @Override + public String nickname() { + return pieceType.getNickname(); + } } diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index 1dafa02def..aa82108439 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -3,4 +3,6 @@ public interface Piece { boolean canMove(int startX, int startY, int endX, int endY); + + String nickname(); } diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index c220dac881..1bd64f7873 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -27,4 +27,9 @@ public boolean canMove(int startX, int startY, int endX, int endY) { } return startX != endX || startY != endY; } + + @Override + public String nickname() { + return pieceType.getNickname(); + } } diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index edfeccfabb..4b7301a257 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -40,4 +40,9 @@ private boolean isSamePosition(int distanceX, int distanceY) { private boolean isOneStep(int distanceX, int distanceY) { return distanceX <= 1 && distanceY <= 1; } + + @Override + public String nickname() { + return pieceType.getNickname(); + } } diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index 5f3ae7fdd3..f769c92b9d 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -30,4 +30,9 @@ public boolean canMove(int startX, int startY, int endX, int endY) { return (distanceX == 2 && distanceY == 3) || (distanceX == 3 && distanceY == 2); } + + @Override + public String nickname() { + return pieceType.getNickname(); + } } diff --git a/src/main/java/janggi/dto/BoardSpot.java b/src/main/java/janggi/dto/BoardSpot.java new file mode 100644 index 0000000000..7ce543e6e6 --- /dev/null +++ b/src/main/java/janggi/dto/BoardSpot.java @@ -0,0 +1,8 @@ +package janggi.dto; + +public record BoardSpot( + String position, + String pieceName +) { + +} diff --git a/src/main/java/janggi/view/InputView.java b/src/main/java/janggi/view/InputView.java new file mode 100644 index 0000000000..9ce14df9ae --- /dev/null +++ b/src/main/java/janggi/view/InputView.java @@ -0,0 +1,5 @@ +package janggi.view; + +public class InputView { + +} diff --git a/src/main/java/janggi/view/OutputView.java b/src/main/java/janggi/view/OutputView.java new file mode 100644 index 0000000000..5b2d92b00e --- /dev/null +++ b/src/main/java/janggi/view/OutputView.java @@ -0,0 +1,71 @@ +package janggi.view; + +import janggi.dto.BoardSpot; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class OutputView { + + public void printMessage(String message) { + System.out.println(message); + } + + public void printNewLine() { + System.out.println(); + } + + public void printStartMessage() { + printMessage("장기 게임을 시작합니다."); + printNewLine(); + } + + public void printBoard(List boardSpots) { + System.out.println(makeBoard(boardSpots)); + } + + private String makeBoard(List boardSpots) { + Map boardSpotMap = makeBoardSpotMap(boardSpots); + StringBuilder builder = new StringBuilder(); + builder.append(makeHeader()); + for (int y = 10; y >= 1; y--) { + builder.append(makeRow(boardSpotMap, y)); + } + return builder.toString(); + } + + private String makeHeader() { + StringBuilder builder = new StringBuilder(" "); + for (int x = 1; x <= 9; x++) { + builder.append(String.format("%-3s", x)); + } + builder.append(System.lineSeparator()); + return builder.toString(); + } + + private String makeRow(Map boardSpotMap, int y) { + StringBuilder builder = new StringBuilder(); + builder.append(String.format("%2d ", y)); + for (int x = 1; x <= 9; x++) { + builder.append(String.format("%-3s", findPieceName(boardSpotMap, x, y))); + } + builder.append(System.lineSeparator()); + return builder.toString(); + } + + private Map makeBoardSpotMap(List boardSpots) { + Map boardSpotMap = new HashMap<>(); + for (BoardSpot boardSpot : boardSpots) { + boardSpotMap.put(boardSpot.position(), boardSpot.pieceName()); + } + return boardSpotMap; + } + + private String findPieceName(Map boardSpotMap, int x, int y) { + return boardSpotMap.getOrDefault(makeKey(x, y), "."); + } + + private String makeKey(int x, int y) { + return x + "," + y; + } +} From 8bdc49c1eb38234545ea305d0dd6de7ad15887b3 Mon Sep 17 00:00:00 2001 From: Sumin Date: Wed, 25 Mar 2026 20:16:46 +0900 Subject: [PATCH 17/99] =?UTF-8?q?feat:=20=EA=B8=B0=EB=AC=BC=20=EC=A2=8C?= =?UTF-8?q?=ED=91=9C=20=EC=9E=85=EB=A0=A5=20=EB=B0=8F=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=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 --- .codex/skills/SKILL.md | 58 +++++++++++++++ src/main/java/janggi/JanggiRunner.java | 19 +++++ src/main/java/janggi/domain/Board.java | 37 ++++++++++ src/main/java/janggi/domain/Pieces.java | 5 ++ src/main/java/janggi/domain/Position.java | 23 ++++++ src/main/java/janggi/domain/Turns.java | 5 ++ src/main/java/janggi/domain/side/Chu.java | 8 ++ src/main/java/janggi/domain/side/Han.java | 8 ++ src/main/java/janggi/domain/side/Team.java | 5 ++ .../java/janggi/domain/side/TeamType.java | 10 ++- .../java/janggi/util/DelimiterParser.java | 15 ++++ src/main/java/janggi/view/InputView.java | 7 ++ src/main/java/janggi/view/OutputView.java | 8 ++ .../java/janggi/domain/piece/ChaTest.java | 73 +++++++++++++++++++ .../java/janggi/domain/piece/GungTest.java | 65 +++++++++++++++++ 15 files changed, 344 insertions(+), 2 deletions(-) create mode 100644 .codex/skills/SKILL.md create mode 100644 src/main/java/janggi/util/DelimiterParser.java create mode 100644 src/test/java/janggi/domain/piece/ChaTest.java create mode 100644 src/test/java/janggi/domain/piece/GungTest.java diff --git a/.codex/skills/SKILL.md b/.codex/skills/SKILL.md new file mode 100644 index 0000000000..aa9541ad59 --- /dev/null +++ b/.codex/skills/SKILL.md @@ -0,0 +1,58 @@ +--- +description: Interview user in-depth to create a detailed spec +allowed-tools: Write +--- + +# 심층 인터뷰를 통한 요구사항 스펙 생성 + +사용자를 인터뷰하여 암묵적 가정, 놓치기 쉬운 요구사항, 트레이드오프를 도출하고 상세한 스펙 문서를 생성합니다. + +## 인터뷰 원칙 + +1. **역방향 요구사항 추출**: 사용자가 "당연하다고 생각해서 안 적는 것들"을 질문으로 끌어내기 +2. **깊이 있는 질문**: 뻔한 질문은 피하고, 구현 세부사항까지 파고들기 +3. **다각도 탐색**: 기술 구현, 우려 지점, 트레이드오프 등 다양한 관점에서 질문 + +## 질문 영역 (참고용, 순서나 범위는 자유롭게) + +- 기술 구현 방식 및 아키텍처 +- 엣지 케이스 및 예외 상황 +- 성능, 확장성 고려사항 +- 기존 시스템과의 통합 +- 우려 지점 및 리스크 +- 트레이드오프 선택 + +## 완료 조건 + +다음 중 하나 이상 충족 시 인터뷰 종료: + +- 핵심 요구사항과 주요 엣지 케이스가 충분히 도출됨 +- 사용자가 "충분하다" 또는 "그만"이라고 표시 +- 새로운 질문에 대해 사용자가 "모르겠다" 또는 "나중에 결정"이 반복됨 + +## 실행 + +$ARGUMENTS + +위 주제에 대해 사용자에게 애매하다면 바로 질문을 하며 심층 인터뷰를 진행하세요. + +- 한 번에 1-2개의 연관된 질문을 던지세요 +- 이전 답변을 기반으로 후속 질문을 발전시키세요 +- 충분한 정보가 모이면 스펙 문서를 작성하세요 + +## 출력 + +인터뷰 완료 후 다음 위치에 스펙 문서 저장: + +``` +docs/codex/001_prd_v1.md +``` + +스펙 문서에 포함할 내용: + +- 기능 개요 및 목적 +- 핵심 요구사항 (Functional Requirements) +- 비기능 요구사항 (Non-functional Requirements) +- 엣지 케이스 및 예외 처리 +- 제약 조건 및 가정 +- 우선순위 및 트레이드오프 결정사항 diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index 1ccff1cd18..d32abda746 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -1,8 +1,14 @@ package janggi; import janggi.domain.Board; +import janggi.domain.Turns; +import janggi.domain.piece.Piece; +import janggi.domain.side.TeamType; +import janggi.util.DelimiterParser; import janggi.view.InputView; import janggi.view.OutputView; +import java.util.ArrayList; +import java.util.List; public class JanggiRunner { @@ -18,5 +24,18 @@ public void execute() { Board board = Board.createInitialBoard(); outputView.printStartMessage(); outputView.printBoard(board.makeSpots()); + + Turns turns = new Turns(new ArrayList<>()); + TeamType nowTurn = turns.getFirstTurn(); + // + while (true) { + outputView.printTurnNotice(nowTurn.name()); + outputView.printAskPiecePosition(); + String rawPiecePosition = inputView.readLine(); + List parsedPiecePosition = DelimiterParser.parse(rawPiecePosition); + Piece piece = board.findPiece(parsedPiecePosition); + + } +// TeamType nowTurn = turns.getPlayTurn(); } } diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 77b15b24d3..22a6933b78 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -1,14 +1,20 @@ package janggi.domain; +import janggi.domain.piece.Piece; import janggi.domain.side.Chu; import janggi.domain.side.Han; import janggi.domain.side.Team; import janggi.dto.BoardSpot; import java.util.ArrayList; import java.util.List; +import java.util.Optional; public class Board { + private static final int FIRST_INDEX = 1; + private static final int LAST_X_INDEX = 9; + private static final int LAST_Y_INDEX = 10; + private final Team chu; private final Team han; @@ -28,4 +34,35 @@ public List makeSpots() { boardSpots.addAll(hanBoardSpots); return boardSpots; } + + public Piece findPiece(List parsedPiecePosition) { + Position inputPosition = Position.makePosition(parsedPiecePosition); + validateRange(inputPosition); + return findPiece(inputPosition) + .orElseThrow(() -> new IllegalArgumentException("입력한 위치에 기물이 없습니다.")); + } + + public Optional findPiece(Position position) { + Optional chuPiece = chu.findPiece(position); + if (chuPiece.isPresent()) { + return chuPiece; + } + return han.findPiece(position); + } + + private void validateRange(Position inputPosition) { + int x = inputPosition.getX(); + int y = inputPosition.getY(); + if (isNotInRange(FIRST_INDEX, LAST_X_INDEX, x) || isNotInRange(FIRST_INDEX, LAST_Y_INDEX, y)) { + throw new IllegalArgumentException("입력한 좌표가 장기판 범위 밖입니다."); + } + } + + private boolean isInRange(int start, int last, int index) { + return index >= start && index <= last; + } + + private boolean isNotInRange(int start, int last, int index) { + return !isInRange(start, last, index); + } } diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index 52613afd9c..0c838884ee 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -14,6 +14,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; public class Pieces { @@ -107,4 +108,8 @@ public List makeSpots() { } return boardSpots; } + + public Optional findPiece(Position position) { + return Optional.ofNullable(value.get(position)); + } } diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java index c56be7d51f..d90ed30ecf 100644 --- a/src/main/java/janggi/domain/Position.java +++ b/src/main/java/janggi/domain/Position.java @@ -1,9 +1,11 @@ package janggi.domain; +import java.util.List; import java.util.Objects; public class Position { + private static final int POSITION_SIZE = 2; private final int x; private final int y; @@ -12,6 +14,27 @@ public Position(int x, int y) { this.y = y; } + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public static Position makePosition(List parsedPiecePosition) { + if (parsedPiecePosition.size() != POSITION_SIZE) { + throw new IllegalArgumentException("기물의 좌표는 두 개로 입력해야 합니다."); + } + try { + int parsedX = Integer.parseInt(parsedPiecePosition.getFirst()); + int parsedY = Integer.parseInt(parsedPiecePosition.getLast()); + return new Position(parsedX, parsedY); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("좌표는 숫자가 입력되어야 합니다."); + } + } + public String makePositionKey() { return x + "," + y; } diff --git a/src/main/java/janggi/domain/Turns.java b/src/main/java/janggi/domain/Turns.java index 8f068650ca..f490a47dcb 100644 --- a/src/main/java/janggi/domain/Turns.java +++ b/src/main/java/janggi/domain/Turns.java @@ -1,5 +1,6 @@ package janggi.domain; +import janggi.domain.side.TeamType; import java.util.List; public class Turns { @@ -9,4 +10,8 @@ public class Turns { public Turns(List value) { this.value = value; } + + public TeamType getFirstTurn() { + return TeamType.CHU; + } } diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index 8ce126b172..3f525378a5 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -1,8 +1,11 @@ package janggi.domain.side; import janggi.domain.Pieces; +import janggi.domain.Position; +import janggi.domain.piece.Piece; import janggi.dto.BoardSpot; import java.util.List; +import java.util.Optional; public class Chu implements Team { @@ -26,6 +29,11 @@ public List makeSpots() { return pieces.makeSpots(); } + @Override + public Optional findPiece(Position position) { + return pieces.findPiece(position); + } + @Override public Team move(int startX, int startY, int endX, int endY) { return new Chu(pieces.move(startX, startY, endX, endY)); diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index e8727b2262..0598a3852e 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -1,8 +1,11 @@ package janggi.domain.side; import janggi.domain.Pieces; +import janggi.domain.Position; +import janggi.domain.piece.Piece; import janggi.dto.BoardSpot; import java.util.List; +import java.util.Optional; public class Han implements Team { @@ -26,6 +29,11 @@ public List makeSpots() { return pieces.makeSpots(); } + @Override + public Optional findPiece(Position position) { + return pieces.findPiece(position); + } + @Override public Team move(int startX, int startY, int endX, int endY) { return null; diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index 0c034917ad..cf1b138cbe 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -1,7 +1,10 @@ package janggi.domain.side; +import janggi.domain.Position; +import janggi.domain.piece.Piece; import janggi.dto.BoardSpot; import java.util.List; +import java.util.Optional; public interface Team { @@ -9,5 +12,7 @@ public interface Team { List makeSpots(); + Optional findPiece(Position position); + Team move(int startX, int startY, int endX, int endY); } diff --git a/src/main/java/janggi/domain/side/TeamType.java b/src/main/java/janggi/domain/side/TeamType.java index 194f7456f0..503be44739 100644 --- a/src/main/java/janggi/domain/side/TeamType.java +++ b/src/main/java/janggi/domain/side/TeamType.java @@ -1,7 +1,13 @@ package janggi.domain.side; public enum TeamType { - CHU, - HAN, + CHU("초나라"), + HAN("한나라"), ; + + private final String name; + + TeamType(String name) { + this.name = name; + } } diff --git a/src/main/java/janggi/util/DelimiterParser.java b/src/main/java/janggi/util/DelimiterParser.java new file mode 100644 index 0000000000..ce99d0f27c --- /dev/null +++ b/src/main/java/janggi/util/DelimiterParser.java @@ -0,0 +1,15 @@ +package janggi.util; + +import java.util.Arrays; +import java.util.List; + +public class DelimiterParser { + + private static final String DELIMITER = ","; + + public static List parse(String input) { + return Arrays.stream(input.split(DELIMITER)) + .map(String::trim) + .toList(); + } +} diff --git a/src/main/java/janggi/view/InputView.java b/src/main/java/janggi/view/InputView.java index 9ce14df9ae..72806d5a88 100644 --- a/src/main/java/janggi/view/InputView.java +++ b/src/main/java/janggi/view/InputView.java @@ -1,5 +1,12 @@ package janggi.view; +import java.util.Scanner; + public class InputView { + private static final Scanner scanner = new Scanner(System.in); + + public String readLine() { + return scanner.nextLine(); + } } diff --git a/src/main/java/janggi/view/OutputView.java b/src/main/java/janggi/view/OutputView.java index 5b2d92b00e..6fc9301f46 100644 --- a/src/main/java/janggi/view/OutputView.java +++ b/src/main/java/janggi/view/OutputView.java @@ -68,4 +68,12 @@ private String findPieceName(Map boardSpotMap, int x, int y) { private String makeKey(int x, int y) { return x + "," + y; } + + public void printTurnNotice(String nowTurn) { + printMessage(nowTurn + "의 차례입니다."); + } + + public void printAskPiecePosition() { + printMessage("움직일 기물의 좌표를 입력해주세요."); + } } diff --git a/src/test/java/janggi/domain/piece/ChaTest.java b/src/test/java/janggi/domain/piece/ChaTest.java new file mode 100644 index 0000000000..6a96bda96c --- /dev/null +++ b/src/test/java/janggi/domain/piece/ChaTest.java @@ -0,0 +1,73 @@ +package janggi.domain.piece; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ChaTest { + + @Test + @DisplayName("시작점에서 오른쪽으로 도달할 수 있는 도착점은 true를 반환한다.") + void canRightReach() { + // given + int startX = 0; + int startY = 0; + int endX = 3; + int endY = 0; + + // when + Cha cha = new Cha(); + + // then + Assertions.assertThat(cha.canMove(startX, startY, endX, endY)).isTrue(); + } + + @Test + @DisplayName("시작점에서 왼쪽으로 도달할 수 있는 도착점은 true를 반환한다.") + void canLeftReach() { + // given + int startX = 2; + int startY = 0; + int endX = 1; + int endY = 0; + + // when + Cha cha = new Cha(); + + // then + Assertions.assertThat(cha.canMove(startX, startY, endX, endY)).isTrue(); + } + + @Test + @DisplayName("시작점에서 위로 도달할 수 있는 도착점은 true를 반환한다.") + void canUpReach() { + // given + int startX = 0; + int startY = 0; + int endX = 0; + int endY = 1; + + // when + Cha cha = new Cha(); + + // then + Assertions.assertThat(cha.canMove(startX, startY, endX, endY)).isTrue(); + } + + @Test + @DisplayName("시작점에서 아래로 도달할 수 있는 도착점은 true를 반환한다.") + void canDownReach() { + // given + int startX = 0; + int startY = 1; + int endX = 0; + int endY = 0; + + // when + Cha cha = new Cha(); + + // then + Assertions.assertThat(cha.canMove(startX, startY, endX, endY)).isTrue(); + } + +} diff --git a/src/test/java/janggi/domain/piece/GungTest.java b/src/test/java/janggi/domain/piece/GungTest.java new file mode 100644 index 0000000000..ca74fae8eb --- /dev/null +++ b/src/test/java/janggi/domain/piece/GungTest.java @@ -0,0 +1,65 @@ +package janggi.domain.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class GungTest { + + @Test + @DisplayName("시작점에서 상하좌우 한 칸 이동할 수 있다.") + void canMoveStraightOneStep() { + // given + Gung gung = new Gung(); + + // when & then + assertAll( + () -> assertThat(gung.canMove(4, 4, 4, 5)).isTrue(), + () -> assertThat(gung.canMove(4, 4, 4, 3)).isTrue(), + () -> assertThat(gung.canMove(4, 4, 5, 4)).isTrue(), + () -> assertThat(gung.canMove(4, 4, 3, 4)).isTrue() + ); + } + + @Test + @DisplayName("시작점에서 대각선 한 칸 이동할 수 있다.") + void canMoveDiagonalOneStep() { + // given + Gung gung = new Gung(); + + // when & then + assertAll( + () -> assertThat(gung.canMove(4, 4, 5, 5)).isTrue(), + () -> assertThat(gung.canMove(4, 4, 5, 3)).isTrue(), + () -> assertThat(gung.canMove(4, 4, 3, 5)).isTrue(), + () -> assertThat(gung.canMove(4, 4, 3, 3)).isTrue() + ); + } + + @Test + @DisplayName("시작점에서 두 칸 이상 이동할 수 없다.") + void cannotMoveOverOneStep() { + // given + Gung gung = new Gung(); + + // when & then + assertAll( + () -> assertThat(gung.canMove(4, 4, 6, 4)).isFalse(), + () -> assertThat(gung.canMove(4, 4, 6, 6)).isFalse(), + () -> assertThat(gung.canMove(4, 4, 4, 6)).isFalse() + ); + + } + + @Test + @DisplayName("제자리로는 이동할 수 없다.") + void cannotMoveSamePosition() { + // given + Gung gung = new Gung(); + + // when & then + assertThat(gung.canMove(4, 4, 4, 4)).isFalse(); + } +} From 02e3cc80fadc814f0a0fd0fa97d516ac985cce45 Mon Sep 17 00:00:00 2001 From: Sumin Date: Thu, 26 Mar 2026 18:06:04 +0900 Subject: [PATCH 18/99] =?UTF-8?q?feat:=20=EC=9E=A5=EA=B8=B0=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=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 --- README.md | 27 ++++-- src/main/java/janggi/JanggiRunner.java | 38 +++++--- src/main/java/janggi/domain/Board.java | 86 ++++++++++++++++++- src/main/java/janggi/domain/Delta.java | 8 ++ src/main/java/janggi/domain/MovePath.java | 64 ++++++++++++++ src/main/java/janggi/domain/Pieces.java | 82 ++++++++++-------- src/main/java/janggi/domain/Position.java | 30 ++++--- src/main/java/janggi/domain/Turn.java | 26 ++++++ src/main/java/janggi/domain/Turns.java | 34 +++++++- src/main/java/janggi/domain/piece/Cha.java | 39 ++++++++- src/main/java/janggi/domain/piece/Gung.java | 33 +++++-- src/main/java/janggi/domain/piece/Jol.java | 42 ++++----- src/main/java/janggi/domain/piece/Ma.java | 38 ++++++-- src/main/java/janggi/domain/piece/Piece.java | 18 +++- src/main/java/janggi/domain/piece/Po.java | 55 +++++++++++- src/main/java/janggi/domain/piece/Sa.java | 35 ++++++-- src/main/java/janggi/domain/piece/Sang.java | 37 ++++++-- src/main/java/janggi/domain/side/Chu.java | 14 ++- src/main/java/janggi/domain/side/Han.java | 14 ++- src/main/java/janggi/domain/side/Team.java | 8 +- .../java/janggi/util/ExceptionHandler.java | 24 ++++++ src/main/java/janggi/view/OutputView.java | 6 +- 22 files changed, 621 insertions(+), 137 deletions(-) create mode 100644 src/main/java/janggi/util/ExceptionHandler.java diff --git a/README.md b/README.md index 5b4b75f4b9..cdc150db50 100644 --- a/README.md +++ b/README.md @@ -136,15 +136,15 @@ ``` # 프로그램 흐름 -- [ ] 장기 게임 시작 문구 출력 (OutputView) -- [ ] 장기판 초기화 -- [ ] 장기판 출력(OutputView) +- [x] 장기 게임 시작 문구 출력 (OutputView) +- [x] 장기판 초기화 +- [x] 장기판 출력(OutputView) - [ ] 초, 한 번갈아가며 진행(Turn) - - [ ] 움직일 기물 좌표 입력(InputView - 콤마로 구분하여 좌표 입력) - - [ ] 입력값 검증 및 Position 객체 생성 - - [ ] 기물 반환 - - [ ] 장기판 내부 좌표인지 검증 - - [ ] 기물 반환 + - [x] 움직일 기물 좌표 입력(InputView - 콤마로 구분하여 좌표 입력) + - [x] 입력값 검증 및 Position 객체 생성 + - [x] 기물 반환 + - [x] 장기판 내부 좌표인지 검증 + - [x] 기물 반환 - [ ] 반환된 기물의 목적 좌표 입력 - [ ] 입력값 검증 및 Position 객체 생성 - [ ] 기물 이동 @@ -156,6 +156,17 @@ - [ ] 갱신된 장기판 출력(OutputView) - [ ] 진행 후 Turns에 Board 상태 추가 +# TODO +- Runner -> Board 참조를 Turns 참조로 변경 + 1. Runner에서 Position 직접 생성 x + 2. Piece 존재 여부 확인(Turns.isExist) x + - 마지막 턴.isPieceExist() x + 3. Piece 반환(보류) + 4. 목적 좌표 Position 생성 + 5. 이동(Turns.doGame(Position, Position)) + 6. 갱신된 보드판 출력 +- 이동 시 장애물이 있는 경우(canMove.isFalse()) 처리 + # 도메인 - Piece(interface - move) diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index d32abda746..270a3afec7 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -1,13 +1,14 @@ package janggi; import janggi.domain.Board; +import janggi.domain.Position; +import janggi.domain.Turn; import janggi.domain.Turns; -import janggi.domain.piece.Piece; import janggi.domain.side.TeamType; import janggi.util.DelimiterParser; +import janggi.util.ExceptionHandler; import janggi.view.InputView; import janggi.view.OutputView; -import java.util.ArrayList; import java.util.List; public class JanggiRunner { @@ -25,17 +26,30 @@ public void execute() { outputView.printStartMessage(); outputView.printBoard(board.makeSpots()); - Turns turns = new Turns(new ArrayList<>()); - TeamType nowTurn = turns.getFirstTurn(); - // + Turns turns = new Turns(List.of(new Turn(TeamType.HAN, board))); while (true) { - outputView.printTurnNotice(nowTurn.name()); - outputView.printAskPiecePosition(); - String rawPiecePosition = inputView.readLine(); - List parsedPiecePosition = DelimiterParser.parse(rawPiecePosition); - Piece piece = board.findPiece(parsedPiecePosition); - + Turns currentTurns = turns; + Position startPosition = ExceptionHandler.retryUntilSuccess(() -> readValidStartPosition(currentTurns)); + Position endPosition = ExceptionHandler.retryUntilSuccess(() -> readValidEndPosition(currentTurns, startPosition)); + turns = turns.doGame(startPosition, endPosition); } -// TeamType nowTurn = turns.getPlayTurn(); + } + + private Position readValidStartPosition(Turns turns) { + outputView.printAskPiecePosition(); + String rawPiecePosition = inputView.readLine(); + List parsedPiecePosition = DelimiterParser.parse(rawPiecePosition); + Position startPosition = Position.makePosition(parsedPiecePosition); + turns.validatePieceExist(startPosition); + return startPosition; + } + + private Position readValidEndPosition(Turns turns, Position startPosition) { + outputView.printAskMovePosition(turns.findPiece(startPosition).nickname()); + String rawMovePosition = inputView.readLine(); + List parsedMovePosition = DelimiterParser.parse(rawMovePosition); + Position endPosition = Position.makePosition(parsedMovePosition); + turns.validateValidEndPosition(startPosition, endPosition); + return endPosition; } } diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 22a6933b78..06c865fff7 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -4,6 +4,7 @@ import janggi.domain.side.Chu; import janggi.domain.side.Han; import janggi.domain.side.Team; +import janggi.domain.side.TeamType; import janggi.dto.BoardSpot; import java.util.ArrayList; import java.util.List; @@ -35,10 +36,15 @@ public List makeSpots() { return boardSpots; } - public Piece findPiece(List parsedPiecePosition) { - Position inputPosition = Position.makePosition(parsedPiecePosition); - validateRange(inputPosition); - return findPiece(inputPosition) + public boolean isMyTeamPieceExist(Position position, TeamType beforeTeam) { + validateRange(position); + Team nowTeam = opponentTeam(beforeTeam); + return nowTeam.findPiece(position).isPresent(); + } + + public Piece findTeamPiece(Position position, TeamType beforeTeam) { + Team nowTeam = opponentTeam(beforeTeam); + return nowTeam.findPiece(position) .orElseThrow(() -> new IllegalArgumentException("입력한 위치에 기물이 없습니다.")); } @@ -50,6 +56,78 @@ public Optional findPiece(Position position) { return han.findPiece(position); } + public boolean hasPiece(Position position) { + return findPiece(position).isPresent(); + } + + private Team currentTeam(TeamType nowTurn) { + if (nowTurn == TeamType.CHU) { + return chu; + } + return han; + } + + private Team opponentTeam(TeamType nowTurn) { + if (nowTurn == TeamType.CHU) { + return han; + } + return chu; + } + + public Board move( + Position startPosition, + Position endPosition, + TeamType nowTurn + ) { + validateRange(startPosition); + validateRange(endPosition); + Piece piece = findTeamPiece(startPosition, opponentTeam(nowTurn)); + validateTargetPosition(currentTeam(nowTurn), endPosition); + validateCanMove(piece, startPosition, endPosition); + Team movedCurrentTeam = currentTeam(nowTurn).move(startPosition, endPosition); + Team remainedOpponentTeam = removeOpponentPiece(nowTurn, endPosition); + return createMovedBoard(nowTurn, movedCurrentTeam, remainedOpponentTeam); + } + + public void canMove(Position startPosition, Position endPosition, TeamType beforeTeam) { + validateRange(startPosition); + validateRange(endPosition); + Piece piece = findTeamPiece(startPosition, beforeTeam); + validateTargetPosition(opponentTeam(beforeTeam), endPosition); + validateCanMove(piece, startPosition, endPosition); + } + + private void validateCanMove(Piece piece, Position piecePosition, Position targetPosition) { + if (!piece.isValidPath(piecePosition, targetPosition, this)) { + throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + } + } + + private void validateTargetPosition(TeamType nowTurn, Position targetPosition) { + validateTargetPosition(currentTeam(nowTurn), targetPosition); + } + + private void validateTargetPosition(Team team, Position targetPosition) { + if (team.findPiece(targetPosition).isPresent()) { + throw new IllegalArgumentException("같은 팀의 기물이 있는 위치로는 이동할 수 없습니다."); + } + } + + private Team removeOpponentPiece(TeamType nowTurn, Position targetPosition) { + Team opponentTeam = opponentTeam(nowTurn); + if (opponentTeam.findPiece(targetPosition).isEmpty()) { + return opponentTeam; + } + return opponentTeam.remove(targetPosition); + } + + private Board createMovedBoard(TeamType nowTurn, Team movedCurrentTeam, Team remainedOpponentTeam) { + if (nowTurn == TeamType.CHU) { + return new Board(movedCurrentTeam, remainedOpponentTeam); + } + return new Board(remainedOpponentTeam, movedCurrentTeam); + } + private void validateRange(Position inputPosition) { int x = inputPosition.getX(); int y = inputPosition.getY(); diff --git a/src/main/java/janggi/domain/Delta.java b/src/main/java/janggi/domain/Delta.java index db2eb0e783..edf9e2494e 100644 --- a/src/main/java/janggi/domain/Delta.java +++ b/src/main/java/janggi/domain/Delta.java @@ -41,4 +41,12 @@ public static Delta createLeftDown() { public static Delta createRightDown() { return new Delta(1, -1); } + + public int dx() { + return dx; + } + + public int dy() { + return dy; + } } diff --git a/src/main/java/janggi/domain/MovePath.java b/src/main/java/janggi/domain/MovePath.java index 8268120406..dae4440440 100644 --- a/src/main/java/janggi/domain/MovePath.java +++ b/src/main/java/janggi/domain/MovePath.java @@ -1,5 +1,6 @@ package janggi.domain; +import java.util.ArrayList; import java.util.List; public class MovePath { @@ -9,4 +10,67 @@ public class MovePath { public MovePath(List path) { this.path = path; } + + public boolean matches(int dx, int dy) { + return totalDx() == dx && totalDy() == dy; + } + + public boolean matchesDirection(int dx, int dy) { + if (path.size() != 1) { + return false; + } + Delta delta = path.getFirst(); + if (delta.dx() == 0) { + return dx == 0 && Integer.signum(dy) == Integer.signum(delta.dy()) && dy != 0; + } + if (delta.dy() == 0) { + return dy == 0 && Integer.signum(dx) == Integer.signum(delta.dx()) && dx != 0; + } + return Math.abs(dx) == Math.abs(dy) + && Integer.signum(dx) == Integer.signum(delta.dx()) + && Integer.signum(dy) == Integer.signum(delta.dy()); + } + + private int totalDx() { + return path.stream() + .mapToInt(Delta::dx) + .sum(); + } + + private int totalDy() { + return path.stream() + .mapToInt(Delta::dy) + .sum(); + } + + public List createRoute(Position start, Position end) { + List route = new ArrayList<>(); + Position current = start; + if (path.size() == 1) { + return createStraightRoute(end, route, current); + } + for (Delta delta : path) { + current = current.move(delta); + route.add(current); + } + return route; + } + + public List intermediatePositions(Position start, Position end) { + List route = createRoute(start, end); + if (route.isEmpty()) { + return route; + } + route.removeLast(); + return route; + } + + private List createStraightRoute(Position end, List route, Position current) { + Delta delta = path.getFirst(); + while (!current.equals(end)) { + current = current.move(delta); + route.add(current); + } + return route; + } } diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index 0c838884ee..ab057bd6c7 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -24,67 +24,72 @@ private Pieces(Map value) { this.value = value; } - public Pieces move(int startX, int startY, int endX, int endY) { - Piece piece = value.get(new Position(startX, startY)); - if (piece.canMove(startX, startY, endX, endY)) { - // TODO: move하기 - // 기존 좌표 삭제 + 새 좌표 삽입 - } - return new Pieces(new HashMap<>(value)); + public Pieces move(Position piecePosition, Position targetPosition) { + Piece piece = value.get(piecePosition); + Map updatedValue = new HashMap<>(value); + updatedValue.remove(piecePosition); + updatedValue.put(targetPosition, piece); + return new Pieces(updatedValue); + } + + public Pieces remove(Position position) { + Map updatedValue = new HashMap<>(value); + updatedValue.remove(position); + return new Pieces(updatedValue); } public static Pieces createHan() { Map pieces = new HashMap<>(); - createChas(pieces, 10); - createMas(pieces, 10); - createSangs(pieces, 10); - createSas(pieces, 10); - createGung(pieces, 9); - createPos(pieces, 8); + createChas(pieces, 10, TeamType.HAN); + createMas(pieces, 10, TeamType.HAN); + createSangs(pieces, 10, TeamType.HAN); + createSas(pieces, 10, TeamType.HAN); + createGung(pieces, 9, TeamType.HAN); + createPos(pieces, 8, TeamType.HAN); createJols(pieces, 7, TeamType.HAN); return new Pieces(pieces); } public static Pieces createChu() { Map pieces = new HashMap<>(); - createChas(pieces, 1); - createMas(pieces, 1); - createSangs(pieces, 1); - createSas(pieces, 1); - createGung(pieces, 2); - createPos(pieces, 3); + createChas(pieces, 1, TeamType.CHU); + createMas(pieces, 1, TeamType.CHU); + createSangs(pieces, 1, TeamType.CHU); + createSas(pieces, 1, TeamType.CHU); + createGung(pieces, 2, TeamType.CHU); + createPos(pieces, 3, TeamType.CHU); createJols(pieces, 4, TeamType.CHU); return new Pieces(pieces); } - private static void createChas(Map pieces, int indexY) { - pieces.put(new Position(1, indexY), new Cha()); - pieces.put(new Position(9, indexY), new Cha()); + private static void createChas(Map pieces, int indexY, TeamType teamType) { + pieces.put(new Position(1, indexY), new Cha(teamType)); + pieces.put(new Position(9, indexY), new Cha(teamType)); } - private static void createMas(Map pieces, int indexY) { - pieces.put(new Position(2, indexY), new Ma()); - pieces.put(new Position(8, indexY), new Ma()); + private static void createMas(Map pieces, int indexY, TeamType teamType) { + pieces.put(new Position(2, indexY), new Ma(teamType)); + pieces.put(new Position(8, indexY), new Ma(teamType)); } - private static void createSangs(Map pieces, int indexY) { - pieces.put(new Position(7, indexY), new Sang()); - pieces.put(new Position(10 - 7, indexY), new Sang()); + private static void createSangs(Map pieces, int indexY, TeamType teamType) { + pieces.put(new Position(7, indexY), new Sang(teamType)); + pieces.put(new Position(10 - 7, indexY), new Sang(teamType)); } - private static void createSas(Map pieces, int indexY) { - pieces.put(new Position(4, indexY), new Sa()); - pieces.put(new Position(10 - 4, indexY), new Sa()); + private static void createSas(Map pieces, int indexY, TeamType teamType) { + pieces.put(new Position(4, indexY), new Sa(teamType)); + pieces.put(new Position(10 - 4, indexY), new Sa(teamType)); } - private static void createGung(Map pieces, int indexY) { - pieces.put(new Position(5, indexY), new Gung()); + private static void createGung(Map pieces, int indexY, TeamType teamType) { + pieces.put(new Position(5, indexY), new Gung(teamType)); } - private static void createPos(Map pieces, int indexY) { - pieces.put(new Position(2, indexY), new Po()); - pieces.put(new Position(10 - 2, indexY), new Po()); + private static void createPos(Map pieces, int indexY, TeamType teamType) { + pieces.put(new Position(2, indexY), new Po(teamType)); + pieces.put(new Position(10 - 2, indexY), new Po(teamType)); } private static void createJols(Map pieces, int indexY, TeamType teamType) { @@ -112,4 +117,9 @@ public List makeSpots() { public Optional findPiece(Position position) { return Optional.ofNullable(value.get(position)); } + + public void checkPieceCanMove(Position startPosition, Position endPosition) { + Piece piece = value.get(startPosition); + piece.isValidMovePattern(startPosition.getX(), startPosition.getY(), endPosition.getX(), endPosition.getY()); + } } diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java index d90ed30ecf..c47b043fce 100644 --- a/src/main/java/janggi/domain/Position.java +++ b/src/main/java/janggi/domain/Position.java @@ -9,19 +9,6 @@ public class Position { private final int x; private final int y; - public Position(int x, int y) { - this.x = x; - this.y = y; - } - - public int getX() { - return x; - } - - public int getY() { - return y; - } - public static Position makePosition(List parsedPiecePosition) { if (parsedPiecePosition.size() != POSITION_SIZE) { throw new IllegalArgumentException("기물의 좌표는 두 개로 입력해야 합니다."); @@ -35,10 +22,27 @@ public static Position makePosition(List parsedPiecePosition) { } } + public Position(int x, int y) { + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + public String makePositionKey() { return x + "," + y; } + public Position move(Delta delta) { + return new Position(x + delta.dx(), y + delta.dy()); + } + @Override public boolean equals(Object object) { if (this == object) { diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index d792b3bae1..41fe1e3cef 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -1,5 +1,6 @@ package janggi.domain; +import janggi.domain.piece.Piece; import janggi.domain.side.TeamType; public class Turn { @@ -11,4 +12,29 @@ public Turn(TeamType movedTeam, Board board) { this.movedTeam = movedTeam; this.board = board; } + + public boolean isMyTeamPieceExist(Position position) { + return board.isMyTeamPieceExist(position, movedTeam); + } + + public Piece findPiece(Position position) { + return board.findTeamPiece(position, movedTeam); + } + + public Turn move(Position startPosition, Position endPosition) { + TeamType nowTurn = opponentTeamType(); + Board movedBoard = board.move(startPosition, endPosition, nowTurn); + return new Turn(nowTurn, movedBoard); + } + + public void canMove(Position startPosition, Position endPosition) { + board.canMove(startPosition, endPosition, movedTeam); + } + + private TeamType opponentTeamType() { + if (movedTeam == TeamType.CHU) { + return TeamType.HAN; + } + return TeamType.CHU; + } } diff --git a/src/main/java/janggi/domain/Turns.java b/src/main/java/janggi/domain/Turns.java index f490a47dcb..46ccb5938d 100644 --- a/src/main/java/janggi/domain/Turns.java +++ b/src/main/java/janggi/domain/Turns.java @@ -1,6 +1,7 @@ package janggi.domain; -import janggi.domain.side.TeamType; +import janggi.domain.piece.Piece; +import java.util.ArrayList; import java.util.List; public class Turns { @@ -11,7 +12,34 @@ public Turns(List value) { this.value = value; } - public TeamType getFirstTurn() { - return TeamType.CHU; + public void validatePieceExist(Position position) { + Turn lastTurn = getLastTurn(); + if (!lastTurn.isMyTeamPieceExist(position)) { + throw new IllegalArgumentException("기물이 존재하는 좌표가 아닙니다."); + } + } + + public void validateValidEndPosition(Position startPosition, Position endPosition) { + Turn lastTurn = getLastTurn(); + if (lastTurn.isMyTeamPieceExist(endPosition)) { + throw new IllegalArgumentException("아군이 존재하는 좌표로 이동할 수 없습니다."); + } + lastTurn.canMove(startPosition, endPosition); + } + + public Piece findPiece(Position position) { + return getLastTurn().findPiece(position); + } + + private Turn getLastTurn() { + return value.getLast(); + } + + public Turns doGame(Position startPosition, Position endPosition) { + Turn lastTurn = getLastTurn(); + Turn newTurn = lastTurn.move(startPosition, endPosition); + List updatedTurns = new ArrayList<>(value); + updatedTurns.add(newTurn); + return new Turns(updatedTurns); } } diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 42dc8fe36f..cc3b41d0c6 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -1,15 +1,21 @@ package janggi.domain.piece; +import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; +import janggi.domain.Position; +import janggi.domain.side.TeamType; import java.util.List; +import java.util.Optional; public class Cha implements Piece { + private final TeamType teamType; private final PieceType pieceType; private final List paths; - public Cha() { + public Cha(TeamType teamType) { + this.teamType = teamType; pieceType = PieceType.CHA; paths = List.of( new MovePath(List.of(Delta.createUp())), @@ -21,11 +27,33 @@ public Cha() { // TODO: start 좌표는 유효한게 보장 되어 있는지? Pieces에서 Map 조회 후 반환하기. @Override - public boolean canMove(int startX, int startY, int endX, int endY) { + public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { + return findMovePath(startX, startY, endX, endY).isPresent(); + } + + @Override + public Optional findMovePath(int startX, int startY, int endX, int endY) { if (isSamePosition(startX, startY, endX, endY)) { + return Optional.empty(); + } + if (!isStraightDirection(startX, startY, endX, endY)) { + return Optional.empty(); + } + int dx = endX - startX; + int dy = endY - startY; + return paths.stream() + .filter(path -> path.matchesDirection(dx, dy)) + .findFirst(); + } + + @Override + public boolean isValidPath(Position start, Position end, Board board) { + Optional movePath = findMovePath(start.getX(), start.getY(), end.getX(), end.getY()); + if (movePath.isEmpty()) { return false; } - return isStraightDirection(startX, startY, endX, endY); + return movePath.get().intermediatePositions(start, end).stream() + .noneMatch(board::hasPiece); } private boolean isSamePosition(int startX, int startY, int endX, int endY) { @@ -40,4 +68,9 @@ private boolean isStraightDirection(int startX, int startY, int endX, int endY) public String nickname() { return pieceType.getNickname(); } + + @Override + public boolean isSameType(TeamType nowTurn) { + return nowTurn == teamType; + } } diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index ef1a6a0daf..361f2aa2b4 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -2,14 +2,18 @@ import janggi.domain.Delta; import janggi.domain.MovePath; +import janggi.domain.side.TeamType; import java.util.List; +import java.util.Optional; public class Gung implements Piece { + private final TeamType teamType; private final PieceType pieceType; private final List paths; - public Gung() { + public Gung(TeamType teamType) { + this.teamType = teamType; pieceType = PieceType.GUNG; paths = List.of( new MovePath(List.of(Delta.createUp())), @@ -24,13 +28,25 @@ public Gung() { } @Override - public boolean canMove(int startX, int startY, int endX, int endY) { - int distanceX = Math.abs(endX - startX); - int distanceY = Math.abs(endY - startY); + public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { + return findMovePath(startX, startY, endX, endY).isPresent(); + } + + @Override + public Optional findMovePath(int startX, int startY, int endX, int endY) { + int dx = endX - startX; + int dy = endY - startY; + int distanceX = Math.abs(dx); + int distanceY = Math.abs(dy); if (isSamePosition(distanceX, distanceY)) { - return false; + return Optional.empty(); + } + if (!isOneStep(distanceX, distanceY)) { + return Optional.empty(); } - return isOneStep(distanceX, distanceY); + return paths.stream() + .filter(path -> path.matches(dx, dy)) + .findFirst(); } private boolean isSamePosition(int distanceX, int distanceY) { @@ -45,4 +61,9 @@ private boolean isOneStep(int distanceX, int distanceY) { public String nickname() { return pieceType.getNickname(); } + + @Override + public boolean isSameType(TeamType nowTurn) { + return teamType == nowTurn; + } } diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 9a79cf08a8..0fa6375e92 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -4,6 +4,7 @@ import janggi.domain.MovePath; import janggi.domain.side.TeamType; import java.util.List; +import java.util.Optional; public class Jol implements Piece { @@ -18,36 +19,26 @@ public Jol(TeamType teamType) { } @Override - public boolean canMove(int startX, int startY, int endX, int endY) { - int distanceX = endX - startX; - int distanceY = endY - startY; - if (isSamePosition(distanceX, distanceY)) { - return false; - } - if (isHorizontalMove(distanceX, distanceY)) { - return true; + public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { + return findMovePath(startX, startY, endX, endY).isPresent(); + } + + @Override + public Optional findMovePath(int startX, int startY, int endX, int endY) { + int dx = endX - startX; + int dy = endY - startY; + if (isSamePosition(dx, dy)) { + return Optional.empty(); } - return isForwardMove(distanceX, distanceY); + return paths.stream() + .filter(path -> path.matches(dx, dy)) + .findFirst(); } private boolean isSamePosition(int distanceX, int distanceY) { return distanceX == 0 && distanceY == 0; } - private boolean isHorizontalMove(int distanceX, int distanceY) { - return Math.abs(distanceX) == 1 && distanceY == 0; - } - - private boolean isForwardMove(int distanceX, int distanceY) { - if (distanceX != 0) { - return false; - } - if (teamType == TeamType.HAN) { - return distanceY == -1; - } - return distanceY == 1; - } - private List createPaths() { if (teamType == TeamType.HAN) { return List.of( @@ -67,4 +58,9 @@ private List createPaths() { public String nickname() { return pieceType.getNickname(); } + + @Override + public boolean isSameType(TeamType nowTurn) { + return teamType == nowTurn; + } } diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 8e9eb4bf5c..8998654eb8 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -1,15 +1,21 @@ package janggi.domain.piece; +import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; +import janggi.domain.Position; +import janggi.domain.side.TeamType; import java.util.List; +import java.util.Optional; public class Ma implements Piece { + private final TeamType teamType; private final PieceType pieceType; private final List paths; - public Ma() { + public Ma(TeamType teamType) { + this.teamType = teamType; pieceType = PieceType.MA; this.paths = List.of( new MovePath(List.of(Delta.createUp(), Delta.createRightUp())), @@ -24,16 +30,36 @@ public Ma() { } @Override - public boolean canMove(int startX, int startY, int endX, int endY) { - int distanceX = Math.abs(endX - startX); - int distanceY = Math.abs(endY - startY); + public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { + return findMovePath(startX, startY, endX, endY).isPresent(); + } + + @Override + public Optional findMovePath(int startX, int startY, int endX, int endY) { + int dx = endX - startX; + int dy = endY - startY; + return paths.stream() + .filter(path -> path.matches(dx, dy)) + .findFirst(); + } - // 마의 이동 거리 공식: (X가 1칸이면 Y는 2칸) 또는 (X가 2칸이면 Y는 1칸) - return (distanceX == 1 && distanceY == 2) || (distanceX == 2 && distanceY == 1); + @Override + public boolean isValidPath(Position start, Position end, Board board) { + Optional movePath = findMovePath(start.getX(), start.getY(), end.getX(), end.getY()); + if (movePath.isEmpty()) { + return false; + } + return movePath.get().intermediatePositions(start, end).stream() + .noneMatch(board::hasPiece); } @Override public String nickname() { return pieceType.getNickname(); } + + @Override + public boolean isSameType(TeamType nowTurn) { + return teamType == nowTurn; + } } diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index aa82108439..04b50b3a25 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -1,8 +1,24 @@ package janggi.domain.piece; +import janggi.domain.Board; +import janggi.domain.MovePath; +import janggi.domain.Position; +import janggi.domain.side.TeamType; +import java.util.Optional; + public interface Piece { - boolean canMove(int startX, int startY, int endX, int endY); + boolean isValidMovePattern(int startX, int startY, int endX, int endY); + + Optional findMovePath(int startX, int startY, int endX, int endY); + + default boolean isValidPath(Position start, Position end, Board board) { + return findMovePath(start.getX(), start.getY(), end.getX(), end.getY()).isPresent(); + } String nickname(); + + boolean isSameType(TeamType nowTurn); + + PieceType getPieceType(); } diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index 1bd64f7873..dfdb8a8c7d 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -1,15 +1,21 @@ package janggi.domain.piece; +import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; +import janggi.domain.Position; +import janggi.domain.side.TeamType; import java.util.List; +import java.util.Optional; public class Po implements Piece { + private final TeamType teamType; private final PieceType pieceType; private final List paths; - public Po() { + public Po(TeamType teamType) { + this.teamType = teamType; pieceType = PieceType.PO; paths = List.of( new MovePath(List.of(Delta.createUp())), @@ -21,15 +27,56 @@ public Po() { // TODO: Pieces에서 사이에 기물이 없다면 애초에 호출하지 않음. Pieces에 Po를 움직일 경우 중간에 좌표가 있는지 확인하는 로직이 필요함. @Override - public boolean canMove(int startX, int startY, int endX, int endY) { - if (startX != endX && startY != endY) { + public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { + return findMovePath(startX, startY, endX, endY).isPresent(); + } + + @Override + public Optional findMovePath(int startX, int startY, int endX, int endY) { + if (startX == endX && startY == endY) { + return Optional.empty(); + } + int dx = endX - startX; + int dy = endY - startY; + return paths.stream() + .filter(path -> path.matchesDirection(dx, dy)) + .findFirst(); + } + + @Override + public boolean isValidPath(Position start, Position end, Board board) { + Optional movePath = findMovePath(start.getX(), start.getY(), end.getX(), end.getY()); + if (movePath.isEmpty()) { + return false; + } + List intermediatePositions = movePath.get().intermediatePositions(start, end); + List obstacles = intermediatePositions.stream() + .map(board::findPiece) + .filter(Optional::isPresent) + .map(Optional::get) + .toList(); + if (obstacles.size() != 1) { return false; } - return startX != endX || startY != endY; + if (obstacles.getFirst().getPieceType() == PieceType.PO) { + return false; + } + Optional targetPiece = board.findPiece(end); + return targetPiece.isEmpty() || !(targetPiece.get().getPieceType() == PieceType.PO); } @Override public String nickname() { return pieceType.getNickname(); } + + @Override + public boolean isSameType(TeamType nowTurn) { + return nowTurn == teamType; + } + + @Override + public PieceType getPieceType() { + return pieceType; + } } diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index 4b7301a257..b1e9ed2a81 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -2,14 +2,18 @@ import janggi.domain.Delta; import janggi.domain.MovePath; +import janggi.domain.side.TeamType; import java.util.List; +import java.util.Optional; -public class Sa implements Piece{ +public class Sa implements Piece { + private final TeamType teamType; private final PieceType pieceType; private final List paths; - public Sa() { + public Sa(TeamType teamType) { + this.teamType = teamType; pieceType = PieceType.SA; paths = List.of( new MovePath(List.of(Delta.createUp())), @@ -24,13 +28,25 @@ public Sa() { } @Override - public boolean canMove(int startX, int startY, int endX, int endY) { - int distanceX = Math.abs(endX - startX); - int distanceY = Math.abs(endY - startY); + public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { + return findMovePath(startX, startY, endX, endY).isPresent(); + } + + @Override + public Optional findMovePath(int startX, int startY, int endX, int endY) { + int dx = endX - startX; + int dy = endY - startY; + int distanceX = Math.abs(dx); + int distanceY = Math.abs(dy); if (isSamePosition(distanceX, distanceY)) { - return false; + return Optional.empty(); + } + if (!isOneStep(distanceX, distanceY)) { + return Optional.empty(); } - return isOneStep(distanceX, distanceY); + return paths.stream() + .filter(path -> path.matches(dx, dy)) + .findFirst(); } private boolean isSamePosition(int distanceX, int distanceY) { @@ -45,4 +61,9 @@ private boolean isOneStep(int distanceX, int distanceY) { public String nickname() { return pieceType.getNickname(); } + + @Override + public boolean isSameType(TeamType nowTurn) { + return nowTurn == teamType; + } } diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index f769c92b9d..c4cb501ce5 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -1,15 +1,21 @@ package janggi.domain.piece; +import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; +import janggi.domain.Position; +import janggi.domain.side.TeamType; import java.util.List; +import java.util.Optional; public class Sang implements Piece { + private final TeamType teamType; private final PieceType pieceType; private final List paths; - public Sang() { + public Sang(TeamType teamType) { + this.teamType = teamType; pieceType = PieceType.SANG; paths = List.of( new MovePath(List.of(Delta.createUp(), Delta.createRightUp(), Delta.createRightUp())), @@ -24,15 +30,36 @@ public Sang() { } @Override - public boolean canMove(int startX, int startY, int endX, int endY) { - int distanceX = Math.abs(endX - startX); - int distanceY = Math.abs(endY - startY); + public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { + return findMovePath(startX, startY, endX, endY).isPresent(); + } + + @Override + public Optional findMovePath(int startX, int startY, int endX, int endY) { + int dx = endX - startX; + int dy = endY - startY; + return paths.stream() + .filter(path -> path.matches(dx, dy)) + .findFirst(); + } - return (distanceX == 2 && distanceY == 3) || (distanceX == 3 && distanceY == 2); + @Override + public boolean isValidPath(Position start, Position end, Board board) { + Optional movePath = findMovePath(start.getX(), start.getY(), end.getX(), end.getY()); + if (movePath.isEmpty()) { + return false; + } + return movePath.get().intermediatePositions(start, end).stream() + .noneMatch(board::hasPiece); } @Override public String nickname() { return pieceType.getNickname(); } + + @Override + public boolean isSameType(TeamType nowTurn) { + return nowTurn == teamType; + } } diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index 3f525378a5..c45e7fb8f3 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -35,8 +35,18 @@ public Optional findPiece(Position position) { } @Override - public Team move(int startX, int startY, int endX, int endY) { - return new Chu(pieces.move(startX, startY, endX, endY)); + public Team remove(Position position) { + return new Chu(pieces.remove(position)); + } + + @Override + public Team move(Position piecePosition, Position targetPosition) { + return new Chu(pieces.move(piecePosition, targetPosition)); + } + + @Override + public void isPieceCanMove(Position startPosition, Position endPosition) { + pieces.checkPieceCanMove(startPosition, endPosition); } // TODO: 구현하기 diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index 0598a3852e..38b238f45b 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -35,8 +35,18 @@ public Optional findPiece(Position position) { } @Override - public Team move(int startX, int startY, int endX, int endY) { - return null; + public Team remove(Position position) { + return new Han(pieces.remove(position)); + } + + @Override + public Team move(Position piecePosition, Position targetPosition) { + return new Han(pieces.move(piecePosition, targetPosition)); + } + + @Override + public void isPieceCanMove(Position startPosition, Position endPosition) { + pieces.checkPieceCanMove(startPosition, endPosition); } // TODO: 구현하기 diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index cf1b138cbe..b20b774402 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -14,5 +14,11 @@ public interface Team { Optional findPiece(Position position); - Team move(int startX, int startY, int endX, int endY); + Team remove(Position position); + + Team move(Position piecePosition, Position targetPosition); + + void isPieceCanMove(Position startPosition, Position endPosition); + + } diff --git a/src/main/java/janggi/util/ExceptionHandler.java b/src/main/java/janggi/util/ExceptionHandler.java new file mode 100644 index 0000000000..7ee81fe2ea --- /dev/null +++ b/src/main/java/janggi/util/ExceptionHandler.java @@ -0,0 +1,24 @@ +package janggi.util; + +import janggi.view.OutputView; +import java.util.function.Supplier; + +public class ExceptionHandler { + + public static T retryUntilSuccess(Supplier supplier) { + while (true) { + try { + return supplier.get(); + } catch (IllegalArgumentException e) { + OutputView.printMessage(e.getMessage()); + } + } + } + + public static void retryUntilSuccess(Runnable runnable) { + retryUntilSuccess(() -> { + runnable.run(); + return null; + }); + } +} diff --git a/src/main/java/janggi/view/OutputView.java b/src/main/java/janggi/view/OutputView.java index 6fc9301f46..1eafdfd0d8 100644 --- a/src/main/java/janggi/view/OutputView.java +++ b/src/main/java/janggi/view/OutputView.java @@ -7,7 +7,7 @@ public class OutputView { - public void printMessage(String message) { + public static void printMessage(String message) { System.out.println(message); } @@ -76,4 +76,8 @@ public void printTurnNotice(String nowTurn) { public void printAskPiecePosition() { printMessage("움직일 기물의 좌표를 입력해주세요."); } + + public void printAskMovePosition(String nickname) { + printMessage(nickname + "의 목적 좌표를 입력해주세요."); + } } From af928a65a306a26d1cc683f02c8e5118f0a5a6a1 Mon Sep 17 00:00:00 2001 From: Sumin Date: Thu, 26 Mar 2026 18:11:01 +0900 Subject: [PATCH 19/99] =?UTF-8?q?feat:=20=EC=9E=A5=EA=B8=B0=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EB=B0=98=ED=99=98=20=EA=B5=AC=ED=98=84=EB=B6=80=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 --- README.md | 2 +- src/main/java/janggi/domain/piece/Cha.java | 5 +++++ src/main/java/janggi/domain/piece/Gung.java | 5 +++++ src/main/java/janggi/domain/piece/Jol.java | 5 +++++ src/main/java/janggi/domain/piece/Ma.java | 5 +++++ src/main/java/janggi/domain/piece/Sa.java | 5 +++++ src/main/java/janggi/domain/piece/Sang.java | 5 +++++ 7 files changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cdc150db50..80d6972648 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ - Position - int x, int y - Board - - Side han, Side cho + - Side han, Side chu - JanggiRunner - InputView - OutputView diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index cc3b41d0c6..4cdc7a373d 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -73,4 +73,9 @@ public String nickname() { public boolean isSameType(TeamType nowTurn) { return nowTurn == teamType; } + + @Override + public PieceType getPieceType() { + return pieceType; + } } diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index 361f2aa2b4..bd647744b6 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -66,4 +66,9 @@ public String nickname() { public boolean isSameType(TeamType nowTurn) { return teamType == nowTurn; } + + @Override + public PieceType getPieceType() { + return pieceType; + } } diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 0fa6375e92..45749a8e54 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -63,4 +63,9 @@ public String nickname() { public boolean isSameType(TeamType nowTurn) { return teamType == nowTurn; } + + @Override + public PieceType getPieceType() { + return pieceType; + } } diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 8998654eb8..1388ba5e15 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -62,4 +62,9 @@ public String nickname() { public boolean isSameType(TeamType nowTurn) { return teamType == nowTurn; } + + @Override + public PieceType getPieceType() { + return pieceType; + } } diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index b1e9ed2a81..36e75bd547 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -66,4 +66,9 @@ public String nickname() { public boolean isSameType(TeamType nowTurn) { return nowTurn == teamType; } + + @Override + public PieceType getPieceType() { + return pieceType; + } } diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index c4cb501ce5..6cb569fa3b 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -62,4 +62,9 @@ public String nickname() { public boolean isSameType(TeamType nowTurn) { return nowTurn == teamType; } + + @Override + public PieceType getPieceType() { + return pieceType; + } } From c5900f0ae638795f6309001c493ea929bab6fcb7 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Fri, 27 Mar 2026 15:02:43 +0900 Subject: [PATCH 20/99] =?UTF-8?q?refactor:=20findTeamPiece=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Board.java | 14 +++++++++----- src/main/java/janggi/domain/Turn.java | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 06c865fff7..311b329f83 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -42,8 +42,12 @@ public boolean isMyTeamPieceExist(Position position, TeamType beforeTeam) { return nowTeam.findPiece(position).isPresent(); } - public Piece findTeamPiece(Position position, TeamType beforeTeam) { - Team nowTeam = opponentTeam(beforeTeam); + public Piece findNextTurnTeamPiece(Position position, TeamType beforeTeamType) { + Team nowTeam = opponentTeam(beforeTeamType); + return findTeamPiece(position, nowTeam); + } + + private Piece findTeamPiece(Position position, Team nowTeam) { return nowTeam.findPiece(position) .orElseThrow(() -> new IllegalArgumentException("입력한 위치에 기물이 없습니다.")); } @@ -89,11 +93,11 @@ public Board move( return createMovedBoard(nowTurn, movedCurrentTeam, remainedOpponentTeam); } - public void canMove(Position startPosition, Position endPosition, TeamType beforeTeam) { + public void canMove(Position startPosition, Position endPosition, TeamType nowTeam) { validateRange(startPosition); validateRange(endPosition); - Piece piece = findTeamPiece(startPosition, beforeTeam); - validateTargetPosition(opponentTeam(beforeTeam), endPosition); + Piece piece = findTeamPiece(startPosition, currentTeam(nowTeam)); + validateTargetPosition(currentTeam(nowTeam), endPosition); validateCanMove(piece, startPosition, endPosition); } diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index 41fe1e3cef..c6118a91e2 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -18,7 +18,7 @@ public boolean isMyTeamPieceExist(Position position) { } public Piece findPiece(Position position) { - return board.findTeamPiece(position, movedTeam); + return board.findNextTurnTeamPiece(position, movedTeam); } public Turn move(Position startPosition, Position endPosition) { @@ -28,7 +28,7 @@ public Turn move(Position startPosition, Position endPosition) { } public void canMove(Position startPosition, Position endPosition) { - board.canMove(startPosition, endPosition, movedTeam); + board.canMove(startPosition, endPosition, opponentTeamType()); } private TeamType opponentTeamType() { From 57b9d5572650969de52352bc1027b1c18bb7a042 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Fri, 27 Mar 2026 15:07:08 +0900 Subject: [PATCH 21/99] =?UTF-8?q?refactor:=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20Turns=20->=20JanggiGame=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/janggi/domain/{Turns.java => JanggiGame.java} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename src/main/java/janggi/domain/{Turns.java => JanggiGame.java} (86%) diff --git a/src/main/java/janggi/domain/Turns.java b/src/main/java/janggi/domain/JanggiGame.java similarity index 86% rename from src/main/java/janggi/domain/Turns.java rename to src/main/java/janggi/domain/JanggiGame.java index 46ccb5938d..af7548c438 100644 --- a/src/main/java/janggi/domain/Turns.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -4,11 +4,11 @@ import java.util.ArrayList; import java.util.List; -public class Turns { +public class JanggiGame { private final List value; - public Turns(List value) { + public JanggiGame(List value) { this.value = value; } @@ -35,11 +35,11 @@ private Turn getLastTurn() { return value.getLast(); } - public Turns doGame(Position startPosition, Position endPosition) { + public JanggiGame doGame(Position startPosition, Position endPosition) { Turn lastTurn = getLastTurn(); Turn newTurn = lastTurn.move(startPosition, endPosition); List updatedTurns = new ArrayList<>(value); updatedTurns.add(newTurn); - return new Turns(updatedTurns); + return new JanggiGame(updatedTurns); } } From 9e4a13604560421da04688a880d85dcb99646f44 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Fri, 27 Mar 2026 15:08:29 +0900 Subject: [PATCH 22/99] =?UTF-8?q?refactor:=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20Turns=20->=20JanggiGame=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/JanggiRunner.java | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index 270a3afec7..ef9f38f25d 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -3,7 +3,7 @@ import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.Turn; -import janggi.domain.Turns; +import janggi.domain.JanggiGame; import janggi.domain.side.TeamType; import janggi.util.DelimiterParser; import janggi.util.ExceptionHandler; @@ -26,30 +26,30 @@ public void execute() { outputView.printStartMessage(); outputView.printBoard(board.makeSpots()); - Turns turns = new Turns(List.of(new Turn(TeamType.HAN, board))); + JanggiGame janggiGame = new JanggiGame(List.of(new Turn(TeamType.HAN, board))); // 여기서 Turn 객체를 Turns 생성자 내부에서 생성해주는게 나을 듯? while (true) { - Turns currentTurns = turns; - Position startPosition = ExceptionHandler.retryUntilSuccess(() -> readValidStartPosition(currentTurns)); - Position endPosition = ExceptionHandler.retryUntilSuccess(() -> readValidEndPosition(currentTurns, startPosition)); - turns = turns.doGame(startPosition, endPosition); + JanggiGame currentJanggiGame = janggiGame; + Position startPosition = ExceptionHandler.retryUntilSuccess(() -> readValidStartPosition(currentJanggiGame)); + Position endPosition = ExceptionHandler.retryUntilSuccess(() -> readValidEndPosition(currentJanggiGame, startPosition)); + janggiGame = janggiGame.doGame(startPosition, endPosition); } } - private Position readValidStartPosition(Turns turns) { + private Position readValidStartPosition(JanggiGame janggiGame) { outputView.printAskPiecePosition(); String rawPiecePosition = inputView.readLine(); List parsedPiecePosition = DelimiterParser.parse(rawPiecePosition); Position startPosition = Position.makePosition(parsedPiecePosition); - turns.validatePieceExist(startPosition); + janggiGame.validatePieceExist(startPosition); return startPosition; } - private Position readValidEndPosition(Turns turns, Position startPosition) { - outputView.printAskMovePosition(turns.findPiece(startPosition).nickname()); + private Position readValidEndPosition(JanggiGame janggiGame, Position startPosition) { + outputView.printAskMovePosition(janggiGame.findPiece(startPosition).nickname()); String rawMovePosition = inputView.readLine(); List parsedMovePosition = DelimiterParser.parse(rawMovePosition); Position endPosition = Position.makePosition(parsedMovePosition); - turns.validateValidEndPosition(startPosition, endPosition); + janggiGame.validateValidEndPosition(startPosition, endPosition); return endPosition; } } From 829e6853148761b98254f45a131c54aa45f943ca Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Fri, 27 Mar 2026 15:12:12 +0900 Subject: [PATCH 23/99] =?UTF-8?q?refactor:=20View=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20static=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A1=9C=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.java | 4 +--- src/main/java/janggi/JanggiRunner.java | 23 ++++++++-------------- src/main/java/janggi/view/InputView.java | 2 +- src/main/java/janggi/view/OutputView.java | 24 +++++++++++------------ 4 files changed, 22 insertions(+), 31 deletions(-) diff --git a/src/main/java/janggi/Application.java b/src/main/java/janggi/Application.java index afb3e08782..9afa43f3db 100644 --- a/src/main/java/janggi/Application.java +++ b/src/main/java/janggi/Application.java @@ -6,9 +6,7 @@ public class Application { public static void main(String[] args) { - InputView inputView = new InputView(); - OutputView outputView = new OutputView(); - JanggiRunner janggiRunner = new JanggiRunner(inputView, outputView); + JanggiRunner janggiRunner = new JanggiRunner(); janggiRunner.execute(); } } diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index ef9f38f25d..16bd27cdc8 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -1,30 +1,23 @@ package janggi; import janggi.domain.Board; +import janggi.domain.JanggiGame; import janggi.domain.Position; import janggi.domain.Turn; -import janggi.domain.JanggiGame; import janggi.domain.side.TeamType; import janggi.util.DelimiterParser; import janggi.util.ExceptionHandler; import janggi.view.InputView; import janggi.view.OutputView; + import java.util.List; public class JanggiRunner { - private final InputView inputView; - private final OutputView outputView; - - public JanggiRunner(InputView inputView, OutputView outputView) { - this.inputView = inputView; - this.outputView = outputView; - } - public void execute() { Board board = Board.createInitialBoard(); - outputView.printStartMessage(); - outputView.printBoard(board.makeSpots()); + OutputView.printStartMessage(); + OutputView.printBoard(board.makeSpots()); JanggiGame janggiGame = new JanggiGame(List.of(new Turn(TeamType.HAN, board))); // 여기서 Turn 객체를 Turns 생성자 내부에서 생성해주는게 나을 듯? while (true) { @@ -36,8 +29,8 @@ public void execute() { } private Position readValidStartPosition(JanggiGame janggiGame) { - outputView.printAskPiecePosition(); - String rawPiecePosition = inputView.readLine(); + OutputView.printAskPiecePosition(); + String rawPiecePosition = InputView.readLine(); List parsedPiecePosition = DelimiterParser.parse(rawPiecePosition); Position startPosition = Position.makePosition(parsedPiecePosition); janggiGame.validatePieceExist(startPosition); @@ -45,8 +38,8 @@ private Position readValidStartPosition(JanggiGame janggiGame) { } private Position readValidEndPosition(JanggiGame janggiGame, Position startPosition) { - outputView.printAskMovePosition(janggiGame.findPiece(startPosition).nickname()); - String rawMovePosition = inputView.readLine(); + OutputView.printAskMovePosition(janggiGame.findPiece(startPosition).nickname()); + String rawMovePosition = InputView.readLine(); List parsedMovePosition = DelimiterParser.parse(rawMovePosition); Position endPosition = Position.makePosition(parsedMovePosition); janggiGame.validateValidEndPosition(startPosition, endPosition); diff --git a/src/main/java/janggi/view/InputView.java b/src/main/java/janggi/view/InputView.java index 72806d5a88..4963d38ae1 100644 --- a/src/main/java/janggi/view/InputView.java +++ b/src/main/java/janggi/view/InputView.java @@ -6,7 +6,7 @@ public class InputView { private static final Scanner scanner = new Scanner(System.in); - public String readLine() { + public static String readLine() { return scanner.nextLine(); } } diff --git a/src/main/java/janggi/view/OutputView.java b/src/main/java/janggi/view/OutputView.java index 1eafdfd0d8..f7e23ddd8b 100644 --- a/src/main/java/janggi/view/OutputView.java +++ b/src/main/java/janggi/view/OutputView.java @@ -11,20 +11,20 @@ public static void printMessage(String message) { System.out.println(message); } - public void printNewLine() { + public static void printNewLine() { System.out.println(); } - public void printStartMessage() { + public static void printStartMessage() { printMessage("장기 게임을 시작합니다."); printNewLine(); } - public void printBoard(List boardSpots) { + public static void printBoard(List boardSpots) { System.out.println(makeBoard(boardSpots)); } - private String makeBoard(List boardSpots) { + private static String makeBoard(List boardSpots) { Map boardSpotMap = makeBoardSpotMap(boardSpots); StringBuilder builder = new StringBuilder(); builder.append(makeHeader()); @@ -34,7 +34,7 @@ private String makeBoard(List boardSpots) { return builder.toString(); } - private String makeHeader() { + private static String makeHeader() { StringBuilder builder = new StringBuilder(" "); for (int x = 1; x <= 9; x++) { builder.append(String.format("%-3s", x)); @@ -43,7 +43,7 @@ private String makeHeader() { return builder.toString(); } - private String makeRow(Map boardSpotMap, int y) { + private static String makeRow(Map boardSpotMap, int y) { StringBuilder builder = new StringBuilder(); builder.append(String.format("%2d ", y)); for (int x = 1; x <= 9; x++) { @@ -53,7 +53,7 @@ private String makeRow(Map boardSpotMap, int y) { return builder.toString(); } - private Map makeBoardSpotMap(List boardSpots) { + private static Map makeBoardSpotMap(List boardSpots) { Map boardSpotMap = new HashMap<>(); for (BoardSpot boardSpot : boardSpots) { boardSpotMap.put(boardSpot.position(), boardSpot.pieceName()); @@ -61,23 +61,23 @@ private Map makeBoardSpotMap(List boardSpots) { return boardSpotMap; } - private String findPieceName(Map boardSpotMap, int x, int y) { + private static String findPieceName(Map boardSpotMap, int x, int y) { return boardSpotMap.getOrDefault(makeKey(x, y), "."); } - private String makeKey(int x, int y) { + private static String makeKey(int x, int y) { return x + "," + y; } - public void printTurnNotice(String nowTurn) { + public static void printTurnNotice(String nowTurn) { printMessage(nowTurn + "의 차례입니다."); } - public void printAskPiecePosition() { + public static void printAskPiecePosition() { printMessage("움직일 기물의 좌표를 입력해주세요."); } - public void printAskMovePosition(String nickname) { + public static void printAskMovePosition(String nickname) { printMessage(nickname + "의 목적 좌표를 입력해주세요."); } } From 08a72d154433aea321ab9f3300d156a74f03f990 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Fri, 27 Mar 2026 15:21:28 +0900 Subject: [PATCH 24/99] =?UTF-8?q?refactor:=20=EC=B4=88=EA=B8=B0=20JanggiGa?= =?UTF-8?q?me=20=EC=83=9D=EC=84=B1=20=EB=A1=9C=EC=A7=81=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/main/java/janggi/JanggiRunner.java | 2 +- src/main/java/janggi/domain/JanggiGame.java | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index 16bd27cdc8..fa460e9604 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -19,7 +19,7 @@ public void execute() { OutputView.printStartMessage(); OutputView.printBoard(board.makeSpots()); - JanggiGame janggiGame = new JanggiGame(List.of(new Turn(TeamType.HAN, board))); // 여기서 Turn 객체를 Turns 생성자 내부에서 생성해주는게 나을 듯? + JanggiGame janggiGame = JanggiGame.createInitialJanggiGame(board); while (true) { JanggiGame currentJanggiGame = janggiGame; Position startPosition = ExceptionHandler.retryUntilSuccess(() -> readValidStartPosition(currentJanggiGame)); diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index af7548c438..d0749e1673 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -1,6 +1,8 @@ package janggi.domain; import janggi.domain.piece.Piece; +import janggi.domain.side.TeamType; + import java.util.ArrayList; import java.util.List; @@ -8,10 +10,14 @@ public class JanggiGame { private final List value; - public JanggiGame(List value) { + private JanggiGame(List value) { this.value = value; } + public static JanggiGame createInitialJanggiGame(Board board) { + return new JanggiGame(List.of(new Turn(TeamType.HAN, board))); + } + public void validatePieceExist(Position position) { Turn lastTurn = getLastTurn(); if (!lastTurn.isMyTeamPieceExist(position)) { From 0b38b646cc5f783fab169d22719127c3c0033ac4 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Fri, 27 Mar 2026 15:29:24 +0900 Subject: [PATCH 25/99] =?UTF-8?q?refactor:=20=EA=B8=B0=EB=AC=BC=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EC=8B=9C=20=EA=B8=B0=EB=AC=BC=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=A1=9C=EC=A7=81=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/Board.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 311b329f83..7a84494a53 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -85,7 +85,7 @@ public Board move( ) { validateRange(startPosition); validateRange(endPosition); - Piece piece = findTeamPiece(startPosition, opponentTeam(nowTurn)); + Piece piece = findTeamPiece(startPosition, currentTeam(nowTurn)); validateTargetPosition(currentTeam(nowTurn), endPosition); validateCanMove(piece, startPosition, endPosition); Team movedCurrentTeam = currentTeam(nowTurn).move(startPosition, endPosition); From d0a96713958700f1a06e78db647d59d9d6809867 Mon Sep 17 00:00:00 2001 From: Sumin Date: Fri, 27 Mar 2026 15:32:56 +0900 Subject: [PATCH 26/99] =?UTF-8?q?feat:=20=ED=84=B4=20=EB=AA=85=EC=8B=9C=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=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/JanggiRunner.java | 1 + src/main/java/janggi/domain/JanggiGame.java | 6 +++++- src/main/java/janggi/domain/Turn.java | 5 +++++ src/main/java/janggi/domain/side/TeamType.java | 4 ++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index fa460e9604..4e0b3c53b2 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -29,6 +29,7 @@ public void execute() { } private Position readValidStartPosition(JanggiGame janggiGame) { + OutputView.printTurnNotice(janggiGame.getCurrentTurnTeamName()); OutputView.printAskPiecePosition(); String rawPiecePosition = InputView.readLine(); List parsedPiecePosition = DelimiterParser.parse(rawPiecePosition); diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index d0749e1673..628aea7d29 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -2,7 +2,6 @@ import janggi.domain.piece.Piece; import janggi.domain.side.TeamType; - import java.util.ArrayList; import java.util.List; @@ -48,4 +47,9 @@ public JanggiGame doGame(Position startPosition, Position endPosition) { updatedTurns.add(newTurn); return new JanggiGame(updatedTurns); } + + public String getCurrentTurnTeamName() { + Turn lastTurn = getLastTurn(); + return lastTurn.nextTurnTeam(); + } } diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index c6118a91e2..653faaf144 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -31,6 +31,11 @@ public void canMove(Position startPosition, Position endPosition) { board.canMove(startPosition, endPosition, opponentTeamType()); } + public String nextTurnTeam() { + TeamType teamType = opponentTeamType(); + return teamType.getName(); + } + private TeamType opponentTeamType() { if (movedTeam == TeamType.CHU) { return TeamType.HAN; diff --git a/src/main/java/janggi/domain/side/TeamType.java b/src/main/java/janggi/domain/side/TeamType.java index 503be44739..6d581c390f 100644 --- a/src/main/java/janggi/domain/side/TeamType.java +++ b/src/main/java/janggi/domain/side/TeamType.java @@ -10,4 +10,8 @@ public enum TeamType { TeamType(String name) { this.name = name; } + + public String getName() { + return name; + } } From 106c6addf44a70ebcdbc8727fbfc7ce2fcbaa239 Mon Sep 17 00:00:00 2001 From: Sumin Date: Fri, 27 Mar 2026 15:40:44 +0900 Subject: [PATCH 27/99] =?UTF-8?q?feat:=20=ED=95=9C=20=ED=84=B4=20=EC=A2=85?= =?UTF-8?q?=EB=A3=8C=20=EC=9D=B4=ED=9B=84=20=EB=B3=B4=EB=93=9C=20=ED=98=84?= =?UTF-8?q?=ED=99=A9=20=EC=B6=9C=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/JanggiRunner.java | 17 ++++++++--------- src/main/java/janggi/domain/Board.java | 2 +- src/main/java/janggi/domain/JanggiGame.java | 11 ++++++++--- src/main/java/janggi/domain/Turn.java | 10 ++++++++++ 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index 4e0b3c53b2..80fb866fa5 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -1,29 +1,28 @@ package janggi; -import janggi.domain.Board; import janggi.domain.JanggiGame; import janggi.domain.Position; -import janggi.domain.Turn; -import janggi.domain.side.TeamType; import janggi.util.DelimiterParser; import janggi.util.ExceptionHandler; import janggi.view.InputView; import janggi.view.OutputView; - import java.util.List; public class JanggiRunner { public void execute() { - Board board = Board.createInitialBoard(); OutputView.printStartMessage(); - OutputView.printBoard(board.makeSpots()); - JanggiGame janggiGame = JanggiGame.createInitialJanggiGame(board); + JanggiGame janggiGame = JanggiGame.createInitialJanggiGame(); while (true) { JanggiGame currentJanggiGame = janggiGame; - Position startPosition = ExceptionHandler.retryUntilSuccess(() -> readValidStartPosition(currentJanggiGame)); - Position endPosition = ExceptionHandler.retryUntilSuccess(() -> readValidEndPosition(currentJanggiGame, startPosition)); + OutputView.printBoard(currentJanggiGame.makeCurrentTurnBoardSnapShot()); + Position startPosition = ExceptionHandler.retryUntilSuccess( + () -> readValidStartPosition(currentJanggiGame) + ); + Position endPosition = ExceptionHandler.retryUntilSuccess( + () -> readValidEndPosition(currentJanggiGame, startPosition) + ); janggiGame = janggiGame.doGame(startPosition, endPosition); } } diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 7a84494a53..ba239b089a 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -28,7 +28,7 @@ public static Board createInitialBoard() { return new Board(Chu.createInitialChu(), Han.createInitialHan()); } - public List makeSpots() { + public List makeSnapShot() { List chuBoardSpots = chu.makeSpots(); List hanBoardSpots = han.makeSpots(); List boardSpots = new ArrayList<>(chuBoardSpots); diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index 628aea7d29..3d7b0adc00 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -1,7 +1,7 @@ package janggi.domain; import janggi.domain.piece.Piece; -import janggi.domain.side.TeamType; +import janggi.dto.BoardSpot; import java.util.ArrayList; import java.util.List; @@ -13,8 +13,13 @@ private JanggiGame(List value) { this.value = value; } - public static JanggiGame createInitialJanggiGame(Board board) { - return new JanggiGame(List.of(new Turn(TeamType.HAN, board))); + public static JanggiGame createInitialJanggiGame() { + return new JanggiGame(List.of(Turn.createInitialTurn())); + } + + public List makeCurrentTurnBoardSnapShot() { + Turn lastTurn = getLastTurn(); + return lastTurn.makeBoardSnapShot(); } public void validatePieceExist(Position position) { diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index 653faaf144..2d79848617 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -2,6 +2,8 @@ import janggi.domain.piece.Piece; import janggi.domain.side.TeamType; +import janggi.dto.BoardSpot; +import java.util.List; public class Turn { @@ -13,6 +15,10 @@ public Turn(TeamType movedTeam, Board board) { this.board = board; } + public static Turn createInitialTurn() { + return new Turn(TeamType.HAN, Board.createInitialBoard()); + } + public boolean isMyTeamPieceExist(Position position) { return board.isMyTeamPieceExist(position, movedTeam); } @@ -42,4 +48,8 @@ private TeamType opponentTeamType() { } return TeamType.CHU; } + + public List makeBoardSnapShot() { + return board.makeSnapShot(); + } } From 40bdea896a650ccd1e270f49c8b7521e090b410b Mon Sep 17 00:00:00 2001 From: Sumin Date: Fri, 27 Mar 2026 15:45:14 +0900 Subject: [PATCH 28/99] =?UTF-8?q?chore:=20=EC=A3=BC=EC=84=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/Cha.java | 1 - src/main/java/janggi/domain/piece/Po.java | 1 - src/main/java/janggi/domain/side/Chu.java | 5 ----- src/main/java/janggi/domain/side/Han.java | 5 ----- 4 files changed, 12 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 4cdc7a373d..8a296fcd73 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -25,7 +25,6 @@ public Cha(TeamType teamType) { ); } - // TODO: start 좌표는 유효한게 보장 되어 있는지? Pieces에서 Map 조회 후 반환하기. @Override public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { return findMovePath(startX, startY, endX, endY).isPresent(); diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index dfdb8a8c7d..53d238135e 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -25,7 +25,6 @@ public Po(TeamType teamType) { ); } - // TODO: Pieces에서 사이에 기물이 없다면 애초에 호출하지 않음. Pieces에 Po를 움직일 경우 중간에 좌표가 있는지 확인하는 로직이 필요함. @Override public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { return findMovePath(startX, startY, endX, endY).isPresent(); diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index c45e7fb8f3..0f487ba31f 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -48,9 +48,4 @@ public Team move(Position piecePosition, Position targetPosition) { public void isPieceCanMove(Position startPosition, Position endPosition) { pieces.checkPieceCanMove(startPosition, endPosition); } - - // TODO: 구현하기 - public int calculateScore() { - return 0; - } } diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index 38b238f45b..e60884476f 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -48,9 +48,4 @@ public Team move(Position piecePosition, Position targetPosition) { public void isPieceCanMove(Position startPosition, Position endPosition) { pieces.checkPieceCanMove(startPosition, endPosition); } - - // TODO: 구현하기 - public int calculateScore() { - return 0; - } } From 74fc4e6c990fca0e4af5b332e3853727b8a3d099 Mon Sep 17 00:00:00 2001 From: Sumin Date: Fri, 27 Mar 2026 17:21:45 +0900 Subject: [PATCH 29/99] =?UTF-8?q?refactor:=20=EB=B9=A0=EC=A7=84=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=EB=B6=80=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 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index ba239b089a..7d824c6940 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -102,13 +102,13 @@ public void canMove(Position startPosition, Position endPosition, TeamType nowTe } private void validateCanMove(Piece piece, Position piecePosition, Position targetPosition) { - if (!piece.isValidPath(piecePosition, targetPosition, this)) { + if (!piece.isValidMovePattern(piecePosition.getX(), piecePosition.getY(), targetPosition.getX(), + targetPosition.getY())) { + throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + } + if (!piece.isObstaclesNotExist(piecePosition, targetPosition, this)) { throw new IllegalArgumentException("이동할 수 없는 위치입니다."); } - } - - private void validateTargetPosition(TeamType nowTurn, Position targetPosition) { - validateTargetPosition(currentTeam(nowTurn), targetPosition); } private void validateTargetPosition(Team team, Position targetPosition) { From 3d674d3c962371e813a06fbc5dfbb096f0e7ceac Mon Sep 17 00:00:00 2001 From: Sumin Date: Fri, 27 Mar 2026 17:22:29 +0900 Subject: [PATCH 30/99] =?UTF-8?q?refactor:=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=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20=EB=AA=A8=ED=98=B8=ED=95=9C=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=EB=AA=85=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/Cha.java | 7 +------ src/main/java/janggi/domain/piece/Gung.java | 12 +++++++----- src/main/java/janggi/domain/piece/Jol.java | 12 +++++++----- src/main/java/janggi/domain/piece/Ma.java | 7 +------ src/main/java/janggi/domain/piece/Piece.java | 7 +------ src/main/java/janggi/domain/piece/Po.java | 7 +------ src/main/java/janggi/domain/piece/Sa.java | 12 +++++++----- src/main/java/janggi/domain/piece/Sang.java | 7 +------ 8 files changed, 26 insertions(+), 45 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 8a296fcd73..85b65fb092 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -46,7 +46,7 @@ public Optional findMovePath(int startX, int startY, int endX, int end } @Override - public boolean isValidPath(Position start, Position end, Board board) { + public boolean isObstaclesNotExist(Position start, Position end, Board board) { Optional movePath = findMovePath(start.getX(), start.getY(), end.getX(), end.getY()); if (movePath.isEmpty()) { return false; @@ -68,11 +68,6 @@ public String nickname() { return pieceType.getNickname(); } - @Override - public boolean isSameType(TeamType nowTurn) { - return nowTurn == teamType; - } - @Override public PieceType getPieceType() { return pieceType; diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index bd647744b6..679a6a635e 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -1,7 +1,9 @@ package janggi.domain.piece; +import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; +import janggi.domain.Position; import janggi.domain.side.TeamType; import java.util.List; import java.util.Optional; @@ -49,6 +51,11 @@ public Optional findMovePath(int startX, int startY, int endX, int end .findFirst(); } + @Override + public boolean isObstaclesNotExist(Position start, Position end, Board board) { + return true; + } + private boolean isSamePosition(int distanceX, int distanceY) { return distanceX == 0 && distanceY == 0; } @@ -62,11 +69,6 @@ public String nickname() { return pieceType.getNickname(); } - @Override - public boolean isSameType(TeamType nowTurn) { - return teamType == nowTurn; - } - @Override public PieceType getPieceType() { return pieceType; diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 45749a8e54..4ff647ee6a 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -1,7 +1,9 @@ package janggi.domain.piece; +import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; +import janggi.domain.Position; import janggi.domain.side.TeamType; import java.util.List; import java.util.Optional; @@ -35,6 +37,11 @@ public Optional findMovePath(int startX, int startY, int endX, int end .findFirst(); } + @Override + public boolean isObstaclesNotExist(Position start, Position end, Board board) { + return true; + } + private boolean isSamePosition(int distanceX, int distanceY) { return distanceX == 0 && distanceY == 0; } @@ -59,11 +66,6 @@ public String nickname() { return pieceType.getNickname(); } - @Override - public boolean isSameType(TeamType nowTurn) { - return teamType == nowTurn; - } - @Override public PieceType getPieceType() { return pieceType; diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 1388ba5e15..af9fd3dcdb 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -44,7 +44,7 @@ public Optional findMovePath(int startX, int startY, int endX, int end } @Override - public boolean isValidPath(Position start, Position end, Board board) { + public boolean isObstaclesNotExist(Position start, Position end, Board board) { Optional movePath = findMovePath(start.getX(), start.getY(), end.getX(), end.getY()); if (movePath.isEmpty()) { return false; @@ -58,11 +58,6 @@ public String nickname() { return pieceType.getNickname(); } - @Override - public boolean isSameType(TeamType nowTurn) { - return teamType == nowTurn; - } - @Override public PieceType getPieceType() { return pieceType; diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index 04b50b3a25..18d690fe02 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -3,7 +3,6 @@ import janggi.domain.Board; import janggi.domain.MovePath; import janggi.domain.Position; -import janggi.domain.side.TeamType; import java.util.Optional; public interface Piece { @@ -12,13 +11,9 @@ public interface Piece { Optional findMovePath(int startX, int startY, int endX, int endY); - default boolean isValidPath(Position start, Position end, Board board) { - return findMovePath(start.getX(), start.getY(), end.getX(), end.getY()).isPresent(); - } + boolean isObstaclesNotExist(Position start, Position end, Board board); String nickname(); - boolean isSameType(TeamType nowTurn); - PieceType getPieceType(); } diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index 53d238135e..53a07ed9c1 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -43,7 +43,7 @@ public Optional findMovePath(int startX, int startY, int endX, int end } @Override - public boolean isValidPath(Position start, Position end, Board board) { + public boolean isObstaclesNotExist(Position start, Position end, Board board) { Optional movePath = findMovePath(start.getX(), start.getY(), end.getX(), end.getY()); if (movePath.isEmpty()) { return false; @@ -69,11 +69,6 @@ public String nickname() { return pieceType.getNickname(); } - @Override - public boolean isSameType(TeamType nowTurn) { - return nowTurn == teamType; - } - @Override public PieceType getPieceType() { return pieceType; diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index 36e75bd547..c6d5768e6c 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -1,7 +1,9 @@ package janggi.domain.piece; +import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; +import janggi.domain.Position; import janggi.domain.side.TeamType; import java.util.List; import java.util.Optional; @@ -49,6 +51,11 @@ public Optional findMovePath(int startX, int startY, int endX, int end .findFirst(); } + @Override + public boolean isObstaclesNotExist(Position start, Position end, Board board) { + return true; + } + private boolean isSamePosition(int distanceX, int distanceY) { return distanceX == 0 && distanceY == 0; } @@ -62,11 +69,6 @@ public String nickname() { return pieceType.getNickname(); } - @Override - public boolean isSameType(TeamType nowTurn) { - return nowTurn == teamType; - } - @Override public PieceType getPieceType() { return pieceType; diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index 6cb569fa3b..becbb9f976 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -44,7 +44,7 @@ public Optional findMovePath(int startX, int startY, int endX, int end } @Override - public boolean isValidPath(Position start, Position end, Board board) { + public boolean isObstaclesNotExist(Position start, Position end, Board board) { Optional movePath = findMovePath(start.getX(), start.getY(), end.getX(), end.getY()); if (movePath.isEmpty()) { return false; @@ -58,11 +58,6 @@ public String nickname() { return pieceType.getNickname(); } - @Override - public boolean isSameType(TeamType nowTurn) { - return nowTurn == teamType; - } - @Override public PieceType getPieceType() { return pieceType; From 8ac904873dcf502c7f26c7f75db0f832e9f4b77b Mon Sep 17 00:00:00 2001 From: Sumin Date: Fri, 27 Mar 2026 17:22:39 +0900 Subject: [PATCH 31/99] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Pieces.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index ab057bd6c7..7393ad325a 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -98,11 +98,6 @@ private static void createJols(Map pieces, int indexY, TeamType } } - public boolean isPieceExists(int x, int y) { - Position position = new Position(x, y); - return value.containsKey(position); - } - public List makeSpots() { List boardSpots = new ArrayList<>(); for (Map.Entry entry : value.entrySet()) { @@ -117,9 +112,4 @@ public List makeSpots() { public Optional findPiece(Position position) { return Optional.ofNullable(value.get(position)); } - - public void checkPieceCanMove(Position startPosition, Position endPosition) { - Piece piece = value.get(startPosition); - piece.isValidMovePattern(startPosition.getX(), startPosition.getY(), endPosition.getX(), endPosition.getY()); - } } From f718e15cd7587088493efc5260fda8f13223d8e8 Mon Sep 17 00:00:00 2001 From: Sumin Date: Fri, 27 Mar 2026 17:22:46 +0900 Subject: [PATCH 32/99] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/side/Chu.java | 10 ---------- src/main/java/janggi/domain/side/Han.java | 10 ---------- src/main/java/janggi/domain/side/Team.java | 6 ------ 3 files changed, 26 deletions(-) diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index 0f487ba31f..0291ae8a1a 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -19,11 +19,6 @@ public static Chu createInitialChu() { return new Chu(Pieces.createChu()); } - @Override - public boolean isPieceExists(int x, int y) { - return pieces.isPieceExists(x, y); - } - @Override public List makeSpots() { return pieces.makeSpots(); @@ -43,9 +38,4 @@ public Team remove(Position position) { public Team move(Position piecePosition, Position targetPosition) { return new Chu(pieces.move(piecePosition, targetPosition)); } - - @Override - public void isPieceCanMove(Position startPosition, Position endPosition) { - pieces.checkPieceCanMove(startPosition, endPosition); - } } diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index e60884476f..e0c6cd611e 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -19,11 +19,6 @@ public static Han createInitialHan() { return new Han(Pieces.createHan()); } - @Override - public boolean isPieceExists(int x, int y) { - return pieces.isPieceExists(x, y); - } - @Override public List makeSpots() { return pieces.makeSpots(); @@ -43,9 +38,4 @@ public Team remove(Position position) { public Team move(Position piecePosition, Position targetPosition) { return new Han(pieces.move(piecePosition, targetPosition)); } - - @Override - public void isPieceCanMove(Position startPosition, Position endPosition) { - pieces.checkPieceCanMove(startPosition, endPosition); - } } diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index b20b774402..88d9bf4e00 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -8,8 +8,6 @@ public interface Team { - boolean isPieceExists(int x, int y); - List makeSpots(); Optional findPiece(Position position); @@ -17,8 +15,4 @@ public interface Team { Team remove(Position position); Team move(Position piecePosition, Position targetPosition); - - void isPieceCanMove(Position startPosition, Position endPosition); - - } From 068f2af663ac1ce4e027442dc606921998ecf0c2 Mon Sep 17 00:00:00 2001 From: Sumin Date: Fri, 27 Mar 2026 17:27:25 +0900 Subject: [PATCH 33/99] =?UTF-8?q?test:=20=EA=B8=B0=EB=AC=BC=20=EC=B0=A8=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 --- .../java/janggi/domain/piece/ChaTest.java | 84 ++++++++++--------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/src/test/java/janggi/domain/piece/ChaTest.java b/src/test/java/janggi/domain/piece/ChaTest.java index 6a96bda96c..c3c97c07b7 100644 --- a/src/test/java/janggi/domain/piece/ChaTest.java +++ b/src/test/java/janggi/domain/piece/ChaTest.java @@ -1,73 +1,79 @@ package janggi.domain.piece; -import org.assertj.core.api.Assertions; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import janggi.domain.Board; +import janggi.domain.Position; +import janggi.domain.side.TeamType; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; class ChaTest { @Test - @DisplayName("시작점에서 오른쪽으로 도달할 수 있는 도착점은 true를 반환한다.") - void canRightReach() { + @DisplayName("차는 상하좌우로 직선 이동할 수 있다.") + void isValidMovePatternStraight() { // given - int startX = 0; - int startY = 0; - int endX = 3; - int endY = 0; - - // when - Cha cha = new Cha(); + Cha cha = new Cha(TeamType.CHU); - // then - Assertions.assertThat(cha.canMove(startX, startY, endX, endY)).isTrue(); + // when & then + assertAll( + () -> assertThat(cha.isValidMovePattern(4, 4, 7, 4)).isTrue(), + () -> assertThat(cha.isValidMovePattern(4, 4, 1, 4)).isTrue(), + () -> assertThat(cha.isValidMovePattern(4, 4, 4, 8)).isTrue(), + () -> assertThat(cha.isValidMovePattern(4, 4, 4, 1)).isTrue() + ); } @Test - @DisplayName("시작점에서 왼쪽으로 도달할 수 있는 도착점은 true를 반환한다.") - void canLeftReach() { + @DisplayName("차는 대각선으로 이동할 수 없다.") + void cannotMoveDiagonal() { // given - int startX = 2; - int startY = 0; - int endX = 1; - int endY = 0; + Cha cha = new Cha(TeamType.CHU); - // when - Cha cha = new Cha(); + // when & then + assertAll( + () -> assertThat(cha.isValidMovePattern(4, 4, 5, 5)).isFalse(), + () -> assertThat(cha.isValidMovePattern(4, 4, 2, 2)).isFalse() + ); + } - // then - Assertions.assertThat(cha.canMove(startX, startY, endX, endY)).isTrue(); + @Test + @DisplayName("차는 제자리로 이동할 수 없다.") + void cannotMoveSamePosition() { + // given + Cha cha = new Cha(TeamType.CHU); + + // when & then + assertThat(cha.isValidMovePattern(4, 4, 4, 4)).isFalse(); } @Test - @DisplayName("시작점에서 위로 도달할 수 있는 도착점은 true를 반환한다.") - void canUpReach() { + @DisplayName("이동 경로가 비어 있으면 차는 이동할 수 있다.") + void isValidPathWhenRouteIsEmpty() { // given - int startX = 0; - int startY = 0; - int endX = 0; - int endY = 1; + Cha cha = new Cha(TeamType.CHU); + Board board = Board.createInitialBoard(); // when - Cha cha = new Cha(); + boolean result = cha.isObstaclesNotExist(new Position(1, 1), new Position(1, 3), board); // then - Assertions.assertThat(cha.canMove(startX, startY, endX, endY)).isTrue(); + assertThat(result).isTrue(); } @Test - @DisplayName("시작점에서 아래로 도달할 수 있는 도착점은 true를 반환한다.") - void canDownReach() { + @DisplayName("이동 경로가 막혀 있으면 차는 이동할 수 없다.") + void cannotMoveWhenRouteIsBlocked() { // given - int startX = 0; - int startY = 1; - int endX = 0; - int endY = 0; + Cha cha = new Cha(TeamType.CHU); + Board board = Board.createInitialBoard(); // when - Cha cha = new Cha(); + boolean result = cha.isObstaclesNotExist(new Position(1, 1), new Position(1, 5), board); // then - Assertions.assertThat(cha.canMove(startX, startY, endX, endY)).isTrue(); + assertThat(result).isFalse(); } - } From 918e0747c3a4395571b02dd9df4b15a1a6c1d017 Mon Sep 17 00:00:00 2001 From: Sumin Date: Fri, 27 Mar 2026 17:27:29 +0900 Subject: [PATCH 34/99] =?UTF-8?q?test:=20=EA=B8=B0=EB=AC=BC=20=EA=B6=81=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 --- .../java/janggi/domain/piece/GungTest.java | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/test/java/janggi/domain/piece/GungTest.java b/src/test/java/janggi/domain/piece/GungTest.java index ca74fae8eb..21e837610d 100644 --- a/src/test/java/janggi/domain/piece/GungTest.java +++ b/src/test/java/janggi/domain/piece/GungTest.java @@ -3,63 +3,64 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import janggi.domain.side.TeamType; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; class GungTest { @Test - @DisplayName("시작점에서 상하좌우 한 칸 이동할 수 있다.") - void canMoveStraightOneStep() { + @DisplayName("궁은 상하좌우 한 칸 이동할 수 있다.") + void isValidMovePatternStraightOneStep() { // given - Gung gung = new Gung(); + Gung gung = new Gung(TeamType.CHU); // when & then assertAll( - () -> assertThat(gung.canMove(4, 4, 4, 5)).isTrue(), - () -> assertThat(gung.canMove(4, 4, 4, 3)).isTrue(), - () -> assertThat(gung.canMove(4, 4, 5, 4)).isTrue(), - () -> assertThat(gung.canMove(4, 4, 3, 4)).isTrue() + () -> assertThat(gung.isValidMovePattern(4, 4, 4, 5)).isTrue(), + () -> assertThat(gung.isValidMovePattern(4, 4, 4, 3)).isTrue(), + () -> assertThat(gung.isValidMovePattern(4, 4, 5, 4)).isTrue(), + () -> assertThat(gung.isValidMovePattern(4, 4, 3, 4)).isTrue() ); } @Test - @DisplayName("시작점에서 대각선 한 칸 이동할 수 있다.") - void canMoveDiagonalOneStep() { + @DisplayName("궁은 대각선 한 칸 이동할 수 있다.") + void isValidMovePatternDiagonalOneStep() { // given - Gung gung = new Gung(); + Gung gung = new Gung(TeamType.CHU); // when & then assertAll( - () -> assertThat(gung.canMove(4, 4, 5, 5)).isTrue(), - () -> assertThat(gung.canMove(4, 4, 5, 3)).isTrue(), - () -> assertThat(gung.canMove(4, 4, 3, 5)).isTrue(), - () -> assertThat(gung.canMove(4, 4, 3, 3)).isTrue() + () -> assertThat(gung.isValidMovePattern(4, 4, 5, 5)).isTrue(), + () -> assertThat(gung.isValidMovePattern(4, 4, 5, 3)).isTrue(), + () -> assertThat(gung.isValidMovePattern(4, 4, 3, 5)).isTrue(), + () -> assertThat(gung.isValidMovePattern(4, 4, 3, 3)).isTrue() ); } @Test - @DisplayName("시작점에서 두 칸 이상 이동할 수 없다.") + @DisplayName("궁은 두 칸 이상 이동할 수 없다.") void cannotMoveOverOneStep() { // given - Gung gung = new Gung(); + Gung gung = new Gung(TeamType.CHU); // when & then assertAll( - () -> assertThat(gung.canMove(4, 4, 6, 4)).isFalse(), - () -> assertThat(gung.canMove(4, 4, 6, 6)).isFalse(), - () -> assertThat(gung.canMove(4, 4, 4, 6)).isFalse() + () -> assertThat(gung.isValidMovePattern(4, 4, 6, 4)).isFalse(), + () -> assertThat(gung.isValidMovePattern(4, 4, 6, 6)).isFalse(), + () -> assertThat(gung.isValidMovePattern(4, 4, 4, 6)).isFalse(), + () -> assertThat(gung.isValidMovePattern(4, 4, 2, 4)).isFalse() ); - } @Test - @DisplayName("제자리로는 이동할 수 없다.") + @DisplayName("궁은 제자리로 이동할 수 없다.") void cannotMoveSamePosition() { // given - Gung gung = new Gung(); + Gung gung = new Gung(TeamType.CHU); // when & then - assertThat(gung.canMove(4, 4, 4, 4)).isFalse(); + assertThat(gung.isValidMovePattern(4, 4, 4, 4)).isFalse(); } } From bc5c056ab7f3f6e94c24e949714eb406f74689c1 Mon Sep 17 00:00:00 2001 From: Sumin Date: Fri, 27 Mar 2026 17:43:20 +0900 Subject: [PATCH 35/99] =?UTF-8?q?refactor:=20JanggiGame=20=EA=B0=80?= =?UTF-8?q?=EB=B3=80=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/JanggiRunner.java | 9 ++++----- src/main/java/janggi/domain/JanggiGame.java | 14 ++++++-------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index 80fb866fa5..d80e24812a 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -15,15 +15,14 @@ public void execute() { JanggiGame janggiGame = JanggiGame.createInitialJanggiGame(); while (true) { - JanggiGame currentJanggiGame = janggiGame; - OutputView.printBoard(currentJanggiGame.makeCurrentTurnBoardSnapShot()); + OutputView.printBoard(janggiGame.makeCurrentTurnBoardSnapShot()); Position startPosition = ExceptionHandler.retryUntilSuccess( - () -> readValidStartPosition(currentJanggiGame) + () -> readValidStartPosition(janggiGame) ); Position endPosition = ExceptionHandler.retryUntilSuccess( - () -> readValidEndPosition(currentJanggiGame, startPosition) + () -> readValidEndPosition(janggiGame, startPosition) ); - janggiGame = janggiGame.doGame(startPosition, endPosition); + janggiGame.doGame(startPosition, endPosition); } } diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index 3d7b0adc00..ac58715c9c 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -7,10 +7,10 @@ public class JanggiGame { - private final List value; + private final List turns; - private JanggiGame(List value) { - this.value = value; + private JanggiGame(List turns) { + this.turns = new ArrayList<>(turns); } public static JanggiGame createInitialJanggiGame() { @@ -42,15 +42,13 @@ public Piece findPiece(Position position) { } private Turn getLastTurn() { - return value.getLast(); + return turns.getLast(); } - public JanggiGame doGame(Position startPosition, Position endPosition) { + public void doGame(Position startPosition, Position endPosition) { Turn lastTurn = getLastTurn(); Turn newTurn = lastTurn.move(startPosition, endPosition); - List updatedTurns = new ArrayList<>(value); - updatedTurns.add(newTurn); - return new JanggiGame(updatedTurns); + turns.add(newTurn); } public String getCurrentTurnTeamName() { From 6cd133255590e4bc3e3a6ec8101b1cd04f5373ab Mon Sep 17 00:00:00 2001 From: Sumin Date: Fri, 27 Mar 2026 17:46:18 +0900 Subject: [PATCH 36/99] =?UTF-8?q?refactor:=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Board.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 7d824c6940..27b6f2d8ae 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -94,7 +94,6 @@ public Board move( } public void canMove(Position startPosition, Position endPosition, TeamType nowTeam) { - validateRange(startPosition); validateRange(endPosition); Piece piece = findTeamPiece(startPosition, currentTeam(nowTeam)); validateTargetPosition(currentTeam(nowTeam), endPosition); From 329e9c245467ff7abd5ef4c523e93082c11a00f5 Mon Sep 17 00:00:00 2001 From: Sumin Date: Fri, 27 Mar 2026 17:47:49 +0900 Subject: [PATCH 37/99] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=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 | 2 +- src/main/java/janggi/domain/Turn.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 27b6f2d8ae..96e6d1375a 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -36,7 +36,7 @@ public List makeSnapShot() { return boardSpots; } - public boolean isMyTeamPieceExist(Position position, TeamType beforeTeam) { + public boolean isPieceExist(Position position, TeamType beforeTeam) { validateRange(position); Team nowTeam = opponentTeam(beforeTeam); return nowTeam.findPiece(position).isPresent(); diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index 2d79848617..be4e4b62c9 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -20,7 +20,7 @@ public static Turn createInitialTurn() { } public boolean isMyTeamPieceExist(Position position) { - return board.isMyTeamPieceExist(position, movedTeam); + return board.isPieceExist(position, movedTeam); } public Piece findPiece(Position position) { From 32ff35727984098ecd62d912aa0403ed147afecb Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Fri, 27 Mar 2026 17:50:04 +0900 Subject: [PATCH 38/99] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=88=9C=EC=84=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/domain/Turn.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index be4e4b62c9..ce1c36dc8b 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -42,14 +42,14 @@ public String nextTurnTeam() { return teamType.getName(); } + public List makeBoardSnapShot() { + return board.makeSnapShot(); + } + private TeamType opponentTeamType() { if (movedTeam == TeamType.CHU) { return TeamType.HAN; } return TeamType.CHU; } - - public List makeBoardSnapShot() { - return board.makeSnapShot(); - } } From 4f1273b98082cb5940ac0316d95a37701da2abc7 Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 10:58:11 +0900 Subject: [PATCH 39/99] =?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 | 196 +++++++++++++++++++++ 1 file changed, 196 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..c63ec5dcdf --- /dev/null +++ b/src/test/java/janggi/domain/BoardTest.java @@ -0,0 +1,196 @@ +package janggi.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import janggi.domain.piece.Jol; +import janggi.domain.side.TeamType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class BoardTest { + + @Test + @DisplayName("시작 위치가 장기판 범위를 벗어나면 이동할 수 없다.") + void cannotMoveWhenStartPositionIsOutOfRange() { + // given + Board board = Board.createInitialBoard(); + Position outOfBound = new Position(0, 0); + + // when & then + assertThatThrownBy(() -> board.canMove(outOfBound, new Position(1, 4), TeamType.CHU)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("입력한 좌표가 장기판 범위 밖입니다."); + } + + @Test + @DisplayName("도착 위치가 장기판 범위를 벗어나면 이동할 수 없다.") + void cannotMoveWhenEndPositionIsOutOfRange() { + // given + Board board = Board.createInitialBoard(); + Position outOfBound = new Position(1, 11); + + // when & then + assertThatThrownBy(() -> board.canMove(new Position(1, 4), outOfBound, TeamType.CHU)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("입력한 좌표가 장기판 범위 밖입니다."); + } + + @Test + @DisplayName("현재 팀의 기물이 없는 시작 위치에서 시작할 수 없다.") + void cannotMoveWhenStartPositionHasNoCurrentTeamPiece() { + // given + Board board = Board.createInitialBoard(); + Position notExistPosition = new Position(2, 2); + + // when & then + assertThatThrownBy(() -> board.canMove(notExistPosition, new Position(1, 1), TeamType.CHU)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("입력한 위치에 기물이 없습니다."); + } + + @Test + @DisplayName("다른 팀의 기물이 있는 위치에서 시작할 수 없다.") + void cannotStartAtOpponentTeamPosition() { + // given + Board board = Board.createInitialBoard(); + Position opponentTeamPosition = new Position(1, 7); + + // when & then + assertThatThrownBy(() -> board.canMove(opponentTeamPosition, new Position(1, 6), TeamType.CHU)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("입력한 위치에 기물이 없습니다."); + } + + @Test + @DisplayName("같은 팀의 기물이 있는 위치로는 이동할 수 없다.") + void cannotMoveToSameTeamPiecePosition() { + // given + Board board = Board.createInitialBoard(); + Position currentTeamPosition = new Position(2, 1); + + // when & then + assertThatThrownBy(() -> board.canMove(new Position(1, 1), currentTeamPosition, TeamType.CHU)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("같은 팀의 기물이 있는 위치로는 이동할 수 없습니다."); + } + + @Test + @DisplayName("이동 패턴에 맞지 않는 사의 이동은 장애물과 관계없이 막는다.") + void cannotMoveWhenMovePatternIsInvalid() { + // given + Board board = Board.createInitialBoard(); + Position saStartPosition = new Position(4, 1); + Position saEndPosition = new Position(4, 3); + + // when & then + assertThatThrownBy(() -> board.canMove(saStartPosition, saEndPosition, TeamType.CHU)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."); + } + + @Test + @DisplayName("장애물이 있는 차의 이동은 막는다.") + void cannotMoveChaWhenPathIsBlocked() { + // given + Board board = Board.createInitialBoard(); + Position chaStartPosition = new Position(1, 1); + Position chaEndPosition = new Position(1, 5); + + // when & then + assertThatThrownBy(() -> board.canMove(chaStartPosition, chaEndPosition, TeamType.CHU)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."); + } + + @Test + @DisplayName("마의 경로 중간에 장애물이 있으면 이동할 수 없다.") + void cannotMoveMaWhenLegIsBlocked() { + // given + Board board = Board.createInitialBoard(); + Position maStartPosition = new Position(2, 1); + Position maEndPosition = new Position(4, 2); + + // when & then + assertThatThrownBy(() -> board.canMove(maStartPosition, maEndPosition, TeamType.CHU)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."); + } + + @Test + @DisplayName("포는 사이에 기물이 없으면 이동할 수 없다.") + void cannotMovePoWithoutBridge() { + // given + Board board = Board.createInitialBoard(); + Position poStartPosition = new Position(2, 3); + Position poEndPosition = new Position(2, 6); + + // when & then + assertThatThrownBy(() -> board.canMove(poStartPosition, poEndPosition, TeamType.CHU)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."); + } + + @Test + @DisplayName("이동 패턴과 장애물 조건을 모두 만족하면 이동할 수 있다.") + void canMoveWhenPatternAndObstacleRulesAreSatisfied() { + // given + Board board = Board.createInitialBoard(); + Position jolStartPosition = new Position(1, 4); + Position jolEndPosition = new Position(2, 4); + Board movedBoard = board.move(jolStartPosition, jolEndPosition, TeamType.CHU); + Position chaStartPosition = new Position(1, 1); + Position chaEndPosition = new Position(1, 3); + Position poStartPosition = new Position(2, 3); + Position poEndPosition = new Position(2, 6); + + // when & then + assertAll( + () -> assertThatCode(() -> movedBoard.canMove(chaStartPosition, chaEndPosition, TeamType.CHU)) + .doesNotThrowAnyException(), + () -> assertThatCode(() -> movedBoard.canMove(poStartPosition, poEndPosition, TeamType.CHU)) + .doesNotThrowAnyException() + ); + } + + @Test + @DisplayName("기물을 이동하면 새 보드를 반환하고 원본 보드는 유지한다.") + void moveReturnsNewBoardWithoutMutatingOriginalBoard() { + // given + Board board = Board.createInitialBoard(); + Position start = new Position(1, 4); + Position end = new Position(1, 5); + + // when + Board movedBoard = board.move(start, end, TeamType.CHU); + + // then + assertAll( + () -> assertThat(board.findPiece(start)).get().isInstanceOf(Jol.class), + () -> assertThat(board.findPiece(end)).isEmpty(), + () -> assertThat(movedBoard.findPiece(start)).isEmpty(), + () -> assertThat(movedBoard.findPiece(end)).get().isInstanceOf(Jol.class) + ); + } + + @Test + @DisplayName("상대 기물이 있는 칸으로 이동하면 상대 기물을 잡는다.") + void moveCapturesOpponentPiece() { + // given + Board board = Board.createInitialBoard(); + + // when + Board firstMovedBoard = board.move(new Position(1, 4), new Position(1, 5), TeamType.CHU); + Board secondMovedBoard = firstMovedBoard.move(new Position(1, 5), new Position(1, 6), TeamType.CHU); + Board capturedBoard = secondMovedBoard.move(new Position(1, 6), new Position(1, 7), TeamType.CHU); + + // then + assertAll( + () -> assertThat(secondMovedBoard.findPiece(new Position(1, 7))).get().isInstanceOf(Jol.class), + () -> assertThat(capturedBoard.findPiece(new Position(1, 6))).isEmpty(), + () -> assertThat(capturedBoard.findPiece(new Position(1, 7))).get().isInstanceOf(Jol.class) + ); + } +} From 1dbfe6b9926aa61096be9967910a5af8e9317d0b Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 10:58:17 +0900 Subject: [PATCH 40/99] =?UTF-8?q?test:=20Chu=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?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/test/java/janggi/domain/side/ChuTest.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/test/java/janggi/domain/side/ChuTest.java diff --git a/src/test/java/janggi/domain/side/ChuTest.java b/src/test/java/janggi/domain/side/ChuTest.java new file mode 100644 index 0000000000..eba14d690a --- /dev/null +++ b/src/test/java/janggi/domain/side/ChuTest.java @@ -0,0 +1,85 @@ +package janggi.domain.side; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import janggi.domain.Position; +import janggi.domain.piece.Cha; +import janggi.domain.piece.Gung; +import janggi.domain.piece.Jol; +import janggi.domain.piece.Ma; +import janggi.domain.piece.Po; +import janggi.domain.piece.Sa; +import janggi.domain.piece.Sang; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ChuTest { + + @Test + @DisplayName("초나라 팀은 초기 배치대로 기물을 가진다.") + void createInitialChu() { + // given + Chu chu = Chu.createInitialChu(); + + // when & then + assertAll( + () -> assertThat(chu.makeSpots()).hasSize(16), + () -> assertThat(chu.findPiece(new Position(1, 1))).get().isInstanceOf(Cha.class), + () -> assertThat(chu.findPiece(new Position(9, 1))).get().isInstanceOf(Cha.class), + () -> assertThat(chu.findPiece(new Position(2, 1))).get().isInstanceOf(Ma.class), + () -> assertThat(chu.findPiece(new Position(8, 1))).get().isInstanceOf(Ma.class), + () -> assertThat(chu.findPiece(new Position(3, 1))).get().isInstanceOf(Sang.class), + () -> assertThat(chu.findPiece(new Position(7, 1))).get().isInstanceOf(Sang.class), + () -> assertThat(chu.findPiece(new Position(4, 1))).get().isInstanceOf(Sa.class), + () -> assertThat(chu.findPiece(new Position(6, 1))).get().isInstanceOf(Sa.class), + () -> assertThat(chu.findPiece(new Position(5, 2))).get().isInstanceOf(Gung.class), + () -> assertThat(chu.findPiece(new Position(2, 3))).get().isInstanceOf(Po.class), + () -> assertThat(chu.findPiece(new Position(8, 3))).get().isInstanceOf(Po.class), + () -> assertThat(chu.findPiece(new Position(1, 4))).get().isInstanceOf(Jol.class), + () -> assertThat(chu.findPiece(new Position(3, 4))).get().isInstanceOf(Jol.class), + () -> assertThat(chu.findPiece(new Position(5, 4))).get().isInstanceOf(Jol.class), + () -> assertThat(chu.findPiece(new Position(7, 4))).get().isInstanceOf(Jol.class), + () -> assertThat(chu.findPiece(new Position(9, 4))).get().isInstanceOf(Jol.class), + () -> assertThat(chu.findPiece(new Position(5, 5))).isEmpty() + ); + } + + @Test + @DisplayName("기물을 이동한 새 팀 상태를 반환한다.") + void move() { + // given + Team chu = Chu.createInitialChu(); + Position chuJolPosition = new Position(1, 4); + Position movedChuJolPosition = new Position(1, 5); + + // when + Team movedChu = chu.move(chuJolPosition, movedChuJolPosition); + + // then + assertAll( + () -> assertThat(movedChu.findPiece(chuJolPosition)).isEmpty(), + () -> assertThat(movedChu.findPiece(movedChuJolPosition)).get().isInstanceOf(Jol.class), + () -> assertThat(chu.findPiece(movedChuJolPosition)).isEmpty(), + () -> assertThat(chu.findPiece(chuJolPosition)).get().isInstanceOf(Jol.class) + ); + } + + @Test + @DisplayName("기물을 제거하면 새 팀 상태를 반환하여 원본은 유지한다.") + void remove() { + // given + Team chu = Chu.createInitialChu(); + Position chuJolPosition = new Position(1, 4); + + // when + Team removedChu = chu.remove(chuJolPosition); + + // then + assertAll( + () -> assertThat(removedChu.findPiece(chuJolPosition)).isEmpty(), + () -> assertThat(removedChu.makeSpots()).hasSize(15), + () -> assertThat(chu.findPiece(chuJolPosition)).get().isInstanceOf(Jol.class) + ); + } +} From 00832cc04227211429bd89a352d27451ef683ba5 Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 10:58:23 +0900 Subject: [PATCH 41/99] =?UTF-8?q?test:=20Han=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?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/test/java/janggi/domain/side/HanTest.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/test/java/janggi/domain/side/HanTest.java diff --git a/src/test/java/janggi/domain/side/HanTest.java b/src/test/java/janggi/domain/side/HanTest.java new file mode 100644 index 0000000000..577ef7f026 --- /dev/null +++ b/src/test/java/janggi/domain/side/HanTest.java @@ -0,0 +1,81 @@ +package janggi.domain.side; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import janggi.domain.Position; +import janggi.domain.piece.Cha; +import janggi.domain.piece.Gung; +import janggi.domain.piece.Jol; +import janggi.domain.piece.Ma; +import janggi.domain.piece.Po; +import janggi.domain.piece.Sa; +import janggi.domain.piece.Sang; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class HanTest { + + @Test + @DisplayName("한나라 팀은 초기 배치대로 기물을 가진다.") + void createInitialHan() { + // given + Han han = Han.createInitialHan(); + + // when & then + assertAll( + () -> assertThat(han.makeSpots()).hasSize(16), + () -> assertThat(han.findPiece(new Position(1, 10))).get().isInstanceOf(Cha.class), + () -> assertThat(han.findPiece(new Position(9, 10))).get().isInstanceOf(Cha.class), + () -> assertThat(han.findPiece(new Position(2, 10))).get().isInstanceOf(Ma.class), + () -> assertThat(han.findPiece(new Position(8, 10))).get().isInstanceOf(Ma.class), + () -> assertThat(han.findPiece(new Position(3, 10))).get().isInstanceOf(Sang.class), + () -> assertThat(han.findPiece(new Position(7, 10))).get().isInstanceOf(Sang.class), + () -> assertThat(han.findPiece(new Position(4, 10))).get().isInstanceOf(Sa.class), + () -> assertThat(han.findPiece(new Position(6, 10))).get().isInstanceOf(Sa.class), + () -> assertThat(han.findPiece(new Position(5, 9))).get().isInstanceOf(Gung.class), + () -> assertThat(han.findPiece(new Position(2, 8))).get().isInstanceOf(Po.class), + () -> assertThat(han.findPiece(new Position(8, 8))).get().isInstanceOf(Po.class), + () -> assertThat(han.findPiece(new Position(1, 7))).get().isInstanceOf(Jol.class), + () -> assertThat(han.findPiece(new Position(3, 7))).get().isInstanceOf(Jol.class), + () -> assertThat(han.findPiece(new Position(5, 7))).get().isInstanceOf(Jol.class), + () -> assertThat(han.findPiece(new Position(7, 7))).get().isInstanceOf(Jol.class), + () -> assertThat(han.findPiece(new Position(9, 7))).get().isInstanceOf(Jol.class), + () -> assertThat(han.findPiece(new Position(5, 6))).isEmpty() + ); + } + + @Test + @DisplayName("기물을 이동한 새 팀 상태를 반환한다.") + void move() { + // given + Team han = Han.createInitialHan(); + + // when + Team movedHan = han.move(new Position(1, 7), new Position(1, 6)); + + // then + assertAll( + () -> assertThat(movedHan.findPiece(new Position(1, 7))).isEmpty(), + () -> assertThat(movedHan.findPiece(new Position(1, 6))).get().isInstanceOf(Jol.class), + () -> assertThat(han.findPiece(new Position(1, 7))).get().isInstanceOf(Jol.class) + ); + } + + @Test + @DisplayName("기물을 제거하면 새 팀 상태를 반환하여 원본은 유지한다.") + void remove() { + // given + Team han = Han.createInitialHan(); + + // when + Team removedHan = han.remove(new Position(1, 7)); + + // then + assertAll( + () -> assertThat(removedHan.findPiece(new Position(1, 7))).isEmpty(), + () -> assertThat(removedHan.makeSpots()).hasSize(15), + () -> assertThat(han.findPiece(new Position(1, 7))).get().isInstanceOf(Jol.class) + ); + } +} From 31678c37a28790732273741134026c2f948959f0 Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 10:58:30 +0900 Subject: [PATCH 42/99] =?UTF-8?q?test:=20Jol=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?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 --- .../java/janggi/domain/piece/JolTest.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/test/java/janggi/domain/piece/JolTest.java diff --git a/src/test/java/janggi/domain/piece/JolTest.java b/src/test/java/janggi/domain/piece/JolTest.java new file mode 100644 index 0000000000..b3008a9a5b --- /dev/null +++ b/src/test/java/janggi/domain/piece/JolTest.java @@ -0,0 +1,86 @@ +package janggi.domain.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import janggi.domain.side.TeamType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class JolTest { + + @Test + @DisplayName("초나라 졸은 앞으로 한 칸 이동할 수 있다.") + void chuIsValidMovePatternForward() { + // given + Jol jol = new Jol(TeamType.CHU); + + // when & then + assertThat(jol.isValidMovePattern(4, 4, 4, 5)).isTrue(); + } + + @Test + @DisplayName("한나라 졸은 앞으로 한 칸 이동할 수 있다.") + void hanIsValidMovePatternForward() { + // given + Jol jol = new Jol(TeamType.HAN); + + // when & then + assertThat(jol.isValidMovePattern(4, 4, 4, 3)).isTrue(); + } + + @Test + @DisplayName("졸은 좌우로 한 칸 이동할 수 있다.") + void isValidMovePatternHorizontally() { + // given + Jol jolOfChu = new Jol(TeamType.CHU); + Jol jolOfHan = new Jol(TeamType.HAN); + + // when & then + assertAll( + () -> assertThat(jolOfChu.isValidMovePattern(4, 4, 5, 4)).isTrue(), + () -> assertThat(jolOfChu.isValidMovePattern(4, 4, 3, 4)).isTrue(), + () -> assertThat(jolOfHan.isValidMovePattern(4, 4, 5, 4)).isTrue(), + () -> assertThat(jolOfHan.isValidMovePattern(4, 4, 3, 4)).isTrue() + ); + } + + @Test + @DisplayName("졸은 뒤로 이동할 수 없다.") + void cannotMoveBackward() { + // given + Jol jolOfChu = new Jol(TeamType.CHU); + Jol hanJol = new Jol(TeamType.HAN); + + // when & then + assertAll( + () -> assertThat(jolOfChu.isValidMovePattern(4, 4, 4, 3)).isFalse(), + () -> assertThat(hanJol.isValidMovePattern(4, 4, 4, 5)).isFalse() + ); + } + + @Test + @DisplayName("졸은 두 칸 이상 이동하거나 대각선으로 이동할 수 없다.") + void cannotMoveInvalidPath() { + // given + Jol jol = new Jol(TeamType.CHU); + + // when & then + assertAll( + () -> assertThat(jol.isValidMovePattern(4, 4, 4, 6)).isFalse(), + () -> assertThat(jol.isValidMovePattern(4, 4, 2, 4)).isFalse(), + () -> assertThat(jol.isValidMovePattern(4, 4, 5, 5)).isFalse(), + () -> assertThat(jol.isValidMovePattern(4, 4, 3, 3)).isFalse() + ); + } + + @Test + @DisplayName("졸은 제자리로 이동할 수 없다.") + void cannotMoveSamePosition() { + // given + Jol jol = new Jol(TeamType.CHU); + + // when & then + assertThat(jol.isValidMovePattern(4, 4, 4, 4)).isFalse(); + } +} From 1b409194a0328e2e71e19c0363e29d68482c60b6 Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 10:58:35 +0900 Subject: [PATCH 43/99] =?UTF-8?q?test:=20Ma=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?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/test/java/janggi/domain/piece/MaTest.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/test/java/janggi/domain/piece/MaTest.java diff --git a/src/test/java/janggi/domain/piece/MaTest.java b/src/test/java/janggi/domain/piece/MaTest.java new file mode 100644 index 0000000000..9a589305cb --- /dev/null +++ b/src/test/java/janggi/domain/piece/MaTest.java @@ -0,0 +1,74 @@ +package janggi.domain.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import janggi.domain.Board; +import janggi.domain.Position; +import janggi.domain.side.TeamType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class MaTest { + + @Test + @DisplayName("마는 여덟 방향의 L자 이동을 할 수 있다.") + void isValidMovePattern() { + // given + Ma ma = new Ma(TeamType.CHU); + + // when & then + assertAll( + () -> assertThat(ma.isValidMovePattern(4, 4, 5, 6)).isTrue(), + () -> assertThat(ma.isValidMovePattern(4, 4, 3, 6)).isTrue(), + () -> assertThat(ma.isValidMovePattern(4, 4, 5, 2)).isTrue(), + () -> assertThat(ma.isValidMovePattern(4, 4, 3, 2)).isTrue(), + () -> assertThat(ma.isValidMovePattern(4, 4, 2, 5)).isTrue(), + () -> assertThat(ma.isValidMovePattern(4, 4, 2, 3)).isTrue(), + () -> assertThat(ma.isValidMovePattern(4, 4, 6, 5)).isTrue(), + () -> assertThat(ma.isValidMovePattern(4, 4, 6, 3)).isTrue() + ); + } + + @Test + @DisplayName("마는 직선이나 대각선으로 이동할 수 없고 제자리 이동도 할 수 없다.") + void cannotMoveInvalidPattern() { + // given + Ma ma = new Ma(TeamType.CHU); + + // when & then + assertAll( + () -> assertThat(ma.isValidMovePattern(4, 4, 4, 5)).isFalse(), + () -> assertThat(ma.isValidMovePattern(4, 4, 5, 5)).isFalse(), + () -> assertThat(ma.isValidMovePattern(4, 4, 4, 4)).isFalse() + ); + } + + @Test + @DisplayName("이동 경로의 첫 칸이 비어 있으면 마는 이동할 수 있다.") + void isValidPathWhenIntermediatePositionIsEmpty() { + // given + Ma ma = new Ma(TeamType.CHU); + Board board = Board.createInitialBoard(); + + // when + boolean result = ma.isObstaclesNotExist(new Position(2, 1), new Position(3, 3), board); + + // then + assertThat(result).isTrue(); + } + + @Test + @DisplayName("이동 경로의 첫 칸이 막혀 있으면 마는 이동할 수 없다.") + void cannotMoveWhenIntermediatePositionIsBlocked() { + // given + Ma ma = new Ma(TeamType.CHU); + Board board = Board.createInitialBoard(); + + // when + boolean result = ma.isObstaclesNotExist(new Position(2, 1), new Position(4, 2), board); + + // then + assertThat(result).isFalse(); + } +} From 1c6c40c1d28cfe3d2bbe61d174d19e01dd9c2388 Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 10:58:43 +0900 Subject: [PATCH 44/99] =?UTF-8?q?test:=20MovePath=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/MovePathTest.java | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 src/test/java/janggi/domain/MovePathTest.java diff --git a/src/test/java/janggi/domain/MovePathTest.java b/src/test/java/janggi/domain/MovePathTest.java new file mode 100644 index 0000000000..bba4db9084 --- /dev/null +++ b/src/test/java/janggi/domain/MovePathTest.java @@ -0,0 +1,158 @@ +package janggi.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class MovePathTest { + + @Test + @DisplayName("경로의 델타 합과 이동량이 같으면 일치한다.") + void matchesWhenTotalDeltaMatches() { + // given + MovePath movePath = new MovePath(List.of(Delta.createUp(), Delta.createRightUp())); + + // when + boolean result = movePath.matches(1, 2); + + // then + assertThat(result).isTrue(); + } + + @Test + @DisplayName("경로의 델타 합과 이동량이 다르면 일치하지 않는다.") + void doesNotMatchWhenTotalDeltaDiffers() { + // given + MovePath movePath = new MovePath(List.of(Delta.createUp(), Delta.createRightUp())); + + // when & then + assertAll( + () -> assertThat(movePath.matches(2, 1)).isFalse(), + () -> assertThat(movePath.matches(1, 1)).isFalse() + ); + } + + @Test + @DisplayName("한 칸 직선 경로는 같은 방향의 여러 칸 직선 이동과 방향이 일치한다.") + void matchesDirectionForStraightPath() { + // given + MovePath upwardPath = new MovePath(List.of(Delta.createUp())); + MovePath rightPath = new MovePath(List.of(Delta.createRight())); + + // when & then + assertAll( + () -> assertThat(upwardPath.matchesDirection(0, 3)).isTrue(), + () -> assertThat(upwardPath.matchesDirection(0, 1)).isTrue(), + () -> assertThat(rightPath.matchesDirection(4, 0)).isTrue(), + () -> assertThat(rightPath.matchesDirection(1, 0)).isTrue() + ); + } + + @Test + @DisplayName("한 칸 경로라도 방향이 다르거나 제자리 이동이면 일치하지 않는다.") + void doesNotMatchDirectionWhenDirectionIsDifferentOrSamePosition() { + // given + MovePath upwardPath = new MovePath(List.of(Delta.createUp())); + MovePath diagonalPath = new MovePath(List.of(Delta.createRightUp())); + + // when & then + assertAll( + () -> assertThat(upwardPath.matchesDirection(0, -2)).isFalse(), + () -> assertThat(upwardPath.matchesDirection(2, 0)).isFalse(), + () -> assertThat(upwardPath.matchesDirection(0, 0)).isFalse(), + () -> assertThat(diagonalPath.matchesDirection(2, 2)).isTrue(), + () -> assertThat(diagonalPath.matchesDirection(-2, -2)).isFalse(), + () -> assertThat(diagonalPath.matchesDirection(2, 1)).isFalse() + ); + } + + @Test + @DisplayName("두 칸 이상으로 구성된 경로는 matchesDirection으로 비교하지 않는다.") + void doesNotMatchDirectionWhenPathHasMultipleSteps() { + // given + MovePath movePath = new MovePath(List.of(Delta.createUp(), Delta.createRightUp())); + + // when + boolean result = movePath.matchesDirection(1, 2); + + // then + assertThat(result).isFalse(); + } + + @Test + @DisplayName("직선 경로는 시작 위치부터 도착 위치까지 모든 칸을 순서대로 만든다.") + void createStraightRoute() { + // given + MovePath movePath = new MovePath(List.of(Delta.createUp())); + + // when + List route = movePath.createRoute(new Position(1, 1), new Position(1, 4)); + + // then + assertThat(route).containsExactly( + new Position(1, 2), + new Position(1, 3), + new Position(1, 4) + ); + } + + @Test + @DisplayName("여러 단계 경로는 각 델타를 적용한 위치들을 순서대로 만든다.") + void createRouteForMultiStepPath() { + // given + MovePath movePath = new MovePath(List.of( + Delta.createUp(), + Delta.createRightUp(), + Delta.createRightUp() + )); + + // when + List route = movePath.createRoute(new Position(4, 4), new Position(6, 7)); + + // then + assertThat(route).containsExactly( + new Position(4, 5), + new Position(5, 6), + new Position(6, 7) + ); + } + + @Test + @DisplayName("중간 경유지는 도착 위치를 제외한 경로만 반환한다.") + void intermediatePositionsExcludesDestination() { + // given + MovePath straightPath = new MovePath(List.of(Delta.createUp())); + MovePath multiStepPath = new MovePath(List.of( + Delta.createUp(), + Delta.createRightUp(), + Delta.createRightUp() + )); + + // when & then + assertAll( + () -> assertThat(straightPath.intermediatePositions(new Position(1, 1), new Position(1, 4))) + .containsExactly(new Position(1, 2), new Position(1, 3)), + () -> assertThat(multiStepPath.intermediatePositions(new Position(4, 4), new Position(6, 7))) + .containsExactly(new Position(4, 5), new Position(5, 6)) + ); + } + + @Test + @DisplayName("한 칸 이동 경로의 중간 경유지는 없다.") + void intermediatePositionsIsEmptyWhenMoveHasNoMiddleStep() { + // given + MovePath movePath = new MovePath(List.of(Delta.createUp())); + + // when + List intermediatePositions = movePath.intermediatePositions( + new Position(3, 3), + new Position(3, 4) + ); + + // then + assertThat(intermediatePositions).isEmpty(); + } +} From c41f608830f3a078cc3549972ac84de772bd6f19 Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 10:58:52 +0900 Subject: [PATCH 45/99] =?UTF-8?q?test:=20Pieces=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/PiecesTest.java | 67 +++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/test/java/janggi/domain/PiecesTest.java diff --git a/src/test/java/janggi/domain/PiecesTest.java b/src/test/java/janggi/domain/PiecesTest.java new file mode 100644 index 0000000000..2fc0ed9f24 --- /dev/null +++ b/src/test/java/janggi/domain/PiecesTest.java @@ -0,0 +1,67 @@ +package janggi.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import janggi.domain.piece.Jol; +import janggi.domain.piece.PieceType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PiecesTest { + + @Test + @DisplayName("기물을 이동하면 새 Pieces를 반환하고 원본은 유지한다.") + void move() { + // given + Pieces pieces = Pieces.createChu(); + Position start = new Position(1, 4); + Position end = new Position(1, 5); + + // when + Pieces movedPieces = pieces.move(start, end); + + // then + assertAll( + () -> assertThat(pieces.findPiece(start)).get().isInstanceOf(Jol.class), + () -> assertThat(pieces.findPiece(end)).isEmpty(), + () -> assertThat(movedPieces.findPiece(start)).isEmpty(), + () -> assertThat(movedPieces.findPiece(end)).get().isInstanceOf(Jol.class) + ); + } + + @Test + @DisplayName("기물을 제거하면 새 Pieces를 반환하고 원본은 유지한다.") + void remove() { + // given + Pieces pieces = Pieces.createChu(); + Position target = new Position(1, 4); + + // when + Pieces removedPieces = pieces.remove(target); + + // then + assertAll( + () -> assertThat(pieces.findPiece(target)).get().isInstanceOf(Jol.class), + () -> assertThat(removedPieces.findPiece(target)).isEmpty(), + () -> assertThat(removedPieces.makeSpots()).hasSize(15) + ); + } + + @Test + @DisplayName("보드 스냅샷은 좌표 키와 기물 이름을 포함한다.") + void makeSpots() { + // given + Pieces pieces = Pieces.createChu(); + + // when + var boardSpots = pieces.makeSpots(); + + // then + assertThat(boardSpots) + .anySatisfy(boardSpot -> { + assertThat(boardSpot.position()).isEqualTo("5,2"); + assertThat(boardSpot.pieceName()).isEqualTo(PieceType.GUNG.getNickname()); + }); + } +} From 214dbaf40341838562ca571554d29bce2f9b9043 Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 10:59:04 +0900 Subject: [PATCH 46/99] =?UTF-8?q?test:=20Position=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/PositionTest.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/test/java/janggi/domain/PositionTest.java diff --git a/src/test/java/janggi/domain/PositionTest.java b/src/test/java/janggi/domain/PositionTest.java new file mode 100644 index 0000000000..5cf61ed48c --- /dev/null +++ b/src/test/java/janggi/domain/PositionTest.java @@ -0,0 +1,84 @@ +package janggi.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PositionTest { + + @Test + @DisplayName("좌표 문자열 두 개를 받아 Position을 생성한다.") + void makePosition() { + // given + List rawPosition = List.of("3", "7"); + + // when + Position position = Position.makePosition(rawPosition); + + // then + assertAll( + () -> assertThat(position.getX()).isEqualTo(3), + () -> assertThat(position.getY()).isEqualTo(7) + ); + } + + @Test + @DisplayName("좌표 입력이 두 개가 아니면 예외가 발생한다.") + void throwWhenInputSizeIsNotTwo() { + // given + List rawPosition = List.of("3"); + + // when & then + assertThatThrownBy(() -> Position.makePosition(rawPosition)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("기물의 좌표는 두 개로 입력해야 합니다."); + } + + @Test + @DisplayName("좌표 입력이 숫자가 아니면 예외가 발생한다.") + void throwWhenInputContainsNonNumericValue() { + // given + List rawPosition = List.of("a", "7"); + + // when & then + assertThatThrownBy(() -> Position.makePosition(rawPosition)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("좌표는 숫자가 입력되어야 합니다."); + } + + @Test + @DisplayName("델타만큼 이동한 새 Position을 반환한다.") + void move() { + // given + Position position = new Position(4, 4); + + // when + Position movedPosition = position.move(Delta.createRightUp()); + + // then + assertAll( + () -> assertThat(movedPosition).isEqualTo(new Position(5, 5)), + () -> assertThat(position).isEqualTo(new Position(4, 4)) + ); + } + + @Test + @DisplayName("같은 좌표의 Position은 동등하고 해시코드도 같다.") + void equalsAndHashCode() { + // given + Position first = new Position(2, 8); + Position second = new Position(2, 8); + Position other = new Position(8, 2); + + // when & then + assertAll( + () -> assertThat(first).isEqualTo(second), + () -> assertThat(first.hashCode()).isEqualTo(second.hashCode()), + () -> assertThat(first).isNotEqualTo(other) + ); + } +} From 88cef728847828ed6ca23277028c3596ad54c399 Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 10:59:17 +0900 Subject: [PATCH 47/99] =?UTF-8?q?test:=20Po=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?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/test/java/janggi/domain/piece/PoTest.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/test/java/janggi/domain/piece/PoTest.java diff --git a/src/test/java/janggi/domain/piece/PoTest.java b/src/test/java/janggi/domain/piece/PoTest.java new file mode 100644 index 0000000000..829b701f74 --- /dev/null +++ b/src/test/java/janggi/domain/piece/PoTest.java @@ -0,0 +1,85 @@ +package janggi.domain.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import janggi.domain.Board; +import janggi.domain.Position; +import janggi.domain.side.TeamType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PoTest { + + @Test + @DisplayName("포는 상하좌우로 직선 이동할 수 있다.") + void isValidMovePatternStraight() { + // given + Po po = new Po(TeamType.CHU); + + // when & then + assertAll( + () -> assertThat(po.isValidMovePattern(4, 4, 4, 8)).isTrue(), + () -> assertThat(po.isValidMovePattern(4, 4, 4, 1)).isTrue(), + () -> assertThat(po.isValidMovePattern(4, 4, 7, 4)).isTrue(), + () -> assertThat(po.isValidMovePattern(4, 4, 1, 4)).isTrue() + ); + } + + @Test + @DisplayName("포는 대각선이나 제자리로 이동할 수 없다.") + void cannotMoveInvalidPattern() { + // given + Po po = new Po(TeamType.CHU); + + // when & then + assertAll( + () -> assertThat(po.isValidMovePattern(4, 4, 5, 5)).isFalse(), + () -> assertThat(po.isValidMovePattern(4, 4, 4, 4)).isFalse() + ); + } + + @Test + @DisplayName("포는 사이에 기물이 하나만 있으면 이동할 수 있다.") + void isObstaclesNotExistWhenExactlyOneBridgeExists() { + // given + Po po = new Po(TeamType.CHU); + Board board = Board.createInitialBoard(); + Board movedBoard = board.move(new Position(1, 4), new Position(2, 4), TeamType.CHU); + + // when + boolean result = po.isObstaclesNotExist(new Position(2, 3), new Position(2, 6), movedBoard); + + // then + assertThat(result).isTrue(); + } + + @Test + @DisplayName("포는 사이에 기물이 없으면 이동할 수 없다.") + void cannotMoveWithoutBridge() { + // given + Po po = new Po(TeamType.CHU); + Board board = Board.createInitialBoard(); + + // when + boolean result = po.isObstaclesNotExist(new Position(2, 3), new Position(2, 6), board); + + // then + assertThat(result).isFalse(); + } + + @Test + @DisplayName("포는 포를 다리로 사용할 수 없고 포를 잡을 수도 없다.") + void cannotUsePoAsBridgeOrTarget() { + // given + Po po = new Po(TeamType.CHU); + Board board = Board.createInitialBoard(); + Board movedBoard = board.move(new Position(1, 4), new Position(2, 4), TeamType.CHU); + + // when & then + assertAll( + () -> assertThat(po.isObstaclesNotExist(new Position(2, 3), new Position(2, 10), board)).isFalse(), + () -> assertThat(po.isObstaclesNotExist(new Position(2, 3), new Position(2, 8), movedBoard)).isFalse() + ); + } +} From 64a72f428c737485f56f1eea4ca494d87eb1a608 Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 10:59:25 +0900 Subject: [PATCH 48/99] =?UTF-8?q?test:=20Sang=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/domain/piece/SangTest.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/test/java/janggi/domain/piece/SangTest.java diff --git a/src/test/java/janggi/domain/piece/SangTest.java b/src/test/java/janggi/domain/piece/SangTest.java new file mode 100644 index 0000000000..b812c13286 --- /dev/null +++ b/src/test/java/janggi/domain/piece/SangTest.java @@ -0,0 +1,74 @@ +package janggi.domain.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import janggi.domain.Board; +import janggi.domain.Position; +import janggi.domain.side.TeamType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class SangTest { + + @Test + @DisplayName("상은 여덟 방향으로 이동할 수 있다.") + void isValidMovePattern() { + // given + Sang sang = new Sang(TeamType.CHU); + + // when & then + assertAll( + () -> assertThat(sang.isValidMovePattern(4, 4, 7, 6)).isTrue(), + () -> assertThat(sang.isValidMovePattern(4, 4, 1, 6)).isTrue(), + () -> assertThat(sang.isValidMovePattern(4, 4, 7, 2)).isTrue(), + () -> assertThat(sang.isValidMovePattern(4, 4, 1, 2)).isTrue(), + () -> assertThat(sang.isValidMovePattern(4, 4, 6, 7)).isTrue(), + () -> assertThat(sang.isValidMovePattern(4, 4, 6, 1)).isTrue(), + () -> assertThat(sang.isValidMovePattern(4, 4, 2, 7)).isTrue(), + () -> assertThat(sang.isValidMovePattern(4, 4, 2, 1)).isTrue() + ); + } + + @Test + @DisplayName("상은 잘못된 이동 패턴이나 제자리 이동을 할 수 없다.") + void cannotMoveInvalidPattern() { + // given + Sang sang = new Sang(TeamType.CHU); + + // when & then + assertAll( + () -> assertThat(sang.isValidMovePattern(4, 4, 4, 5)).isFalse(), + () -> assertThat(sang.isValidMovePattern(4, 4, 5, 5)).isFalse(), + () -> assertThat(sang.isValidMovePattern(4, 4, 4, 4)).isFalse() + ); + } + + @Test + @DisplayName("이동 경로가 비어 있으면 상은 이동할 수 있다.") + void isObstaclesNotExistWhenIntermediatePositionsAreEmpty() { + // given + Sang sang = new Sang(TeamType.CHU); + Board board = Board.createInitialBoard(); + + // when + boolean result = sang.isObstaclesNotExist(new Position(4, 5), new Position(7, 7), board); + + // then + assertThat(result).isTrue(); + } + + @Test + @DisplayName("이동 경로 중 하나라도 막혀 있으면 상은 이동할 수 없다.") + void cannotMoveWhenIntermediatePositionIsBlocked() { + // given + Sang sang = new Sang(TeamType.CHU); + Board board = Board.createInitialBoard(); + + // when + boolean result = sang.isObstaclesNotExist(new Position(4, 2), new Position(7, 4), board); + + // then + assertThat(result).isFalse(); + } +} From 28279e813d4b3ec916b886574d2c760e3818ed04 Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 10:59:30 +0900 Subject: [PATCH 49/99] =?UTF-8?q?test:=20Sa=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?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/test/java/janggi/domain/piece/SaTest.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/test/java/janggi/domain/piece/SaTest.java diff --git a/src/test/java/janggi/domain/piece/SaTest.java b/src/test/java/janggi/domain/piece/SaTest.java new file mode 100644 index 0000000000..95a47492a7 --- /dev/null +++ b/src/test/java/janggi/domain/piece/SaTest.java @@ -0,0 +1,56 @@ +package janggi.domain.piece; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import janggi.domain.side.TeamType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class SaTest { + + @Test + @DisplayName("사는 상하좌우 한 칸 이동할 수 있다.") + void isValidMovePatternStraightOneStep() { + // given + Sa sa = new Sa(TeamType.CHU); + + // when & then + assertAll( + () -> assertThat(sa.isValidMovePattern(4, 4, 4, 5)).isTrue(), + () -> assertThat(sa.isValidMovePattern(4, 4, 4, 3)).isTrue(), + () -> assertThat(sa.isValidMovePattern(4, 4, 5, 4)).isTrue(), + () -> assertThat(sa.isValidMovePattern(4, 4, 3, 4)).isTrue() + ); + } + + @Test + @DisplayName("사는 대각선 한 칸 이동할 수 있다.") + void isValidMovePatternDiagonalOneStep() { + // given + Sa sa = new Sa(TeamType.CHU); + + // when & then + assertAll( + () -> assertThat(sa.isValidMovePattern(4, 4, 5, 5)).isTrue(), + () -> assertThat(sa.isValidMovePattern(4, 4, 5, 3)).isTrue(), + () -> assertThat(sa.isValidMovePattern(4, 4, 3, 5)).isTrue(), + () -> assertThat(sa.isValidMovePattern(4, 4, 3, 3)).isTrue() + ); + } + + @Test + @DisplayName("사는 두 칸 이상 이동하거나 제자리로 이동할 수 없다.") + void cannotMoveInvalidPattern() { + // given + Sa sa = new Sa(TeamType.CHU); + + // when & then + assertAll( + () -> assertThat(sa.isValidMovePattern(4, 4, 6, 4)).isFalse(), + () -> assertThat(sa.isValidMovePattern(4, 4, 6, 6)).isFalse(), + () -> assertThat(sa.isValidMovePattern(4, 4, 4, 6)).isFalse(), + () -> assertThat(sa.isValidMovePattern(4, 4, 4, 4)).isFalse() + ); + } +} From a3ff7c53776f0ba1f109d1cc5e8ffc2b8040299a Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 10:59:35 +0900 Subject: [PATCH 50/99] =?UTF-8?q?test:=20Turn=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/TurnTest.java | 107 ++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/test/java/janggi/domain/TurnTest.java diff --git a/src/test/java/janggi/domain/TurnTest.java b/src/test/java/janggi/domain/TurnTest.java new file mode 100644 index 0000000000..7dd1e74acd --- /dev/null +++ b/src/test/java/janggi/domain/TurnTest.java @@ -0,0 +1,107 @@ +package janggi.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import janggi.domain.piece.Jol; +import janggi.domain.piece.Piece; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TurnTest { + + @Test + @DisplayName("초기 턴의 다음 차례는 초나라다.") + void createInitialTurn() { + // given + Turn turn = Turn.createInitialTurn(); + + // when + String nextTurnTeam = turn.nextTurnTeam(); + + // then + assertThat(nextTurnTeam).isEqualTo("초나라"); + } + + @Test + @DisplayName("현재 차례 팀의 기물 존재 여부를 확인한다.") + void isMyTeamPieceExist() { + // given + Turn turn = Turn.createInitialTurn(); + Position chuJolPosition = new Position(1, 4); + Position hanJolPosition = new Position(1, 7); + + // when & then + assertAll( + () -> assertThat(turn.isMyTeamPieceExist(chuJolPosition)).isTrue(), + () -> assertThat(turn.isMyTeamPieceExist(hanJolPosition)).isFalse() + ); + } + + @Test + @DisplayName("현재 차례 팀의 기물을 조회한다.") + void findPiece() { + // given + Turn turn = Turn.createInitialTurn(); + Position chuJolPosition = new Position(1, 4); + + // when + Piece piece = turn.findPiece(chuJolPosition); + + // then + assertThat(piece).isInstanceOf(Jol.class); + } + + @Test + @DisplayName("현재 차례 팀의 올바른 이동은 허용한다.") + void canMove() { + // given + Turn turn = Turn.createInitialTurn(); + Position chuJolStartPosition = new Position(1, 4); + Position chuJolEndPosition = new Position(1, 5); + + // when & then + assertThatCode(() -> turn.canMove(chuJolStartPosition, chuJolEndPosition)) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("현재 차례 팀의 잘못된 이동은 예외가 발생한다.") + void throwWhenMoveIsInvalid() { + // given + Turn turn = Turn.createInitialTurn(); + Position chuJolStartPosition = new Position(1, 4); + Position chuJolEndPosition = new Position(1, 6); + + // when & then + assertThatThrownBy(() -> turn.canMove(chuJolStartPosition, chuJolEndPosition)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."); + } + + @Test + @DisplayName("기물을 이동하면 새 Turn을 반환한다.") + void move() { + // given + Turn turn = Turn.createInitialTurn(); + + // when + Turn movedTurn = turn.move(new Position(1, 4), new Position(1, 5)); + + // then + assertAll( + () -> assertThat(turn.nextTurnTeam()).isEqualTo("초나라"), + () -> assertThat(movedTurn.nextTurnTeam()).isEqualTo("한나라"), + () -> assertThat(turn.makeBoardSnapShot()).anySatisfy(boardSpot -> { + assertThat(boardSpot.position()).isEqualTo("1,4"); + assertThat(boardSpot.pieceName()).isEqualTo("졸"); + }), + () -> assertThat(movedTurn.makeBoardSnapShot()).anySatisfy(boardSpot -> { + assertThat(boardSpot.position()).isEqualTo("1,5"); + assertThat(boardSpot.pieceName()).isEqualTo("졸"); + }) + ); + } +} From d03b4bfd33cff4d2c8484904b2a91ec8617bab95 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 11:05:21 +0900 Subject: [PATCH 51/99] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=BB=A8=EB=B2=A4=EC=85=98=20=EC=A4=80=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Board.java | 50 ++++++++++----------- src/main/java/janggi/domain/JanggiGame.java | 8 ++-- src/main/java/janggi/domain/MovePath.java | 24 +++++----- src/main/java/janggi/domain/Pieces.java | 30 ++++++------- 4 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 96e6d1375a..4773fddedc 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -47,11 +47,6 @@ public Piece findNextTurnTeamPiece(Position position, TeamType beforeTeamType) { return findTeamPiece(position, nowTeam); } - private Piece findTeamPiece(Position position, Team nowTeam) { - return nowTeam.findPiece(position) - .orElseThrow(() -> new IllegalArgumentException("입력한 위치에 기물이 없습니다.")); - } - public Optional findPiece(Position position) { Optional chuPiece = chu.findPiece(position); if (chuPiece.isPresent()) { @@ -64,24 +59,17 @@ public boolean hasPiece(Position position) { return findPiece(position).isPresent(); } - private Team currentTeam(TeamType nowTurn) { - if (nowTurn == TeamType.CHU) { - return chu; - } - return han; - } - - private Team opponentTeam(TeamType nowTurn) { - if (nowTurn == TeamType.CHU) { - return han; - } - return chu; + public void canMove(Position startPosition, Position endPosition, TeamType nowTeam) { + validateRange(endPosition); + Piece piece = findTeamPiece(startPosition, currentTeam(nowTeam)); + validateTargetPosition(currentTeam(nowTeam), endPosition); + validateCanMove(piece, startPosition, endPosition); } public Board move( - Position startPosition, - Position endPosition, - TeamType nowTurn + Position startPosition, + Position endPosition, + TeamType nowTurn ) { validateRange(startPosition); validateRange(endPosition); @@ -93,11 +81,23 @@ public Board move( return createMovedBoard(nowTurn, movedCurrentTeam, remainedOpponentTeam); } - public void canMove(Position startPosition, Position endPosition, TeamType nowTeam) { - validateRange(endPosition); - Piece piece = findTeamPiece(startPosition, currentTeam(nowTeam)); - validateTargetPosition(currentTeam(nowTeam), endPosition); - validateCanMove(piece, startPosition, endPosition); + private Piece findTeamPiece(Position position, Team nowTeam) { // public 메서드 아래로 두는지 질문 + return nowTeam.findPiece(position) + .orElseThrow(() -> new IllegalArgumentException("입력한 위치에 기물이 없습니다.")); + } + + private Team currentTeam(TeamType nowTurn) { + if (nowTurn == TeamType.CHU) { + return chu; + } + return han; + } + + private Team opponentTeam(TeamType nowTurn) { + if (nowTurn == TeamType.CHU) { + return han; + } + return chu; } private void validateCanMove(Piece piece, Position piecePosition, Position targetPosition) { diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index ac58715c9c..7571f37438 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -41,10 +41,6 @@ public Piece findPiece(Position position) { return getLastTurn().findPiece(position); } - private Turn getLastTurn() { - return turns.getLast(); - } - public void doGame(Position startPosition, Position endPosition) { Turn lastTurn = getLastTurn(); Turn newTurn = lastTurn.move(startPosition, endPosition); @@ -55,4 +51,8 @@ public String getCurrentTurnTeamName() { Turn lastTurn = getLastTurn(); return lastTurn.nextTurnTeam(); } + + private Turn getLastTurn() { + return turns.getLast(); + } } diff --git a/src/main/java/janggi/domain/MovePath.java b/src/main/java/janggi/domain/MovePath.java index dae4440440..1dbe99a795 100644 --- a/src/main/java/janggi/domain/MovePath.java +++ b/src/main/java/janggi/domain/MovePath.java @@ -31,18 +31,6 @@ public boolean matchesDirection(int dx, int dy) { && Integer.signum(dy) == Integer.signum(delta.dy()); } - private int totalDx() { - return path.stream() - .mapToInt(Delta::dx) - .sum(); - } - - private int totalDy() { - return path.stream() - .mapToInt(Delta::dy) - .sum(); - } - public List createRoute(Position start, Position end) { List route = new ArrayList<>(); Position current = start; @@ -65,6 +53,18 @@ public List intermediatePositions(Position start, Position end) { return route; } + private int totalDx() { + return path.stream() + .mapToInt(Delta::dx) + .sum(); + } + + private int totalDy() { + return path.stream() + .mapToInt(Delta::dy) + .sum(); + } + private List createStraightRoute(Position end, List route, Position current) { Delta delta = path.getFirst(); while (!current.equals(end)) { diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index 7393ad325a..169b3e470d 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -63,6 +63,21 @@ public static Pieces createChu() { } + public List makeSpots() { + List boardSpots = new ArrayList<>(); + for (Map.Entry entry : value.entrySet()) { + boardSpots.add(new BoardSpot( + entry.getKey().makePositionKey(), + entry.getValue().nickname() + )); + } + return boardSpots; + } + + public Optional findPiece(Position position) { + return Optional.ofNullable(value.get(position)); + } + private static void createChas(Map pieces, int indexY, TeamType teamType) { pieces.put(new Position(1, indexY), new Cha(teamType)); pieces.put(new Position(9, indexY), new Cha(teamType)); @@ -97,19 +112,4 @@ private static void createJols(Map pieces, int indexY, TeamType pieces.put(new Position(i, indexY), new Jol(teamType)); } } - - public List makeSpots() { - List boardSpots = new ArrayList<>(); - for (Map.Entry entry : value.entrySet()) { - boardSpots.add(new BoardSpot( - entry.getKey().makePositionKey(), - entry.getValue().nickname() - )); - } - return boardSpots; - } - - public Optional findPiece(Position position) { - return Optional.ofNullable(value.get(position)); - } } From 70ccba4e9f66ac34f381f1077a6f8793c4dd23ea Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 11:06:36 +0900 Subject: [PATCH 52/99] =?UTF-8?q?refactor:=20=EC=83=9D=EC=84=B1=EC=9E=90?= =?UTF-8?q?=20=EC=A0=91=EA=B7=BC=20=EC=A0=9C=EC=96=B4=EC=9E=90=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/main/java/janggi/domain/Turn.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index ce1c36dc8b..af32bfa92e 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -10,7 +10,7 @@ public class Turn { private final TeamType movedTeam; private final Board board; - public Turn(TeamType movedTeam, Board board) { + private Turn(TeamType movedTeam, Board board) { this.movedTeam = movedTeam; this.board = board; } From 7723bfb35c770aa957cd5f28f79578c855e852db Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 11:10:20 +0900 Subject: [PATCH 53/99] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85,=20=EB=B3=80=EC=88=98=EB=AA=85=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/Board.java | 4 ++-- src/main/java/janggi/domain/Pieces.java | 8 ++++---- src/main/java/janggi/domain/side/Chu.java | 4 ++-- src/main/java/janggi/domain/side/Han.java | 4 ++-- src/main/java/janggi/domain/side/Team.java | 2 +- src/test/java/janggi/domain/PiecesTest.java | 6 +++--- src/test/java/janggi/domain/side/ChuTest.java | 4 ++-- src/test/java/janggi/domain/side/HanTest.java | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 4773fddedc..7e826748bd 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -29,8 +29,8 @@ public static Board createInitialBoard() { } public List makeSnapShot() { - List chuBoardSpots = chu.makeSpots(); - List hanBoardSpots = han.makeSpots(); + List chuBoardSpots = chu.makeSnapShot(); + List hanBoardSpots = han.makeSnapShot(); List boardSpots = new ArrayList<>(chuBoardSpots); boardSpots.addAll(hanBoardSpots); return boardSpots; diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index 169b3e470d..ffdc7e6ced 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -63,15 +63,15 @@ public static Pieces createChu() { } - public List makeSpots() { - List boardSpots = new ArrayList<>(); + public List makeSnapShot() { + List snapShot = new ArrayList<>(); for (Map.Entry entry : value.entrySet()) { - boardSpots.add(new BoardSpot( + snapShot.add(new BoardSpot( entry.getKey().makePositionKey(), entry.getValue().nickname() )); } - return boardSpots; + return snapShot; } public Optional findPiece(Position position) { diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index 0291ae8a1a..b6b7cbc483 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -20,8 +20,8 @@ public static Chu createInitialChu() { } @Override - public List makeSpots() { - return pieces.makeSpots(); + public List makeSnapShot() { + return pieces.makeSnapShot(); } @Override diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index e0c6cd611e..8cf08e5ad8 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -20,8 +20,8 @@ public static Han createInitialHan() { } @Override - public List makeSpots() { - return pieces.makeSpots(); + public List makeSnapShot() { + return pieces.makeSnapShot(); } @Override diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index 88d9bf4e00..c8c0104f70 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -8,7 +8,7 @@ public interface Team { - List makeSpots(); + List makeSnapShot(); Optional findPiece(Position position); diff --git a/src/test/java/janggi/domain/PiecesTest.java b/src/test/java/janggi/domain/PiecesTest.java index 2fc0ed9f24..1edd453fef 100644 --- a/src/test/java/janggi/domain/PiecesTest.java +++ b/src/test/java/janggi/domain/PiecesTest.java @@ -44,18 +44,18 @@ void remove() { assertAll( () -> assertThat(pieces.findPiece(target)).get().isInstanceOf(Jol.class), () -> assertThat(removedPieces.findPiece(target)).isEmpty(), - () -> assertThat(removedPieces.makeSpots()).hasSize(15) + () -> assertThat(removedPieces.makeSnapShot()).hasSize(15) ); } @Test @DisplayName("보드 스냅샷은 좌표 키와 기물 이름을 포함한다.") - void makeSpots() { + void makeSnapShot() { // given Pieces pieces = Pieces.createChu(); // when - var boardSpots = pieces.makeSpots(); + var boardSpots = pieces.makeSnapShot(); // then assertThat(boardSpots) diff --git a/src/test/java/janggi/domain/side/ChuTest.java b/src/test/java/janggi/domain/side/ChuTest.java index eba14d690a..442a0d2ed1 100644 --- a/src/test/java/janggi/domain/side/ChuTest.java +++ b/src/test/java/janggi/domain/side/ChuTest.java @@ -24,7 +24,7 @@ void createInitialChu() { // when & then assertAll( - () -> assertThat(chu.makeSpots()).hasSize(16), + () -> assertThat(chu.makeSnapShot()).hasSize(16), () -> assertThat(chu.findPiece(new Position(1, 1))).get().isInstanceOf(Cha.class), () -> assertThat(chu.findPiece(new Position(9, 1))).get().isInstanceOf(Cha.class), () -> assertThat(chu.findPiece(new Position(2, 1))).get().isInstanceOf(Ma.class), @@ -78,7 +78,7 @@ void remove() { // then assertAll( () -> assertThat(removedChu.findPiece(chuJolPosition)).isEmpty(), - () -> assertThat(removedChu.makeSpots()).hasSize(15), + () -> assertThat(removedChu.makeSnapShot()).hasSize(15), () -> assertThat(chu.findPiece(chuJolPosition)).get().isInstanceOf(Jol.class) ); } diff --git a/src/test/java/janggi/domain/side/HanTest.java b/src/test/java/janggi/domain/side/HanTest.java index 577ef7f026..5be46b543f 100644 --- a/src/test/java/janggi/domain/side/HanTest.java +++ b/src/test/java/janggi/domain/side/HanTest.java @@ -24,7 +24,7 @@ void createInitialHan() { // when & then assertAll( - () -> assertThat(han.makeSpots()).hasSize(16), + () -> assertThat(han.makeSnapShot()).hasSize(16), () -> assertThat(han.findPiece(new Position(1, 10))).get().isInstanceOf(Cha.class), () -> assertThat(han.findPiece(new Position(9, 10))).get().isInstanceOf(Cha.class), () -> assertThat(han.findPiece(new Position(2, 10))).get().isInstanceOf(Ma.class), @@ -74,7 +74,7 @@ void remove() { // then assertAll( () -> assertThat(removedHan.findPiece(new Position(1, 7))).isEmpty(), - () -> assertThat(removedHan.makeSpots()).hasSize(15), + () -> assertThat(removedHan.makeSnapShot()).hasSize(15), () -> assertThat(han.findPiece(new Position(1, 7))).get().isInstanceOf(Jol.class) ); } From 3a5c315f27d2f40905359951ad261d2ad5b6a470 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 11:11:36 +0900 Subject: [PATCH 54/99] =?UTF-8?q?refactor:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= 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, 8 insertions(+), 14 deletions(-) diff --git a/src/main/java/janggi/domain/piece/PieceType.java b/src/main/java/janggi/domain/piece/PieceType.java index 1c25338efa..2b6fc7d98f 100644 --- a/src/main/java/janggi/domain/piece/PieceType.java +++ b/src/main/java/janggi/domain/piece/PieceType.java @@ -1,28 +1,22 @@ package janggi.domain.piece; public enum PieceType { - CHA("차", 13), - GUNG("궁", 0), - JOL("졸", 2), - MA("마", 5), - PO("포", 7), - SA("사", 3), - SANG("상", 3), + CHA("차"), + GUNG("궁"), + JOL("졸"), + MA("마"), + PO("포"), + SA("사"), + SANG("상"), ; - PieceType(String nickname, int score) { + PieceType(String nickname) { this.nickname = nickname; - this.score = score; } private final String nickname; - private final int score; public String getNickname() { return nickname; } - - public int getScore() { - return score; - } } From 0df1517dbfc0f1a9f83e89dd2130156c4b9f0311 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 12:44:52 +0900 Subject: [PATCH 55/99] =?UTF-8?q?refactor:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/util/ExceptionHandler.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/janggi/util/ExceptionHandler.java b/src/main/java/janggi/util/ExceptionHandler.java index 7ee81fe2ea..b1caf0db90 100644 --- a/src/main/java/janggi/util/ExceptionHandler.java +++ b/src/main/java/janggi/util/ExceptionHandler.java @@ -14,11 +14,4 @@ public static T retryUntilSuccess(Supplier supplier) { } } } - - public static void retryUntilSuccess(Runnable runnable) { - retryUntilSuccess(() -> { - runnable.run(); - return null; - }); - } } From 14b6120c0e13434bafaedfa5eb09d8e1ca5dddaf Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 13:59:49 +0900 Subject: [PATCH 56/99] =?UTF-8?q?refactor:=20=EC=9E=A5=EA=B8=B0=ED=8C=90?= =?UTF-8?q?=20=EC=B6=9C=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=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/Board.java | 17 ++- src/main/java/janggi/domain/JanggiGame.java | 4 +- src/main/java/janggi/domain/Pieces.java | 16 ++- src/main/java/janggi/domain/Turn.java | 5 +- src/main/java/janggi/domain/piece/Cha.java | 5 + src/main/java/janggi/domain/piece/Gung.java | 5 + src/main/java/janggi/domain/piece/Jol.java | 5 + src/main/java/janggi/domain/piece/Ma.java | 5 + src/main/java/janggi/domain/piece/Piece.java | 3 + src/main/java/janggi/domain/piece/Po.java | 5 + src/main/java/janggi/domain/piece/Sa.java | 5 + src/main/java/janggi/domain/piece/Sang.java | 5 + src/main/java/janggi/domain/side/Chu.java | 5 +- src/main/java/janggi/domain/side/Han.java | 5 +- src/main/java/janggi/domain/side/Team.java | 5 +- src/main/java/janggi/dto/BoardSpot.java | 8 +- src/main/java/janggi/dto/BoardSpots.java | 10 ++ src/main/java/janggi/view/OutputView.java | 105 ++++++++++++------ src/test/java/janggi/domain/PiecesTest.java | 16 +-- src/test/java/janggi/domain/TurnTest.java | 17 +-- src/test/java/janggi/domain/side/ChuTest.java | 4 +- src/test/java/janggi/domain/side/HanTest.java | 4 +- 22 files changed, 172 insertions(+), 87 deletions(-) create mode 100644 src/main/java/janggi/dto/BoardSpots.java diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 7e826748bd..94030a21ac 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -6,8 +6,8 @@ import janggi.domain.side.Team; import janggi.domain.side.TeamType; import janggi.dto.BoardSpot; -import java.util.ArrayList; -import java.util.List; +import janggi.dto.BoardSpots; +import java.util.HashMap; import java.util.Optional; public class Board { @@ -28,12 +28,10 @@ public static Board createInitialBoard() { return new Board(Chu.createInitialChu(), Han.createInitialHan()); } - public List makeSnapShot() { - List chuBoardSpots = chu.makeSnapShot(); - List hanBoardSpots = han.makeSnapShot(); - List boardSpots = new ArrayList<>(chuBoardSpots); - boardSpots.addAll(hanBoardSpots); - return boardSpots; + public BoardSpots makeSnapShot() { + HashMap boardSpots = new HashMap<>(chu.makeSnapShot().value()); + boardSpots.putAll(han.makeSnapShot().value()); + return new BoardSpots(boardSpots); } public boolean isPieceExist(Position position, TeamType beforeTeam) { @@ -60,6 +58,7 @@ public boolean hasPiece(Position position) { } public void canMove(Position startPosition, Position endPosition, TeamType nowTeam) { + validateRange(startPosition); validateRange(endPosition); Piece piece = findTeamPiece(startPosition, currentTeam(nowTeam)); validateTargetPosition(currentTeam(nowTeam), endPosition); @@ -81,7 +80,7 @@ public Board move( return createMovedBoard(nowTurn, movedCurrentTeam, remainedOpponentTeam); } - private Piece findTeamPiece(Position position, Team nowTeam) { // public 메서드 아래로 두는지 질문 + private Piece findTeamPiece(Position position, Team nowTeam) { return nowTeam.findPiece(position) .orElseThrow(() -> new IllegalArgumentException("입력한 위치에 기물이 없습니다.")); } diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index 7571f37438..ac113801b9 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -1,7 +1,7 @@ package janggi.domain; import janggi.domain.piece.Piece; -import janggi.dto.BoardSpot; +import janggi.dto.BoardSpots; import java.util.ArrayList; import java.util.List; @@ -17,7 +17,7 @@ public static JanggiGame createInitialJanggiGame() { return new JanggiGame(List.of(Turn.createInitialTurn())); } - public List makeCurrentTurnBoardSnapShot() { + public BoardSpots makeCurrentTurnBoardSnapShot() { Turn lastTurn = getLastTurn(); return lastTurn.makeBoardSnapShot(); } diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index ffdc7e6ced..718feafa40 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -10,9 +10,8 @@ import janggi.domain.piece.Sang; import janggi.domain.side.TeamType; import janggi.dto.BoardSpot; -import java.util.ArrayList; +import janggi.dto.BoardSpots; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Optional; @@ -63,15 +62,14 @@ public static Pieces createChu() { } - public List makeSnapShot() { - List snapShot = new ArrayList<>(); + public BoardSpots makeSnapShot() { + Map snapShot = new HashMap<>(); for (Map.Entry entry : value.entrySet()) { - snapShot.add(new BoardSpot( - entry.getKey().makePositionKey(), - entry.getValue().nickname() - )); + Position position = entry.getKey(); + Piece piece = entry.getValue(); + snapShot.put(position, new BoardSpot(position, piece.nickname(), piece.getTeamType())); } - return snapShot; + return new BoardSpots(snapShot); } public Optional findPiece(Position position) { diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index af32bfa92e..8193dd8c16 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -2,8 +2,7 @@ import janggi.domain.piece.Piece; import janggi.domain.side.TeamType; -import janggi.dto.BoardSpot; -import java.util.List; +import janggi.dto.BoardSpots; public class Turn { @@ -42,7 +41,7 @@ public String nextTurnTeam() { return teamType.getName(); } - public List makeBoardSnapShot() { + public BoardSpots makeBoardSnapShot() { return board.makeSnapShot(); } diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 85b65fb092..7c8966abe9 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -72,4 +72,9 @@ public String nickname() { public PieceType getPieceType() { return pieceType; } + + @Override + public TeamType getTeamType() { + return teamType; + } } diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index 679a6a635e..5110458650 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -73,4 +73,9 @@ public String nickname() { public PieceType getPieceType() { return pieceType; } + + @Override + public TeamType getTeamType() { + return teamType; + } } diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 4ff647ee6a..7be3af0c20 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -70,4 +70,9 @@ public String nickname() { public PieceType getPieceType() { return pieceType; } + + @Override + public TeamType getTeamType() { + return teamType; + } } diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index af9fd3dcdb..08b6ee6433 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -62,4 +62,9 @@ public String nickname() { public PieceType getPieceType() { return pieceType; } + + @Override + public TeamType getTeamType() { + return teamType; + } } diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index 18d690fe02..8b4c7c6c2c 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -3,6 +3,7 @@ import janggi.domain.Board; import janggi.domain.MovePath; import janggi.domain.Position; +import janggi.domain.side.TeamType; import java.util.Optional; public interface Piece { @@ -16,4 +17,6 @@ public interface Piece { String nickname(); PieceType getPieceType(); + + TeamType getTeamType(); } diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index 53a07ed9c1..0d8d15207d 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -73,4 +73,9 @@ public String nickname() { public PieceType getPieceType() { return pieceType; } + + @Override + public TeamType getTeamType() { + return teamType; + } } diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index c6d5768e6c..60d00c909f 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -73,4 +73,9 @@ public String nickname() { public PieceType getPieceType() { return pieceType; } + + @Override + public TeamType getTeamType() { + return teamType; + } } diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index becbb9f976..2bd29c3cd2 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -62,4 +62,9 @@ public String nickname() { public PieceType getPieceType() { return pieceType; } + + @Override + public TeamType getTeamType() { + return teamType; + } } diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index b6b7cbc483..7ca00802d4 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -3,8 +3,7 @@ import janggi.domain.Pieces; import janggi.domain.Position; import janggi.domain.piece.Piece; -import janggi.dto.BoardSpot; -import java.util.List; +import janggi.dto.BoardSpots; import java.util.Optional; public class Chu implements Team { @@ -20,7 +19,7 @@ public static Chu createInitialChu() { } @Override - public List makeSnapShot() { + public BoardSpots makeSnapShot() { return pieces.makeSnapShot(); } diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index 8cf08e5ad8..4466436f9b 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -3,8 +3,7 @@ import janggi.domain.Pieces; import janggi.domain.Position; import janggi.domain.piece.Piece; -import janggi.dto.BoardSpot; -import java.util.List; +import janggi.dto.BoardSpots; import java.util.Optional; public class Han implements Team { @@ -20,7 +19,7 @@ public static Han createInitialHan() { } @Override - public List makeSnapShot() { + public BoardSpots makeSnapShot() { return pieces.makeSnapShot(); } diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index c8c0104f70..bfe576bbc0 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -2,13 +2,12 @@ import janggi.domain.Position; import janggi.domain.piece.Piece; -import janggi.dto.BoardSpot; -import java.util.List; +import janggi.dto.BoardSpots; import java.util.Optional; public interface Team { - List makeSnapShot(); + BoardSpots makeSnapShot(); Optional findPiece(Position position); diff --git a/src/main/java/janggi/dto/BoardSpot.java b/src/main/java/janggi/dto/BoardSpot.java index 7ce543e6e6..317d8d24ca 100644 --- a/src/main/java/janggi/dto/BoardSpot.java +++ b/src/main/java/janggi/dto/BoardSpot.java @@ -1,8 +1,12 @@ package janggi.dto; +import janggi.domain.Position; +import janggi.domain.side.TeamType; + public record BoardSpot( - String position, - String pieceName + Position position, + String pieceName, + TeamType teamType ) { } diff --git a/src/main/java/janggi/dto/BoardSpots.java b/src/main/java/janggi/dto/BoardSpots.java new file mode 100644 index 0000000000..67e62c8da4 --- /dev/null +++ b/src/main/java/janggi/dto/BoardSpots.java @@ -0,0 +1,10 @@ +package janggi.dto; + +import janggi.domain.Position; +import java.util.Map; + +public record BoardSpots( + Map value +) { + +} diff --git a/src/main/java/janggi/view/OutputView.java b/src/main/java/janggi/view/OutputView.java index f7e23ddd8b..6c9782eeca 100644 --- a/src/main/java/janggi/view/OutputView.java +++ b/src/main/java/janggi/view/OutputView.java @@ -1,16 +1,37 @@ package janggi.view; +import janggi.domain.Position; +import janggi.domain.side.TeamType; import janggi.dto.BoardSpot; -import java.util.HashMap; -import java.util.List; +import janggi.dto.BoardSpots; import java.util.Map; public class OutputView { + private static final String RED = "\u001B[31m"; + private static final String GREEN = "\u001B[32m"; + private static final String RESET = "\u001B[0m"; + private static final String EMPTY = "\uFF0E"; + private static final String FULL_WIDTH_SPACE = "\u3000"; + private static final String FULL_WIDTH_ZERO = "\uFF10"; + private static final String FULL_WIDTH_ONE = "\uFF11"; + private static final String FULL_WIDTH_TWO = "\uFF12"; + private static final String FULL_WIDTH_THREE = "\uFF13"; + private static final String FULL_WIDTH_FOUR = "\uFF14"; + private static final String FULL_WIDTH_FIVE = "\uFF15"; + private static final String FULL_WIDTH_SIX = "\uFF16"; + private static final String FULL_WIDTH_SEVEN = "\uFF17"; + private static final String FULL_WIDTH_EIGHT = "\uFF18"; + private static final String FULL_WIDTH_NINE = "\uFF19"; + public static void printMessage(String message) { System.out.println(message); } + public static void printSameLine(String message) { + System.out.print(message); + } + public static void printNewLine() { System.out.println(); } @@ -20,53 +41,69 @@ public static void printStartMessage() { printNewLine(); } - public static void printBoard(List boardSpots) { - System.out.println(makeBoard(boardSpots)); - } - - private static String makeBoard(List boardSpots) { - Map boardSpotMap = makeBoardSpotMap(boardSpots); - StringBuilder builder = new StringBuilder(); - builder.append(makeHeader()); + public static void printBoard(BoardSpots boardSpots) { + Map boardSpotsMap = boardSpots.value(); + printHeader(); for (int y = 10; y >= 1; y--) { - builder.append(makeRow(boardSpotMap, y)); + printRow(boardSpotsMap, y); } - return builder.toString(); } - private static String makeHeader() { - StringBuilder builder = new StringBuilder(" "); + private static void printHeader() { + printSameLine(" "); + printSameLine(FULL_WIDTH_SPACE.repeat(2)); for (int x = 1; x <= 9; x++) { - builder.append(String.format("%-3s", x)); + printSameLine(toFullWidthNumber(x) + FULL_WIDTH_SPACE); } - builder.append(System.lineSeparator()); - return builder.toString(); + printNewLine(); } - private static String makeRow(Map boardSpotMap, int y) { - StringBuilder builder = new StringBuilder(); - builder.append(String.format("%2d ", y)); + private static void printRow(Map boardSpotMap, int y) { + printSameLine(formatRowNumber(y) + FULL_WIDTH_SPACE); for (int x = 1; x <= 9; x++) { - builder.append(String.format("%-3s", findPieceName(boardSpotMap, x, y))); + printSameLine(makeCell(x, y, boardSpotMap) + FULL_WIDTH_SPACE); } - builder.append(System.lineSeparator()); - return builder.toString(); + printNewLine(); + } + + private static String makeCell(int x, int y, Map boardSpotMap) { + Position position = new Position(x, y); + if (boardSpotMap.containsKey(position)) { + BoardSpot spot = boardSpotMap.get(position); + return getColor(spot.teamType()) + spot.pieceName() + RESET; + } + return EMPTY; } - private static Map makeBoardSpotMap(List boardSpots) { - Map boardSpotMap = new HashMap<>(); - for (BoardSpot boardSpot : boardSpots) { - boardSpotMap.put(boardSpot.position(), boardSpot.pieceName()); + private static String formatRowNumber(int number) { + if (number < 10) { + return FULL_WIDTH_SPACE + toFullWidthNumber(number); } - return boardSpotMap; + return toFullWidthNumber(number); } - private static String findPieceName(Map boardSpotMap, int x, int y) { - return boardSpotMap.getOrDefault(makeKey(x, y), "."); + private static String toFullWidthNumber(int number) { + return String.valueOf(number) + .replace("0", FULL_WIDTH_ZERO) + .replace("1", FULL_WIDTH_ONE) + .replace("2", FULL_WIDTH_TWO) + .replace("3", FULL_WIDTH_THREE) + .replace("4", FULL_WIDTH_FOUR) + .replace("5", FULL_WIDTH_FIVE) + .replace("6", FULL_WIDTH_SIX) + .replace("7", FULL_WIDTH_SEVEN) + .replace("8", FULL_WIDTH_EIGHT) + .replace("9", FULL_WIDTH_NINE); } - private static String makeKey(int x, int y) { - return x + "," + y; + private static String getColor(TeamType teamType) { + if (teamType == TeamType.CHU) { + return GREEN; + } + if (teamType == TeamType.HAN) { + return RED; + } + return RESET; } public static void printTurnNotice(String nowTurn) { @@ -74,10 +111,10 @@ public static void printTurnNotice(String nowTurn) { } public static void printAskPiecePosition() { - printMessage("움직일 기물의 좌표를 입력해주세요."); + printMessage("움직일 기물의 좌표를 입력해주세요. (ex. 1,3)"); } public static void printAskMovePosition(String nickname) { - printMessage(nickname + "의 목적 좌표를 입력해주세요."); + printMessage(nickname + "의 목적 좌표를 입력해주세요. (ex. 1,3)"); } } diff --git a/src/test/java/janggi/domain/PiecesTest.java b/src/test/java/janggi/domain/PiecesTest.java index 1edd453fef..4a1aea3fa2 100644 --- a/src/test/java/janggi/domain/PiecesTest.java +++ b/src/test/java/janggi/domain/PiecesTest.java @@ -44,24 +44,26 @@ void remove() { assertAll( () -> assertThat(pieces.findPiece(target)).get().isInstanceOf(Jol.class), () -> assertThat(removedPieces.findPiece(target)).isEmpty(), - () -> assertThat(removedPieces.makeSnapShot()).hasSize(15) + () -> assertThat(removedPieces.makeSnapShot().value()).hasSize(15) ); } @Test - @DisplayName("보드 스냅샷은 좌표 키와 기물 이름을 포함한다.") + @DisplayName("보드 스냅샷은 좌표, 기물 이름, 팀 정보를 포함한다.") void makeSnapShot() { // given Pieces pieces = Pieces.createChu(); // when var boardSpots = pieces.makeSnapShot(); + var gungSpot = boardSpots.value().get(new Position(5, 2)); // then - assertThat(boardSpots) - .anySatisfy(boardSpot -> { - assertThat(boardSpot.position()).isEqualTo("5,2"); - assertThat(boardSpot.pieceName()).isEqualTo(PieceType.GUNG.getNickname()); - }); + assertThat(gungSpot).isNotNull(); + assertAll( + () -> assertThat(gungSpot.position()).isEqualTo(new Position(5, 2)), + () -> assertThat(gungSpot.pieceName()).isEqualTo(PieceType.GUNG.getNickname()), + () -> assertThat(gungSpot.teamType()).isEqualTo(janggi.domain.side.TeamType.CHU) + ); } } diff --git a/src/test/java/janggi/domain/TurnTest.java b/src/test/java/janggi/domain/TurnTest.java index 7dd1e74acd..2d5185bcff 100644 --- a/src/test/java/janggi/domain/TurnTest.java +++ b/src/test/java/janggi/domain/TurnTest.java @@ -7,6 +7,7 @@ import janggi.domain.piece.Jol; import janggi.domain.piece.Piece; +import janggi.dto.BoardSpot; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -89,19 +90,19 @@ void move() { // when Turn movedTurn = turn.move(new Position(1, 4), new Position(1, 5)); + BoardSpot originSpot = turn.makeBoardSnapShot().value().get(new Position(1, 4)); + BoardSpot movedSpot = movedTurn.makeBoardSnapShot().value().get(new Position(1, 5)); // then assertAll( () -> assertThat(turn.nextTurnTeam()).isEqualTo("초나라"), () -> assertThat(movedTurn.nextTurnTeam()).isEqualTo("한나라"), - () -> assertThat(turn.makeBoardSnapShot()).anySatisfy(boardSpot -> { - assertThat(boardSpot.position()).isEqualTo("1,4"); - assertThat(boardSpot.pieceName()).isEqualTo("졸"); - }), - () -> assertThat(movedTurn.makeBoardSnapShot()).anySatisfy(boardSpot -> { - assertThat(boardSpot.position()).isEqualTo("1,5"); - assertThat(boardSpot.pieceName()).isEqualTo("졸"); - }) + () -> assertThat(originSpot).isNotNull(), + () -> assertThat(originSpot.position()).isEqualTo(new Position(1, 4)), + () -> assertThat(originSpot.pieceName()).isEqualTo("졸"), + () -> assertThat(movedSpot).isNotNull(), + () -> assertThat(movedSpot.position()).isEqualTo(new Position(1, 5)), + () -> assertThat(movedSpot.pieceName()).isEqualTo("졸") ); } } diff --git a/src/test/java/janggi/domain/side/ChuTest.java b/src/test/java/janggi/domain/side/ChuTest.java index 442a0d2ed1..bc5a7cc36d 100644 --- a/src/test/java/janggi/domain/side/ChuTest.java +++ b/src/test/java/janggi/domain/side/ChuTest.java @@ -24,7 +24,7 @@ void createInitialChu() { // when & then assertAll( - () -> assertThat(chu.makeSnapShot()).hasSize(16), + () -> assertThat(chu.makeSnapShot().value()).hasSize(16), () -> assertThat(chu.findPiece(new Position(1, 1))).get().isInstanceOf(Cha.class), () -> assertThat(chu.findPiece(new Position(9, 1))).get().isInstanceOf(Cha.class), () -> assertThat(chu.findPiece(new Position(2, 1))).get().isInstanceOf(Ma.class), @@ -78,7 +78,7 @@ void remove() { // then assertAll( () -> assertThat(removedChu.findPiece(chuJolPosition)).isEmpty(), - () -> assertThat(removedChu.makeSnapShot()).hasSize(15), + () -> assertThat(removedChu.makeSnapShot().value()).hasSize(15), () -> assertThat(chu.findPiece(chuJolPosition)).get().isInstanceOf(Jol.class) ); } diff --git a/src/test/java/janggi/domain/side/HanTest.java b/src/test/java/janggi/domain/side/HanTest.java index 5be46b543f..0411ac19f2 100644 --- a/src/test/java/janggi/domain/side/HanTest.java +++ b/src/test/java/janggi/domain/side/HanTest.java @@ -24,7 +24,7 @@ void createInitialHan() { // when & then assertAll( - () -> assertThat(han.makeSnapShot()).hasSize(16), + () -> assertThat(han.makeSnapShot().value()).hasSize(16), () -> assertThat(han.findPiece(new Position(1, 10))).get().isInstanceOf(Cha.class), () -> assertThat(han.findPiece(new Position(9, 10))).get().isInstanceOf(Cha.class), () -> assertThat(han.findPiece(new Position(2, 10))).get().isInstanceOf(Ma.class), @@ -74,7 +74,7 @@ void remove() { // then assertAll( () -> assertThat(removedHan.findPiece(new Position(1, 7))).isEmpty(), - () -> assertThat(removedHan.makeSnapShot()).hasSize(15), + () -> assertThat(removedHan.makeSnapShot().value()).hasSize(15), () -> assertThat(han.findPiece(new Position(1, 7))).get().isInstanceOf(Jol.class) ); } From 6f5bc1cbd55679e751270efdafb652fe17823853 Mon Sep 17 00:00:00 2001 From: Sumin Date: Mon, 30 Mar 2026 14:29:15 +0900 Subject: [PATCH 57/99] =?UTF-8?q?refactor:=20ExceptionHandler=20->=20Actio?= =?UTF-8?q?nExecutor=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=AA=85=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/JanggiRunner.java | 6 +++--- .../util/{ExceptionHandler.java => ActionExecutor.java} | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/janggi/util/{ExceptionHandler.java => ActionExecutor.java} (92%) diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index d80e24812a..094fd8faa3 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -3,7 +3,7 @@ import janggi.domain.JanggiGame; import janggi.domain.Position; import janggi.util.DelimiterParser; -import janggi.util.ExceptionHandler; +import janggi.util.ActionExecutor; import janggi.view.InputView; import janggi.view.OutputView; import java.util.List; @@ -16,10 +16,10 @@ public void execute() { JanggiGame janggiGame = JanggiGame.createInitialJanggiGame(); while (true) { OutputView.printBoard(janggiGame.makeCurrentTurnBoardSnapShot()); - Position startPosition = ExceptionHandler.retryUntilSuccess( + Position startPosition = ActionExecutor.retryUntilSuccess( () -> readValidStartPosition(janggiGame) ); - Position endPosition = ExceptionHandler.retryUntilSuccess( + Position endPosition = ActionExecutor.retryUntilSuccess( () -> readValidEndPosition(janggiGame, startPosition) ); janggiGame.doGame(startPosition, endPosition); diff --git a/src/main/java/janggi/util/ExceptionHandler.java b/src/main/java/janggi/util/ActionExecutor.java similarity index 92% rename from src/main/java/janggi/util/ExceptionHandler.java rename to src/main/java/janggi/util/ActionExecutor.java index b1caf0db90..601c89d24e 100644 --- a/src/main/java/janggi/util/ExceptionHandler.java +++ b/src/main/java/janggi/util/ActionExecutor.java @@ -3,7 +3,7 @@ import janggi.view.OutputView; import java.util.function.Supplier; -public class ExceptionHandler { +public class ActionExecutor { public static T retryUntilSuccess(Supplier supplier) { while (true) { From 5f63b19beedbc6e4f6e060ad6564857e6641fd66 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 14:47:27 +0900 Subject: [PATCH 58/99] =?UTF-8?q?refactor:=20=EA=B8=B0=EB=AC=BC=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EC=8B=9C=20=EC=A4=91=EB=B3=B5=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Board.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 94030a21ac..e6d7e110b0 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -70,11 +70,6 @@ public Board move( Position endPosition, TeamType nowTurn ) { - validateRange(startPosition); - validateRange(endPosition); - Piece piece = findTeamPiece(startPosition, currentTeam(nowTurn)); - validateTargetPosition(currentTeam(nowTurn), endPosition); - validateCanMove(piece, startPosition, endPosition); Team movedCurrentTeam = currentTeam(nowTurn).move(startPosition, endPosition); Team remainedOpponentTeam = removeOpponentPiece(nowTurn, endPosition); return createMovedBoard(nowTurn, movedCurrentTeam, remainedOpponentTeam); From bb76c43081c2540e60b1638cf0bf4b0813ee4b2b Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 14:57:33 +0900 Subject: [PATCH 59/99] =?UTF-8?q?docs:=20readme=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 244 ++++++++++++------------------------------------------ 1 file changed, 53 insertions(+), 191 deletions(-) diff --git a/README.md b/README.md index 80d6972648..22c5974033 100644 --- a/README.md +++ b/README.md @@ -1,200 +1,62 @@ # java-janggi -## 🚀 사이클1: 미션 (보드 초기화 + 기물 이동) - -> 규칙을 적용하면서 장기를 구현한다. - -### 기능 요구 사항 - -> 장기(Janggi) 규칙을 참고하여 다음을 구현한다. - -**1.1단계 - 보드 초기화** - -- 게임 시작 시 장기판과 전체 기물을 올바른 위치에 초기화한다. -- 1.1단계에서는 기물의 이동은 구현하지 않는다. - -**1.2단계 - 기물 이동** - -- 각 기물의 이동 규칙을 구현한다. -- 기물의 이동 규칙은 직접 요구사항을 분석하여 정의한다. -- 궁성(宮城) 영역은 구현하지 않는다. (사이클2에서 다룬다) - -### 프로그래밍 요구 사항 - -- 자바 코드 컨벤션을 지키면서 프로그래밍한다. -- 기본적으로 Java Style Guide을 원칙으로 한다. -- indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다. - - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. - - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다. -- 3항 연산자를 쓰지 않는다. -- else 예약어를 쓰지 않는다. - - else 예약어를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다. - - 힌트: if문에서 값을 반환하는 방식으로 구현하면 else 예약어를 사용하지 않아도 된다. -- 모든 기능을 TDD로 구현해 단위 테스트가 존재해야 한다. 단, UI(System.out, System.in) 로직은 제외 - - 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다. - - UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다. -- 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다. - - 함수(또는 메소드)가 한 가지 일만 하도록 최대한 작게 만들어라. -- 배열 대신 컬렉션을 사용한다. -- 모든 원시 값과 문자열을 포장한다. -- 줄여 쓰지 않는다(축약 금지). -- 일급 컬렉션을 쓴다. -- 모든 엔티티를 작게 유지한다. -- 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다. - -### 추가된 요구 사항 - -- 도메인의 의존성을 최소한으로 구현한다. -- 한 줄에 점을 하나만 찍는다. -- 게터/세터/프로퍼티를 쓰지 않는다. -- 모든 객체지향 생활 체조 원칙을 잘 지키며 구현한다. -- 프로그래밍 체크리스트의 원칙을 지키면서 프로그래밍한다. - -## 과제 진행 요구 사항 - -- 구현을 시작하기 전에 기능 요구 사항을 분석하여 기능 목록을 정리한다. -- README.md 파일에 구현할 기능 목록을 정리해 추가한다. -- Git의 커밋 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가한다. - - AngularJS Git Commit Message Conventions 참고해 커밋 메시지를 작성한다. - -## 미션 중 할 일 - -1. 토론 활동에서 정한 규칙을 의식하며 코드 작성 -2. 규칙 때문에 코드를 변경한 곳 기록 -3. 막히는 순간 기록 - -## 미션 중 기록 - -필수 기록: - -- 상태 위치를 결정할 때 고민한 순간 1회 -- 불변/캡슐화를 적용한 코드 1곳 -- 규칙 적용으로 변경한 설계 1곳 -- 조건문을 다형성으로 대체한 코드 1곳 -- 인터페이스/추상 클래스를 도입한 이유 -- 새 기물 추가 시 변경 범위 테스트 (가상으로) +## 프로그램 흐름 +1. **[출력] 장기 게임 시작 문구를 출력한다.** +2. **[중간 과정] 장기판을 초기화한다.** +3. **[출력] 초기화된 장기판을 출력한다.** +4. **[입력] 움직일 기물 좌표를 입력받는다.** + + [예외처리] 좌표를 쉼표로 구분하지 않았을 경우 IllegalArgmentException 예외를 발생시킨다. + + [예외처리] 좌표를 숫자로 입력하지 않았을 경우 IllegalArgmentException 예외를 발생시킨다. + + [예외처리] 장기판 범위 밖의 좌표를 입력했을 경우 IllegalArgmentException 예외를 발생시킨다. + + [예외처리] 입력한 좌표에 기물이 없을 경우 IllegalArgmentException 예외를 발생시킨다. +5. **[입력] 목적지 좌표를 입력받는다.** + + [예외처리] 좌표를 쉼표로 구분하지 않았을 경우 IllegalArgmentException 예외를 발생시킨다. + + [예외처리] 좌표를 숫자로 입력하지 않았을 경우 IllegalArgmentException 예외를 발생시킨다. + + [예외처리] 장기판 범위 밖의 좌표를 입력했을 경우 IllegalArgmentException 예외를 발생시킨다. + + [예외처리] 입력한 좌표 또는 이동 경로에 아군 기물이 존재할 경우 IllegalArgmentException 예외를 발생시킨다. +6. **[중간 과정] 기물을 이동시킨다.** +7. **[출력] 갱신된 장기판을 출력한다.** ## 출력예시 - ``` 장기 게임을 시작합니다. - 1 2 3 4 5 6 7 8 9 -10 차 마 상 사 왕 사 상 마 차 - 9 . . . . . . . . . - 8 . 포 . . . . . 포 . - 7 졸 . 졸 . 졸 . 졸 . 졸 - 6 . . . . . . . . . - 5 . . . . . . . . . - 4 병 . 병 . 병 . 병 . 병 - 3 . 포 . . . . . 포 . - 2 . . . . . . . . . - 1 차 마 상 사 왕 사 상 마 차 - -현재 차례: 한 -이동할 기물의 출발 좌표를 입력하세요. 예: 1,4 -> 1,4 -이동할 기물의 도착 좌표를 입력하세요. 예: 1,4 -> 2,4 - -이동을 완료했습니다. - - 1 2 3 4 5 6 7 8 9 -10 차 마 상 사 왕 사 상 마 차 - 9 . . . . . . . . . - 8 . 포 . . . . . 포 . - 7 졸 . 졸 . 졸 . 졸 . 졸 - 6 . . . . . . . . . - 5 . . . . . . . . . - 4 차 . 병 . 병 . 병 . 병 - 3 . 포 . . . . . 포 . - 2 . . . . . . . . . - 1 . 마 상 사 왕 사 상 마 차 - -현재 차례: 초 -이동할 기물의 출발 좌표와 도착 좌표를 입력하세요. 예: a10 a9 -> b8 b5 - -오류: 포는 정확히 하나의 기물을 넘어야 합니다. -현재 차례: 초 -이동할 기물의 출발 좌표와 도착 좌표를 입력하세요. 예: a10 a9 -> a7 a6 - -이동을 완료했습니다. +   1 2 3 4 5 6 7 8 9  +10 차 마 상 사 . 사 상 마 차  + 9 . . . . 궁 . . . .  + 8 . 포 . . . . . 포 .  + 7 졸 . 졸 . 졸 . 졸 . 졸  + 6 . . . . . . . . .  + 5 . . . . . . . . .  + 4 졸 . 졸 . 졸 . 졸 . 졸  + 3 . 포 . . . . . 포 .  + 2 . . . . 궁 . . . .  + 1 차 마 상 사 . 사 상 마 차  +초나라의 차례입니다. +움직일 기물의 좌표를 입력해주세요. (ex. 1,3) +1,1 +차의 목적 좌표를 입력해주세요. (ex. 1,3) +1,3 +   1 2 3 4 5 6 7 8 9  +10 차 마 상 사 . 사 상 마 차  + 9 . . . . 궁 . . . .  + 8 . 포 . . . . . 포 .  + 7 졸 . 졸 . 졸 . 졸 . 졸  + 6 . . . . . . . . .  + 5 . . . . . . . . .  + 4 졸 . 졸 . 졸 . 졸 . 졸  + 3 차 포 . . . . . 포 .  + 2 . . . . 궁 . . . .  + 1 . 마 상 사 . 사 상 마 차  +한나라의 차례입니다. +움직일 기물의 좌표를 입력해주세요. (ex. 1,3) ``` - -게임 종료 예시 - -``` -> e4 e1 - -왕을 포획했습니다. -승자: 한 - -게임을 종료합니다. -``` - -# 프로그램 흐름 -- [x] 장기 게임 시작 문구 출력 (OutputView) -- [x] 장기판 초기화 -- [x] 장기판 출력(OutputView) -- [ ] 초, 한 번갈아가며 진행(Turn) - - [x] 움직일 기물 좌표 입력(InputView - 콤마로 구분하여 좌표 입력) - - [x] 입력값 검증 및 Position 객체 생성 - - [x] 기물 반환 - - [x] 장기판 내부 좌표인지 검증 - - [x] 기물 반환 - - [ ] 반환된 기물의 목적 좌표 입력 - - [ ] 입력값 검증 및 Position 객체 생성 - - [ ] 기물 이동 - - [ ] 기물 이동 가능 여부 검증 - - [ ] 이동 경로 내에 아군 기물이 존재하는지 - - [ ] 이동 목적 좌표에 아군 기물이 존재하는지 - - [ ] 기물 Position Map 갱신(Pieces) - - [ ] 적 기물 잡았을 경우 score 갱신 - - [ ] 갱신된 장기판 출력(OutputView) -- [ ] 진행 후 Turns에 Board 상태 추가 - -# TODO -- Runner -> Board 참조를 Turns 참조로 변경 - 1. Runner에서 Position 직접 생성 x - 2. Piece 존재 여부 확인(Turns.isExist) x - - 마지막 턴.isPieceExist() x - 3. Piece 반환(보류) - 4. 목적 좌표 Position 생성 - 5. 이동(Turns.doGame(Position, Position)) - 6. 갱신된 보드판 출력 -- 이동 시 장애물이 있는 경우(canMove.isFalse()) 처리 - -# 도메인 - -- Piece(interface - move) - - gung - - sa - - jol(byeong) - - sang - - ma - - cha - - po - - List -- Pieces - - Map -- Position - - int x, int y -- Board - - Side han, Side chu -- JanggiRunner -- InputView -- OutputView -- Delta - - 칸 이동수 관리. -- MovePath - - List -- Side - - int score - - Pieces -- Turn - - Board -- Turns - - List From 5e1f8c51cc1bd9ec834a4ca0c451e7fe062d59e4 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 15:46:02 +0900 Subject: [PATCH 60/99] =?UTF-8?q?refactor:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Position.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java index c47b043fce..d5c1be40e1 100644 --- a/src/main/java/janggi/domain/Position.java +++ b/src/main/java/janggi/domain/Position.java @@ -35,10 +35,6 @@ public int getY() { return y; } - public String makePositionKey() { - return x + "," + y; - } - public Position move(Delta delta) { return new Position(x + delta.dx(), y + delta.dy()); } From 8c1b511e93726fc5dd6f250e57fe7db0bc63845b Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 19:22:08 +0900 Subject: [PATCH 61/99] =?UTF-8?q?refactor:=20Pieces=EC=97=90=EC=84=9C=20Ma?= =?UTF-8?q?p=EC=9D=84=20=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=ED=95=9C=20=EC=A4=84=EC=97=90=20.=20?= =?UTF-8?q?=ED=95=98=EB=82=98=EB=A7=8C=20=EC=A1=B4=EC=9E=AC=ED=95=98?= =?UTF-8?q?=EB=8F=84=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 --- .codex/skills/SKILL.md | 58 ------------------- src/main/java/janggi/domain/Board.java | 4 +- src/main/java/janggi/domain/Pieces.java | 4 +- src/main/java/janggi/domain/side/Chu.java | 6 +- src/main/java/janggi/domain/side/Han.java | 5 +- src/main/java/janggi/domain/side/Team.java | 5 +- src/test/java/janggi/domain/PiecesTest.java | 4 +- src/test/java/janggi/domain/side/ChuTest.java | 4 +- src/test/java/janggi/domain/side/HanTest.java | 4 +- 9 files changed, 22 insertions(+), 72 deletions(-) delete mode 100644 .codex/skills/SKILL.md diff --git a/.codex/skills/SKILL.md b/.codex/skills/SKILL.md deleted file mode 100644 index aa9541ad59..0000000000 --- a/.codex/skills/SKILL.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -description: Interview user in-depth to create a detailed spec -allowed-tools: Write ---- - -# 심층 인터뷰를 통한 요구사항 스펙 생성 - -사용자를 인터뷰하여 암묵적 가정, 놓치기 쉬운 요구사항, 트레이드오프를 도출하고 상세한 스펙 문서를 생성합니다. - -## 인터뷰 원칙 - -1. **역방향 요구사항 추출**: 사용자가 "당연하다고 생각해서 안 적는 것들"을 질문으로 끌어내기 -2. **깊이 있는 질문**: 뻔한 질문은 피하고, 구현 세부사항까지 파고들기 -3. **다각도 탐색**: 기술 구현, 우려 지점, 트레이드오프 등 다양한 관점에서 질문 - -## 질문 영역 (참고용, 순서나 범위는 자유롭게) - -- 기술 구현 방식 및 아키텍처 -- 엣지 케이스 및 예외 상황 -- 성능, 확장성 고려사항 -- 기존 시스템과의 통합 -- 우려 지점 및 리스크 -- 트레이드오프 선택 - -## 완료 조건 - -다음 중 하나 이상 충족 시 인터뷰 종료: - -- 핵심 요구사항과 주요 엣지 케이스가 충분히 도출됨 -- 사용자가 "충분하다" 또는 "그만"이라고 표시 -- 새로운 질문에 대해 사용자가 "모르겠다" 또는 "나중에 결정"이 반복됨 - -## 실행 - -$ARGUMENTS - -위 주제에 대해 사용자에게 애매하다면 바로 질문을 하며 심층 인터뷰를 진행하세요. - -- 한 번에 1-2개의 연관된 질문을 던지세요 -- 이전 답변을 기반으로 후속 질문을 발전시키세요 -- 충분한 정보가 모이면 스펙 문서를 작성하세요 - -## 출력 - -인터뷰 완료 후 다음 위치에 스펙 문서 저장: - -``` -docs/codex/001_prd_v1.md -``` - -스펙 문서에 포함할 내용: - -- 기능 개요 및 목적 -- 핵심 요구사항 (Functional Requirements) -- 비기능 요구사항 (Non-functional Requirements) -- 엣지 케이스 및 예외 처리 -- 제약 조건 및 가정 -- 우선순위 및 트레이드오프 결정사항 diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index e6d7e110b0..2b61e7072e 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -29,8 +29,8 @@ public static Board createInitialBoard() { } public BoardSpots makeSnapShot() { - HashMap boardSpots = new HashMap<>(chu.makeSnapShot().value()); - boardSpots.putAll(han.makeSnapShot().value()); + HashMap boardSpots = new HashMap<>(chu.makeSnapShot()); + boardSpots.putAll(han.makeSnapShot()); return new BoardSpots(boardSpots); } diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index 718feafa40..ffc40630ae 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -62,14 +62,14 @@ public static Pieces createChu() { } - public BoardSpots makeSnapShot() { + public Map makeSnapShot() { Map snapShot = new HashMap<>(); for (Map.Entry entry : value.entrySet()) { Position position = entry.getKey(); Piece piece = entry.getValue(); snapShot.put(position, new BoardSpot(position, piece.nickname(), piece.getTeamType())); } - return new BoardSpots(snapShot); + return snapShot; } public Optional findPiece(Position position) { diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index 7ca00802d4..82cbd5fa93 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -3,7 +3,9 @@ import janggi.domain.Pieces; import janggi.domain.Position; import janggi.domain.piece.Piece; -import janggi.dto.BoardSpots; +import janggi.dto.BoardSpot; + +import java.util.Map; import java.util.Optional; public class Chu implements Team { @@ -19,7 +21,7 @@ public static Chu createInitialChu() { } @Override - public BoardSpots makeSnapShot() { + public Map makeSnapShot() { return pieces.makeSnapShot(); } diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index 4466436f9b..c92476984e 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -3,7 +3,10 @@ import janggi.domain.Pieces; import janggi.domain.Position; import janggi.domain.piece.Piece; +import janggi.dto.BoardSpot; import janggi.dto.BoardSpots; + +import java.util.Map; import java.util.Optional; public class Han implements Team { @@ -19,7 +22,7 @@ public static Han createInitialHan() { } @Override - public BoardSpots makeSnapShot() { + public Map makeSnapShot() { return pieces.makeSnapShot(); } diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index bfe576bbc0..2e0bc3eb83 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -2,12 +2,15 @@ import janggi.domain.Position; import janggi.domain.piece.Piece; +import janggi.dto.BoardSpot; import janggi.dto.BoardSpots; + +import java.util.Map; import java.util.Optional; public interface Team { - BoardSpots makeSnapShot(); + Map makeSnapShot(); Optional findPiece(Position position); diff --git a/src/test/java/janggi/domain/PiecesTest.java b/src/test/java/janggi/domain/PiecesTest.java index 4a1aea3fa2..57efbd3ab1 100644 --- a/src/test/java/janggi/domain/PiecesTest.java +++ b/src/test/java/janggi/domain/PiecesTest.java @@ -44,7 +44,7 @@ void remove() { assertAll( () -> assertThat(pieces.findPiece(target)).get().isInstanceOf(Jol.class), () -> assertThat(removedPieces.findPiece(target)).isEmpty(), - () -> assertThat(removedPieces.makeSnapShot().value()).hasSize(15) + () -> assertThat(removedPieces.makeSnapShot()).hasSize(15) ); } @@ -56,7 +56,7 @@ void makeSnapShot() { // when var boardSpots = pieces.makeSnapShot(); - var gungSpot = boardSpots.value().get(new Position(5, 2)); + var gungSpot = boardSpots.get(new Position(5, 2)); // then assertThat(gungSpot).isNotNull(); diff --git a/src/test/java/janggi/domain/side/ChuTest.java b/src/test/java/janggi/domain/side/ChuTest.java index bc5a7cc36d..442a0d2ed1 100644 --- a/src/test/java/janggi/domain/side/ChuTest.java +++ b/src/test/java/janggi/domain/side/ChuTest.java @@ -24,7 +24,7 @@ void createInitialChu() { // when & then assertAll( - () -> assertThat(chu.makeSnapShot().value()).hasSize(16), + () -> assertThat(chu.makeSnapShot()).hasSize(16), () -> assertThat(chu.findPiece(new Position(1, 1))).get().isInstanceOf(Cha.class), () -> assertThat(chu.findPiece(new Position(9, 1))).get().isInstanceOf(Cha.class), () -> assertThat(chu.findPiece(new Position(2, 1))).get().isInstanceOf(Ma.class), @@ -78,7 +78,7 @@ void remove() { // then assertAll( () -> assertThat(removedChu.findPiece(chuJolPosition)).isEmpty(), - () -> assertThat(removedChu.makeSnapShot().value()).hasSize(15), + () -> assertThat(removedChu.makeSnapShot()).hasSize(15), () -> assertThat(chu.findPiece(chuJolPosition)).get().isInstanceOf(Jol.class) ); } diff --git a/src/test/java/janggi/domain/side/HanTest.java b/src/test/java/janggi/domain/side/HanTest.java index 0411ac19f2..5be46b543f 100644 --- a/src/test/java/janggi/domain/side/HanTest.java +++ b/src/test/java/janggi/domain/side/HanTest.java @@ -24,7 +24,7 @@ void createInitialHan() { // when & then assertAll( - () -> assertThat(han.makeSnapShot().value()).hasSize(16), + () -> assertThat(han.makeSnapShot()).hasSize(16), () -> assertThat(han.findPiece(new Position(1, 10))).get().isInstanceOf(Cha.class), () -> assertThat(han.findPiece(new Position(9, 10))).get().isInstanceOf(Cha.class), () -> assertThat(han.findPiece(new Position(2, 10))).get().isInstanceOf(Ma.class), @@ -74,7 +74,7 @@ void remove() { // then assertAll( () -> assertThat(removedHan.findPiece(new Position(1, 7))).isEmpty(), - () -> assertThat(removedHan.makeSnapShot().value()).hasSize(15), + () -> assertThat(removedHan.makeSnapShot()).hasSize(15), () -> assertThat(han.findPiece(new Position(1, 7))).get().isInstanceOf(Jol.class) ); } From 61e83d6802e83e237f7cc249fda96502c0254b8e Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 19:24:19 +0900 Subject: [PATCH 62/99] =?UTF-8?q?chore:=20=EB=AF=B8=EC=82=AC=EC=9A=A9=20im?= =?UTF-8?q?port=EB=AC=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/Application.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/janggi/Application.java b/src/main/java/janggi/Application.java index 9afa43f3db..b8b3cedc9b 100644 --- a/src/main/java/janggi/Application.java +++ b/src/main/java/janggi/Application.java @@ -1,8 +1,5 @@ package janggi; -import janggi.view.InputView; -import janggi.view.OutputView; - public class Application { public static void main(String[] args) { From 0c11a0652ef4906fd4dfd26263568e97ac36193c Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 19:28:46 +0900 Subject: [PATCH 63/99] =?UTF-8?q?refactor:=20JanggiGame=EC=9D=84=20JanggiR?= =?UTF-8?q?unner=EC=9D=98=20=ED=95=84=EB=93=9C=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/JanggiRunner.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index 094fd8faa3..687c9ecee5 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -10,23 +10,24 @@ public class JanggiRunner { + private final JanggiGame janggiGame; + + public JanggiRunner() { + this.janggiGame = JanggiGame.createInitialJanggiGame(); + } + public void execute() { OutputView.printStartMessage(); - JanggiGame janggiGame = JanggiGame.createInitialJanggiGame(); while (true) { OutputView.printBoard(janggiGame.makeCurrentTurnBoardSnapShot()); - Position startPosition = ActionExecutor.retryUntilSuccess( - () -> readValidStartPosition(janggiGame) - ); - Position endPosition = ActionExecutor.retryUntilSuccess( - () -> readValidEndPosition(janggiGame, startPosition) - ); + Position startPosition = ActionExecutor.retryUntilSuccess(this::readValidStartPosition); + Position endPosition = ActionExecutor.retryUntilSuccess(() -> readValidEndPosition(startPosition)); janggiGame.doGame(startPosition, endPosition); } } - private Position readValidStartPosition(JanggiGame janggiGame) { + private Position readValidStartPosition() { OutputView.printTurnNotice(janggiGame.getCurrentTurnTeamName()); OutputView.printAskPiecePosition(); String rawPiecePosition = InputView.readLine(); @@ -36,7 +37,7 @@ private Position readValidStartPosition(JanggiGame janggiGame) { return startPosition; } - private Position readValidEndPosition(JanggiGame janggiGame, Position startPosition) { + private Position readValidEndPosition(Position startPosition) { OutputView.printAskMovePosition(janggiGame.findPiece(startPosition).nickname()); String rawMovePosition = InputView.readLine(); List parsedMovePosition = DelimiterParser.parse(rawMovePosition); From 7ab7e226b60145ee4bdcf633e113888281ea2ffd Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 19:46:19 +0900 Subject: [PATCH 64/99] =?UTF-8?q?refactor:=20=EA=B8=B0=EB=AC=BC=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=EC=97=90=20=EC=A0=81?= =?UTF-8?q?=ED=95=A9=ED=95=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=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 | 6 +++--- src/main/java/janggi/domain/Pieces.java | 15 ++++++--------- src/main/java/janggi/domain/Position.java | 11 ++++++----- src/main/java/janggi/domain/Turn.java | 2 +- src/main/java/janggi/domain/side/Chu.java | 5 +++++ src/main/java/janggi/domain/side/Han.java | 5 +++++ src/main/java/janggi/domain/side/Team.java | 2 ++ 7 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 2b61e7072e..8884df011f 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -34,10 +34,10 @@ public BoardSpots makeSnapShot() { return new BoardSpots(boardSpots); } - public boolean isPieceExist(Position position, TeamType beforeTeam) { + public boolean isPieceExist(Position position, TeamType currentTeamType) { validateRange(position); - Team nowTeam = opponentTeam(beforeTeam); - return nowTeam.findPiece(position).isPresent(); + Team currentTeam = currentTeam(currentTeamType); + return currentTeam.isPieceExist(position); } public Piece findNextTurnTeamPiece(Position position, TeamType beforeTeamType) { diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index ffc40630ae..73a2dacccc 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -1,16 +1,9 @@ package janggi.domain; -import janggi.domain.piece.Cha; -import janggi.domain.piece.Gung; -import janggi.domain.piece.Jol; -import janggi.domain.piece.Ma; -import janggi.domain.piece.Piece; -import janggi.domain.piece.Po; -import janggi.domain.piece.Sa; -import janggi.domain.piece.Sang; +import janggi.domain.piece.*; import janggi.domain.side.TeamType; import janggi.dto.BoardSpot; -import janggi.dto.BoardSpots; + import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -72,6 +65,10 @@ public Map makeSnapShot() { return snapShot; } + public boolean isPieceExist(Position position) { + return value.containsKey(position); + } + public Optional findPiece(Position position) { return Optional.ofNullable(value.get(position)); } diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java index d5c1be40e1..663170789d 100644 --- a/src/main/java/janggi/domain/Position.java +++ b/src/main/java/janggi/domain/Position.java @@ -6,9 +6,15 @@ public class Position { private static final int POSITION_SIZE = 2; + private final int x; private final int y; + public Position(int x, int y) { + this.x = x; + this.y = y; + } + public static Position makePosition(List parsedPiecePosition) { if (parsedPiecePosition.size() != POSITION_SIZE) { throw new IllegalArgumentException("기물의 좌표는 두 개로 입력해야 합니다."); @@ -22,11 +28,6 @@ public static Position makePosition(List parsedPiecePosition) { } } - public Position(int x, int y) { - this.x = x; - this.y = y; - } - public int getX() { return x; } diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index 8193dd8c16..521f2de836 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -19,7 +19,7 @@ public static Turn createInitialTurn() { } public boolean isMyTeamPieceExist(Position position) { - return board.isPieceExist(position, movedTeam); + return board.isPieceExist(position, opponentTeamType()); } public Piece findPiece(Position position) { diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index 82cbd5fa93..9891d55b03 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -25,6 +25,11 @@ public Map makeSnapShot() { return pieces.makeSnapShot(); } + @Override + public boolean isPieceExist(Position position) { + return pieces.isPieceExist(position); + } + @Override public Optional findPiece(Position position) { return pieces.findPiece(position); diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index c92476984e..ab5b7ef6f7 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -26,6 +26,11 @@ public Map makeSnapShot() { return pieces.makeSnapShot(); } + @Override + public boolean isPieceExist(Position position) { + return pieces.isPieceExist(position); + } + @Override public Optional findPiece(Position position) { return pieces.findPiece(position); diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index 2e0bc3eb83..70fee91b80 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -12,6 +12,8 @@ public interface Team { Map makeSnapShot(); + boolean isPieceExist(Position position); + Optional findPiece(Position position); Team remove(Position position); From 7ddf820343c81c85aaea406b3320b07302d53e93 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Mon, 30 Mar 2026 20:03:57 +0900 Subject: [PATCH 65/99] =?UTF-8?q?refactor:=20=ED=95=9C=20=EC=A4=84?= =?UTF-8?q?=EC=97=90=20.=20=ED=95=98=EB=82=98=EB=A7=8C=20=EC=A1=B4?= =?UTF-8?q?=EC=9E=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20=EA=B8=B0=EB=AC=BC=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=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/JanggiRunner.java | 3 ++- src/main/java/janggi/domain/Board.java | 15 ++++++++++----- src/main/java/janggi/domain/JanggiGame.java | 4 ++++ src/main/java/janggi/domain/Pieces.java | 2 +- src/main/java/janggi/domain/Turn.java | 6 +++++- src/main/java/janggi/domain/piece/Cha.java | 4 ++-- src/main/java/janggi/domain/piece/Gung.java | 4 ++-- src/main/java/janggi/domain/piece/Jol.java | 4 ++-- src/main/java/janggi/domain/piece/Ma.java | 4 ++-- src/main/java/janggi/domain/piece/Piece.java | 2 +- src/main/java/janggi/domain/piece/PieceType.java | 10 +++++----- src/main/java/janggi/domain/piece/Po.java | 4 ++-- src/main/java/janggi/domain/piece/Sa.java | 4 ++-- src/main/java/janggi/domain/piece/Sang.java | 4 ++-- src/main/java/janggi/view/OutputView.java | 4 ++-- src/test/java/janggi/domain/PiecesTest.java | 2 +- 16 files changed, 45 insertions(+), 31 deletions(-) diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index 687c9ecee5..d8b82358cb 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -38,7 +38,8 @@ private Position readValidStartPosition() { } private Position readValidEndPosition(Position startPosition) { - OutputView.printAskMovePosition(janggiGame.findPiece(startPosition).nickname()); + OutputView.printAskMovePosition(janggiGame.findPiece(startPosition).name()); + OutputView.printAskMovePosition(janggiGame.getPieceName(startPosition)); String rawMovePosition = InputView.readLine(); List parsedMovePosition = DelimiterParser.parse(rawMovePosition); Position endPosition = Position.makePosition(parsedMovePosition); diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 8884df011f..21e4244c97 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -40,9 +40,9 @@ public boolean isPieceExist(Position position, TeamType currentTeamType) { return currentTeam.isPieceExist(position); } - public Piece findNextTurnTeamPiece(Position position, TeamType beforeTeamType) { - Team nowTeam = opponentTeam(beforeTeamType); - return findTeamPiece(position, nowTeam); + public Piece findNextTurnTeamPiece(Position position, TeamType currentTeamType) { + Team currentTeam = currentTeam(currentTeamType); + return findTeamPiece(position, currentTeam); } public Optional findPiece(Position position) { @@ -75,8 +75,13 @@ public Board move( return createMovedBoard(nowTurn, movedCurrentTeam, remainedOpponentTeam); } - private Piece findTeamPiece(Position position, Team nowTeam) { - return nowTeam.findPiece(position) + public String getPieceName(Position position, TeamType currentTeamType) { + Piece piece = findTeamPiece(position, currentTeam(currentTeamType)); + return piece.name(); + } + + private Piece findTeamPiece(Position position, Team currentTeam) { + return currentTeam.findPiece(position) .orElseThrow(() -> new IllegalArgumentException("입력한 위치에 기물이 없습니다.")); } diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index ac113801b9..dd57f583e4 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -41,6 +41,10 @@ public Piece findPiece(Position position) { return getLastTurn().findPiece(position); } + public String getPieceName(Position position) { + return getLastTurn().getPieceName(position); + } + public void doGame(Position startPosition, Position endPosition) { Turn lastTurn = getLastTurn(); Turn newTurn = lastTurn.move(startPosition, endPosition); diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index 73a2dacccc..09ebd5694b 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -60,7 +60,7 @@ public Map makeSnapShot() { for (Map.Entry entry : value.entrySet()) { Position position = entry.getKey(); Piece piece = entry.getValue(); - snapShot.put(position, new BoardSpot(position, piece.nickname(), piece.getTeamType())); + snapShot.put(position, new BoardSpot(position, piece.name(), piece.getTeamType())); } return snapShot; } diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index 521f2de836..66e488aa0f 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -23,7 +23,11 @@ public boolean isMyTeamPieceExist(Position position) { } public Piece findPiece(Position position) { - return board.findNextTurnTeamPiece(position, movedTeam); + return board.findNextTurnTeamPiece(position, opponentTeamType()); + } + + public String getPieceName(Position position) { + return board.getPieceName(position, opponentTeamType()); } public Turn move(Position startPosition, Position endPosition) { diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 7c8966abe9..8eb944eb9d 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -64,8 +64,8 @@ private boolean isStraightDirection(int startX, int startY, int endX, int endY) } @Override - public String nickname() { - return pieceType.getNickname(); + public String name() { + return pieceType.getName(); } @Override diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index 5110458650..d58ddae260 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -65,8 +65,8 @@ private boolean isOneStep(int distanceX, int distanceY) { } @Override - public String nickname() { - return pieceType.getNickname(); + public String name() { + return pieceType.getName(); } @Override diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 7be3af0c20..ed1c9910b3 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -62,8 +62,8 @@ private List createPaths() { } @Override - public String nickname() { - return pieceType.getNickname(); + public String name() { + return pieceType.getName(); } @Override diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 08b6ee6433..2abc6f0fa2 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -54,8 +54,8 @@ public boolean isObstaclesNotExist(Position start, Position end, Board board) { } @Override - public String nickname() { - return pieceType.getNickname(); + public String name() { + return pieceType.getName(); } @Override diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index 8b4c7c6c2c..cfdab43f76 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -14,7 +14,7 @@ public interface Piece { boolean isObstaclesNotExist(Position start, Position end, Board board); - String nickname(); + String name(); PieceType getPieceType(); diff --git a/src/main/java/janggi/domain/piece/PieceType.java b/src/main/java/janggi/domain/piece/PieceType.java index 2b6fc7d98f..983016a8af 100644 --- a/src/main/java/janggi/domain/piece/PieceType.java +++ b/src/main/java/janggi/domain/piece/PieceType.java @@ -10,13 +10,13 @@ public enum PieceType { SANG("상"), ; - PieceType(String nickname) { - this.nickname = nickname; + PieceType(String name) { + this.name = name; } - private final String nickname; + private final String name; - public String getNickname() { - return nickname; + public String getName() { + return name; } } diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index 0d8d15207d..c559064b8f 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -65,8 +65,8 @@ public boolean isObstaclesNotExist(Position start, Position end, Board board) { } @Override - public String nickname() { - return pieceType.getNickname(); + public String name() { + return pieceType.getName(); } @Override diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index 60d00c909f..f96b762531 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -65,8 +65,8 @@ private boolean isOneStep(int distanceX, int distanceY) { } @Override - public String nickname() { - return pieceType.getNickname(); + public String name() { + return pieceType.getName(); } @Override diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index 2bd29c3cd2..c3275c01bd 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -54,8 +54,8 @@ public boolean isObstaclesNotExist(Position start, Position end, Board board) { } @Override - public String nickname() { - return pieceType.getNickname(); + public String name() { + return pieceType.getName(); } @Override diff --git a/src/main/java/janggi/view/OutputView.java b/src/main/java/janggi/view/OutputView.java index 6c9782eeca..33cf9a7785 100644 --- a/src/main/java/janggi/view/OutputView.java +++ b/src/main/java/janggi/view/OutputView.java @@ -114,7 +114,7 @@ public static void printAskPiecePosition() { printMessage("움직일 기물의 좌표를 입력해주세요. (ex. 1,3)"); } - public static void printAskMovePosition(String nickname) { - printMessage(nickname + "의 목적 좌표를 입력해주세요. (ex. 1,3)"); + public static void printAskMovePosition(String pieceName) { + printMessage(pieceName + "의 목적 좌표를 입력해주세요. (ex. 1,3)"); } } diff --git a/src/test/java/janggi/domain/PiecesTest.java b/src/test/java/janggi/domain/PiecesTest.java index 57efbd3ab1..4e55c4c11e 100644 --- a/src/test/java/janggi/domain/PiecesTest.java +++ b/src/test/java/janggi/domain/PiecesTest.java @@ -62,7 +62,7 @@ void makeSnapShot() { assertThat(gungSpot).isNotNull(); assertAll( () -> assertThat(gungSpot.position()).isEqualTo(new Position(5, 2)), - () -> assertThat(gungSpot.pieceName()).isEqualTo(PieceType.GUNG.getNickname()), + () -> assertThat(gungSpot.pieceName()).isEqualTo(PieceType.GUNG.getName()), () -> assertThat(gungSpot.teamType()).isEqualTo(janggi.domain.side.TeamType.CHU) ); } From c24802b07dc92e7baa3107f1fa23b37bfd050d0f Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Tue, 31 Mar 2026 14:49:32 +0900 Subject: [PATCH 66/99] =?UTF-8?q?=EA=B8=B0=EB=AC=BC=EB=B3=84=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EA=B7=9C=EC=B9=99=20=EB=A1=9C=EC=A7=81=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/main/java/janggi/domain/Board.java | 31 +++++------ src/main/java/janggi/domain/JanggiGame.java | 4 +- src/main/java/janggi/domain/Turn.java | 2 +- src/main/java/janggi/domain/piece/Cha.java | 25 +++++++-- src/main/java/janggi/domain/piece/Gung.java | 22 ++++---- src/main/java/janggi/domain/piece/Jol.java | 22 ++++---- src/main/java/janggi/domain/piece/Ma.java | 28 ++++++---- src/main/java/janggi/domain/piece/Piece.java | 8 +-- src/main/java/janggi/domain/piece/Po.java | 51 ++++++++++++++++--- src/main/java/janggi/domain/piece/Sa.java | 22 ++++---- src/main/java/janggi/domain/piece/Sang.java | 28 ++++++---- src/test/java/janggi/domain/BoardTest.java | 33 ++++++------ src/test/java/janggi/domain/TurnTest.java | 6 +-- .../java/janggi/domain/piece/ChaTest.java | 18 ++++--- .../java/janggi/domain/piece/GungTest.java | 31 ++++++----- .../java/janggi/domain/piece/JolTest.java | 31 ++++++----- src/test/java/janggi/domain/piece/MaTest.java | 26 ++++++---- src/test/java/janggi/domain/piece/PoTest.java | 16 +++--- src/test/java/janggi/domain/piece/SaTest.java | 29 ++++++----- .../java/janggi/domain/piece/SangTest.java | 26 ++++++---- 20 files changed, 281 insertions(+), 178 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 21e4244c97..f4fbedc1a5 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -57,11 +57,10 @@ public boolean hasPiece(Position position) { return findPiece(position).isPresent(); } - public void canMove(Position startPosition, Position endPosition, TeamType nowTeam) { - validateRange(startPosition); + public void canMove(Position startPosition, Position endPosition, TeamType currentTeamType) { validateRange(endPosition); - Piece piece = findTeamPiece(startPosition, currentTeam(nowTeam)); - validateTargetPosition(currentTeam(nowTeam), endPosition); + Piece piece = findTeamPiece(startPosition, currentTeam(currentTeamType)); + validateEndPosition(currentTeam(currentTeamType), endPosition); validateCanMove(piece, startPosition, endPosition); } @@ -99,20 +98,14 @@ private Team opponentTeam(TeamType nowTurn) { return chu; } - private void validateCanMove(Piece piece, Position piecePosition, Position targetPosition) { - if (!piece.isValidMovePattern(piecePosition.getX(), piecePosition.getY(), targetPosition.getX(), - targetPosition.getY())) { - throw new IllegalArgumentException("이동할 수 없는 위치입니다."); - } - if (!piece.isObstaclesNotExist(piecePosition, targetPosition, this)) { - throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + private void validateEndPosition(Team team, Position endPosition) { + if (team.findPiece(endPosition).isPresent()) { + throw new IllegalArgumentException("같은 팀의 기물이 있는 위치로는 이동할 수 없습니다."); } } - private void validateTargetPosition(Team team, Position targetPosition) { - if (team.findPiece(targetPosition).isPresent()) { - throw new IllegalArgumentException("같은 팀의 기물이 있는 위치로는 이동할 수 없습니다."); - } + private void validateCanMove(Piece piece, Position startPosition, Position endPosition) { + piece.validateCanMove(startPosition, endPosition, this); } private Team removeOpponentPiece(TeamType nowTurn, Position targetPosition) { @@ -138,11 +131,11 @@ private void validateRange(Position inputPosition) { } } - private boolean isInRange(int start, int last, int index) { - return index >= start && index <= last; - } - private boolean isNotInRange(int start, int last, int index) { return !isInRange(start, last, index); } + + private boolean isInRange(int start, int last, int index) { + return index >= start && index <= last; + } } diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index dd57f583e4..0a977a4dd6 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -24,14 +24,14 @@ public BoardSpots makeCurrentTurnBoardSnapShot() { public void validatePieceExist(Position position) { Turn lastTurn = getLastTurn(); - if (!lastTurn.isMyTeamPieceExist(position)) { + if (!lastTurn.isCurrentTeamPieceExist(position)) { throw new IllegalArgumentException("기물이 존재하는 좌표가 아닙니다."); } } public void validateValidEndPosition(Position startPosition, Position endPosition) { Turn lastTurn = getLastTurn(); - if (lastTurn.isMyTeamPieceExist(endPosition)) { + if (lastTurn.isCurrentTeamPieceExist(endPosition)) { throw new IllegalArgumentException("아군이 존재하는 좌표로 이동할 수 없습니다."); } lastTurn.canMove(startPosition, endPosition); diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index 66e488aa0f..b8284292d8 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -18,7 +18,7 @@ public static Turn createInitialTurn() { return new Turn(TeamType.HAN, Board.createInitialBoard()); } - public boolean isMyTeamPieceExist(Position position) { + public boolean isCurrentTeamPieceExist(Position position) { return board.isPieceExist(position, opponentTeamType()); } diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 8eb944eb9d..6e81236465 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -26,12 +26,28 @@ public Cha(TeamType teamType) { } @Override - public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { - return findMovePath(startX, startY, endX, endY).isPresent(); + public void validateCanMove(Position start, Position end, Board board) { + if (!isValidMovePattern(start, end)) { + throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + } + + if (!isObstaclesNotExist(start, end, board)) { + throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); + } + } + + @Override + public boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); } @Override - public Optional findMovePath(int startX, int startY, int endX, int endY) { + public Optional findMovePath(Position start, Position end) { + int startX = start.getX(); + int startY = start.getY(); + int endX = end.getX(); + int endY = end.getY(); + if (isSamePosition(startX, startY, endX, endY)) { return Optional.empty(); } @@ -45,9 +61,8 @@ public Optional findMovePath(int startX, int startY, int endX, int end .findFirst(); } - @Override public boolean isObstaclesNotExist(Position start, Position end, Board board) { - Optional movePath = findMovePath(start.getX(), start.getY(), end.getX(), end.getY()); + Optional movePath = findMovePath(start, end); if (movePath.isEmpty()) { return false; } diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index d58ddae260..5fddb51b49 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -30,14 +30,21 @@ public Gung(TeamType teamType) { } @Override - public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { - return findMovePath(startX, startY, endX, endY).isPresent(); + public void validateCanMove(Position start, Position end, Board board) { + if (!isValidMovePattern(start, end)) { + throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + } + } + + @Override + public boolean isValidMovePattern(Position startPosition, Position endPosition) { + return findMovePath(startPosition, endPosition).isPresent(); } @Override - public Optional findMovePath(int startX, int startY, int endX, int endY) { - int dx = endX - startX; - int dy = endY - startY; + public Optional findMovePath(Position startPosition, Position endPosition) { + int dx = endPosition.getX() - startPosition.getX(); + int dy = endPosition.getY() - startPosition.getY(); int distanceX = Math.abs(dx); int distanceY = Math.abs(dy); if (isSamePosition(distanceX, distanceY)) { @@ -51,11 +58,6 @@ public Optional findMovePath(int startX, int startY, int endX, int end .findFirst(); } - @Override - public boolean isObstaclesNotExist(Position start, Position end, Board board) { - return true; - } - private boolean isSamePosition(int distanceX, int distanceY) { return distanceX == 0 && distanceY == 0; } diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index ed1c9910b3..7e9a7bc672 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -21,14 +21,21 @@ public Jol(TeamType teamType) { } @Override - public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { - return findMovePath(startX, startY, endX, endY).isPresent(); + public void validateCanMove(Position start, Position end, Board board) { + if (!isValidMovePattern(start, end)) { + throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + } + } + + @Override + public boolean isValidMovePattern(Position startPosition, Position endPosition) { + return findMovePath(startPosition, endPosition).isPresent(); } @Override - public Optional findMovePath(int startX, int startY, int endX, int endY) { - int dx = endX - startX; - int dy = endY - startY; + public Optional findMovePath(Position startPosition, Position endPosition) { + int dx = endPosition.getX() - startPosition.getX(); + int dy = endPosition.getY() - startPosition.getY(); if (isSamePosition(dx, dy)) { return Optional.empty(); } @@ -37,11 +44,6 @@ public Optional findMovePath(int startX, int startY, int endX, int end .findFirst(); } - @Override - public boolean isObstaclesNotExist(Position start, Position end, Board board) { - return true; - } - private boolean isSamePosition(int distanceX, int distanceY) { return distanceX == 0 && distanceY == 0; } diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 2abc6f0fa2..fd462531d4 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -30,26 +30,36 @@ public Ma(TeamType teamType) { } @Override - public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { - return findMovePath(startX, startY, endX, endY).isPresent(); + public void validateCanMove(Position start, Position end, Board board) { + if (!isValidMovePattern(start, end)) { + throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + } + + if (!isObstaclesNotExist(start, end, board)) { + throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); + } } @Override - public Optional findMovePath(int startX, int startY, int endX, int endY) { - int dx = endX - startX; - int dy = endY - startY; + public boolean isValidMovePattern(Position startPosition, Position endPosition) { + return findMovePath(startPosition, endPosition).isPresent(); + } + + @Override + public Optional findMovePath(Position startPosition, Position endPosition) { + int dx = endPosition.getX() - startPosition.getX(); + int dy = endPosition.getY() - startPosition.getY(); return paths.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); } - @Override - public boolean isObstaclesNotExist(Position start, Position end, Board board) { - Optional movePath = findMovePath(start.getX(), start.getY(), end.getX(), end.getY()); + public boolean isObstaclesNotExist(Position startPosition, Position endPosition, Board board) { + Optional movePath = findMovePath(startPosition, endPosition); if (movePath.isEmpty()) { return false; } - return movePath.get().intermediatePositions(start, end).stream() + return movePath.get().intermediatePositions(startPosition, endPosition).stream() .noneMatch(board::hasPiece); } diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index cfdab43f76..cd86a69d7d 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -8,15 +8,17 @@ public interface Piece { - boolean isValidMovePattern(int startX, int startY, int endX, int endY); + boolean isValidMovePattern(Position startPosition, Position endPosition); - Optional findMovePath(int startX, int startY, int endX, int endY); + Optional findMovePath(Position startPosition, Position endPosition); - boolean isObstaclesNotExist(Position start, Position end, Board board); +// boolean isObstaclesNotExist(Position start, Position end, Board board); String name(); PieceType getPieceType(); TeamType getTeamType(); + + void validateCanMove(Position start, Position end, Board board); } diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index c559064b8f..564297227b 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -26,12 +26,26 @@ public Po(TeamType teamType) { } @Override - public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { - return findMovePath(startX, startY, endX, endY).isPresent(); + public void validateCanMove(Position start, Position end, Board board) { + if (!isValidMovePattern(start, end)) { + throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + } + + validateObstacles(start, end, board); + } + + @Override + public boolean isValidMovePattern(Position startPosition, Position endPosition) { + return findMovePath(startPosition, endPosition).isPresent(); } @Override - public Optional findMovePath(int startX, int startY, int endX, int endY) { + public Optional findMovePath(Position startPosition, Position endPosition) { + int startX = startPosition.getX(); + int startY = startPosition.getY(); + int endX = endPosition.getX(); + int endY = endPosition.getY(); + if (startX == endX && startY == endY) { return Optional.empty(); } @@ -42,13 +56,12 @@ public Optional findMovePath(int startX, int startY, int endX, int end .findFirst(); } - @Override - public boolean isObstaclesNotExist(Position start, Position end, Board board) { - Optional movePath = findMovePath(start.getX(), start.getY(), end.getX(), end.getY()); + public boolean isObstaclesNotExist(Position startPosition, Position endPosition, Board board) { + Optional movePath = findMovePath(startPosition, endPosition); if (movePath.isEmpty()) { return false; } - List intermediatePositions = movePath.get().intermediatePositions(start, end); + List intermediatePositions = movePath.get().intermediatePositions(startPosition, endPosition); List obstacles = intermediatePositions.stream() .map(board::findPiece) .filter(Optional::isPresent) @@ -60,7 +73,7 @@ public boolean isObstaclesNotExist(Position start, Position end, Board board) { if (obstacles.getFirst().getPieceType() == PieceType.PO) { return false; } - Optional targetPiece = board.findPiece(end); + Optional targetPiece = board.findPiece(endPosition); return targetPiece.isEmpty() || !(targetPiece.get().getPieceType() == PieceType.PO); } @@ -78,4 +91,26 @@ public PieceType getPieceType() { public TeamType getTeamType() { return teamType; } + + private void validateObstacles(Position start, Position end, Board board) { + Optional movePath = findMovePath(start, end); + if (movePath.isEmpty()) { + throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + } + List intermediatePositions = movePath.get().intermediatePositions(start, end); + List obstacles = intermediatePositions.stream() + .map(board::findPiece) + .filter(Optional::isPresent) + .map(Optional::get) + .toList(); + if (obstacles.isEmpty()) { + throw new IllegalArgumentException("이동 경로에 기물이 존재하지 않아 이동할 수 없습니다."); + } + if (obstacles.size() > 1) { + throw new IllegalArgumentException("이동 경로에 기물이 1개 이상 존재합니다."); + } + if (obstacles.getFirst().getPieceType() == PieceType.PO) { + throw new IllegalArgumentException("포는 포를 잡을 수 없습니다."); + } + } } diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index f96b762531..88cca0181e 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -30,14 +30,21 @@ public Sa(TeamType teamType) { } @Override - public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { - return findMovePath(startX, startY, endX, endY).isPresent(); + public void validateCanMove(Position start, Position end, Board board) { + if (!isValidMovePattern(start, end)) { + throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + } + } + + @Override + public boolean isValidMovePattern(Position startPosition, Position endPosition) { + return findMovePath(startPosition, endPosition).isPresent(); } @Override - public Optional findMovePath(int startX, int startY, int endX, int endY) { - int dx = endX - startX; - int dy = endY - startY; + public Optional findMovePath(Position startPosition, Position endPosition) { + int dx = endPosition.getX() - startPosition.getX(); + int dy = endPosition.getY() - startPosition.getY(); int distanceX = Math.abs(dx); int distanceY = Math.abs(dy); if (isSamePosition(distanceX, distanceY)) { @@ -51,11 +58,6 @@ public Optional findMovePath(int startX, int startY, int endX, int end .findFirst(); } - @Override - public boolean isObstaclesNotExist(Position start, Position end, Board board) { - return true; - } - private boolean isSamePosition(int distanceX, int distanceY) { return distanceX == 0 && distanceY == 0; } diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index c3275c01bd..61562dfaf4 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -30,26 +30,36 @@ public Sang(TeamType teamType) { } @Override - public boolean isValidMovePattern(int startX, int startY, int endX, int endY) { - return findMovePath(startX, startY, endX, endY).isPresent(); + public void validateCanMove(Position start, Position end, Board board) { + if (!isValidMovePattern(start, end)) { + throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + } + + if (!isObstaclesNotExist(start, end, board)) { + throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); + } } @Override - public Optional findMovePath(int startX, int startY, int endX, int endY) { - int dx = endX - startX; - int dy = endY - startY; + public boolean isValidMovePattern(Position startPosition, Position endPosition) { + return findMovePath(startPosition, endPosition).isPresent(); + } + + @Override + public Optional findMovePath(Position startPosition, Position endPosition) { + int dx = endPosition.getX() - startPosition.getX(); + int dy = endPosition.getY() - startPosition.getY(); return paths.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); } - @Override - public boolean isObstaclesNotExist(Position start, Position end, Board board) { - Optional movePath = findMovePath(start.getX(), start.getY(), end.getX(), end.getY()); + public boolean isObstaclesNotExist(Position startPosition, Position endPosition, Board board) { + Optional movePath = findMovePath(startPosition, endPosition); if (movePath.isEmpty()) { return false; } - return movePath.get().intermediatePositions(start, end).stream() + return movePath.get().intermediatePositions(startPosition, endPosition).stream() .noneMatch(board::hasPiece); } diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java index c63ec5dcdf..45da169cb7 100644 --- a/src/test/java/janggi/domain/BoardTest.java +++ b/src/test/java/janggi/domain/BoardTest.java @@ -12,18 +12,19 @@ class BoardTest { - @Test - @DisplayName("시작 위치가 장기판 범위를 벗어나면 이동할 수 없다.") - void cannotMoveWhenStartPositionIsOutOfRange() { - // given - Board board = Board.createInitialBoard(); - Position outOfBound = new Position(0, 0); - - // when & then - assertThatThrownBy(() -> board.canMove(outOfBound, new Position(1, 4), TeamType.CHU)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("입력한 좌표가 장기판 범위 밖입니다."); - } + // canMove에서 시작위치 검증 안 하기 때문에 없어도 되는 테스트 +// @Test +// @DisplayName("시작 위치가 장기판 범위를 벗어나면 이동할 수 없다.") +// void cannotMoveWhenStartPositionIsOutOfRange() { +// // given +// Board board = Board.createInitialBoard(); +// Position outOfBound = new Position(0, 0); +// +// // when & then +// assertThatThrownBy(() -> board.canMove(outOfBound, new Position(1, 4), TeamType.CHU)) +// .isInstanceOf(IllegalArgumentException.class) +// .hasMessage("입력한 좌표가 장기판 범위 밖입니다."); +// } @Test @DisplayName("도착 위치가 장기판 범위를 벗어나면 이동할 수 없다.") @@ -92,7 +93,7 @@ void cannotMoveWhenMovePatternIsInvalid() { } @Test - @DisplayName("장애물이 있는 차의 이동은 막는다.") + @DisplayName("차의 경로 중간에 장애물이 있으면 이동할 수 없다.") void cannotMoveChaWhenPathIsBlocked() { // given Board board = Board.createInitialBoard(); @@ -102,7 +103,7 @@ void cannotMoveChaWhenPathIsBlocked() { // when & then assertThatThrownBy(() -> board.canMove(chaStartPosition, chaEndPosition, TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."); + .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); } @Test @@ -116,7 +117,7 @@ void cannotMoveMaWhenLegIsBlocked() { // when & then assertThatThrownBy(() -> board.canMove(maStartPosition, maEndPosition, TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."); + .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); } @Test @@ -130,7 +131,7 @@ void cannotMovePoWithoutBridge() { // when & then assertThatThrownBy(() -> board.canMove(poStartPosition, poEndPosition, TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."); + .hasMessage("이동 경로에 기물이 존재하지 않아 이동할 수 없습니다."); } @Test diff --git a/src/test/java/janggi/domain/TurnTest.java b/src/test/java/janggi/domain/TurnTest.java index 2d5185bcff..715bfb3824 100644 --- a/src/test/java/janggi/domain/TurnTest.java +++ b/src/test/java/janggi/domain/TurnTest.java @@ -28,7 +28,7 @@ void createInitialTurn() { @Test @DisplayName("현재 차례 팀의 기물 존재 여부를 확인한다.") - void isMyTeamPieceExist() { + void isCurrentTeamPieceExist() { // given Turn turn = Turn.createInitialTurn(); Position chuJolPosition = new Position(1, 4); @@ -36,8 +36,8 @@ void isMyTeamPieceExist() { // when & then assertAll( - () -> assertThat(turn.isMyTeamPieceExist(chuJolPosition)).isTrue(), - () -> assertThat(turn.isMyTeamPieceExist(hanJolPosition)).isFalse() + () -> assertThat(turn.isCurrentTeamPieceExist(chuJolPosition)).isTrue(), + () -> assertThat(turn.isCurrentTeamPieceExist(hanJolPosition)).isFalse() ); } diff --git a/src/test/java/janggi/domain/piece/ChaTest.java b/src/test/java/janggi/domain/piece/ChaTest.java index c3c97c07b7..26ee50a562 100644 --- a/src/test/java/janggi/domain/piece/ChaTest.java +++ b/src/test/java/janggi/domain/piece/ChaTest.java @@ -19,10 +19,10 @@ void isValidMovePatternStraight() { // when & then assertAll( - () -> assertThat(cha.isValidMovePattern(4, 4, 7, 4)).isTrue(), - () -> assertThat(cha.isValidMovePattern(4, 4, 1, 4)).isTrue(), - () -> assertThat(cha.isValidMovePattern(4, 4, 4, 8)).isTrue(), - () -> assertThat(cha.isValidMovePattern(4, 4, 4, 1)).isTrue() + () -> assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(7, 4))).isTrue(), + () -> assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(1, 4))).isTrue(), + () -> assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(4, 8))).isTrue(), + () -> assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(4, 1))).isTrue() ); } @@ -34,8 +34,8 @@ void cannotMoveDiagonal() { // when & then assertAll( - () -> assertThat(cha.isValidMovePattern(4, 4, 5, 5)).isFalse(), - () -> assertThat(cha.isValidMovePattern(4, 4, 2, 2)).isFalse() + () -> assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isFalse(), + () -> assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(2, 2))).isFalse() ); } @@ -46,7 +46,7 @@ void cannotMoveSamePosition() { Cha cha = new Cha(TeamType.CHU); // when & then - assertThat(cha.isValidMovePattern(4, 4, 4, 4)).isFalse(); + assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse(); } @Test @@ -76,4 +76,8 @@ void cannotMoveWhenRouteIsBlocked() { // then assertThat(result).isFalse(); } + + private Position createPosition(int x, int y) { + return new Position(x, y); + } } diff --git a/src/test/java/janggi/domain/piece/GungTest.java b/src/test/java/janggi/domain/piece/GungTest.java index 21e837610d..c44108266d 100644 --- a/src/test/java/janggi/domain/piece/GungTest.java +++ b/src/test/java/janggi/domain/piece/GungTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import janggi.domain.Position; import janggi.domain.side.TeamType; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -17,10 +18,10 @@ void isValidMovePatternStraightOneStep() { // when & then assertAll( - () -> assertThat(gung.isValidMovePattern(4, 4, 4, 5)).isTrue(), - () -> assertThat(gung.isValidMovePattern(4, 4, 4, 3)).isTrue(), - () -> assertThat(gung.isValidMovePattern(4, 4, 5, 4)).isTrue(), - () -> assertThat(gung.isValidMovePattern(4, 4, 3, 4)).isTrue() + () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(4, 5))).isTrue(), + () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(4, 3))).isTrue(), + () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(5, 4))).isTrue(), + () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(3, 4))).isTrue() ); } @@ -32,10 +33,10 @@ void isValidMovePatternDiagonalOneStep() { // when & then assertAll( - () -> assertThat(gung.isValidMovePattern(4, 4, 5, 5)).isTrue(), - () -> assertThat(gung.isValidMovePattern(4, 4, 5, 3)).isTrue(), - () -> assertThat(gung.isValidMovePattern(4, 4, 3, 5)).isTrue(), - () -> assertThat(gung.isValidMovePattern(4, 4, 3, 3)).isTrue() + () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isTrue(), + () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(5, 3))).isTrue(), + () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(3, 5))).isTrue(), + () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(3, 3))).isTrue() ); } @@ -47,10 +48,10 @@ void cannotMoveOverOneStep() { // when & then assertAll( - () -> assertThat(gung.isValidMovePattern(4, 4, 6, 4)).isFalse(), - () -> assertThat(gung.isValidMovePattern(4, 4, 6, 6)).isFalse(), - () -> assertThat(gung.isValidMovePattern(4, 4, 4, 6)).isFalse(), - () -> assertThat(gung.isValidMovePattern(4, 4, 2, 4)).isFalse() + () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(6, 4))).isFalse(), + () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(6, 6))).isFalse(), + () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(4, 6))).isFalse(), + () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(2, 4))).isFalse() ); } @@ -61,6 +62,10 @@ void cannotMoveSamePosition() { Gung gung = new Gung(TeamType.CHU); // when & then - assertThat(gung.isValidMovePattern(4, 4, 4, 4)).isFalse(); + assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse(); + } + + private Position createPosition(int x, int y) { + return new Position(x, y); } } diff --git a/src/test/java/janggi/domain/piece/JolTest.java b/src/test/java/janggi/domain/piece/JolTest.java index b3008a9a5b..9b6590cb94 100644 --- a/src/test/java/janggi/domain/piece/JolTest.java +++ b/src/test/java/janggi/domain/piece/JolTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import janggi.domain.Position; import janggi.domain.side.TeamType; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -16,7 +17,7 @@ void chuIsValidMovePatternForward() { Jol jol = new Jol(TeamType.CHU); // when & then - assertThat(jol.isValidMovePattern(4, 4, 4, 5)).isTrue(); + assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(4, 5))).isTrue(); } @Test @@ -26,7 +27,7 @@ void hanIsValidMovePatternForward() { Jol jol = new Jol(TeamType.HAN); // when & then - assertThat(jol.isValidMovePattern(4, 4, 4, 3)).isTrue(); + assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(4, 3))).isTrue(); } @Test @@ -38,10 +39,10 @@ void isValidMovePatternHorizontally() { // when & then assertAll( - () -> assertThat(jolOfChu.isValidMovePattern(4, 4, 5, 4)).isTrue(), - () -> assertThat(jolOfChu.isValidMovePattern(4, 4, 3, 4)).isTrue(), - () -> assertThat(jolOfHan.isValidMovePattern(4, 4, 5, 4)).isTrue(), - () -> assertThat(jolOfHan.isValidMovePattern(4, 4, 3, 4)).isTrue() + () -> assertThat(jolOfChu.isValidMovePattern(createPosition(4, 4), createPosition(5, 4))).isTrue(), + () -> assertThat(jolOfChu.isValidMovePattern(createPosition(4, 4), createPosition(3, 4))).isTrue(), + () -> assertThat(jolOfHan.isValidMovePattern(createPosition(4, 4), createPosition(5, 4))).isTrue(), + () -> assertThat(jolOfHan.isValidMovePattern(createPosition(4, 4), createPosition(3, 4))).isTrue() ); } @@ -54,8 +55,8 @@ void cannotMoveBackward() { // when & then assertAll( - () -> assertThat(jolOfChu.isValidMovePattern(4, 4, 4, 3)).isFalse(), - () -> assertThat(hanJol.isValidMovePattern(4, 4, 4, 5)).isFalse() + () -> assertThat(jolOfChu.isValidMovePattern(createPosition(4, 4), createPosition(4, 3))).isFalse(), + () -> assertThat(hanJol.isValidMovePattern(createPosition(4, 4), createPosition(4, 5))).isFalse() ); } @@ -67,10 +68,10 @@ void cannotMoveInvalidPath() { // when & then assertAll( - () -> assertThat(jol.isValidMovePattern(4, 4, 4, 6)).isFalse(), - () -> assertThat(jol.isValidMovePattern(4, 4, 2, 4)).isFalse(), - () -> assertThat(jol.isValidMovePattern(4, 4, 5, 5)).isFalse(), - () -> assertThat(jol.isValidMovePattern(4, 4, 3, 3)).isFalse() + () -> assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(4, 6))).isFalse(), + () -> assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(2, 4))).isFalse(), + () -> assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isFalse(), + () -> assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(3, 3))).isFalse() ); } @@ -81,6 +82,10 @@ void cannotMoveSamePosition() { Jol jol = new Jol(TeamType.CHU); // when & then - assertThat(jol.isValidMovePattern(4, 4, 4, 4)).isFalse(); + assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse(); + } + + private Position createPosition(int x, int y) { + return new Position(x, y); } } diff --git a/src/test/java/janggi/domain/piece/MaTest.java b/src/test/java/janggi/domain/piece/MaTest.java index 9a589305cb..348c6eaf30 100644 --- a/src/test/java/janggi/domain/piece/MaTest.java +++ b/src/test/java/janggi/domain/piece/MaTest.java @@ -19,14 +19,14 @@ void isValidMovePattern() { // when & then assertAll( - () -> assertThat(ma.isValidMovePattern(4, 4, 5, 6)).isTrue(), - () -> assertThat(ma.isValidMovePattern(4, 4, 3, 6)).isTrue(), - () -> assertThat(ma.isValidMovePattern(4, 4, 5, 2)).isTrue(), - () -> assertThat(ma.isValidMovePattern(4, 4, 3, 2)).isTrue(), - () -> assertThat(ma.isValidMovePattern(4, 4, 2, 5)).isTrue(), - () -> assertThat(ma.isValidMovePattern(4, 4, 2, 3)).isTrue(), - () -> assertThat(ma.isValidMovePattern(4, 4, 6, 5)).isTrue(), - () -> assertThat(ma.isValidMovePattern(4, 4, 6, 3)).isTrue() + () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(5, 6))).isTrue(), + () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(3, 6))).isTrue(), + () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(5, 2))).isTrue(), + () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(3, 2))).isTrue(), + () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(2, 5))).isTrue(), + () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(2, 3))).isTrue(), + () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(6, 5))).isTrue(), + () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(6, 3))).isTrue() ); } @@ -38,9 +38,9 @@ void cannotMoveInvalidPattern() { // when & then assertAll( - () -> assertThat(ma.isValidMovePattern(4, 4, 4, 5)).isFalse(), - () -> assertThat(ma.isValidMovePattern(4, 4, 5, 5)).isFalse(), - () -> assertThat(ma.isValidMovePattern(4, 4, 4, 4)).isFalse() + () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(4, 5))).isFalse(), + () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isFalse(), + () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse() ); } @@ -71,4 +71,8 @@ void cannotMoveWhenIntermediatePositionIsBlocked() { // then assertThat(result).isFalse(); } + + private Position createPosition(int x, int y) { + return new Position(x, y); + } } diff --git a/src/test/java/janggi/domain/piece/PoTest.java b/src/test/java/janggi/domain/piece/PoTest.java index 829b701f74..f0628140a8 100644 --- a/src/test/java/janggi/domain/piece/PoTest.java +++ b/src/test/java/janggi/domain/piece/PoTest.java @@ -19,10 +19,10 @@ void isValidMovePatternStraight() { // when & then assertAll( - () -> assertThat(po.isValidMovePattern(4, 4, 4, 8)).isTrue(), - () -> assertThat(po.isValidMovePattern(4, 4, 4, 1)).isTrue(), - () -> assertThat(po.isValidMovePattern(4, 4, 7, 4)).isTrue(), - () -> assertThat(po.isValidMovePattern(4, 4, 1, 4)).isTrue() + () -> assertThat(po.isValidMovePattern(createPosition(4, 4), createPosition(4, 8))).isTrue(), + () -> assertThat(po.isValidMovePattern(createPosition(4, 4), createPosition(4, 1))).isTrue(), + () -> assertThat(po.isValidMovePattern(createPosition(4, 4), createPosition(7, 4))).isTrue(), + () -> assertThat(po.isValidMovePattern(createPosition(4, 4), createPosition(1, 4))).isTrue() ); } @@ -34,8 +34,8 @@ void cannotMoveInvalidPattern() { // when & then assertAll( - () -> assertThat(po.isValidMovePattern(4, 4, 5, 5)).isFalse(), - () -> assertThat(po.isValidMovePattern(4, 4, 4, 4)).isFalse() + () -> assertThat(po.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isFalse(), + () -> assertThat(po.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse() ); } @@ -82,4 +82,8 @@ void cannotUsePoAsBridgeOrTarget() { () -> assertThat(po.isObstaclesNotExist(new Position(2, 3), new Position(2, 8), movedBoard)).isFalse() ); } + + private Position createPosition(int x, int y) { + return new Position(x, y); + } } diff --git a/src/test/java/janggi/domain/piece/SaTest.java b/src/test/java/janggi/domain/piece/SaTest.java index 95a47492a7..2a4ea78d74 100644 --- a/src/test/java/janggi/domain/piece/SaTest.java +++ b/src/test/java/janggi/domain/piece/SaTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import janggi.domain.Position; import janggi.domain.side.TeamType; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -17,10 +18,10 @@ void isValidMovePatternStraightOneStep() { // when & then assertAll( - () -> assertThat(sa.isValidMovePattern(4, 4, 4, 5)).isTrue(), - () -> assertThat(sa.isValidMovePattern(4, 4, 4, 3)).isTrue(), - () -> assertThat(sa.isValidMovePattern(4, 4, 5, 4)).isTrue(), - () -> assertThat(sa.isValidMovePattern(4, 4, 3, 4)).isTrue() + () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(4, 5))).isTrue(), + () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(4, 3))).isTrue(), + () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(5, 4))).isTrue(), + () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(3, 4))).isTrue() ); } @@ -32,10 +33,10 @@ void isValidMovePatternDiagonalOneStep() { // when & then assertAll( - () -> assertThat(sa.isValidMovePattern(4, 4, 5, 5)).isTrue(), - () -> assertThat(sa.isValidMovePattern(4, 4, 5, 3)).isTrue(), - () -> assertThat(sa.isValidMovePattern(4, 4, 3, 5)).isTrue(), - () -> assertThat(sa.isValidMovePattern(4, 4, 3, 3)).isTrue() + () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isTrue(), + () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(5, 3))).isTrue(), + () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(3, 5))).isTrue(), + () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(3, 3))).isTrue() ); } @@ -47,10 +48,14 @@ void cannotMoveInvalidPattern() { // when & then assertAll( - () -> assertThat(sa.isValidMovePattern(4, 4, 6, 4)).isFalse(), - () -> assertThat(sa.isValidMovePattern(4, 4, 6, 6)).isFalse(), - () -> assertThat(sa.isValidMovePattern(4, 4, 4, 6)).isFalse(), - () -> assertThat(sa.isValidMovePattern(4, 4, 4, 4)).isFalse() + () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(6, 4))).isFalse(), + () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(6, 6))).isFalse(), + () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(4, 6))).isFalse(), + () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse() ); } + + private Position createPosition(int x, int y) { + return new Position(x, y); + } } diff --git a/src/test/java/janggi/domain/piece/SangTest.java b/src/test/java/janggi/domain/piece/SangTest.java index b812c13286..65f22ea0c1 100644 --- a/src/test/java/janggi/domain/piece/SangTest.java +++ b/src/test/java/janggi/domain/piece/SangTest.java @@ -19,14 +19,14 @@ void isValidMovePattern() { // when & then assertAll( - () -> assertThat(sang.isValidMovePattern(4, 4, 7, 6)).isTrue(), - () -> assertThat(sang.isValidMovePattern(4, 4, 1, 6)).isTrue(), - () -> assertThat(sang.isValidMovePattern(4, 4, 7, 2)).isTrue(), - () -> assertThat(sang.isValidMovePattern(4, 4, 1, 2)).isTrue(), - () -> assertThat(sang.isValidMovePattern(4, 4, 6, 7)).isTrue(), - () -> assertThat(sang.isValidMovePattern(4, 4, 6, 1)).isTrue(), - () -> assertThat(sang.isValidMovePattern(4, 4, 2, 7)).isTrue(), - () -> assertThat(sang.isValidMovePattern(4, 4, 2, 1)).isTrue() + () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(7, 6))).isTrue(), + () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(1, 6))).isTrue(), + () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(7, 2))).isTrue(), + () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(1, 2))).isTrue(), + () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(6, 7))).isTrue(), + () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(6, 1))).isTrue(), + () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(2, 7))).isTrue(), + () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(2, 1))).isTrue() ); } @@ -38,9 +38,9 @@ void cannotMoveInvalidPattern() { // when & then assertAll( - () -> assertThat(sang.isValidMovePattern(4, 4, 4, 5)).isFalse(), - () -> assertThat(sang.isValidMovePattern(4, 4, 5, 5)).isFalse(), - () -> assertThat(sang.isValidMovePattern(4, 4, 4, 4)).isFalse() + () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(4, 5))).isFalse(), + () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isFalse(), + () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse() ); } @@ -71,4 +71,8 @@ void cannotMoveWhenIntermediatePositionIsBlocked() { // then assertThat(result).isFalse(); } + + private Position createPosition(int x, int y) { + return new Position(x, y); + } } From 4b66bface86a9cc7ad1e47e27b794b3cfa14de50 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Tue, 31 Mar 2026 15:20:18 +0900 Subject: [PATCH 67/99] =?UTF-8?q?refactor:=20=ED=98=B8=EC=B6=9C=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=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/domain/Board.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index f4fbedc1a5..f1e5f42969 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -99,7 +99,7 @@ private Team opponentTeam(TeamType nowTurn) { } private void validateEndPosition(Team team, Position endPosition) { - if (team.findPiece(endPosition).isPresent()) { + if (team.isPieceExist(endPosition)) { throw new IllegalArgumentException("같은 팀의 기물이 있는 위치로는 이동할 수 없습니다."); } } From 8b6c2734e5d880391540d607107ae7a22a99ffa4 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Tue, 31 Mar 2026 15:20:46 +0900 Subject: [PATCH 68/99] =?UTF-8?q?refactor:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/Piece.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index cd86a69d7d..f7dc431ccc 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -12,8 +12,6 @@ public interface Piece { Optional findMovePath(Position startPosition, Position endPosition); -// boolean isObstaclesNotExist(Position start, Position end, Board board); - String name(); PieceType getPieceType(); From 0a2f89be1c7d323ae11336dacdd14c7b2b115c3a Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Tue, 31 Mar 2026 15:43:08 +0900 Subject: [PATCH 69/99] =?UTF-8?q?chore:=20=EB=B3=80=EC=88=98=EB=AA=85,=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=EB=AA=85=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/Board.java | 54 +++++++++----------- src/main/java/janggi/domain/JanggiGame.java | 10 ++-- src/main/java/janggi/domain/Pieces.java | 8 +-- src/main/java/janggi/domain/Turn.java | 12 ++--- src/main/java/janggi/domain/piece/Gung.java | 10 ++-- src/main/java/janggi/domain/piece/Jol.java | 10 ++-- src/main/java/janggi/domain/piece/Ma.java | 10 ++-- src/main/java/janggi/domain/piece/Piece.java | 4 +- src/main/java/janggi/domain/piece/Po.java | 22 ++++---- src/main/java/janggi/domain/piece/Sa.java | 10 ++-- src/main/java/janggi/domain/piece/Sang.java | 16 +++--- src/main/java/janggi/domain/side/Chu.java | 4 +- src/main/java/janggi/domain/side/Han.java | 4 +- src/main/java/janggi/domain/side/Team.java | 2 +- src/test/java/janggi/domain/BoardTest.java | 22 ++++---- src/test/java/janggi/domain/TurnTest.java | 6 +-- 16 files changed, 100 insertions(+), 104 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index f1e5f42969..cda722a98c 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -57,21 +57,17 @@ public boolean hasPiece(Position position) { return findPiece(position).isPresent(); } - public void canMove(Position startPosition, Position endPosition, TeamType currentTeamType) { - validateRange(endPosition); - Piece piece = findTeamPiece(startPosition, currentTeam(currentTeamType)); - validateEndPosition(currentTeam(currentTeamType), endPosition); - validateCanMove(piece, startPosition, endPosition); + public void validateCanMove(Position start, Position end, TeamType currentTeamType) { + validateRange(end); + Piece piece = findTeamPiece(start, currentTeam(currentTeamType)); + validateEndPosition(currentTeam(currentTeamType), end); + validateCanMove(piece, start, end); } - public Board move( - Position startPosition, - Position endPosition, - TeamType nowTurn - ) { - Team movedCurrentTeam = currentTeam(nowTurn).move(startPosition, endPosition); - Team remainedOpponentTeam = removeOpponentPiece(nowTurn, endPosition); - return createMovedBoard(nowTurn, movedCurrentTeam, remainedOpponentTeam); + public Board move(Position start, Position end, TeamType currentTeamType) { + Team updatedCurrentTeam = currentTeam(currentTeamType).move(start, end); + Team updatedOpponentTeam = removeOpponentPiece(currentTeamType, end); + return createMovedBoard(currentTeamType, updatedCurrentTeam, updatedOpponentTeam); } public String getPieceName(Position position, TeamType currentTeamType) { @@ -84,43 +80,43 @@ private Piece findTeamPiece(Position position, Team currentTeam) { .orElseThrow(() -> new IllegalArgumentException("입력한 위치에 기물이 없습니다.")); } - private Team currentTeam(TeamType nowTurn) { - if (nowTurn == TeamType.CHU) { + private Team currentTeam(TeamType currentTeamType) { + if (currentTeamType == TeamType.CHU) { return chu; } return han; } - private Team opponentTeam(TeamType nowTurn) { - if (nowTurn == TeamType.CHU) { + private Team opponentTeam(TeamType currentTeamType) { + if (currentTeamType == TeamType.CHU) { return han; } return chu; } - private void validateEndPosition(Team team, Position endPosition) { - if (team.isPieceExist(endPosition)) { + private void validateEndPosition(Team team, Position end) { + if (team.isPieceExist(end)) { throw new IllegalArgumentException("같은 팀의 기물이 있는 위치로는 이동할 수 없습니다."); } } - private void validateCanMove(Piece piece, Position startPosition, Position endPosition) { - piece.validateCanMove(startPosition, endPosition, this); + private void validateCanMove(Piece piece, Position start, Position end) { + piece.validateCanMove(start, end, this); } - private Team removeOpponentPiece(TeamType nowTurn, Position targetPosition) { - Team opponentTeam = opponentTeam(nowTurn); - if (opponentTeam.findPiece(targetPosition).isEmpty()) { + private Team removeOpponentPiece(TeamType currentTeamType, Position end) { + Team opponentTeam = opponentTeam(currentTeamType); + if (opponentTeam.findPiece(end).isEmpty()) { return opponentTeam; } - return opponentTeam.remove(targetPosition); + return opponentTeam.remove(end); } - private Board createMovedBoard(TeamType nowTurn, Team movedCurrentTeam, Team remainedOpponentTeam) { - if (nowTurn == TeamType.CHU) { - return new Board(movedCurrentTeam, remainedOpponentTeam); + private Board createMovedBoard(TeamType currentTeamType, Team updatedCurrentTeam, Team updatedOpponentTeam) { + if (currentTeamType == TeamType.CHU) { + return new Board(updatedCurrentTeam, updatedOpponentTeam); } - return new Board(remainedOpponentTeam, movedCurrentTeam); + return new Board(updatedOpponentTeam, updatedCurrentTeam); } private void validateRange(Position inputPosition) { diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index 0a977a4dd6..e9bdfa27d5 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -29,12 +29,12 @@ public void validatePieceExist(Position position) { } } - public void validateValidEndPosition(Position startPosition, Position endPosition) { + public void validateValidEndPosition(Position start, Position end) { Turn lastTurn = getLastTurn(); - if (lastTurn.isCurrentTeamPieceExist(endPosition)) { + if (lastTurn.isCurrentTeamPieceExist(end)) { throw new IllegalArgumentException("아군이 존재하는 좌표로 이동할 수 없습니다."); } - lastTurn.canMove(startPosition, endPosition); + lastTurn.validateCanMove(start, end); } public Piece findPiece(Position position) { @@ -45,9 +45,9 @@ public String getPieceName(Position position) { return getLastTurn().getPieceName(position); } - public void doGame(Position startPosition, Position endPosition) { + public void doGame(Position start, Position end) { Turn lastTurn = getLastTurn(); - Turn newTurn = lastTurn.move(startPosition, endPosition); + Turn newTurn = lastTurn.move(start, end); turns.add(newTurn); } diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index 09ebd5694b..601dc5650c 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -16,11 +16,11 @@ private Pieces(Map value) { this.value = value; } - public Pieces move(Position piecePosition, Position targetPosition) { - Piece piece = value.get(piecePosition); + public Pieces move(Position start, Position end) { + Piece piece = value.get(start); Map updatedValue = new HashMap<>(value); - updatedValue.remove(piecePosition); - updatedValue.put(targetPosition, piece); + updatedValue.remove(start); + updatedValue.put(end, piece); return new Pieces(updatedValue); } diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index b8284292d8..ff65582d80 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -30,14 +30,14 @@ public String getPieceName(Position position) { return board.getPieceName(position, opponentTeamType()); } - public Turn move(Position startPosition, Position endPosition) { - TeamType nowTurn = opponentTeamType(); - Board movedBoard = board.move(startPosition, endPosition, nowTurn); - return new Turn(nowTurn, movedBoard); + public Turn move(Position start, Position end) { + TeamType currentTeamType = opponentTeamType(); + Board movedBoard = board.move(start, end, currentTeamType); + return new Turn(currentTeamType, movedBoard); } - public void canMove(Position startPosition, Position endPosition) { - board.canMove(startPosition, endPosition, opponentTeamType()); + public void validateCanMove(Position start, Position end) { + board.validateCanMove(start, end, opponentTeamType()); } public String nextTurnTeam() { diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index 5fddb51b49..4aca414fa4 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -37,14 +37,14 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position startPosition, Position endPosition) { - return findMovePath(startPosition, endPosition).isPresent(); + public boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); } @Override - public Optional findMovePath(Position startPosition, Position endPosition) { - int dx = endPosition.getX() - startPosition.getX(); - int dy = endPosition.getY() - startPosition.getY(); + public Optional findMovePath(Position start, Position end) { + int dx = end.getX() - start.getX(); + int dy = end.getY() - start.getY(); int distanceX = Math.abs(dx); int distanceY = Math.abs(dy); if (isSamePosition(distanceX, distanceY)) { diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 7e9a7bc672..2c2f99b84f 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -28,14 +28,14 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position startPosition, Position endPosition) { - return findMovePath(startPosition, endPosition).isPresent(); + public boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); } @Override - public Optional findMovePath(Position startPosition, Position endPosition) { - int dx = endPosition.getX() - startPosition.getX(); - int dy = endPosition.getY() - startPosition.getY(); + public Optional findMovePath(Position start, Position end) { + int dx = end.getX() - start.getX(); + int dy = end.getY() - start.getY(); if (isSamePosition(dx, dy)) { return Optional.empty(); } diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index fd462531d4..aab004c79e 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -41,14 +41,14 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position startPosition, Position endPosition) { - return findMovePath(startPosition, endPosition).isPresent(); + public boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); } @Override - public Optional findMovePath(Position startPosition, Position endPosition) { - int dx = endPosition.getX() - startPosition.getX(); - int dy = endPosition.getY() - startPosition.getY(); + public Optional findMovePath(Position start, Position end) { + int dx = end.getX() - start.getX(); + int dy = end.getY() - start.getY(); return paths.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index f7dc431ccc..97d1881208 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -8,9 +8,9 @@ public interface Piece { - boolean isValidMovePattern(Position startPosition, Position endPosition); + boolean isValidMovePattern(Position start, Position end); - Optional findMovePath(Position startPosition, Position endPosition); + Optional findMovePath(Position start, Position end); String name(); diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index 564297227b..d8a4f8b7de 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -35,16 +35,16 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position startPosition, Position endPosition) { - return findMovePath(startPosition, endPosition).isPresent(); + public boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); } @Override - public Optional findMovePath(Position startPosition, Position endPosition) { - int startX = startPosition.getX(); - int startY = startPosition.getY(); - int endX = endPosition.getX(); - int endY = endPosition.getY(); + public Optional findMovePath(Position start, Position end) { + int startX = start.getX(); + int startY = start.getY(); + int endX = end.getX(); + int endY = end.getY(); if (startX == endX && startY == endY) { return Optional.empty(); @@ -56,12 +56,12 @@ public Optional findMovePath(Position startPosition, Position endPosit .findFirst(); } - public boolean isObstaclesNotExist(Position startPosition, Position endPosition, Board board) { - Optional movePath = findMovePath(startPosition, endPosition); + public boolean isObstaclesNotExist(Position start, Position end, Board board) { + Optional movePath = findMovePath(start, end); if (movePath.isEmpty()) { return false; } - List intermediatePositions = movePath.get().intermediatePositions(startPosition, endPosition); + List intermediatePositions = movePath.get().intermediatePositions(start, end); List obstacles = intermediatePositions.stream() .map(board::findPiece) .filter(Optional::isPresent) @@ -73,7 +73,7 @@ public boolean isObstaclesNotExist(Position startPosition, Position endPosition, if (obstacles.getFirst().getPieceType() == PieceType.PO) { return false; } - Optional targetPiece = board.findPiece(endPosition); + Optional targetPiece = board.findPiece(end); return targetPiece.isEmpty() || !(targetPiece.get().getPieceType() == PieceType.PO); } diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index 88cca0181e..e942952fef 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -37,14 +37,14 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position startPosition, Position endPosition) { - return findMovePath(startPosition, endPosition).isPresent(); + public boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); } @Override - public Optional findMovePath(Position startPosition, Position endPosition) { - int dx = endPosition.getX() - startPosition.getX(); - int dy = endPosition.getY() - startPosition.getY(); + public Optional findMovePath(Position start, Position end) { + int dx = end.getX() - start.getX(); + int dy = end.getY() - start.getY(); int distanceX = Math.abs(dx); int distanceY = Math.abs(dy); if (isSamePosition(distanceX, distanceY)) { diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index 61562dfaf4..bc658341f2 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -41,25 +41,25 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position startPosition, Position endPosition) { - return findMovePath(startPosition, endPosition).isPresent(); + public boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); } @Override - public Optional findMovePath(Position startPosition, Position endPosition) { - int dx = endPosition.getX() - startPosition.getX(); - int dy = endPosition.getY() - startPosition.getY(); + public Optional findMovePath(Position start, Position end) { + int dx = end.getX() - start.getX(); + int dy = end.getY() - start.getY(); return paths.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); } - public boolean isObstaclesNotExist(Position startPosition, Position endPosition, Board board) { - Optional movePath = findMovePath(startPosition, endPosition); + public boolean isObstaclesNotExist(Position start, Position end, Board board) { + Optional movePath = findMovePath(start, end); if (movePath.isEmpty()) { return false; } - return movePath.get().intermediatePositions(startPosition, endPosition).stream() + return movePath.get().intermediatePositions(start, end).stream() .noneMatch(board::hasPiece); } diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index 9891d55b03..68c83b811d 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -41,7 +41,7 @@ public Team remove(Position position) { } @Override - public Team move(Position piecePosition, Position targetPosition) { - return new Chu(pieces.move(piecePosition, targetPosition)); + public Team move(Position start, Position end) { + return new Chu(pieces.move(start, end)); } } diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index ab5b7ef6f7..60c95403db 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -42,7 +42,7 @@ public Team remove(Position position) { } @Override - public Team move(Position piecePosition, Position targetPosition) { - return new Han(pieces.move(piecePosition, targetPosition)); + public Team move(Position start, Position end) { + return new Han(pieces.move(start, end)); } } diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index 70fee91b80..b894445205 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -18,5 +18,5 @@ public interface Team { Team remove(Position position); - Team move(Position piecePosition, Position targetPosition); + Team move(Position start, Position end); } diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java index 45da169cb7..420d3e524d 100644 --- a/src/test/java/janggi/domain/BoardTest.java +++ b/src/test/java/janggi/domain/BoardTest.java @@ -34,7 +34,7 @@ void cannotMoveWhenEndPositionIsOutOfRange() { Position outOfBound = new Position(1, 11); // when & then - assertThatThrownBy(() -> board.canMove(new Position(1, 4), outOfBound, TeamType.CHU)) + assertThatThrownBy(() -> board.validateCanMove(new Position(1, 4), outOfBound, TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("입력한 좌표가 장기판 범위 밖입니다."); } @@ -47,7 +47,7 @@ void cannotMoveWhenStartPositionHasNoCurrentTeamPiece() { Position notExistPosition = new Position(2, 2); // when & then - assertThatThrownBy(() -> board.canMove(notExistPosition, new Position(1, 1), TeamType.CHU)) + assertThatThrownBy(() -> board.validateCanMove(notExistPosition, new Position(1, 1), TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("입력한 위치에 기물이 없습니다."); } @@ -60,7 +60,7 @@ void cannotStartAtOpponentTeamPosition() { Position opponentTeamPosition = new Position(1, 7); // when & then - assertThatThrownBy(() -> board.canMove(opponentTeamPosition, new Position(1, 6), TeamType.CHU)) + assertThatThrownBy(() -> board.validateCanMove(opponentTeamPosition, new Position(1, 6), TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("입력한 위치에 기물이 없습니다."); } @@ -73,7 +73,7 @@ void cannotMoveToSameTeamPiecePosition() { Position currentTeamPosition = new Position(2, 1); // when & then - assertThatThrownBy(() -> board.canMove(new Position(1, 1), currentTeamPosition, TeamType.CHU)) + assertThatThrownBy(() -> board.validateCanMove(new Position(1, 1), currentTeamPosition, TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("같은 팀의 기물이 있는 위치로는 이동할 수 없습니다."); } @@ -87,7 +87,7 @@ void cannotMoveWhenMovePatternIsInvalid() { Position saEndPosition = new Position(4, 3); // when & then - assertThatThrownBy(() -> board.canMove(saStartPosition, saEndPosition, TeamType.CHU)) + assertThatThrownBy(() -> board.validateCanMove(saStartPosition, saEndPosition, TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."); } @@ -101,7 +101,7 @@ void cannotMoveChaWhenPathIsBlocked() { Position chaEndPosition = new Position(1, 5); // when & then - assertThatThrownBy(() -> board.canMove(chaStartPosition, chaEndPosition, TeamType.CHU)) + assertThatThrownBy(() -> board.validateCanMove(chaStartPosition, chaEndPosition, TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); } @@ -115,7 +115,7 @@ void cannotMoveMaWhenLegIsBlocked() { Position maEndPosition = new Position(4, 2); // when & then - assertThatThrownBy(() -> board.canMove(maStartPosition, maEndPosition, TeamType.CHU)) + assertThatThrownBy(() -> board.validateCanMove(maStartPosition, maEndPosition, TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); } @@ -129,14 +129,14 @@ void cannotMovePoWithoutBridge() { Position poEndPosition = new Position(2, 6); // when & then - assertThatThrownBy(() -> board.canMove(poStartPosition, poEndPosition, TeamType.CHU)) + assertThatThrownBy(() -> board.validateCanMove(poStartPosition, poEndPosition, TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동 경로에 기물이 존재하지 않아 이동할 수 없습니다."); } @Test @DisplayName("이동 패턴과 장애물 조건을 모두 만족하면 이동할 수 있다.") - void canMoveWhenPatternAndObstacleRulesAreSatisfied() { + void validateCanMoveWhenPatternAndObstacleRulesAreSatisfied() { // given Board board = Board.createInitialBoard(); Position jolStartPosition = new Position(1, 4); @@ -149,9 +149,9 @@ void canMoveWhenPatternAndObstacleRulesAreSatisfied() { // when & then assertAll( - () -> assertThatCode(() -> movedBoard.canMove(chaStartPosition, chaEndPosition, TeamType.CHU)) + () -> assertThatCode(() -> movedBoard.validateCanMove(chaStartPosition, chaEndPosition, TeamType.CHU)) .doesNotThrowAnyException(), - () -> assertThatCode(() -> movedBoard.canMove(poStartPosition, poEndPosition, TeamType.CHU)) + () -> assertThatCode(() -> movedBoard.validateCanMove(poStartPosition, poEndPosition, TeamType.CHU)) .doesNotThrowAnyException() ); } diff --git a/src/test/java/janggi/domain/TurnTest.java b/src/test/java/janggi/domain/TurnTest.java index 715bfb3824..3c1f49916c 100644 --- a/src/test/java/janggi/domain/TurnTest.java +++ b/src/test/java/janggi/domain/TurnTest.java @@ -57,14 +57,14 @@ void findPiece() { @Test @DisplayName("현재 차례 팀의 올바른 이동은 허용한다.") - void canMove() { + void validateCanMove() { // given Turn turn = Turn.createInitialTurn(); Position chuJolStartPosition = new Position(1, 4); Position chuJolEndPosition = new Position(1, 5); // when & then - assertThatCode(() -> turn.canMove(chuJolStartPosition, chuJolEndPosition)) + assertThatCode(() -> turn.validateCanMove(chuJolStartPosition, chuJolEndPosition)) .doesNotThrowAnyException(); } @@ -77,7 +77,7 @@ void throwWhenMoveIsInvalid() { Position chuJolEndPosition = new Position(1, 6); // when & then - assertThatThrownBy(() -> turn.canMove(chuJolStartPosition, chuJolEndPosition)) + assertThatThrownBy(() -> turn.validateCanMove(chuJolStartPosition, chuJolEndPosition)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."); } From dbb4aae38891aae8824968ef9fd83e4a2c51afd5 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Tue, 31 Mar 2026 15:45:31 +0900 Subject: [PATCH 70/99] =?UTF-8?q?chore:=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=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/domain/Board.java | 2 +- src/test/java/janggi/domain/BoardTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index cda722a98c..52d4c91395 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -96,7 +96,7 @@ private Team opponentTeam(TeamType currentTeamType) { private void validateEndPosition(Team team, Position end) { if (team.isPieceExist(end)) { - throw new IllegalArgumentException("같은 팀의 기물이 있는 위치로는 이동할 수 없습니다."); + throw new IllegalArgumentException("아군이 존재하는 좌표로는 이동할 수 없습니다."); } } diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java index 420d3e524d..8c40a2ac9d 100644 --- a/src/test/java/janggi/domain/BoardTest.java +++ b/src/test/java/janggi/domain/BoardTest.java @@ -75,7 +75,7 @@ void cannotMoveToSameTeamPiecePosition() { // when & then assertThatThrownBy(() -> board.validateCanMove(new Position(1, 1), currentTeamPosition, TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("같은 팀의 기물이 있는 위치로는 이동할 수 없습니다."); + .hasMessage("아군이 존재하는 좌표로는 이동할 수 없습니다."); } @Test From ae37c36297111d76887239b55038491ceee284d6 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Tue, 31 Mar 2026 15:53:34 +0900 Subject: [PATCH 71/99] =?UTF-8?q?chore:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=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 | 4 ++-- src/main/java/janggi/domain/Turn.java | 2 +- src/test/java/janggi/domain/TurnTest.java | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index e9bdfa27d5..1326251973 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -24,14 +24,14 @@ public BoardSpots makeCurrentTurnBoardSnapShot() { public void validatePieceExist(Position position) { Turn lastTurn = getLastTurn(); - if (!lastTurn.isCurrentTeamPieceExist(position)) { + if (!lastTurn.isNextTurnTeamPieceExists(position)) { throw new IllegalArgumentException("기물이 존재하는 좌표가 아닙니다."); } } public void validateValidEndPosition(Position start, Position end) { Turn lastTurn = getLastTurn(); - if (lastTurn.isCurrentTeamPieceExist(end)) { + if (lastTurn.isNextTurnTeamPieceExists(end)) { throw new IllegalArgumentException("아군이 존재하는 좌표로 이동할 수 없습니다."); } lastTurn.validateCanMove(start, end); diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index ff65582d80..f0922bbc28 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -18,7 +18,7 @@ public static Turn createInitialTurn() { return new Turn(TeamType.HAN, Board.createInitialBoard()); } - public boolean isCurrentTeamPieceExist(Position position) { + public boolean isNextTurnTeamPieceExists(Position position) { return board.isPieceExist(position, opponentTeamType()); } diff --git a/src/test/java/janggi/domain/TurnTest.java b/src/test/java/janggi/domain/TurnTest.java index 3c1f49916c..a534735fbe 100644 --- a/src/test/java/janggi/domain/TurnTest.java +++ b/src/test/java/janggi/domain/TurnTest.java @@ -28,7 +28,7 @@ void createInitialTurn() { @Test @DisplayName("현재 차례 팀의 기물 존재 여부를 확인한다.") - void isCurrentTeamPieceExist() { + void isNextTurnTeamPieceExists() { // given Turn turn = Turn.createInitialTurn(); Position chuJolPosition = new Position(1, 4); @@ -36,8 +36,8 @@ void isCurrentTeamPieceExist() { // when & then assertAll( - () -> assertThat(turn.isCurrentTeamPieceExist(chuJolPosition)).isTrue(), - () -> assertThat(turn.isCurrentTeamPieceExist(hanJolPosition)).isFalse() + () -> assertThat(turn.isNextTurnTeamPieceExists(chuJolPosition)).isTrue(), + () -> assertThat(turn.isNextTurnTeamPieceExists(hanJolPosition)).isFalse() ); } From 0026eb7acc1740ddfbcf9281e91b362f857050ec Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Tue, 31 Mar 2026 15:55:48 +0900 Subject: [PATCH 72/99] =?UTF-8?q?refactor:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/JanggiRunner.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index d8b82358cb..440adda4cc 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -38,7 +38,6 @@ private Position readValidStartPosition() { } private Position readValidEndPosition(Position startPosition) { - OutputView.printAskMovePosition(janggiGame.findPiece(startPosition).name()); OutputView.printAskMovePosition(janggiGame.getPieceName(startPosition)); String rawMovePosition = InputView.readLine(); List parsedMovePosition = DelimiterParser.parse(rawMovePosition); From 45903381e6377b88a9a18e20a8b7c9ad9e6059ff Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Tue, 31 Mar 2026 15:59:26 +0900 Subject: [PATCH 73/99] =?UTF-8?q?refactor:=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/JanggiRunner.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index 440adda4cc..2d9dc4a0c3 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -30,19 +30,21 @@ public void execute() { private Position readValidStartPosition() { OutputView.printTurnNotice(janggiGame.getCurrentTurnTeamName()); OutputView.printAskPiecePosition(); - String rawPiecePosition = InputView.readLine(); - List parsedPiecePosition = DelimiterParser.parse(rawPiecePosition); - Position startPosition = Position.makePosition(parsedPiecePosition); + Position startPosition = createPosition(); janggiGame.validatePieceExist(startPosition); return startPosition; } private Position readValidEndPosition(Position startPosition) { OutputView.printAskMovePosition(janggiGame.getPieceName(startPosition)); - String rawMovePosition = InputView.readLine(); - List parsedMovePosition = DelimiterParser.parse(rawMovePosition); - Position endPosition = Position.makePosition(parsedMovePosition); + Position endPosition = createPosition(); janggiGame.validateValidEndPosition(startPosition, endPosition); return endPosition; } + + private static Position createPosition() { + String rawPiecePosition = InputView.readLine(); + List parsedPiecePosition = DelimiterParser.parse(rawPiecePosition); + return Position.makePosition(parsedPiecePosition); + } } From 544c68fa53ae7c6a15eba20ab5fd93adccee9439 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Tue, 31 Mar 2026 16:44:42 +0900 Subject: [PATCH 74/99] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=88=9C=EC=84=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/JanggiRunner.java | 2 +- src/main/java/janggi/domain/Board.java | 79 +++++++++------------ src/main/java/janggi/domain/JanggiGame.java | 29 +++----- src/main/java/janggi/domain/Pieces.java | 31 ++++---- src/main/java/janggi/domain/Turn.java | 31 ++++---- src/main/java/janggi/domain/side/Chu.java | 12 ++-- src/main/java/janggi/domain/side/Han.java | 13 ++-- src/main/java/janggi/domain/side/Team.java | 7 +- src/test/java/janggi/domain/TurnTest.java | 24 +------ 9 files changed, 93 insertions(+), 135 deletions(-) diff --git a/src/main/java/janggi/JanggiRunner.java b/src/main/java/janggi/JanggiRunner.java index 2d9dc4a0c3..333c0ed083 100644 --- a/src/main/java/janggi/JanggiRunner.java +++ b/src/main/java/janggi/JanggiRunner.java @@ -31,7 +31,7 @@ private Position readValidStartPosition() { OutputView.printTurnNotice(janggiGame.getCurrentTurnTeamName()); OutputView.printAskPiecePosition(); Position startPosition = createPosition(); - janggiGame.validatePieceExist(startPosition); + janggiGame.validatePieceExists(startPosition); return startPosition; } diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index 52d4c91395..f47db672cd 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -34,15 +34,22 @@ public BoardSpots makeSnapShot() { return new BoardSpots(boardSpots); } - public boolean isPieceExist(Position position, TeamType currentTeamType) { + public boolean isPieceExists(Position position, TeamType currentTeamType) { validateRange(position); Team currentTeam = currentTeam(currentTeamType); - return currentTeam.isPieceExist(position); + return currentTeam.isPieceExists(position); } - public Piece findNextTurnTeamPiece(Position position, TeamType currentTeamType) { - Team currentTeam = currentTeam(currentTeamType); - return findTeamPiece(position, currentTeam); + public String getPieceName(Position position, TeamType currentTeamType) { + Piece piece = findTeamPiece(position, currentTeam(currentTeamType)); + return piece.name(); + } + + public void validateCanMove(Position start, Position end, TeamType currentTeamType) { + validateRange(end); + Piece piece = findTeamPiece(start, currentTeam(currentTeamType)); + validateEndPosition(currentTeam(currentTeamType), end); + piece.validateCanMove(start, end, this); } public Optional findPiece(Position position) { @@ -57,27 +64,26 @@ public boolean hasPiece(Position position) { return findPiece(position).isPresent(); } - public void validateCanMove(Position start, Position end, TeamType currentTeamType) { - validateRange(end); - Piece piece = findTeamPiece(start, currentTeam(currentTeamType)); - validateEndPosition(currentTeam(currentTeamType), end); - validateCanMove(piece, start, end); - } - public Board move(Position start, Position end, TeamType currentTeamType) { Team updatedCurrentTeam = currentTeam(currentTeamType).move(start, end); Team updatedOpponentTeam = removeOpponentPiece(currentTeamType, end); return createMovedBoard(currentTeamType, updatedCurrentTeam, updatedOpponentTeam); } - public String getPieceName(Position position, TeamType currentTeamType) { - Piece piece = findTeamPiece(position, currentTeam(currentTeamType)); - return piece.name(); + private void validateRange(Position inputPosition) { + int x = inputPosition.getX(); + int y = inputPosition.getY(); + if (isNotInRange(FIRST_INDEX, LAST_X_INDEX, x) || isNotInRange(FIRST_INDEX, LAST_Y_INDEX, y)) { + throw new IllegalArgumentException("입력한 좌표가 장기판 범위 밖입니다."); + } } - private Piece findTeamPiece(Position position, Team currentTeam) { - return currentTeam.findPiece(position) - .orElseThrow(() -> new IllegalArgumentException("입력한 위치에 기물이 없습니다.")); + private boolean isNotInRange(int start, int last, int index) { + return !isInRange(start, last, index); + } + + private boolean isInRange(int start, int last, int index) { + return index >= start && index <= last; } private Team currentTeam(TeamType currentTeamType) { @@ -87,23 +93,17 @@ private Team currentTeam(TeamType currentTeamType) { return han; } - private Team opponentTeam(TeamType currentTeamType) { - if (currentTeamType == TeamType.CHU) { - return han; - } - return chu; + private Piece findTeamPiece(Position position, Team currentTeam) { + return currentTeam.findPiece(position) + .orElseThrow(() -> new IllegalArgumentException("입력한 위치에 기물이 없습니다.")); } private void validateEndPosition(Team team, Position end) { - if (team.isPieceExist(end)) { + if (team.isPieceExists(end)) { throw new IllegalArgumentException("아군이 존재하는 좌표로는 이동할 수 없습니다."); } } - private void validateCanMove(Piece piece, Position start, Position end) { - piece.validateCanMove(start, end, this); - } - private Team removeOpponentPiece(TeamType currentTeamType, Position end) { Team opponentTeam = opponentTeam(currentTeamType); if (opponentTeam.findPiece(end).isEmpty()) { @@ -112,26 +112,17 @@ private Team removeOpponentPiece(TeamType currentTeamType, Position end) { return opponentTeam.remove(end); } - private Board createMovedBoard(TeamType currentTeamType, Team updatedCurrentTeam, Team updatedOpponentTeam) { + private Team opponentTeam(TeamType currentTeamType) { if (currentTeamType == TeamType.CHU) { - return new Board(updatedCurrentTeam, updatedOpponentTeam); + return han; } - return new Board(updatedOpponentTeam, updatedCurrentTeam); + return chu; } - private void validateRange(Position inputPosition) { - int x = inputPosition.getX(); - int y = inputPosition.getY(); - if (isNotInRange(FIRST_INDEX, LAST_X_INDEX, x) || isNotInRange(FIRST_INDEX, LAST_Y_INDEX, y)) { - throw new IllegalArgumentException("입력한 좌표가 장기판 범위 밖입니다."); + private Board createMovedBoard(TeamType currentTeamType, Team updatedCurrentTeam, Team updatedOpponentTeam) { + if (currentTeamType == TeamType.CHU) { + return new Board(updatedCurrentTeam, updatedOpponentTeam); } - } - - private boolean isNotInRange(int start, int last, int index) { - return !isInRange(start, last, index); - } - - private boolean isInRange(int start, int last, int index) { - return index >= start && index <= last; + return new Board(updatedOpponentTeam, updatedCurrentTeam); } } diff --git a/src/main/java/janggi/domain/JanggiGame.java b/src/main/java/janggi/domain/JanggiGame.java index 1326251973..41cf45b190 100644 --- a/src/main/java/janggi/domain/JanggiGame.java +++ b/src/main/java/janggi/domain/JanggiGame.java @@ -1,7 +1,7 @@ package janggi.domain; -import janggi.domain.piece.Piece; import janggi.dto.BoardSpots; + import java.util.ArrayList; import java.util.List; @@ -22,38 +22,31 @@ public BoardSpots makeCurrentTurnBoardSnapShot() { return lastTurn.makeBoardSnapShot(); } - public void validatePieceExist(Position position) { + public String getCurrentTurnTeamName() { Turn lastTurn = getLastTurn(); - if (!lastTurn.isNextTurnTeamPieceExists(position)) { - throw new IllegalArgumentException("기물이 존재하는 좌표가 아닙니다."); - } + return lastTurn.nextTurnTeam(); } - public void validateValidEndPosition(Position start, Position end) { + public void validatePieceExists(Position position) { Turn lastTurn = getLastTurn(); - if (lastTurn.isNextTurnTeamPieceExists(end)) { - throw new IllegalArgumentException("아군이 존재하는 좌표로 이동할 수 없습니다."); + if (!lastTurn.isNextTurnTeamPieceExists(position)) { + throw new IllegalArgumentException("기물이 존재하는 좌표가 아닙니다."); } - lastTurn.validateCanMove(start, end); - } - - public Piece findPiece(Position position) { - return getLastTurn().findPiece(position); } public String getPieceName(Position position) { return getLastTurn().getPieceName(position); } - public void doGame(Position start, Position end) { + public void validateValidEndPosition(Position start, Position end) { Turn lastTurn = getLastTurn(); - Turn newTurn = lastTurn.move(start, end); - turns.add(newTurn); + lastTurn.validateCanMove(start, end); } - public String getCurrentTurnTeamName() { + public void doGame(Position start, Position end) { Turn lastTurn = getLastTurn(); - return lastTurn.nextTurnTeam(); + Turn newTurn = lastTurn.move(start, end); + turns.add(newTurn); } private Turn getLastTurn() { diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index 601dc5650c..43c8024b22 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -16,20 +16,6 @@ private Pieces(Map value) { this.value = value; } - public Pieces move(Position start, Position end) { - Piece piece = value.get(start); - Map updatedValue = new HashMap<>(value); - updatedValue.remove(start); - updatedValue.put(end, piece); - return new Pieces(updatedValue); - } - - public Pieces remove(Position position) { - Map updatedValue = new HashMap<>(value); - updatedValue.remove(position); - return new Pieces(updatedValue); - } - public static Pieces createHan() { Map pieces = new HashMap<>(); createChas(pieces, 10, TeamType.HAN); @@ -54,7 +40,6 @@ public static Pieces createChu() { return new Pieces(pieces); } - public Map makeSnapShot() { Map snapShot = new HashMap<>(); for (Map.Entry entry : value.entrySet()) { @@ -65,7 +50,7 @@ public Map makeSnapShot() { return snapShot; } - public boolean isPieceExist(Position position) { + public boolean isPieceExists(Position position) { return value.containsKey(position); } @@ -73,6 +58,20 @@ public Optional findPiece(Position position) { return Optional.ofNullable(value.get(position)); } + public Pieces move(Position start, Position end) { + Piece piece = value.get(start); + Map updatedValue = new HashMap<>(value); + updatedValue.remove(start); + updatedValue.put(end, piece); + return new Pieces(updatedValue); + } + + public Pieces remove(Position position) { + Map updatedValue = new HashMap<>(value); + updatedValue.remove(position); + return new Pieces(updatedValue); + } + private static void createChas(Map pieces, int indexY, TeamType teamType) { pieces.put(new Position(1, indexY), new Cha(teamType)); pieces.put(new Position(9, indexY), new Cha(teamType)); diff --git a/src/main/java/janggi/domain/Turn.java b/src/main/java/janggi/domain/Turn.java index f0922bbc28..36c4a13f32 100644 --- a/src/main/java/janggi/domain/Turn.java +++ b/src/main/java/janggi/domain/Turn.java @@ -1,6 +1,5 @@ package janggi.domain; -import janggi.domain.piece.Piece; import janggi.domain.side.TeamType; import janggi.dto.BoardSpots; @@ -18,35 +17,31 @@ public static Turn createInitialTurn() { return new Turn(TeamType.HAN, Board.createInitialBoard()); } - public boolean isNextTurnTeamPieceExists(Position position) { - return board.isPieceExist(position, opponentTeamType()); + public BoardSpots makeBoardSnapShot() { + return board.makeSnapShot(); } - public Piece findPiece(Position position) { - return board.findNextTurnTeamPiece(position, opponentTeamType()); + public String nextTurnTeam() { + TeamType teamType = opponentTeamType(); + return teamType.getName(); } - public String getPieceName(Position position) { - return board.getPieceName(position, opponentTeamType()); + public boolean isNextTurnTeamPieceExists(Position position) { + return board.isPieceExists(position, opponentTeamType()); } - public Turn move(Position start, Position end) { - TeamType currentTeamType = opponentTeamType(); - Board movedBoard = board.move(start, end, currentTeamType); - return new Turn(currentTeamType, movedBoard); + public String getPieceName(Position position) { + return board.getPieceName(position, opponentTeamType()); } public void validateCanMove(Position start, Position end) { board.validateCanMove(start, end, opponentTeamType()); } - public String nextTurnTeam() { - TeamType teamType = opponentTeamType(); - return teamType.getName(); - } - - public BoardSpots makeBoardSnapShot() { - return board.makeSnapShot(); + public Turn move(Position start, Position end) { + TeamType currentTeamType = opponentTeamType(); + Board movedBoard = board.move(start, end, currentTeamType); + return new Turn(currentTeamType, movedBoard); } private TeamType opponentTeamType() { diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index 68c83b811d..4395a6ab95 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -26,8 +26,8 @@ public Map makeSnapShot() { } @Override - public boolean isPieceExist(Position position) { - return pieces.isPieceExist(position); + public boolean isPieceExists(Position position) { + return pieces.isPieceExists(position); } @Override @@ -36,12 +36,12 @@ public Optional findPiece(Position position) { } @Override - public Team remove(Position position) { - return new Chu(pieces.remove(position)); + public Team move(Position start, Position end) { + return new Chu(pieces.move(start, end)); } @Override - public Team move(Position start, Position end) { - return new Chu(pieces.move(start, end)); + public Team remove(Position position) { + return new Chu(pieces.remove(position)); } } diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index 60c95403db..b84ffca3f6 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -4,7 +4,6 @@ import janggi.domain.Position; import janggi.domain.piece.Piece; import janggi.dto.BoardSpot; -import janggi.dto.BoardSpots; import java.util.Map; import java.util.Optional; @@ -27,8 +26,8 @@ public Map makeSnapShot() { } @Override - public boolean isPieceExist(Position position) { - return pieces.isPieceExist(position); + public boolean isPieceExists(Position position) { + return pieces.isPieceExists(position); } @Override @@ -37,12 +36,12 @@ public Optional findPiece(Position position) { } @Override - public Team remove(Position position) { - return new Han(pieces.remove(position)); + public Team move(Position start, Position end) { + return new Han(pieces.move(start, end)); } @Override - public Team move(Position start, Position end) { - return new Han(pieces.move(start, end)); + public Team remove(Position position) { + return new Han(pieces.remove(position)); } } diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index b894445205..6af551bb49 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -3,7 +3,6 @@ import janggi.domain.Position; import janggi.domain.piece.Piece; import janggi.dto.BoardSpot; -import janggi.dto.BoardSpots; import java.util.Map; import java.util.Optional; @@ -12,11 +11,11 @@ public interface Team { Map makeSnapShot(); - boolean isPieceExist(Position position); + boolean isPieceExists(Position position); Optional findPiece(Position position); - Team remove(Position position); - Team move(Position start, Position end); + + Team remove(Position position); } diff --git a/src/test/java/janggi/domain/TurnTest.java b/src/test/java/janggi/domain/TurnTest.java index a534735fbe..2fd83c37d4 100644 --- a/src/test/java/janggi/domain/TurnTest.java +++ b/src/test/java/janggi/domain/TurnTest.java @@ -1,16 +1,12 @@ package janggi.domain; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - -import janggi.domain.piece.Jol; -import janggi.domain.piece.Piece; import janggi.dto.BoardSpot; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertAll; + class TurnTest { @Test @@ -41,20 +37,6 @@ void isNextTurnTeamPieceExists() { ); } - @Test - @DisplayName("현재 차례 팀의 기물을 조회한다.") - void findPiece() { - // given - Turn turn = Turn.createInitialTurn(); - Position chuJolPosition = new Position(1, 4); - - // when - Piece piece = turn.findPiece(chuJolPosition); - - // then - assertThat(piece).isInstanceOf(Jol.class); - } - @Test @DisplayName("현재 차례 팀의 올바른 이동은 허용한다.") void validateCanMove() { From baa91d42c5da9b78dd99f827b53bb02e4dd96462 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Tue, 31 Mar 2026 20:11:21 +0900 Subject: [PATCH 75/99] =?UTF-8?q?refactor:=20Piece=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=EC=97=90=EC=84=9C=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=8F=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/main/java/janggi/domain/piece/Cha.java | 36 +++--- src/main/java/janggi/domain/piece/Gung.java | 34 +++-- src/main/java/janggi/domain/piece/Jol.java | 34 +++-- src/main/java/janggi/domain/piece/Ma.java | 36 +++--- src/main/java/janggi/domain/piece/Piece.java | 8 +- src/main/java/janggi/domain/piece/Po.java | 57 +++------ src/main/java/janggi/domain/piece/Sa.java | 34 +++-- src/main/java/janggi/domain/piece/Sang.java | 36 +++--- .../java/janggi/domain/piece/ChaTest.java | 113 ++++++++--------- .../java/janggi/domain/piece/GungTest.java | 95 ++++++++------ .../java/janggi/domain/piece/JolTest.java | 119 +++++++++--------- src/test/java/janggi/domain/piece/MaTest.java | 111 ++++++++-------- src/test/java/janggi/domain/piece/PoTest.java | 111 ++++++++-------- src/test/java/janggi/domain/piece/SaTest.java | 81 ++++++------ .../java/janggi/domain/piece/SangTest.java | 102 +++++++-------- 15 files changed, 503 insertions(+), 504 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 6e81236465..1e781270a2 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -37,12 +37,25 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); + public String name() { + return pieceType.getName(); + } + + @Override + public PieceType getPieceType() { + return pieceType; } @Override - public Optional findMovePath(Position start, Position end) { + public TeamType getTeamType() { + return teamType; + } + + private boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); + } + + private Optional findMovePath(Position start, Position end) { int startX = start.getX(); int startY = start.getY(); int endX = end.getX(); @@ -61,7 +74,7 @@ public Optional findMovePath(Position start, Position end) { .findFirst(); } - public boolean isObstaclesNotExist(Position start, Position end, Board board) { + private boolean isObstaclesNotExist(Position start, Position end, Board board) { Optional movePath = findMovePath(start, end); if (movePath.isEmpty()) { return false; @@ -77,19 +90,4 @@ private boolean isSamePosition(int startX, int startY, int endX, int endY) { private boolean isStraightDirection(int startX, int startY, int endX, int endY) { return startX == endX || startY == endY; } - - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } } diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index 4aca414fa4..62c7c53ffa 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -37,12 +37,25 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); + public String name() { + return pieceType.getName(); + } + + @Override + public PieceType getPieceType() { + return pieceType; } @Override - public Optional findMovePath(Position start, Position end) { + public TeamType getTeamType() { + return teamType; + } + + private boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); + } + + private Optional findMovePath(Position start, Position end) { int dx = end.getX() - start.getX(); int dy = end.getY() - start.getY(); int distanceX = Math.abs(dx); @@ -65,19 +78,4 @@ private boolean isSamePosition(int distanceX, int distanceY) { private boolean isOneStep(int distanceX, int distanceY) { return distanceX <= 1 && distanceY <= 1; } - - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } } diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 2c2f99b84f..56c59e1585 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -28,12 +28,25 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); + public String name() { + return pieceType.getName(); + } + + @Override + public PieceType getPieceType() { + return pieceType; } @Override - public Optional findMovePath(Position start, Position end) { + public TeamType getTeamType() { + return teamType; + } + + private boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); + } + + private Optional findMovePath(Position start, Position end) { int dx = end.getX() - start.getX(); int dy = end.getY() - start.getY(); if (isSamePosition(dx, dy)) { @@ -62,19 +75,4 @@ private List createPaths() { new MovePath(List.of(Delta.createRight())) ); } - - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } } diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index aab004c79e..535cd0f728 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -41,12 +41,25 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); + public String name() { + return pieceType.getName(); + } + + @Override + public PieceType getPieceType() { + return pieceType; } @Override - public Optional findMovePath(Position start, Position end) { + public TeamType getTeamType() { + return teamType; + } + + private boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); + } + + private Optional findMovePath(Position start, Position end) { int dx = end.getX() - start.getX(); int dy = end.getY() - start.getY(); return paths.stream() @@ -54,7 +67,7 @@ public Optional findMovePath(Position start, Position end) { .findFirst(); } - public boolean isObstaclesNotExist(Position startPosition, Position endPosition, Board board) { + private boolean isObstaclesNotExist(Position startPosition, Position endPosition, Board board) { Optional movePath = findMovePath(startPosition, endPosition); if (movePath.isEmpty()) { return false; @@ -62,19 +75,4 @@ public boolean isObstaclesNotExist(Position startPosition, Position endPosition, return movePath.get().intermediatePositions(startPosition, endPosition).stream() .noneMatch(board::hasPiece); } - - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } } diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index 97d1881208..179c1550f8 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -1,22 +1,16 @@ package janggi.domain.piece; import janggi.domain.Board; -import janggi.domain.MovePath; import janggi.domain.Position; import janggi.domain.side.TeamType; -import java.util.Optional; public interface Piece { - boolean isValidMovePattern(Position start, Position end); - - Optional findMovePath(Position start, Position end); + void validateCanMove(Position start, Position end, Board board); String name(); PieceType getPieceType(); TeamType getTeamType(); - - void validateCanMove(Position start, Position end, Board board); } diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index d8a4f8b7de..ae966132ed 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -35,12 +35,25 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); + public String name() { + return pieceType.getName(); + } + + @Override + public PieceType getPieceType() { + return pieceType; } @Override - public Optional findMovePath(Position start, Position end) { + public TeamType getTeamType() { + return teamType; + } + + private boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); + } + + private Optional findMovePath(Position start, Position end) { int startX = start.getX(); int startY = start.getY(); int endX = end.getX(); @@ -56,42 +69,6 @@ public Optional findMovePath(Position start, Position end) { .findFirst(); } - public boolean isObstaclesNotExist(Position start, Position end, Board board) { - Optional movePath = findMovePath(start, end); - if (movePath.isEmpty()) { - return false; - } - List intermediatePositions = movePath.get().intermediatePositions(start, end); - List obstacles = intermediatePositions.stream() - .map(board::findPiece) - .filter(Optional::isPresent) - .map(Optional::get) - .toList(); - if (obstacles.size() != 1) { - return false; - } - if (obstacles.getFirst().getPieceType() == PieceType.PO) { - return false; - } - Optional targetPiece = board.findPiece(end); - return targetPiece.isEmpty() || !(targetPiece.get().getPieceType() == PieceType.PO); - } - - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } - private void validateObstacles(Position start, Position end, Board board) { Optional movePath = findMovePath(start, end); if (movePath.isEmpty()) { @@ -110,7 +87,7 @@ private void validateObstacles(Position start, Position end, Board board) { throw new IllegalArgumentException("이동 경로에 기물이 1개 이상 존재합니다."); } if (obstacles.getFirst().getPieceType() == PieceType.PO) { - throw new IllegalArgumentException("포는 포를 잡을 수 없습니다."); + throw new IllegalArgumentException("포는 포를 넘을 수 없습니다."); } } } diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index e942952fef..26fa652fa8 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -37,12 +37,25 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); + public String name() { + return pieceType.getName(); + } + + @Override + public PieceType getPieceType() { + return pieceType; } @Override - public Optional findMovePath(Position start, Position end) { + public TeamType getTeamType() { + return teamType; + } + + private boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); + } + + private Optional findMovePath(Position start, Position end) { int dx = end.getX() - start.getX(); int dy = end.getY() - start.getY(); int distanceX = Math.abs(dx); @@ -65,19 +78,4 @@ private boolean isSamePosition(int distanceX, int distanceY) { private boolean isOneStep(int distanceX, int distanceY) { return distanceX <= 1 && distanceY <= 1; } - - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } } diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index bc658341f2..449f581d09 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -41,12 +41,25 @@ public void validateCanMove(Position start, Position end, Board board) { } @Override - public boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); + public String name() { + return pieceType.getName(); + } + + @Override + public PieceType getPieceType() { + return pieceType; } @Override - public Optional findMovePath(Position start, Position end) { + public TeamType getTeamType() { + return teamType; + } + + private boolean isValidMovePattern(Position start, Position end) { + return findMovePath(start, end).isPresent(); + } + + private Optional findMovePath(Position start, Position end) { int dx = end.getX() - start.getX(); int dy = end.getY() - start.getY(); return paths.stream() @@ -54,7 +67,7 @@ public Optional findMovePath(Position start, Position end) { .findFirst(); } - public boolean isObstaclesNotExist(Position start, Position end, Board board) { + private boolean isObstaclesNotExist(Position start, Position end, Board board) { Optional movePath = findMovePath(start, end); if (movePath.isEmpty()) { return false; @@ -62,19 +75,4 @@ public boolean isObstaclesNotExist(Position start, Position end, Board board) { return movePath.get().intermediatePositions(start, end).stream() .noneMatch(board::hasPiece); } - - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } } diff --git a/src/test/java/janggi/domain/piece/ChaTest.java b/src/test/java/janggi/domain/piece/ChaTest.java index 26ee50a562..aa0c5f15d6 100644 --- a/src/test/java/janggi/domain/piece/ChaTest.java +++ b/src/test/java/janggi/domain/piece/ChaTest.java @@ -1,83 +1,84 @@ package janggi.domain.piece; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; class ChaTest { - @Test - @DisplayName("차는 상하좌우로 직선 이동할 수 있다.") - void isValidMovePatternStraight() { - // given - Cha cha = new Cha(TeamType.CHU); + private Cha cha; + private Board board; - // when & then - assertAll( - () -> assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(7, 4))).isTrue(), - () -> assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(1, 4))).isTrue(), - () -> assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(4, 8))).isTrue(), - () -> assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(4, 1))).isTrue() - ); + @BeforeEach + void setUp() { + board = Board.createInitialBoard(); + cha = new Cha(TeamType.HAN); } - @Test - @DisplayName("차는 대각선으로 이동할 수 없다.") - void cannotMoveDiagonal() { + @ParameterizedTest + @DisplayName("차는 이동 경로에 장애물이 없다면 상하좌우 직선으로 이동할 수 있다.") + @CsvSource({ + "4, 5, 4, 9", + "4, 5, 4, 2", + "4, 5, 1, 5", + "4, 5, 9, 5" + }) + void validateCanMove_Success(int startX, int startY, int endX, int endY) { // given - Cha cha = new Cha(TeamType.CHU); + Position start = new Position(startX, startY); + Position end = new Position(endX, endY); // when & then - assertAll( - () -> assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isFalse(), - () -> assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(2, 2))).isFalse() - ); + assertThatCode(() -> cha.validateCanMove(start, end, board)) + .doesNotThrowAnyException(); } @Test - @DisplayName("차는 제자리로 이동할 수 없다.") - void cannotMoveSamePosition() { + @DisplayName("직선 경로가 아니거나 제자리로 이동할 경우 예외 발생") + void validateCanMove_Fail_InvalidPattern() { // given - Cha cha = new Cha(TeamType.CHU); + Position start = new Position(4, 4); + Position diagonalEnd = new Position(5, 5); + Position knightEnd = new Position(5, 6); + Position sameEnd = new Position(4, 4); // when & then - assertThat(cha.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse(); - } - - @Test - @DisplayName("이동 경로가 비어 있으면 차는 이동할 수 있다.") - void isValidPathWhenRouteIsEmpty() { - // given - Cha cha = new Cha(TeamType.CHU); - Board board = Board.createInitialBoard(); - - // when - boolean result = cha.isObstaclesNotExist(new Position(1, 1), new Position(1, 3), board); - - // then - assertThat(result).isTrue(); + assertAll( + () -> assertThatThrownBy(() -> cha.validateCanMove(start, diagonalEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), + () -> assertThatThrownBy(() -> cha.validateCanMove(start, knightEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), + () -> assertThatThrownBy(() -> cha.validateCanMove(start, sameEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다.") + ); } - @Test - @DisplayName("이동 경로가 막혀 있으면 차는 이동할 수 없다.") - void cannotMoveWhenRouteIsBlocked() { + @ParameterizedTest + @DisplayName("이동 경로 중간에 기물이 존재할 경우 예외 발생") + @CsvSource({ + "4, 4, 1, 4", + "4, 4, 7, 4" + }) + void validateCanMove_Fail_ObstacleExist(int startX, int startY, int endX, int endY) { // given - Cha cha = new Cha(TeamType.CHU); - Board board = Board.createInitialBoard(); - - // when - boolean result = cha.isObstaclesNotExist(new Position(1, 1), new Position(1, 5), board); + Position start = new Position(startX, startY); + Position end = new Position(endX, endY); - // then - assertThat(result).isFalse(); - } - - private Position createPosition(int x, int y) { - return new Position(x, y); + // when & then + assertThatThrownBy(() -> cha.validateCanMove(start, end, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); } -} +} \ No newline at end of file diff --git a/src/test/java/janggi/domain/piece/GungTest.java b/src/test/java/janggi/domain/piece/GungTest.java index c44108266d..797cb13767 100644 --- a/src/test/java/janggi/domain/piece/GungTest.java +++ b/src/test/java/janggi/domain/piece/GungTest.java @@ -1,71 +1,88 @@ package janggi.domain.piece; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; class GungTest { - @Test - @DisplayName("궁은 상하좌우 한 칸 이동할 수 있다.") - void isValidMovePatternStraightOneStep() { - // given - Gung gung = new Gung(TeamType.CHU); + private Gung gung; + private Board board; - // when & then - assertAll( - () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(4, 5))).isTrue(), - () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(4, 3))).isTrue(), - () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(5, 4))).isTrue(), - () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(3, 4))).isTrue() - ); + @BeforeEach + void setUp() { + // 한나라(HAN) 궁으로 설정 + gung = new Gung(TeamType.HAN); + board = createBoard(); } - @Test - @DisplayName("궁은 대각선 한 칸 이동할 수 있다.") - void isValidMovePatternDiagonalOneStep() { + @ParameterizedTest + @DisplayName("한나라 궁은 궁성 내에서 상하좌우 및 대각선으로 한 칸 이동할 수 있다.") + @CsvSource({ + "5, 9, 5, 10", + "5, 9, 5, 8", + "5, 9, 4, 9", + "5, 9, 6, 9", + "5, 9, 4, 10", + "5, 9, 6, 10", + "5, 9, 4, 8", + "5, 9, 6, 8" + }) + void validateCanMove_Success(int startX, int startY, int endX, int endY) { // given - Gung gung = new Gung(TeamType.CHU); + Position start = createPosition(startX, startY); + Position end = createPosition(endX, endY); // when & then - assertAll( - () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isTrue(), - () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(5, 3))).isTrue(), - () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(3, 5))).isTrue(), - () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(3, 3))).isTrue() - ); + assertThatCode(() -> gung.validateCanMove(start, end, board)) + .doesNotThrowAnyException(); } - @Test - @DisplayName("궁은 두 칸 이상 이동할 수 없다.") - void cannotMoveOverOneStep() { + @ParameterizedTest + @DisplayName("한 칸을 초과하거나 제자리인 경우 예외 발생") + @CsvSource({ + "5, 9, 5, 9", + "5, 9, 5, 7", + "5, 9, 3, 9", + "5, 9, 3, 7" + }) + void validateCanMove_Fail_InvalidDistance(int startX, int startY, int endX, int endY) { // given - Gung gung = new Gung(TeamType.CHU); + Position start = createPosition(startX, startY); + Position invalidDistanceEnd = createPosition(endX, endY); // when & then - assertAll( - () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(6, 4))).isFalse(), - () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(6, 6))).isFalse(), - () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(4, 6))).isFalse(), - () -> assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(2, 4))).isFalse() - ); + assertThatThrownBy(() -> gung.validateCanMove(start, invalidDistanceEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."); } @Test - @DisplayName("궁은 제자리로 이동할 수 없다.") - void cannotMoveSamePosition() { + @DisplayName("궁이 이동할 수 없는 패턴(예: 마의 행마)인 경우 예외 발생") + void validateCanMove_Fail_InvalidPattern() { // given - Gung gung = new Gung(TeamType.CHU); + Position start = createPosition(5, 9); + Position knightEnd = createPosition(6, 7); // when & then - assertThat(gung.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse(); + assertThatThrownBy(() -> gung.validateCanMove(start, knightEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."); + } + + private Board createBoard() { + return Board.createInitialBoard(); } private Position createPosition(int x, int y) { return new Position(x, y); } -} +} \ No newline at end of file diff --git a/src/test/java/janggi/domain/piece/JolTest.java b/src/test/java/janggi/domain/piece/JolTest.java index 9b6590cb94..9dadc3add0 100644 --- a/src/test/java/janggi/domain/piece/JolTest.java +++ b/src/test/java/janggi/domain/piece/JolTest.java @@ -1,91 +1,88 @@ package janggi.domain.piece; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; +import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; class JolTest { - @Test - @DisplayName("초나라 졸은 앞으로 한 칸 이동할 수 있다.") - void chuIsValidMovePatternForward() { - // given - Jol jol = new Jol(TeamType.CHU); - - // when & then - assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(4, 5))).isTrue(); - } - - @Test - @DisplayName("한나라 졸은 앞으로 한 칸 이동할 수 있다.") - void hanIsValidMovePatternForward() { - // given - Jol jol = new Jol(TeamType.HAN); + private Board board; + private Jol chuJol; + private Jol hanJol; - // when & then - assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(4, 3))).isTrue(); + @BeforeEach + void setUp() { + board = Board.createInitialBoard(); + chuJol = new Jol(TeamType.CHU); + hanJol = new Jol(TeamType.HAN); } - @Test - @DisplayName("졸은 좌우로 한 칸 이동할 수 있다.") - void isValidMovePatternHorizontally() { + @ParameterizedTest + @DisplayName("초나라 졸은 위, 왼쪽, 오른쪽으로 한 칸씩 이동할 수 있다.") + @CsvSource({ + "1, 4, 1, 5", + "3, 4, 2, 4", + "3, 4, 4, 4" + }) + void validateCanMove_ChuJol_Success(int startX, int startY, int endX, int endY) { // given - Jol jolOfChu = new Jol(TeamType.CHU); - Jol jolOfHan = new Jol(TeamType.HAN); + Position start = new Position(startX, startY); + Position end = new Position(endX, endY); // when & then - assertAll( - () -> assertThat(jolOfChu.isValidMovePattern(createPosition(4, 4), createPosition(5, 4))).isTrue(), - () -> assertThat(jolOfChu.isValidMovePattern(createPosition(4, 4), createPosition(3, 4))).isTrue(), - () -> assertThat(jolOfHan.isValidMovePattern(createPosition(4, 4), createPosition(5, 4))).isTrue(), - () -> assertThat(jolOfHan.isValidMovePattern(createPosition(4, 4), createPosition(3, 4))).isTrue() - ); + assertThatCode(() -> chuJol.validateCanMove(start, end, board)) + .doesNotThrowAnyException(); } - @Test - @DisplayName("졸은 뒤로 이동할 수 없다.") - void cannotMoveBackward() { + @ParameterizedTest + @DisplayName("한나라 졸은 아래, 왼쪽, 오른쪽으로 한 칸씩 이동할 수 있다.") + @CsvSource({ + "1, 7, 1, 6", + "3, 7, 2, 7", + "3, 7, 4, 7" + }) + void validateCanMove_HanJol_Success(int startX, int startY, int endX, int endY) { // given - Jol jolOfChu = new Jol(TeamType.CHU); - Jol hanJol = new Jol(TeamType.HAN); + Position start = new Position(startX, startY); + Position end = new Position(endX, endY); // when & then - assertAll( - () -> assertThat(jolOfChu.isValidMovePattern(createPosition(4, 4), createPosition(4, 3))).isFalse(), - () -> assertThat(hanJol.isValidMovePattern(createPosition(4, 4), createPosition(4, 5))).isFalse() - ); + assertThatCode(() -> hanJol.validateCanMove(start, end, board)) + .doesNotThrowAnyException(); } @Test - @DisplayName("졸은 두 칸 이상 이동하거나 대각선으로 이동할 수 없다.") - void cannotMoveInvalidPath() { + @DisplayName("졸이 후진하거나, 대각선으로 움직이거나, 두 칸 이상 이동할 경우 예외 발생") + void validateCanMove_Fail_InvalidPattern() { // given - Jol jol = new Jol(TeamType.CHU); + Position chuStart = new Position(1, 4); + Position hanStart = new Position(1, 7); - // when & then assertAll( - () -> assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(4, 6))).isFalse(), - () -> assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(2, 4))).isFalse(), - () -> assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isFalse(), - () -> assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(3, 3))).isFalse() + () -> assertThatThrownBy(() -> chuJol.validateCanMove(chuStart, new Position(1, 3), board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), + () -> assertThatThrownBy(() -> hanJol.validateCanMove(hanStart, new Position(1, 8), board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), + () -> assertThatThrownBy(() -> chuJol.validateCanMove(chuStart, new Position(2, 5), board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), + () -> assertThatThrownBy(() -> chuJol.validateCanMove(chuStart, new Position(1, 6), board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), + () -> assertThatThrownBy(() -> chuJol.validateCanMove(chuStart, chuStart, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다.") ); } - - @Test - @DisplayName("졸은 제자리로 이동할 수 없다.") - void cannotMoveSamePosition() { - // given - Jol jol = new Jol(TeamType.CHU); - - // when & then - assertThat(jol.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse(); - } - - private Position createPosition(int x, int y) { - return new Position(x, y); - } -} +} \ No newline at end of file diff --git a/src/test/java/janggi/domain/piece/MaTest.java b/src/test/java/janggi/domain/piece/MaTest.java index 348c6eaf30..c9cf9875df 100644 --- a/src/test/java/janggi/domain/piece/MaTest.java +++ b/src/test/java/janggi/domain/piece/MaTest.java @@ -1,78 +1,89 @@ package janggi.domain.piece; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; class MaTest { - @Test - @DisplayName("마는 여덟 방향의 L자 이동을 할 수 있다.") - void isValidMovePattern() { - // given - Ma ma = new Ma(TeamType.CHU); + private Ma ma; + private Board board; - // when & then - assertAll( - () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(5, 6))).isTrue(), - () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(3, 6))).isTrue(), - () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(5, 2))).isTrue(), - () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(3, 2))).isTrue(), - () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(2, 5))).isTrue(), - () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(2, 3))).isTrue(), - () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(6, 5))).isTrue(), - () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(6, 3))).isTrue() - ); + @BeforeEach + void setUp() { + board = Board.createInitialBoard(); + ma = new Ma(TeamType.HAN); } - @Test - @DisplayName("마는 직선이나 대각선으로 이동할 수 없고 제자리 이동도 할 수 없다.") - void cannotMoveInvalidPattern() { + @ParameterizedTest + @DisplayName("마는 이동 경로(멱)에 장애물이 없다면 L자 모양으로 이동할 수 있다.") + @CsvSource({ + "4, 4, 5, 6", + "4, 4, 3, 6", + "4, 4, 5, 2", + "4, 4, 3, 2" + }) + void validateCanMove_Success(int startX, int startY, int endX, int endY) { // given - Ma ma = new Ma(TeamType.CHU); + Position start = new Position(startX, startY); + Position end = new Position(endX, endY); // when & then - assertAll( - () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(4, 5))).isFalse(), - () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isFalse(), - () -> assertThat(ma.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse() - ); + assertThatCode(() -> ma.validateCanMove(start, end, board)) + .doesNotThrowAnyException(); } @Test - @DisplayName("이동 경로의 첫 칸이 비어 있으면 마는 이동할 수 있다.") - void isValidPathWhenIntermediatePositionIsEmpty() { + @DisplayName("마의 행마법(L자)이 아니거나 제자리로 이동할 경우 예외 발생") + void validateCanMove_Fail_InvalidPattern() { // given - Ma ma = new Ma(TeamType.CHU); - Board board = Board.createInitialBoard(); - - // when - boolean result = ma.isObstaclesNotExist(new Position(2, 1), new Position(3, 3), board); + Position start = new Position(4, 4); + Position straightEnd = new Position(4, 6); + Position diagonalEnd = new Position(5, 5); + Position longEnd = new Position(6, 7); - // then - assertThat(result).isTrue(); + // when & then + assertAll( + () -> assertThatThrownBy(() -> ma.validateCanMove(start, straightEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), + () -> assertThatThrownBy(() -> ma.validateCanMove(start, diagonalEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), + () -> assertThatThrownBy(() -> ma.validateCanMove(start, longEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), + () -> assertThatThrownBy(() -> ma.validateCanMove(start, start, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다.") + ); } - @Test - @DisplayName("이동 경로의 첫 칸이 막혀 있으면 마는 이동할 수 없다.") - void cannotMoveWhenIntermediatePositionIsBlocked() { + @ParameterizedTest + @DisplayName("마의 멱(직선 방향의 첫 칸)에 기물이 존재할 경우 예외 발생") + @CsvSource({ + "4, 4, 6, 5", + "4, 4, 6, 3", + "4, 4, 2, 5", + "4, 4, 2, 3" + }) + void validateCanMove_Fail_ObstacleExist(int startX, int startY, int endX, int endY) { // given - Ma ma = new Ma(TeamType.CHU); - Board board = Board.createInitialBoard(); + Position start = new Position(startX, startY); + Position end = new Position(endX, endY); - // when - boolean result = ma.isObstaclesNotExist(new Position(2, 1), new Position(4, 2), board); - - // then - assertThat(result).isFalse(); - } - - private Position createPosition(int x, int y) { - return new Position(x, y); + // when & then + assertThatThrownBy(() -> ma.validateCanMove(start, end, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); } -} +} \ No newline at end of file diff --git a/src/test/java/janggi/domain/piece/PoTest.java b/src/test/java/janggi/domain/piece/PoTest.java index f0628140a8..dccc65b73d 100644 --- a/src/test/java/janggi/domain/piece/PoTest.java +++ b/src/test/java/janggi/domain/piece/PoTest.java @@ -1,89 +1,92 @@ package janggi.domain.piece; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; class PoTest { - @Test - @DisplayName("포는 상하좌우로 직선 이동할 수 있다.") - void isValidMovePatternStraight() { + private Po po; + private Board board; + + @BeforeEach + void setUp() { + board = Board.createInitialBoard(); + po = new Po(TeamType.HAN); + } + + @ParameterizedTest + @DisplayName("포는 경로 사이에 기물이 정확히 하나(포 제외) 있을 때만 넘어갈 수 있다.") + @CsvSource({ + "3, 8, 3, 6", + "1, 8, 1, 6", + "7, 8, 7, 6" + }) + void validateCanMove_Success(int startX, int startY, int endX, int endY) { // given - Po po = new Po(TeamType.CHU); + Position start = new Position(startX, startY); + Position end = new Position(endX, endY); // when & then - assertAll( - () -> assertThat(po.isValidMovePattern(createPosition(4, 4), createPosition(4, 8))).isTrue(), - () -> assertThat(po.isValidMovePattern(createPosition(4, 4), createPosition(4, 1))).isTrue(), - () -> assertThat(po.isValidMovePattern(createPosition(4, 4), createPosition(7, 4))).isTrue(), - () -> assertThat(po.isValidMovePattern(createPosition(4, 4), createPosition(1, 4))).isTrue() - ); + assertThatCode(() -> po.validateCanMove(start, end, board)) + .doesNotThrowAnyException(); } @Test - @DisplayName("포는 대각선이나 제자리로 이동할 수 없다.") - void cannotMoveInvalidPattern() { - // given - Po po = new Po(TeamType.CHU); + @DisplayName("직선 경로가 아니거나 제자리 이동일 경우 예외 발생") + void validateCanMove_Fail_InvalidPattern() { + Position start = new Position(4, 4); - // when & then assertAll( - () -> assertThat(po.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isFalse(), - () -> assertThat(po.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse() + () -> assertThatThrownBy(() -> po.validateCanMove(start, new Position(5, 5), board)) + .isInstanceOf(IllegalArgumentException.class), + () -> assertThatThrownBy(() -> po.validateCanMove(start, start, board)) + .isInstanceOf(IllegalArgumentException.class) ); } @Test - @DisplayName("포는 사이에 기물이 하나만 있으면 이동할 수 있다.") - void isObstaclesNotExistWhenExactlyOneBridgeExists() { - // given - Po po = new Po(TeamType.CHU); - Board board = Board.createInitialBoard(); - Board movedBoard = board.move(new Position(1, 4), new Position(2, 4), TeamType.CHU); - - // when - boolean result = po.isObstaclesNotExist(new Position(2, 3), new Position(2, 6), movedBoard); + @DisplayName("이동 경로에 기물이 하나도 없을 경우 예외 발생") + void validateCanMove_Fail_NoObstacle() { + Position start = new Position(4, 4); + Position end = new Position(4, 6); - // then - assertThat(result).isTrue(); + // when & then + assertThatThrownBy(() -> po.validateCanMove(start, end, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동 경로에 기물이 존재하지 않아 이동할 수 없습니다."); } @Test - @DisplayName("포는 사이에 기물이 없으면 이동할 수 없다.") - void cannotMoveWithoutBridge() { - // given - Po po = new Po(TeamType.CHU); - Board board = Board.createInitialBoard(); - - // when - boolean result = po.isObstaclesNotExist(new Position(2, 3), new Position(2, 6), board); + @DisplayName("이동 경로에 기물이 2개 이상일 경우 예외 발생") + void validateCanMove_Fail_TooManyObstacles() { + Position start = new Position(2, 10); + Position end = new Position(2, 1); - // then - assertThat(result).isFalse(); + // when & then + assertThatThrownBy(() -> po.validateCanMove(start, end, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동 경로에 기물이 1개 이상 존재합니다."); } @Test - @DisplayName("포는 포를 다리로 사용할 수 없고 포를 잡을 수도 없다.") - void cannotUsePoAsBridgeOrTarget() { - // given - Po po = new Po(TeamType.CHU); - Board board = Board.createInitialBoard(); - Board movedBoard = board.move(new Position(1, 4), new Position(2, 4), TeamType.CHU); + @DisplayName("포의 이동 경로에 포가 존재할 경우 예외 발생") + void validateCanMove_Fail_PoAsObstacle() { + Position start = new Position(2, 9); + Position end = new Position(2, 7); // when & then - assertAll( - () -> assertThat(po.isObstaclesNotExist(new Position(2, 3), new Position(2, 10), board)).isFalse(), - () -> assertThat(po.isObstaclesNotExist(new Position(2, 3), new Position(2, 8), movedBoard)).isFalse() - ); - } - - private Position createPosition(int x, int y) { - return new Position(x, y); + assertThatThrownBy(() -> po.validateCanMove(start, end, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("포는 포를 넘을 수 없습니다."); } -} +} \ No newline at end of file diff --git a/src/test/java/janggi/domain/piece/SaTest.java b/src/test/java/janggi/domain/piece/SaTest.java index 2a4ea78d74..c7917418b1 100644 --- a/src/test/java/janggi/domain/piece/SaTest.java +++ b/src/test/java/janggi/domain/piece/SaTest.java @@ -1,61 +1,72 @@ package janggi.domain.piece; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; +import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; class SaTest { - @Test - @DisplayName("사는 상하좌우 한 칸 이동할 수 있다.") - void isValidMovePatternStraightOneStep() { - // given - Sa sa = new Sa(TeamType.CHU); + private Sa sa; + private Board board; - // when & then - assertAll( - () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(4, 5))).isTrue(), - () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(4, 3))).isTrue(), - () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(5, 4))).isTrue(), - () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(3, 4))).isTrue() - ); + @BeforeEach + void setUp() { + board = Board.createInitialBoard(); + sa = new Sa(TeamType.HAN); } - @Test - @DisplayName("사는 대각선 한 칸 이동할 수 있다.") - void isValidMovePatternDiagonalOneStep() { + @ParameterizedTest + @DisplayName("사는 상하좌우 및 대각선으로 한 칸 이동할 수 있다.") + @CsvSource({ + "4, 1, 4, 2", + "4, 1, 5, 1", + "4, 1, 3, 1", + "4, 1, 5, 2", + "4, 1, 3, 2" + }) + void validateCanMove_Success(int startX, int startY, int endX, int endY) { // given - Sa sa = new Sa(TeamType.CHU); + Position start = new Position(startX, startY); + Position end = new Position(endX, endY); // when & then - assertAll( - () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isTrue(), - () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(5, 3))).isTrue(), - () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(3, 5))).isTrue(), - () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(3, 3))).isTrue() - ); + assertThatCode(() -> sa.validateCanMove(start, end, board)) + .doesNotThrowAnyException(); } @Test - @DisplayName("사는 두 칸 이상 이동하거나 제자리로 이동할 수 없다.") - void cannotMoveInvalidPattern() { + @DisplayName("한 칸을 초과하여 이동하거나 제자리 이동일 경우 예외 발생") + void validateCanMove_Fail_InvalidPattern() { // given - Sa sa = new Sa(TeamType.CHU); + Position start = new Position(4, 1); + Position moveTwoSteps = new Position(4, 3); + Position moveLongDiagonal = new Position(6, 3); + Position knightMove = new Position(5, 3); + Position samePosition = new Position(4, 1); // when & then assertAll( - () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(6, 4))).isFalse(), - () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(6, 6))).isFalse(), - () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(4, 6))).isFalse(), - () -> assertThat(sa.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse() + () -> assertThatThrownBy(() -> sa.validateCanMove(start, moveTwoSteps, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), + () -> assertThatThrownBy(() -> sa.validateCanMove(start, moveLongDiagonal, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), + () -> assertThatThrownBy(() -> sa.validateCanMove(start, knightMove, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), + () -> assertThatThrownBy(() -> sa.validateCanMove(start, samePosition, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다.") ); } - - private Position createPosition(int x, int y) { - return new Position(x, y); - } -} +} \ No newline at end of file diff --git a/src/test/java/janggi/domain/piece/SangTest.java b/src/test/java/janggi/domain/piece/SangTest.java index 65f22ea0c1..963c845da0 100644 --- a/src/test/java/janggi/domain/piece/SangTest.java +++ b/src/test/java/janggi/domain/piece/SangTest.java @@ -1,78 +1,78 @@ package janggi.domain.piece; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; class SangTest { - @Test - @DisplayName("상은 여덟 방향으로 이동할 수 있다.") - void isValidMovePattern() { - // given - Sang sang = new Sang(TeamType.CHU); + private Sang sang; + private Board board; - // when & then - assertAll( - () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(7, 6))).isTrue(), - () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(1, 6))).isTrue(), - () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(7, 2))).isTrue(), - () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(1, 2))).isTrue(), - () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(6, 7))).isTrue(), - () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(6, 1))).isTrue(), - () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(2, 7))).isTrue(), - () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(2, 1))).isTrue() - ); + @BeforeEach + void setUp() { + board = Board.createInitialBoard(); + sang = new Sang(TeamType.HAN); } - @Test - @DisplayName("상은 잘못된 이동 패턴이나 제자리 이동을 할 수 없다.") - void cannotMoveInvalidPattern() { + @ParameterizedTest + @DisplayName("상은 경로상에 장애물이 없다면 '1칸 직선 + 2칸 대각선'으로 이동할 수 있다.") + @CsvSource({ + "4, 4, 6, 7", + "4, 4, 2, 7", + "4, 5, 6, 2", + "4, 4, 2, 1" + }) + void validateCanMove_Success(int startX, int startY, int endX, int endY) { // given - Sang sang = new Sang(TeamType.CHU); + Position start = new Position(startX, startY); + Position end = new Position(endX, endY); // when & then - assertAll( - () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(4, 5))).isFalse(), - () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(5, 5))).isFalse(), - () -> assertThat(sang.isValidMovePattern(createPosition(4, 4), createPosition(4, 4))).isFalse() - ); + assertThatCode(() -> sang.validateCanMove(start, end, board)) + .doesNotThrowAnyException(); } @Test - @DisplayName("이동 경로가 비어 있으면 상은 이동할 수 있다.") - void isObstaclesNotExistWhenIntermediatePositionsAreEmpty() { - // given - Sang sang = new Sang(TeamType.CHU); - Board board = Board.createInitialBoard(); + @DisplayName("상의 행마 패턴(대형 L자)이 아닐 경우 예외 발생") + void validateCanMove_Fail_InvalidPattern() { + Position start = new Position(4, 4); - // when - boolean result = sang.isObstaclesNotExist(new Position(4, 5), new Position(7, 7), board); - - // then - assertThat(result).isTrue(); + assertAll( + () -> assertThatThrownBy(() -> sang.validateCanMove(start, new Position(4, 7), board)) + .isInstanceOf(IllegalArgumentException.class), + () -> assertThatThrownBy(() -> sang.validateCanMove(start, new Position(5, 6), board)) + .isInstanceOf(IllegalArgumentException.class), + () -> assertThatThrownBy(() -> sang.validateCanMove(start, start, board)) + .isInstanceOf(IllegalArgumentException.class) + ); } - @Test - @DisplayName("이동 경로 중 하나라도 막혀 있으면 상은 이동할 수 없다.") - void cannotMoveWhenIntermediatePositionIsBlocked() { + @ParameterizedTest + @DisplayName("상 이동 경로의 첫 번째 멱(직선)이나 두 번째 멱(대각선)에 기물이 있을 경우 예외 발생") + @CsvSource({ + "4, 4, 7, 6", + "4, 4, 1, 6", + "4, 4, 7, 2", + "4, 4, 1, 2" + }) + void validateCanMove_Fail_ObstacleExist(int startX, int startY, int endX, int endY) { // given - Sang sang = new Sang(TeamType.CHU); - Board board = Board.createInitialBoard(); + Position start = new Position(startX, startY); + Position end = new Position(endX, endY); - // when - boolean result = sang.isObstaclesNotExist(new Position(4, 2), new Position(7, 4), board); - - // then - assertThat(result).isFalse(); - } - - private Position createPosition(int x, int y) { - return new Position(x, y); + // when & then + assertThatThrownBy(() -> sang.validateCanMove(start, end, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); } -} +} \ No newline at end of file From 1c198af040edc16dd4879d35cdf8576dca109333 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Tue, 31 Mar 2026 21:19:48 +0900 Subject: [PATCH 76/99] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=8F=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/main/java/.gitkeep | 0 src/test/java/.gitkeep | 0 src/test/java/janggi/domain/BoardTest.java | 160 +----------------- src/test/java/janggi/domain/MovePathTest.java | 158 ----------------- src/test/java/janggi/domain/PiecesTest.java | 69 -------- src/test/java/janggi/domain/PositionTest.java | 22 +-- src/test/java/janggi/domain/TurnTest.java | 90 ---------- 7 files changed, 9 insertions(+), 490 deletions(-) delete mode 100644 src/main/java/.gitkeep delete mode 100644 src/test/java/.gitkeep delete mode 100644 src/test/java/janggi/domain/MovePathTest.java delete mode 100644 src/test/java/janggi/domain/PiecesTest.java delete mode 100644 src/test/java/janggi/domain/TurnTest.java diff --git a/src/main/java/.gitkeep b/src/main/java/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/test/java/.gitkeep b/src/test/java/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java index 8c40a2ac9d..e6a1c49328 100644 --- a/src/test/java/janggi/domain/BoardTest.java +++ b/src/test/java/janggi/domain/BoardTest.java @@ -1,33 +1,15 @@ package janggi.domain; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - -import janggi.domain.piece.Jol; import janggi.domain.side.TeamType; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -class BoardTest { +import static org.assertj.core.api.Assertions.assertThatThrownBy; - // canMove에서 시작위치 검증 안 하기 때문에 없어도 되는 테스트 -// @Test -// @DisplayName("시작 위치가 장기판 범위를 벗어나면 이동할 수 없다.") -// void cannotMoveWhenStartPositionIsOutOfRange() { -// // given -// Board board = Board.createInitialBoard(); -// Position outOfBound = new Position(0, 0); -// -// // when & then -// assertThatThrownBy(() -> board.canMove(outOfBound, new Position(1, 4), TeamType.CHU)) -// .isInstanceOf(IllegalArgumentException.class) -// .hasMessage("입력한 좌표가 장기판 범위 밖입니다."); -// } +class BoardTest { @Test - @DisplayName("도착 위치가 장기판 범위를 벗어나면 이동할 수 없다.") + @DisplayName("도착 좌표가 장기판 범위 밖일 경우 예외 발생") void cannotMoveWhenEndPositionIsOutOfRange() { // given Board board = Board.createInitialBoard(); @@ -40,33 +22,20 @@ void cannotMoveWhenEndPositionIsOutOfRange() { } @Test - @DisplayName("현재 팀의 기물이 없는 시작 위치에서 시작할 수 없다.") + @DisplayName("입력한 좌표에 기물이 없을 경우 예외 발생") void cannotMoveWhenStartPositionHasNoCurrentTeamPiece() { // given Board board = Board.createInitialBoard(); Position notExistPosition = new Position(2, 2); // when & then - assertThatThrownBy(() -> board.validateCanMove(notExistPosition, new Position(1, 1), TeamType.CHU)) + assertThatThrownBy(() -> board.isPieceExists(notExistPosition, TeamType.CHU)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("입력한 위치에 기물이 없습니다."); } @Test - @DisplayName("다른 팀의 기물이 있는 위치에서 시작할 수 없다.") - void cannotStartAtOpponentTeamPosition() { - // given - Board board = Board.createInitialBoard(); - Position opponentTeamPosition = new Position(1, 7); - - // when & then - assertThatThrownBy(() -> board.validateCanMove(opponentTeamPosition, new Position(1, 6), TeamType.CHU)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("입력한 위치에 기물이 없습니다."); - } - - @Test - @DisplayName("같은 팀의 기물이 있는 위치로는 이동할 수 없다.") + @DisplayName("목적 좌표에 아군 기물이 있을 경우 예외 발생") void cannotMoveToSameTeamPiecePosition() { // given Board board = Board.createInitialBoard(); @@ -77,121 +46,4 @@ void cannotMoveToSameTeamPiecePosition() { .isInstanceOf(IllegalArgumentException.class) .hasMessage("아군이 존재하는 좌표로는 이동할 수 없습니다."); } - - @Test - @DisplayName("이동 패턴에 맞지 않는 사의 이동은 장애물과 관계없이 막는다.") - void cannotMoveWhenMovePatternIsInvalid() { - // given - Board board = Board.createInitialBoard(); - Position saStartPosition = new Position(4, 1); - Position saEndPosition = new Position(4, 3); - - // when & then - assertThatThrownBy(() -> board.validateCanMove(saStartPosition, saEndPosition, TeamType.CHU)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."); - } - - @Test - @DisplayName("차의 경로 중간에 장애물이 있으면 이동할 수 없다.") - void cannotMoveChaWhenPathIsBlocked() { - // given - Board board = Board.createInitialBoard(); - Position chaStartPosition = new Position(1, 1); - Position chaEndPosition = new Position(1, 5); - - // when & then - assertThatThrownBy(() -> board.validateCanMove(chaStartPosition, chaEndPosition, TeamType.CHU)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); - } - - @Test - @DisplayName("마의 경로 중간에 장애물이 있으면 이동할 수 없다.") - void cannotMoveMaWhenLegIsBlocked() { - // given - Board board = Board.createInitialBoard(); - Position maStartPosition = new Position(2, 1); - Position maEndPosition = new Position(4, 2); - - // when & then - assertThatThrownBy(() -> board.validateCanMove(maStartPosition, maEndPosition, TeamType.CHU)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); - } - - @Test - @DisplayName("포는 사이에 기물이 없으면 이동할 수 없다.") - void cannotMovePoWithoutBridge() { - // given - Board board = Board.createInitialBoard(); - Position poStartPosition = new Position(2, 3); - Position poEndPosition = new Position(2, 6); - - // when & then - assertThatThrownBy(() -> board.validateCanMove(poStartPosition, poEndPosition, TeamType.CHU)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동 경로에 기물이 존재하지 않아 이동할 수 없습니다."); - } - - @Test - @DisplayName("이동 패턴과 장애물 조건을 모두 만족하면 이동할 수 있다.") - void validateCanMoveWhenPatternAndObstacleRulesAreSatisfied() { - // given - Board board = Board.createInitialBoard(); - Position jolStartPosition = new Position(1, 4); - Position jolEndPosition = new Position(2, 4); - Board movedBoard = board.move(jolStartPosition, jolEndPosition, TeamType.CHU); - Position chaStartPosition = new Position(1, 1); - Position chaEndPosition = new Position(1, 3); - Position poStartPosition = new Position(2, 3); - Position poEndPosition = new Position(2, 6); - - // when & then - assertAll( - () -> assertThatCode(() -> movedBoard.validateCanMove(chaStartPosition, chaEndPosition, TeamType.CHU)) - .doesNotThrowAnyException(), - () -> assertThatCode(() -> movedBoard.validateCanMove(poStartPosition, poEndPosition, TeamType.CHU)) - .doesNotThrowAnyException() - ); - } - - @Test - @DisplayName("기물을 이동하면 새 보드를 반환하고 원본 보드는 유지한다.") - void moveReturnsNewBoardWithoutMutatingOriginalBoard() { - // given - Board board = Board.createInitialBoard(); - Position start = new Position(1, 4); - Position end = new Position(1, 5); - - // when - Board movedBoard = board.move(start, end, TeamType.CHU); - - // then - assertAll( - () -> assertThat(board.findPiece(start)).get().isInstanceOf(Jol.class), - () -> assertThat(board.findPiece(end)).isEmpty(), - () -> assertThat(movedBoard.findPiece(start)).isEmpty(), - () -> assertThat(movedBoard.findPiece(end)).get().isInstanceOf(Jol.class) - ); - } - - @Test - @DisplayName("상대 기물이 있는 칸으로 이동하면 상대 기물을 잡는다.") - void moveCapturesOpponentPiece() { - // given - Board board = Board.createInitialBoard(); - - // when - Board firstMovedBoard = board.move(new Position(1, 4), new Position(1, 5), TeamType.CHU); - Board secondMovedBoard = firstMovedBoard.move(new Position(1, 5), new Position(1, 6), TeamType.CHU); - Board capturedBoard = secondMovedBoard.move(new Position(1, 6), new Position(1, 7), TeamType.CHU); - - // then - assertAll( - () -> assertThat(secondMovedBoard.findPiece(new Position(1, 7))).get().isInstanceOf(Jol.class), - () -> assertThat(capturedBoard.findPiece(new Position(1, 6))).isEmpty(), - () -> assertThat(capturedBoard.findPiece(new Position(1, 7))).get().isInstanceOf(Jol.class) - ); - } } diff --git a/src/test/java/janggi/domain/MovePathTest.java b/src/test/java/janggi/domain/MovePathTest.java deleted file mode 100644 index bba4db9084..0000000000 --- a/src/test/java/janggi/domain/MovePathTest.java +++ /dev/null @@ -1,158 +0,0 @@ -package janggi.domain; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class MovePathTest { - - @Test - @DisplayName("경로의 델타 합과 이동량이 같으면 일치한다.") - void matchesWhenTotalDeltaMatches() { - // given - MovePath movePath = new MovePath(List.of(Delta.createUp(), Delta.createRightUp())); - - // when - boolean result = movePath.matches(1, 2); - - // then - assertThat(result).isTrue(); - } - - @Test - @DisplayName("경로의 델타 합과 이동량이 다르면 일치하지 않는다.") - void doesNotMatchWhenTotalDeltaDiffers() { - // given - MovePath movePath = new MovePath(List.of(Delta.createUp(), Delta.createRightUp())); - - // when & then - assertAll( - () -> assertThat(movePath.matches(2, 1)).isFalse(), - () -> assertThat(movePath.matches(1, 1)).isFalse() - ); - } - - @Test - @DisplayName("한 칸 직선 경로는 같은 방향의 여러 칸 직선 이동과 방향이 일치한다.") - void matchesDirectionForStraightPath() { - // given - MovePath upwardPath = new MovePath(List.of(Delta.createUp())); - MovePath rightPath = new MovePath(List.of(Delta.createRight())); - - // when & then - assertAll( - () -> assertThat(upwardPath.matchesDirection(0, 3)).isTrue(), - () -> assertThat(upwardPath.matchesDirection(0, 1)).isTrue(), - () -> assertThat(rightPath.matchesDirection(4, 0)).isTrue(), - () -> assertThat(rightPath.matchesDirection(1, 0)).isTrue() - ); - } - - @Test - @DisplayName("한 칸 경로라도 방향이 다르거나 제자리 이동이면 일치하지 않는다.") - void doesNotMatchDirectionWhenDirectionIsDifferentOrSamePosition() { - // given - MovePath upwardPath = new MovePath(List.of(Delta.createUp())); - MovePath diagonalPath = new MovePath(List.of(Delta.createRightUp())); - - // when & then - assertAll( - () -> assertThat(upwardPath.matchesDirection(0, -2)).isFalse(), - () -> assertThat(upwardPath.matchesDirection(2, 0)).isFalse(), - () -> assertThat(upwardPath.matchesDirection(0, 0)).isFalse(), - () -> assertThat(diagonalPath.matchesDirection(2, 2)).isTrue(), - () -> assertThat(diagonalPath.matchesDirection(-2, -2)).isFalse(), - () -> assertThat(diagonalPath.matchesDirection(2, 1)).isFalse() - ); - } - - @Test - @DisplayName("두 칸 이상으로 구성된 경로는 matchesDirection으로 비교하지 않는다.") - void doesNotMatchDirectionWhenPathHasMultipleSteps() { - // given - MovePath movePath = new MovePath(List.of(Delta.createUp(), Delta.createRightUp())); - - // when - boolean result = movePath.matchesDirection(1, 2); - - // then - assertThat(result).isFalse(); - } - - @Test - @DisplayName("직선 경로는 시작 위치부터 도착 위치까지 모든 칸을 순서대로 만든다.") - void createStraightRoute() { - // given - MovePath movePath = new MovePath(List.of(Delta.createUp())); - - // when - List route = movePath.createRoute(new Position(1, 1), new Position(1, 4)); - - // then - assertThat(route).containsExactly( - new Position(1, 2), - new Position(1, 3), - new Position(1, 4) - ); - } - - @Test - @DisplayName("여러 단계 경로는 각 델타를 적용한 위치들을 순서대로 만든다.") - void createRouteForMultiStepPath() { - // given - MovePath movePath = new MovePath(List.of( - Delta.createUp(), - Delta.createRightUp(), - Delta.createRightUp() - )); - - // when - List route = movePath.createRoute(new Position(4, 4), new Position(6, 7)); - - // then - assertThat(route).containsExactly( - new Position(4, 5), - new Position(5, 6), - new Position(6, 7) - ); - } - - @Test - @DisplayName("중간 경유지는 도착 위치를 제외한 경로만 반환한다.") - void intermediatePositionsExcludesDestination() { - // given - MovePath straightPath = new MovePath(List.of(Delta.createUp())); - MovePath multiStepPath = new MovePath(List.of( - Delta.createUp(), - Delta.createRightUp(), - Delta.createRightUp() - )); - - // when & then - assertAll( - () -> assertThat(straightPath.intermediatePositions(new Position(1, 1), new Position(1, 4))) - .containsExactly(new Position(1, 2), new Position(1, 3)), - () -> assertThat(multiStepPath.intermediatePositions(new Position(4, 4), new Position(6, 7))) - .containsExactly(new Position(4, 5), new Position(5, 6)) - ); - } - - @Test - @DisplayName("한 칸 이동 경로의 중간 경유지는 없다.") - void intermediatePositionsIsEmptyWhenMoveHasNoMiddleStep() { - // given - MovePath movePath = new MovePath(List.of(Delta.createUp())); - - // when - List intermediatePositions = movePath.intermediatePositions( - new Position(3, 3), - new Position(3, 4) - ); - - // then - assertThat(intermediatePositions).isEmpty(); - } -} diff --git a/src/test/java/janggi/domain/PiecesTest.java b/src/test/java/janggi/domain/PiecesTest.java deleted file mode 100644 index 4e55c4c11e..0000000000 --- a/src/test/java/janggi/domain/PiecesTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package janggi.domain; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -import janggi.domain.piece.Jol; -import janggi.domain.piece.PieceType; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class PiecesTest { - - @Test - @DisplayName("기물을 이동하면 새 Pieces를 반환하고 원본은 유지한다.") - void move() { - // given - Pieces pieces = Pieces.createChu(); - Position start = new Position(1, 4); - Position end = new Position(1, 5); - - // when - Pieces movedPieces = pieces.move(start, end); - - // then - assertAll( - () -> assertThat(pieces.findPiece(start)).get().isInstanceOf(Jol.class), - () -> assertThat(pieces.findPiece(end)).isEmpty(), - () -> assertThat(movedPieces.findPiece(start)).isEmpty(), - () -> assertThat(movedPieces.findPiece(end)).get().isInstanceOf(Jol.class) - ); - } - - @Test - @DisplayName("기물을 제거하면 새 Pieces를 반환하고 원본은 유지한다.") - void remove() { - // given - Pieces pieces = Pieces.createChu(); - Position target = new Position(1, 4); - - // when - Pieces removedPieces = pieces.remove(target); - - // then - assertAll( - () -> assertThat(pieces.findPiece(target)).get().isInstanceOf(Jol.class), - () -> assertThat(removedPieces.findPiece(target)).isEmpty(), - () -> assertThat(removedPieces.makeSnapShot()).hasSize(15) - ); - } - - @Test - @DisplayName("보드 스냅샷은 좌표, 기물 이름, 팀 정보를 포함한다.") - void makeSnapShot() { - // given - Pieces pieces = Pieces.createChu(); - - // when - var boardSpots = pieces.makeSnapShot(); - var gungSpot = boardSpots.get(new Position(5, 2)); - - // then - assertThat(gungSpot).isNotNull(); - assertAll( - () -> assertThat(gungSpot.position()).isEqualTo(new Position(5, 2)), - () -> assertThat(gungSpot.pieceName()).isEqualTo(PieceType.GUNG.getName()), - () -> assertThat(gungSpot.teamType()).isEqualTo(janggi.domain.side.TeamType.CHU) - ); - } -} diff --git a/src/test/java/janggi/domain/PositionTest.java b/src/test/java/janggi/domain/PositionTest.java index 5cf61ed48c..68fb07465d 100644 --- a/src/test/java/janggi/domain/PositionTest.java +++ b/src/test/java/janggi/domain/PositionTest.java @@ -11,7 +11,7 @@ class PositionTest { @Test - @DisplayName("좌표 문자열 두 개를 받아 Position을 생성한다.") + @DisplayName("좌표 문자열 두 개를 받아 Position 생성 성공") void makePosition() { // given List rawPosition = List.of("3", "7"); @@ -27,7 +27,7 @@ void makePosition() { } @Test - @DisplayName("좌표 입력이 두 개가 아니면 예외가 발생한다.") + @DisplayName("좌표 입력이 두 개가 아닐 경우 예외 발생") void throwWhenInputSizeIsNotTwo() { // given List rawPosition = List.of("3"); @@ -39,7 +39,7 @@ void throwWhenInputSizeIsNotTwo() { } @Test - @DisplayName("좌표 입력이 숫자가 아니면 예외가 발생한다.") + @DisplayName("좌표 입력이 숫자가 아닐 경우 예외 발생") void throwWhenInputContainsNonNumericValue() { // given List rawPosition = List.of("a", "7"); @@ -65,20 +65,4 @@ void move() { () -> assertThat(position).isEqualTo(new Position(4, 4)) ); } - - @Test - @DisplayName("같은 좌표의 Position은 동등하고 해시코드도 같다.") - void equalsAndHashCode() { - // given - Position first = new Position(2, 8); - Position second = new Position(2, 8); - Position other = new Position(8, 2); - - // when & then - assertAll( - () -> assertThat(first).isEqualTo(second), - () -> assertThat(first.hashCode()).isEqualTo(second.hashCode()), - () -> assertThat(first).isNotEqualTo(other) - ); - } } diff --git a/src/test/java/janggi/domain/TurnTest.java b/src/test/java/janggi/domain/TurnTest.java deleted file mode 100644 index 2fd83c37d4..0000000000 --- a/src/test/java/janggi/domain/TurnTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package janggi.domain; - -import janggi.dto.BoardSpot; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.assertAll; - -class TurnTest { - - @Test - @DisplayName("초기 턴의 다음 차례는 초나라다.") - void createInitialTurn() { - // given - Turn turn = Turn.createInitialTurn(); - - // when - String nextTurnTeam = turn.nextTurnTeam(); - - // then - assertThat(nextTurnTeam).isEqualTo("초나라"); - } - - @Test - @DisplayName("현재 차례 팀의 기물 존재 여부를 확인한다.") - void isNextTurnTeamPieceExists() { - // given - Turn turn = Turn.createInitialTurn(); - Position chuJolPosition = new Position(1, 4); - Position hanJolPosition = new Position(1, 7); - - // when & then - assertAll( - () -> assertThat(turn.isNextTurnTeamPieceExists(chuJolPosition)).isTrue(), - () -> assertThat(turn.isNextTurnTeamPieceExists(hanJolPosition)).isFalse() - ); - } - - @Test - @DisplayName("현재 차례 팀의 올바른 이동은 허용한다.") - void validateCanMove() { - // given - Turn turn = Turn.createInitialTurn(); - Position chuJolStartPosition = new Position(1, 4); - Position chuJolEndPosition = new Position(1, 5); - - // when & then - assertThatCode(() -> turn.validateCanMove(chuJolStartPosition, chuJolEndPosition)) - .doesNotThrowAnyException(); - } - - @Test - @DisplayName("현재 차례 팀의 잘못된 이동은 예외가 발생한다.") - void throwWhenMoveIsInvalid() { - // given - Turn turn = Turn.createInitialTurn(); - Position chuJolStartPosition = new Position(1, 4); - Position chuJolEndPosition = new Position(1, 6); - - // when & then - assertThatThrownBy(() -> turn.validateCanMove(chuJolStartPosition, chuJolEndPosition)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."); - } - - @Test - @DisplayName("기물을 이동하면 새 Turn을 반환한다.") - void move() { - // given - Turn turn = Turn.createInitialTurn(); - - // when - Turn movedTurn = turn.move(new Position(1, 4), new Position(1, 5)); - BoardSpot originSpot = turn.makeBoardSnapShot().value().get(new Position(1, 4)); - BoardSpot movedSpot = movedTurn.makeBoardSnapShot().value().get(new Position(1, 5)); - - // then - assertAll( - () -> assertThat(turn.nextTurnTeam()).isEqualTo("초나라"), - () -> assertThat(movedTurn.nextTurnTeam()).isEqualTo("한나라"), - () -> assertThat(originSpot).isNotNull(), - () -> assertThat(originSpot.position()).isEqualTo(new Position(1, 4)), - () -> assertThat(originSpot.pieceName()).isEqualTo("졸"), - () -> assertThat(movedSpot).isNotNull(), - () -> assertThat(movedSpot.position()).isEqualTo(new Position(1, 5)), - () -> assertThat(movedSpot.pieceName()).isEqualTo("졸") - ); - } -} From aa9d5721d0ff13fd86933cfb79095094bdceb87e Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Tue, 31 Mar 2026 21:20:22 +0900 Subject: [PATCH 77/99] =?UTF-8?q?refactor:=20BoardSpot=EC=97=90=EC=84=9C?= =?UTF-8?q?=20Position=20=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Pieces.java | 2 +- src/main/java/janggi/dto/BoardSpot.java | 8 +------- src/main/java/janggi/dto/BoardSpots.java | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java index 43c8024b22..1ac9ef029d 100644 --- a/src/main/java/janggi/domain/Pieces.java +++ b/src/main/java/janggi/domain/Pieces.java @@ -45,7 +45,7 @@ public Map makeSnapShot() { for (Map.Entry entry : value.entrySet()) { Position position = entry.getKey(); Piece piece = entry.getValue(); - snapShot.put(position, new BoardSpot(position, piece.name(), piece.getTeamType())); + snapShot.put(position, new BoardSpot(piece.name(), piece.getTeamType())); } return snapShot; } diff --git a/src/main/java/janggi/dto/BoardSpot.java b/src/main/java/janggi/dto/BoardSpot.java index 317d8d24ca..9fc3d268c3 100644 --- a/src/main/java/janggi/dto/BoardSpot.java +++ b/src/main/java/janggi/dto/BoardSpot.java @@ -1,12 +1,6 @@ package janggi.dto; -import janggi.domain.Position; import janggi.domain.side.TeamType; -public record BoardSpot( - Position position, - String pieceName, - TeamType teamType -) { - +public record BoardSpot(String pieceName, TeamType teamType) { } diff --git a/src/main/java/janggi/dto/BoardSpots.java b/src/main/java/janggi/dto/BoardSpots.java index 67e62c8da4..e896eb4806 100644 --- a/src/main/java/janggi/dto/BoardSpots.java +++ b/src/main/java/janggi/dto/BoardSpots.java @@ -3,8 +3,5 @@ import janggi.domain.Position; import java.util.Map; -public record BoardSpots( - Map value -) { - +public record BoardSpots(Map value) { } From e3d00ea5f00ece78a21e9135d9edea2195c52f45 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Wed, 1 Apr 2026 14:13:03 +0900 Subject: [PATCH 78/99] =?UTF-8?q?refactor:=20=EB=8F=99=EC=9D=BC=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?Position=20=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Position.java | 4 ++++ src/main/java/janggi/domain/piece/Cha.java | 6 +++--- src/main/java/janggi/domain/piece/Gung.java | 6 +++--- src/main/java/janggi/domain/piece/Jol.java | 6 +++--- src/main/java/janggi/domain/piece/Ma.java | 8 ++++++++ src/main/java/janggi/domain/piece/Po.java | 7 ++++++- src/main/java/janggi/domain/piece/Sa.java | 6 +++--- src/main/java/janggi/domain/piece/Sang.java | 8 ++++++++ src/test/java/janggi/domain/BoardTest.java | 8 ++++---- 9 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java index 663170789d..e7a1bad2d8 100644 --- a/src/main/java/janggi/domain/Position.java +++ b/src/main/java/janggi/domain/Position.java @@ -40,6 +40,10 @@ public Position move(Delta delta) { return new Position(x + delta.dx(), y + delta.dy()); } + public boolean isSamePosition(Position position) { + return x == position.getX() && y == position.getY(); + } + @Override public boolean equals(Object object) { if (this == object) { diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 1e781270a2..f0c2045522 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -61,7 +61,7 @@ private Optional findMovePath(Position start, Position end) { int endX = end.getX(); int endY = end.getY(); - if (isSamePosition(startX, startY, endX, endY)) { + if (isSamePosition(start, end)) { return Optional.empty(); } if (!isStraightDirection(startX, startY, endX, endY)) { @@ -83,8 +83,8 @@ private boolean isObstaclesNotExist(Position start, Position end, Board board) { .noneMatch(board::hasPiece); } - private boolean isSamePosition(int startX, int startY, int endX, int endY) { - return startX == endX && startY == endY; + private boolean isSamePosition(Position start, Position end) { + return start.isSamePosition(end); } private boolean isStraightDirection(int startX, int startY, int endX, int endY) { diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index 62c7c53ffa..7b431668ff 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -60,7 +60,7 @@ private Optional findMovePath(Position start, Position end) { int dy = end.getY() - start.getY(); int distanceX = Math.abs(dx); int distanceY = Math.abs(dy); - if (isSamePosition(distanceX, distanceY)) { + if (isSamePosition(start, end)) { return Optional.empty(); } if (!isOneStep(distanceX, distanceY)) { @@ -71,8 +71,8 @@ private Optional findMovePath(Position start, Position end) { .findFirst(); } - private boolean isSamePosition(int distanceX, int distanceY) { - return distanceX == 0 && distanceY == 0; + private boolean isSamePosition(Position start, Position end) { + return start.isSamePosition(end); } private boolean isOneStep(int distanceX, int distanceY) { diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 56c59e1585..55fc98eef7 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -49,7 +49,7 @@ private boolean isValidMovePattern(Position start, Position end) { private Optional findMovePath(Position start, Position end) { int dx = end.getX() - start.getX(); int dy = end.getY() - start.getY(); - if (isSamePosition(dx, dy)) { + if (isSamePosition(start, end)) { return Optional.empty(); } return paths.stream() @@ -57,8 +57,8 @@ private Optional findMovePath(Position start, Position end) { .findFirst(); } - private boolean isSamePosition(int distanceX, int distanceY) { - return distanceX == 0 && distanceY == 0; + private boolean isSamePosition(Position start, Position end) { + return start.isSamePosition(end); } private List createPaths() { diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 535cd0f728..d28411bef2 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -60,6 +60,10 @@ private boolean isValidMovePattern(Position start, Position end) { } private Optional findMovePath(Position start, Position end) { + if (isSamePosition(start, end)) { + return Optional.empty(); + } + int dx = end.getX() - start.getX(); int dy = end.getY() - start.getY(); return paths.stream() @@ -67,6 +71,10 @@ private Optional findMovePath(Position start, Position end) { .findFirst(); } + private boolean isSamePosition(Position start, Position end) { + return start.isSamePosition(end); + } + private boolean isObstaclesNotExist(Position startPosition, Position endPosition, Board board) { Optional movePath = findMovePath(startPosition, endPosition); if (movePath.isEmpty()) { diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index ae966132ed..2a665f4a93 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -59,9 +59,10 @@ private Optional findMovePath(Position start, Position end) { int endX = end.getX(); int endY = end.getY(); - if (startX == endX && startY == endY) { + if (isSamePosition(start, end)) { return Optional.empty(); } + int dx = endX - startX; int dy = endY - startY; return paths.stream() @@ -90,4 +91,8 @@ private void validateObstacles(Position start, Position end, Board board) { throw new IllegalArgumentException("포는 포를 넘을 수 없습니다."); } } + + private boolean isSamePosition(Position start, Position end) { + return start.isSamePosition(end); + } } diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index 26fa652fa8..ebb7a4c2e4 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -60,7 +60,7 @@ private Optional findMovePath(Position start, Position end) { int dy = end.getY() - start.getY(); int distanceX = Math.abs(dx); int distanceY = Math.abs(dy); - if (isSamePosition(distanceX, distanceY)) { + if (isSamePosition(start, end)) { return Optional.empty(); } if (!isOneStep(distanceX, distanceY)) { @@ -71,8 +71,8 @@ private Optional findMovePath(Position start, Position end) { .findFirst(); } - private boolean isSamePosition(int distanceX, int distanceY) { - return distanceX == 0 && distanceY == 0; + private boolean isSamePosition(Position start, Position end) { + return start.isSamePosition(end); } private boolean isOneStep(int distanceX, int distanceY) { diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index 449f581d09..4f58c3ecd8 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -60,6 +60,10 @@ private boolean isValidMovePattern(Position start, Position end) { } private Optional findMovePath(Position start, Position end) { + if (isSamePosition(start, end)) { + return Optional.empty(); + } + int dx = end.getX() - start.getX(); int dy = end.getY() - start.getY(); return paths.stream() @@ -67,6 +71,10 @@ private Optional findMovePath(Position start, Position end) { .findFirst(); } + private boolean isSamePosition(Position start, Position end) { + return start.isSamePosition(end); + } + private boolean isObstaclesNotExist(Position start, Position end, Board board) { Optional movePath = findMovePath(start, end); if (movePath.isEmpty()) { diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java index e6a1c49328..4ba1e1dc05 100644 --- a/src/test/java/janggi/domain/BoardTest.java +++ b/src/test/java/janggi/domain/BoardTest.java @@ -1,6 +1,7 @@ package janggi.domain; import janggi.domain.side.TeamType; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -22,16 +23,15 @@ void cannotMoveWhenEndPositionIsOutOfRange() { } @Test - @DisplayName("입력한 좌표에 기물이 없을 경우 예외 발생") + @DisplayName("입력한 좌표에 기물이 없을 경우 false 반환") void cannotMoveWhenStartPositionHasNoCurrentTeamPiece() { // given Board board = Board.createInitialBoard(); Position notExistPosition = new Position(2, 2); // when & then - assertThatThrownBy(() -> board.isPieceExists(notExistPosition, TeamType.CHU)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("입력한 위치에 기물이 없습니다."); + boolean pieceExists = board.isPieceExists(notExistPosition, TeamType.CHU); + Assertions.assertThat(pieceExists).isFalse(); } @Test From 8f0f014ef5dc193c27267486aed709cf88da5340 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Wed, 1 Apr 2026 14:31:07 +0900 Subject: [PATCH 79/99] =?UTF-8?q?refactor:=20=ED=95=9C=20=EC=B9=B8?= =?UTF-8?q?=EB=A7=8C=20=EA=B0=94=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20Position=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Position.java | 9 +++++++++ src/main/java/janggi/domain/piece/Cha.java | 9 ++++++--- src/main/java/janggi/domain/piece/Gung.java | 14 +++++++------- src/main/java/janggi/domain/piece/Jol.java | 6 ++++-- src/main/java/janggi/domain/piece/Ma.java | 1 + src/main/java/janggi/domain/piece/Po.java | 9 +++++---- src/main/java/janggi/domain/piece/Sa.java | 15 ++++++++------- src/main/java/janggi/domain/piece/Sang.java | 1 + 8 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java index e7a1bad2d8..b149dcf8a2 100644 --- a/src/main/java/janggi/domain/Position.java +++ b/src/main/java/janggi/domain/Position.java @@ -44,6 +44,15 @@ public boolean isSamePosition(Position position) { return x == position.getX() && y == position.getY(); } + public boolean isOneStep(Position position) { + int dx = position.getX() - x; + int dy = position.getY() - y; + int distanceX = Math.abs(dx); + int distanceY = Math.abs(dy); + + return distanceX <= 1 && distanceY <= 1; + } + @Override public boolean equals(Object object) { if (this == object) { diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index f0c2045522..9943596d65 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -56,19 +56,22 @@ private boolean isValidMovePattern(Position start, Position end) { } private Optional findMovePath(Position start, Position end) { + if (isSamePosition(start, end)) { + return Optional.empty(); + } + int startX = start.getX(); int startY = start.getY(); int endX = end.getX(); int endY = end.getY(); - if (isSamePosition(start, end)) { - return Optional.empty(); - } if (!isStraightDirection(startX, startY, endX, endY)) { return Optional.empty(); } + int dx = endX - startX; int dy = endY - startY; + return paths.stream() .filter(path -> path.matchesDirection(dx, dy)) .findFirst(); diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index 7b431668ff..935471336a 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -56,16 +56,16 @@ private boolean isValidMovePattern(Position start, Position end) { } private Optional findMovePath(Position start, Position end) { - int dx = end.getX() - start.getX(); - int dy = end.getY() - start.getY(); - int distanceX = Math.abs(dx); - int distanceY = Math.abs(dy); if (isSamePosition(start, end)) { return Optional.empty(); } - if (!isOneStep(distanceX, distanceY)) { + if (!isOneStep(start, end)) { return Optional.empty(); } + + int dx = end.getX() - start.getX(); + int dy = end.getY() - start.getY(); + return paths.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); @@ -75,7 +75,7 @@ private boolean isSamePosition(Position start, Position end) { return start.isSamePosition(end); } - private boolean isOneStep(int distanceX, int distanceY) { - return distanceX <= 1 && distanceY <= 1; + private boolean isOneStep(Position start, Position end) { + return start.isOneStep(end); } } diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 55fc98eef7..bebc3db4b5 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -47,11 +47,13 @@ private boolean isValidMovePattern(Position start, Position end) { } private Optional findMovePath(Position start, Position end) { - int dx = end.getX() - start.getX(); - int dy = end.getY() - start.getY(); if (isSamePosition(start, end)) { return Optional.empty(); } + + int dx = end.getX() - start.getX(); + int dy = end.getY() - start.getY(); + return paths.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index d28411bef2..4af06274e2 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -66,6 +66,7 @@ private Optional findMovePath(Position start, Position end) { int dx = end.getX() - start.getX(); int dy = end.getY() - start.getY(); + return paths.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index 2a665f4a93..f15d7364d5 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -54,17 +54,18 @@ private boolean isValidMovePattern(Position start, Position end) { } private Optional findMovePath(Position start, Position end) { + if (isSamePosition(start, end)) { + return Optional.empty(); + } + int startX = start.getX(); int startY = start.getY(); int endX = end.getX(); int endY = end.getY(); - if (isSamePosition(start, end)) { - return Optional.empty(); - } - int dx = endX - startX; int dy = endY - startY; + return paths.stream() .filter(path -> path.matchesDirection(dx, dy)) .findFirst(); diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index ebb7a4c2e4..d7b5f36cb2 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -56,16 +56,17 @@ private boolean isValidMovePattern(Position start, Position end) { } private Optional findMovePath(Position start, Position end) { - int dx = end.getX() - start.getX(); - int dy = end.getY() - start.getY(); - int distanceX = Math.abs(dx); - int distanceY = Math.abs(dy); if (isSamePosition(start, end)) { return Optional.empty(); } - if (!isOneStep(distanceX, distanceY)) { + + if (!isOneStep(start, end)) { return Optional.empty(); } + + int dx = end.getX() - start.getX(); + int dy = end.getY() - start.getY(); + return paths.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); @@ -75,7 +76,7 @@ private boolean isSamePosition(Position start, Position end) { return start.isSamePosition(end); } - private boolean isOneStep(int distanceX, int distanceY) { - return distanceX <= 1 && distanceY <= 1; + private boolean isOneStep(Position start, Position end) { + return start.isOneStep(end); } } diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index 4f58c3ecd8..e4e08cc630 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -66,6 +66,7 @@ private Optional findMovePath(Position start, Position end) { int dx = end.getX() - start.getX(); int dy = end.getY() - start.getY(); + return paths.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); From 95a3b059dec62e930f37d8bdf8ccc56efac6af05 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Wed, 1 Apr 2026 15:08:52 +0900 Subject: [PATCH 80/99] =?UTF-8?q?refactor:=20=EC=A7=81=EC=84=A0=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EC=B2=B4=ED=81=AC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?Position=20=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Position.java | 4 ++++ src/main/java/janggi/domain/piece/Cha.java | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java index b149dcf8a2..60921fd7e9 100644 --- a/src/main/java/janggi/domain/Position.java +++ b/src/main/java/janggi/domain/Position.java @@ -53,6 +53,10 @@ public boolean isOneStep(Position position) { return distanceX <= 1 && distanceY <= 1; } + public boolean isStraightDirection(Position position) { + return x == position.getX() || y == position.getY(); + } + @Override public boolean equals(Object object) { if (this == object) { diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 9943596d65..5ca478455e 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -60,15 +60,15 @@ private Optional findMovePath(Position start, Position end) { return Optional.empty(); } + if (!isStraightDirection(start, end)) { + return Optional.empty(); + } + int startX = start.getX(); int startY = start.getY(); int endX = end.getX(); int endY = end.getY(); - if (!isStraightDirection(startX, startY, endX, endY)) { - return Optional.empty(); - } - int dx = endX - startX; int dy = endY - startY; @@ -90,7 +90,7 @@ private boolean isSamePosition(Position start, Position end) { return start.isSamePosition(end); } - private boolean isStraightDirection(int startX, int startY, int endX, int endY) { - return startX == endX || startY == endY; + private boolean isStraightDirection(Position start, Position end) { + return start.isStraightDirection(end); } } From 604b71219aa4006c8792f24d16e452d793a8d823 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Wed, 1 Apr 2026 15:15:49 +0900 Subject: [PATCH 81/99] =?UTF-8?q?refactor:=20delta=20=EA=B3=84=EC=82=B0=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20Position=20=EA=B0=9D=EC=B2=B4=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Position.java | 8 ++++++++ src/main/java/janggi/domain/piece/Cha.java | 9 ++------- src/main/java/janggi/domain/piece/Gung.java | 4 ++-- src/main/java/janggi/domain/piece/Jol.java | 4 ++-- src/main/java/janggi/domain/piece/Ma.java | 4 ++-- src/main/java/janggi/domain/piece/Po.java | 9 ++------- src/main/java/janggi/domain/piece/Sa.java | 4 ++-- src/main/java/janggi/domain/piece/Sang.java | 4 ++-- 8 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java index 60921fd7e9..fe26dd11c4 100644 --- a/src/main/java/janggi/domain/Position.java +++ b/src/main/java/janggi/domain/Position.java @@ -57,6 +57,14 @@ public boolean isStraightDirection(Position position) { return x == position.getX() || y == position.getY(); } + public int deltaX(Position position) { + return position.getX() - x; + } + + public int deltaY(Position position) { + return position.getY() - y; + } + @Override public boolean equals(Object object) { if (this == object) { diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 5ca478455e..e8162397a9 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -64,13 +64,8 @@ private Optional findMovePath(Position start, Position end) { return Optional.empty(); } - int startX = start.getX(); - int startY = start.getY(); - int endX = end.getX(); - int endY = end.getY(); - - int dx = endX - startX; - int dy = endY - startY; + int dx = start.deltaX(end); + int dy = start.deltaY(end); return paths.stream() .filter(path -> path.matchesDirection(dx, dy)) diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index 935471336a..f86a387ee9 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -63,8 +63,8 @@ private Optional findMovePath(Position start, Position end) { return Optional.empty(); } - int dx = end.getX() - start.getX(); - int dy = end.getY() - start.getY(); + int dx = start.deltaX(end); + int dy = start.deltaY(end); return paths.stream() .filter(path -> path.matches(dx, dy)) diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index bebc3db4b5..231a86c5c2 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -51,8 +51,8 @@ private Optional findMovePath(Position start, Position end) { return Optional.empty(); } - int dx = end.getX() - start.getX(); - int dy = end.getY() - start.getY(); + int dx = start.deltaX(end); + int dy = start.deltaY(end); return paths.stream() .filter(path -> path.matches(dx, dy)) diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 4af06274e2..3ac1156e42 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -64,8 +64,8 @@ private Optional findMovePath(Position start, Position end) { return Optional.empty(); } - int dx = end.getX() - start.getX(); - int dy = end.getY() - start.getY(); + int dx = start.deltaX(end); + int dy = start.deltaY(end); return paths.stream() .filter(path -> path.matches(dx, dy)) diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index f15d7364d5..6bb394bb55 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -58,13 +58,8 @@ private Optional findMovePath(Position start, Position end) { return Optional.empty(); } - int startX = start.getX(); - int startY = start.getY(); - int endX = end.getX(); - int endY = end.getY(); - - int dx = endX - startX; - int dy = endY - startY; + int dx = start.deltaX(end); + int dy = start.deltaY(end); return paths.stream() .filter(path -> path.matchesDirection(dx, dy)) diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index d7b5f36cb2..c229f68d77 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -64,8 +64,8 @@ private Optional findMovePath(Position start, Position end) { return Optional.empty(); } - int dx = end.getX() - start.getX(); - int dy = end.getY() - start.getY(); + int dx = start.deltaX(end); + int dy = start.deltaY(end); return paths.stream() .filter(path -> path.matches(dx, dy)) diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index e4e08cc630..c85e09a67e 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -64,8 +64,8 @@ private Optional findMovePath(Position start, Position end) { return Optional.empty(); } - int dx = end.getX() - start.getX(); - int dy = end.getY() - start.getY(); + int dx = start.deltaX(end); + int dy = start.deltaY(end); return paths.stream() .filter(path -> path.matches(dx, dy)) From 53f1b21131c00714bdcd1cd4eb889befa55a459b Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Wed, 1 Apr 2026 18:08:07 +0900 Subject: [PATCH 82/99] =?UTF-8?q?refactor:=20Delta=EB=8A=94=20=EB=B0=94?= =?UTF-8?q?=EB=80=8C=EC=A7=80=20=EC=95=8A=EA=B8=B0=20=EB=95=8C=EB=AC=B8?= =?UTF-8?q?=EC=97=90=20enum=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=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/domain/Delta.java | 50 ++++++------------- src/main/java/janggi/domain/MovePath.java | 16 +++--- src/main/java/janggi/domain/Position.java | 2 +- src/test/java/janggi/domain/PositionTest.java | 2 +- 4 files changed, 24 insertions(+), 46 deletions(-) diff --git a/src/main/java/janggi/domain/Delta.java b/src/main/java/janggi/domain/Delta.java index edf9e2494e..ac4ded8320 100644 --- a/src/main/java/janggi/domain/Delta.java +++ b/src/main/java/janggi/domain/Delta.java @@ -1,52 +1,30 @@ package janggi.domain; -public class Delta { +public enum Delta { + + UP(0, 1), + DOWN(0, -1), + RIGHT(1, 0), + LEFT(-1, 0), + RIGHT_UP(1, 1), + LEFT_UP(-1, 1), + RIGHT_DOWN(1, -1), + LEFT_DOWN(-1, -1), + ; private final int dx; private final int dy; - private Delta(int dx, int dy) { + Delta(int dx, int dy) { this.dx = dx; this.dy = dy; } - public static Delta createUp() { - return new Delta(0, 1); - } - - public static Delta createDown() { - return new Delta(0, -1); - } - - public static Delta createRight() { - return new Delta(1, 0); - } - - public static Delta createLeft() { - return new Delta(-1, 0); - } - - public static Delta createLeftUp() { - return new Delta(-1, 1); - } - - public static Delta createRightUp() { - return new Delta(1, 1); - } - - public static Delta createLeftDown() { - return new Delta(-1, -1); - } - - public static Delta createRightDown() { - return new Delta(1, -1); - } - - public int dx() { + public int getDx() { return dx; } - public int dy() { + public int getDy() { return dy; } } diff --git a/src/main/java/janggi/domain/MovePath.java b/src/main/java/janggi/domain/MovePath.java index 1dbe99a795..9d09192e79 100644 --- a/src/main/java/janggi/domain/MovePath.java +++ b/src/main/java/janggi/domain/MovePath.java @@ -20,15 +20,15 @@ public boolean matchesDirection(int dx, int dy) { return false; } Delta delta = path.getFirst(); - if (delta.dx() == 0) { - return dx == 0 && Integer.signum(dy) == Integer.signum(delta.dy()) && dy != 0; + if (delta.getDx() == 0) { + return dx == 0 && Integer.signum(dy) == Integer.signum(delta.getDy()) && dy != 0; } - if (delta.dy() == 0) { - return dy == 0 && Integer.signum(dx) == Integer.signum(delta.dx()) && dx != 0; + if (delta.getDy() == 0) { + return dy == 0 && Integer.signum(dx) == Integer.signum(delta.getDx()) && dx != 0; } return Math.abs(dx) == Math.abs(dy) - && Integer.signum(dx) == Integer.signum(delta.dx()) - && Integer.signum(dy) == Integer.signum(delta.dy()); + && Integer.signum(dx) == Integer.signum(delta.getDx()) + && Integer.signum(dy) == Integer.signum(delta.getDy()); } public List createRoute(Position start, Position end) { @@ -55,13 +55,13 @@ public List intermediatePositions(Position start, Position end) { private int totalDx() { return path.stream() - .mapToInt(Delta::dx) + .mapToInt(Delta::getDx) .sum(); } private int totalDy() { return path.stream() - .mapToInt(Delta::dy) + .mapToInt(Delta::getDy) .sum(); } diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java index fe26dd11c4..9f74ee3cbb 100644 --- a/src/main/java/janggi/domain/Position.java +++ b/src/main/java/janggi/domain/Position.java @@ -37,7 +37,7 @@ public int getY() { } public Position move(Delta delta) { - return new Position(x + delta.dx(), y + delta.dy()); + return new Position(x + delta.getDx(), y + delta.getDy()); } public boolean isSamePosition(Position position) { diff --git a/src/test/java/janggi/domain/PositionTest.java b/src/test/java/janggi/domain/PositionTest.java index 68fb07465d..96891b529a 100644 --- a/src/test/java/janggi/domain/PositionTest.java +++ b/src/test/java/janggi/domain/PositionTest.java @@ -57,7 +57,7 @@ void move() { Position position = new Position(4, 4); // when - Position movedPosition = position.move(Delta.createRightUp()); + Position movedPosition = position.move(Delta.RIGHT_UP); // then assertAll( From d4aa3678a5229f829996d80f2ab9afbe51a7713f Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Wed, 1 Apr 2026 18:09:07 +0900 Subject: [PATCH 83/99] =?UTF-8?q?refactor:=20MovePath=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EB=A7=A4=EB=B2=88=20=EC=83=9D=EC=84=B1=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=83=81=EC=88=98=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=EB=A1=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/piece/Cha.java | 16 ++++++------ src/main/java/janggi/domain/piece/Gung.java | 22 ++++++++-------- src/main/java/janggi/domain/piece/Jol.java | 28 ++++++++++++--------- src/main/java/janggi/domain/piece/Ma.java | 22 ++++++++-------- src/main/java/janggi/domain/piece/Po.java | 14 ++++++----- src/main/java/janggi/domain/piece/Sa.java | 22 ++++++++-------- src/main/java/janggi/domain/piece/Sang.java | 22 ++++++++-------- 7 files changed, 81 insertions(+), 65 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index e8162397a9..99d9f35100 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -10,6 +10,13 @@ public class Cha implements Piece { + private static final List PATHS = List.of( + new MovePath(List.of(Delta.UP)), + new MovePath(List.of(Delta.DOWN)), + new MovePath(List.of(Delta.LEFT)), + new MovePath(List.of(Delta.RIGHT)) + ); + private final TeamType teamType; private final PieceType pieceType; private final List paths; @@ -17,12 +24,7 @@ public class Cha implements Piece { public Cha(TeamType teamType) { this.teamType = teamType; pieceType = PieceType.CHA; - paths = List.of( - new MovePath(List.of(Delta.createUp())), - new MovePath(List.of(Delta.createDown())), - new MovePath(List.of(Delta.createLeft())), - new MovePath(List.of(Delta.createRight())) - ); + paths = PATHS; } @Override @@ -64,7 +66,7 @@ private Optional findMovePath(Position start, Position end) { return Optional.empty(); } - int dx = start.deltaX(end); + int dx = start.deltaX(end); // deltaX 말고 더 알아듣기 쉬운 메서드명으로 수정 필요 int dy = start.deltaY(end); return paths.stream() diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index f86a387ee9..5558e5f689 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -10,6 +10,17 @@ public class Gung implements Piece { + private static final List PATHS = List.of( + new MovePath(List.of(Delta.UP)), + new MovePath(List.of(Delta.DOWN)), + new MovePath(List.of(Delta.RIGHT)), + new MovePath(List.of(Delta.LEFT)), + new MovePath(List.of(Delta.RIGHT_UP)), + new MovePath(List.of(Delta.RIGHT_DOWN)), + new MovePath(List.of(Delta.LEFT_UP)), + new MovePath(List.of(Delta.LEFT_DOWN)) + ); + private final TeamType teamType; private final PieceType pieceType; private final List paths; @@ -17,16 +28,7 @@ public class Gung implements Piece { public Gung(TeamType teamType) { this.teamType = teamType; pieceType = PieceType.GUNG; - paths = List.of( - new MovePath(List.of(Delta.createUp())), - new MovePath(List.of(Delta.createDown())), - new MovePath(List.of(Delta.createLeft())), - new MovePath(List.of(Delta.createRight())), - new MovePath(List.of(Delta.createRightUp())), - new MovePath(List.of(Delta.createRightDown())), - new MovePath(List.of(Delta.createLeftUp())), - new MovePath(List.of(Delta.createLeftDown())) - ); + paths = PATHS; } @Override diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 231a86c5c2..81d3e3abab 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -10,6 +10,18 @@ public class Jol implements Piece { + private static final List CHU_PATHS = List.of( + new MovePath(List.of(Delta.UP)), + new MovePath(List.of(Delta.LEFT)), + new MovePath(List.of(Delta.RIGHT)) + ); + + private static final List HAN_PATHS = List.of( + new MovePath(List.of(Delta.DOWN)), + new MovePath(List.of(Delta.LEFT)), + new MovePath(List.of(Delta.RIGHT)) + ); + private final TeamType teamType; private final PieceType pieceType; private final List paths; @@ -17,7 +29,7 @@ public class Jol implements Piece { public Jol(TeamType teamType) { this.teamType = teamType; pieceType = PieceType.JOL; - paths = createPaths(); + paths = selectPaths(teamType); } @Override @@ -63,18 +75,10 @@ private boolean isSamePosition(Position start, Position end) { return start.isSamePosition(end); } - private List createPaths() { + private List selectPaths(TeamType teamType) { if (teamType == TeamType.HAN) { - return List.of( - new MovePath(List.of(Delta.createDown())), - new MovePath(List.of(Delta.createLeft())), - new MovePath(List.of(Delta.createRight())) - ); + return HAN_PATHS; } - return List.of( - new MovePath(List.of(Delta.createUp())), - new MovePath(List.of(Delta.createLeft())), - new MovePath(List.of(Delta.createRight())) - ); + return CHU_PATHS; } } diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 3ac1156e42..3f560ef19e 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -10,6 +10,17 @@ public class Ma implements Piece { + private static final List PATHS = List.of( + new MovePath(List.of(Delta.UP, Delta.RIGHT_UP)), + new MovePath(List.of(Delta.UP, Delta.LEFT_UP)), + new MovePath(List.of(Delta.DOWN, Delta.RIGHT_DOWN)), + new MovePath(List.of(Delta.DOWN, Delta.LEFT_DOWN)), + new MovePath(List.of(Delta.LEFT, Delta.LEFT_UP)), + new MovePath(List.of(Delta.LEFT, Delta.LEFT_DOWN)), + new MovePath(List.of(Delta.RIGHT, Delta.RIGHT_UP)), + new MovePath(List.of(Delta.RIGHT, Delta.RIGHT_DOWN)) + ); + private final TeamType teamType; private final PieceType pieceType; private final List paths; @@ -17,16 +28,7 @@ public class Ma implements Piece { public Ma(TeamType teamType) { this.teamType = teamType; pieceType = PieceType.MA; - this.paths = List.of( - new MovePath(List.of(Delta.createUp(), Delta.createRightUp())), - new MovePath(List.of(Delta.createUp(), Delta.createLeftUp())), - new MovePath(List.of(Delta.createDown(), Delta.createRightDown())), - new MovePath(List.of(Delta.createDown(), Delta.createLeftDown())), - new MovePath(List.of(Delta.createLeft(), Delta.createLeftUp())), - new MovePath(List.of(Delta.createLeft(), Delta.createLeftDown())), - new MovePath(List.of(Delta.createRight(), Delta.createRightUp())), - new MovePath(List.of(Delta.createRight(), Delta.createRightDown())) - ); + this.paths = PATHS; } @Override diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index 6bb394bb55..503410425a 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -10,6 +10,13 @@ public class Po implements Piece { + private static final List PATHS = List.of( + new MovePath(List.of(Delta.UP)), + new MovePath(List.of(Delta.DOWN)), + new MovePath(List.of(Delta.LEFT)), + new MovePath(List.of(Delta.RIGHT)) + ); + private final TeamType teamType; private final PieceType pieceType; private final List paths; @@ -17,12 +24,7 @@ public class Po implements Piece { public Po(TeamType teamType) { this.teamType = teamType; pieceType = PieceType.PO; - paths = List.of( - new MovePath(List.of(Delta.createUp())), - new MovePath(List.of(Delta.createDown())), - new MovePath(List.of(Delta.createLeft())), - new MovePath(List.of(Delta.createRight())) - ); + paths = PATHS; } @Override diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index c229f68d77..f3bc990c3e 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -10,6 +10,17 @@ public class Sa implements Piece { + private static final List PATHS = List.of( + new MovePath(List.of(Delta.UP)), + new MovePath(List.of(Delta.DOWN)), + new MovePath(List.of(Delta.LEFT)), + new MovePath(List.of(Delta.RIGHT)), + new MovePath(List.of(Delta.RIGHT_UP)), + new MovePath(List.of(Delta.RIGHT_DOWN)), + new MovePath(List.of(Delta.LEFT_UP)), + new MovePath(List.of(Delta.LEFT_DOWN)) + ); + private final TeamType teamType; private final PieceType pieceType; private final List paths; @@ -17,16 +28,7 @@ public class Sa implements Piece { public Sa(TeamType teamType) { this.teamType = teamType; pieceType = PieceType.SA; - paths = List.of( - new MovePath(List.of(Delta.createUp())), - new MovePath(List.of(Delta.createDown())), - new MovePath(List.of(Delta.createLeft())), - new MovePath(List.of(Delta.createRight())), - new MovePath(List.of(Delta.createRightUp())), - new MovePath(List.of(Delta.createRightDown())), - new MovePath(List.of(Delta.createLeftUp())), - new MovePath(List.of(Delta.createLeftDown())) - ); + paths = PATHS; } @Override diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index c85e09a67e..801b58c0bb 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -10,6 +10,17 @@ public class Sang implements Piece { + private static final List PATHS = List.of( + new MovePath(List.of(Delta.UP, Delta.RIGHT_UP, Delta.RIGHT_UP)), + new MovePath(List.of(Delta.UP, Delta.LEFT_UP, Delta.LEFT_UP)), + new MovePath(List.of(Delta.DOWN, Delta.RIGHT_DOWN, Delta.RIGHT_DOWN)), + new MovePath(List.of(Delta.DOWN, Delta.LEFT_DOWN, Delta.LEFT_DOWN)), + new MovePath(List.of(Delta.LEFT, Delta.LEFT_UP, Delta.LEFT_UP)), + new MovePath(List.of(Delta.LEFT, Delta.LEFT_DOWN, Delta.LEFT_DOWN)), + new MovePath(List.of(Delta.RIGHT, Delta.RIGHT_UP, Delta.RIGHT_UP)), + new MovePath(List.of(Delta.RIGHT, Delta.RIGHT_DOWN, Delta.RIGHT_DOWN)) + ); + private final TeamType teamType; private final PieceType pieceType; private final List paths; @@ -17,16 +28,7 @@ public class Sang implements Piece { public Sang(TeamType teamType) { this.teamType = teamType; pieceType = PieceType.SANG; - paths = List.of( - new MovePath(List.of(Delta.createUp(), Delta.createRightUp(), Delta.createRightUp())), - new MovePath(List.of(Delta.createUp(), Delta.createLeftUp(), Delta.createLeftUp())), - new MovePath(List.of(Delta.createDown(), Delta.createRightDown(), Delta.createRightDown())), - new MovePath(List.of(Delta.createDown(), Delta.createLeftDown(), Delta.createLeftDown())), - new MovePath(List.of(Delta.createLeft(), Delta.createLeftUp(), Delta.createLeftUp())), - new MovePath(List.of(Delta.createLeft(), Delta.createLeftDown(), Delta.createLeftDown())), - new MovePath(List.of(Delta.createRight(), Delta.createRightUp(), Delta.createRightUp())), - new MovePath(List.of(Delta.createRight(), Delta.createRightDown(), Delta.createRightDown())) - ); + paths = PATHS; } @Override From 31c58d997a0a510d9932285e1333619ab7760673 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Wed, 1 Apr 2026 22:58:58 +0900 Subject: [PATCH 84/99] =?UTF-8?q?refactor:=20Piece=20interface=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=94=EC=83=81=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=ED=95=98=EC=97=AC=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B0=8F=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Board.java | 1 + src/main/java/janggi/domain/piece/Cha.java | 27 +++----------------- src/main/java/janggi/domain/piece/Gung.java | 27 +++----------------- src/main/java/janggi/domain/piece/Jol.java | 27 +++----------------- src/main/java/janggi/domain/piece/Ma.java | 27 +++----------------- src/main/java/janggi/domain/piece/Piece.java | 24 +++++++++++++---- src/main/java/janggi/domain/piece/Po.java | 27 +++----------------- src/main/java/janggi/domain/piece/Sa.java | 27 +++----------------- src/main/java/janggi/domain/piece/Sang.java | 27 +++----------------- 9 files changed, 41 insertions(+), 173 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index f47db672cd..be94d86e07 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -7,6 +7,7 @@ import janggi.domain.side.TeamType; import janggi.dto.BoardSpot; import janggi.dto.BoardSpots; + import java.util.HashMap; import java.util.Optional; diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 99d9f35100..433c1a34d5 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Optional; -public class Cha implements Piece { +public class Cha extends Piece { private static final List PATHS = List.of( new MovePath(List.of(Delta.UP)), @@ -17,14 +17,8 @@ public class Cha implements Piece { new MovePath(List.of(Delta.RIGHT)) ); - private final TeamType teamType; - private final PieceType pieceType; - private final List paths; - public Cha(TeamType teamType) { - this.teamType = teamType; - pieceType = PieceType.CHA; - paths = PATHS; + super(teamType, PieceType.CHA); } @Override @@ -38,21 +32,6 @@ public void validateCanMove(Position start, Position end, Board board) { } } - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } - private boolean isValidMovePattern(Position start, Position end) { return findMovePath(start, end).isPresent(); } @@ -69,7 +48,7 @@ private Optional findMovePath(Position start, Position end) { int dx = start.deltaX(end); // deltaX 말고 더 알아듣기 쉬운 메서드명으로 수정 필요 int dy = start.deltaY(end); - return paths.stream() + return PATHS.stream() .filter(path -> path.matchesDirection(dx, dy)) .findFirst(); } diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index 5558e5f689..a9eb020045 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Optional; -public class Gung implements Piece { +public class Gung extends Piece { private static final List PATHS = List.of( new MovePath(List.of(Delta.UP)), @@ -21,14 +21,8 @@ public class Gung implements Piece { new MovePath(List.of(Delta.LEFT_DOWN)) ); - private final TeamType teamType; - private final PieceType pieceType; - private final List paths; - public Gung(TeamType teamType) { - this.teamType = teamType; - pieceType = PieceType.GUNG; - paths = PATHS; + super(teamType, PieceType.GUNG); } @Override @@ -38,21 +32,6 @@ public void validateCanMove(Position start, Position end, Board board) { } } - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } - private boolean isValidMovePattern(Position start, Position end) { return findMovePath(start, end).isPresent(); } @@ -68,7 +47,7 @@ private Optional findMovePath(Position start, Position end) { int dx = start.deltaX(end); int dy = start.deltaY(end); - return paths.stream() + return PATHS.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); } diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 81d3e3abab..9562d41515 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Optional; -public class Jol implements Piece { +public class Jol extends Piece { private static final List CHU_PATHS = List.of( new MovePath(List.of(Delta.UP)), @@ -22,14 +22,8 @@ public class Jol implements Piece { new MovePath(List.of(Delta.RIGHT)) ); - private final TeamType teamType; - private final PieceType pieceType; - private final List paths; - public Jol(TeamType teamType) { - this.teamType = teamType; - pieceType = PieceType.JOL; - paths = selectPaths(teamType); + super(teamType, PieceType.JOL); } @Override @@ -39,21 +33,6 @@ public void validateCanMove(Position start, Position end, Board board) { } } - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } - private boolean isValidMovePattern(Position start, Position end) { return findMovePath(start, end).isPresent(); } @@ -66,7 +45,7 @@ private Optional findMovePath(Position start, Position end) { int dx = start.deltaX(end); int dy = start.deltaY(end); - return paths.stream() + return selectPaths(getTeamType()).stream() .filter(path -> path.matches(dx, dy)) .findFirst(); } diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 3f560ef19e..40b30dfe5f 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Optional; -public class Ma implements Piece { +public class Ma extends Piece { private static final List PATHS = List.of( new MovePath(List.of(Delta.UP, Delta.RIGHT_UP)), @@ -21,14 +21,8 @@ public class Ma implements Piece { new MovePath(List.of(Delta.RIGHT, Delta.RIGHT_DOWN)) ); - private final TeamType teamType; - private final PieceType pieceType; - private final List paths; - public Ma(TeamType teamType) { - this.teamType = teamType; - pieceType = PieceType.MA; - this.paths = PATHS; + super(teamType, PieceType.MA); } @Override @@ -42,21 +36,6 @@ public void validateCanMove(Position start, Position end, Board board) { } } - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } - private boolean isValidMovePattern(Position start, Position end) { return findMovePath(start, end).isPresent(); } @@ -69,7 +48,7 @@ private Optional findMovePath(Position start, Position end) { int dx = start.deltaX(end); int dy = start.deltaY(end); - return paths.stream() + return PATHS.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); } diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index 179c1550f8..208a076d0a 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -4,13 +4,27 @@ import janggi.domain.Position; import janggi.domain.side.TeamType; -public interface Piece { +public abstract class Piece { - void validateCanMove(Position start, Position end, Board board); + private final TeamType teamType; + private final PieceType pieceType; - String name(); + public Piece(TeamType teamType, PieceType pieceType) { + this.teamType = teamType; + this.pieceType = pieceType; + } - PieceType getPieceType(); + public String name() { + return pieceType.getName(); + } - TeamType getTeamType(); + public PieceType getPieceType() { + return pieceType; + } + + public TeamType getTeamType() { + return teamType; + } + + public abstract void validateCanMove(Position start, Position end, Board board); } diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index 503410425a..fafa99eff9 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Optional; -public class Po implements Piece { +public class Po extends Piece { private static final List PATHS = List.of( new MovePath(List.of(Delta.UP)), @@ -17,14 +17,8 @@ public class Po implements Piece { new MovePath(List.of(Delta.RIGHT)) ); - private final TeamType teamType; - private final PieceType pieceType; - private final List paths; - public Po(TeamType teamType) { - this.teamType = teamType; - pieceType = PieceType.PO; - paths = PATHS; + super(teamType, PieceType.PO); } @Override @@ -36,21 +30,6 @@ public void validateCanMove(Position start, Position end, Board board) { validateObstacles(start, end, board); } - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } - private boolean isValidMovePattern(Position start, Position end) { return findMovePath(start, end).isPresent(); } @@ -63,7 +42,7 @@ private Optional findMovePath(Position start, Position end) { int dx = start.deltaX(end); int dy = start.deltaY(end); - return paths.stream() + return PATHS.stream() .filter(path -> path.matchesDirection(dx, dy)) .findFirst(); } diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index f3bc990c3e..266d6727e9 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Optional; -public class Sa implements Piece { +public class Sa extends Piece { private static final List PATHS = List.of( new MovePath(List.of(Delta.UP)), @@ -21,14 +21,8 @@ public class Sa implements Piece { new MovePath(List.of(Delta.LEFT_DOWN)) ); - private final TeamType teamType; - private final PieceType pieceType; - private final List paths; - public Sa(TeamType teamType) { - this.teamType = teamType; - pieceType = PieceType.SA; - paths = PATHS; + super(teamType, PieceType.SA); } @Override @@ -38,21 +32,6 @@ public void validateCanMove(Position start, Position end, Board board) { } } - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } - private boolean isValidMovePattern(Position start, Position end) { return findMovePath(start, end).isPresent(); } @@ -69,7 +48,7 @@ private Optional findMovePath(Position start, Position end) { int dx = start.deltaX(end); int dy = start.deltaY(end); - return paths.stream() + return PATHS.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); } diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index 801b58c0bb..3c506d30ff 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Optional; -public class Sang implements Piece { +public class Sang extends Piece { private static final List PATHS = List.of( new MovePath(List.of(Delta.UP, Delta.RIGHT_UP, Delta.RIGHT_UP)), @@ -21,14 +21,8 @@ public class Sang implements Piece { new MovePath(List.of(Delta.RIGHT, Delta.RIGHT_DOWN, Delta.RIGHT_DOWN)) ); - private final TeamType teamType; - private final PieceType pieceType; - private final List paths; - public Sang(TeamType teamType) { - this.teamType = teamType; - pieceType = PieceType.SANG; - paths = PATHS; + super(teamType, PieceType.SANG); } @Override @@ -42,21 +36,6 @@ public void validateCanMove(Position start, Position end, Board board) { } } - @Override - public String name() { - return pieceType.getName(); - } - - @Override - public PieceType getPieceType() { - return pieceType; - } - - @Override - public TeamType getTeamType() { - return teamType; - } - private boolean isValidMovePattern(Position start, Position end) { return findMovePath(start, end).isPresent(); } @@ -69,7 +48,7 @@ private Optional findMovePath(Position start, Position end) { int dx = start.deltaX(end); int dy = start.deltaY(end); - return paths.stream() + return PATHS.stream() .filter(path -> path.matches(dx, dy)) .findFirst(); } From dfac78ccba8bfe83381b66e31bc0c571542ca948 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 11:06:38 +0900 Subject: [PATCH 85/99] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=88=9C=EC=84=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/domain/piece/Cha.java | 18 +++++++------- src/main/java/janggi/domain/piece/Ma.java | 18 +++++++------- src/main/java/janggi/domain/piece/Po.java | 26 ++++++++++----------- src/main/java/janggi/domain/piece/Sang.java | 18 +++++++------- 4 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 433c1a34d5..5b33f718e8 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -36,6 +36,15 @@ private boolean isValidMovePattern(Position start, Position end) { return findMovePath(start, end).isPresent(); } + private boolean isObstaclesNotExist(Position start, Position end, Board board) { + Optional movePath = findMovePath(start, end); + if (movePath.isEmpty()) { + return false; + } + return movePath.get().intermediatePositions(start, end).stream() + .noneMatch(board::hasPiece); + } + private Optional findMovePath(Position start, Position end) { if (isSamePosition(start, end)) { return Optional.empty(); @@ -53,15 +62,6 @@ private Optional findMovePath(Position start, Position end) { .findFirst(); } - private boolean isObstaclesNotExist(Position start, Position end, Board board) { - Optional movePath = findMovePath(start, end); - if (movePath.isEmpty()) { - return false; - } - return movePath.get().intermediatePositions(start, end).stream() - .noneMatch(board::hasPiece); - } - private boolean isSamePosition(Position start, Position end) { return start.isSamePosition(end); } diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 40b30dfe5f..ae2b5524b7 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -40,6 +40,15 @@ private boolean isValidMovePattern(Position start, Position end) { return findMovePath(start, end).isPresent(); } + private boolean isObstaclesNotExist(Position startPosition, Position endPosition, Board board) { + Optional movePath = findMovePath(startPosition, endPosition); + if (movePath.isEmpty()) { + return false; + } + return movePath.get().intermediatePositions(startPosition, endPosition).stream() + .noneMatch(board::hasPiece); + } + private Optional findMovePath(Position start, Position end) { if (isSamePosition(start, end)) { return Optional.empty(); @@ -56,13 +65,4 @@ private Optional findMovePath(Position start, Position end) { private boolean isSamePosition(Position start, Position end) { return start.isSamePosition(end); } - - private boolean isObstaclesNotExist(Position startPosition, Position endPosition, Board board) { - Optional movePath = findMovePath(startPosition, endPosition); - if (movePath.isEmpty()) { - return false; - } - return movePath.get().intermediatePositions(startPosition, endPosition).stream() - .noneMatch(board::hasPiece); - } } diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index fafa99eff9..726495a062 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -34,19 +34,6 @@ private boolean isValidMovePattern(Position start, Position end) { return findMovePath(start, end).isPresent(); } - private Optional findMovePath(Position start, Position end) { - if (isSamePosition(start, end)) { - return Optional.empty(); - } - - int dx = start.deltaX(end); - int dy = start.deltaY(end); - - return PATHS.stream() - .filter(path -> path.matchesDirection(dx, dy)) - .findFirst(); - } - private void validateObstacles(Position start, Position end, Board board) { Optional movePath = findMovePath(start, end); if (movePath.isEmpty()) { @@ -69,6 +56,19 @@ private void validateObstacles(Position start, Position end, Board board) { } } + private Optional findMovePath(Position start, Position end) { + if (isSamePosition(start, end)) { + return Optional.empty(); + } + + int dx = start.deltaX(end); + int dy = start.deltaY(end); + + return PATHS.stream() + .filter(path -> path.matchesDirection(dx, dy)) + .findFirst(); + } + private boolean isSamePosition(Position start, Position end) { return start.isSamePosition(end); } diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index 3c506d30ff..f1f9d1265c 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -40,6 +40,15 @@ private boolean isValidMovePattern(Position start, Position end) { return findMovePath(start, end).isPresent(); } + private boolean isObstaclesNotExist(Position start, Position end, Board board) { + Optional movePath = findMovePath(start, end); + if (movePath.isEmpty()) { + return false; + } + return movePath.get().intermediatePositions(start, end).stream() + .noneMatch(board::hasPiece); + } + private Optional findMovePath(Position start, Position end) { if (isSamePosition(start, end)) { return Optional.empty(); @@ -56,13 +65,4 @@ private Optional findMovePath(Position start, Position end) { private boolean isSamePosition(Position start, Position end) { return start.isSamePosition(end); } - - private boolean isObstaclesNotExist(Position start, Position end, Board board) { - Optional movePath = findMovePath(start, end); - if (movePath.isEmpty()) { - return false; - } - return movePath.get().intermediatePositions(start, end).stream() - .noneMatch(board::hasPiece); - } } From 6fbbd6e567ef00c08b8af7ff7f59c2207a56e7a9 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 11:19:07 +0900 Subject: [PATCH 86/99] =?UTF-8?q?refactor:=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=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/domain/piece/Po.java | 2 +- src/test/java/janggi/domain/piece/PoTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index 726495a062..6f6a169f64 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -49,7 +49,7 @@ private void validateObstacles(Position start, Position end, Board board) { throw new IllegalArgumentException("이동 경로에 기물이 존재하지 않아 이동할 수 없습니다."); } if (obstacles.size() > 1) { - throw new IllegalArgumentException("이동 경로에 기물이 1개 이상 존재합니다."); + throw new IllegalArgumentException("이동 경로에 기물이 2개 이상 존재합니다."); } if (obstacles.getFirst().getPieceType() == PieceType.PO) { throw new IllegalArgumentException("포는 포를 넘을 수 없습니다."); diff --git a/src/test/java/janggi/domain/piece/PoTest.java b/src/test/java/janggi/domain/piece/PoTest.java index dccc65b73d..6a8112aa4f 100644 --- a/src/test/java/janggi/domain/piece/PoTest.java +++ b/src/test/java/janggi/domain/piece/PoTest.java @@ -75,7 +75,7 @@ void validateCanMove_Fail_TooManyObstacles() { // when & then assertThatThrownBy(() -> po.validateCanMove(start, end, board)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동 경로에 기물이 1개 이상 존재합니다."); + .hasMessage("이동 경로에 기물이 2개 이상 존재합니다."); } @Test From 871c4695bfe72c8abfaecc64d745ece34b68ab9c Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 16:10:56 +0900 Subject: [PATCH 87/99] =?UTF-8?q?chore:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=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/Board.java | 4 +- src/test/java/janggi/domain/piece/MaTest.java | 89 ------------------- 2 files changed, 2 insertions(+), 91 deletions(-) delete mode 100644 src/test/java/janggi/domain/piece/MaTest.java diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index be94d86e07..a0cc672993 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -48,8 +48,8 @@ public String getPieceName(Position position, TeamType currentTeamType) { public void validateCanMove(Position start, Position end, TeamType currentTeamType) { validateRange(end); + validateTeamPieceExistsAtEnd(currentTeam(currentTeamType), end); Piece piece = findTeamPiece(start, currentTeam(currentTeamType)); - validateEndPosition(currentTeam(currentTeamType), end); piece.validateCanMove(start, end, this); } @@ -99,7 +99,7 @@ private Piece findTeamPiece(Position position, Team currentTeam) { .orElseThrow(() -> new IllegalArgumentException("입력한 위치에 기물이 없습니다.")); } - private void validateEndPosition(Team team, Position end) { + private void validateTeamPieceExistsAtEnd(Team team, Position end) { if (team.isPieceExists(end)) { throw new IllegalArgumentException("아군이 존재하는 좌표로는 이동할 수 없습니다."); } diff --git a/src/test/java/janggi/domain/piece/MaTest.java b/src/test/java/janggi/domain/piece/MaTest.java deleted file mode 100644 index c9cf9875df..0000000000 --- a/src/test/java/janggi/domain/piece/MaTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package janggi.domain.piece; - -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - -import janggi.domain.Board; -import janggi.domain.Position; -import janggi.domain.side.TeamType; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -class MaTest { - - private Ma ma; - private Board board; - - @BeforeEach - void setUp() { - board = Board.createInitialBoard(); - ma = new Ma(TeamType.HAN); - } - - @ParameterizedTest - @DisplayName("마는 이동 경로(멱)에 장애물이 없다면 L자 모양으로 이동할 수 있다.") - @CsvSource({ - "4, 4, 5, 6", - "4, 4, 3, 6", - "4, 4, 5, 2", - "4, 4, 3, 2" - }) - void validateCanMove_Success(int startX, int startY, int endX, int endY) { - // given - Position start = new Position(startX, startY); - Position end = new Position(endX, endY); - - // when & then - assertThatCode(() -> ma.validateCanMove(start, end, board)) - .doesNotThrowAnyException(); - } - - @Test - @DisplayName("마의 행마법(L자)이 아니거나 제자리로 이동할 경우 예외 발생") - void validateCanMove_Fail_InvalidPattern() { - // given - Position start = new Position(4, 4); - Position straightEnd = new Position(4, 6); - Position diagonalEnd = new Position(5, 5); - Position longEnd = new Position(6, 7); - - // when & then - assertAll( - () -> assertThatThrownBy(() -> ma.validateCanMove(start, straightEnd, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> ma.validateCanMove(start, diagonalEnd, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> ma.validateCanMove(start, longEnd, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> ma.validateCanMove(start, start, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다.") - ); - } - - @ParameterizedTest - @DisplayName("마의 멱(직선 방향의 첫 칸)에 기물이 존재할 경우 예외 발생") - @CsvSource({ - "4, 4, 6, 5", - "4, 4, 6, 3", - "4, 4, 2, 5", - "4, 4, 2, 3" - }) - void validateCanMove_Fail_ObstacleExist(int startX, int startY, int endX, int endY) { - // given - Position start = new Position(startX, startY); - Position end = new Position(endX, endY); - - // when & then - assertThatThrownBy(() -> ma.validateCanMove(start, end, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); - } -} \ No newline at end of file From 3ee750cd0ca66b47bc04eb74bedc77fdcb60255e Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 16:11:18 +0900 Subject: [PATCH 88/99] =?UTF-8?q?refactor:=20=EA=B8=B0=EB=AC=BC=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EA=B0=80=EB=8A=A5=20=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=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 | 48 ++++------ src/main/java/janggi/domain/piece/Gung.java | 27 ++---- src/main/java/janggi/domain/piece/Jol.java | 21 ++--- src/main/java/janggi/domain/piece/Ma.java | 41 ++++----- .../janggi/domain/piece/OneStepPiece.java | 17 ++++ src/main/java/janggi/domain/piece/Po.java | 46 ++++------ src/main/java/janggi/domain/piece/Sa.java | 30 ++----- src/main/java/janggi/domain/piece/Sang.java | 40 ++++----- .../java/janggi/domain/piece/ChaTest.java | 21 +++-- .../java/janggi/domain/piece/GungTest.java | 26 +++--- .../java/janggi/domain/piece/JolTest.java | 37 +++++--- src/test/java/janggi/domain/piece/MaTest.java | 88 +++++++++++++++++++ src/test/java/janggi/domain/piece/PoTest.java | 33 ++++--- src/test/java/janggi/domain/piece/SaTest.java | 29 +++--- .../java/janggi/domain/piece/SangTest.java | 25 ++++-- 15 files changed, 297 insertions(+), 232 deletions(-) create mode 100644 src/main/java/janggi/domain/piece/OneStepPiece.java create mode 100644 src/test/java/janggi/domain/piece/MaTest.java diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 5b33f718e8..0a3c3c3d59 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -5,8 +5,8 @@ import janggi.domain.MovePath; import janggi.domain.Position; import janggi.domain.side.TeamType; + import java.util.List; -import java.util.Optional; public class Cha extends Piece { @@ -23,50 +23,32 @@ public Cha(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { - if (!isValidMovePattern(start, end)) { - throw new IllegalArgumentException("이동할 수 없는 위치입니다."); - } - - if (!isObstaclesNotExist(start, end, board)) { - throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); - } - } - - private boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); - } - - private boolean isObstaclesNotExist(Position start, Position end, Board board) { - Optional movePath = findMovePath(start, end); - if (movePath.isEmpty()) { - return false; - } - return movePath.get().intermediatePositions(start, end).stream() - .noneMatch(board::hasPiece); + MovePath movePath = findMovePath(start, end); + validatePieceInPath(movePath, start, end, board); } - private Optional findMovePath(Position start, Position end) { + private MovePath findMovePath(Position start, Position end) { if (isSamePosition(start, end)) { - return Optional.empty(); - } - - if (!isStraightDirection(start, end)) { - return Optional.empty(); + throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); } int dx = start.deltaX(end); // deltaX 말고 더 알아듣기 쉬운 메서드명으로 수정 필요 int dy = start.deltaY(end); return PATHS.stream() - .filter(path -> path.matchesDirection(dx, dy)) - .findFirst(); + .filter(path -> path.matchesDirection(dx, dy)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); } - private boolean isSamePosition(Position start, Position end) { - return start.isSamePosition(end); + private void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { + if (movePath.intermediatePositions(start, end).stream() + .anyMatch(board::hasPiece)) { + throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); + } } - private boolean isStraightDirection(Position start, Position end) { - return start.isStraightDirection(end); + private boolean isSamePosition(Position start, Position end) { + return start.isSamePosition(end); } } diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index a9eb020045..6fc4b7452e 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -5,8 +5,8 @@ import janggi.domain.MovePath; import janggi.domain.Position; import janggi.domain.side.TeamType; + import java.util.List; -import java.util.Optional; public class Gung extends Piece { @@ -27,36 +27,21 @@ public Gung(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { - if (!isValidMovePattern(start, end)) { - throw new IllegalArgumentException("이동할 수 없는 위치입니다."); - } - } - - private boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); - } - - private Optional findMovePath(Position start, Position end) { if (isSamePosition(start, end)) { - return Optional.empty(); - } - if (!isOneStep(start, end)) { - return Optional.empty(); + throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); } int dx = start.deltaX(end); int dy = start.deltaY(end); - return PATHS.stream() - .filter(path -> path.matches(dx, dy)) - .findFirst(); + PATHS.stream() + .filter(path -> path.matches(dx, dy)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); } private boolean isSamePosition(Position start, Position end) { return start.isSamePosition(end); } - private boolean isOneStep(Position start, Position end) { - return start.isOneStep(end); - } } diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 9562d41515..5e49d49a54 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -5,8 +5,8 @@ import janggi.domain.MovePath; import janggi.domain.Position; import janggi.domain.side.TeamType; + import java.util.List; -import java.util.Optional; public class Jol extends Piece { @@ -28,26 +28,17 @@ public Jol(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { - if (!isValidMovePattern(start, end)) { - throw new IllegalArgumentException("이동할 수 없는 위치입니다."); - } - } - - private boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); - } - - private Optional findMovePath(Position start, Position end) { if (isSamePosition(start, end)) { - return Optional.empty(); + throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); } int dx = start.deltaX(end); int dy = start.deltaY(end); - return selectPaths(getTeamType()).stream() - .filter(path -> path.matches(dx, dy)) - .findFirst(); + selectPaths(getTeamType()).stream() + .filter(path -> path.matches(dx, dy)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); } private boolean isSamePosition(Position start, Position end) { diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index ae2b5524b7..1a7151f8cf 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -5,8 +5,8 @@ import janggi.domain.MovePath; import janggi.domain.Position; import janggi.domain.side.TeamType; + import java.util.List; -import java.util.Optional; public class Ma extends Piece { @@ -27,39 +27,30 @@ public Ma(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { - if (!isValidMovePattern(start, end)) { - throw new IllegalArgumentException("이동할 수 없는 위치입니다."); - } - - if (!isObstaclesNotExist(start, end, board)) { - throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); - } - } - - private boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); + MovePath movePath = findMovePath(start, end); + validatePieceInPath(movePath, start, end, board); } - private boolean isObstaclesNotExist(Position startPosition, Position endPosition, Board board) { - Optional movePath = findMovePath(startPosition, endPosition); - if (movePath.isEmpty()) { - return false; - } - return movePath.get().intermediatePositions(startPosition, endPosition).stream() - .noneMatch(board::hasPiece); - } - - private Optional findMovePath(Position start, Position end) { + private MovePath findMovePath(Position start, Position end) { if (isSamePosition(start, end)) { - return Optional.empty(); + throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); } int dx = start.deltaX(end); int dy = start.deltaY(end); return PATHS.stream() - .filter(path -> path.matches(dx, dy)) - .findFirst(); + .filter(path -> path.matches(dx, dy)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); + + } + + private void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { + if (movePath.intermediatePositions(start, end).stream() + .anyMatch(board::hasPiece)) { + throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); + } } private boolean isSamePosition(Position start, Position end) { diff --git a/src/main/java/janggi/domain/piece/OneStepPiece.java b/src/main/java/janggi/domain/piece/OneStepPiece.java new file mode 100644 index 0000000000..25c9c76aef --- /dev/null +++ b/src/main/java/janggi/domain/piece/OneStepPiece.java @@ -0,0 +1,17 @@ +package janggi.domain.piece; + +import janggi.domain.Board; +import janggi.domain.Position; +import janggi.domain.side.TeamType; + +public abstract class OneStepPiece extends Piece{ + + public OneStepPiece(TeamType teamType, PieceType pieceType) { + super(teamType, pieceType); + } + + @Override + public void validateCanMove(Position start, Position end, Board board) { + + } +} diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index 6f6a169f64..07c161949f 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -23,23 +23,30 @@ public Po(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { - if (!isValidMovePattern(start, end)) { - throw new IllegalArgumentException("이동할 수 없는 위치입니다."); + MovePath movePath = findMovePath(start, end); + validateObstacles(movePath, start, end, board); + } + + private MovePath findMovePath(Position start, Position end) { + if (isSamePosition(start, end)) { + throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); } - validateObstacles(start, end, board); + int dx = start.deltaX(end); + int dy = start.deltaY(end); + + return PATHS.stream() + .filter(path -> path.matchesDirection(dx, dy)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); } - private boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); + private boolean isSamePosition(Position start, Position end) { + return start.isSamePosition(end); } - private void validateObstacles(Position start, Position end, Board board) { - Optional movePath = findMovePath(start, end); - if (movePath.isEmpty()) { - throw new IllegalArgumentException("이동할 수 없는 위치입니다."); - } - List intermediatePositions = movePath.get().intermediatePositions(start, end); + private void validateObstacles(MovePath movePath, Position start, Position end, Board board) { + List intermediatePositions = movePath.intermediatePositions(start, end); List obstacles = intermediatePositions.stream() .map(board::findPiece) .filter(Optional::isPresent) @@ -55,21 +62,4 @@ private void validateObstacles(Position start, Position end, Board board) { throw new IllegalArgumentException("포는 포를 넘을 수 없습니다."); } } - - private Optional findMovePath(Position start, Position end) { - if (isSamePosition(start, end)) { - return Optional.empty(); - } - - int dx = start.deltaX(end); - int dy = start.deltaY(end); - - return PATHS.stream() - .filter(path -> path.matchesDirection(dx, dy)) - .findFirst(); - } - - private boolean isSamePosition(Position start, Position end) { - return start.isSamePosition(end); - } } diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index 266d6727e9..e5bf2bb30f 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -5,8 +5,8 @@ import janggi.domain.MovePath; import janggi.domain.Position; import janggi.domain.side.TeamType; + import java.util.List; -import java.util.Optional; public class Sa extends Piece { @@ -27,37 +27,21 @@ public Sa(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { - if (!isValidMovePattern(start, end)) { - throw new IllegalArgumentException("이동할 수 없는 위치입니다."); - } - } - - private boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); - } - - private Optional findMovePath(Position start, Position end) { if (isSamePosition(start, end)) { - return Optional.empty(); - } - - if (!isOneStep(start, end)) { - return Optional.empty(); + throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); } int dx = start.deltaX(end); int dy = start.deltaY(end); - return PATHS.stream() - .filter(path -> path.matches(dx, dy)) - .findFirst(); + PATHS.stream() + .filter(path -> path.matches(dx, dy)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); + } private boolean isSamePosition(Position start, Position end) { return start.isSamePosition(end); } - - private boolean isOneStep(Position start, Position end) { - return start.isOneStep(end); - } } diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index f1f9d1265c..aaad0a4a6e 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -5,8 +5,8 @@ import janggi.domain.MovePath; import janggi.domain.Position; import janggi.domain.side.TeamType; + import java.util.List; -import java.util.Optional; public class Sang extends Piece { @@ -27,39 +27,29 @@ public Sang(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { - if (!isValidMovePattern(start, end)) { - throw new IllegalArgumentException("이동할 수 없는 위치입니다."); - } - - if (!isObstaclesNotExist(start, end, board)) { - throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); - } - } - - private boolean isValidMovePattern(Position start, Position end) { - return findMovePath(start, end).isPresent(); + MovePath movePath = findMovePath(start, end); + validatePieceInPath(movePath, start, end, board); } - private boolean isObstaclesNotExist(Position start, Position end, Board board) { - Optional movePath = findMovePath(start, end); - if (movePath.isEmpty()) { - return false; - } - return movePath.get().intermediatePositions(start, end).stream() - .noneMatch(board::hasPiece); - } - - private Optional findMovePath(Position start, Position end) { + private MovePath findMovePath(Position start, Position end) { if (isSamePosition(start, end)) { - return Optional.empty(); + throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); } int dx = start.deltaX(end); int dy = start.deltaY(end); return PATHS.stream() - .filter(path -> path.matches(dx, dy)) - .findFirst(); + .filter(path -> path.matches(dx, dy)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); + } + + private void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { + if (movePath.intermediatePositions(start, end).stream() + .anyMatch(board::hasPiece)) { + throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); + } } private boolean isSamePosition(Position start, Position end) { diff --git a/src/test/java/janggi/domain/piece/ChaTest.java b/src/test/java/janggi/domain/piece/ChaTest.java index aa0c5f15d6..0f05ace588 100644 --- a/src/test/java/janggi/domain/piece/ChaTest.java +++ b/src/test/java/janggi/domain/piece/ChaTest.java @@ -43,13 +43,25 @@ void validateCanMove_Success(int startX, int startY, int endX, int endY) { } @Test - @DisplayName("직선 경로가 아니거나 제자리로 이동할 경우 예외 발생") - void validateCanMove_Fail_InvalidPattern() { + @DisplayName("제자리로 이동할 경우 예외 발생") + void validateCanMove_Fail_Same_Position() { + // given + Position start = new Position(4, 4); + Position sameEnd = new Position(4, 4); + + // when & then + assertThatThrownBy(() -> cha.validateCanMove(start, sameEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("출발지와 목적지가 동일합니다."); + } + + @Test + @DisplayName("차가 이동할 수 없는 위치일 경우 예외 발생") + void validateCanMove_Fail_Invalid_Position() { // given Position start = new Position(4, 4); Position diagonalEnd = new Position(5, 5); Position knightEnd = new Position(5, 6); - Position sameEnd = new Position(4, 4); // when & then assertAll( @@ -57,9 +69,6 @@ void validateCanMove_Fail_InvalidPattern() { .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."), () -> assertThatThrownBy(() -> cha.validateCanMove(start, knightEnd, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> cha.validateCanMove(start, sameEnd, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다.") ); diff --git a/src/test/java/janggi/domain/piece/GungTest.java b/src/test/java/janggi/domain/piece/GungTest.java index 797cb13767..a3f265aa2e 100644 --- a/src/test/java/janggi/domain/piece/GungTest.java +++ b/src/test/java/janggi/domain/piece/GungTest.java @@ -1,8 +1,5 @@ package janggi.domain.piece; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; @@ -12,6 +9,9 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + class GungTest { private Gung gung; @@ -46,23 +46,17 @@ void validateCanMove_Success(int startX, int startY, int endX, int endY) { .doesNotThrowAnyException(); } - @ParameterizedTest - @DisplayName("한 칸을 초과하거나 제자리인 경우 예외 발생") - @CsvSource({ - "5, 9, 5, 9", - "5, 9, 5, 7", - "5, 9, 3, 9", - "5, 9, 3, 7" - }) - void validateCanMove_Fail_InvalidDistance(int startX, int startY, int endX, int endY) { + @Test + @DisplayName("제자리로 이동할 경우 예외 발생") + void validateCanMove_Fail_Same_Position() { // given - Position start = createPosition(startX, startY); - Position invalidDistanceEnd = createPosition(endX, endY); + Position start = new Position(4, 4); + Position sameEnd = new Position(4, 4); // when & then - assertThatThrownBy(() -> gung.validateCanMove(start, invalidDistanceEnd, board)) + assertThatThrownBy(() -> gung.validateCanMove(start, sameEnd, board)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."); + .hasMessage("출발지와 목적지가 동일합니다."); } @Test diff --git a/src/test/java/janggi/domain/piece/JolTest.java b/src/test/java/janggi/domain/piece/JolTest.java index 9dadc3add0..10b3a542fa 100644 --- a/src/test/java/janggi/domain/piece/JolTest.java +++ b/src/test/java/janggi/domain/piece/JolTest.java @@ -61,26 +61,37 @@ void validateCanMove_HanJol_Success(int startX, int startY, int endX, int endY) } @Test - @DisplayName("졸이 후진하거나, 대각선으로 움직이거나, 두 칸 이상 이동할 경우 예외 발생") - void validateCanMove_Fail_InvalidPattern() { + @DisplayName("제자리로 이동할 경우 예외 발생") + void validateCanMove_Fail_Same_Position() { // given - Position chuStart = new Position(1, 4); - Position hanStart = new Position(1, 7); + Position start = new Position(4, 4); + Position sameEnd = new Position(4, 4); + // when & then + assertThatThrownBy(() -> chuJol.validateCanMove(start, sameEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("출발지와 목적지가 동일합니다."); + } + + @Test + @DisplayName("졸이 이동할 수 없는 위치일 경우 예외 발생") + void validateCanMove_Fail_Invalid_Position() { + // given + Position chuJolStart = new Position(4, 4); + Position chuJolInvalidEnd = new Position(4, 3); + Position hanJolStart = new Position(4, 4); + Position hanJolInvalidEnd = new Position(4, 5); + Position diagonalEnd = new Position(5, 5); + + /// when & then assertAll( - () -> assertThatThrownBy(() -> chuJol.validateCanMove(chuStart, new Position(1, 3), board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> hanJol.validateCanMove(hanStart, new Position(1, 8), board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> chuJol.validateCanMove(chuStart, new Position(2, 5), board)) + () -> assertThatThrownBy(() -> chuJol.validateCanMove(chuJolStart, chuJolInvalidEnd, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> chuJol.validateCanMove(chuStart, new Position(1, 6), board)) + () -> assertThatThrownBy(() -> hanJol.validateCanMove(hanJolStart, hanJolInvalidEnd, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> chuJol.validateCanMove(chuStart, chuStart, board)) + () -> assertThatThrownBy(() -> chuJol.validateCanMove(chuJolStart, diagonalEnd, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다.") ); diff --git a/src/test/java/janggi/domain/piece/MaTest.java b/src/test/java/janggi/domain/piece/MaTest.java new file mode 100644 index 0000000000..376575f712 --- /dev/null +++ b/src/test/java/janggi/domain/piece/MaTest.java @@ -0,0 +1,88 @@ +package janggi.domain.piece; + +import janggi.domain.Board; +import janggi.domain.Position; +import janggi.domain.side.TeamType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class MaTest { + + private Ma ma; + private Board board; + + @BeforeEach + void setUp() { + board = Board.createInitialBoard(); + ma = new Ma(TeamType.HAN); + } + + @ParameterizedTest + @DisplayName("마는 이동 경로(멱)에 장애물이 없다면 L자 모양으로 이동할 수 있다.") + @CsvSource({ + "4, 4, 5, 6", + "4, 4, 3, 6", + "4, 4, 5, 2", + "4, 4, 3, 2" + }) + void validateCanMove_Success(int startX, int startY, int endX, int endY) { + // given + Position start = new Position(startX, startY); + Position end = new Position(endX, endY); + + // when & then + assertThatCode(() -> ma.validateCanMove(start, end, board)) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("제자리로 이동할 경우 예외 발생") + void validateCanMove_Fail_Same_Position() { + // given + Position start = new Position(4, 4); + Position sameEnd = new Position(4, 4); + + // when & then + assertThatThrownBy(() -> ma.validateCanMove(start, sameEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("출발지와 목적지가 동일합니다."); + } + + @Test + @DisplayName("마가 이동할 수 없는 위치일 경우 예외 발생") + void validateCanMove_Fail_Invalid_Position() { + // given + Position start = new Position(4, 4); + Position diagonalEnd = new Position(5, 5); + + // when & then + assertThatThrownBy(() -> ma.validateCanMove(start, diagonalEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."); + } + + @ParameterizedTest + @DisplayName("이동 경로 중간에 기물이 존재할 경우 예외 발생") + @CsvSource({ + "4, 4, 6, 5", + "4, 4, 6, 3", + "4, 4, 2, 5", + "4, 4, 2, 3" + }) + void validateCanMove_Fail_ObstacleExist(int startX, int startY, int endX, int endY) { + // given + Position start = new Position(startX, startY); + Position end = new Position(endX, endY); + + // when & then + assertThatThrownBy(() -> ma.validateCanMove(start, end, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); + } +} \ No newline at end of file diff --git a/src/test/java/janggi/domain/piece/PoTest.java b/src/test/java/janggi/domain/piece/PoTest.java index 6a8112aa4f..bbbf6112ea 100644 --- a/src/test/java/janggi/domain/piece/PoTest.java +++ b/src/test/java/janggi/domain/piece/PoTest.java @@ -1,9 +1,5 @@ package janggi.domain.piece; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; @@ -13,6 +9,9 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + class PoTest { private Po po; @@ -42,16 +41,28 @@ void validateCanMove_Success(int startX, int startY, int endX, int endY) { } @Test - @DisplayName("직선 경로가 아니거나 제자리 이동일 경우 예외 발생") + @DisplayName("제자리로 이동할 경우 예외 발생") + void validateCanMove_Fail_Same_Position() { + // given + Position start = new Position(4, 4); + Position sameEnd = new Position(4, 4); + + // when & then + assertThatThrownBy(() -> po.validateCanMove(start, sameEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("출발지와 목적지가 동일합니다."); + } + + @Test + @DisplayName("포가 이동할 수 없는 위치일 경우 예외 발생") void validateCanMove_Fail_InvalidPattern() { + // given Position start = new Position(4, 4); + Position invalidEnd = new Position(5, 5); - assertAll( - () -> assertThatThrownBy(() -> po.validateCanMove(start, new Position(5, 5), board)) - .isInstanceOf(IllegalArgumentException.class), - () -> assertThatThrownBy(() -> po.validateCanMove(start, start, board)) - .isInstanceOf(IllegalArgumentException.class) - ); + // when & then + assertThatThrownBy(() -> po.validateCanMove(start, invalidEnd, board)) + .isInstanceOf(IllegalArgumentException.class); } @Test diff --git a/src/test/java/janggi/domain/piece/SaTest.java b/src/test/java/janggi/domain/piece/SaTest.java index c7917418b1..a185be0b9b 100644 --- a/src/test/java/janggi/domain/piece/SaTest.java +++ b/src/test/java/janggi/domain/piece/SaTest.java @@ -1,9 +1,5 @@ package janggi.domain.piece; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; @@ -13,6 +9,10 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + class SaTest { private Sa sa; @@ -44,14 +44,26 @@ void validateCanMove_Success(int startX, int startY, int endX, int endY) { } @Test - @DisplayName("한 칸을 초과하여 이동하거나 제자리 이동일 경우 예외 발생") - void validateCanMove_Fail_InvalidPattern() { + @DisplayName("제자리로 이동할 경우 예외 발생") + void validateCanMove_Fail_Same_Position() { + // given + Position start = new Position(4, 4); + Position sameEnd = new Position(4, 4); + + // when & then + assertThatThrownBy(() -> sa.validateCanMove(start, sameEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("출발지와 목적지가 동일합니다."); + } + + @Test + @DisplayName("사가 이동할 수 없는 위치일 경우 예외 발생") + void validateCanMove_Fail_Invalid_Position() { // given Position start = new Position(4, 1); Position moveTwoSteps = new Position(4, 3); Position moveLongDiagonal = new Position(6, 3); Position knightMove = new Position(5, 3); - Position samePosition = new Position(4, 1); // when & then assertAll( @@ -62,9 +74,6 @@ void validateCanMove_Fail_InvalidPattern() { .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."), () -> assertThatThrownBy(() -> sa.validateCanMove(start, knightMove, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> sa.validateCanMove(start, samePosition, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다.") ); diff --git a/src/test/java/janggi/domain/piece/SangTest.java b/src/test/java/janggi/domain/piece/SangTest.java index 963c845da0..fa8957f9e1 100644 --- a/src/test/java/janggi/domain/piece/SangTest.java +++ b/src/test/java/janggi/domain/piece/SangTest.java @@ -43,22 +43,35 @@ void validateCanMove_Success(int startX, int startY, int endX, int endY) { } @Test - @DisplayName("상의 행마 패턴(대형 L자)이 아닐 경우 예외 발생") - void validateCanMove_Fail_InvalidPattern() { + @DisplayName("제자리로 이동할 경우 예외 발생") + void validateCanMove_Fail_Same_Position() { + // given + Position start = new Position(4, 4); + Position sameEnd = new Position(4, 4); + + // when & then + assertThatThrownBy(() -> sang.validateCanMove(start, sameEnd, board)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("출발지와 목적지가 동일합니다."); + } + + @Test + @DisplayName("상이 이동할 수 없는 위치일 경우 예외 발생") + void validateCanMove_Fail_Invalid_Position() { Position start = new Position(4, 4); assertAll( () -> assertThatThrownBy(() -> sang.validateCanMove(start, new Position(4, 7), board)) - .isInstanceOf(IllegalArgumentException.class), + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다."), () -> assertThatThrownBy(() -> sang.validateCanMove(start, new Position(5, 6), board)) - .isInstanceOf(IllegalArgumentException.class), - () -> assertThatThrownBy(() -> sang.validateCanMove(start, start, board)) .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 위치입니다.") ); } @ParameterizedTest - @DisplayName("상 이동 경로의 첫 번째 멱(직선)이나 두 번째 멱(대각선)에 기물이 있을 경우 예외 발생") + @DisplayName("이동 경로 중간에 기물이 존재할 경우 예외 발생") @CsvSource({ "4, 4, 7, 6", "4, 4, 1, 6", From 5f067b4c7161a0796c7198b49fbfc19d94458cf2 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 16:31:01 +0900 Subject: [PATCH 89/99] =?UTF-8?q?refactor:=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EC=83=81=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/Cha.java | 4 ---- src/main/java/janggi/domain/piece/Gung.java | 5 ----- src/main/java/janggi/domain/piece/Jol.java | 4 ---- src/main/java/janggi/domain/piece/Ma.java | 4 ---- src/main/java/janggi/domain/piece/Piece.java | 4 ++++ src/main/java/janggi/domain/piece/Po.java | 4 ---- src/main/java/janggi/domain/piece/Sa.java | 4 ---- src/main/java/janggi/domain/piece/Sang.java | 4 ---- 8 files changed, 4 insertions(+), 29 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 0a3c3c3d59..cf50a58d59 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -47,8 +47,4 @@ private void validatePieceInPath(MovePath movePath, Position start, Position end throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); } } - - private boolean isSamePosition(Position start, Position end) { - return start.isSamePosition(end); - } } diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index 6fc4b7452e..3353a88065 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -39,9 +39,4 @@ public void validateCanMove(Position start, Position end, Board board) { .findFirst() .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); } - - private boolean isSamePosition(Position start, Position end) { - return start.isSamePosition(end); - } - } diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 5e49d49a54..24e6f5a484 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -41,10 +41,6 @@ public void validateCanMove(Position start, Position end, Board board) { .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); } - private boolean isSamePosition(Position start, Position end) { - return start.isSamePosition(end); - } - private List selectPaths(TeamType teamType) { if (teamType == TeamType.HAN) { return HAN_PATHS; diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 1a7151f8cf..5c8bec7c30 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -52,8 +52,4 @@ private void validatePieceInPath(MovePath movePath, Position start, Position end throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); } } - - private boolean isSamePosition(Position start, Position end) { - return start.isSamePosition(end); - } } diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index 208a076d0a..643b5baaf5 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -26,5 +26,9 @@ public TeamType getTeamType() { return teamType; } + public boolean isSamePosition(Position start, Position end) { + return start.isSamePosition(end); + } + public abstract void validateCanMove(Position start, Position end, Board board); } diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index 07c161949f..ce1e500709 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -41,10 +41,6 @@ private MovePath findMovePath(Position start, Position end) { .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); } - private boolean isSamePosition(Position start, Position end) { - return start.isSamePosition(end); - } - private void validateObstacles(MovePath movePath, Position start, Position end, Board board) { List intermediatePositions = movePath.intermediatePositions(start, end); List obstacles = intermediatePositions.stream() diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index e5bf2bb30f..3429115a77 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -40,8 +40,4 @@ public void validateCanMove(Position start, Position end, Board board) { .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); } - - private boolean isSamePosition(Position start, Position end) { - return start.isSamePosition(end); - } } diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index aaad0a4a6e..80e6100bb2 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -51,8 +51,4 @@ private void validatePieceInPath(MovePath movePath, Position start, Position end throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); } } - - private boolean isSamePosition(Position start, Position end) { - return start.isSamePosition(end); - } } From 26e1f8bb47a4b5531b95004f1bdf6e99552d91ac Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 16:39:08 +0900 Subject: [PATCH 90/99] =?UTF-8?q?chore:=20=EB=B9=88=20=EC=A4=84=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/Ma.java | 1 - src/main/java/janggi/domain/piece/Sa.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 5c8bec7c30..ec92bc0923 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -43,7 +43,6 @@ private MovePath findMovePath(Position start, Position end) { .filter(path -> path.matches(dx, dy)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); - } private void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index 3429115a77..4371204633 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -38,6 +38,5 @@ public void validateCanMove(Position start, Position end, Board board) { .filter(path -> path.matches(dx, dy)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); - } } From 079cf1e8585f1addbc9b0af8f1fd1a5cf6b8d423 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 16:44:20 +0900 Subject: [PATCH 91/99] =?UTF-8?q?refactor:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Position.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java index 9f74ee3cbb..ecd48b61be 100644 --- a/src/main/java/janggi/domain/Position.java +++ b/src/main/java/janggi/domain/Position.java @@ -44,19 +44,6 @@ public boolean isSamePosition(Position position) { return x == position.getX() && y == position.getY(); } - public boolean isOneStep(Position position) { - int dx = position.getX() - x; - int dy = position.getY() - y; - int distanceX = Math.abs(dx); - int distanceY = Math.abs(dy); - - return distanceX <= 1 && distanceY <= 1; - } - - public boolean isStraightDirection(Position position) { - return x == position.getX() || y == position.getY(); - } - public int deltaX(Position position) { return position.getX() - x; } From b0619a039ba20b438af4de31cf0d7966daf4de56 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 16:58:08 +0900 Subject: [PATCH 92/99] =?UTF-8?q?refactor:=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20=EC=98=88=EC=99=B8=20=EB=B0=9C=EC=83=9D=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=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/Cha.java | 5 +---- src/main/java/janggi/domain/piece/Gung.java | 4 +--- src/main/java/janggi/domain/piece/Jol.java | 4 +--- src/main/java/janggi/domain/piece/Ma.java | 5 +---- src/main/java/janggi/domain/piece/Piece.java | 6 ++++-- src/main/java/janggi/domain/piece/Po.java | 5 +---- src/main/java/janggi/domain/piece/Sa.java | 4 +--- src/main/java/janggi/domain/piece/Sang.java | 5 +---- 8 files changed, 11 insertions(+), 27 deletions(-) diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index cf50a58d59..8c39ae09a8 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -23,15 +23,12 @@ public Cha(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { + checkSamePosition(start, end); MovePath movePath = findMovePath(start, end); validatePieceInPath(movePath, start, end, board); } private MovePath findMovePath(Position start, Position end) { - if (isSamePosition(start, end)) { - throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); - } - int dx = start.deltaX(end); // deltaX 말고 더 알아듣기 쉬운 메서드명으로 수정 필요 int dy = start.deltaY(end); diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index 3353a88065..01f770048f 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -27,9 +27,7 @@ public Gung(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { - if (isSamePosition(start, end)) { - throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); - } + checkSamePosition(start, end); int dx = start.deltaX(end); int dy = start.deltaY(end); diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 24e6f5a484..2027ab8d4c 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -28,9 +28,7 @@ public Jol(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { - if (isSamePosition(start, end)) { - throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); - } + checkSamePosition(start, end); int dx = start.deltaX(end); int dy = start.deltaY(end); diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index ec92bc0923..3b7f8c5d47 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -27,15 +27,12 @@ public Ma(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { + checkSamePosition(start, end); MovePath movePath = findMovePath(start, end); validatePieceInPath(movePath, start, end, board); } private MovePath findMovePath(Position start, Position end) { - if (isSamePosition(start, end)) { - throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); - } - int dx = start.deltaX(end); int dy = start.deltaY(end); diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index 643b5baaf5..e18549e924 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -26,8 +26,10 @@ public TeamType getTeamType() { return teamType; } - public boolean isSamePosition(Position start, Position end) { - return start.isSamePosition(end); + public void checkSamePosition(Position start, Position end) { + if (start.isSamePosition(end)) { + throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); + } } public abstract void validateCanMove(Position start, Position end, Board board); diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index ce1e500709..921b53da34 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -23,15 +23,12 @@ public Po(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { + checkSamePosition(start, end); MovePath movePath = findMovePath(start, end); validateObstacles(movePath, start, end, board); } private MovePath findMovePath(Position start, Position end) { - if (isSamePosition(start, end)) { - throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); - } - int dx = start.deltaX(end); int dy = start.deltaY(end); diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index 4371204633..0161b1933d 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -27,9 +27,7 @@ public Sa(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { - if (isSamePosition(start, end)) { - throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); - } + checkSamePosition(start, end); int dx = start.deltaX(end); int dy = start.deltaY(end); diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index 80e6100bb2..a4c4b64dd3 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -27,15 +27,12 @@ public Sang(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { + checkSamePosition(start, end); MovePath movePath = findMovePath(start, end); validatePieceInPath(movePath, start, end, board); } private MovePath findMovePath(Position start, Position end) { - if (isSamePosition(start, end)) { - throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); - } - int dx = start.deltaX(end); int dy = start.deltaY(end); From bceaae4e57d6cc89f254956b531320b9b37fdecf Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 17:49:14 +0900 Subject: [PATCH 93/99] =?UTF-8?q?refactor:=20Pieces=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=A0=9C=EA=B1=B0=20&=20Team=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EC=83=81=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=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/domain/Pieces.java | 109 --------------------- src/main/java/janggi/domain/side/Chu.java | 52 +++++----- src/main/java/janggi/domain/side/Han.java | 54 +++++----- src/main/java/janggi/domain/side/Team.java | 73 ++++++++++++-- 4 files changed, 119 insertions(+), 169 deletions(-) delete mode 100644 src/main/java/janggi/domain/Pieces.java diff --git a/src/main/java/janggi/domain/Pieces.java b/src/main/java/janggi/domain/Pieces.java deleted file mode 100644 index 1ac9ef029d..0000000000 --- a/src/main/java/janggi/domain/Pieces.java +++ /dev/null @@ -1,109 +0,0 @@ -package janggi.domain; - -import janggi.domain.piece.*; -import janggi.domain.side.TeamType; -import janggi.dto.BoardSpot; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -public class Pieces { - - private final Map value; - - private Pieces(Map value) { - this.value = value; - } - - public static Pieces createHan() { - Map pieces = new HashMap<>(); - createChas(pieces, 10, TeamType.HAN); - createMas(pieces, 10, TeamType.HAN); - createSangs(pieces, 10, TeamType.HAN); - createSas(pieces, 10, TeamType.HAN); - createGung(pieces, 9, TeamType.HAN); - createPos(pieces, 8, TeamType.HAN); - createJols(pieces, 7, TeamType.HAN); - return new Pieces(pieces); - } - - public static Pieces createChu() { - Map pieces = new HashMap<>(); - createChas(pieces, 1, TeamType.CHU); - createMas(pieces, 1, TeamType.CHU); - createSangs(pieces, 1, TeamType.CHU); - createSas(pieces, 1, TeamType.CHU); - createGung(pieces, 2, TeamType.CHU); - createPos(pieces, 3, TeamType.CHU); - createJols(pieces, 4, TeamType.CHU); - return new Pieces(pieces); - } - - public Map makeSnapShot() { - Map snapShot = new HashMap<>(); - for (Map.Entry entry : value.entrySet()) { - Position position = entry.getKey(); - Piece piece = entry.getValue(); - snapShot.put(position, new BoardSpot(piece.name(), piece.getTeamType())); - } - return snapShot; - } - - public boolean isPieceExists(Position position) { - return value.containsKey(position); - } - - public Optional findPiece(Position position) { - return Optional.ofNullable(value.get(position)); - } - - public Pieces move(Position start, Position end) { - Piece piece = value.get(start); - Map updatedValue = new HashMap<>(value); - updatedValue.remove(start); - updatedValue.put(end, piece); - return new Pieces(updatedValue); - } - - public Pieces remove(Position position) { - Map updatedValue = new HashMap<>(value); - updatedValue.remove(position); - return new Pieces(updatedValue); - } - - private static void createChas(Map pieces, int indexY, TeamType teamType) { - pieces.put(new Position(1, indexY), new Cha(teamType)); - pieces.put(new Position(9, indexY), new Cha(teamType)); - } - - private static void createMas(Map pieces, int indexY, TeamType teamType) { - pieces.put(new Position(2, indexY), new Ma(teamType)); - pieces.put(new Position(8, indexY), new Ma(teamType)); - } - - private static void createSangs(Map pieces, int indexY, TeamType teamType) { - pieces.put(new Position(7, indexY), new Sang(teamType)); - pieces.put(new Position(10 - 7, indexY), new Sang(teamType)); - } - - private static void createSas(Map pieces, int indexY, TeamType teamType) { - pieces.put(new Position(4, indexY), new Sa(teamType)); - pieces.put(new Position(10 - 4, indexY), new Sa(teamType)); - } - - private static void createGung(Map pieces, int indexY, TeamType teamType) { - pieces.put(new Position(5, indexY), new Gung(teamType)); - } - - private static void createPos(Map pieces, int indexY, TeamType teamType) { - pieces.put(new Position(2, indexY), new Po(teamType)); - pieces.put(new Position(10 - 2, indexY), new Po(teamType)); - } - - private static void createJols(Map pieces, int indexY, TeamType teamType) { - for (int i = 1; i < 10; i += 2) { - pieces.put(new Position(i, indexY), new Jol(teamType)); - } - } -} diff --git a/src/main/java/janggi/domain/side/Chu.java b/src/main/java/janggi/domain/side/Chu.java index 4395a6ab95..1a09e3256a 100644 --- a/src/main/java/janggi/domain/side/Chu.java +++ b/src/main/java/janggi/domain/side/Chu.java @@ -1,47 +1,47 @@ package janggi.domain.side; -import janggi.domain.Pieces; import janggi.domain.Position; import janggi.domain.piece.Piece; -import janggi.dto.BoardSpot; +import java.util.HashMap; import java.util.Map; -import java.util.Optional; -public class Chu implements Team { +public class Chu extends Team { - private final Pieces pieces; - - private Chu(Pieces pieces) { - this.pieces = pieces; + public Chu(Map pieces) { + super(pieces); } public static Chu createInitialChu() { - return new Chu(Pieces.createChu()); - } - - @Override - public Map makeSnapShot() { - return pieces.makeSnapShot(); - } - - @Override - public boolean isPieceExists(Position position) { - return pieces.isPieceExists(position); - } - - @Override - public Optional findPiece(Position position) { - return pieces.findPiece(position); + return new Chu(initializePieces()); } @Override public Team move(Position start, Position end) { - return new Chu(pieces.move(start, end)); + Map pieces = getPieces(); + Piece piece = pieces.get(start); + Map updatedPieces = new HashMap<>(pieces); + updatedPieces.remove(start); + updatedPieces.put(end, piece); + return new Chu(updatedPieces); } @Override public Team remove(Position position) { - return new Chu(pieces.remove(position)); + Map updatedPieces = new HashMap<>(getPieces()); + updatedPieces.remove(position); + return new Chu(updatedPieces); + } + + private static Map initializePieces() { + Map pieces = new HashMap<>(); + createChas(pieces, 1, TeamType.CHU); + createMas(pieces, 1, TeamType.CHU); + createSangs(pieces, 1, TeamType.CHU); + createSas(pieces, 1, TeamType.CHU); + createGung(pieces, 2, TeamType.CHU); + createPos(pieces, 3, TeamType.CHU); + createJols(pieces, 4, TeamType.CHU); + return pieces; } } diff --git a/src/main/java/janggi/domain/side/Han.java b/src/main/java/janggi/domain/side/Han.java index b84ffca3f6..9df9d5cb92 100644 --- a/src/main/java/janggi/domain/side/Han.java +++ b/src/main/java/janggi/domain/side/Han.java @@ -1,47 +1,47 @@ package janggi.domain.side; -import janggi.domain.Pieces; import janggi.domain.Position; -import janggi.domain.piece.Piece; -import janggi.dto.BoardSpot; +import janggi.domain.piece.*; +import java.util.HashMap; import java.util.Map; -import java.util.Optional; -public class Han implements Team { +public class Han extends Team { - private final Pieces pieces; - - private Han(Pieces pieces) { - this.pieces = pieces; + public Han(Map pieces) { + super(pieces); } public static Han createInitialHan() { - return new Han(Pieces.createHan()); - } - - @Override - public Map makeSnapShot() { - return pieces.makeSnapShot(); - } - - @Override - public boolean isPieceExists(Position position) { - return pieces.isPieceExists(position); - } - - @Override - public Optional findPiece(Position position) { - return pieces.findPiece(position); + return new Han(initializePieces()); } @Override public Team move(Position start, Position end) { - return new Han(pieces.move(start, end)); + Map pieces = getPieces(); + Piece piece = pieces.get(start); + Map updatedPieces = new HashMap<>(pieces); + updatedPieces.remove(start); + updatedPieces.put(end, piece); + return new Han(updatedPieces); } @Override public Team remove(Position position) { - return new Han(pieces.remove(position)); + Map updatedPieces = new HashMap<>(getPieces()); + updatedPieces.remove(position); + return new Han(updatedPieces); + } + + private static Map initializePieces() { + Map pieces = new HashMap<>(); + createChas(pieces, 10, TeamType.HAN); + createMas(pieces, 10, TeamType.HAN); + createSangs(pieces, 10, TeamType.HAN); + createSas(pieces, 10, TeamType.HAN); + createGung(pieces, 9, TeamType.HAN); + createPos(pieces, 8, TeamType.HAN); + createJols(pieces, 7, TeamType.HAN); + return pieces; } } diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index 6af551bb49..0a7d1b4b19 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -1,21 +1,80 @@ package janggi.domain.side; import janggi.domain.Position; -import janggi.domain.piece.Piece; +import janggi.domain.piece.*; import janggi.dto.BoardSpot; +import java.util.HashMap; import java.util.Map; import java.util.Optional; -public interface Team { +public abstract class Team { - Map makeSnapShot(); + private final Map pieces; - boolean isPieceExists(Position position); + public Team(Map pieces) { + this.pieces = pieces; + } - Optional findPiece(Position position); + public Map getPieces() { + return pieces; + } - Team move(Position start, Position end); + public Map makeSnapShot() { + Map snapShot = new HashMap<>(); + for (Map.Entry entry : pieces.entrySet()) { + Position position = entry.getKey(); + Piece piece = entry.getValue(); + snapShot.put(position, new BoardSpot(piece.name(), piece.getTeamType())); + } + return snapShot; + } + + public boolean isPieceExists(Position position) { + return pieces.containsKey(position); + } + + public Optional findPiece(Position position) { + return Optional.ofNullable(pieces.get(position)); + } + + protected static void createChas(Map pieces, int indexY, TeamType teamType) { + pieces.put(new Position(1, indexY), new Cha(teamType)); + pieces.put(new Position(9, indexY), new Cha(teamType)); + } + + protected static void createMas(Map pieces, int indexY, TeamType teamType) { + pieces.put(new Position(2, indexY), new Ma(teamType)); + pieces.put(new Position(8, indexY), new Ma(teamType)); + } + + protected static void createSangs(Map pieces, int indexY, TeamType teamType) { + pieces.put(new Position(7, indexY), new Sang(teamType)); + pieces.put(new Position(10 - 7, indexY), new Sang(teamType)); + } + + protected static void createSas(Map pieces, int indexY, TeamType teamType) { + pieces.put(new Position(4, indexY), new Sa(teamType)); + pieces.put(new Position(10 - 4, indexY), new Sa(teamType)); + } + + protected static void createGung(Map pieces, int indexY, TeamType teamType) { + pieces.put(new Position(5, indexY), new Gung(teamType)); + } + + protected static void createPos(Map pieces, int indexY, TeamType teamType) { + pieces.put(new Position(2, indexY), new Po(teamType)); + pieces.put(new Position(10 - 2, indexY), new Po(teamType)); + } + + protected static void createJols(Map pieces, int indexY, TeamType teamType) { + for (int i = 1; i < 10; i += 2) { + pieces.put(new Position(i, indexY), new Jol(teamType)); + } + } + + public abstract Team remove(Position position); + + public abstract Team move(Position start, Position end); - Team remove(Position position); } From 9c14ad8d60685b57d34daf909606c33e37c4d52a Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 19:34:36 +0900 Subject: [PATCH 94/99] =?UTF-8?q?refactor:=20getter=EC=97=90=20=EB=B0=A9?= =?UTF-8?q?=EC=96=B4=EC=A0=81=20=EB=B3=B5=EC=82=AC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/side/Team.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index 0a7d1b4b19..8a7d916951 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -4,9 +4,7 @@ import janggi.domain.piece.*; import janggi.dto.BoardSpot; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; +import java.util.*; public abstract class Team { @@ -17,7 +15,7 @@ public Team(Map pieces) { } public Map getPieces() { - return pieces; + return Collections.unmodifiableMap(pieces); } public Map makeSnapShot() { From 5ab4d46f6f459b39152f17bbd118520b2b818d60 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 19:48:34 +0900 Subject: [PATCH 95/99] =?UTF-8?q?refactor:=20DTO=20=EB=82=B4=EB=B6=80?= =?UTF-8?q?=EC=97=90=20=ED=8C=A9=ED=86=A0=EB=A6=AC=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=B4=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=EC=97=90=EC=84=9C=20DTO=20=EB=82=B4=EB=B6=80?= =?UTF-8?q?=20=EA=B5=AC=EC=A1=B0=EB=A5=BC=20=EB=AA=A8=EB=A5=B4=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/domain/Board.java | 8 ++++---- src/main/java/janggi/dto/BoardSpot.java | 5 +++++ src/main/java/janggi/dto/BoardSpots.java | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index a0cc672993..f3d8c67ae0 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -5,10 +5,10 @@ import janggi.domain.side.Han; import janggi.domain.side.Team; import janggi.domain.side.TeamType; -import janggi.dto.BoardSpot; import janggi.dto.BoardSpots; import java.util.HashMap; +import java.util.Map; import java.util.Optional; public class Board { @@ -30,9 +30,9 @@ public static Board createInitialBoard() { } public BoardSpots makeSnapShot() { - HashMap boardSpots = new HashMap<>(chu.makeSnapShot()); - boardSpots.putAll(han.makeSnapShot()); - return new BoardSpots(boardSpots); + Map allPieces = new HashMap<>(chu.getPieces()); + allPieces.putAll(han.getPieces()); + return BoardSpots.from(allPieces); } public boolean isPieceExists(Position position, TeamType currentTeamType) { diff --git a/src/main/java/janggi/dto/BoardSpot.java b/src/main/java/janggi/dto/BoardSpot.java index 9fc3d268c3..681cb6e104 100644 --- a/src/main/java/janggi/dto/BoardSpot.java +++ b/src/main/java/janggi/dto/BoardSpot.java @@ -1,6 +1,11 @@ package janggi.dto; +import janggi.domain.piece.Piece; import janggi.domain.side.TeamType; public record BoardSpot(String pieceName, TeamType teamType) { + + public static BoardSpot from(Piece piece) { + return new BoardSpot(piece.name(), piece.getTeamType()); + } } diff --git a/src/main/java/janggi/dto/BoardSpots.java b/src/main/java/janggi/dto/BoardSpots.java index e896eb4806..45fc96e0aa 100644 --- a/src/main/java/janggi/dto/BoardSpots.java +++ b/src/main/java/janggi/dto/BoardSpots.java @@ -1,7 +1,22 @@ package janggi.dto; import janggi.domain.Position; +import janggi.domain.piece.Piece; + +import java.util.HashMap; import java.util.Map; public record BoardSpots(Map value) { + + public static BoardSpots from(Map pieces) { + Map boardSpots = new HashMap<>(); + for (Map.Entry entry : pieces.entrySet()) { + Position position = entry.getKey(); + Piece piece = entry.getValue(); + + BoardSpot boardSpot = BoardSpot.from(piece); + boardSpots.put(position, boardSpot); + } + return new BoardSpots(boardSpots); + } } From ad0ab80778cca554e073f7f79031e9ba7328a755 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 20:02:36 +0900 Subject: [PATCH 96/99] =?UTF-8?q?chore:=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=88=9C=EC=84=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/domain/side/Team.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/janggi/domain/side/Team.java b/src/main/java/janggi/domain/side/Team.java index 8a7d916951..60a440c153 100644 --- a/src/main/java/janggi/domain/side/Team.java +++ b/src/main/java/janggi/domain/side/Team.java @@ -71,8 +71,8 @@ protected static void createJols(Map pieces, int indexY, TeamTy } } - public abstract Team remove(Position position); - public abstract Team move(Position start, Position end); + public abstract Team remove(Position position); + } From 1270f3925023d14ec1db06a7ce8e677542e62c65 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 20:39:03 +0900 Subject: [PATCH 97/99] =?UTF-8?q?refactor:=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/Gung.java | 3 +++ src/main/java/janggi/domain/piece/Jol.java | 3 +++ src/main/java/janggi/domain/piece/Sa.java | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index 01f770048f..ba3d2cd1e6 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -28,7 +28,10 @@ public Gung(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { checkSamePosition(start, end); + checkMovePath(start, end); + } + private void checkMovePath(Position start, Position end) { int dx = start.deltaX(end); int dy = start.deltaY(end); diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 2027ab8d4c..25613d4f48 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -29,7 +29,10 @@ public Jol(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { checkSamePosition(start, end); + checkMovePath(start, end); + } + private void checkMovePath(Position start, Position end) { int dx = start.deltaX(end); int dy = start.deltaY(end); diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index 0161b1933d..dfca8a63be 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -28,7 +28,10 @@ public Sa(TeamType teamType) { @Override public void validateCanMove(Position start, Position end, Board board) { checkSamePosition(start, end); + checkMovePath(start, end); + } + private void checkMovePath(Position start, Position end) { int dx = start.deltaX(end); int dy = start.deltaY(end); From ba02709881fa1f615a5d9153f77c912b5a5f7528 Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Thu, 2 Apr 2026 21:33:11 +0900 Subject: [PATCH 98/99] =?UTF-8?q?refactor:=20=EC=B6=94=EC=83=81=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A5=BC=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EC=A4=91=EB=B3=B5=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/piece/Cha.java | 21 +++-------- src/main/java/janggi/domain/piece/Gung.java | 19 ++-------- src/main/java/janggi/domain/piece/Jol.java | 27 +++----------- .../janggi/domain/piece/LeapingPiece.java | 37 +++++++++++++++++++ src/main/java/janggi/domain/piece/Ma.java | 27 ++------------ .../janggi/domain/piece/OneStepPiece.java | 17 --------- src/main/java/janggi/domain/piece/Piece.java | 5 +++ src/main/java/janggi/domain/piece/Po.java | 21 +++-------- src/main/java/janggi/domain/piece/Sa.java | 19 ++-------- src/main/java/janggi/domain/piece/Sang.java | 27 ++------------ .../janggi/domain/piece/SlidingPiece.java | 32 ++++++++++++++++ .../janggi/domain/piece/SteppingPiece.java | 31 ++++++++++++++++ 12 files changed, 132 insertions(+), 151 deletions(-) create mode 100644 src/main/java/janggi/domain/piece/LeapingPiece.java delete mode 100644 src/main/java/janggi/domain/piece/OneStepPiece.java create mode 100644 src/main/java/janggi/domain/piece/SlidingPiece.java create mode 100644 src/main/java/janggi/domain/piece/SteppingPiece.java diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 8c39ae09a8..0e152f0c56 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -8,7 +8,7 @@ import java.util.List; -public class Cha extends Piece { +public class Cha extends SlidingPiece { private static final List PATHS = List.of( new MovePath(List.of(Delta.UP)), @@ -22,23 +22,12 @@ public Cha(TeamType teamType) { } @Override - public void validateCanMove(Position start, Position end, Board board) { - checkSamePosition(start, end); - MovePath movePath = findMovePath(start, end); - validatePieceInPath(movePath, start, end, board); + protected List getPaths() { + return PATHS; } - private MovePath findMovePath(Position start, Position end) { - int dx = start.deltaX(end); // deltaX 말고 더 알아듣기 쉬운 메서드명으로 수정 필요 - int dy = start.deltaY(end); - - return PATHS.stream() - .filter(path -> path.matchesDirection(dx, dy)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); - } - - private void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { + @Override + protected void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { if (movePath.intermediatePositions(start, end).stream() .anyMatch(board::hasPiece)) { throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); diff --git a/src/main/java/janggi/domain/piece/Gung.java b/src/main/java/janggi/domain/piece/Gung.java index ba3d2cd1e6..50873f2393 100644 --- a/src/main/java/janggi/domain/piece/Gung.java +++ b/src/main/java/janggi/domain/piece/Gung.java @@ -1,14 +1,12 @@ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; -import janggi.domain.Position; import janggi.domain.side.TeamType; import java.util.List; -public class Gung extends Piece { +public class Gung extends SteppingPiece { private static final List PATHS = List.of( new MovePath(List.of(Delta.UP)), @@ -26,18 +24,7 @@ public Gung(TeamType teamType) { } @Override - public void validateCanMove(Position start, Position end, Board board) { - checkSamePosition(start, end); - checkMovePath(start, end); - } - - private void checkMovePath(Position start, Position end) { - int dx = start.deltaX(end); - int dy = start.deltaY(end); - - PATHS.stream() - .filter(path -> path.matches(dx, dy)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); + protected List getPaths() { + return PATHS; } } diff --git a/src/main/java/janggi/domain/piece/Jol.java b/src/main/java/janggi/domain/piece/Jol.java index 25613d4f48..cef9e1a898 100644 --- a/src/main/java/janggi/domain/piece/Jol.java +++ b/src/main/java/janggi/domain/piece/Jol.java @@ -1,14 +1,12 @@ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; -import janggi.domain.Position; import janggi.domain.side.TeamType; import java.util.List; -public class Jol extends Piece { +public class Jol extends SteppingPiece { private static final List CHU_PATHS = List.of( new MovePath(List.of(Delta.UP)), @@ -27,25 +25,10 @@ public Jol(TeamType teamType) { } @Override - public void validateCanMove(Position start, Position end, Board board) { - checkSamePosition(start, end); - checkMovePath(start, end); - } - - private void checkMovePath(Position start, Position end) { - int dx = start.deltaX(end); - int dy = start.deltaY(end); - - selectPaths(getTeamType()).stream() - .filter(path -> path.matches(dx, dy)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); - } - - private List selectPaths(TeamType teamType) { - if (teamType == TeamType.HAN) { - return HAN_PATHS; + protected List getPaths() { + if (getTeamType() == TeamType.CHU) { + return CHU_PATHS; } - return CHU_PATHS; + return HAN_PATHS; } } diff --git a/src/main/java/janggi/domain/piece/LeapingPiece.java b/src/main/java/janggi/domain/piece/LeapingPiece.java new file mode 100644 index 0000000000..cc793560ec --- /dev/null +++ b/src/main/java/janggi/domain/piece/LeapingPiece.java @@ -0,0 +1,37 @@ +package janggi.domain.piece; + +import janggi.domain.Board; +import janggi.domain.MovePath; +import janggi.domain.Position; +import janggi.domain.side.TeamType; + +public abstract class LeapingPiece extends Piece{ + + public LeapingPiece(TeamType teamType, PieceType pieceType) { + super(teamType, pieceType); + } + + @Override + public void validateCanMove(Position start, Position end, Board board) { + checkSamePosition(start, end); + MovePath movePath = findMovePath(start, end); + validatePieceInPath(movePath, start, end, board); + } + + private MovePath findMovePath(Position start, Position end) { + int dx = start.deltaX(end); + int dy = start.deltaY(end); + + return getPaths().stream() + .filter(path -> path.matches(dx, dy)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); + } + + private void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { + if (movePath.intermediatePositions(start, end).stream() + .anyMatch(board::hasPiece)) { + throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); + } + } +} diff --git a/src/main/java/janggi/domain/piece/Ma.java b/src/main/java/janggi/domain/piece/Ma.java index 3b7f8c5d47..4aeba13839 100644 --- a/src/main/java/janggi/domain/piece/Ma.java +++ b/src/main/java/janggi/domain/piece/Ma.java @@ -1,14 +1,12 @@ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; -import janggi.domain.Position; import janggi.domain.side.TeamType; import java.util.List; -public class Ma extends Piece { +public class Ma extends LeapingPiece { private static final List PATHS = List.of( new MovePath(List.of(Delta.UP, Delta.RIGHT_UP)), @@ -26,26 +24,7 @@ public Ma(TeamType teamType) { } @Override - public void validateCanMove(Position start, Position end, Board board) { - checkSamePosition(start, end); - MovePath movePath = findMovePath(start, end); - validatePieceInPath(movePath, start, end, board); - } - - private MovePath findMovePath(Position start, Position end) { - int dx = start.deltaX(end); - int dy = start.deltaY(end); - - return PATHS.stream() - .filter(path -> path.matches(dx, dy)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); - } - - private void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { - if (movePath.intermediatePositions(start, end).stream() - .anyMatch(board::hasPiece)) { - throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); - } + protected List getPaths() { + return PATHS; } } diff --git a/src/main/java/janggi/domain/piece/OneStepPiece.java b/src/main/java/janggi/domain/piece/OneStepPiece.java deleted file mode 100644 index 25c9c76aef..0000000000 --- a/src/main/java/janggi/domain/piece/OneStepPiece.java +++ /dev/null @@ -1,17 +0,0 @@ -package janggi.domain.piece; - -import janggi.domain.Board; -import janggi.domain.Position; -import janggi.domain.side.TeamType; - -public abstract class OneStepPiece extends Piece{ - - public OneStepPiece(TeamType teamType, PieceType pieceType) { - super(teamType, pieceType); - } - - @Override - public void validateCanMove(Position start, Position end, Board board) { - - } -} diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index e18549e924..0ff35acedc 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -1,9 +1,12 @@ package janggi.domain.piece; import janggi.domain.Board; +import janggi.domain.MovePath; import janggi.domain.Position; import janggi.domain.side.TeamType; +import java.util.List; + public abstract class Piece { private final TeamType teamType; @@ -33,4 +36,6 @@ public void checkSamePosition(Position start, Position end) { } public abstract void validateCanMove(Position start, Position end, Board board); + + protected abstract List getPaths(); } diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index 921b53da34..bd4ea00d21 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Optional; -public class Po extends Piece { +public class Po extends SlidingPiece { private static final List PATHS = List.of( new MovePath(List.of(Delta.UP)), @@ -22,23 +22,12 @@ public Po(TeamType teamType) { } @Override - public void validateCanMove(Position start, Position end, Board board) { - checkSamePosition(start, end); - MovePath movePath = findMovePath(start, end); - validateObstacles(movePath, start, end, board); + protected List getPaths() { + return PATHS; } - private MovePath findMovePath(Position start, Position end) { - int dx = start.deltaX(end); - int dy = start.deltaY(end); - - return PATHS.stream() - .filter(path -> path.matchesDirection(dx, dy)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); - } - - private void validateObstacles(MovePath movePath, Position start, Position end, Board board) { + @Override + protected void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { List intermediatePositions = movePath.intermediatePositions(start, end); List obstacles = intermediatePositions.stream() .map(board::findPiece) diff --git a/src/main/java/janggi/domain/piece/Sa.java b/src/main/java/janggi/domain/piece/Sa.java index dfca8a63be..b4ceb8c3fa 100644 --- a/src/main/java/janggi/domain/piece/Sa.java +++ b/src/main/java/janggi/domain/piece/Sa.java @@ -1,14 +1,12 @@ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; -import janggi.domain.Position; import janggi.domain.side.TeamType; import java.util.List; -public class Sa extends Piece { +public class Sa extends SteppingPiece { private static final List PATHS = List.of( new MovePath(List.of(Delta.UP)), @@ -26,18 +24,7 @@ public Sa(TeamType teamType) { } @Override - public void validateCanMove(Position start, Position end, Board board) { - checkSamePosition(start, end); - checkMovePath(start, end); - } - - private void checkMovePath(Position start, Position end) { - int dx = start.deltaX(end); - int dy = start.deltaY(end); - - PATHS.stream() - .filter(path -> path.matches(dx, dy)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); + protected List getPaths() { + return PATHS; } } diff --git a/src/main/java/janggi/domain/piece/Sang.java b/src/main/java/janggi/domain/piece/Sang.java index a4c4b64dd3..410220d17a 100644 --- a/src/main/java/janggi/domain/piece/Sang.java +++ b/src/main/java/janggi/domain/piece/Sang.java @@ -1,14 +1,12 @@ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; -import janggi.domain.Position; import janggi.domain.side.TeamType; import java.util.List; -public class Sang extends Piece { +public class Sang extends LeapingPiece { private static final List PATHS = List.of( new MovePath(List.of(Delta.UP, Delta.RIGHT_UP, Delta.RIGHT_UP)), @@ -26,26 +24,7 @@ public Sang(TeamType teamType) { } @Override - public void validateCanMove(Position start, Position end, Board board) { - checkSamePosition(start, end); - MovePath movePath = findMovePath(start, end); - validatePieceInPath(movePath, start, end, board); - } - - private MovePath findMovePath(Position start, Position end) { - int dx = start.deltaX(end); - int dy = start.deltaY(end); - - return PATHS.stream() - .filter(path -> path.matches(dx, dy)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); - } - - private void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { - if (movePath.intermediatePositions(start, end).stream() - .anyMatch(board::hasPiece)) { - throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); - } + protected List getPaths() { + return PATHS; } } diff --git a/src/main/java/janggi/domain/piece/SlidingPiece.java b/src/main/java/janggi/domain/piece/SlidingPiece.java new file mode 100644 index 0000000000..fc727a6d40 --- /dev/null +++ b/src/main/java/janggi/domain/piece/SlidingPiece.java @@ -0,0 +1,32 @@ +package janggi.domain.piece; + +import janggi.domain.Board; +import janggi.domain.MovePath; +import janggi.domain.Position; +import janggi.domain.side.TeamType; + +public abstract class SlidingPiece extends Piece { + + public SlidingPiece(TeamType teamType, PieceType pieceType) { + super(teamType, pieceType); + } + + @Override + public void validateCanMove(Position start, Position end, Board board) { + checkSamePosition(start, end); + MovePath movePath = findMovePath(start, end); + validatePieceInPath(movePath, start, end, board); + } + + private MovePath findMovePath(Position start, Position end) { + int dx = start.deltaX(end); + int dy = start.deltaY(end); + + return getPaths().stream() + .filter(path -> path.matchesDirection(dx, dy)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); + } + + protected abstract void validatePieceInPath(MovePath movePath, Position start, Position end, Board board); +} diff --git a/src/main/java/janggi/domain/piece/SteppingPiece.java b/src/main/java/janggi/domain/piece/SteppingPiece.java new file mode 100644 index 0000000000..94209c00d4 --- /dev/null +++ b/src/main/java/janggi/domain/piece/SteppingPiece.java @@ -0,0 +1,31 @@ +package janggi.domain.piece; + +import janggi.domain.Board; +import janggi.domain.MovePath; +import janggi.domain.Position; +import janggi.domain.side.TeamType; + +import java.util.List; + +public abstract class SteppingPiece extends Piece{ + + public SteppingPiece(TeamType teamType, PieceType pieceType) { + super(teamType, pieceType); + } + + @Override + public void validateCanMove(Position start, Position end, Board board) { + checkSamePosition(start, end); + checkMovePath(start, end); + } + + private void checkMovePath(Position start, Position end) { + int dx = start.deltaX(end); + int dy = start.deltaY(end); + + getPaths().stream() + .filter(path -> path.matches(dx, dy)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); + } +} From 2ffdaf48ade05065a7d48a764bf58206f798cbfc Mon Sep 17 00:00:00 2001 From: Chocoding1 Date: Fri, 3 Apr 2026 21:11:12 +0900 Subject: [PATCH 99/99] =?UTF-8?q?refactor:=20Piece=EC=99=80=20Board=20?= =?UTF-8?q?=EA=B0=84=EC=9D=98=20=EC=88=9C=ED=99=98=20=EC=B0=B8=EC=A1=B0=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/janggi/domain/Board.java | 38 +++++--- src/main/java/janggi/domain/piece/Cha.java | 16 ++-- .../janggi/domain/piece/LeapingPiece.java | 22 ++--- src/main/java/janggi/domain/piece/Piece.java | 11 +-- src/main/java/janggi/domain/piece/Po.java | 18 ++-- .../janggi/domain/piece/SlidingPiece.java | 10 +-- .../janggi/domain/piece/SteppingPiece.java | 14 ++- src/test/java/janggi/domain/BoardTest.java | 24 ++++- .../java/janggi/domain/piece/ChaTest.java | 73 +++++++--------- .../java/janggi/domain/piece/GungTest.java | 46 +++++----- .../java/janggi/domain/piece/JolTest.java | 55 ++++++------ .../janggi/domain/piece/LeapingPieceTest.java | 26 ++++++ src/test/java/janggi/domain/piece/MaTest.java | 75 ++++++++-------- src/test/java/janggi/domain/piece/PoTest.java | 80 +++++++++-------- src/test/java/janggi/domain/piece/SaTest.java | 41 +++++---- .../java/janggi/domain/piece/SangTest.java | 87 ++++++++++--------- .../domain/piece/SteppingPieceTest.java | 28 ++++++ 17 files changed, 385 insertions(+), 279 deletions(-) create mode 100644 src/test/java/janggi/domain/piece/LeapingPieceTest.java create mode 100644 src/test/java/janggi/domain/piece/SteppingPieceTest.java diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java index f3d8c67ae0..da2b9f239f 100644 --- a/src/main/java/janggi/domain/Board.java +++ b/src/main/java/janggi/domain/Board.java @@ -8,6 +8,7 @@ import janggi.dto.BoardSpots; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -48,9 +49,9 @@ public String getPieceName(Position position, TeamType currentTeamType) { public void validateCanMove(Position start, Position end, TeamType currentTeamType) { validateRange(end); + checkSamePosition(start, end); validateTeamPieceExistsAtEnd(currentTeam(currentTeamType), end); - Piece piece = findTeamPiece(start, currentTeam(currentTeamType)); - piece.validateCanMove(start, end, this); + validatePiecesInPath(start, end, currentTeam(currentTeamType)); } public Optional findPiece(Position position) { @@ -61,10 +62,6 @@ public Optional findPiece(Position position) { return han.findPiece(position); } - public boolean hasPiece(Position position) { - return findPiece(position).isPresent(); - } - public Board move(Position start, Position end, TeamType currentTeamType) { Team updatedCurrentTeam = currentTeam(currentTeamType).move(start, end); Team updatedOpponentTeam = removeOpponentPiece(currentTeamType, end); @@ -87,6 +84,18 @@ private boolean isInRange(int start, int last, int index) { return index >= start && index <= last; } + private void checkSamePosition(Position start, Position end) { + if (start.isSamePosition(end)) { + throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); + } + } + + private void validateTeamPieceExistsAtEnd(Team team, Position end) { + if (team.isPieceExists(end)) { + throw new IllegalArgumentException("아군이 존재하는 좌표로는 이동할 수 없습니다."); + } + } + private Team currentTeam(TeamType currentTeamType) { if (currentTeamType == TeamType.CHU) { return chu; @@ -94,17 +103,22 @@ private Team currentTeam(TeamType currentTeamType) { return han; } + private void validatePiecesInPath(Position start, Position end, Team currentTeam) { + Piece piece = findTeamPiece(start, currentTeam); + List piecePositionsInPath = piece.getPiecePositionsInPath(start, end); + List piecesInPath = piecePositionsInPath.stream() + .map(this::findPiece) + .filter(Optional::isPresent) + .map(Optional::get) + .toList(); + piece.validateCanMove(piecesInPath); + } + private Piece findTeamPiece(Position position, Team currentTeam) { return currentTeam.findPiece(position) .orElseThrow(() -> new IllegalArgumentException("입력한 위치에 기물이 없습니다.")); } - private void validateTeamPieceExistsAtEnd(Team team, Position end) { - if (team.isPieceExists(end)) { - throw new IllegalArgumentException("아군이 존재하는 좌표로는 이동할 수 없습니다."); - } - } - private Team removeOpponentPiece(TeamType currentTeamType, Position end) { Team opponentTeam = opponentTeam(currentTeamType); if (opponentTeam.findPiece(end).isEmpty()) { diff --git a/src/main/java/janggi/domain/piece/Cha.java b/src/main/java/janggi/domain/piece/Cha.java index 0e152f0c56..38ca7fb616 100644 --- a/src/main/java/janggi/domain/piece/Cha.java +++ b/src/main/java/janggi/domain/piece/Cha.java @@ -1,9 +1,7 @@ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; -import janggi.domain.Position; import janggi.domain.side.TeamType; import java.util.List; @@ -21,16 +19,24 @@ public Cha(TeamType teamType) { super(teamType, PieceType.CHA); } + @Override protected List getPaths() { return PATHS; } @Override - protected void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { - if (movePath.intermediatePositions(start, end).stream() - .anyMatch(board::hasPiece)) { + public void validateCanMove(List piecesInPath) { + if (!piecesInPath.isEmpty()) { throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); } } + +// @Override +// protected void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { +// if (movePath.intermediatePositions(start, end).stream() +// .anyMatch(board::hasPiece)) { +// throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); +// } +// } } diff --git a/src/main/java/janggi/domain/piece/LeapingPiece.java b/src/main/java/janggi/domain/piece/LeapingPiece.java index cc793560ec..d055a0d96e 100644 --- a/src/main/java/janggi/domain/piece/LeapingPiece.java +++ b/src/main/java/janggi/domain/piece/LeapingPiece.java @@ -1,10 +1,11 @@ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.MovePath; import janggi.domain.Position; import janggi.domain.side.TeamType; +import java.util.List; + public abstract class LeapingPiece extends Piece{ public LeapingPiece(TeamType teamType, PieceType pieceType) { @@ -12,10 +13,16 @@ public LeapingPiece(TeamType teamType, PieceType pieceType) { } @Override - public void validateCanMove(Position start, Position end, Board board) { - checkSamePosition(start, end); + public void validateCanMove(List piecesInPath) { + if (!piecesInPath.isEmpty()) { + throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); + } + } + + @Override + public List getPiecePositionsInPath(Position start, Position end) { MovePath movePath = findMovePath(start, end); - validatePieceInPath(movePath, start, end, board); + return movePath.intermediatePositions(start, end); } private MovePath findMovePath(Position start, Position end) { @@ -27,11 +34,4 @@ private MovePath findMovePath(Position start, Position end) { .findFirst() .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); } - - private void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { - if (movePath.intermediatePositions(start, end).stream() - .anyMatch(board::hasPiece)) { - throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); - } - } } diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java index 0ff35acedc..fdbbb0785a 100644 --- a/src/main/java/janggi/domain/piece/Piece.java +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -1,6 +1,5 @@ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.MovePath; import janggi.domain.Position; import janggi.domain.side.TeamType; @@ -29,13 +28,9 @@ public TeamType getTeamType() { return teamType; } - public void checkSamePosition(Position start, Position end) { - if (start.isSamePosition(end)) { - throw new IllegalArgumentException("출발지와 목적지가 동일합니다."); - } - } - - public abstract void validateCanMove(Position start, Position end, Board board); + public abstract void validateCanMove(List piecesInPath); protected abstract List getPaths(); + + public abstract List getPiecePositionsInPath(Position start, Position end); } diff --git a/src/main/java/janggi/domain/piece/Po.java b/src/main/java/janggi/domain/piece/Po.java index bd4ea00d21..9e240b780f 100644 --- a/src/main/java/janggi/domain/piece/Po.java +++ b/src/main/java/janggi/domain/piece/Po.java @@ -1,12 +1,10 @@ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.Delta; import janggi.domain.MovePath; -import janggi.domain.Position; import janggi.domain.side.TeamType; + import java.util.List; -import java.util.Optional; public class Po extends SlidingPiece { @@ -27,20 +25,14 @@ protected List getPaths() { } @Override - protected void validatePieceInPath(MovePath movePath, Position start, Position end, Board board) { - List intermediatePositions = movePath.intermediatePositions(start, end); - List obstacles = intermediatePositions.stream() - .map(board::findPiece) - .filter(Optional::isPresent) - .map(Optional::get) - .toList(); - if (obstacles.isEmpty()) { + public void validateCanMove(List piecesInPath) { + if (piecesInPath.isEmpty()) { throw new IllegalArgumentException("이동 경로에 기물이 존재하지 않아 이동할 수 없습니다."); } - if (obstacles.size() > 1) { + if (piecesInPath.size() > 1) { throw new IllegalArgumentException("이동 경로에 기물이 2개 이상 존재합니다."); } - if (obstacles.getFirst().getPieceType() == PieceType.PO) { + if (piecesInPath.getFirst().getPieceType() == PieceType.PO) { throw new IllegalArgumentException("포는 포를 넘을 수 없습니다."); } } diff --git a/src/main/java/janggi/domain/piece/SlidingPiece.java b/src/main/java/janggi/domain/piece/SlidingPiece.java index fc727a6d40..fc9c054a8a 100644 --- a/src/main/java/janggi/domain/piece/SlidingPiece.java +++ b/src/main/java/janggi/domain/piece/SlidingPiece.java @@ -1,10 +1,11 @@ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.MovePath; import janggi.domain.Position; import janggi.domain.side.TeamType; +import java.util.List; + public abstract class SlidingPiece extends Piece { public SlidingPiece(TeamType teamType, PieceType pieceType) { @@ -12,10 +13,9 @@ public SlidingPiece(TeamType teamType, PieceType pieceType) { } @Override - public void validateCanMove(Position start, Position end, Board board) { - checkSamePosition(start, end); + public List getPiecePositionsInPath(Position start, Position end) { MovePath movePath = findMovePath(start, end); - validatePieceInPath(movePath, start, end, board); + return movePath.intermediatePositions(start, end); } private MovePath findMovePath(Position start, Position end) { @@ -27,6 +27,4 @@ private MovePath findMovePath(Position start, Position end) { .findFirst() .orElseThrow(() -> new IllegalArgumentException("이동할 수 없는 위치입니다.")); } - - protected abstract void validatePieceInPath(MovePath movePath, Position start, Position end, Board board); } diff --git a/src/main/java/janggi/domain/piece/SteppingPiece.java b/src/main/java/janggi/domain/piece/SteppingPiece.java index 94209c00d4..3dbeaee437 100644 --- a/src/main/java/janggi/domain/piece/SteppingPiece.java +++ b/src/main/java/janggi/domain/piece/SteppingPiece.java @@ -1,10 +1,9 @@ package janggi.domain.piece; -import janggi.domain.Board; -import janggi.domain.MovePath; import janggi.domain.Position; import janggi.domain.side.TeamType; +import java.util.Collections; import java.util.List; public abstract class SteppingPiece extends Piece{ @@ -14,9 +13,16 @@ public SteppingPiece(TeamType teamType, PieceType pieceType) { } @Override - public void validateCanMove(Position start, Position end, Board board) { - checkSamePosition(start, end); + public void validateCanMove(List piecesInPath) { + if (!piecesInPath.isEmpty()) { + throw new IllegalArgumentException("이동 경로에 기물이 존재하여 이동할 수 없습니다."); + } + } + + @Override + public List getPiecePositionsInPath(Position start, Position end) { checkMovePath(start, end); + return Collections.emptyList(); } private void checkMovePath(Position start, Position end) { diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java index 4ba1e1dc05..2e90169524 100644 --- a/src/test/java/janggi/domain/BoardTest.java +++ b/src/test/java/janggi/domain/BoardTest.java @@ -2,6 +2,7 @@ import janggi.domain.side.TeamType; import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -9,11 +10,17 @@ class BoardTest { + private Board board; + + @BeforeEach + void setUp() { + board = Board.createInitialBoard(); + } + @Test @DisplayName("도착 좌표가 장기판 범위 밖일 경우 예외 발생") void cannotMoveWhenEndPositionIsOutOfRange() { // given - Board board = Board.createInitialBoard(); Position outOfBound = new Position(1, 11); // when & then @@ -22,11 +29,23 @@ void cannotMoveWhenEndPositionIsOutOfRange() { .hasMessage("입력한 좌표가 장기판 범위 밖입니다."); } + @Test + @DisplayName("출발지와 목적지가 동일할 경우 예외 발생") + void checkSamePosition() { + // given + Position start = new Position(4, 4); + Position end = new Position(4, 4); + + // when & then + assertThatThrownBy(() -> board.validateCanMove(start, end, TeamType.CHU)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("출발지와 목적지가 동일합니다."); + } + @Test @DisplayName("입력한 좌표에 기물이 없을 경우 false 반환") void cannotMoveWhenStartPositionHasNoCurrentTeamPiece() { // given - Board board = Board.createInitialBoard(); Position notExistPosition = new Position(2, 2); // when & then @@ -38,7 +57,6 @@ void cannotMoveWhenStartPositionHasNoCurrentTeamPiece() { @DisplayName("목적 좌표에 아군 기물이 있을 경우 예외 발생") void cannotMoveToSameTeamPiecePosition() { // given - Board board = Board.createInitialBoard(); Position currentTeamPosition = new Position(2, 1); // when & then diff --git a/src/test/java/janggi/domain/piece/ChaTest.java b/src/test/java/janggi/domain/piece/ChaTest.java index 0f05ace588..155c5fcc3a 100644 --- a/src/test/java/janggi/domain/piece/ChaTest.java +++ b/src/test/java/janggi/domain/piece/ChaTest.java @@ -1,13 +1,14 @@ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -16,45 +17,42 @@ class ChaTest { private Cha cha; - private Board board; @BeforeEach void setUp() { - board = Board.createInitialBoard(); cha = new Cha(TeamType.HAN); } - @ParameterizedTest - @DisplayName("차는 이동 경로에 장애물이 없다면 상하좌우 직선으로 이동할 수 있다.") - @CsvSource({ - "4, 5, 4, 9", - "4, 5, 4, 2", - "4, 5, 1, 5", - "4, 5, 9, 5" - }) - void validateCanMove_Success(int startX, int startY, int endX, int endY) { + @Test + @DisplayName("차는 이동 경로에 장애물이 없다면 이동할 수 있다.") + void validateCanMove_Success() { // given - Position start = new Position(startX, startY); - Position end = new Position(endX, endY); + List pieces = Collections.emptyList(); // when & then - assertThatCode(() -> cha.validateCanMove(start, end, board)) + assertThatCode(() -> cha.validateCanMove(pieces)) .doesNotThrowAnyException(); } - @Test - @DisplayName("제자리로 이동할 경우 예외 발생") - void validateCanMove_Fail_Same_Position() { - // given - Position start = new Position(4, 4); - Position sameEnd = new Position(4, 4); - - // when & then - assertThatThrownBy(() -> cha.validateCanMove(start, sameEnd, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("출발지와 목적지가 동일합니다."); - } + /** + * 제자리 테스트는 Board에서 + */ +// @Test +// @DisplayName("제자리로 이동할 경우 예외 발생") +// void validateCanMove_Fail_Same_Position() { +// // given +// Position start = new Position(4, 4); +// Position sameEnd = new Position(4, 4); +// +// // when & then +// assertThatThrownBy(() -> cha.validateCanMove(start, sameEnd, board)) +// .isInstanceOf(IllegalArgumentException.class) +// .hasMessage("출발지와 목적지가 동일합니다."); +// } + /** + * SlidingPiece에서 검증 + */ @Test @DisplayName("차가 이동할 수 없는 위치일 경우 예외 발생") void validateCanMove_Fail_Invalid_Position() { @@ -65,28 +63,23 @@ void validateCanMove_Fail_Invalid_Position() { // when & then assertAll( - () -> assertThatThrownBy(() -> cha.validateCanMove(start, diagonalEnd, board)) + () -> assertThatThrownBy(() -> cha.getPiecePositionsInPath(start, diagonalEnd)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> cha.validateCanMove(start, knightEnd, board)) + () -> assertThatThrownBy(() -> cha.getPiecePositionsInPath(start, knightEnd)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다.") ); } - @ParameterizedTest + @Test @DisplayName("이동 경로 중간에 기물이 존재할 경우 예외 발생") - @CsvSource({ - "4, 4, 1, 4", - "4, 4, 7, 4" - }) - void validateCanMove_Fail_ObstacleExist(int startX, int startY, int endX, int endY) { + void validateCanMove_Fail_Exist_Pieces_In_Path() { // given - Position start = new Position(startX, startY); - Position end = new Position(endX, endY); + List piecesInPath = new ArrayList<>(List.of(new Gung(TeamType.CHU))); // when & then - assertThatThrownBy(() -> cha.validateCanMove(start, end, board)) + assertThatThrownBy(() -> cha.validateCanMove(piecesInPath)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); } diff --git a/src/test/java/janggi/domain/piece/GungTest.java b/src/test/java/janggi/domain/piece/GungTest.java index a3f265aa2e..fb88e4f8d8 100644 --- a/src/test/java/janggi/domain/piece/GungTest.java +++ b/src/test/java/janggi/domain/piece/GungTest.java @@ -1,6 +1,8 @@ +/** + * gung은 steppingPiece 테스트로 검증 가능 + */ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; import org.junit.jupiter.api.BeforeEach; @@ -15,13 +17,10 @@ class GungTest { private Gung gung; - private Board board; @BeforeEach void setUp() { - // 한나라(HAN) 궁으로 설정 gung = new Gung(TeamType.HAN); - board = createBoard(); } @ParameterizedTest @@ -42,22 +41,31 @@ void validateCanMove_Success(int startX, int startY, int endX, int endY) { Position end = createPosition(endX, endY); // when & then - assertThatCode(() -> gung.validateCanMove(start, end, board)) + assertThatCode(() -> gung.getPiecePositionsInPath(start, end)) .doesNotThrowAnyException(); } - @Test - @DisplayName("제자리로 이동할 경우 예외 발생") - void validateCanMove_Fail_Same_Position() { - // given - Position start = new Position(4, 4); - Position sameEnd = new Position(4, 4); +// @Test +// @DisplayName("이동 경로에 기물이 존재하는 경우 예외 발생") +// void validateCanMove_Fail_Piece_Exists_In_Path() { +// List = new ArrayList<>(new Gung(TeamType.CHU)); +// } - // when & then - assertThatThrownBy(() -> gung.validateCanMove(start, sameEnd, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("출발지와 목적지가 동일합니다."); - } + /** + * 제자리 이동은 Board로 이전 + */ +// @Test +// @DisplayName("제자리로 이동할 경우 예외 발생") +// void validateCanMove_Fail_Same_Position() { +// // given +// Position start = new Position(4, 4); +// Position sameEnd = new Position(4, 4); +// +// // when & then +// assertThatThrownBy(() -> gung.validateCanMove(start, sameEnd, board)) +// .isInstanceOf(IllegalArgumentException.class) +// .hasMessage("출발지와 목적지가 동일합니다."); +// } @Test @DisplayName("궁이 이동할 수 없는 패턴(예: 마의 행마)인 경우 예외 발생") @@ -67,15 +75,11 @@ void validateCanMove_Fail_InvalidPattern() { Position knightEnd = createPosition(6, 7); // when & then - assertThatThrownBy(() -> gung.validateCanMove(start, knightEnd, board)) + assertThatThrownBy(() -> gung.getPiecePositionsInPath(start, knightEnd)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."); } - private Board createBoard() { - return Board.createInitialBoard(); - } - private Position createPosition(int x, int y) { return new Position(x, y); } diff --git a/src/test/java/janggi/domain/piece/JolTest.java b/src/test/java/janggi/domain/piece/JolTest.java index 10b3a542fa..4e26948b45 100644 --- a/src/test/java/janggi/domain/piece/JolTest.java +++ b/src/test/java/janggi/domain/piece/JolTest.java @@ -1,10 +1,10 @@ +/** + * 졸은 steppingPiece에서 테스트 가능 + * 다만 이동 경로에 대해서는 검증 필요할지도 + * 기물마다 다른 것은 각각 테스트해줘야 할듯 + */ package janggi.domain.piece; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - -import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; import org.junit.jupiter.api.BeforeEach; @@ -13,15 +13,17 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + class JolTest { - private Board board; private Jol chuJol; private Jol hanJol; @BeforeEach void setUp() { - board = Board.createInitialBoard(); chuJol = new Jol(TeamType.CHU); hanJol = new Jol(TeamType.HAN); } @@ -39,7 +41,7 @@ void validateCanMove_ChuJol_Success(int startX, int startY, int endX, int endY) Position end = new Position(endX, endY); // when & then - assertThatCode(() -> chuJol.validateCanMove(start, end, board)) + assertThatCode(() -> chuJol.getPiecePositionsInPath(start, end)) .doesNotThrowAnyException(); } @@ -56,22 +58,25 @@ void validateCanMove_HanJol_Success(int startX, int startY, int endX, int endY) Position end = new Position(endX, endY); // when & then - assertThatCode(() -> hanJol.validateCanMove(start, end, board)) + assertThatCode(() -> hanJol.getPiecePositionsInPath(start, end)) .doesNotThrowAnyException(); } - @Test - @DisplayName("제자리로 이동할 경우 예외 발생") - void validateCanMove_Fail_Same_Position() { - // given - Position start = new Position(4, 4); - Position sameEnd = new Position(4, 4); - - // when & then - assertThatThrownBy(() -> chuJol.validateCanMove(start, sameEnd, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("출발지와 목적지가 동일합니다."); - } + /** + * 제자리는 Board에서 + */ +// @Test +// @DisplayName("제자리로 이동할 경우 예외 발생") +// void validateCanMove_Fail_Same_Position() { +// // given +// Position start = new Position(4, 4); +// Position sameEnd = new Position(4, 4); +// +// // when & then +// assertThatThrownBy(() -> chuJol.validateCanMove(start, sameEnd, board)) +// .isInstanceOf(IllegalArgumentException.class) +// .hasMessage("출발지와 목적지가 동일합니다."); +// } @Test @DisplayName("졸이 이동할 수 없는 위치일 경우 예외 발생") @@ -83,15 +88,15 @@ void validateCanMove_Fail_Invalid_Position() { Position hanJolInvalidEnd = new Position(4, 5); Position diagonalEnd = new Position(5, 5); - /// when & then + // when & then assertAll( - () -> assertThatThrownBy(() -> chuJol.validateCanMove(chuJolStart, chuJolInvalidEnd, board)) + () -> assertThatThrownBy(() -> chuJol.getPiecePositionsInPath(chuJolStart, chuJolInvalidEnd)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> hanJol.validateCanMove(hanJolStart, hanJolInvalidEnd, board)) + () -> assertThatThrownBy(() -> hanJol.getPiecePositionsInPath(hanJolStart, hanJolInvalidEnd)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> chuJol.validateCanMove(chuJolStart, diagonalEnd, board)) + () -> assertThatThrownBy(() -> chuJol.getPiecePositionsInPath(chuJolStart, diagonalEnd)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다.") ); diff --git a/src/test/java/janggi/domain/piece/LeapingPieceTest.java b/src/test/java/janggi/domain/piece/LeapingPieceTest.java new file mode 100644 index 0000000000..99b5e4b398 --- /dev/null +++ b/src/test/java/janggi/domain/piece/LeapingPieceTest.java @@ -0,0 +1,26 @@ +package janggi.domain.piece; + +import janggi.domain.side.TeamType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LeapingPieceTest { + + @Test + @DisplayName("이동 경로에 기물이 존재할 경우 예외 발생") + void validateCanMove_Fail_Exist_Piece_In_Path() { + // given + List piecesInPath = new ArrayList<>(List.of(new Jol(TeamType.CHU))); + Ma ma = new Ma(TeamType.CHU); + + // when & then + assertThatThrownBy(() -> ma.validateCanMove(piecesInPath)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); + } +} \ No newline at end of file diff --git a/src/test/java/janggi/domain/piece/MaTest.java b/src/test/java/janggi/domain/piece/MaTest.java index 376575f712..53c4eddff8 100644 --- a/src/test/java/janggi/domain/piece/MaTest.java +++ b/src/test/java/janggi/domain/piece/MaTest.java @@ -1,6 +1,9 @@ +/** + * 마는 LeapingPiece에서 테스트해줘야 + * 근데 각 기물 이동 규칙은 여기서 테스트해야 함 + */ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; import org.junit.jupiter.api.BeforeEach; @@ -15,11 +18,9 @@ class MaTest { private Ma ma; - private Board board; @BeforeEach void setUp() { - board = Board.createInitialBoard(); ma = new Ma(TeamType.HAN); } @@ -37,22 +38,25 @@ void validateCanMove_Success(int startX, int startY, int endX, int endY) { Position end = new Position(endX, endY); // when & then - assertThatCode(() -> ma.validateCanMove(start, end, board)) + assertThatCode(() -> ma.getPiecePositionsInPath(start, end)) .doesNotThrowAnyException(); } - @Test - @DisplayName("제자리로 이동할 경우 예외 발생") - void validateCanMove_Fail_Same_Position() { - // given - Position start = new Position(4, 4); - Position sameEnd = new Position(4, 4); - - // when & then - assertThatThrownBy(() -> ma.validateCanMove(start, sameEnd, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("출발지와 목적지가 동일합니다."); - } + /** + * Board에서 진행 + */ +// @Test +// @DisplayName("제자리로 이동할 경우 예외 발생") +// void validateCanMove_Fail_Same_Position() { +// // given +// Position start = new Position(4, 4); +// Position sameEnd = new Position(4, 4); +// +// // when & then +// assertThatThrownBy(() -> ma.validateCanMove(start, sameEnd, board)) +// .isInstanceOf(IllegalArgumentException.class) +// .hasMessage("출발지와 목적지가 동일합니다."); +// } @Test @DisplayName("마가 이동할 수 없는 위치일 경우 예외 발생") @@ -62,27 +66,28 @@ void validateCanMove_Fail_Invalid_Position() { Position diagonalEnd = new Position(5, 5); // when & then - assertThatThrownBy(() -> ma.validateCanMove(start, diagonalEnd, board)) + assertThatThrownBy(() -> ma.getPiecePositionsInPath(start, diagonalEnd)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."); } - @ParameterizedTest - @DisplayName("이동 경로 중간에 기물이 존재할 경우 예외 발생") - @CsvSource({ - "4, 4, 6, 5", - "4, 4, 6, 3", - "4, 4, 2, 5", - "4, 4, 2, 3" - }) - void validateCanMove_Fail_ObstacleExist(int startX, int startY, int endX, int endY) { - // given - Position start = new Position(startX, startY); - Position end = new Position(endX, endY); - - // when & then - assertThatThrownBy(() -> ma.validateCanMove(start, end, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); - } + // LeapingPiece에서 테스트 +// @ParameterizedTest +// @DisplayName("이동 경로 중간에 기물이 존재할 경우 예외 발생") +// @CsvSource({ +// "4, 4, 6, 5", +// "4, 4, 6, 3", +// "4, 4, 2, 5", +// "4, 4, 2, 3" +// }) +// void validateCanMove_Fail_ObstacleExist(int startX, int startY, int endX, int endY) { +// // given +// Position start = new Position(startX, startY); +// Position end = new Position(endX, endY); +// +// // when & then +// assertThatThrownBy(() -> ma.getPiecePositionsInPath(start, end)) +// .isInstanceOf(IllegalArgumentException.class) +// .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); +// } } \ No newline at end of file diff --git a/src/test/java/janggi/domain/piece/PoTest.java b/src/test/java/janggi/domain/piece/PoTest.java index bbbf6112ea..7f292cdb6f 100644 --- a/src/test/java/janggi/domain/piece/PoTest.java +++ b/src/test/java/janggi/domain/piece/PoTest.java @@ -1,58 +1,62 @@ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; class PoTest { private Po po; - private Board board; @BeforeEach void setUp() { - board = Board.createInitialBoard(); po = new Po(TeamType.HAN); } - @ParameterizedTest + @Test @DisplayName("포는 경로 사이에 기물이 정확히 하나(포 제외) 있을 때만 넘어갈 수 있다.") - @CsvSource({ - "3, 8, 3, 6", - "1, 8, 1, 6", - "7, 8, 7, 6" - }) - void validateCanMove_Success(int startX, int startY, int endX, int endY) { + void validateCanMove_Success() { // given - Position start = new Position(startX, startY); - Position end = new Position(endX, endY); +// Position start = new Position(startX, startY); +// Position end = new Position(endX, endY); + List piecesInPath = new ArrayList<>(List.of(new Jol(TeamType.CHU))); // when & then - assertThatCode(() -> po.validateCanMove(start, end, board)) + assertThatCode(() -> po.validateCanMove(piecesInPath)) .doesNotThrowAnyException(); } - @Test - @DisplayName("제자리로 이동할 경우 예외 발생") - void validateCanMove_Fail_Same_Position() { - // given - Position start = new Position(4, 4); - Position sameEnd = new Position(4, 4); - - // when & then - assertThatThrownBy(() -> po.validateCanMove(start, sameEnd, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("출발지와 목적지가 동일합니다."); - } - + /** + * 이건 Board 테스트에서 + */ +// @Test +// @DisplayName("제자리로 이동할 경우 예외 발생") +// void validateCanMove_Fail_Same_Position() { +// // given +// Position start = new Position(4, 4); +// Position sameEnd = new Position(4, 4); +// +// // when & then +// assertThatThrownBy(() -> po.validateCanMove(start, sameEnd, board)) +// .isInstanceOf(IllegalArgumentException.class) +// .hasMessage("출발지와 목적지가 동일합니다."); +// } + + /** + * 이건 SlidingPiece에서 테스트 + */ @Test @DisplayName("포가 이동할 수 없는 위치일 경우 예외 발생") void validateCanMove_Fail_InvalidPattern() { @@ -61,18 +65,19 @@ void validateCanMove_Fail_InvalidPattern() { Position invalidEnd = new Position(5, 5); // when & then - assertThatThrownBy(() -> po.validateCanMove(start, invalidEnd, board)) + assertThatThrownBy(() -> po.getPiecePositionsInPath(start, invalidEnd)) .isInstanceOf(IllegalArgumentException.class); } @Test @DisplayName("이동 경로에 기물이 하나도 없을 경우 예외 발생") void validateCanMove_Fail_NoObstacle() { - Position start = new Position(4, 4); - Position end = new Position(4, 6); +// Position start = new Position(4, 4); +// Position end = new Position(4, 6); + List pieces = Collections.emptyList(); // when & then - assertThatThrownBy(() -> po.validateCanMove(start, end, board)) + assertThatThrownBy(() -> po.validateCanMove(pieces)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동 경로에 기물이 존재하지 않아 이동할 수 없습니다."); } @@ -80,11 +85,13 @@ void validateCanMove_Fail_NoObstacle() { @Test @DisplayName("이동 경로에 기물이 2개 이상일 경우 예외 발생") void validateCanMove_Fail_TooManyObstacles() { - Position start = new Position(2, 10); - Position end = new Position(2, 1); +// Position start = new Position(2, 10); +// Position end = new Position(2, 1); + + List pieces = new ArrayList<>(List.of(new Cha(TeamType.CHU), new Gung(TeamType.HAN))); // when & then - assertThatThrownBy(() -> po.validateCanMove(start, end, board)) + assertThatThrownBy(() -> po.validateCanMove(pieces)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동 경로에 기물이 2개 이상 존재합니다."); } @@ -92,11 +99,12 @@ void validateCanMove_Fail_TooManyObstacles() { @Test @DisplayName("포의 이동 경로에 포가 존재할 경우 예외 발생") void validateCanMove_Fail_PoAsObstacle() { - Position start = new Position(2, 9); - Position end = new Position(2, 7); +// Position start = new Position(2, 9); +// Position end = new Position(2, 7); + List pieces = new ArrayList<>(List.of(new Po(TeamType.CHU))); // when & then - assertThatThrownBy(() -> po.validateCanMove(start, end, board)) + assertThatThrownBy(() -> po.validateCanMove(pieces)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("포는 포를 넘을 수 없습니다."); } diff --git a/src/test/java/janggi/domain/piece/SaTest.java b/src/test/java/janggi/domain/piece/SaTest.java index a185be0b9b..b2afb6394f 100644 --- a/src/test/java/janggi/domain/piece/SaTest.java +++ b/src/test/java/janggi/domain/piece/SaTest.java @@ -1,6 +1,8 @@ +/** + * SteppingPiece에서 테스트 + */ package janggi.domain.piece; -import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; import org.junit.jupiter.api.BeforeEach; @@ -16,11 +18,9 @@ class SaTest { private Sa sa; - private Board board; @BeforeEach void setUp() { - board = Board.createInitialBoard(); sa = new Sa(TeamType.HAN); } @@ -39,22 +39,25 @@ void validateCanMove_Success(int startX, int startY, int endX, int endY) { Position end = new Position(endX, endY); // when & then - assertThatCode(() -> sa.validateCanMove(start, end, board)) + assertThatCode(() -> sa.getPiecePositionsInPath(start, end)) .doesNotThrowAnyException(); } - @Test - @DisplayName("제자리로 이동할 경우 예외 발생") - void validateCanMove_Fail_Same_Position() { - // given - Position start = new Position(4, 4); - Position sameEnd = new Position(4, 4); - - // when & then - assertThatThrownBy(() -> sa.validateCanMove(start, sameEnd, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("출발지와 목적지가 동일합니다."); - } + /** + * 제자리 테스트는 Board에서 + */ +// @Test +// @DisplayName("제자리로 이동할 경우 예외 발생") +// void validateCanMove_Fail_Same_Position() { +// // given +// Position start = new Position(4, 4); +// Position sameEnd = new Position(4, 4); +// +// // when & then +// assertThatThrownBy(() -> sa.validateCanMove(start, sameEnd, board)) +// .isInstanceOf(IllegalArgumentException.class) +// .hasMessage("출발지와 목적지가 동일합니다."); +// } @Test @DisplayName("사가 이동할 수 없는 위치일 경우 예외 발생") @@ -67,13 +70,13 @@ void validateCanMove_Fail_Invalid_Position() { // when & then assertAll( - () -> assertThatThrownBy(() -> sa.validateCanMove(start, moveTwoSteps, board)) + () -> assertThatThrownBy(() -> sa.getPiecePositionsInPath(start, moveTwoSteps)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> sa.validateCanMove(start, moveLongDiagonal, board)) + () -> assertThatThrownBy(() -> sa.getPiecePositionsInPath(start, moveLongDiagonal)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> sa.validateCanMove(start, knightMove, board)) + () -> assertThatThrownBy(() -> sa.getPiecePositionsInPath(start, knightMove)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다.") ); diff --git a/src/test/java/janggi/domain/piece/SangTest.java b/src/test/java/janggi/domain/piece/SangTest.java index fa8957f9e1..c917425740 100644 --- a/src/test/java/janggi/domain/piece/SangTest.java +++ b/src/test/java/janggi/domain/piece/SangTest.java @@ -1,10 +1,8 @@ +/** + * LeapingPiece에서 테스트 + */ package janggi.domain.piece; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - -import janggi.domain.Board; import janggi.domain.Position; import janggi.domain.side.TeamType; import org.junit.jupiter.api.BeforeEach; @@ -13,14 +11,16 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + class SangTest { private Sang sang; - private Board board; @BeforeEach void setUp() { - board = Board.createInitialBoard(); sang = new Sang(TeamType.HAN); } @@ -38,22 +38,25 @@ void validateCanMove_Success(int startX, int startY, int endX, int endY) { Position end = new Position(endX, endY); // when & then - assertThatCode(() -> sang.validateCanMove(start, end, board)) + assertThatCode(() -> sang.getPiecePositionsInPath(start, end)) .doesNotThrowAnyException(); } - @Test - @DisplayName("제자리로 이동할 경우 예외 발생") - void validateCanMove_Fail_Same_Position() { - // given - Position start = new Position(4, 4); - Position sameEnd = new Position(4, 4); - - // when & then - assertThatThrownBy(() -> sang.validateCanMove(start, sameEnd, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("출발지와 목적지가 동일합니다."); - } + /** + * Board에서 진행 + */ +// @Test +// @DisplayName("제자리로 이동할 경우 예외 발생") +// void validateCanMove_Fail_Same_Position() { +// // given +// Position start = new Position(4, 4); +// Position sameEnd = new Position(4, 4); +// +// // when & then +// assertThatThrownBy(() -> sang.validateCanMove(start, sameEnd, board)) +// .isInstanceOf(IllegalArgumentException.class) +// .hasMessage("출발지와 목적지가 동일합니다."); +// } @Test @DisplayName("상이 이동할 수 없는 위치일 경우 예외 발생") @@ -61,31 +64,33 @@ void validateCanMove_Fail_Invalid_Position() { Position start = new Position(4, 4); assertAll( - () -> assertThatThrownBy(() -> sang.validateCanMove(start, new Position(4, 7), board)) + () -> assertThatThrownBy(() -> sang.getPiecePositionsInPath(start, new Position(4, 7))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다."), - () -> assertThatThrownBy(() -> sang.validateCanMove(start, new Position(5, 6), board)) + () -> assertThatThrownBy(() -> sang.getPiecePositionsInPath(start, new Position(5, 6))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 위치입니다.") ); } - - @ParameterizedTest - @DisplayName("이동 경로 중간에 기물이 존재할 경우 예외 발생") - @CsvSource({ - "4, 4, 7, 6", - "4, 4, 1, 6", - "4, 4, 7, 2", - "4, 4, 1, 2" - }) - void validateCanMove_Fail_ObstacleExist(int startX, int startY, int endX, int endY) { - // given - Position start = new Position(startX, startY); - Position end = new Position(endX, endY); - - // when & then - assertThatThrownBy(() -> sang.validateCanMove(start, end, board)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); - } +/** + * LeapingPiece에서 테스트 + */ +// @ParameterizedTest +// @DisplayName("이동 경로 중간에 기물이 존재할 경우 예외 발생") +// @CsvSource({ +// "4, 4, 7, 6", +// "4, 4, 1, 6", +// "4, 4, 7, 2", +// "4, 4, 1, 2" +// }) +// void validateCanMove_Fail_ObstacleExist(int startX, int startY, int endX, int endY) { +// // given +// Position start = new Position(startX, startY); +// Position end = new Position(endX, endY); +// +// // when & then +// assertThatThrownBy(() -> sang.validateCanMove(start, end, board)) +// .isInstanceOf(IllegalArgumentException.class) +// .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); +// } } \ No newline at end of file diff --git a/src/test/java/janggi/domain/piece/SteppingPieceTest.java b/src/test/java/janggi/domain/piece/SteppingPieceTest.java new file mode 100644 index 0000000000..5e58b12cb9 --- /dev/null +++ b/src/test/java/janggi/domain/piece/SteppingPieceTest.java @@ -0,0 +1,28 @@ +package janggi.domain.piece; + +import janggi.domain.side.TeamType; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class SteppingPieceTest { + + + @Test + @DisplayName("이동 경로에 기물이 존재할 경우 예외 발생") + void validateCanMove_Fail_Exist_Pieces_In_Path() { + // given + List piecesInPath = new ArrayList<>(List.of(new Jol(TeamType.CHU))); + Gung gung = new Gung(TeamType.CHU); + + // when & then + assertThatThrownBy(() -> gung.validateCanMove(piecesInPath)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동 경로에 기물이 존재하여 이동할 수 없습니다."); + } +} \ No newline at end of file