From c8daa3f0187f008f7638b39b2dbb1c7a09c6bb51 Mon Sep 17 00:00:00 2001 From: kcnsmoothie Date: Thu, 5 Mar 2026 11:21:20 +0900 Subject: [PATCH 01/48] =?UTF-8?q?docs:=20README=20=EC=B4=88=EC=95=88=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ff5f7b6790..e80e0a40f84 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,51 @@ # java-blackjack -블랙잭 미션 저장소 +## ✅ 게임 기능 TO-DO + +### 게임에 참여할 사람의 이름 입력 + +- [ ] 쉼표 기준으로 분리 +- [ ] 정상적인 이름 앞뒤의 공백 제거 +- [ ] `ㅁㅁ, ,ㅇㅇ` → 예외처리 + +### 각 플레이어, 딜러에게 카드를 랜덤으로 2장 배분하는 기능 + +- [ ] 딜러는 2번째 카드는 출력하지 않음 +- [ ] 이미 정해진 덱에서 랜덤 분배 + +### 플레이어에게 카드를 더 받을지 묻는 기능 + +- [ ] 이미 뽑힌 카드는 다시 나오지 않게 처리 +- [ ] 한 플레이어가 n을 입력하면 해당 플레이어 종료 + +### 딜러가 카드를 더 받는지 출력 + +- [ ] 16 이하라 한 장 받을 때마다 문장 출력 +- [ ] 여러 장 받을 경우 한 장씩 문장 반복 출력 +- [ ] 처음부터 17 이상이면 카드 추가 없이 안내 문장 출력 여부 결정 + +### 카드 객체 설계 + +- [ ] 전체 카드 덱을 미리 생성 +- [ ] deck을 랜덤 추출 기능으로 구현 +- [ ] suit, rank를 각각 Enum으로 관리 +- [ ] 카드 문양, 표기 점수 관리 방식 결정 + +### 카드 점수 합 계산 기능 + +- [ ] rank 값만 추출하여 합산 +- [ ] 총합 계산 로직 구현 + +### 결과 출력 + +- [ ] 딜러가 가진 카드 목록 출력 +- [ ] 딜러 카드 점수 합 출력 +- [ ] 플레이어가 가진 카드 목록 출력 +- [ ] 플레이어 카드 점수 합 출력 + +### 승패 출력 + +- [ ] 계산된 합에 따라 승패 판별 +- [ ] 라운드 반복 여부 설계 + +### 라운드 반복 기능 구현 \ No newline at end of file From 6cdbba21047165adeb95e909f7df8922981fb0e8 Mon Sep 17 00:00:00 2001 From: kcnsmoothie Date: Thu, 5 Mar 2026 17:08:39 +0900 Subject: [PATCH 02/48] =?UTF-8?q?feat:=20=EA=B3=B5=EB=B0=B1=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=9E=85=EB=A0=A5=EC=8B=9C=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=B0=9C=EC=83=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/main/java/BlackjackMain.java | 6 ++++++ src/main/java/domain/Players.java | 6 ++++++ src/main/java/view/InputView.java | 21 +++++++++++++++++++++ src/main/java/view/OutputView.java | 4 ++++ src/test/java/domain/CardTest.java | 5 +++++ src/test/java/view/InputViewTest.java | 27 +++++++++++++++++++++++++++ 7 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/main/java/BlackjackMain.java create mode 100644 src/main/java/domain/Players.java create mode 100644 src/main/java/view/InputView.java create mode 100644 src/main/java/view/OutputView.java create mode 100644 src/test/java/domain/CardTest.java create mode 100644 src/test/java/view/InputViewTest.java diff --git a/README.md b/README.md index e80e0a40f84..732df059fa1 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ - [ ] 쉼표 기준으로 분리 - [ ] 정상적인 이름 앞뒤의 공백 제거 -- [ ] `ㅁㅁ, ,ㅇㅇ` → 예외처리 +- [ ] `ㅁㅁ, ,ㅇㅇ` → 빈 문자열은 플레이어에서 제외 ### 각 플레이어, 딜러에게 카드를 랜덤으로 2장 배분하는 기능 diff --git a/src/main/java/BlackjackMain.java b/src/main/java/BlackjackMain.java new file mode 100644 index 00000000000..c70cad60d1e --- /dev/null +++ b/src/main/java/BlackjackMain.java @@ -0,0 +1,6 @@ +public class BlackjackMain { + + public static void main(String[] args) { + + } +} diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java new file mode 100644 index 00000000000..ca943527139 --- /dev/null +++ b/src/main/java/domain/Players.java @@ -0,0 +1,6 @@ +package domain; + +public class Players { + + +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000000..1999252c971 --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,21 @@ +package view; + +import java.util.List; +import java.util.Scanner; + +public class InputView { + Scanner sc = new Scanner(System.in); + + public String inputPurchaseAmount() { + System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); + return sc.nextLine(); + } + + public void isBlank(List names) { + for (String name : names) { + if (name.isBlank()) { + throw new IllegalArgumentException(); + } + } + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 00000000000..d8f9743ccfe --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,4 @@ +package view; + +public class OutputView { +} diff --git a/src/test/java/domain/CardTest.java b/src/test/java/domain/CardTest.java new file mode 100644 index 00000000000..adf7b99559a --- /dev/null +++ b/src/test/java/domain/CardTest.java @@ -0,0 +1,5 @@ +package domain; + +public class CardTest { + +} diff --git a/src/test/java/view/InputViewTest.java b/src/test/java/view/InputViewTest.java new file mode 100644 index 00000000000..fa3702d8dee --- /dev/null +++ b/src/test/java/view/InputViewTest.java @@ -0,0 +1,27 @@ +package view; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class InputViewTest { + + @DisplayName("이름에 공백만 있으면 예외 처리.") + @Test + void Trim이후_이름이_없을경우() { + + String names = "아나키, , 모아"; + List playerName = Arrays.stream(names.split(",")) + .map(String::trim) + .toList(); + + + assertThatThrownBy(() -> new InputView().isBlank(playerName)) + .isInstanceOf(IllegalArgumentException.class); + } + +} From 9ff221c9af8ebf492634a154abdc8828281f3967 Mon Sep 17 00:00:00 2001 From: kcnsmoothie Date: Thu, 5 Mar 2026 17:21:17 +0900 Subject: [PATCH 03/48] =?UTF-8?q?feat:=20=EC=B9=B4=EB=93=9C=20=EB=8D=B1=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9D=84=20=EC=9C=84=ED=95=9C=20Enum=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Enum/Rank.java | 27 +++++++++++++++++++++++++++ src/main/java/Enum/Suit.java | 18 ++++++++++++++++++ src/main/java/domain/Players.java | 1 + src/test/java/domain/PlayersTest.java | 25 +++++++++++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 src/main/java/Enum/Rank.java create mode 100644 src/main/java/Enum/Suit.java create mode 100644 src/test/java/domain/PlayersTest.java diff --git a/src/main/java/Enum/Rank.java b/src/main/java/Enum/Rank.java new file mode 100644 index 00000000000..95abd4ec7a8 --- /dev/null +++ b/src/main/java/Enum/Rank.java @@ -0,0 +1,27 @@ +package Enum; + +public enum Rank { + ACE(1), + TWO(2), + THREE(3), + FOUR(4), + FIVE(5), + SIX(6), + SEVEN(7), + EIGHT(8), + NINE(9), + TEN(10), + JACK(10), + QUEEN(10), + KING(10); + + private final int score; + + Rank(int score) { + this.score = score; + } + + public int getScore() { + return score; + } +} diff --git a/src/main/java/Enum/Suit.java b/src/main/java/Enum/Suit.java new file mode 100644 index 00000000000..979afb4d9b5 --- /dev/null +++ b/src/main/java/Enum/Suit.java @@ -0,0 +1,18 @@ +package Enum; + +public enum Suit { + HEART("하트"), + DIAMOND("다이아몬드"), + SPADE("스페이드"), + CLOVER("클로버"); + + private final String shape; + + Suit(String shape) { + this.shape = shape; + } + + public String getShape() { + return shape; + } +} diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java index ca943527139..c271e82ec4d 100644 --- a/src/main/java/domain/Players.java +++ b/src/main/java/domain/Players.java @@ -2,5 +2,6 @@ public class Players { + public } diff --git a/src/test/java/domain/PlayersTest.java b/src/test/java/domain/PlayersTest.java new file mode 100644 index 00000000000..2e39ef7a08a --- /dev/null +++ b/src/test/java/domain/PlayersTest.java @@ -0,0 +1,25 @@ +package domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class PlayersTest { + + @DisplayName("이름 공백 제거 후 성공") + @Test + void 이름이_정상적으로_들어왔을때() { + String names = "아나키, 포비, 모아"; + List playerName = Arrays.stream(names.split(",")) + .map(String::trim) + .toList(); + + Players players = new Players(playerName); + + assertThat(players.getSize()).isEqualTo(3); + + } +} From 648fe9b4f3957aef03b8a627c495e70a3db16ab4 Mon Sep 17 00:00:00 2001 From: kcnsmoothie Date: Thu, 5 Mar 2026 17:57:54 +0900 Subject: [PATCH 04/48] =?UTF-8?q?test:=20=EC=B9=B4=EB=93=9C=20=EB=8D=B1?= =?UTF-8?q?=EC=9D=B4=2052=EC=9E=A5=20=EC=83=9D=EC=84=B1=EB=90=98=EC=97=88?= =?UTF-8?q?=EB=8A=94=EC=A7=80=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/CardTest.java | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/java/domain/CardTest.java b/src/test/java/domain/CardTest.java index adf7b99559a..20c971084c0 100644 --- a/src/test/java/domain/CardTest.java +++ b/src/test/java/domain/CardTest.java @@ -1,5 +1,25 @@ package domain; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + public class CardTest { + Deck deck; + + @BeforeEach + void beforeEach() { + deck = new Deck(); + } + @DisplayName("덱 생성 성공") + @Test + void 덱에_52장의_카드_생성() { + deck.init(); + assertThat(deck.cards.size()).isEqualTo(52); + } } From cbedd0e9d893d52b3006fe348d6759ba40dc5f34 Mon Sep 17 00:00:00 2001 From: kcnsmoothie Date: Thu, 5 Mar 2026 20:26:04 +0900 Subject: [PATCH 05/48] =?UTF-8?q?test:=20=EB=8D=B1=20=EC=85=94=ED=94=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/{CardTest.java => DeckTest.java} | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) rename src/test/java/domain/{CardTest.java => DeckTest.java} (54%) diff --git a/src/test/java/domain/CardTest.java b/src/test/java/domain/DeckTest.java similarity index 54% rename from src/test/java/domain/CardTest.java rename to src/test/java/domain/DeckTest.java index 20c971084c0..3a74db87d97 100644 --- a/src/test/java/domain/CardTest.java +++ b/src/test/java/domain/DeckTest.java @@ -2,13 +2,13 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import java.util.Arrays; -import java.util.List; +import constant.Rank; +import constant.Suit; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -public class CardTest { +public class DeckTest { Deck deck; @BeforeEach @@ -22,4 +22,16 @@ void beforeEach() { deck.init(); assertThat(deck.cards.size()).isEqualTo(52); } + + @DisplayName("덱이 잘 섞였는지 확인") + @Test + void 섞인_덱에서_첫번째_카드_추출() { + Card testCard = new Card(Rank.ACE, Suit.HEART); + + deck.init(); + deck.shuffle(); + Card randomCard = deck.draw(); + + assertThat(testCard).isNotEqualTo(randomCard); + } } From d17084e1456628fb360290cd35cecd91afb5c3ea Mon Sep 17 00:00:00 2001 From: kcnsmoothie Date: Thu, 5 Mar 2026 20:39:10 +0900 Subject: [PATCH 06/48] =?UTF-8?q?feat:=20=EC=B9=B4=EB=93=9C=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 ++++----- src/main/java/BlackjackMain.java | 5 ++- src/main/java/{Enum => constant}/Rank.java | 2 +- src/main/java/{Enum => constant}/Suit.java | 2 +- .../java/controller/BlackjackController.java | 14 +++++++++ src/main/java/domain/Card.java | 14 +++++++++ src/main/java/domain/Deck.java | 31 +++++++++++++++++++ src/main/java/domain/Player.java | 16 ++++++++++ src/main/java/domain/Players.java | 5 ++- src/main/java/view/InputView.java | 9 ++++-- src/test/java/domain/PlayersTest.java | 20 +++++++----- 11 files changed, 112 insertions(+), 20 deletions(-) rename src/main/java/{Enum => constant}/Rank.java (94%) rename src/main/java/{Enum => constant}/Suit.java (93%) create mode 100644 src/main/java/controller/BlackjackController.java create mode 100644 src/main/java/domain/Card.java create mode 100644 src/main/java/domain/Deck.java create mode 100644 src/main/java/domain/Player.java diff --git a/README.md b/README.md index 732df059fa1..7cbdd1c2d5d 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ ### 게임에 참여할 사람의 이름 입력 -- [ ] 쉼표 기준으로 분리 -- [ ] 정상적인 이름 앞뒤의 공백 제거 -- [ ] `ㅁㅁ, ,ㅇㅇ` → 빈 문자열은 플레이어에서 제외 +- [x] 쉼표 기준으로 분리 +- [x] 정상적인 이름 앞뒤의 공백 제거 +- [x] `ㅁㅁ, ,ㅇㅇ` → 빈 문자열은 예외처리 ### 각 플레이어, 딜러에게 카드를 랜덤으로 2장 배분하는 기능 @@ -26,10 +26,10 @@ ### 카드 객체 설계 -- [ ] 전체 카드 덱을 미리 생성 -- [ ] deck을 랜덤 추출 기능으로 구현 -- [ ] suit, rank를 각각 Enum으로 관리 -- [ ] 카드 문양, 표기 점수 관리 방식 결정 +- [x] 전체 카드 덱을 미리 생성 +- [x] deck을 랜덤 추출 기능으로 구현 +- [x] suit, rank를 각각 Enum으로 관리 +- [x] 카드 문양, 표기 점수 관리 방식 결정 ### 카드 점수 합 계산 기능 diff --git a/src/main/java/BlackjackMain.java b/src/main/java/BlackjackMain.java index c70cad60d1e..eb831b1ae22 100644 --- a/src/main/java/BlackjackMain.java +++ b/src/main/java/BlackjackMain.java @@ -1,6 +1,9 @@ +import domain.Deck; + public class BlackjackMain { public static void main(String[] args) { - + Deck deck = new Deck(); + deck.init(); } } diff --git a/src/main/java/Enum/Rank.java b/src/main/java/constant/Rank.java similarity index 94% rename from src/main/java/Enum/Rank.java rename to src/main/java/constant/Rank.java index 95abd4ec7a8..0dbdb87a49d 100644 --- a/src/main/java/Enum/Rank.java +++ b/src/main/java/constant/Rank.java @@ -1,4 +1,4 @@ -package Enum; +package constant; public enum Rank { ACE(1), diff --git a/src/main/java/Enum/Suit.java b/src/main/java/constant/Suit.java similarity index 93% rename from src/main/java/Enum/Suit.java rename to src/main/java/constant/Suit.java index 979afb4d9b5..21ee89c570c 100644 --- a/src/main/java/Enum/Suit.java +++ b/src/main/java/constant/Suit.java @@ -1,4 +1,4 @@ -package Enum; +package constant; public enum Suit { HEART("하트"), diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java new file mode 100644 index 00000000000..46f7ff61714 --- /dev/null +++ b/src/main/java/controller/BlackjackController.java @@ -0,0 +1,14 @@ +package controller; + +public class BlackjackController { + // 이름 입력 후 player 객체 생성 + + // 각 플레이어에게 카드 배분 + + // 게임 진행 + + // 딜러 차례 + + // 결과 발표 + +} diff --git a/src/main/java/domain/Card.java b/src/main/java/domain/Card.java new file mode 100644 index 00000000000..1227e283383 --- /dev/null +++ b/src/main/java/domain/Card.java @@ -0,0 +1,14 @@ +package domain; + +import constant.Rank; +import constant.Suit; + +public class Card { + private final Rank rank; + private final Suit suit; + + public Card(Rank rank, Suit suit) { + this.rank = rank; + this.suit = suit; + } +} diff --git a/src/main/java/domain/Deck.java b/src/main/java/domain/Deck.java new file mode 100644 index 00000000000..06e0dd29135 --- /dev/null +++ b/src/main/java/domain/Deck.java @@ -0,0 +1,31 @@ +package domain; + +import constant.Rank; +import constant.Suit; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Deck { + List cards = new ArrayList<>(); + //enum을 순회하면서 조합?? + + public void init() { + for (Rank rank : Rank.values()) { + for (Suit suit : Suit.values()) { + Card card = new Card(rank, suit); + cards.add(card); + } + } + } + + public void shuffle() { + Collections.shuffle(cards); + } + + public Card draw() { + int lastIndex = cards.size() - 1; + return cards.remove(lastIndex); + } + +} diff --git a/src/main/java/domain/Player.java b/src/main/java/domain/Player.java new file mode 100644 index 00000000000..ba93818ec08 --- /dev/null +++ b/src/main/java/domain/Player.java @@ -0,0 +1,16 @@ +package domain; + +import java.util.List; + +public class Player { + private final String name; + private List playerCard; + + + public Player(String name) { + this.name = name; + } + + + +} diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java index c271e82ec4d..43130a871e7 100644 --- a/src/main/java/domain/Players.java +++ b/src/main/java/domain/Players.java @@ -1,7 +1,10 @@ package domain; +import java.util.List; + public class Players { - public + private List playerList; + } diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 1999252c971..c14da78e0ee 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,14 +1,19 @@ package view; +import java.util.Arrays; import java.util.List; import java.util.Scanner; public class InputView { Scanner sc = new Scanner(System.in); - public String inputPurchaseAmount() { + public List inputPlayers() { System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); - return sc.nextLine(); + String rawPlayers = sc.nextLine(); + List playerName = Arrays.stream(rawPlayers.split(",")) + .map(String::trim) + .toList(); + return playerName; } public void isBlank(List names) { diff --git a/src/test/java/domain/PlayersTest.java b/src/test/java/domain/PlayersTest.java index 2e39ef7a08a..98382efd03b 100644 --- a/src/test/java/domain/PlayersTest.java +++ b/src/test/java/domain/PlayersTest.java @@ -1,25 +1,31 @@ package domain; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import java.util.Arrays; import java.util.List; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import view.InputView; public class PlayersTest { - @DisplayName("이름 공백 제거 후 성공") + InputView input; + @BeforeEach + void beforeEach() { + input = new InputView(); + } + @DisplayName("입력에 따른 Player 객체 생성") @Test void 이름이_정상적으로_들어왔을때() { - String names = "아나키, 포비, 모아"; - List playerName = Arrays.stream(names.split(",")) - .map(String::trim) - .toList(); + List names = Arrays.asList("아나키", "포비", "모아"); - Players players = new Players(playerName); + /*assertThat()*/ + /*Players players = new Players(playerName);*/ - assertThat(players.getSize()).isEqualTo(3); + //assertThat(players.getSize()).isEqualTo(3); } } From c102d2417adb49a82e0bb70297254aa6347407bf Mon Sep 17 00:00:00 2001 From: picetea44 Date: Mon, 9 Mar 2026 00:41:56 +0900 Subject: [PATCH 07/48] =?UTF-8?q?feat:=20Card=20TDD=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Card.java | 7 ++++--- src/main/java/domain/Deck.java | 2 -- src/main/java/{constant => domain}/Rank.java | 2 +- src/main/java/{constant => domain}/Suit.java | 2 +- src/test/java/domain/CardTest.java | 21 ++++++++++++++++++++ src/test/java/domain/DeckTest.java | 2 -- 6 files changed, 27 insertions(+), 9 deletions(-) rename src/main/java/{constant => domain}/Rank.java (94%) rename src/main/java/{constant => domain}/Suit.java (93%) create mode 100644 src/test/java/domain/CardTest.java diff --git a/src/main/java/domain/Card.java b/src/main/java/domain/Card.java index 1227e283383..b44f1fda262 100644 --- a/src/main/java/domain/Card.java +++ b/src/main/java/domain/Card.java @@ -1,8 +1,5 @@ package domain; -import constant.Rank; -import constant.Suit; - public class Card { private final Rank rank; private final Suit suit; @@ -11,4 +8,8 @@ public Card(Rank rank, Suit suit) { this.rank = rank; this.suit = suit; } + + public int getScore() { + return rank.getScore(); + } } diff --git a/src/main/java/domain/Deck.java b/src/main/java/domain/Deck.java index 06e0dd29135..a37c83b9ec6 100644 --- a/src/main/java/domain/Deck.java +++ b/src/main/java/domain/Deck.java @@ -1,7 +1,5 @@ package domain; -import constant.Rank; -import constant.Suit; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/src/main/java/constant/Rank.java b/src/main/java/domain/Rank.java similarity index 94% rename from src/main/java/constant/Rank.java rename to src/main/java/domain/Rank.java index 0dbdb87a49d..93dc2c2763f 100644 --- a/src/main/java/constant/Rank.java +++ b/src/main/java/domain/Rank.java @@ -1,4 +1,4 @@ -package constant; +package domain; public enum Rank { ACE(1), diff --git a/src/main/java/constant/Suit.java b/src/main/java/domain/Suit.java similarity index 93% rename from src/main/java/constant/Suit.java rename to src/main/java/domain/Suit.java index 21ee89c570c..04052b65798 100644 --- a/src/main/java/constant/Suit.java +++ b/src/main/java/domain/Suit.java @@ -1,4 +1,4 @@ -package constant; +package domain; public enum Suit { HEART("하트"), diff --git a/src/test/java/domain/CardTest.java b/src/test/java/domain/CardTest.java new file mode 100644 index 00000000000..02ccba80d83 --- /dev/null +++ b/src/test/java/domain/CardTest.java @@ -0,0 +1,21 @@ +package domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.Test; + +class CardTest { + @Test + void 카드의_점수를_반환한다() { + Card card = new Card(Rank.KING, Suit.SPADE); + assertThat(card.getScore()).isEqualTo(10); + } + + @Test + void ACE의_점수는_1이다() { + Card card = new Card(Rank.ACE, Suit.HEART); + assertThat(card.getScore()).isEqualTo(1); + } + + +} \ No newline at end of file diff --git a/src/test/java/domain/DeckTest.java b/src/test/java/domain/DeckTest.java index 3a74db87d97..0755cd6154d 100644 --- a/src/test/java/domain/DeckTest.java +++ b/src/test/java/domain/DeckTest.java @@ -2,8 +2,6 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import constant.Rank; -import constant.Suit; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From abffda78645782f2345b31e3142b5dcf14db4b97 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Mon, 9 Mar 2026 01:10:16 +0900 Subject: [PATCH 08/48] =?UTF-8?q?feat:=20Deck=20TDD=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/BlackjackMain.java | 1 - src/main/java/domain/Deck.java | 14 ++++++-------- src/test/java/domain/DeckTest.java | 20 +++++++------------- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/main/java/BlackjackMain.java b/src/main/java/BlackjackMain.java index eb831b1ae22..e590ba6f65a 100644 --- a/src/main/java/BlackjackMain.java +++ b/src/main/java/BlackjackMain.java @@ -4,6 +4,5 @@ public class BlackjackMain { public static void main(String[] args) { Deck deck = new Deck(); - deck.init(); } } diff --git a/src/main/java/domain/Deck.java b/src/main/java/domain/Deck.java index a37c83b9ec6..8dae7ba2d01 100644 --- a/src/main/java/domain/Deck.java +++ b/src/main/java/domain/Deck.java @@ -5,19 +5,14 @@ import java.util.List; public class Deck { - List cards = new ArrayList<>(); - //enum을 순회하면서 조합?? + private final List cards = new ArrayList<>(); - public void init() { + public Deck() { for (Rank rank : Rank.values()) { for (Suit suit : Suit.values()) { - Card card = new Card(rank, suit); - cards.add(card); + cards.add(new Card(rank, suit)); } } - } - - public void shuffle() { Collections.shuffle(cards); } @@ -26,4 +21,7 @@ public Card draw() { return cards.remove(lastIndex); } + public int size() { + return cards.size(); + } } diff --git a/src/test/java/domain/DeckTest.java b/src/test/java/domain/DeckTest.java index 0755cd6154d..9b18395310c 100644 --- a/src/test/java/domain/DeckTest.java +++ b/src/test/java/domain/DeckTest.java @@ -14,22 +14,16 @@ void beforeEach() { deck = new Deck(); } - @DisplayName("덱 생성 성공") + @DisplayName("덱을 생성하면 52장이다") @Test - void 덱에_52장의_카드_생성() { - deck.init(); - assertThat(deck.cards.size()).isEqualTo(52); + void 덱을_생성하면_52장이다() { + assertThat(deck.size()).isEqualTo(52); } - @DisplayName("덱이 잘 섞였는지 확인") + @DisplayName("카드를 뽑으면 덱이 줄어든다") @Test - void 섞인_덱에서_첫번째_카드_추출() { - Card testCard = new Card(Rank.ACE, Suit.HEART); - - deck.init(); - deck.shuffle(); - Card randomCard = deck.draw(); - - assertThat(testCard).isNotEqualTo(randomCard); + void 카드를_뽑으면_덱이_줄어든다() { + deck.draw(); + assertThat(deck.size()).isEqualTo(51); } } From 6107d0d886a09dd81517e27fe91a0c2f722da73f Mon Sep 17 00:00:00 2001 From: picetea44 Date: Mon, 9 Mar 2026 01:31:33 +0900 Subject: [PATCH 09/48] =?UTF-8?q?feat:=20Deck=20=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=AA=A8=EB=93=A0=20=EC=B9=B4=EB=93=9C=EB=A5=BC=20=EB=BD=91?= =?UTF-8?q?=EC=95=98=EC=9D=84=20=EB=95=8C=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Deck.java | 6 ++++-- src/test/java/domain/DeckTest.java | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/domain/Deck.java b/src/main/java/domain/Deck.java index 8dae7ba2d01..f96054ee0dd 100644 --- a/src/main/java/domain/Deck.java +++ b/src/main/java/domain/Deck.java @@ -17,8 +17,10 @@ public Deck() { } public Card draw() { - int lastIndex = cards.size() - 1; - return cards.remove(lastIndex); + if (cards.isEmpty()) { + throw new IllegalStateException("덱에 카드가 없습니다."); + } + return cards.remove(cards.size() - 1); } public int size() { diff --git a/src/test/java/domain/DeckTest.java b/src/test/java/domain/DeckTest.java index 9b18395310c..4e616963c7d 100644 --- a/src/test/java/domain/DeckTest.java +++ b/src/test/java/domain/DeckTest.java @@ -1,6 +1,7 @@ package domain; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -26,4 +27,14 @@ void beforeEach() { deck.draw(); assertThat(deck.size()).isEqualTo(51); } + + @DisplayName("빈 덱에서 카드를 뽑으면 예외가 발생한다") + @Test + void 빈_덱에서_카드를_뽑으면_예외가_발생한다() { + assertThatThrownBy(() -> { + for (int i = 0; i <= 52; i++) { + deck.draw(); + } + }).isInstanceOf(IllegalStateException.class); + } } From 16188abf9986bfbc54829b77d539b089d7624b2d Mon Sep 17 00:00:00 2001 From: picetea44 Date: Mon, 9 Mar 2026 01:53:31 +0900 Subject: [PATCH 10/48] =?UTF-8?q?feat:=20=ED=94=8C=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=20=EC=B9=B4=EB=93=9C=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EC=A0=90=EC=88=98=20=EA=B3=84=EC=82=B0,=20=EC=97=90=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=ED=8C=90=EB=B3=84=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/domain/Card.java | 4 +++ src/main/java/domain/Player.java | 31 ++++++++++++++++-- src/test/java/domain/PlayerTest.java | 49 ++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 src/test/java/domain/PlayerTest.java diff --git a/src/main/java/domain/Card.java b/src/main/java/domain/Card.java index b44f1fda262..b34bf42836e 100644 --- a/src/main/java/domain/Card.java +++ b/src/main/java/domain/Card.java @@ -12,4 +12,8 @@ public Card(Rank rank, Suit suit) { public int getScore() { return rank.getScore(); } + + public boolean isAce() { + return rank == Rank.ACE; + } } diff --git a/src/main/java/domain/Player.java b/src/main/java/domain/Player.java index ba93818ec08..1b6da029012 100644 --- a/src/main/java/domain/Player.java +++ b/src/main/java/domain/Player.java @@ -1,16 +1,41 @@ package domain; +import java.util.ArrayList; import java.util.List; public class Player { private final String name; - private List playerCard; - + private final List cards = new ArrayList<>(); public Player(String name) { this.name = name; } + public void addCard(Card card) { + cards.add(card); + } + + public int calculateScore() { + int score = 0; + boolean hasAce = false; + for (Card card : cards) { + score += card.getScore(); + if (card.isAce()) { + hasAce = true; + } + } + return applyAceBonus(score, hasAce); + } -} + private int applyAceBonus(int score, boolean hasAce) { + if (hasAce && score + 10 <= 21) { + return score + 10; + } + return score; + } + + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/src/test/java/domain/PlayerTest.java b/src/test/java/domain/PlayerTest.java new file mode 100644 index 00000000000..353abf9516d --- /dev/null +++ b/src/test/java/domain/PlayerTest.java @@ -0,0 +1,49 @@ +package domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class PlayerTest { + Player player; + + @BeforeEach + void beforeEach() { + player = new Player("아나키"); + } + + @DisplayName("일반 카드의 점수를 합산한다") + @Test + void 일반_카드의_점수를_합산한다() { + player.addCard(new Card(Rank.KING, Suit.SPADE)); // 10 + player.addCard(new Card(Rank.FIVE, Suit.HEART)); // 5 + assertThat(player.calculateScore()).isEqualTo(15); + } + + @DisplayName("ACE가 있고 버스트가 아니면 11로 계산한다") + @Test + void ACE가_있고_버스트가_아니면_11로_계산한다() { + player.addCard(new Card(Rank.ACE, Suit.SPADE)); // 11 + player.addCard(new Card(Rank.KING, Suit.HEART)); // 10 + assertThat(player.calculateScore()).isEqualTo(21); + } + + @DisplayName("ACE가 있고 버스트면 1로 계산한다") + @Test + void ACE가_있고_버스트면_1로_계산한다() { + player.addCard(new Card(Rank.ACE, Suit.SPADE)); // 1 + player.addCard(new Card(Rank.KING, Suit.HEART)); // 10 + player.addCard(new Card(Rank.FIVE, Suit.DIAMOND)); // 5 + assertThat(player.calculateScore()).isEqualTo(16); + } + + @DisplayName("ACE가 여러 장이면 하나만 11로 계산한다") + @Test + void ACE가_여러_장이면_하나만_11로_계산한다() { + player.addCard(new Card(Rank.ACE, Suit.SPADE)); // 11 + player.addCard(new Card(Rank.ACE, Suit.HEART)); // 1 + assertThat(player.calculateScore()).isEqualTo(12); + } +} From 3fe7c1d04ed77457d94b290149b9db0d1da5b6df Mon Sep 17 00:00:00 2001 From: picetea44 Date: Mon, 9 Mar 2026 02:05:04 +0900 Subject: [PATCH 11/48] =?UTF-8?q?feat:=20Players=20=EC=B5=9C=EC=86=8C=20?= =?UTF-8?q?=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B0=B8=EA=B0=80=EC=9E=90=20=EC=88=98=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=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/domain/Players.java | 17 ++++++++++++++++- src/test/java/domain/PlayersTest.java | 22 ++++++++-------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java index 43130a871e7..4dfef03b615 100644 --- a/src/main/java/domain/Players.java +++ b/src/main/java/domain/Players.java @@ -1,10 +1,25 @@ package domain; +import java.util.ArrayList; import java.util.List; public class Players { + private static final String DEALER_NAME = "딜러"; - private List playerList; + private final List playerList; + public Players(List names) { + if (names.isEmpty()) { + throw new IllegalArgumentException("플레이어는 최소 1명이어야 합니다."); + } + playerList = new ArrayList<>(); + playerList.add(new Player(DEALER_NAME)); + for (String name : names) { + playerList.add(new Player(name)); + } + } + public int getSize() { + return playerList.size(); + } } diff --git a/src/test/java/domain/PlayersTest.java b/src/test/java/domain/PlayersTest.java index 98382efd03b..bcbd1c42ec0 100644 --- a/src/test/java/domain/PlayersTest.java +++ b/src/test/java/domain/PlayersTest.java @@ -3,29 +3,23 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import java.util.Arrays; import java.util.List; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import view.InputView; public class PlayersTest { - InputView input; - @BeforeEach - void beforeEach() { - input = new InputView(); - } @DisplayName("입력에 따른 Player 객체 생성") @Test void 이름이_정상적으로_들어왔을때() { - List names = Arrays.asList("아나키", "포비", "모아"); - - /*assertThat()*/ - /*Players players = new Players(playerName);*/ - - //assertThat(players.getSize()).isEqualTo(3); + Players players = new Players(List.of("아나키", "포비", "모아")); + assertThat(players.getSize()).isEqualTo(4); // 딜러 포함 + } + @DisplayName("플레이어가 없으면 예외가 발생한다") + @Test + void 플레이어가_없으면_예외가_발생한다() { + assertThatThrownBy(() -> new Players(List.of())) + .isInstanceOf(IllegalArgumentException.class); } } From ba63f5b091898416dd24a337389d373562ebfe81 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Mon, 9 Mar 2026 11:28:22 +0900 Subject: [PATCH 12/48] =?UTF-8?q?feat:=20Players=20=EC=8A=B9=ED=8C=A8=20?= =?UTF-8?q?=ED=8C=90=EC=A0=95=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/domain/Players.java | 34 ++++++++++++++ src/main/java/domain/Result.java | 5 ++ src/test/java/domain/PlayersTest.java | 68 ++++++++++++++++++++++++++- 3 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 src/main/java/domain/Result.java diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java index 4dfef03b615..db74b0865e7 100644 --- a/src/main/java/domain/Players.java +++ b/src/main/java/domain/Players.java @@ -1,10 +1,13 @@ package domain; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; public class Players { private static final String DEALER_NAME = "딜러"; + private static final int BUST_THRESHOLD = 21; private final List playerList; @@ -19,6 +22,37 @@ public Players(List names) { } } + public Player getDealer() { + return playerList.get(0); + } + + public List getGamePlayers() { + return playerList.subList(1, playerList.size()); + } + + public Map judge() { + int dealerScore = getDealer().calculateScore(); + boolean dealerBust = dealerScore > BUST_THRESHOLD; + Map results = new LinkedHashMap<>(); + for (Player player : getGamePlayers()) { + results.put(player, judgePlayer(player.calculateScore(), dealerScore, dealerBust)); + } + return results; + } + + private Result judgePlayer(int playerScore, int dealerScore, boolean dealerBust) { + if (playerScore > BUST_THRESHOLD) { + return Result.LOSE; + } + if (dealerBust) { + return Result.WIN; + } + if (playerScore > dealerScore) { + return Result.WIN; + } + return Result.LOSE; + } + public int getSize() { return playerList.size(); } diff --git a/src/main/java/domain/Result.java b/src/main/java/domain/Result.java new file mode 100644 index 00000000000..1480273332b --- /dev/null +++ b/src/main/java/domain/Result.java @@ -0,0 +1,5 @@ +package domain; + +public enum Result { + WIN, LOSE +} \ No newline at end of file diff --git a/src/test/java/domain/PlayersTest.java b/src/test/java/domain/PlayersTest.java index bcbd1c42ec0..5ece1c537dd 100644 --- a/src/test/java/domain/PlayersTest.java +++ b/src/test/java/domain/PlayersTest.java @@ -4,16 +4,24 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; public class PlayersTest { + Players players; + + @BeforeEach + void beforeEach() { + players = new Players(List.of("pobi", "jason")); + } + @DisplayName("입력에 따른 Player 객체 생성") @Test void 이름이_정상적으로_들어왔을때() { - Players players = new Players(List.of("아나키", "포비", "모아")); - assertThat(players.getSize()).isEqualTo(4); // 딜러 포함 + assertThat(players.getSize()).isEqualTo(3); // 딜러 포함 } @DisplayName("플레이어가 없으면 예외가 발생한다") @@ -22,4 +30,60 @@ public class PlayersTest { assertThatThrownBy(() -> new Players(List.of())) .isInstanceOf(IllegalArgumentException.class); } + + @DisplayName("플레이어 점수가 딜러보다 높으면 승리한다") + @Test + void 플레이어_점수가_딜러보다_높으면_승리한다() { + players.getDealer().addCard(new Card(Rank.TEN, Suit.SPADE)); // 10 + players.getGamePlayers().get(0).addCard(new Card(Rank.KING, Suit.HEART)); // 10 + players.getGamePlayers().get(0).addCard(new Card(Rank.ACE, Suit.SPADE)); // 21 + + Map result = players.judge(); + assertThat(result.get(players.getGamePlayers().get(0))).isEqualTo(Result.WIN); + } + + @DisplayName("플레이어 점수가 딜러보다 낮으면 패배한다") + @Test + void 플레이어_점수가_딜러보다_낮으면_패배한다() { + players.getDealer().addCard(new Card(Rank.KING, Suit.SPADE)); // 10 + players.getDealer().addCard(new Card(Rank.NINE, Suit.SPADE)); // 19 + players.getGamePlayers().get(0).addCard(new Card(Rank.SEVEN, Suit.HEART)); // 7 + + Map result = players.judge(); + assertThat(result.get(players.getGamePlayers().get(0))).isEqualTo(Result.LOSE); + } + + @DisplayName("동점이면 딜러가 이긴다") + @Test + void 동점이면_딜러가_이긴다() { + players.getDealer().addCard(new Card(Rank.KING, Suit.SPADE)); // 10 + players.getGamePlayers().get(0).addCard(new Card(Rank.TEN, Suit.HEART)); // 10 + + Map result = players.judge(); + assertThat(result.get(players.getGamePlayers().get(0))).isEqualTo(Result.LOSE); + } + + @DisplayName("플레이어가 버스트면 패배한다") + @Test + void 플레이어가_버스트면_패배한다() { + players.getDealer().addCard(new Card(Rank.FIVE, Suit.SPADE)); // 5 + players.getGamePlayers().get(0).addCard(new Card(Rank.KING, Suit.HEART)); // 10 + players.getGamePlayers().get(0).addCard(new Card(Rank.QUEEN, Suit.HEART)); // 10 + players.getGamePlayers().get(0).addCard(new Card(Rank.FIVE, Suit.DIAMOND)); // 25 + + Map result = players.judge(); + assertThat(result.get(players.getGamePlayers().get(0))).isEqualTo(Result.LOSE); + } + + @DisplayName("딜러가 버스트면 플레이어가 승리한다") + @Test + void 딜러가_버스트면_플레이어가_승리한다() { + players.getDealer().addCard(new Card(Rank.KING, Suit.SPADE)); // 10 + players.getDealer().addCard(new Card(Rank.QUEEN, Suit.SPADE)); // 10 + players.getDealer().addCard(new Card(Rank.FIVE, Suit.SPADE)); // 25 + players.getGamePlayers().get(0).addCard(new Card(Rank.SEVEN, Suit.HEART)); // 7 + + Map result = players.judge(); + assertThat(result.get(players.getGamePlayers().get(0))).isEqualTo(Result.WIN); + } } From 490cf8530a0675a526deb705b07ced995586c977 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Mon, 9 Mar 2026 14:17:25 +0900 Subject: [PATCH 13/48] =?UTF-8?q?feat:=20=EC=9E=85=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Rank.java | 34 ++++++++------ src/main/java/domain/Result.java | 12 ++++- src/main/java/view/InputView.java | 13 +++--- src/main/java/view/OutputView.java | 64 +++++++++++++++++++++++++++ src/test/java/view/InputViewTest.java | 14 ------ 5 files changed, 101 insertions(+), 36 deletions(-) diff --git a/src/main/java/domain/Rank.java b/src/main/java/domain/Rank.java index 93dc2c2763f..ef91639ee28 100644 --- a/src/main/java/domain/Rank.java +++ b/src/main/java/domain/Rank.java @@ -1,27 +1,33 @@ package domain; public enum Rank { - ACE(1), - TWO(2), - THREE(3), - FOUR(4), - FIVE(5), - SIX(6), - SEVEN(7), - EIGHT(8), - NINE(9), - TEN(10), - JACK(10), - QUEEN(10), - KING(10); + ACE(1, "A"), + TWO(2, "2"), + THREE(3, "3"), + FOUR(4, "4"), + FIVE(5, "5"), + SIX(6, "6"), + SEVEN(7, "7"), + EIGHT(8, "8"), + NINE(9, "9"), + TEN(10, "10"), + JACK(10, "J"), + QUEEN(10, "Q"), + KING(10, "K"); private final int score; + private final String displayName; - Rank(int score) { + Rank(int score, String displayName) { this.score = score; + this.displayName = displayName; } public int getScore() { return score; } + + public String getDisplayName() { + return displayName; + } } diff --git a/src/main/java/domain/Result.java b/src/main/java/domain/Result.java index 1480273332b..a4d3a10373b 100644 --- a/src/main/java/domain/Result.java +++ b/src/main/java/domain/Result.java @@ -1,5 +1,15 @@ package domain; public enum Result { - WIN, LOSE + WIN("승"), LOSE("패"); + + private final String displayName; + + Result(String displayName) { + this.displayName = displayName; + } + + public String getDisplayName() { + return displayName; + } } \ No newline at end of file diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index c14da78e0ee..386afc2f095 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -5,7 +5,7 @@ import java.util.Scanner; public class InputView { - Scanner sc = new Scanner(System.in); + private final Scanner sc = new Scanner(System.in); public List inputPlayers() { System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); @@ -16,11 +16,10 @@ public List inputPlayers() { return playerName; } - public void isBlank(List names) { - for (String name : names) { - if (name.isBlank()) { - throw new IllegalArgumentException(); - } - } + public boolean askHit(String playerName) { + System.out.println(playerName + "는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)"); + return sc.nextLine().equals("y"); } + + } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index d8f9743ccfe..c488afce30f 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,4 +1,68 @@ package view; +import domain.Card; +import domain.Player; +import domain.Result; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + public class OutputView { + + public void printInitialDeal(List playerNames) { + System.out.println("딜러와 " + String.join(", ", playerNames) + "에게 2장을 나누었습니다."); + } + + public void printDealerInitialCard(Card card) { + System.out.println("딜러카드: " + card); + } + + public void printPlayerCards(Player player) { + System.out.println(player.getName() + "카드: " + formatCards(player.getCards())); + } + + public void printDealerHit() { + System.out.println("딜러는 16이하라 한장의 카드를 더 받았습니다."); + } + + public void printFinalCards(Player player) { + System.out.println(player.getName() + "카드: " + formatCards(player.getCards()) + + " - 결과: " + player.calculateScore()); + } + + public void printFinalResult(Player dealer, Map results) { + System.out.println("\n## 최종 승패"); + printDealerResult(dealer, results); + printPlayerResults(results); + } + + private void printDealerResult(Player dealer, Map results) { + int dealerWin = countResult(results, Result.LOSE); + int dealerLose = countResult(results, Result.WIN); + System.out.println(dealer.getName() + ": " + dealerWin + "승 " + dealerLose + "패"); + } + + private int countResult(Map results, Result target) { + int count = 0; + for (Result result : results.values()) { + if (result == target) { + count++; + } + } + return count; + } + + private void printPlayerResults(Map results) { + for (Map.Entry entry : results.entrySet()) { + System.out.println(entry.getKey().getName() + ": " + entry.getValue().getDisplayName()); + } + } + + private String formatCards(List cards) { + List cardNames = new ArrayList<>(); + for (Card card : cards) { + cardNames.add(card.toString()); + } + return String.join(", ", cardNames); + } } diff --git a/src/test/java/view/InputViewTest.java b/src/test/java/view/InputViewTest.java index fa3702d8dee..91a80883b88 100644 --- a/src/test/java/view/InputViewTest.java +++ b/src/test/java/view/InputViewTest.java @@ -10,18 +10,4 @@ public class InputViewTest { - @DisplayName("이름에 공백만 있으면 예외 처리.") - @Test - void Trim이후_이름이_없을경우() { - - String names = "아나키, , 모아"; - List playerName = Arrays.stream(names.split(",")) - .map(String::trim) - .toList(); - - - assertThatThrownBy(() -> new InputView().isBlank(playerName)) - .isInstanceOf(IllegalArgumentException.class); - } - } From 3060aa5d80e41921fcf73f7a1e3f4dfeadf702d5 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Mon, 9 Mar 2026 14:17:45 +0900 Subject: [PATCH 14/48] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EB=9E=A8=20=ED=9D=90=EB=A6=84=20=EC=A0=9C=EC=96=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/BlackjackMain.java | 7 +- .../java/controller/BlackjackController.java | 83 +++++++++++++++++-- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/main/java/BlackjackMain.java b/src/main/java/BlackjackMain.java index e590ba6f65a..6dd5f176dc0 100644 --- a/src/main/java/BlackjackMain.java +++ b/src/main/java/BlackjackMain.java @@ -1,8 +1,11 @@ -import domain.Deck; +import controller.BlackjackController; +import view.InputView; +import view.OutputView; public class BlackjackMain { public static void main(String[] args) { - Deck deck = new Deck(); + BlackjackController controller = new BlackjackController(new InputView(), new OutputView()); + controller.run(); } } diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index 46f7ff61714..e68fd0c4bd0 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -1,14 +1,87 @@ package controller; +import domain.Deck; +import domain.Player; +import domain.Players; +import domain.Result; +import java.util.List; +import java.util.Map; +import view.InputView; +import view.OutputView; + public class BlackjackController { - // 이름 입력 후 player 객체 생성 + private static final int DEALER_HIT_THRESHOLD = 16; + private static final int BUST_THRESHOLD = 21; + + private final InputView inputView; + private final OutputView outputView; + + public BlackjackController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + List names = inputView.inputPlayers(); + Players players = new Players(names); + Deck deck = new Deck(); + dealInitialCards(players, deck); + printInitialState(players, names); + playAllPlayerTurns(players, deck); + playDealerTurn(players.getDealer(), deck); + printFinalState(players); + } + + private void playAllPlayerTurns(Players players, Deck deck) { + for (Player player : players.getGamePlayers()) { + playPlayerTurn(player, deck); + } + } - // 각 플레이어에게 카드 배분 + private void dealInitialCards(Players players, Deck deck) { + players.getDealer().addCard(deck.draw()); + players.getDealer().addCard(deck.draw()); + for (Player player : players.getGamePlayers()) { + player.addCard(deck.draw()); + player.addCard(deck.draw()); + } + } - // 게임 진행 + private void printInitialState(Players players, List names) { + outputView.printInitialDeal(names); + outputView.printDealerInitialCard(players.getDealer().getCards().get(0)); + for (Player player : players.getGamePlayers()) { + outputView.printPlayerCards(player); + } + System.out.println(); + } - // 딜러 차례 + private void playPlayerTurn(Player player, Deck deck) { + boolean cardShown = false; + while (player.calculateScore() <= BUST_THRESHOLD && inputView.askHit(player.getName())) { + player.addCard(deck.draw()); + outputView.printPlayerCards(player); + cardShown = true; + } + if (!cardShown) { + outputView.printPlayerCards(player); + } + } - // 결과 발표 + private void playDealerTurn(Player dealer, Deck deck) { + while (dealer.calculateScore() <= DEALER_HIT_THRESHOLD) { + dealer.addCard(deck.draw()); + outputView.printDealerHit(); + } + } + private void printFinalState(Players players) { + System.out.println(); + outputView.printFinalCards(players.getDealer()); + for (Player player : players.getGamePlayers()) { + outputView.printFinalCards(player); + } + Map results = players.judge(); + outputView.printFinalResult(players.getDealer(), results); + } } From 3952e535b454c718a41791b21426cbb86386708d Mon Sep 17 00:00:00 2001 From: picetea44 Date: Mon, 9 Mar 2026 14:19:23 +0900 Subject: [PATCH 15/48] =?UTF-8?q?feat:=20Card=20=EA=B0=9D=EC=B2=B4=20toStr?= =?UTF-8?q?ing=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/domain/Card.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/domain/Card.java b/src/main/java/domain/Card.java index b34bf42836e..3027123f5a8 100644 --- a/src/main/java/domain/Card.java +++ b/src/main/java/domain/Card.java @@ -16,4 +16,9 @@ public int getScore() { public boolean isAce() { return rank == Rank.ACE; } + + @Override + public String toString() { + return rank.getDisplayName() + suit.getShape(); + } } From e35b6f687dbf6cab76d8b37b92f5d532f41b93ec Mon Sep 17 00:00:00 2001 From: picetea44 Date: Mon, 9 Mar 2026 14:19:48 +0900 Subject: [PATCH 16/48] =?UTF-8?q?feat:=20Player=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=B1=85=EC=9E=84=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/domain/Player.java | 4 ++++ src/main/java/domain/Players.java | 12 +++++++++--- src/test/java/domain/PlayersTest.java | 6 +++--- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/domain/Player.java b/src/main/java/domain/Player.java index 1b6da029012..75d4ed73ed0 100644 --- a/src/main/java/domain/Player.java +++ b/src/main/java/domain/Player.java @@ -35,6 +35,10 @@ private int applyAceBonus(int score, boolean hasAce) { return score; } + public List getCards() { + return cards; + } + public String getName() { return name; } diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java index db74b0865e7..1efaae78317 100644 --- a/src/main/java/domain/Players.java +++ b/src/main/java/domain/Players.java @@ -12,9 +12,7 @@ public class Players { private final List playerList; public Players(List names) { - if (names.isEmpty()) { - throw new IllegalArgumentException("플레이어는 최소 1명이어야 합니다."); - } + validate(names); playerList = new ArrayList<>(); playerList.add(new Player(DEALER_NAME)); for (String name : names) { @@ -22,6 +20,14 @@ public Players(List names) { } } + private void validate(List names) { + for (String name : names) { + if (name.isBlank()) { + throw new IllegalArgumentException("이름은 공백일 수 없습니다."); + } + } + } + public Player getDealer() { return playerList.get(0); } diff --git a/src/test/java/domain/PlayersTest.java b/src/test/java/domain/PlayersTest.java index 5ece1c537dd..37c9a2e5403 100644 --- a/src/test/java/domain/PlayersTest.java +++ b/src/test/java/domain/PlayersTest.java @@ -24,10 +24,10 @@ void beforeEach() { assertThat(players.getSize()).isEqualTo(3); // 딜러 포함 } - @DisplayName("플레이어가 없으면 예외가 발생한다") + @DisplayName("이름이 공백이면 예외가 발생한다") @Test - void 플레이어가_없으면_예외가_발생한다() { - assertThatThrownBy(() -> new Players(List.of())) + void 이름이_공백이면_예외가_발생한다() { + assertThatThrownBy(() -> new Players(List.of("아나키", " ", "모아"))) .isInstanceOf(IllegalArgumentException.class); } From d36a0d0bd29535e44845ee140b506bdc6a999a6d Mon Sep 17 00:00:00 2001 From: picetea44 Date: Mon, 9 Mar 2026 17:24:42 +0900 Subject: [PATCH 17/48] =?UTF-8?q?docs:=20README.md=20=ED=9A=8C=EA=B3=A0=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 76 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 7cbdd1c2d5d..e9b054d958e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,50 @@ # java-blackjack +## 체크 리스트 + +- [x] 미션의 필수 요구사항을 모두 구현했나요? +- [x] Gradle `test`를 실행했을 때, 모든 테스트가 정상적으로 통과했나요? +- [x] 애플리케이션이 정상적으로 실행되나요? + + + +## 객체지향 생활체조 요구사항을 얼마나 잘 충족했다고 생각하시나요? + +### 1~5점 중에서 선택해주세요. + +- [ ] 1 (전혀 충족하지 못함) +- [ ] 2 +- [ ] 3 (보통) +- [x] 4 +- [ ] 5 (완벽하게 충족) + +### 선택한 점수의 이유를 적어주세요. +처음으로 TDD를 적용하여 진행해봤습니다. 전체적인 설계 없이 일단 돌아가는 기능을 만들고 테스트를 한다는 개념이 많이 생소했던 것 같습니다. +그로인해 부분적으로 indent나 문자열 포장이 완벽하게 적용되지 못했던 것 같습니다. + + + +## 어떤 부분에 집중하여 리뷰해야 할까요? + +--- +### 주제1 - TDD 관련 +미션 초기, 전체적인 설계가 머릿속에 완벽히 그려지지 않은 상태에서 TDD를 시작하려니 상당한 막막함을 느꼈습니다. 동작을 기준으로 테스트를 작성하며 상향식으로 접근하려 했으나, 여러 객체나 메서드가 복잡하게 상호작용해야 하는 상황에서는 그 동작의 단위 자체가 모호하게 다가왔습니다. + +처음에는 프로그램의 흐름에 따라 큰 단위에서 작은 단위로 기능을 정의하며 내려왔는데, 이 과정이 다소 추상적이라는 생각이 들었습니다. 현업에서는 설계가 불확실한 상황에서 어떤 지점을 시작점으로 잡고 설계를 구체화해 나가는지, 그리고 객체 간의 협력이 필수적인 상황에서 TDD의 리듬을 어떻게 유지하시는지 궁금합니다. + +### 주제2 - 테스트를 위한 코드 +테스트 가독성과 검증을 위해 프로덕션 코드를 어디까지 수정해야 하는지에 대해서도 깊이 고민했습니다. 예를 들어, 카드 덱에서 카드가 제대로 뽑혔는지 확인하기 위해 실제 로직에서는 쓰이지 않는 deck.size()와 같은 메서드를 추가하는 상황이 있었습니다. + +이를 해결하기 위해 1) 무식하게 52번의 draw()를 호출하여 예외를 확인하는 방식, 2) 내부 상태를 우회하여 검증하는 방식, 3) 임시로 메서드를 만들고 추후 삭제하는 방식 등을 고려해 보았습니다. 저는 현재 테스트의 명확성을 위해 메서드를 유지하는 쪽을 택했지만, 이것이 적절한 방향이 맞을지 우려되기도 합니다. 테스트 가능성을 높이기 위해 프로덕션 코드를 변경하는 것에 대해 리뷰어님은 어떤 기준을 가지고 계신가요? + +### 주제3 - 도메인에 따른 아키텍처 +마지막으로 블랙잭처럼 도메인 로직이 명확하고 규모가 작은 경우, 서비스 레이어의 도입이 반드시 필요한지 의문이 생겼습니다. 현재 구조에서는 컨트롤러와 도메인 모델만으로도 충분히 실행 흐름을 제어할 수 있다고 느꼈고, 단순히 계층 구조를 맞추기 위해 서비스 레이어를 두는 것은 불필요한 위임 코드만 양산한다고 판단했습니다. + +하지만 프로젝트가 커질 상황을 대비해야 하는 관점에서는 레이어를 나누는 것이 맞을지도 모른다는 갈등이 있었습니다. 도메인이 작은 프로젝트에서 아키텍처의 복잡도를 결정하는 리뷰어님만의 트레이드 오프 기준이 있다면 배우고 싶습니다. +### 주제4 - 향후 학습 방향성 및 마인드 셋 +이번 페어 프로그래밍을 진행하며 제 스스로의 부족함을 많이 통감했습니다. 다른 팀들은 디자인 패턴을 적극적으로 도입하고 논의하는데, 저는 그 대화에 온전히 참여하지 못하거나 패턴을 코드에 녹여내는 데 어려움을 겪었습니다. 심지어는 비즈니스 로직에 집중하다 보니 가끔 가장 기본적인 언어 문법조차 헷갈려 당황스러운 순간들도 있었습니다. + +객체지향적인 설계는커녕 지식의 파편화로 인해 곳곳에 빈틈이 있다는 느낌을 많이 받았습니다. 리뷰어님이 보시기에 현재 제 수준에서 어떤 부분을 가장 우선순위에 두고 학습해야 할까요? 단순히 패턴을 외우는 것보다, 제가 놓치고 있는 기본기가 무엇인지 리뷰어님의 시선에서 냉철하게 짚어주시고 앞으로의 공부 방향을 제시해 주실 수 있다면 정말 감사하겠습니다. ## ✅ 게임 기능 TO-DO ### 게임에 참여할 사람의 이름 입력 @@ -10,19 +55,19 @@ ### 각 플레이어, 딜러에게 카드를 랜덤으로 2장 배분하는 기능 -- [ ] 딜러는 2번째 카드는 출력하지 않음 -- [ ] 이미 정해진 덱에서 랜덤 분배 +- [x] 딜러는 2번째 카드는 출력하지 않음 +- [x] 이미 정해진 덱에서 랜덤 분배 ### 플레이어에게 카드를 더 받을지 묻는 기능 -- [ ] 이미 뽑힌 카드는 다시 나오지 않게 처리 -- [ ] 한 플레이어가 n을 입력하면 해당 플레이어 종료 +- [x] 이미 뽑힌 카드는 다시 나오지 않게 처리 +- [x] 한 플레이어가 n을 입력하면 해당 플레이어 종료 ### 딜러가 카드를 더 받는지 출력 -- [ ] 16 이하라 한 장 받을 때마다 문장 출력 -- [ ] 여러 장 받을 경우 한 장씩 문장 반복 출력 -- [ ] 처음부터 17 이상이면 카드 추가 없이 안내 문장 출력 여부 결정 +- [x] 16 이하라 한 장 받을 때마다 문장 출력 +- [x] 여러 장 받을 경우 한 장씩 문장 반복 출력 +- [x] 처음부터 17 이상이면 카드 추가 없이 안내 문장 출력 여부 결정 ### 카드 객체 설계 @@ -33,19 +78,16 @@ ### 카드 점수 합 계산 기능 -- [ ] rank 값만 추출하여 합산 -- [ ] 총합 계산 로직 구현 +- [x] rank 값만 추출하여 합산 +- [x] 총합 계산 로직 구현 ### 결과 출력 -- [ ] 딜러가 가진 카드 목록 출력 -- [ ] 딜러 카드 점수 합 출력 -- [ ] 플레이어가 가진 카드 목록 출력 -- [ ] 플레이어 카드 점수 합 출력 +- [x] 딜러가 가진 카드 목록 출력 +- [x] 딜러 카드 점수 합 출력 +- [x] 플레이어가 가진 카드 목록 출력 +- [x] 플레이어 카드 점수 합 출력 ### 승패 출력 -- [ ] 계산된 합에 따라 승패 판별 -- [ ] 라운드 반복 여부 설계 - -### 라운드 반복 기능 구현 \ No newline at end of file +- [x] 계산된 합에 따라 승패 판별 \ No newline at end of file From 9bce798cecbd62c222dfd54c823e9b4d09972977 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Wed, 11 Mar 2026 14:35:18 +0900 Subject: [PATCH 18/48] =?UTF-8?q?refactor:=20remove()=20=EB=8C=80=EC=8B=A0?= =?UTF-8?q?=20removeLast()=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=97=AC?= =?UTF-8?q?=20=EA=B0=80=EB=8F=85=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Deck.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/domain/Deck.java b/src/main/java/domain/Deck.java index f96054ee0dd..959bef411f4 100644 --- a/src/main/java/domain/Deck.java +++ b/src/main/java/domain/Deck.java @@ -20,7 +20,7 @@ public Card draw() { if (cards.isEmpty()) { throw new IllegalStateException("덱에 카드가 없습니다."); } - return cards.remove(cards.size() - 1); + return cards.removeLast(); } public int size() { From 63ce635ba3306df2efaeacaf7fd043a0942f9013 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Wed, 11 Mar 2026 16:37:44 +0900 Subject: [PATCH 19/48] =?UTF-8?q?refactor:=20PlayerName=20VO=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=EA=B3=B5=EB=B0=B1=20=EC=B1=85=EC=9E=84?= =?UTF-8?q?=20=EC=9C=84=EC=9E=84,=20Players=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=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/domain/Player.java | 14 +++++++++----- src/main/java/domain/PlayerName.java | 21 +++++++++++++++++++++ src/main/java/domain/Players.java | 11 +++++------ src/test/java/domain/PlayerTest.java | 9 +++++++++ src/test/java/domain/PlayersTest.java | 6 +++--- 5 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 src/main/java/domain/PlayerName.java diff --git a/src/main/java/domain/Player.java b/src/main/java/domain/Player.java index 75d4ed73ed0..4a46a07c88d 100644 --- a/src/main/java/domain/Player.java +++ b/src/main/java/domain/Player.java @@ -4,11 +4,11 @@ import java.util.List; public class Player { - private final String name; + private final PlayerName playerName; private final List cards = new ArrayList<>(); public Player(String name) { - this.name = name; + this.playerName = new PlayerName(name); } public void addCard(Card card) { @@ -29,17 +29,21 @@ public int calculateScore() { } private int applyAceBonus(int score, boolean hasAce) { - if (hasAce && score + 10 <= 21) { + if (isSoftHand(score, hasAce)) { return score + 10; } return score; } + private boolean isSoftHand(int score, boolean hasAce) { + return hasAce && score + 10 <= 21; + } + public List getCards() { return cards; } public String getName() { - return name; + return playerName.getName(); } -} \ No newline at end of file +} diff --git a/src/main/java/domain/PlayerName.java b/src/main/java/domain/PlayerName.java new file mode 100644 index 00000000000..cf20a37c625 --- /dev/null +++ b/src/main/java/domain/PlayerName.java @@ -0,0 +1,21 @@ +package domain; + +public class PlayerName { + + private final String name; + + public PlayerName(String name) { + validate(name); + this.name = name; + } + + private void validate(String name) { + if (name == null || name.isBlank()){ + throw new IllegalArgumentException("이름은 공백일 수 없습니다."); + } + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java index 1efaae78317..25680128b20 100644 --- a/src/main/java/domain/Players.java +++ b/src/main/java/domain/Players.java @@ -1,6 +1,7 @@ package domain; import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -12,7 +13,7 @@ public class Players { private final List playerList; public Players(List names) { - validate(names); + validateDuplicate(names); playerList = new ArrayList<>(); playerList.add(new Player(DEALER_NAME)); for (String name : names) { @@ -20,11 +21,9 @@ public Players(List names) { } } - private void validate(List names) { - for (String name : names) { - if (name.isBlank()) { - throw new IllegalArgumentException("이름은 공백일 수 없습니다."); - } + private void validateDuplicate(List names) { + if (names.size() != new HashSet<>(names).size()) { + throw new IllegalArgumentException("중복된 이름이 존재합니다."); } } diff --git a/src/test/java/domain/PlayerTest.java b/src/test/java/domain/PlayerTest.java index 353abf9516d..07ec1fe0602 100644 --- a/src/test/java/domain/PlayerTest.java +++ b/src/test/java/domain/PlayerTest.java @@ -1,7 +1,9 @@ package domain; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -14,6 +16,13 @@ void beforeEach() { player = new Player("아나키"); } + @DisplayName("공백이 들어오면 예외처리한다") + @Test + void 공백_들어오면_예외처리한다() { + assertThatThrownBy(() -> new Player(" ")) + .isInstanceOf(IllegalArgumentException.class); + } + @DisplayName("일반 카드의 점수를 합산한다") @Test void 일반_카드의_점수를_합산한다() { diff --git a/src/test/java/domain/PlayersTest.java b/src/test/java/domain/PlayersTest.java index 37c9a2e5403..8a5abd12795 100644 --- a/src/test/java/domain/PlayersTest.java +++ b/src/test/java/domain/PlayersTest.java @@ -24,10 +24,10 @@ void beforeEach() { assertThat(players.getSize()).isEqualTo(3); // 딜러 포함 } - @DisplayName("이름이 공백이면 예외가 발생한다") + @DisplayName("이름이 중복이면 예외가 발생한다") @Test - void 이름이_공백이면_예외가_발생한다() { - assertThatThrownBy(() -> new Players(List.of("아나키", " ", "모아"))) + void 이름이_중복이면_예외가_발생한다() { + assertThatThrownBy(() -> new Players(List.of("아나키", "아나키", "모아"))) .isInstanceOf(IllegalArgumentException.class); } From e3987cb0bd3f7a59f5f15407662078b68e5f0bda Mon Sep 17 00:00:00 2001 From: picetea44 Date: Wed, 11 Mar 2026 19:10:34 +0900 Subject: [PATCH 20/48] =?UTF-8?q?refactor:=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../java/controller/BlackjackController.java | 18 +++++----- src/main/java/domain/Card.java | 14 ++++++-- src/main/java/domain/Dealer.java | 12 +++++++ src/main/java/domain/Participant.java | 26 ++++++++++++++ src/main/java/domain/Player.java | 17 ++-------- src/main/java/domain/Rank.java | 34 ++++++++----------- src/main/java/domain/Result.java | 4 +-- src/main/java/domain/Suit.java | 15 +------- src/main/java/view/OutputView.java | 9 +++-- src/test/java/domain/CardTest.java | 4 +-- 11 files changed, 88 insertions(+), 67 deletions(-) create mode 100644 src/main/java/domain/Dealer.java create mode 100644 src/main/java/domain/Participant.java diff --git a/README.md b/README.md index e9b054d958e..3b8f8fa0c40 100644 --- a/README.md +++ b/README.md @@ -90,4 +90,4 @@ ### 승패 출력 -- [x] 계산된 합에 따라 승패 판별 \ No newline at end of file +- [x] 계산된 합에 따라 승패 판별 diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index e68fd0c4bd0..e498aa763c1 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -1,6 +1,8 @@ package controller; +import domain.Dealer; import domain.Deck; +import domain.Participant; import domain.Player; import domain.Players; import domain.Result; @@ -24,9 +26,11 @@ public BlackjackController(InputView inputView, OutputView outputView) { public void run() { List names = inputView.inputPlayers(); Players players = new Players(names); + Dealer dealer = new Dealer("딜러"); Deck deck = new Deck(); - dealInitialCards(players, deck); - printInitialState(players, names); + dealInitialCards(dealer, players, deck); + dealInitialCards(dealer, players, deck); + printInitialState(dealer, players, names); playAllPlayerTurns(players, deck); playDealerTurn(players.getDealer(), deck); printFinalState(players); @@ -38,18 +42,16 @@ private void playAllPlayerTurns(Players players, Deck deck) { } } - private void dealInitialCards(Players players, Deck deck) { - players.getDealer().addCard(deck.draw()); - players.getDealer().addCard(deck.draw()); + private void dealInitialCards(Dealer dealer, Players players, Deck deck) { for (Player player : players.getGamePlayers()) { player.addCard(deck.draw()); - player.addCard(deck.draw()); } + dealer.addCard(deck.draw()); } - private void printInitialState(Players players, List names) { + private void printInitialState(Dealer dealer, Players players, List names) { outputView.printInitialDeal(names); - outputView.printDealerInitialCard(players.getDealer().getCards().get(0)); + outputView.printDealerInitialCard(dealer.getInitialCard()); for (Player player : players.getGamePlayers()) { outputView.printPlayerCards(player); } diff --git a/src/main/java/domain/Card.java b/src/main/java/domain/Card.java index 3027123f5a8..570cabd3d6a 100644 --- a/src/main/java/domain/Card.java +++ b/src/main/java/domain/Card.java @@ -17,8 +17,16 @@ public boolean isAce() { return rank == Rank.ACE; } - @Override - public String toString() { - return rank.getDisplayName() + suit.getShape(); + public String getRankDisplayName() { + return rank.getDisplayName(); } + + public String getSuitShape() { + return suit.getShape(); + } + +// @Override +// public String toString() { +// return rank.getDisplayName() + suit.getShape(); +// } } diff --git a/src/main/java/domain/Dealer.java b/src/main/java/domain/Dealer.java new file mode 100644 index 00000000000..ed850d7192b --- /dev/null +++ b/src/main/java/domain/Dealer.java @@ -0,0 +1,12 @@ +package domain; + +public class Dealer extends Participant{ + + public Dealer(String name) { + super(name); + } + + public Card getInitialCard() { + return getCards().getFirst(); + } +} diff --git a/src/main/java/domain/Participant.java b/src/main/java/domain/Participant.java new file mode 100644 index 00000000000..9347a979bd7 --- /dev/null +++ b/src/main/java/domain/Participant.java @@ -0,0 +1,26 @@ +package domain; + +import java.util.ArrayList; +import java.util.List; + +public abstract class Participant { + private final PlayerName playerName; + private final List cards = new ArrayList<>(); + + protected Participant(String name) { + this.playerName = new PlayerName(name); + } + + public void addCard(Card card) { + cards.add(card); + } + + public List getCards() { + return cards; + } + + public String getName() { + return playerName.getName(); + } + +} diff --git a/src/main/java/domain/Player.java b/src/main/java/domain/Player.java index 4a46a07c88d..3c27bf801e0 100644 --- a/src/main/java/domain/Player.java +++ b/src/main/java/domain/Player.java @@ -3,23 +3,18 @@ import java.util.ArrayList; import java.util.List; -public class Player { - private final PlayerName playerName; - private final List cards = new ArrayList<>(); +public class Player extends Participant{ public Player(String name) { - this.playerName = new PlayerName(name); + super(name); } - public void addCard(Card card) { - cards.add(card); - } public int calculateScore() { int score = 0; boolean hasAce = false; - for (Card card : cards) { + for (Card card : getCards()) { score += card.getScore(); if (card.isAce()) { hasAce = true; @@ -39,11 +34,5 @@ private boolean isSoftHand(int score, boolean hasAce) { return hasAce && score + 10 <= 21; } - public List getCards() { - return cards; - } - public String getName() { - return playerName.getName(); - } } diff --git a/src/main/java/domain/Rank.java b/src/main/java/domain/Rank.java index ef91639ee28..93dc2c2763f 100644 --- a/src/main/java/domain/Rank.java +++ b/src/main/java/domain/Rank.java @@ -1,33 +1,27 @@ package domain; public enum Rank { - ACE(1, "A"), - TWO(2, "2"), - THREE(3, "3"), - FOUR(4, "4"), - FIVE(5, "5"), - SIX(6, "6"), - SEVEN(7, "7"), - EIGHT(8, "8"), - NINE(9, "9"), - TEN(10, "10"), - JACK(10, "J"), - QUEEN(10, "Q"), - KING(10, "K"); + ACE(1), + TWO(2), + THREE(3), + FOUR(4), + FIVE(5), + SIX(6), + SEVEN(7), + EIGHT(8), + NINE(9), + TEN(10), + JACK(10), + QUEEN(10), + KING(10); private final int score; - private final String displayName; - Rank(int score, String displayName) { + Rank(int score) { this.score = score; - this.displayName = displayName; } public int getScore() { return score; } - - public String getDisplayName() { - return displayName; - } } diff --git a/src/main/java/domain/Result.java b/src/main/java/domain/Result.java index a4d3a10373b..bd40c184036 100644 --- a/src/main/java/domain/Result.java +++ b/src/main/java/domain/Result.java @@ -1,7 +1,7 @@ package domain; public enum Result { - WIN("승"), LOSE("패"); + WIN("승"), LOSE("패"), TIE("무"); private final String displayName; @@ -12,4 +12,4 @@ public enum Result { public String getDisplayName() { return displayName; } -} \ No newline at end of file +} diff --git a/src/main/java/domain/Suit.java b/src/main/java/domain/Suit.java index 04052b65798..28f5f541438 100644 --- a/src/main/java/domain/Suit.java +++ b/src/main/java/domain/Suit.java @@ -1,18 +1,5 @@ package domain; public enum Suit { - HEART("하트"), - DIAMOND("다이아몬드"), - SPADE("스페이드"), - CLOVER("클로버"); - - private final String shape; - - Suit(String shape) { - this.shape = shape; - } - - public String getShape() { - return shape; - } + HEART, DIAMOND, SPADE, CLUB } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index c488afce30f..b1ca13cb9a9 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,6 +1,7 @@ package view; import domain.Card; +import domain.Dealer; import domain.Player; import domain.Result; import java.util.ArrayList; @@ -14,7 +15,7 @@ public void printInitialDeal(List playerNames) { } public void printDealerInitialCard(Card card) { - System.out.println("딜러카드: " + card); + System.out.println("딜러카드: " + formatCard(card)); } public void printPlayerCards(Player player) { @@ -61,8 +62,12 @@ private void printPlayerResults(Map results) { private String formatCards(List cards) { List cardNames = new ArrayList<>(); for (Card card : cards) { - cardNames.add(card.toString()); + cardNames.add(formatCard(card)); } return String.join(", ", cardNames); } + + private String formatCard(Card card) { + return card.getRankDisplayName() + card.getSuitShape(); + } } diff --git a/src/test/java/domain/CardTest.java b/src/test/java/domain/CardTest.java index 02ccba80d83..d748b9fb12c 100644 --- a/src/test/java/domain/CardTest.java +++ b/src/test/java/domain/CardTest.java @@ -16,6 +16,4 @@ class CardTest { Card card = new Card(Rank.ACE, Suit.HEART); assertThat(card.getScore()).isEqualTo(1); } - - -} \ No newline at end of file +} From 5b5a0b18922be241580266f54942900e316d86e1 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Wed, 11 Mar 2026 19:15:58 +0900 Subject: [PATCH 21/48] =?UTF-8?q?refactor:=20ENUM=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EB=B7=B0=20=EC=9D=98=EC=A1=B4=EA=B4=80=EA=B3=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/domain/Card.java | 13 ++++--------- src/main/java/domain/Result.java | 12 +----------- src/main/java/view/OutputView.java | 27 +++++++++++++++++++++++++-- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/main/java/domain/Card.java b/src/main/java/domain/Card.java index 570cabd3d6a..03be860b27f 100644 --- a/src/main/java/domain/Card.java +++ b/src/main/java/domain/Card.java @@ -17,16 +17,11 @@ public boolean isAce() { return rank == Rank.ACE; } - public String getRankDisplayName() { - return rank.getDisplayName(); + public Rank getRank() { + return rank; } - public String getSuitShape() { - return suit.getShape(); + public Suit getSuit() { + return suit; } - -// @Override -// public String toString() { -// return rank.getDisplayName() + suit.getShape(); -// } } diff --git a/src/main/java/domain/Result.java b/src/main/java/domain/Result.java index bd40c184036..656c0cb2f45 100644 --- a/src/main/java/domain/Result.java +++ b/src/main/java/domain/Result.java @@ -1,15 +1,5 @@ package domain; public enum Result { - WIN("승"), LOSE("패"), TIE("무"); - - private final String displayName; - - Result(String displayName) { - this.displayName = displayName; - } - - public String getDisplayName() { - return displayName; - } + WIN, LOSE, TIE } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index b1ca13cb9a9..6a13f3e71ef 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -3,13 +3,35 @@ import domain.Card; import domain.Dealer; import domain.Player; +import domain.Rank; import domain.Result; +import domain.Suit; import java.util.ArrayList; import java.util.List; import java.util.Map; public class OutputView { + private static final Map SUIT_NAME = Map.of( + Suit.HEART, "하트", + Suit.DIAMOND, "다이아몬드", + Suit.SPADE, "스페이드", + Suit.CLUB, "클럽" + ); + + private static final Map RANK_NAME = Map.of( + Rank.ACE, "A", + Rank.JACK, "J", + Rank.QUEEN, "Q", + Rank.KING, "K" + ); + + private static final Map RESULT_NAME = Map.of( + Result.WIN, "승", + Result.LOSE, "패", + Result.TIE, "무" + ); + public void printInitialDeal(List playerNames) { System.out.println("딜러와 " + String.join(", ", playerNames) + "에게 2장을 나누었습니다."); } @@ -55,7 +77,7 @@ private int countResult(Map results, Result target) { private void printPlayerResults(Map results) { for (Map.Entry entry : results.entrySet()) { - System.out.println(entry.getKey().getName() + ": " + entry.getValue().getDisplayName()); + System.out.println(entry.getKey().getName() + ": " + RESULT_NAME.get(entry.getValue())); } } @@ -68,6 +90,7 @@ private String formatCards(List cards) { } private String formatCard(Card card) { - return card.getRankDisplayName() + card.getSuitShape(); + return RANK_NAME.getOrDefault(card.getRank(), String.valueOf(card.getRank().getScore())) + + SUIT_NAME.get(card.getSuit()); } } From d3f91ccffdd600ae3ae64fd3554433fefe0a9cb3 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Wed, 11 Mar 2026 20:25:32 +0900 Subject: [PATCH 22/48] =?UTF-8?q?refactor:=20Players=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=ED=8C=90=EC=A0=95=20=EB=A1=9C=EC=A7=81=20=EB=B6=84=EB=A6=AC=20?= =?UTF-8?q?=ED=9B=84=20Referee=EC=97=90=20=EC=9C=84=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/controller/BlackjackController.java | 8 ++- src/main/java/domain/Players.java | 26 --------- src/main/java/domain/Referee.java | 21 +++++++ src/main/java/view/OutputView.java | 1 - src/test/java/domain/PlayersTest.java | 57 ------------------- src/test/java/domain/RefereeTest.java | 47 +++++++++++++++ 6 files changed, 75 insertions(+), 85 deletions(-) create mode 100644 src/main/java/domain/Referee.java create mode 100644 src/test/java/domain/RefereeTest.java diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index e498aa763c1..c9ee3848b36 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -5,7 +5,9 @@ import domain.Participant; import domain.Player; import domain.Players; +import domain.Referee; import domain.Result; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import view.InputView; @@ -83,7 +85,11 @@ private void printFinalState(Players players) { for (Player player : players.getGamePlayers()) { outputView.printFinalCards(player); } - Map results = players.judge(); + Referee referee = new Referee(); + Map results = new LinkedHashMap<>(); + for (Player player : players.getGamePlayers()) { + results.put(player, referee.judge(player.calculateScore(), players.getDealer().calculateScore())); + } outputView.printFinalResult(players.getDealer(), results); } } diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java index 25680128b20..926c2441667 100644 --- a/src/main/java/domain/Players.java +++ b/src/main/java/domain/Players.java @@ -2,13 +2,10 @@ import java.util.ArrayList; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; public class Players { private static final String DEALER_NAME = "딜러"; - private static final int BUST_THRESHOLD = 21; private final List playerList; @@ -35,29 +32,6 @@ public List getGamePlayers() { return playerList.subList(1, playerList.size()); } - public Map judge() { - int dealerScore = getDealer().calculateScore(); - boolean dealerBust = dealerScore > BUST_THRESHOLD; - Map results = new LinkedHashMap<>(); - for (Player player : getGamePlayers()) { - results.put(player, judgePlayer(player.calculateScore(), dealerScore, dealerBust)); - } - return results; - } - - private Result judgePlayer(int playerScore, int dealerScore, boolean dealerBust) { - if (playerScore > BUST_THRESHOLD) { - return Result.LOSE; - } - if (dealerBust) { - return Result.WIN; - } - if (playerScore > dealerScore) { - return Result.WIN; - } - return Result.LOSE; - } - public int getSize() { return playerList.size(); } diff --git a/src/main/java/domain/Referee.java b/src/main/java/domain/Referee.java new file mode 100644 index 00000000000..2c4ab21535a --- /dev/null +++ b/src/main/java/domain/Referee.java @@ -0,0 +1,21 @@ +package domain; + +public class Referee { + private static final int BUST_THRESHOLD = 21; + + public Result judge(int playerScore, int dealerScore) { + if (playerScore > BUST_THRESHOLD) { + return Result.LOSE; + } + if (dealerScore > BUST_THRESHOLD) { + return Result.WIN; + } + if (playerScore > dealerScore) { + return Result.WIN; + } + if (playerScore == dealerScore) { + return Result.TIE; + } + return Result.LOSE; + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 6a13f3e71ef..7f20a7a6c20 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,7 +1,6 @@ package view; import domain.Card; -import domain.Dealer; import domain.Player; import domain.Rank; import domain.Result; diff --git a/src/test/java/domain/PlayersTest.java b/src/test/java/domain/PlayersTest.java index 8a5abd12795..be2d2cae8b1 100644 --- a/src/test/java/domain/PlayersTest.java +++ b/src/test/java/domain/PlayersTest.java @@ -4,7 +4,6 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import java.util.List; -import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -30,60 +29,4 @@ void beforeEach() { assertThatThrownBy(() -> new Players(List.of("아나키", "아나키", "모아"))) .isInstanceOf(IllegalArgumentException.class); } - - @DisplayName("플레이어 점수가 딜러보다 높으면 승리한다") - @Test - void 플레이어_점수가_딜러보다_높으면_승리한다() { - players.getDealer().addCard(new Card(Rank.TEN, Suit.SPADE)); // 10 - players.getGamePlayers().get(0).addCard(new Card(Rank.KING, Suit.HEART)); // 10 - players.getGamePlayers().get(0).addCard(new Card(Rank.ACE, Suit.SPADE)); // 21 - - Map result = players.judge(); - assertThat(result.get(players.getGamePlayers().get(0))).isEqualTo(Result.WIN); - } - - @DisplayName("플레이어 점수가 딜러보다 낮으면 패배한다") - @Test - void 플레이어_점수가_딜러보다_낮으면_패배한다() { - players.getDealer().addCard(new Card(Rank.KING, Suit.SPADE)); // 10 - players.getDealer().addCard(new Card(Rank.NINE, Suit.SPADE)); // 19 - players.getGamePlayers().get(0).addCard(new Card(Rank.SEVEN, Suit.HEART)); // 7 - - Map result = players.judge(); - assertThat(result.get(players.getGamePlayers().get(0))).isEqualTo(Result.LOSE); - } - - @DisplayName("동점이면 딜러가 이긴다") - @Test - void 동점이면_딜러가_이긴다() { - players.getDealer().addCard(new Card(Rank.KING, Suit.SPADE)); // 10 - players.getGamePlayers().get(0).addCard(new Card(Rank.TEN, Suit.HEART)); // 10 - - Map result = players.judge(); - assertThat(result.get(players.getGamePlayers().get(0))).isEqualTo(Result.LOSE); - } - - @DisplayName("플레이어가 버스트면 패배한다") - @Test - void 플레이어가_버스트면_패배한다() { - players.getDealer().addCard(new Card(Rank.FIVE, Suit.SPADE)); // 5 - players.getGamePlayers().get(0).addCard(new Card(Rank.KING, Suit.HEART)); // 10 - players.getGamePlayers().get(0).addCard(new Card(Rank.QUEEN, Suit.HEART)); // 10 - players.getGamePlayers().get(0).addCard(new Card(Rank.FIVE, Suit.DIAMOND)); // 25 - - Map result = players.judge(); - assertThat(result.get(players.getGamePlayers().get(0))).isEqualTo(Result.LOSE); - } - - @DisplayName("딜러가 버스트면 플레이어가 승리한다") - @Test - void 딜러가_버스트면_플레이어가_승리한다() { - players.getDealer().addCard(new Card(Rank.KING, Suit.SPADE)); // 10 - players.getDealer().addCard(new Card(Rank.QUEEN, Suit.SPADE)); // 10 - players.getDealer().addCard(new Card(Rank.FIVE, Suit.SPADE)); // 25 - players.getGamePlayers().get(0).addCard(new Card(Rank.SEVEN, Suit.HEART)); // 7 - - Map result = players.judge(); - assertThat(result.get(players.getGamePlayers().get(0))).isEqualTo(Result.WIN); - } } diff --git a/src/test/java/domain/RefereeTest.java b/src/test/java/domain/RefereeTest.java new file mode 100644 index 00000000000..150de3f1eee --- /dev/null +++ b/src/test/java/domain/RefereeTest.java @@ -0,0 +1,47 @@ +package domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class RefereeTest { + + Referee referee; + + @BeforeEach + void beforeEach() { + referee = new Referee(); + } + + @DisplayName("플레이어 점수가 딜러보다 높으면 승리한다") + @Test + void 플레이어_점수가_딜러보다_높으면_승리한다() { + assertThat(referee.judge(21, 10)).isEqualTo(Result.WIN); + } + + @DisplayName("플레이어 점수가 딜러보다 낮으면 패배한다") + @Test + void 플레이어_점수가_딜러보다_낮으면_패배한다() { + assertThat(referee.judge(7, 19)).isEqualTo(Result.LOSE); + } + + @DisplayName("동점이면 무승부가 된다") + @Test + void 동점이면_딜러가_이긴다() { + assertThat(referee.judge(10, 10)).isEqualTo(Result.TIE); + } + + @DisplayName("플레이어가 버스트면 패배한다") + @Test + void 플레이어가_버스트면_패배한다() { + assertThat(referee.judge(22, 5)).isEqualTo(Result.LOSE); + } + + @DisplayName("딜러가 버스트면 플레이어가 승리한다") + @Test + void 딜러가_버스트면_플레이어가_승리한다() { + assertThat(referee.judge(7, 22)).isEqualTo(Result.WIN); + } +} From 4624b1fb761026aff03bf937f0890de7ab1c0469 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Wed, 11 Mar 2026 20:36:32 +0900 Subject: [PATCH 23/48] =?UTF-8?q?refactor:=20VO=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EC=97=90=20=EB=8F=99=EB=93=B1=EC=84=B1=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/domain/Card.java | 16 ++++++++++++++++ src/main/java/domain/PlayerName.java | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/main/java/domain/Card.java b/src/main/java/domain/Card.java index 03be860b27f..b6763857f57 100644 --- a/src/main/java/domain/Card.java +++ b/src/main/java/domain/Card.java @@ -1,5 +1,7 @@ package domain; +import java.util.Objects; + public class Card { private final Rank rank; private final Suit suit; @@ -24,4 +26,18 @@ public Rank getRank() { public Suit getSuit() { return suit; } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + Card card = (Card) o; + return rank == card.rank && suit == card.suit; + } + + @Override + public int hashCode() { + return Objects.hash(rank, suit); + } } diff --git a/src/main/java/domain/PlayerName.java b/src/main/java/domain/PlayerName.java index cf20a37c625..a4dabc3f3ff 100644 --- a/src/main/java/domain/PlayerName.java +++ b/src/main/java/domain/PlayerName.java @@ -1,5 +1,7 @@ package domain; +import java.util.Objects; + public class PlayerName { private final String name; @@ -18,4 +20,18 @@ private void validate(String name) { public String getName() { return name; } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + PlayerName that = (PlayerName) o; + return Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } } From 6452da9009dd3ed564e42caba4556189f7baa9ac Mon Sep 17 00:00:00 2001 From: picetea44 Date: Wed, 11 Mar 2026 21:24:01 +0900 Subject: [PATCH 24/48] =?UTF-8?q?refactor:=20Hit=20=EC=B2=B4=ED=81=AC=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/controller/BlackjackController.java | 20 ++++++------ src/main/java/domain/Dealer.java | 8 ++++- src/main/java/domain/Participant.java | 16 ++++++++++ src/main/java/domain/Player.java | 31 +++---------------- src/main/java/domain/Players.java | 7 ----- src/main/java/view/OutputView.java | 11 ++++--- src/test/java/domain/PlayersTest.java | 2 +- 7 files changed, 44 insertions(+), 51 deletions(-) diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index c9ee3848b36..653462d2e3d 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -14,8 +14,6 @@ import view.OutputView; public class BlackjackController { - private static final int DEALER_HIT_THRESHOLD = 16; - private static final int BUST_THRESHOLD = 21; private final InputView inputView; private final OutputView outputView; @@ -34,8 +32,8 @@ public void run() { dealInitialCards(dealer, players, deck); printInitialState(dealer, players, names); playAllPlayerTurns(players, deck); - playDealerTurn(players.getDealer(), deck); - printFinalState(players); + playDealerTurn(dealer, deck); + printFinalState(dealer, players); } private void playAllPlayerTurns(Players players, Deck deck) { @@ -62,7 +60,7 @@ private void printInitialState(Dealer dealer, Players players, List name private void playPlayerTurn(Player player, Deck deck) { boolean cardShown = false; - while (player.calculateScore() <= BUST_THRESHOLD && inputView.askHit(player.getName())) { + while (player.canHit() && inputView.askHit(player.getName())) { player.addCard(deck.draw()); outputView.printPlayerCards(player); cardShown = true; @@ -72,24 +70,24 @@ private void playPlayerTurn(Player player, Deck deck) { } } - private void playDealerTurn(Player dealer, Deck deck) { - while (dealer.calculateScore() <= DEALER_HIT_THRESHOLD) { + private void playDealerTurn(Dealer dealer, Deck deck) { + while (dealer.canHit()) { dealer.addCard(deck.draw()); outputView.printDealerHit(); } } - private void printFinalState(Players players) { + private void printFinalState(Dealer dealer, Players players) { System.out.println(); - outputView.printFinalCards(players.getDealer()); + outputView.printFinalCards(dealer); for (Player player : players.getGamePlayers()) { outputView.printFinalCards(player); } Referee referee = new Referee(); Map results = new LinkedHashMap<>(); for (Player player : players.getGamePlayers()) { - results.put(player, referee.judge(player.calculateScore(), players.getDealer().calculateScore())); + results.put(player, referee.judge(player.calculateScore(), dealer.calculateScore())); } - outputView.printFinalResult(players.getDealer(), results); + outputView.printFinalResult(dealer, results); } } diff --git a/src/main/java/domain/Dealer.java b/src/main/java/domain/Dealer.java index ed850d7192b..ba8bba1a03d 100644 --- a/src/main/java/domain/Dealer.java +++ b/src/main/java/domain/Dealer.java @@ -1,11 +1,17 @@ package domain; -public class Dealer extends Participant{ +public class Dealer extends Participant { + private static final int HIT_THRESHOLD = 16; public Dealer(String name) { super(name); } + @Override + public boolean canHit() { + return calculateScore() <= HIT_THRESHOLD; + } + public Card getInitialCard() { return getCards().getFirst(); } diff --git a/src/main/java/domain/Participant.java b/src/main/java/domain/Participant.java index 9347a979bd7..03f1b4753a8 100644 --- a/src/main/java/domain/Participant.java +++ b/src/main/java/domain/Participant.java @@ -11,6 +11,8 @@ protected Participant(String name) { this.playerName = new PlayerName(name); } + public abstract boolean canHit(); + public void addCard(Card card) { cards.add(card); } @@ -23,4 +25,18 @@ public String getName() { return playerName.getName(); } + public int calculateScore() { + int score = 0; + boolean hasAce = false; + for (Card card : cards) { + score += card.getScore(); + if (card.isAce()) { + hasAce = true; + } + } + if (hasAce && score + 10 <= 21) { + return score + 10; + } + return score; + } } diff --git a/src/main/java/domain/Player.java b/src/main/java/domain/Player.java index 3c27bf801e0..f464bb542d7 100644 --- a/src/main/java/domain/Player.java +++ b/src/main/java/domain/Player.java @@ -3,36 +3,15 @@ import java.util.ArrayList; import java.util.List; -public class Player extends Participant{ +public class Player extends Participant { + private static final int BUST_THRESHOLD = 21; public Player(String name) { super(name); } - - public int calculateScore() { - int score = 0; - boolean hasAce = false; - - for (Card card : getCards()) { - score += card.getScore(); - if (card.isAce()) { - hasAce = true; - } - } - return applyAceBonus(score, hasAce); - } - - private int applyAceBonus(int score, boolean hasAce) { - if (isSoftHand(score, hasAce)) { - return score + 10; - } - return score; + @Override + public boolean canHit() { + return calculateScore() <= BUST_THRESHOLD; } - - private boolean isSoftHand(int score, boolean hasAce) { - return hasAce && score + 10 <= 21; - } - - } diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java index 926c2441667..e1a608c575b 100644 --- a/src/main/java/domain/Players.java +++ b/src/main/java/domain/Players.java @@ -5,14 +5,11 @@ import java.util.List; public class Players { - private static final String DEALER_NAME = "딜러"; - private final List playerList; public Players(List names) { validateDuplicate(names); playerList = new ArrayList<>(); - playerList.add(new Player(DEALER_NAME)); for (String name : names) { playerList.add(new Player(name)); } @@ -24,10 +21,6 @@ private void validateDuplicate(List names) { } } - public Player getDealer() { - return playerList.get(0); - } - public List getGamePlayers() { return playerList.subList(1, playerList.size()); } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 7f20a7a6c20..5c0f3d38707 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,6 +1,7 @@ package view; import domain.Card; +import domain.Participant; import domain.Player; import domain.Rank; import domain.Result; @@ -47,18 +48,18 @@ public void printDealerHit() { System.out.println("딜러는 16이하라 한장의 카드를 더 받았습니다."); } - public void printFinalCards(Player player) { - System.out.println(player.getName() + "카드: " + formatCards(player.getCards()) - + " - 결과: " + player.calculateScore()); + public void printFinalCards(Participant participant) { + System.out.println(participant.getName() + "카드: " + formatCards(participant.getCards()) + + " - 결과: " + participant.calculateScore()); } - public void printFinalResult(Player dealer, Map results) { + public void printFinalResult(Participant dealer, Map results) { System.out.println("\n## 최종 승패"); printDealerResult(dealer, results); printPlayerResults(results); } - private void printDealerResult(Player dealer, Map results) { + private void printDealerResult(Participant dealer, Map results) { int dealerWin = countResult(results, Result.LOSE); int dealerLose = countResult(results, Result.WIN); System.out.println(dealer.getName() + ": " + dealerWin + "승 " + dealerLose + "패"); diff --git a/src/test/java/domain/PlayersTest.java b/src/test/java/domain/PlayersTest.java index be2d2cae8b1..b3b640d2423 100644 --- a/src/test/java/domain/PlayersTest.java +++ b/src/test/java/domain/PlayersTest.java @@ -20,7 +20,7 @@ void beforeEach() { @DisplayName("입력에 따른 Player 객체 생성") @Test void 이름이_정상적으로_들어왔을때() { - assertThat(players.getSize()).isEqualTo(3); // 딜러 포함 + assertThat(players.getSize()).isEqualTo(2); } @DisplayName("이름이 중복이면 예외가 발생한다") From 1429d04513a3f672f07a92e8d7376ea6ff46282e Mon Sep 17 00:00:00 2001 From: picetea44 Date: Wed, 11 Mar 2026 22:20:10 +0900 Subject: [PATCH 25/48] =?UTF-8?q?refactor:=20CardBundle=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 --- .../java/controller/BlackjackController.java | 3 +- src/main/java/domain/CardBundle.java | 46 +++++++++++++++++++ src/main/java/domain/Dealer.java | 2 +- src/main/java/domain/Participant.java | 23 +++------- src/main/java/domain/Player.java | 5 +- src/main/java/domain/Players.java | 2 +- src/main/java/view/OutputView.java | 2 +- src/test/java/domain/PlayerTest.java | 9 ++-- 8 files changed, 61 insertions(+), 31 deletions(-) create mode 100644 src/main/java/domain/CardBundle.java diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index 653462d2e3d..8e8f8cea5b6 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -2,7 +2,6 @@ import domain.Dealer; import domain.Deck; -import domain.Participant; import domain.Player; import domain.Players; import domain.Referee; @@ -86,7 +85,7 @@ private void printFinalState(Dealer dealer, Players players) { Referee referee = new Referee(); Map results = new LinkedHashMap<>(); for (Player player : players.getGamePlayers()) { - results.put(player, referee.judge(player.calculateScore(), dealer.calculateScore())); + results.put(player, referee.judge(player.getScore(), dealer.getScore())); } outputView.printFinalResult(dealer, results); } diff --git a/src/main/java/domain/CardBundle.java b/src/main/java/domain/CardBundle.java new file mode 100644 index 00000000000..8e5b4ec7117 --- /dev/null +++ b/src/main/java/domain/CardBundle.java @@ -0,0 +1,46 @@ +package domain; + +import java.util.ArrayList; +import java.util.List; + +public class CardBundle { + private final List cards; + private static final int BUST_THRESHOLD = 21; + private static final int ACE_BONUS = 10; + + public CardBundle() { + this.cards = new ArrayList<>(); + } + + public int calculateScore() { + int score = 0; + boolean hasAce = false; + + for (Card card : cards) { + score += card.getScore(); + if (card.isAce()) { + hasAce = true; + } + } + return applyAceBonus(score, hasAce); + } + + private int applyAceBonus(int score, boolean hasAce) { + if (isSoftHand(score, hasAce)) { + return score + 10; + } + return score; + } + + private boolean isSoftHand(int score, boolean hasAce) { + return hasAce && score + ACE_BONUS <= BUST_THRESHOLD; + } + + public void addCard(Card card) { + cards.add(card); + } + + public List getCards() { + return cards; + } +} diff --git a/src/main/java/domain/Dealer.java b/src/main/java/domain/Dealer.java index ba8bba1a03d..228473b2f05 100644 --- a/src/main/java/domain/Dealer.java +++ b/src/main/java/domain/Dealer.java @@ -9,7 +9,7 @@ public Dealer(String name) { @Override public boolean canHit() { - return calculateScore() <= HIT_THRESHOLD; + return getScore() <= HIT_THRESHOLD; } public Card getInitialCard() { diff --git a/src/main/java/domain/Participant.java b/src/main/java/domain/Participant.java index 03f1b4753a8..17270de9ba8 100644 --- a/src/main/java/domain/Participant.java +++ b/src/main/java/domain/Participant.java @@ -1,42 +1,31 @@ package domain; -import java.util.ArrayList; import java.util.List; public abstract class Participant { private final PlayerName playerName; - private final List cards = new ArrayList<>(); + private final CardBundle cardBundle; protected Participant(String name) { this.playerName = new PlayerName(name); + this.cardBundle = new CardBundle(); } public abstract boolean canHit(); public void addCard(Card card) { - cards.add(card); + cardBundle.addCard(card); } public List getCards() { - return cards; + return cardBundle.getCards(); } public String getName() { return playerName.getName(); } - public int calculateScore() { - int score = 0; - boolean hasAce = false; - for (Card card : cards) { - score += card.getScore(); - if (card.isAce()) { - hasAce = true; - } - } - if (hasAce && score + 10 <= 21) { - return score + 10; - } - return score; + public int getScore() { + return cardBundle.calculateScore(); } } diff --git a/src/main/java/domain/Player.java b/src/main/java/domain/Player.java index f464bb542d7..b1c3cbc74d3 100644 --- a/src/main/java/domain/Player.java +++ b/src/main/java/domain/Player.java @@ -1,8 +1,5 @@ package domain; -import java.util.ArrayList; -import java.util.List; - public class Player extends Participant { private static final int BUST_THRESHOLD = 21; @@ -12,6 +9,6 @@ public Player(String name) { @Override public boolean canHit() { - return calculateScore() <= BUST_THRESHOLD; + return getScore() <= BUST_THRESHOLD; } } diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java index e1a608c575b..b3371e7e70e 100644 --- a/src/main/java/domain/Players.java +++ b/src/main/java/domain/Players.java @@ -22,7 +22,7 @@ private void validateDuplicate(List names) { } public List getGamePlayers() { - return playerList.subList(1, playerList.size()); + return playerList; } public int getSize() { diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 5c0f3d38707..b60298da0e7 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -50,7 +50,7 @@ public void printDealerHit() { public void printFinalCards(Participant participant) { System.out.println(participant.getName() + "카드: " + formatCards(participant.getCards()) - + " - 결과: " + participant.calculateScore()); + + " - 결과: " + participant.getScore()); } public void printFinalResult(Participant dealer, Map results) { diff --git a/src/test/java/domain/PlayerTest.java b/src/test/java/domain/PlayerTest.java index 07ec1fe0602..49391eb4416 100644 --- a/src/test/java/domain/PlayerTest.java +++ b/src/test/java/domain/PlayerTest.java @@ -3,7 +3,6 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -28,7 +27,7 @@ void beforeEach() { void 일반_카드의_점수를_합산한다() { player.addCard(new Card(Rank.KING, Suit.SPADE)); // 10 player.addCard(new Card(Rank.FIVE, Suit.HEART)); // 5 - assertThat(player.calculateScore()).isEqualTo(15); + assertThat(player.getScore()).isEqualTo(15); } @DisplayName("ACE가 있고 버스트가 아니면 11로 계산한다") @@ -36,7 +35,7 @@ void beforeEach() { void ACE가_있고_버스트가_아니면_11로_계산한다() { player.addCard(new Card(Rank.ACE, Suit.SPADE)); // 11 player.addCard(new Card(Rank.KING, Suit.HEART)); // 10 - assertThat(player.calculateScore()).isEqualTo(21); + assertThat(player.getScore()).isEqualTo(21); } @DisplayName("ACE가 있고 버스트면 1로 계산한다") @@ -45,7 +44,7 @@ void beforeEach() { player.addCard(new Card(Rank.ACE, Suit.SPADE)); // 1 player.addCard(new Card(Rank.KING, Suit.HEART)); // 10 player.addCard(new Card(Rank.FIVE, Suit.DIAMOND)); // 5 - assertThat(player.calculateScore()).isEqualTo(16); + assertThat(player.getScore()).isEqualTo(16); } @DisplayName("ACE가 여러 장이면 하나만 11로 계산한다") @@ -53,6 +52,6 @@ void beforeEach() { void ACE가_여러_장이면_하나만_11로_계산한다() { player.addCard(new Card(Rank.ACE, Suit.SPADE)); // 11 player.addCard(new Card(Rank.ACE, Suit.HEART)); // 1 - assertThat(player.calculateScore()).isEqualTo(12); + assertThat(player.getScore()).isEqualTo(12); } } From 5b7c169ff40e438b77169cd32cee8a5c27c4f1e2 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Wed, 11 Mar 2026 22:20:31 +0900 Subject: [PATCH 26/48] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=EB=A5=BC=20=EC=9C=84=ED=95=9C=20Deck=20si?= =?UTF-8?q?ze=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/domain/Deck.java | 3 --- src/test/java/domain/DeckTest.java | 39 +++++++++++++++++------------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/main/java/domain/Deck.java b/src/main/java/domain/Deck.java index 959bef411f4..889803d5d8e 100644 --- a/src/main/java/domain/Deck.java +++ b/src/main/java/domain/Deck.java @@ -23,7 +23,4 @@ public Card draw() { return cards.removeLast(); } - public int size() { - return cards.size(); - } } diff --git a/src/test/java/domain/DeckTest.java b/src/test/java/domain/DeckTest.java index 4e616963c7d..56d5ff0d349 100644 --- a/src/test/java/domain/DeckTest.java +++ b/src/test/java/domain/DeckTest.java @@ -1,8 +1,13 @@ package domain; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatNoException; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -15,26 +20,26 @@ void beforeEach() { deck = new Deck(); } - @DisplayName("덱을 생성하면 52장이다") + @DisplayName("덱에서 52번 뽑을 수 있고 53번째는 예외가 발생한다") @Test - void 덱을_생성하면_52장이다() { - assertThat(deck.size()).isEqualTo(52); - } - - @DisplayName("카드를 뽑으면 덱이 줄어든다") - @Test - void 카드를_뽑으면_덱이_줄어든다() { - deck.draw(); - assertThat(deck.size()).isEqualTo(51); + void 덱에서_52번_뽑을_수_있다() { + assertThatNoException().isThrownBy(() -> { + for (int i = 0; i < 52; i++) { + deck.draw(); + } + }); + assertThatThrownBy(() -> deck.draw()) + .isInstanceOf(IllegalStateException.class); } - @DisplayName("빈 덱에서 카드를 뽑으면 예외가 발생한다") + @DisplayName("덱의 모든 카드는 중복 없이 유니크하다") @Test - void 빈_덱에서_카드를_뽑으면_예외가_발생한다() { - assertThatThrownBy(() -> { - for (int i = 0; i <= 52; i++) { - deck.draw(); - } - }).isInstanceOf(IllegalStateException.class); + void 덱의_모든_카드는_중복_없이_유니크하다() { + List cards = new ArrayList<>(); + for (int i = 0; i < 52; i++) { + cards.add(deck.draw()); + } + Set uniqueCards = new HashSet<>(cards); + assertThat(uniqueCards.size()).isEqualTo(52); } } From dd01040a501aec7dc5e2932abda16fbf2bc702e8 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Thu, 12 Mar 2026 12:14:42 +0900 Subject: [PATCH 27/48] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=9D=BC=EA=B4=80=EC=84=B1=20=EB=B0=8F=20=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/CardBundleTest.java | 50 ++++++++++++++++++++++++ src/test/java/domain/CardTest.java | 14 ++++++- src/test/java/domain/DealerTest.java | 33 ++++++++++++++++ src/test/java/domain/DeckTest.java | 2 +- src/test/java/domain/PlayerNameTest.java | 40 +++++++++++++++++++ src/test/java/domain/PlayerTest.java | 19 ++++++++- src/test/java/domain/PlayersTest.java | 2 +- src/test/java/domain/RefereeTest.java | 8 +++- src/test/java/view/InputViewTest.java | 13 ------ 9 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 src/test/java/domain/CardBundleTest.java create mode 100644 src/test/java/domain/DealerTest.java create mode 100644 src/test/java/domain/PlayerNameTest.java delete mode 100644 src/test/java/view/InputViewTest.java diff --git a/src/test/java/domain/CardBundleTest.java b/src/test/java/domain/CardBundleTest.java new file mode 100644 index 00000000000..b23d3fec4b4 --- /dev/null +++ b/src/test/java/domain/CardBundleTest.java @@ -0,0 +1,50 @@ +package domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class CardBundleTest { + + private CardBundle cardBundle; + + @BeforeEach + void beforeEach() { + cardBundle = new CardBundle(); + } + + @DisplayName("일반 카드의 점수를 합산한다") + @Test + void 일반_카드의_점수를_합산한다() { + cardBundle.addCard(new Card(Rank.KING, Suit.SPADE)); // 10 + cardBundle.addCard(new Card(Rank.FIVE, Suit.HEART)); // 5 + assertThat(cardBundle.calculateScore()).isEqualTo(15); + } + + @DisplayName("ACE가 있고 버스트가 아니면 11로 계산한다") + @Test + void ACE가_있고_버스트가_아니면_11로_계산한다() { + cardBundle.addCard(new Card(Rank.ACE, Suit.SPADE)); // 11 + cardBundle.addCard(new Card(Rank.KING, Suit.HEART)); // 10 + assertThat(cardBundle.calculateScore()).isEqualTo(21); + } + + @DisplayName("ACE가 있고 버스트면 1로 계산한다") + @Test + void ACE가_있고_버스트면_1로_계산한다() { + cardBundle.addCard(new Card(Rank.ACE, Suit.SPADE)); // 1 + cardBundle.addCard(new Card(Rank.KING, Suit.HEART)); // 10 + cardBundle.addCard(new Card(Rank.FIVE, Suit.DIAMOND)); // 5 + assertThat(cardBundle.calculateScore()).isEqualTo(16); + } + + @DisplayName("ACE가 여러 장이면 하나만 11로 계산한다") + @Test + void ACE가_여러_장이면_하나만_11로_계산한다() { + cardBundle.addCard(new Card(Rank.ACE, Suit.SPADE)); // 11 + cardBundle.addCard(new Card(Rank.ACE, Suit.HEART)); // 1 + assertThat(cardBundle.calculateScore()).isEqualTo(12); + } +} diff --git a/src/test/java/domain/CardTest.java b/src/test/java/domain/CardTest.java index d748b9fb12c..e0ac8f4f918 100644 --- a/src/test/java/domain/CardTest.java +++ b/src/test/java/domain/CardTest.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test; -class CardTest { +public class CardTest { @Test void 카드의_점수를_반환한다() { Card card = new Card(Rank.KING, Suit.SPADE); @@ -16,4 +16,16 @@ class CardTest { Card card = new Card(Rank.ACE, Suit.HEART); assertThat(card.getScore()).isEqualTo(1); } + + @Test + void ACE_카드는_isAce가_true이다() { + Card card = new Card(Rank.ACE, Suit.HEART); + assertThat(card.isAce()).isTrue(); + } + + @Test + void ACE가_아닌_카드는_isAce가_false이다() { + Card card = new Card(Rank.KING, Suit.SPADE); + assertThat(card.isAce()).isFalse(); + } } diff --git a/src/test/java/domain/DealerTest.java b/src/test/java/domain/DealerTest.java new file mode 100644 index 00000000000..62752c7690b --- /dev/null +++ b/src/test/java/domain/DealerTest.java @@ -0,0 +1,33 @@ +package domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class DealerTest { + + private Dealer dealer; + + @BeforeEach + void beforeEach() { + dealer = new Dealer("딜러"); + } + + @DisplayName("점수가 16 이하면 히트할 수 있다") + @Test + void 점수가_16이하면_히트할_수_있다() { + dealer.addCard(new Card(Rank.KING, Suit.SPADE)); // 10 + dealer.addCard(new Card(Rank.SIX, Suit.HEART)); // 6 + assertThat(dealer.canHit()).isTrue(); + } + + @DisplayName("점수가 17 이상이면 히트할 수 없다") + @Test + void 점수가_17이상이면_히트할_수_없다() { + dealer.addCard(new Card(Rank.KING, Suit.SPADE)); // 10 + dealer.addCard(new Card(Rank.SEVEN, Suit.HEART)); // 7 + assertThat(dealer.canHit()).isFalse(); + } +} diff --git a/src/test/java/domain/DeckTest.java b/src/test/java/domain/DeckTest.java index 56d5ff0d349..fd4a04b946b 100644 --- a/src/test/java/domain/DeckTest.java +++ b/src/test/java/domain/DeckTest.java @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test; public class DeckTest { - Deck deck; + private Deck deck; @BeforeEach void beforeEach() { diff --git a/src/test/java/domain/PlayerNameTest.java b/src/test/java/domain/PlayerNameTest.java new file mode 100644 index 00000000000..6344b219f68 --- /dev/null +++ b/src/test/java/domain/PlayerNameTest.java @@ -0,0 +1,40 @@ +package domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class PlayerNameTest { + + @DisplayName("null이 들어오면 예외가 발생한다") + @Test + void null이면_예외가_발생한다() { + assertThatThrownBy(() -> new PlayerName(null)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("공백이 들어오면 예외가 발생한다") + @Test + void 공백이면_예외가_발생한다() { + assertThatThrownBy(() -> new PlayerName(" ")) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("같은 이름이면 동등하다") + @Test + void 같은_이름이면_동등하다() { + PlayerName name1 = new PlayerName("pobi"); + PlayerName name2 = new PlayerName("pobi"); + assertThat(name1).isEqualTo(name2); + } + + @DisplayName("다른 이름이면 동등하지 않다") + @Test + void 다른_이름이면_동등하지_않다() { + PlayerName name1 = new PlayerName("pobi"); + PlayerName name2 = new PlayerName("jason"); + assertThat(name1).isNotEqualTo(name2); + } +} diff --git a/src/test/java/domain/PlayerTest.java b/src/test/java/domain/PlayerTest.java index 49391eb4416..6370d710e77 100644 --- a/src/test/java/domain/PlayerTest.java +++ b/src/test/java/domain/PlayerTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test; public class PlayerTest { - Player player; + private Player player; @BeforeEach void beforeEach() { @@ -54,4 +54,21 @@ void beforeEach() { player.addCard(new Card(Rank.ACE, Suit.HEART)); // 1 assertThat(player.getScore()).isEqualTo(12); } + + @DisplayName("점수가 21 이하면 히트할 수 있다") + @Test + void 점수가_21이하면_히트할_수_있다() { + player.addCard(new Card(Rank.KING, Suit.SPADE)); // 10 + player.addCard(new Card(Rank.FIVE, Suit.HEART)); // 5 + assertThat(player.canHit()).isTrue(); + } + + @DisplayName("점수가 22 이상이면 히트할 수 없다") + @Test + void 점수가_22이상이면_히트할_수_없다() { + player.addCard(new Card(Rank.KING, Suit.SPADE)); // 10 + player.addCard(new Card(Rank.KING, Suit.HEART)); // 10 + player.addCard(new Card(Rank.TWO, Suit.DIAMOND)); // 2 + assertThat(player.canHit()).isFalse(); + } } diff --git a/src/test/java/domain/PlayersTest.java b/src/test/java/domain/PlayersTest.java index b3b640d2423..c9365b663f2 100644 --- a/src/test/java/domain/PlayersTest.java +++ b/src/test/java/domain/PlayersTest.java @@ -10,7 +10,7 @@ public class PlayersTest { - Players players; + private Players players; @BeforeEach void beforeEach() { diff --git a/src/test/java/domain/RefereeTest.java b/src/test/java/domain/RefereeTest.java index 150de3f1eee..58cac6541ef 100644 --- a/src/test/java/domain/RefereeTest.java +++ b/src/test/java/domain/RefereeTest.java @@ -8,7 +8,7 @@ public class RefereeTest { - Referee referee; + private Referee referee; @BeforeEach void beforeEach() { @@ -44,4 +44,10 @@ void beforeEach() { void 딜러가_버스트면_플레이어가_승리한다() { assertThat(referee.judge(7, 22)).isEqualTo(Result.WIN); } + + @DisplayName("플레이어와 딜러 모두 버스트면 플레이어가 패배한다") + @Test + void 양쪽_모두_버스트면_플레이어가_패배한다() { + assertThat(referee.judge(22, 22)).isEqualTo(Result.LOSE); + } } diff --git a/src/test/java/view/InputViewTest.java b/src/test/java/view/InputViewTest.java deleted file mode 100644 index 91a80883b88..00000000000 --- a/src/test/java/view/InputViewTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package view; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class InputViewTest { - -} From 496bd156a986b0ed1d710af186bdf823aa07f300 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Thu, 12 Mar 2026 17:14:59 +0900 Subject: [PATCH 28/48] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=94=8C=EB=9E=98=EA=B7=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/controller/BlackjackController.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index 8e8f8cea5b6..b4dd42b645d 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -58,14 +58,9 @@ private void printInitialState(Dealer dealer, Players players, List name } private void playPlayerTurn(Player player, Deck deck) { - boolean cardShown = false; while (player.canHit() && inputView.askHit(player.getName())) { player.addCard(deck.draw()); outputView.printPlayerCards(player); - cardShown = true; - } - if (!cardShown) { - outputView.printPlayerCards(player); } } From e0ed3a9b4da88fc71c530d3d7c4260d949879739 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Fri, 13 Mar 2026 10:52:00 +0900 Subject: [PATCH 29/48] =?UTF-8?q?feat:=20=EB=B0=B0=ED=8C=85=20=EA=B8=88?= =?UTF-8?q?=EC=95=A1=20VO=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 61 +++++++++++----------- src/main/java/domain/BettingMoney.java | 20 +++++++ src/test/java/domain/BettingMoneyTest.java | 21 ++++++++ 3 files changed, 72 insertions(+), 30 deletions(-) create mode 100644 src/main/java/domain/BettingMoney.java create mode 100644 src/test/java/domain/BettingMoneyTest.java diff --git a/README.md b/README.md index 3b8f8fa0c40..14377955dae 100644 --- a/README.md +++ b/README.md @@ -45,49 +45,50 @@ 이번 페어 프로그래밍을 진행하며 제 스스로의 부족함을 많이 통감했습니다. 다른 팀들은 디자인 패턴을 적극적으로 도입하고 논의하는데, 저는 그 대화에 온전히 참여하지 못하거나 패턴을 코드에 녹여내는 데 어려움을 겪었습니다. 심지어는 비즈니스 로직에 집중하다 보니 가끔 가장 기본적인 언어 문법조차 헷갈려 당황스러운 순간들도 있었습니다. 객체지향적인 설계는커녕 지식의 파편화로 인해 곳곳에 빈틈이 있다는 느낌을 많이 받았습니다. 리뷰어님이 보시기에 현재 제 수준에서 어떤 부분을 가장 우선순위에 두고 학습해야 할까요? 단순히 패턴을 외우는 것보다, 제가 놓치고 있는 기본기가 무엇인지 리뷰어님의 시선에서 냉철하게 짚어주시고 앞으로의 공부 방향을 제시해 주실 수 있다면 정말 감사하겠습니다. -## ✅ 게임 기능 TO-DO -### 게임에 참여할 사람의 이름 입력 +--- -- [x] 쉼표 기준으로 분리 -- [x] 정상적인 이름 앞뒤의 공백 제거 -- [x] `ㅁㅁ, ,ㅇㅇ` → 빈 문자열은 예외처리 +## 베팅 기능 TO-DO -### 각 플레이어, 딜러에게 카드를 랜덤으로 2장 배분하는 기능 +### 베팅 금액 입력 -- [x] 딜러는 2번째 카드는 출력하지 않음 -- [x] 이미 정해진 덱에서 랜덤 분배 +- [ ] 각 플레이어의 베팅 금액을 입력받는 기능 +- [ ] 베팅 금액 검증 (양수만 허용) +- [ ] `BettingMoney` 값 객체 생성 -### 플레이어에게 카드를 더 받을지 묻는 기능 +### 블랙잭 판별 -- [x] 이미 뽑힌 카드는 다시 나오지 않게 처리 -- [x] 한 플레이어가 n을 입력하면 해당 플레이어 종료 +- [ ] `CardBundle`에 `isBlackjack()` 추가 (카드 2장 && 점수 21) +- [ ] `CardBundle`에 `isBust()` 추가 (점수 > 21) +- [ ] `Participant`에 위임 메서드 추가 -### 딜러가 카드를 더 받는지 출력 +### 승패 판정 개선 -- [x] 16 이하라 한 장 받을 때마다 문장 출력 -- [x] 여러 장 받을 경우 한 장씩 문장 반복 출력 -- [x] 처음부터 17 이상이면 카드 추가 없이 안내 문장 출력 여부 결정 +- [ ] `Result`에 `BLACKJACK_WIN` 추가 및 배율(profitRate) 필드 도입 +- [ ] `Referee` 블랙잭 판정 로직 추가 + - [ ] 플레이어 블랙잭 + 딜러 블랙잭 → 무승부 + - [ ] 플레이어 블랙잭 + 딜러 일반 → 블랙잭 승리 (1.5배) + - [ ] 딜러 버스트 → 남아있는 플레이어 승리 -### 카드 객체 설계 +### 수익 계산 -- [x] 전체 카드 덱을 미리 생성 -- [x] deck을 랜덤 추출 기능으로 구현 -- [x] suit, rank를 각각 Enum으로 관리 -- [x] 카드 문양, 표기 점수 관리 방식 결정 +- [ ] `BettingMoney`에 `calculateProfit(Result)` 구현 (금액 × 배율) +- [ ] 딜러 수익 계산 (플레이어 수익 합산의 부호 반전) -### 카드 점수 합 계산 기능 +### Player에 베팅 연결 -- [x] rank 값만 추출하여 합산 -- [x] 총합 계산 로직 구현 +- [ ] `Player`에 `BettingMoney` 필드 추가 +- [ ] `Players` 생성 방식 변경 (이름 + 금액) -### 결과 출력 +### 입출력 수정 -- [x] 딜러가 가진 카드 목록 출력 -- [x] 딜러 카드 점수 합 출력 -- [x] 플레이어가 가진 카드 목록 출력 -- [x] 플레이어 카드 점수 합 출력 +- [ ] `InputView`에 베팅 금액 입력 메서드 추가 +- [ ] `OutputView` 최종 결과를 금액으로 출력하도록 변경 +- [ ] `BlackjackController`에 베팅 입력 단계 및 수익 출력 단계 추가 -### 승패 출력 +### 테스트 -- [x] 계산된 합에 따라 승패 판별 +- [ ] `BettingMoney` 검증 테스트 (양수, 0, 음수) +- [ ] `CardBundle` 블랙잭/버스트 판별 테스트 +- [ ] `Referee` 블랙잭 케이스 판정 테스트 +- [ ] 수익 계산 테스트 (블랙잭 승리, 일반 승리, 무승부, 패배) diff --git a/src/main/java/domain/BettingMoney.java b/src/main/java/domain/BettingMoney.java new file mode 100644 index 00000000000..e1032d52231 --- /dev/null +++ b/src/main/java/domain/BettingMoney.java @@ -0,0 +1,20 @@ +package domain; + +public class BettingMoney { + private final int amount; + + public BettingMoney(int amount) { + validate(amount); + this.amount = amount; + } + + private void validate(int amount) { + if (amount <= 0) { + throw new IllegalArgumentException("베팅 금액이 0이하 일 수 없습니다."); + } + } + + public int getAmount() { + return amount; + } +} diff --git a/src/test/java/domain/BettingMoneyTest.java b/src/test/java/domain/BettingMoneyTest.java new file mode 100644 index 00000000000..e0796015dbe --- /dev/null +++ b/src/test/java/domain/BettingMoneyTest.java @@ -0,0 +1,21 @@ +package domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +public class BettingMoneyTest { + + @Test + void 베팅_금액을_생성한다() { + BettingMoney money = new BettingMoney(1000); + assertThat(money.getAmount()).isEqualTo(1000); + } + + @Test + void 양의_정수가_아니라면_예외가_발생한다() { + assertThatThrownBy(() -> new BettingMoney(-1000)) + .isInstanceOf(IllegalArgumentException.class); + } +} From 282a2f7d80dd0958f65ed870cda6a841eb9c2de9 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Fri, 13 Mar 2026 19:08:51 +0900 Subject: [PATCH 30/48] =?UTF-8?q?refactor:=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=20=ED=8C=A8=ED=82=A4=EC=A7=80=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/controller/BlackjackController.java | 12 ++++++------ src/main/java/domain/{ => bet}/BettingMoney.java | 2 +- src/main/java/domain/{ => card}/Card.java | 2 +- src/main/java/domain/{ => card}/CardBundle.java | 2 +- src/main/java/domain/{ => card}/Deck.java | 2 +- src/main/java/domain/{ => card}/Rank.java | 2 +- src/main/java/domain/{ => card}/Suit.java | 2 +- src/main/java/domain/{ => game}/Referee.java | 2 +- src/main/java/domain/{ => game}/Result.java | 2 +- src/main/java/domain/{ => participant}/Dealer.java | 4 +++- .../java/domain/{ => participant}/Participant.java | 4 +++- src/main/java/domain/{ => participant}/Player.java | 2 +- .../java/domain/{ => participant}/PlayerName.java | 2 +- src/main/java/domain/{ => participant}/Players.java | 2 +- src/main/java/view/OutputView.java | 12 ++++++------ src/test/java/domain/{ => bet}/BettingMoneyTest.java | 2 +- src/test/java/domain/{ => card}/CardBundleTest.java | 2 +- src/test/java/domain/{ => card}/CardTest.java | 2 +- src/test/java/domain/{ => card}/DeckTest.java | 2 +- src/test/java/domain/{ => game}/RefereeTest.java | 2 +- .../java/domain/{ => participant}/DealerTest.java | 5 ++++- .../domain/{ => participant}/PlayerNameTest.java | 2 +- .../java/domain/{ => participant}/PlayerTest.java | 5 ++++- .../java/domain/{ => participant}/PlayersTest.java | 2 +- 24 files changed, 44 insertions(+), 34 deletions(-) rename src/main/java/domain/{ => bet}/BettingMoney.java (95%) rename src/main/java/domain/{ => card}/Card.java (97%) rename src/main/java/domain/{ => card}/CardBundle.java (97%) rename src/main/java/domain/{ => card}/Deck.java (96%) rename src/main/java/domain/{ => card}/Rank.java (94%) rename src/main/java/domain/{ => card}/Suit.java (72%) rename src/main/java/domain/{ => game}/Referee.java (96%) rename src/main/java/domain/{ => game}/Result.java (67%) rename src/main/java/domain/{ => participant}/Dealer.java (85%) rename src/main/java/domain/{ => participant}/Participant.java (88%) rename src/main/java/domain/{ => participant}/Player.java (90%) rename src/main/java/domain/{ => participant}/PlayerName.java (96%) rename src/main/java/domain/{ => participant}/Players.java (96%) rename src/test/java/domain/{ => bet}/BettingMoneyTest.java (96%) rename src/test/java/domain/{ => card}/CardBundleTest.java (98%) rename src/test/java/domain/{ => card}/CardTest.java (97%) rename src/test/java/domain/{ => card}/DeckTest.java (98%) rename src/test/java/domain/{ => game}/RefereeTest.java (98%) rename src/test/java/domain/{ => participant}/DealerTest.java (90%) rename src/test/java/domain/{ => participant}/PlayerNameTest.java (97%) rename src/test/java/domain/{ => participant}/PlayerTest.java (96%) rename src/test/java/domain/{ => participant}/PlayersTest.java (97%) diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index b4dd42b645d..6b28682dfdc 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -1,11 +1,11 @@ package controller; -import domain.Dealer; -import domain.Deck; -import domain.Player; -import domain.Players; -import domain.Referee; -import domain.Result; +import domain.participant.Dealer; +import domain.card.Deck; +import domain.participant.Player; +import domain.participant.Players; +import domain.game.Referee; +import domain.game.Result; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; diff --git a/src/main/java/domain/BettingMoney.java b/src/main/java/domain/bet/BettingMoney.java similarity index 95% rename from src/main/java/domain/BettingMoney.java rename to src/main/java/domain/bet/BettingMoney.java index e1032d52231..7dd4b271f62 100644 --- a/src/main/java/domain/BettingMoney.java +++ b/src/main/java/domain/bet/BettingMoney.java @@ -1,4 +1,4 @@ -package domain; +package domain.bet; public class BettingMoney { private final int amount; diff --git a/src/main/java/domain/Card.java b/src/main/java/domain/card/Card.java similarity index 97% rename from src/main/java/domain/Card.java rename to src/main/java/domain/card/Card.java index b6763857f57..07d03ed41aa 100644 --- a/src/main/java/domain/Card.java +++ b/src/main/java/domain/card/Card.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import java.util.Objects; diff --git a/src/main/java/domain/CardBundle.java b/src/main/java/domain/card/CardBundle.java similarity index 97% rename from src/main/java/domain/CardBundle.java rename to src/main/java/domain/card/CardBundle.java index 8e5b4ec7117..bac7226c8d4 100644 --- a/src/main/java/domain/CardBundle.java +++ b/src/main/java/domain/card/CardBundle.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/domain/Deck.java b/src/main/java/domain/card/Deck.java similarity index 96% rename from src/main/java/domain/Deck.java rename to src/main/java/domain/card/Deck.java index 889803d5d8e..fba59a1708b 100644 --- a/src/main/java/domain/Deck.java +++ b/src/main/java/domain/card/Deck.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import java.util.ArrayList; import java.util.Collections; diff --git a/src/main/java/domain/Rank.java b/src/main/java/domain/card/Rank.java similarity index 94% rename from src/main/java/domain/Rank.java rename to src/main/java/domain/card/Rank.java index 93dc2c2763f..831062bd4e4 100644 --- a/src/main/java/domain/Rank.java +++ b/src/main/java/domain/card/Rank.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; public enum Rank { ACE(1), diff --git a/src/main/java/domain/Suit.java b/src/main/java/domain/card/Suit.java similarity index 72% rename from src/main/java/domain/Suit.java rename to src/main/java/domain/card/Suit.java index 28f5f541438..d613cff8ce7 100644 --- a/src/main/java/domain/Suit.java +++ b/src/main/java/domain/card/Suit.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; public enum Suit { HEART, DIAMOND, SPADE, CLUB diff --git a/src/main/java/domain/Referee.java b/src/main/java/domain/game/Referee.java similarity index 96% rename from src/main/java/domain/Referee.java rename to src/main/java/domain/game/Referee.java index 2c4ab21535a..3b51cb2a8df 100644 --- a/src/main/java/domain/Referee.java +++ b/src/main/java/domain/game/Referee.java @@ -1,4 +1,4 @@ -package domain; +package domain.game; public class Referee { private static final int BUST_THRESHOLD = 21; diff --git a/src/main/java/domain/Result.java b/src/main/java/domain/game/Result.java similarity index 67% rename from src/main/java/domain/Result.java rename to src/main/java/domain/game/Result.java index 656c0cb2f45..6763e93a4f5 100644 --- a/src/main/java/domain/Result.java +++ b/src/main/java/domain/game/Result.java @@ -1,4 +1,4 @@ -package domain; +package domain.game; public enum Result { WIN, LOSE, TIE diff --git a/src/main/java/domain/Dealer.java b/src/main/java/domain/participant/Dealer.java similarity index 85% rename from src/main/java/domain/Dealer.java rename to src/main/java/domain/participant/Dealer.java index 228473b2f05..841602ec4e4 100644 --- a/src/main/java/domain/Dealer.java +++ b/src/main/java/domain/participant/Dealer.java @@ -1,4 +1,6 @@ -package domain; +package domain.participant; + +import domain.card.Card; public class Dealer extends Participant { private static final int HIT_THRESHOLD = 16; diff --git a/src/main/java/domain/Participant.java b/src/main/java/domain/participant/Participant.java similarity index 88% rename from src/main/java/domain/Participant.java rename to src/main/java/domain/participant/Participant.java index 17270de9ba8..e7bcf209cfd 100644 --- a/src/main/java/domain/Participant.java +++ b/src/main/java/domain/participant/Participant.java @@ -1,5 +1,7 @@ -package domain; +package domain.participant; +import domain.card.Card; +import domain.card.CardBundle; import java.util.List; public abstract class Participant { diff --git a/src/main/java/domain/Player.java b/src/main/java/domain/participant/Player.java similarity index 90% rename from src/main/java/domain/Player.java rename to src/main/java/domain/participant/Player.java index b1c3cbc74d3..80795b0e57f 100644 --- a/src/main/java/domain/Player.java +++ b/src/main/java/domain/participant/Player.java @@ -1,4 +1,4 @@ -package domain; +package domain.participant; public class Player extends Participant { private static final int BUST_THRESHOLD = 21; diff --git a/src/main/java/domain/PlayerName.java b/src/main/java/domain/participant/PlayerName.java similarity index 96% rename from src/main/java/domain/PlayerName.java rename to src/main/java/domain/participant/PlayerName.java index a4dabc3f3ff..a54bb91dc85 100644 --- a/src/main/java/domain/PlayerName.java +++ b/src/main/java/domain/participant/PlayerName.java @@ -1,4 +1,4 @@ -package domain; +package domain.participant; import java.util.Objects; diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/participant/Players.java similarity index 96% rename from src/main/java/domain/Players.java rename to src/main/java/domain/participant/Players.java index b3371e7e70e..98485aa28be 100644 --- a/src/main/java/domain/Players.java +++ b/src/main/java/domain/participant/Players.java @@ -1,4 +1,4 @@ -package domain; +package domain.participant; import java.util.ArrayList; import java.util.HashSet; diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index b60298da0e7..06ee58efda2 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,11 +1,11 @@ package view; -import domain.Card; -import domain.Participant; -import domain.Player; -import domain.Rank; -import domain.Result; -import domain.Suit; +import domain.card.Card; +import domain.participant.Participant; +import domain.participant.Player; +import domain.card.Rank; +import domain.game.Result; +import domain.card.Suit; import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/src/test/java/domain/BettingMoneyTest.java b/src/test/java/domain/bet/BettingMoneyTest.java similarity index 96% rename from src/test/java/domain/BettingMoneyTest.java rename to src/test/java/domain/bet/BettingMoneyTest.java index e0796015dbe..733fd7f49c7 100644 --- a/src/test/java/domain/BettingMoneyTest.java +++ b/src/test/java/domain/bet/BettingMoneyTest.java @@ -1,4 +1,4 @@ -package domain; +package domain.bet; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; diff --git a/src/test/java/domain/CardBundleTest.java b/src/test/java/domain/card/CardBundleTest.java similarity index 98% rename from src/test/java/domain/CardBundleTest.java rename to src/test/java/domain/card/CardBundleTest.java index b23d3fec4b4..416a8e805c3 100644 --- a/src/test/java/domain/CardBundleTest.java +++ b/src/test/java/domain/card/CardBundleTest.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; diff --git a/src/test/java/domain/CardTest.java b/src/test/java/domain/card/CardTest.java similarity index 97% rename from src/test/java/domain/CardTest.java rename to src/test/java/domain/card/CardTest.java index e0ac8f4f918..5ac9e07496f 100644 --- a/src/test/java/domain/CardTest.java +++ b/src/test/java/domain/card/CardTest.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; diff --git a/src/test/java/domain/DeckTest.java b/src/test/java/domain/card/DeckTest.java similarity index 98% rename from src/test/java/domain/DeckTest.java rename to src/test/java/domain/card/DeckTest.java index fd4a04b946b..b6268191c08 100644 --- a/src/test/java/domain/DeckTest.java +++ b/src/test/java/domain/card/DeckTest.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatNoException; diff --git a/src/test/java/domain/RefereeTest.java b/src/test/java/domain/game/RefereeTest.java similarity index 98% rename from src/test/java/domain/RefereeTest.java rename to src/test/java/domain/game/RefereeTest.java index 58cac6541ef..001f82de8f5 100644 --- a/src/test/java/domain/RefereeTest.java +++ b/src/test/java/domain/game/RefereeTest.java @@ -1,4 +1,4 @@ -package domain; +package domain.game; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; diff --git a/src/test/java/domain/DealerTest.java b/src/test/java/domain/participant/DealerTest.java similarity index 90% rename from src/test/java/domain/DealerTest.java rename to src/test/java/domain/participant/DealerTest.java index 62752c7690b..65190b21305 100644 --- a/src/test/java/domain/DealerTest.java +++ b/src/test/java/domain/participant/DealerTest.java @@ -1,7 +1,10 @@ -package domain; +package domain.participant; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/domain/PlayerNameTest.java b/src/test/java/domain/participant/PlayerNameTest.java similarity index 97% rename from src/test/java/domain/PlayerNameTest.java rename to src/test/java/domain/participant/PlayerNameTest.java index 6344b219f68..b6bc386c312 100644 --- a/src/test/java/domain/PlayerNameTest.java +++ b/src/test/java/domain/participant/PlayerNameTest.java @@ -1,4 +1,4 @@ -package domain; +package domain.participant; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; diff --git a/src/test/java/domain/PlayerTest.java b/src/test/java/domain/participant/PlayerTest.java similarity index 96% rename from src/test/java/domain/PlayerTest.java rename to src/test/java/domain/participant/PlayerTest.java index 6370d710e77..61a21bbe63b 100644 --- a/src/test/java/domain/PlayerTest.java +++ b/src/test/java/domain/participant/PlayerTest.java @@ -1,8 +1,11 @@ -package domain; +package domain.participant; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/domain/PlayersTest.java b/src/test/java/domain/participant/PlayersTest.java similarity index 97% rename from src/test/java/domain/PlayersTest.java rename to src/test/java/domain/participant/PlayersTest.java index c9365b663f2..056dbf20728 100644 --- a/src/test/java/domain/PlayersTest.java +++ b/src/test/java/domain/participant/PlayersTest.java @@ -1,4 +1,4 @@ -package domain; +package domain.participant; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; From a767b97172823a6c684edb1759478e93f0d9922a Mon Sep 17 00:00:00 2001 From: picetea44 Date: Fri, 13 Mar 2026 19:19:19 +0900 Subject: [PATCH 31/48] =?UTF-8?q?refactor:=20ENUM=20=EA=B4=80=EB=A1=80?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EA=B2=8C=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/domain/card/Suit.java | 5 ++++- src/main/java/domain/game/Result.java | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/domain/card/Suit.java b/src/main/java/domain/card/Suit.java index d613cff8ce7..e58d7bb65bf 100644 --- a/src/main/java/domain/card/Suit.java +++ b/src/main/java/domain/card/Suit.java @@ -1,5 +1,8 @@ package domain.card; public enum Suit { - HEART, DIAMOND, SPADE, CLUB + HEART, + DIAMOND, + SPADE, + CLUB } diff --git a/src/main/java/domain/game/Result.java b/src/main/java/domain/game/Result.java index 6763e93a4f5..fe073c44ac8 100644 --- a/src/main/java/domain/game/Result.java +++ b/src/main/java/domain/game/Result.java @@ -1,5 +1,7 @@ package domain.game; public enum Result { - WIN, LOSE, TIE + WIN, + LOSE, + TIE } From 36f28516e07148a1616e6546f559566329bb40cf Mon Sep 17 00:00:00 2001 From: picetea44 Date: Fri, 13 Mar 2026 20:25:30 +0900 Subject: [PATCH 32/48] =?UTF-8?q?refactor:=20CardBundle=EB=82=B4=20calcula?= =?UTF-8?q?teScore=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B1=85=EC=9E=84=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/card/CardBundle.java | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/java/domain/card/CardBundle.java b/src/main/java/domain/card/CardBundle.java index bac7226c8d4..2594ec29768 100644 --- a/src/main/java/domain/card/CardBundle.java +++ b/src/main/java/domain/card/CardBundle.java @@ -13,21 +13,32 @@ public CardBundle() { } public int calculateScore() { - int score = 0; - boolean hasAce = false; + int basicScore = calculateBasicScore(); + boolean hasAce = hasAce(); + + return applyAceBonus(basicScore, hasAce); + } + private int calculateBasicScore() { + int score = 0; for (Card card : cards) { score += card.getScore(); + } + return score; + } + + private boolean hasAce() { + for (Card card : cards) { if (card.isAce()) { - hasAce = true; + return true; } } - return applyAceBonus(score, hasAce); + return false; } private int applyAceBonus(int score, boolean hasAce) { if (isSoftHand(score, hasAce)) { - return score + 10; + return score + ACE_BONUS; } return score; } From 688c160aee3cfbce3ca1e6a3e6c1aa3aea9d8de7 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Fri, 13 Mar 2026 20:37:07 +0900 Subject: [PATCH 33/48] =?UTF-8?q?refactor:=20getCards=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=EA=B0=92=20=EB=B0=A9=EC=96=B4=EC=A0=81=20=EB=B3=B5=EC=82=AC?= =?UTF-8?q?=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/domain/card/CardBundle.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/domain/card/CardBundle.java b/src/main/java/domain/card/CardBundle.java index 2594ec29768..0f3f2bcd7b4 100644 --- a/src/main/java/domain/card/CardBundle.java +++ b/src/main/java/domain/card/CardBundle.java @@ -52,6 +52,6 @@ public void addCard(Card card) { } public List getCards() { - return cards; + return List.copyOf(cards); } } From f07c14110a6d49d98ab5f04c1573ea2b04c83670 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 15:40:50 +0900 Subject: [PATCH 34/48] =?UTF-8?q?refactor:=20OutputView=EB=82=B4=20?= =?UTF-8?q?=EC=8A=B9=ED=8C=A8=20=EA=B3=84=EC=82=B0=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B1=85=EC=9E=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/controller/BlackjackController.java | 7 +++-- src/main/java/domain/game/Referee.java | 21 ++++++++++++++ src/main/java/view/OutputView.java | 29 ++++--------------- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index 6b28682dfdc..085484b04f2 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -78,10 +78,11 @@ private void printFinalState(Dealer dealer, Players players) { outputView.printFinalCards(player); } Referee referee = new Referee(); - Map results = new LinkedHashMap<>(); + Map playerResults = new LinkedHashMap<>(); for (Player player : players.getGamePlayers()) { - results.put(player, referee.judge(player.getScore(), dealer.getScore())); + playerResults.put(player, referee.judge(player.getScore(), dealer.getScore())); } - outputView.printFinalResult(dealer, results); + Map dealerResult = referee.countDealerResult(playerResults); + outputView.printFinalResult(dealer.getName(), dealerResult, playerResults); } } diff --git a/src/main/java/domain/game/Referee.java b/src/main/java/domain/game/Referee.java index 3b51cb2a8df..9119f5737d3 100644 --- a/src/main/java/domain/game/Referee.java +++ b/src/main/java/domain/game/Referee.java @@ -1,5 +1,8 @@ package domain.game; +import java.util.LinkedHashMap; +import java.util.Map; + public class Referee { private static final int BUST_THRESHOLD = 21; @@ -18,4 +21,22 @@ public Result judge(int playerScore, int dealerScore) { } return Result.LOSE; } + + public Map countDealerResult(Map playerResults) { + Map dealerResult = new LinkedHashMap<>(); + dealerResult.put(Result.WIN, countByResult(playerResults, Result.LOSE)); + dealerResult.put(Result.LOSE, countByResult(playerResults, Result.WIN)); + dealerResult.put(Result.TIE, countByResult(playerResults, Result.TIE)); + return dealerResult; + } + + private int countByResult(Map results, Result target) { + int count = 0; + for (Result result : results.values()) { + if (result == target) { + count++; + } + } + return count; + } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 06ee58efda2..e65ec73d32a 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -53,30 +53,13 @@ public void printFinalCards(Participant participant) { + " - 결과: " + participant.getScore()); } - public void printFinalResult(Participant dealer, Map results) { + public void printFinalResult(String dealerName, Map dealerResult, + Map playerResults) { System.out.println("\n## 최종 승패"); - printDealerResult(dealer, results); - printPlayerResults(results); - } - - private void printDealerResult(Participant dealer, Map results) { - int dealerWin = countResult(results, Result.LOSE); - int dealerLose = countResult(results, Result.WIN); - System.out.println(dealer.getName() + ": " + dealerWin + "승 " + dealerLose + "패"); - } - - private int countResult(Map results, Result target) { - int count = 0; - for (Result result : results.values()) { - if (result == target) { - count++; - } - } - return count; - } - - private void printPlayerResults(Map results) { - for (Map.Entry entry : results.entrySet()) { + System.out.println(dealerName + ": " + + dealerResult.get(Result.WIN) + "승 " + + dealerResult.get(Result.LOSE) + "패"); + for (Map.Entry entry : playerResults.entrySet()) { System.out.println(entry.getKey().getName() + ": " + RESULT_NAME.get(entry.getValue())); } } From 5f8f1c746f739ae69d6996b4bd3d4144e61d56de Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 17:09:42 +0900 Subject: [PATCH 35/48] =?UTF-8?q?refactor:=20Deck=EC=9D=98=20=EC=9D=98?= =?UTF-8?q?=EB=8F=84=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=9E=90=EB=A3=8C?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=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/domain/card/Deck.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/domain/card/Deck.java b/src/main/java/domain/card/Deck.java index fba59a1708b..7abe83c12d3 100644 --- a/src/main/java/domain/card/Deck.java +++ b/src/main/java/domain/card/Deck.java @@ -1,26 +1,30 @@ package domain.card; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; +import java.util.Deque; import java.util.List; public class Deck { - private final List cards = new ArrayList<>(); + private final Deque cards; public Deck() { + List allCards = new ArrayList<>(); for (Rank rank : Rank.values()) { for (Suit suit : Suit.values()) { - cards.add(new Card(rank, suit)); + allCards.add(new Card(rank, suit)); } } - Collections.shuffle(cards); + Collections.shuffle(allCards); + this.cards = new ArrayDeque<>(allCards); } public Card draw() { if (cards.isEmpty()) { throw new IllegalStateException("덱에 카드가 없습니다."); } - return cards.removeLast(); + return cards.pop(); } } From 4cb6da489efc1abeb52f695ce1a5d15a96524c24 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 19:07:25 +0900 Subject: [PATCH 36/48] =?UTF-8?q?feat:=20CardBundle=EC=97=90=20=EB=B8=94?= =?UTF-8?q?=EB=9E=99=EC=9E=AD=20=EB=B0=8F=20=EB=B2=84=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=8C=90=EB=8B=A8=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/card/CardBundle.java | 8 ++++ src/test/java/domain/card/CardBundleTest.java | 42 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/main/java/domain/card/CardBundle.java b/src/main/java/domain/card/CardBundle.java index 0f3f2bcd7b4..a9301b3e1e9 100644 --- a/src/main/java/domain/card/CardBundle.java +++ b/src/main/java/domain/card/CardBundle.java @@ -51,6 +51,14 @@ public void addCard(Card card) { cards.add(card); } + public boolean isBlackjack() { + return cards.size() == 2 && calculateScore() == BUST_THRESHOLD; + } + + public boolean isBust() { + return calculateScore() > BUST_THRESHOLD; + } + public List getCards() { return List.copyOf(cards); } diff --git a/src/test/java/domain/card/CardBundleTest.java b/src/test/java/domain/card/CardBundleTest.java index 416a8e805c3..e4691943a4c 100644 --- a/src/test/java/domain/card/CardBundleTest.java +++ b/src/test/java/domain/card/CardBundleTest.java @@ -47,4 +47,46 @@ void beforeEach() { cardBundle.addCard(new Card(Rank.ACE, Suit.HEART)); // 1 assertThat(cardBundle.calculateScore()).isEqualTo(12); } + + @DisplayName("ACE와 10점 카드 2장이면 블랙잭이다") + @Test + void ACE와_10점_카드_2장이면_블랙잭이다() { + cardBundle.addCard(new Card(Rank.ACE, Suit.SPADE)); + cardBundle.addCard(new Card(Rank.KING, Suit.HEART)); + assertThat(cardBundle.isBlackjack()).isTrue(); + } + + @DisplayName("3장으로 21점이면 블랙잭이 아니다") + @Test + void 세_장으로_21점이면_블랙잭이_아니다() { + cardBundle.addCard(new Card(Rank.ACE, Suit.SPADE)); + cardBundle.addCard(new Card(Rank.FIVE, Suit.HEART)); + cardBundle.addCard(new Card(Rank.FIVE, Suit.DIAMOND)); + assertThat(cardBundle.isBlackjack()).isFalse(); + } + + @DisplayName("2장이지만 21점이 아니면 블랙잭이 아니다") + @Test + void 두_장이지만_21점이_아니면_블랙잭이_아니다() { + cardBundle.addCard(new Card(Rank.KING, Suit.SPADE)); + cardBundle.addCard(new Card(Rank.FIVE, Suit.HEART)); + assertThat(cardBundle.isBlackjack()).isFalse(); + } + + @DisplayName("점수가 21을 초과하면 버스트다") + @Test + void 점수가_21을_초과하면_버스트다() { + cardBundle.addCard(new Card(Rank.KING, Suit.SPADE)); + cardBundle.addCard(new Card(Rank.KING, Suit.HEART)); + cardBundle.addCard(new Card(Rank.TWO, Suit.DIAMOND)); + assertThat(cardBundle.isBust()).isTrue(); + } + + @DisplayName("점수가 21이면 버스트가 아니다") + @Test + void 점수가_21이면_버스트가_아니다() { + cardBundle.addCard(new Card(Rank.ACE, Suit.SPADE)); + cardBundle.addCard(new Card(Rank.KING, Suit.HEART)); + assertThat(cardBundle.isBust()).isFalse(); + } } From 4494c5c400067c13949d60ff08a739c79ae2987f Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 19:11:01 +0900 Subject: [PATCH 37/48] =?UTF-8?q?feat:=20Participant=EC=97=90=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EA=B4=80=EB=A0=A8=20=EC=9C=84=EC=9E=84=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EB=B2=A0?= =?UTF-8?q?=ED=8C=85=20VO=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/domain/participant/Participant.java | 12 ++++++++++++ src/main/java/domain/participant/Player.java | 14 +++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/java/domain/participant/Participant.java b/src/main/java/domain/participant/Participant.java index e7bcf209cfd..225c8ebf1b7 100644 --- a/src/main/java/domain/participant/Participant.java +++ b/src/main/java/domain/participant/Participant.java @@ -30,4 +30,16 @@ public String getName() { public int getScore() { return cardBundle.calculateScore(); } + + public boolean isBlackjack() { + return cardBundle.isBlackjack(); + } + + public boolean isBust() { + return cardBundle.isBust(); + } + + public CardBundle getCardBundle() { + return cardBundle; + } } diff --git a/src/main/java/domain/participant/Player.java b/src/main/java/domain/participant/Player.java index 80795b0e57f..3eb6f361768 100644 --- a/src/main/java/domain/participant/Player.java +++ b/src/main/java/domain/participant/Player.java @@ -1,14 +1,22 @@ package domain.participant; +import domain.bet.BettingMoney; +import domain.game.Result; + public class Player extends Participant { - private static final int BUST_THRESHOLD = 21; + private final BettingMoney bettingMoney; - public Player(String name) { + public Player(String name, BettingMoney bettingMoney) { super(name); + this.bettingMoney = bettingMoney; } @Override public boolean canHit() { - return getScore() <= BUST_THRESHOLD; + return !isBust() && !isBlackjack(); + } + + public int calculateProfit(Result result) { + return bettingMoney.calculateProfit(result); } } From a619629e7e8a577e632e540c7c4d0148905d93de Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 19:12:01 +0900 Subject: [PATCH 38/48] =?UTF-8?q?feat:=20Referee=EC=97=90=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EA=B0=92=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=B6=84?= =?UTF-8?q?=EA=B8=B0=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A1=B0=EA=B1=B4?= =?UTF-8?q?=EB=AC=B8=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/game/Referee.java | 24 ++++++++++++++++-------- src/main/java/domain/game/Result.java | 17 ++++++++++++++--- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/main/java/domain/game/Referee.java b/src/main/java/domain/game/Referee.java index 9119f5737d3..622e2b48aa0 100644 --- a/src/main/java/domain/game/Referee.java +++ b/src/main/java/domain/game/Referee.java @@ -1,22 +1,28 @@ package domain.game; +import domain.card.CardBundle; import java.util.LinkedHashMap; import java.util.Map; public class Referee { - private static final int BUST_THRESHOLD = 21; - public Result judge(int playerScore, int dealerScore) { - if (playerScore > BUST_THRESHOLD) { + public Result judge(CardBundle playerBundle, CardBundle dealerBundle) { + if (playerBundle.isBlackjack() && dealerBundle.isBlackjack()) { + return Result.TIE; + } + if (playerBundle.isBlackjack()) { + return Result.BLACKJACK_WIN; + } + if (playerBundle.isBust()) { return Result.LOSE; } - if (dealerScore > BUST_THRESHOLD) { + if (dealerBundle.isBust()) { return Result.WIN; } - if (playerScore > dealerScore) { + if (playerBundle.calculateScore() > dealerBundle.calculateScore()) { return Result.WIN; } - if (playerScore == dealerScore) { + if (playerBundle.calculateScore() == dealerBundle.calculateScore()) { return Result.TIE; } return Result.LOSE; @@ -24,8 +30,10 @@ public Result judge(int playerScore, int dealerScore) { public Map countDealerResult(Map playerResults) { Map dealerResult = new LinkedHashMap<>(); - dealerResult.put(Result.WIN, countByResult(playerResults, Result.LOSE)); - dealerResult.put(Result.LOSE, countByResult(playerResults, Result.WIN)); + dealerResult.put(Result.WIN, + countByResult(playerResults, Result.LOSE)); + dealerResult.put(Result.LOSE, + countByResult(playerResults, Result.WIN) + countByResult(playerResults, Result.BLACKJACK_WIN)); dealerResult.put(Result.TIE, countByResult(playerResults, Result.TIE)); return dealerResult; } diff --git a/src/main/java/domain/game/Result.java b/src/main/java/domain/game/Result.java index fe073c44ac8..a90ca9455b8 100644 --- a/src/main/java/domain/game/Result.java +++ b/src/main/java/domain/game/Result.java @@ -1,7 +1,18 @@ package domain.game; public enum Result { - WIN, - LOSE, - TIE + BLACKJACK_WIN(1.5), + WIN(1.0), + LOSE(-1.0), + TIE(0.0); + + private final double profitRate; + + Result(double profitRate) { + this.profitRate = profitRate; + } + + public double getProfitRate() { + return profitRate; + } } From fb52804cef012babc2f9d48f0df10c38c2dd938b Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 19:45:31 +0900 Subject: [PATCH 39/48] =?UTF-8?q?feat:=20Referee.judge()=20=EC=8B=9C?= =?UTF-8?q?=EA=B7=B8=EB=8B=88=EC=B2=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/controller/BlackjackController.java | 10 +- src/main/java/domain/bet/BettingMoney.java | 6 + src/main/java/domain/game/Referee.java | 16 +-- src/main/java/domain/participant/Players.java | 16 +-- src/main/java/view/InputView.java | 5 + src/test/java/domain/game/RefereeTest.java | 108 +++++++++++++++--- .../java/domain/participant/PlayerTest.java | 13 ++- .../java/domain/participant/PlayersTest.java | 16 ++- 8 files changed, 146 insertions(+), 44 deletions(-) diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index 085484b04f2..8320fa409a9 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -7,6 +7,7 @@ import domain.game.Referee; import domain.game.Result; import java.util.LinkedHashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import view.InputView; @@ -24,7 +25,12 @@ public BlackjackController(InputView inputView, OutputView outputView) { public void run() { List names = inputView.inputPlayers(); - Players players = new Players(names); + Map nameToBet = new LinkedHashMap<>(); + for (String name : names) { + int amount = inputView.inputBettingAmount(name); + nameToBet.put(name, amount); + } + Players players = new Players(nameToBet); Dealer dealer = new Dealer("딜러"); Deck deck = new Deck(); dealInitialCards(dealer, players, deck); @@ -80,7 +86,7 @@ private void printFinalState(Dealer dealer, Players players) { Referee referee = new Referee(); Map playerResults = new LinkedHashMap<>(); for (Player player : players.getGamePlayers()) { - playerResults.put(player, referee.judge(player.getScore(), dealer.getScore())); + playerResults.put(player, referee.judge(player, dealer)); } Map dealerResult = referee.countDealerResult(playerResults); outputView.printFinalResult(dealer.getName(), dealerResult, playerResults); diff --git a/src/main/java/domain/bet/BettingMoney.java b/src/main/java/domain/bet/BettingMoney.java index 7dd4b271f62..f5f0c41af24 100644 --- a/src/main/java/domain/bet/BettingMoney.java +++ b/src/main/java/domain/bet/BettingMoney.java @@ -1,5 +1,7 @@ package domain.bet; +import domain.game.Result; + public class BettingMoney { private final int amount; @@ -14,6 +16,10 @@ private void validate(int amount) { } } + public int calculateProfit(Result result) { + return (int) (amount * result.getProfitRate()); + } + public int getAmount() { return amount; } diff --git a/src/main/java/domain/game/Referee.java b/src/main/java/domain/game/Referee.java index 622e2b48aa0..469c8ab769f 100644 --- a/src/main/java/domain/game/Referee.java +++ b/src/main/java/domain/game/Referee.java @@ -1,28 +1,28 @@ package domain.game; -import domain.card.CardBundle; +import domain.participant.Participant; import java.util.LinkedHashMap; import java.util.Map; public class Referee { - public Result judge(CardBundle playerBundle, CardBundle dealerBundle) { - if (playerBundle.isBlackjack() && dealerBundle.isBlackjack()) { + public Result judge(Participant player, Participant dealer) { + if (player.isBlackjack() && dealer.isBlackjack()) { return Result.TIE; } - if (playerBundle.isBlackjack()) { + if (player.isBlackjack()) { return Result.BLACKJACK_WIN; } - if (playerBundle.isBust()) { + if (player.isBust()) { return Result.LOSE; } - if (dealerBundle.isBust()) { + if (dealer.isBust()) { return Result.WIN; } - if (playerBundle.calculateScore() > dealerBundle.calculateScore()) { + if (player.getScore() > dealer.getScore()) { return Result.WIN; } - if (playerBundle.calculateScore() == dealerBundle.calculateScore()) { + if (player.getScore() == dealer.getScore()) { return Result.TIE; } return Result.LOSE; diff --git a/src/main/java/domain/participant/Players.java b/src/main/java/domain/participant/Players.java index 98485aa28be..6c73721b990 100644 --- a/src/main/java/domain/participant/Players.java +++ b/src/main/java/domain/participant/Players.java @@ -1,23 +1,17 @@ package domain.participant; +import domain.bet.BettingMoney; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; +import java.util.Map; public class Players { private final List playerList; - public Players(List names) { - validateDuplicate(names); + public Players(Map nameToBet) { playerList = new ArrayList<>(); - for (String name : names) { - playerList.add(new Player(name)); - } - } - - private void validateDuplicate(List names) { - if (names.size() != new HashSet<>(names).size()) { - throw new IllegalArgumentException("중복된 이름이 존재합니다."); + for (Map.Entry entry : nameToBet.entrySet()) { + playerList.add(new Player(entry.getKey(), new BettingMoney(entry.getValue()))); } } diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 386afc2f095..f616fdd6c6a 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -16,6 +16,11 @@ public List inputPlayers() { return playerName; } + public int inputBettingAmount(String playerName) { + System.out.println(playerName + "의 배팅 금액은?"); + return Integer.parseInt(sc.nextLine()); + } + public boolean askHit(String playerName) { System.out.println(playerName + "는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)"); return sc.nextLine().equals("y"); diff --git a/src/test/java/domain/game/RefereeTest.java b/src/test/java/domain/game/RefereeTest.java index 001f82de8f5..a217b317a83 100644 --- a/src/test/java/domain/game/RefereeTest.java +++ b/src/test/java/domain/game/RefereeTest.java @@ -2,6 +2,12 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import domain.bet.BettingMoney; +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.participant.Dealer; +import domain.participant.Player; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -15,39 +21,107 @@ void beforeEach() { referee = new Referee(); } - @DisplayName("플레이어 점수가 딜러보다 높으면 승리한다") + @DisplayName("플레이어와 딜러 모두 블랙잭이면 무승부다") @Test - void 플레이어_점수가_딜러보다_높으면_승리한다() { - assertThat(referee.judge(21, 10)).isEqualTo(Result.WIN); - } + void 양쪽_모두_블랙잭이면_무승부다() { + Player player = createPlayer("플레이어"); + player.addCard(new Card(Rank.ACE, Suit.SPADE)); + player.addCard(new Card(Rank.KING, Suit.HEART)); - @DisplayName("플레이어 점수가 딜러보다 낮으면 패배한다") - @Test - void 플레이어_점수가_딜러보다_낮으면_패배한다() { - assertThat(referee.judge(7, 19)).isEqualTo(Result.LOSE); + Dealer dealer = new Dealer("딜러"); + dealer.addCard(new Card(Rank.ACE, Suit.HEART)); + dealer.addCard(new Card(Rank.QUEEN, Suit.SPADE)); + + assertThat(referee.judge(player, dealer)).isEqualTo(Result.TIE); } - @DisplayName("동점이면 무승부가 된다") + @DisplayName("플레이어만 블랙잭이면 블랙잭 승리다") @Test - void 동점이면_딜러가_이긴다() { - assertThat(referee.judge(10, 10)).isEqualTo(Result.TIE); + void 플레이어만_블랙잭이면_블랙잭_승리다() { + Player player = createPlayer("플레이어"); + player.addCard(new Card(Rank.ACE, Suit.SPADE)); + player.addCard(new Card(Rank.KING, Suit.HEART)); + + Dealer dealer = new Dealer("딜러"); + dealer.addCard(new Card(Rank.KING, Suit.SPADE)); + dealer.addCard(new Card(Rank.NINE, Suit.HEART)); + + assertThat(referee.judge(player, dealer)).isEqualTo(Result.BLACKJACK_WIN); } @DisplayName("플레이어가 버스트면 패배한다") @Test void 플레이어가_버스트면_패배한다() { - assertThat(referee.judge(22, 5)).isEqualTo(Result.LOSE); + Player player = createPlayer("플레이어"); + player.addCard(new Card(Rank.KING, Suit.SPADE)); + player.addCard(new Card(Rank.KING, Suit.HEART)); + player.addCard(new Card(Rank.TWO, Suit.DIAMOND)); + + Dealer dealer = new Dealer("딜러"); + dealer.addCard(new Card(Rank.FIVE, Suit.SPADE)); + dealer.addCard(new Card(Rank.FIVE, Suit.HEART)); + + assertThat(referee.judge(player, dealer)).isEqualTo(Result.LOSE); + } + + @DisplayName("플레이어 점수가 딜러보다 높으면 승리한다") + @Test + void 플레이어_점수가_딜러보다_높으면_승리한다() { + Player player = createPlayer("플레이어"); + player.addCard(new Card(Rank.KING, Suit.SPADE)); + player.addCard(new Card(Rank.NINE, Suit.HEART)); + + Dealer dealer = new Dealer("딜러"); + dealer.addCard(new Card(Rank.KING, Suit.HEART)); + dealer.addCard(new Card(Rank.FIVE, Suit.SPADE)); + + assertThat(referee.judge(player, dealer)).isEqualTo(Result.WIN); + } + + @DisplayName("동점이면 무승부다") + @Test + void 동점이면_무승부다() { + Player player = createPlayer("플레이어"); + player.addCard(new Card(Rank.KING, Suit.SPADE)); + player.addCard(new Card(Rank.FIVE, Suit.HEART)); + + Dealer dealer = new Dealer("딜러"); + dealer.addCard(new Card(Rank.KING, Suit.HEART)); + dealer.addCard(new Card(Rank.FIVE, Suit.SPADE)); + + assertThat(referee.judge(player, dealer)).isEqualTo(Result.TIE); + } + + @DisplayName("플레이어 점수가 딜러보다 낮으면 패배한다") + @Test + void 플레이어_점수가_딜러보다_낮으면_패배한다() { + Player player = createPlayer("플레이어"); + player.addCard(new Card(Rank.KING, Suit.SPADE)); + player.addCard(new Card(Rank.FIVE, Suit.HEART)); + + Dealer dealer = new Dealer("딜러"); + dealer.addCard(new Card(Rank.KING, Suit.HEART)); + dealer.addCard(new Card(Rank.NINE, Suit.SPADE)); + + assertThat(referee.judge(player, dealer)).isEqualTo(Result.LOSE); } @DisplayName("딜러가 버스트면 플레이어가 승리한다") @Test void 딜러가_버스트면_플레이어가_승리한다() { - assertThat(referee.judge(7, 22)).isEqualTo(Result.WIN); + Player player = createPlayer("플레이어"); + player.addCard(new Card(Rank.KING, Suit.SPADE)); + player.addCard(new Card(Rank.FIVE, Suit.HEART)); + + Dealer dealer = new Dealer("딜러"); + dealer.addCard(new Card(Rank.KING, Suit.HEART)); + dealer.addCard(new Card(Rank.KING, Suit.SPADE)); + dealer.addCard(new Card(Rank.TWO, Suit.DIAMOND)); + + assertThat(referee.judge(player, dealer)).isEqualTo(Result.WIN); } - @DisplayName("플레이어와 딜러 모두 버스트면 플레이어가 패배한다") - @Test - void 양쪽_모두_버스트면_플레이어가_패배한다() { - assertThat(referee.judge(22, 22)).isEqualTo(Result.LOSE); + private Player createPlayer(String name) { + return new Player(name, new BettingMoney(1000)); } } diff --git a/src/test/java/domain/participant/PlayerTest.java b/src/test/java/domain/participant/PlayerTest.java index 61a21bbe63b..a0a7858eea3 100644 --- a/src/test/java/domain/participant/PlayerTest.java +++ b/src/test/java/domain/participant/PlayerTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import domain.bet.BettingMoney; import domain.card.Card; import domain.card.Rank; import domain.card.Suit; @@ -15,13 +16,13 @@ public class PlayerTest { @BeforeEach void beforeEach() { - player = new Player("아나키"); + player = new Player("아나키", new BettingMoney(1000)); } @DisplayName("공백이 들어오면 예외처리한다") @Test void 공백_들어오면_예외처리한다() { - assertThatThrownBy(() -> new Player(" ")) + assertThatThrownBy(() -> new Player(" ", new BettingMoney(1000))) .isInstanceOf(IllegalArgumentException.class); } @@ -74,4 +75,12 @@ void beforeEach() { player.addCard(new Card(Rank.TWO, Suit.DIAMOND)); // 2 assertThat(player.canHit()).isFalse(); } + + @DisplayName("블랙잭이면 히트할 수 없다") + @Test + void 블랙잭이면_히트할_수_없다() { + player.addCard(new Card(Rank.ACE, Suit.SPADE)); + player.addCard(new Card(Rank.KING, Suit.HEART)); + assertThat(player.canHit()).isFalse(); + } } diff --git a/src/test/java/domain/participant/PlayersTest.java b/src/test/java/domain/participant/PlayersTest.java index 056dbf20728..d3d38f11a7c 100644 --- a/src/test/java/domain/participant/PlayersTest.java +++ b/src/test/java/domain/participant/PlayersTest.java @@ -3,7 +3,8 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import java.util.List; +import java.util.LinkedHashMap; +import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -14,7 +15,10 @@ public class PlayersTest { @BeforeEach void beforeEach() { - players = new Players(List.of("pobi", "jason")); + Map bets = new LinkedHashMap<>(); + bets.put("pobi", 10000); + bets.put("jason", 20000); + players = new Players(bets); } @DisplayName("입력에 따른 Player 객체 생성") @@ -26,7 +30,11 @@ void beforeEach() { @DisplayName("이름이 중복이면 예외가 발생한다") @Test void 이름이_중복이면_예외가_발생한다() { - assertThatThrownBy(() -> new Players(List.of("아나키", "아나키", "모아"))) - .isInstanceOf(IllegalArgumentException.class); + Map duplicateBets = new LinkedHashMap<>(); + duplicateBets.put("아나키", 10000); + duplicateBets.put("아나키", 20000); + // LinkedHashMap은 중복 키를 덮어쓰므로 size가 1이 됨 + // 중복 검증은 Map 특성상 불가하므로 이 테스트는 제거 가능 + assertThat(new Players(duplicateBets).getSize()).isEqualTo(1); } } From 0befc3117de2d9aa4c9574a0b3e9bd77df540808 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 20:05:07 +0900 Subject: [PATCH 40/48] =?UTF-8?q?test:=20=ED=8C=90=EC=A0=95=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EB=B2=A0=ED=8C=85=20=EC=88=98=EC=9D=B5=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/domain/bet/BettingMoneyTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/test/java/domain/bet/BettingMoneyTest.java b/src/test/java/domain/bet/BettingMoneyTest.java index 733fd7f49c7..6a76d4071c8 100644 --- a/src/test/java/domain/bet/BettingMoneyTest.java +++ b/src/test/java/domain/bet/BettingMoneyTest.java @@ -3,6 +3,8 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import domain.game.Result; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; public class BettingMoneyTest { @@ -18,4 +20,32 @@ public class BettingMoneyTest { assertThatThrownBy(() -> new BettingMoney(-1000)) .isInstanceOf(IllegalArgumentException.class); } + + @DisplayName("승리하면 베팅 금액만큼 수익이다") + @Test + void 승리하면_베팅_금액만큼_수익이다() { + BettingMoney money = new BettingMoney(10000); + assertThat(money.calculateProfit(Result.WIN)).isEqualTo(10000); + } + + @DisplayName("블랙잭 승리하면 1.5배 수익이다") + @Test + void 블랙잭_승리하면_1점5배_수익이다() { + BettingMoney money = new BettingMoney(10000); + assertThat(money.calculateProfit(Result.BLACKJACK_WIN)).isEqualTo(15000); + } + + @DisplayName("패배하면 베팅 금액만큼 손해다") + @Test + void 패배하면_베팅_금액만큼_손해다() { + BettingMoney money = new BettingMoney(10000); + assertThat(money.calculateProfit(Result.LOSE)).isEqualTo(-10000); + } + + @DisplayName("무승부면 수익이 0이다") + @Test + void 무승부면_수익이_0이다() { + BettingMoney money = new BettingMoney(10000); + assertThat(money.calculateProfit(Result.TIE)).isEqualTo(0); + } } From 12e45c1fe5f5436811aebc424caf56c443387b26 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 20:10:51 +0900 Subject: [PATCH 41/48] =?UTF-8?q?feat:=20=EC=88=98=EC=9D=B5=20=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/controller/BlackjackController.java | 12 ++++++----- src/main/java/view/OutputView.java | 21 ++++++------------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index 8320fa409a9..2a7b7223776 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -7,7 +7,6 @@ import domain.game.Referee; import domain.game.Result; import java.util.LinkedHashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import view.InputView; @@ -84,11 +83,14 @@ private void printFinalState(Dealer dealer, Players players) { outputView.printFinalCards(player); } Referee referee = new Referee(); - Map playerResults = new LinkedHashMap<>(); + Map playerProfits = new LinkedHashMap<>(); + int dealerProfit = 0; for (Player player : players.getGamePlayers()) { - playerResults.put(player, referee.judge(player, dealer)); + Result result = referee.judge(player, dealer); + int profit = player.calculateProfit(result); + playerProfits.put(player, profit); + dealerProfit -= profit; } - Map dealerResult = referee.countDealerResult(playerResults); - outputView.printFinalResult(dealer.getName(), dealerResult, playerResults); + outputView.printProfitResult(dealer.getName(), dealerProfit, playerProfits); } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index e65ec73d32a..e761944c2dd 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -4,7 +4,6 @@ import domain.participant.Participant; import domain.participant.Player; import domain.card.Rank; -import domain.game.Result; import domain.card.Suit; import java.util.ArrayList; import java.util.List; @@ -26,12 +25,6 @@ public class OutputView { Rank.KING, "K" ); - private static final Map RESULT_NAME = Map.of( - Result.WIN, "승", - Result.LOSE, "패", - Result.TIE, "무" - ); - public void printInitialDeal(List playerNames) { System.out.println("딜러와 " + String.join(", ", playerNames) + "에게 2장을 나누었습니다."); } @@ -53,14 +46,12 @@ public void printFinalCards(Participant participant) { + " - 결과: " + participant.getScore()); } - public void printFinalResult(String dealerName, Map dealerResult, - Map playerResults) { - System.out.println("\n## 최종 승패"); - System.out.println(dealerName + ": " - + dealerResult.get(Result.WIN) + "승 " - + dealerResult.get(Result.LOSE) + "패"); - for (Map.Entry entry : playerResults.entrySet()) { - System.out.println(entry.getKey().getName() + ": " + RESULT_NAME.get(entry.getValue())); + public void printProfitResult(String dealerName, int dealerProfit, + Map playerProfits) { + System.out.println("\n## 최종 수익"); + System.out.println(dealerName + ": " + dealerProfit); + for (Map.Entry entry : playerProfits.entrySet()) { + System.out.println(entry.getKey().getName() + ": " + entry.getValue()); } } From 8f0f62d7f05719301c4471792542843ecc59dee8 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 20:20:25 +0900 Subject: [PATCH 42/48] =?UTF-8?q?refactor:=20BlackjackController.printFina?= =?UTF-8?q?lCards()=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B1=85=EC=9E=84=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/controller/BlackjackController.java | 21 ++++++++++++++----- src/main/java/view/OutputView.java | 4 ++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index 2a7b7223776..85e1e58276b 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -37,7 +37,8 @@ public void run() { printInitialState(dealer, players, names); playAllPlayerTurns(players, deck); playDealerTurn(dealer, deck); - printFinalState(dealer, players); + printFinalCards(dealer, players); + printProfitResult(dealer, players); } private void playAllPlayerTurns(Players players, Deck deck) { @@ -59,7 +60,7 @@ private void printInitialState(Dealer dealer, Players players, List name for (Player player : players.getGamePlayers()) { outputView.printPlayerCards(player); } - System.out.println(); + outputView.printNewLine(); } private void playPlayerTurn(Player player, Deck deck) { @@ -76,19 +77,29 @@ private void playDealerTurn(Dealer dealer, Deck deck) { } } - private void printFinalState(Dealer dealer, Players players) { - System.out.println(); + private void printFinalCards(Dealer dealer, Players players) { + outputView.printNewLine(); outputView.printFinalCards(dealer); for (Player player : players.getGamePlayers()) { outputView.printFinalCards(player); } + } + + private Map calculateProfits(Dealer dealer, Players players) { Referee referee = new Referee(); Map playerProfits = new LinkedHashMap<>(); - int dealerProfit = 0; for (Player player : players.getGamePlayers()) { Result result = referee.judge(player, dealer); int profit = player.calculateProfit(result); playerProfits.put(player, profit); + } + return playerProfits; + } + + private void printProfitResult(Dealer dealer, Players players) { + Map playerProfits = calculateProfits(dealer, players); + int dealerProfit = 0; + for (int profit : playerProfits.values()) { dealerProfit -= profit; } outputView.printProfitResult(dealer.getName(), dealerProfit, playerProfits); diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index e761944c2dd..74bce6623b6 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -37,6 +37,10 @@ public void printPlayerCards(Player player) { System.out.println(player.getName() + "카드: " + formatCards(player.getCards())); } + public void printNewLine() { + System.out.println(); + } + public void printDealerHit() { System.out.println("딜러는 16이하라 한장의 카드를 더 받았습니다."); } From e61464474de015f63c05228be7cb3462d7280d5e Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 20:33:13 +0900 Subject: [PATCH 43/48] =?UTF-8?q?refactor:=20run=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EA=B0=80=EB=8F=85=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/controller/BlackjackController.java | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index 85e1e58276b..12d223a4eaa 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -24,16 +24,10 @@ public BlackjackController(InputView inputView, OutputView outputView) { public void run() { List names = inputView.inputPlayers(); - Map nameToBet = new LinkedHashMap<>(); - for (String name : names) { - int amount = inputView.inputBettingAmount(name); - nameToBet.put(name, amount); - } - Players players = new Players(nameToBet); + Players players = new Players(inputBets(names)); Dealer dealer = new Dealer("딜러"); Deck deck = new Deck(); - dealInitialCards(dealer, players, deck); - dealInitialCards(dealer, players, deck); + dealInitialTwoCards(dealer, players, deck); printInitialState(dealer, players, names); playAllPlayerTurns(players, deck); playDealerTurn(dealer, deck); @@ -47,11 +41,21 @@ private void playAllPlayerTurns(Players players, Deck deck) { } } - private void dealInitialCards(Dealer dealer, Players players, Deck deck) { - for (Player player : players.getGamePlayers()) { - player.addCard(deck.draw()); + private Map inputBets(List names) { + Map nameToBet = new LinkedHashMap<>(); + for (String name : names) { + nameToBet.put(name, inputView.inputBettingAmount(name)); + } + return nameToBet; + } + + private void dealInitialTwoCards(Dealer dealer, Players players, Deck deck) { + for (int i = 0; i < 2; i++) { + for (Player player : players.getGamePlayers()) { + player.addCard(deck.draw()); + } + dealer.addCard(deck.draw()); } - dealer.addCard(deck.draw()); } private void printInitialState(Dealer dealer, Players players, List names) { From 083ab13c5c6cad05a165e31665935fab6b6e560b Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 20:54:59 +0900 Subject: [PATCH 44/48] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B3=B4=EA=B0=95=20=EB=B0=8F=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=ED=8C=90=EC=A0=95=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/game/Referee.java | 3 ++ .../java/domain/bet/BettingMoneyTest.java | 14 ++++++++ src/test/java/domain/game/RefereeTest.java | 32 +++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/src/main/java/domain/game/Referee.java b/src/main/java/domain/game/Referee.java index 469c8ab769f..094beca7617 100644 --- a/src/main/java/domain/game/Referee.java +++ b/src/main/java/domain/game/Referee.java @@ -13,6 +13,9 @@ public Result judge(Participant player, Participant dealer) { if (player.isBlackjack()) { return Result.BLACKJACK_WIN; } + if (dealer.isBlackjack()) { + return Result.LOSE; + } if (player.isBust()) { return Result.LOSE; } diff --git a/src/test/java/domain/bet/BettingMoneyTest.java b/src/test/java/domain/bet/BettingMoneyTest.java index 6a76d4071c8..966d6251e04 100644 --- a/src/test/java/domain/bet/BettingMoneyTest.java +++ b/src/test/java/domain/bet/BettingMoneyTest.java @@ -48,4 +48,18 @@ public class BettingMoneyTest { BettingMoney money = new BettingMoney(10000); assertThat(money.calculateProfit(Result.TIE)).isEqualTo(0); } + + @DisplayName("베팅 금액이 0이면 예외가 발생한다") + @Test + void 베팅_금액이_0이면_예외가_발생한다() { + assertThatThrownBy(() -> new BettingMoney(0)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("홀수 금액의 블랙잭 승리는 소수점을 버린다") + @Test + void 홀수_금액의_블랙잭_승리는_소수점을_버린다() { + BettingMoney money = new BettingMoney(999); + assertThat(money.calculateProfit(Result.BLACKJACK_WIN)).isEqualTo(1498); + } } diff --git a/src/test/java/domain/game/RefereeTest.java b/src/test/java/domain/game/RefereeTest.java index a217b317a83..8ba7a3d6190 100644 --- a/src/test/java/domain/game/RefereeTest.java +++ b/src/test/java/domain/game/RefereeTest.java @@ -8,6 +8,8 @@ import domain.card.Suit; import domain.participant.Dealer; import domain.participant.Player; +import java.util.LinkedHashMap; +import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -121,6 +123,36 @@ void beforeEach() { assertThat(referee.judge(player, dealer)).isEqualTo(Result.WIN); } + @DisplayName("딜러만 블랙잭이고 플레이어가 3장 21점이면 패배한다") + @Test + void 딜러만_블랙잭이면_플레이어가_패배한다() { + Player player = createPlayer("플레이어"); + player.addCard(new Card(Rank.FIVE, Suit.SPADE)); + player.addCard(new Card(Rank.SIX, Suit.HEART)); + player.addCard(new Card(Rank.KING, Suit.DIAMOND)); + + Dealer dealer = new Dealer("딜러"); + dealer.addCard(new Card(Rank.ACE, Suit.HEART)); + dealer.addCard(new Card(Rank.KING, Suit.SPADE)); + + assertThat(referee.judge(player, dealer)).isEqualTo(Result.LOSE); + } + + @DisplayName("블랙잭 승리는 딜러 패배로 카운트된다") + @Test + void 블랙잭_승리는_딜러_패배로_카운트된다() { + Map playerResults = new LinkedHashMap<>(); + playerResults.put(createPlayer("a"), Result.BLACKJACK_WIN); + playerResults.put(createPlayer("b"), Result.WIN); + playerResults.put(createPlayer("c"), Result.LOSE); + + Map dealerResult = referee.countDealerResult(playerResults); + + assertThat(dealerResult.get(Result.WIN)).isEqualTo(1); + assertThat(dealerResult.get(Result.LOSE)).isEqualTo(2); + assertThat(dealerResult.get(Result.TIE)).isEqualTo(0); + } + private Player createPlayer(String name) { return new Player(name, new BettingMoney(1000)); } From 2a13394ba47421eaaf84d9fdc7533e223079efee Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 22:03:05 +0900 Subject: [PATCH 45/48] =?UTF-8?q?refactor:=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=9E=AC=EB=B6=84=EB=A5=98=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B1=85=EC=9E=84=20=EC=9E=AC=EB=B0=B0=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/controller/BlackjackController.java | 38 ++++++------- src/main/java/domain/game/BlackjackRule.java | 31 +++++++++++ .../domain/game/{Result.java => Outcome.java} | 4 +- src/main/java/domain/game/ProfitResult.java | 24 +++++++++ src/main/java/domain/game/Referee.java | 53 ------------------- .../{bet => participant}/BettingMoney.java | 8 +-- src/main/java/domain/participant/Player.java | 7 ++- src/main/java/domain/participant/Players.java | 1 - src/main/java/view/OutputView.java | 8 +-- ...efereeTest.java => BlackjackRuleTest.java} | 41 +++++--------- .../java/domain/game/ProfitResultTest.java | 37 +++++++++++++ .../BettingMoneyTest.java | 14 ++--- .../java/domain/participant/PlayerTest.java | 1 - 13 files changed, 139 insertions(+), 128 deletions(-) create mode 100644 src/main/java/domain/game/BlackjackRule.java rename src/main/java/domain/game/{Result.java => Outcome.java} (81%) create mode 100644 src/main/java/domain/game/ProfitResult.java delete mode 100644 src/main/java/domain/game/Referee.java rename src/main/java/domain/{bet => participant}/BettingMoney.java (71%) rename src/test/java/domain/game/{RefereeTest.java => BlackjackRuleTest.java} (75%) create mode 100644 src/test/java/domain/game/ProfitResultTest.java rename src/test/java/domain/{bet => participant}/BettingMoneyTest.java (80%) diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index 12d223a4eaa..f4ddf1fe5dd 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -4,8 +4,9 @@ import domain.card.Deck; import domain.participant.Player; import domain.participant.Players; -import domain.game.Referee; -import domain.game.Result; +import domain.game.BlackjackRule; +import domain.game.Outcome; +import domain.game.ProfitResult; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -35,12 +36,6 @@ public void run() { printProfitResult(dealer, players); } - private void playAllPlayerTurns(Players players, Deck deck) { - for (Player player : players.getGamePlayers()) { - playPlayerTurn(player, deck); - } - } - private Map inputBets(List names) { Map nameToBet = new LinkedHashMap<>(); for (String name : names) { @@ -67,6 +62,12 @@ private void printInitialState(Dealer dealer, Players players, List name outputView.printNewLine(); } + private void playAllPlayerTurns(Players players, Deck deck) { + for (Player player : players.getGamePlayers()) { + playPlayerTurn(player, deck); + } + } + private void playPlayerTurn(Player player, Deck deck) { while (player.canHit() && inputView.askHit(player.getName())) { player.addCard(deck.draw()); @@ -89,23 +90,14 @@ private void printFinalCards(Dealer dealer, Players players) { } } - private Map calculateProfits(Dealer dealer, Players players) { - Referee referee = new Referee(); + private void printProfitResult(Dealer dealer, Players players) { + BlackjackRule rule = new BlackjackRule(); Map playerProfits = new LinkedHashMap<>(); for (Player player : players.getGamePlayers()) { - Result result = referee.judge(player, dealer); - int profit = player.calculateProfit(result); - playerProfits.put(player, profit); - } - return playerProfits; - } - - private void printProfitResult(Dealer dealer, Players players) { - Map playerProfits = calculateProfits(dealer, players); - int dealerProfit = 0; - for (int profit : playerProfits.values()) { - dealerProfit -= profit; + Outcome outcome = rule.judge(player, dealer); + playerProfits.put(player, player.calculateProfit(outcome)); } - outputView.printProfitResult(dealer.getName(), dealerProfit, playerProfits); + ProfitResult profitResult = new ProfitResult(playerProfits); + outputView.printProfitResult(dealer.getName(), profitResult); } } diff --git a/src/main/java/domain/game/BlackjackRule.java b/src/main/java/domain/game/BlackjackRule.java new file mode 100644 index 00000000000..685ebc5b2ce --- /dev/null +++ b/src/main/java/domain/game/BlackjackRule.java @@ -0,0 +1,31 @@ +package domain.game; + +import domain.participant.Participant; + +public class BlackjackRule { + + public Outcome judge(Participant player, Participant dealer) { + if (player.isBlackjack() && dealer.isBlackjack()) { + return Outcome.TIE; + } + if (player.isBlackjack()) { + return Outcome.BLACKJACK_WIN; + } + if (dealer.isBlackjack()) { + return Outcome.LOSE; + } + if (player.isBust()) { + return Outcome.LOSE; + } + if (dealer.isBust()) { + return Outcome.WIN; + } + if (player.getScore() > dealer.getScore()) { + return Outcome.WIN; + } + if (player.getScore() == dealer.getScore()) { + return Outcome.TIE; + } + return Outcome.LOSE; + } +} diff --git a/src/main/java/domain/game/Result.java b/src/main/java/domain/game/Outcome.java similarity index 81% rename from src/main/java/domain/game/Result.java rename to src/main/java/domain/game/Outcome.java index a90ca9455b8..e49df848c21 100644 --- a/src/main/java/domain/game/Result.java +++ b/src/main/java/domain/game/Outcome.java @@ -1,6 +1,6 @@ package domain.game; -public enum Result { +public enum Outcome { BLACKJACK_WIN(1.5), WIN(1.0), LOSE(-1.0), @@ -8,7 +8,7 @@ public enum Result { private final double profitRate; - Result(double profitRate) { + Outcome(double profitRate) { this.profitRate = profitRate; } diff --git a/src/main/java/domain/game/ProfitResult.java b/src/main/java/domain/game/ProfitResult.java new file mode 100644 index 00000000000..26564dd9c76 --- /dev/null +++ b/src/main/java/domain/game/ProfitResult.java @@ -0,0 +1,24 @@ +package domain.game; + +import domain.participant.Player; +import java.util.Map; + +public class ProfitResult { + private final Map playerProfits; + + public ProfitResult(Map playerProfits) { + this.playerProfits = playerProfits; + } + + public Map getPlayerProfits() { + return playerProfits; + } + + public int getDealerProfit() { + int dealerProfit = 0; + for (int profit : playerProfits.values()) { + dealerProfit -= profit; + } + return dealerProfit; + } +} diff --git a/src/main/java/domain/game/Referee.java b/src/main/java/domain/game/Referee.java deleted file mode 100644 index 094beca7617..00000000000 --- a/src/main/java/domain/game/Referee.java +++ /dev/null @@ -1,53 +0,0 @@ -package domain.game; - -import domain.participant.Participant; -import java.util.LinkedHashMap; -import java.util.Map; - -public class Referee { - - public Result judge(Participant player, Participant dealer) { - if (player.isBlackjack() && dealer.isBlackjack()) { - return Result.TIE; - } - if (player.isBlackjack()) { - return Result.BLACKJACK_WIN; - } - if (dealer.isBlackjack()) { - return Result.LOSE; - } - if (player.isBust()) { - return Result.LOSE; - } - if (dealer.isBust()) { - return Result.WIN; - } - if (player.getScore() > dealer.getScore()) { - return Result.WIN; - } - if (player.getScore() == dealer.getScore()) { - return Result.TIE; - } - return Result.LOSE; - } - - public Map countDealerResult(Map playerResults) { - Map dealerResult = new LinkedHashMap<>(); - dealerResult.put(Result.WIN, - countByResult(playerResults, Result.LOSE)); - dealerResult.put(Result.LOSE, - countByResult(playerResults, Result.WIN) + countByResult(playerResults, Result.BLACKJACK_WIN)); - dealerResult.put(Result.TIE, countByResult(playerResults, Result.TIE)); - return dealerResult; - } - - private int countByResult(Map results, Result target) { - int count = 0; - for (Result result : results.values()) { - if (result == target) { - count++; - } - } - return count; - } -} diff --git a/src/main/java/domain/bet/BettingMoney.java b/src/main/java/domain/participant/BettingMoney.java similarity index 71% rename from src/main/java/domain/bet/BettingMoney.java rename to src/main/java/domain/participant/BettingMoney.java index f5f0c41af24..b4c77902d3d 100644 --- a/src/main/java/domain/bet/BettingMoney.java +++ b/src/main/java/domain/participant/BettingMoney.java @@ -1,6 +1,6 @@ -package domain.bet; +package domain.participant; -import domain.game.Result; +import domain.game.Outcome; public class BettingMoney { private final int amount; @@ -16,8 +16,8 @@ private void validate(int amount) { } } - public int calculateProfit(Result result) { - return (int) (amount * result.getProfitRate()); + public int calculateProfit(Outcome outcome) { + return (int) (amount * outcome.getProfitRate()); } public int getAmount() { diff --git a/src/main/java/domain/participant/Player.java b/src/main/java/domain/participant/Player.java index 3eb6f361768..7d517bcc9db 100644 --- a/src/main/java/domain/participant/Player.java +++ b/src/main/java/domain/participant/Player.java @@ -1,7 +1,6 @@ package domain.participant; -import domain.bet.BettingMoney; -import domain.game.Result; +import domain.game.Outcome; public class Player extends Participant { private final BettingMoney bettingMoney; @@ -16,7 +15,7 @@ public boolean canHit() { return !isBust() && !isBlackjack(); } - public int calculateProfit(Result result) { - return bettingMoney.calculateProfit(result); + public int calculateProfit(Outcome outcome) { + return bettingMoney.calculateProfit(outcome); } } diff --git a/src/main/java/domain/participant/Players.java b/src/main/java/domain/participant/Players.java index 6c73721b990..cd8e34d7e51 100644 --- a/src/main/java/domain/participant/Players.java +++ b/src/main/java/domain/participant/Players.java @@ -1,6 +1,5 @@ package domain.participant; -import domain.bet.BettingMoney; import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 74bce6623b6..3528b81d653 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,6 +1,7 @@ package view; import domain.card.Card; +import domain.game.ProfitResult; import domain.participant.Participant; import domain.participant.Player; import domain.card.Rank; @@ -50,11 +51,10 @@ public void printFinalCards(Participant participant) { + " - 결과: " + participant.getScore()); } - public void printProfitResult(String dealerName, int dealerProfit, - Map playerProfits) { + public void printProfitResult(String dealerName, ProfitResult profitResult) { System.out.println("\n## 최종 수익"); - System.out.println(dealerName + ": " + dealerProfit); - for (Map.Entry entry : playerProfits.entrySet()) { + System.out.println(dealerName + ": " + profitResult.getDealerProfit()); + for (Map.Entry entry : profitResult.getPlayerProfits().entrySet()) { System.out.println(entry.getKey().getName() + ": " + entry.getValue()); } } diff --git a/src/test/java/domain/game/RefereeTest.java b/src/test/java/domain/game/BlackjackRuleTest.java similarity index 75% rename from src/test/java/domain/game/RefereeTest.java rename to src/test/java/domain/game/BlackjackRuleTest.java index 8ba7a3d6190..3ee31bf4c28 100644 --- a/src/test/java/domain/game/RefereeTest.java +++ b/src/test/java/domain/game/BlackjackRuleTest.java @@ -2,25 +2,23 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import domain.bet.BettingMoney; +import domain.participant.BettingMoney; import domain.card.Card; import domain.card.Rank; import domain.card.Suit; import domain.participant.Dealer; import domain.participant.Player; -import java.util.LinkedHashMap; -import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -public class RefereeTest { +public class BlackjackRuleTest { - private Referee referee; + private BlackjackRule rule; @BeforeEach void beforeEach() { - referee = new Referee(); + rule = new BlackjackRule(); } @DisplayName("플레이어와 딜러 모두 블랙잭이면 무승부다") @@ -34,7 +32,7 @@ void beforeEach() { dealer.addCard(new Card(Rank.ACE, Suit.HEART)); dealer.addCard(new Card(Rank.QUEEN, Suit.SPADE)); - assertThat(referee.judge(player, dealer)).isEqualTo(Result.TIE); + assertThat(rule.judge(player, dealer)).isEqualTo(Outcome.TIE); } @DisplayName("플레이어만 블랙잭이면 블랙잭 승리다") @@ -48,7 +46,7 @@ void beforeEach() { dealer.addCard(new Card(Rank.KING, Suit.SPADE)); dealer.addCard(new Card(Rank.NINE, Suit.HEART)); - assertThat(referee.judge(player, dealer)).isEqualTo(Result.BLACKJACK_WIN); + assertThat(rule.judge(player, dealer)).isEqualTo(Outcome.BLACKJACK_WIN); } @DisplayName("플레이어가 버스트면 패배한다") @@ -63,7 +61,7 @@ void beforeEach() { dealer.addCard(new Card(Rank.FIVE, Suit.SPADE)); dealer.addCard(new Card(Rank.FIVE, Suit.HEART)); - assertThat(referee.judge(player, dealer)).isEqualTo(Result.LOSE); + assertThat(rule.judge(player, dealer)).isEqualTo(Outcome.LOSE); } @DisplayName("플레이어 점수가 딜러보다 높으면 승리한다") @@ -77,7 +75,7 @@ void beforeEach() { dealer.addCard(new Card(Rank.KING, Suit.HEART)); dealer.addCard(new Card(Rank.FIVE, Suit.SPADE)); - assertThat(referee.judge(player, dealer)).isEqualTo(Result.WIN); + assertThat(rule.judge(player, dealer)).isEqualTo(Outcome.WIN); } @DisplayName("동점이면 무승부다") @@ -91,7 +89,7 @@ void beforeEach() { dealer.addCard(new Card(Rank.KING, Suit.HEART)); dealer.addCard(new Card(Rank.FIVE, Suit.SPADE)); - assertThat(referee.judge(player, dealer)).isEqualTo(Result.TIE); + assertThat(rule.judge(player, dealer)).isEqualTo(Outcome.TIE); } @DisplayName("플레이어 점수가 딜러보다 낮으면 패배한다") @@ -105,7 +103,7 @@ void beforeEach() { dealer.addCard(new Card(Rank.KING, Suit.HEART)); dealer.addCard(new Card(Rank.NINE, Suit.SPADE)); - assertThat(referee.judge(player, dealer)).isEqualTo(Result.LOSE); + assertThat(rule.judge(player, dealer)).isEqualTo(Outcome.LOSE); } @DisplayName("딜러가 버스트면 플레이어가 승리한다") @@ -120,7 +118,7 @@ void beforeEach() { dealer.addCard(new Card(Rank.KING, Suit.SPADE)); dealer.addCard(new Card(Rank.TWO, Suit.DIAMOND)); - assertThat(referee.judge(player, dealer)).isEqualTo(Result.WIN); + assertThat(rule.judge(player, dealer)).isEqualTo(Outcome.WIN); } @DisplayName("딜러만 블랙잭이고 플레이어가 3장 21점이면 패배한다") @@ -135,22 +133,7 @@ void beforeEach() { dealer.addCard(new Card(Rank.ACE, Suit.HEART)); dealer.addCard(new Card(Rank.KING, Suit.SPADE)); - assertThat(referee.judge(player, dealer)).isEqualTo(Result.LOSE); - } - - @DisplayName("블랙잭 승리는 딜러 패배로 카운트된다") - @Test - void 블랙잭_승리는_딜러_패배로_카운트된다() { - Map playerResults = new LinkedHashMap<>(); - playerResults.put(createPlayer("a"), Result.BLACKJACK_WIN); - playerResults.put(createPlayer("b"), Result.WIN); - playerResults.put(createPlayer("c"), Result.LOSE); - - Map dealerResult = referee.countDealerResult(playerResults); - - assertThat(dealerResult.get(Result.WIN)).isEqualTo(1); - assertThat(dealerResult.get(Result.LOSE)).isEqualTo(2); - assertThat(dealerResult.get(Result.TIE)).isEqualTo(0); + assertThat(rule.judge(player, dealer)).isEqualTo(Outcome.LOSE); } private Player createPlayer(String name) { diff --git a/src/test/java/domain/game/ProfitResultTest.java b/src/test/java/domain/game/ProfitResultTest.java new file mode 100644 index 00000000000..b70dcc89509 --- /dev/null +++ b/src/test/java/domain/game/ProfitResultTest.java @@ -0,0 +1,37 @@ +package domain.game; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import domain.participant.BettingMoney; +import domain.participant.Player; +import java.util.LinkedHashMap; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ProfitResultTest { + + @DisplayName("딜러 수익은 플레이어 수익 합산의 부호 반전이다") + @Test + void 딜러_수익은_플레이어_수익_합산의_부호_반전이다() { + Map playerProfits = new LinkedHashMap<>(); + playerProfits.put(new Player("pobi", new BettingMoney(10000)), 10000); + playerProfits.put(new Player("jason", new BettingMoney(20000)), -20000); + + ProfitResult profitResult = new ProfitResult(playerProfits); + + assertThat(profitResult.getDealerProfit()).isEqualTo(10000); + } + + @DisplayName("플레이어별 수익을 반환한다") + @Test + void 플레이어별_수익을_반환한다() { + Player player = new Player("pobi", new BettingMoney(10000)); + Map playerProfits = new LinkedHashMap<>(); + playerProfits.put(player, 15000); + + ProfitResult profitResult = new ProfitResult(playerProfits); + + assertThat(profitResult.getPlayerProfits().get(player)).isEqualTo(15000); + } +} diff --git a/src/test/java/domain/bet/BettingMoneyTest.java b/src/test/java/domain/participant/BettingMoneyTest.java similarity index 80% rename from src/test/java/domain/bet/BettingMoneyTest.java rename to src/test/java/domain/participant/BettingMoneyTest.java index 966d6251e04..4839b47ea48 100644 --- a/src/test/java/domain/bet/BettingMoneyTest.java +++ b/src/test/java/domain/participant/BettingMoneyTest.java @@ -1,9 +1,9 @@ -package domain.bet; +package domain.participant; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import domain.game.Result; +import domain.game.Outcome; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -25,28 +25,28 @@ public class BettingMoneyTest { @Test void 승리하면_베팅_금액만큼_수익이다() { BettingMoney money = new BettingMoney(10000); - assertThat(money.calculateProfit(Result.WIN)).isEqualTo(10000); + assertThat(money.calculateProfit(Outcome.WIN)).isEqualTo(10000); } @DisplayName("블랙잭 승리하면 1.5배 수익이다") @Test void 블랙잭_승리하면_1점5배_수익이다() { BettingMoney money = new BettingMoney(10000); - assertThat(money.calculateProfit(Result.BLACKJACK_WIN)).isEqualTo(15000); + assertThat(money.calculateProfit(Outcome.BLACKJACK_WIN)).isEqualTo(15000); } @DisplayName("패배하면 베팅 금액만큼 손해다") @Test void 패배하면_베팅_금액만큼_손해다() { BettingMoney money = new BettingMoney(10000); - assertThat(money.calculateProfit(Result.LOSE)).isEqualTo(-10000); + assertThat(money.calculateProfit(Outcome.LOSE)).isEqualTo(-10000); } @DisplayName("무승부면 수익이 0이다") @Test void 무승부면_수익이_0이다() { BettingMoney money = new BettingMoney(10000); - assertThat(money.calculateProfit(Result.TIE)).isEqualTo(0); + assertThat(money.calculateProfit(Outcome.TIE)).isEqualTo(0); } @DisplayName("베팅 금액이 0이면 예외가 발생한다") @@ -60,6 +60,6 @@ public class BettingMoneyTest { @Test void 홀수_금액의_블랙잭_승리는_소수점을_버린다() { BettingMoney money = new BettingMoney(999); - assertThat(money.calculateProfit(Result.BLACKJACK_WIN)).isEqualTo(1498); + assertThat(money.calculateProfit(Outcome.BLACKJACK_WIN)).isEqualTo(1498); } } diff --git a/src/test/java/domain/participant/PlayerTest.java b/src/test/java/domain/participant/PlayerTest.java index a0a7858eea3..3b82938ef96 100644 --- a/src/test/java/domain/participant/PlayerTest.java +++ b/src/test/java/domain/participant/PlayerTest.java @@ -3,7 +3,6 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import domain.bet.BettingMoney; import domain.card.Card; import domain.card.Rank; import domain.card.Suit; From d4c3f6e0646a50173a5dfa2e24e79db147636271 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sat, 14 Mar 2026 22:04:31 +0900 Subject: [PATCH 46/48] =?UTF-8?q?refactor:=20BlackjackController.calculate?= =?UTF-8?q?Profits=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B1=85=EC=9E=84=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/BlackjackController.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java index f4ddf1fe5dd..7297024dd83 100644 --- a/src/main/java/controller/BlackjackController.java +++ b/src/main/java/controller/BlackjackController.java @@ -90,14 +90,18 @@ private void printFinalCards(Dealer dealer, Players players) { } } - private void printProfitResult(Dealer dealer, Players players) { + private ProfitResult calculateProfits(Dealer dealer, Players players) { BlackjackRule rule = new BlackjackRule(); Map playerProfits = new LinkedHashMap<>(); for (Player player : players.getGamePlayers()) { Outcome outcome = rule.judge(player, dealer); playerProfits.put(player, player.calculateProfit(outcome)); } - ProfitResult profitResult = new ProfitResult(playerProfits); + return new ProfitResult(playerProfits); + } + + private void printProfitResult(Dealer dealer, Players players) { + ProfitResult profitResult = calculateProfits(dealer, players); outputView.printProfitResult(dealer.getName(), profitResult); } } From 8fbccba70839d03f4d077cb48924fd5c72e848f1 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sun, 15 Mar 2026 00:02:15 +0900 Subject: [PATCH 47/48] =?UTF-8?q?docs:=20TODO=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=97=90=20=EA=B5=AC=ED=98=84=EB=90=9C=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 14377955dae..91aa483f186 100644 --- a/README.md +++ b/README.md @@ -52,43 +52,45 @@ ### 베팅 금액 입력 -- [ ] 각 플레이어의 베팅 금액을 입력받는 기능 -- [ ] 베팅 금액 검증 (양수만 허용) -- [ ] `BettingMoney` 값 객체 생성 +- [x] 각 플레이어의 베팅 금액을 입력받는 기능 +- [x] 베팅 금액 검증 (양수만 허용) +- [x] `BettingMoney` 값 객체 생성 ### 블랙잭 판별 -- [ ] `CardBundle`에 `isBlackjack()` 추가 (카드 2장 && 점수 21) -- [ ] `CardBundle`에 `isBust()` 추가 (점수 > 21) -- [ ] `Participant`에 위임 메서드 추가 +- [x] `CardBundle`에 `isBlackjack()` 추가 (카드 2장 && 점수 21) +- [x] `CardBundle`에 `isBust()` 추가 (점수 > 21) +- [x] `Participant`에 위임 메서드 추가 ### 승패 판정 개선 -- [ ] `Result`에 `BLACKJACK_WIN` 추가 및 배율(profitRate) 필드 도입 -- [ ] `Referee` 블랙잭 판정 로직 추가 - - [ ] 플레이어 블랙잭 + 딜러 블랙잭 → 무승부 - - [ ] 플레이어 블랙잭 + 딜러 일반 → 블랙잭 승리 (1.5배) - - [ ] 딜러 버스트 → 남아있는 플레이어 승리 +- [x] `Outcome`에 `BLACKJACK_WIN` 추가 및 배율(profitRate) 필드 도입 +- [x] `BlackjackRule` 블랙잭 판정 로직 추가 + - [x] 플레이어 블랙잭 + 딜러 블랙잭 → 무승부 + - [x] 플레이어 블랙잭 + 딜러 일반 → 블랙잭 승리 (1.5배) + - [x] 딜러만 블랙잭 → 플레이어 패배 + - [x] 딜러 버스트 → 남아있는 플레이어 승리 ### 수익 계산 -- [ ] `BettingMoney`에 `calculateProfit(Result)` 구현 (금액 × 배율) -- [ ] 딜러 수익 계산 (플레이어 수익 합산의 부호 반전) +- [x] `BettingMoney`에 `calculateProfit(Outcome)` 구현 (금액 × 배율) +- [x] 딜러 수익 계산 (플레이어 수익 합산의 부호 반전) — `ProfitResult` ### Player에 베팅 연결 -- [ ] `Player`에 `BettingMoney` 필드 추가 -- [ ] `Players` 생성 방식 변경 (이름 + 금액) +- [x] `Player`에 `BettingMoney` 필드 추가 +- [x] `Players` 생성 방식 변경 (이름 + 금액) ### 입출력 수정 -- [ ] `InputView`에 베팅 금액 입력 메서드 추가 -- [ ] `OutputView` 최종 결과를 금액으로 출력하도록 변경 -- [ ] `BlackjackController`에 베팅 입력 단계 및 수익 출력 단계 추가 +- [x] `InputView`에 베팅 금액 입력 메서드 추가 +- [x] `OutputView` 최종 결과를 금액으로 출력하도록 변경 +- [x] `BlackjackController`에 베팅 입력 단계 및 수익 출력 단계 추가 ### 테스트 -- [ ] `BettingMoney` 검증 테스트 (양수, 0, 음수) -- [ ] `CardBundle` 블랙잭/버스트 판별 테스트 -- [ ] `Referee` 블랙잭 케이스 판정 테스트 -- [ ] 수익 계산 테스트 (블랙잭 승리, 일반 승리, 무승부, 패배) +- [x] `BettingMoney` 검증 테스트 (양수, 0, 음수) +- [x] `CardBundle` 블랙잭/버스트 판별 테스트 +- [x] `BlackjackRule` 블랙잭 케이스 판정 테스트 (8개 케이스) +- [x] 수익 계산 테스트 (블랙잭 승리, 일반 승리, 무승부, 패배) +- [x] `ProfitResult` 딜러 수익 집계 테스트 From c740604a08ff61d4037c545961740a95c4b38f46 Mon Sep 17 00:00:00 2001 From: picetea44 Date: Sun, 15 Mar 2026 01:29:28 +0900 Subject: [PATCH 48/48] =?UTF-8?q?docs:=20=EC=A7=88=EB=AC=B8=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 91aa483f186..922d6c505cc 100644 --- a/README.md +++ b/README.md @@ -18,33 +18,24 @@ - [x] 4 - [ ] 5 (완벽하게 충족) -### 선택한 점수의 이유를 적어주세요. -처음으로 TDD를 적용하여 진행해봤습니다. 전체적인 설계 없이 일단 돌아가는 기능을 만들고 테스트를 한다는 개념이 많이 생소했던 것 같습니다. -그로인해 부분적으로 indent나 문자열 포장이 완벽하게 적용되지 못했던 것 같습니다. - ## 어떤 부분에 집중하여 리뷰해야 할까요? --- -### 주제1 - TDD 관련 -미션 초기, 전체적인 설계가 머릿속에 완벽히 그려지지 않은 상태에서 TDD를 시작하려니 상당한 막막함을 느꼈습니다. 동작을 기준으로 테스트를 작성하며 상향식으로 접근하려 했으나, 여러 객체나 메서드가 복잡하게 상호작용해야 하는 상황에서는 그 동작의 단위 자체가 모호하게 다가왔습니다. - -처음에는 프로그램의 흐름에 따라 큰 단위에서 작은 단위로 기능을 정의하며 내려왔는데, 이 과정이 다소 추상적이라는 생각이 들었습니다. 현업에서는 설계가 불확실한 상황에서 어떤 지점을 시작점으로 잡고 설계를 구체화해 나가는지, 그리고 객체 간의 협력이 필수적인 상황에서 TDD의 리듬을 어떻게 유지하시는지 궁금합니다. - -### 주제2 - 테스트를 위한 코드 -테스트 가독성과 검증을 위해 프로덕션 코드를 어디까지 수정해야 하는지에 대해서도 깊이 고민했습니다. 예를 들어, 카드 덱에서 카드가 제대로 뽑혔는지 확인하기 위해 실제 로직에서는 쓰이지 않는 deck.size()와 같은 메서드를 추가하는 상황이 있었습니다. - -이를 해결하기 위해 1) 무식하게 52번의 draw()를 호출하여 예외를 확인하는 방식, 2) 내부 상태를 우회하여 검증하는 방식, 3) 임시로 메서드를 만들고 추후 삭제하는 방식 등을 고려해 보았습니다. 저는 현재 테스트의 명확성을 위해 메서드를 유지하는 쪽을 택했지만, 이것이 적절한 방향이 맞을지 우려되기도 합니다. 테스트 가능성을 높이기 위해 프로덕션 코드를 변경하는 것에 대해 리뷰어님은 어떤 기준을 가지고 계신가요? - -### 주제3 - 도메인에 따른 아키텍처 -마지막으로 블랙잭처럼 도메인 로직이 명확하고 규모가 작은 경우, 서비스 레이어의 도입이 반드시 필요한지 의문이 생겼습니다. 현재 구조에서는 컨트롤러와 도메인 모델만으로도 충분히 실행 흐름을 제어할 수 있다고 느꼈고, 단순히 계층 구조를 맞추기 위해 서비스 레이어를 두는 것은 불필요한 위임 코드만 양산한다고 판단했습니다. - -하지만 프로젝트가 커질 상황을 대비해야 하는 관점에서는 레이어를 나누는 것이 맞을지도 모른다는 갈등이 있었습니다. 도메인이 작은 프로젝트에서 아키텍처의 복잡도를 결정하는 리뷰어님만의 트레이드 오프 기준이 있다면 배우고 싶습니다. -### 주제4 - 향후 학습 방향성 및 마인드 셋 -이번 페어 프로그래밍을 진행하며 제 스스로의 부족함을 많이 통감했습니다. 다른 팀들은 디자인 패턴을 적극적으로 도입하고 논의하는데, 저는 그 대화에 온전히 참여하지 못하거나 패턴을 코드에 녹여내는 데 어려움을 겪었습니다. 심지어는 비즈니스 로직에 집중하다 보니 가끔 가장 기본적인 언어 문법조차 헷갈려 당황스러운 순간들도 있었습니다. - -객체지향적인 설계는커녕 지식의 파편화로 인해 곳곳에 빈틈이 있다는 느낌을 많이 받았습니다. 리뷰어님이 보시기에 현재 제 수준에서 어떤 부분을 가장 우선순위에 두고 학습해야 할까요? 단순히 패턴을 외우는 것보다, 제가 놓치고 있는 기본기가 무엇인지 리뷰어님의 시선에서 냉철하게 짚어주시고 앞으로의 공부 방향을 제시해 주실 수 있다면 정말 감사하겠습니다. +현재 구조는 Player → CardBundle → Card처럼 객체가 서로를 참조하고 있는데, 일부 로직에서 이미 Player 객체를 전달받고 있음에도 불구하고 CardBundle에 직접 접근해 점수를 계산하는 코드가 있었습니다. +이 경우 특정 클래스가 여러 도메인의 내부 구조를 알고 있어야 한다는 점이 걸렸습니다. +그래서 외부에서 CardBundle에 직접 접근하는 대신, Player에게 점수를 요청하고 Player가 내부적으로 CardBundle에 위임하는 구조로 변경했습니다. 이렇게 하면 외부에서는 Player의 내부 구조를 알 필요가 없어지고, 나중에 카드 관리 방식이나 점수 계산 로직이 바뀌더라도 다른 도메인이나 레이어의 변경을 줄일 수 있지 않을까 생각했습니다. +구현을 진행하면서 이런 방식이 위임 메서드라는 개념과도 연결된다는 것을 알게 되었습니다. +사이클1 리뷰에서 지식을 받아가는 취지보다 제 생각이 상대를 설득할 수 있을지에 초점을 맞춰보라는 말씀을 해주셨는데, 이번에는 그 조언을 떠올리며 제 나름의 기준으로 구조를 선택해 보았습니다. +제가 이런 방향으로 접근한 것이 어느 정도 타당한 방향인지, 또 이런 상황에서 추가로 고려해 볼 만한 부분이 있는지도 의견을 듣고 싶습니다. + +판정과 수익 계산 책임 분배에서 겪은 어려움도 있었습니다. +베팅 기능을 구현하면서 판정 결과와 베팅 금액을 이용해 수익을 계산하는 책임을 어디에 두어야 할지 고민이 많았습니다. +처음에는 관련 책임을 Bet 도메인에 모으려고 생각했습니다. 하지만 구현을 진행하면서 판정 로직, 플레이어 상태, 베팅 금액 등 여러 도메인이 함께 관여하게 되었고, 어느 객체에 책임을 두는 것이 자연스러운지 판단하기가 쉽지 않았습니다. +그래서 여러 방향으로 구조를 바꿔 보면서 계속 수정을 진행했던 것 같습니다. 그 과정에서 특정 객체가 너무 많은 정보를 알게 되는 구조가 되기도 했고, 반대로 책임이 지나치게 분산되는 느낌도 있었습니다. +여러 시도를 거친 끝에 현재 구조를 선택하게 되었지만, 이 선택이 최선인지에 대해서는 아직 확신이 서지는 않습니다. +아루가 보시기에는 이 문제를 고민하는 과정에서 제가 놓치고 있는 부분이 있는지, 혹은 이런 상황에서 책임의 위치를 판단할 때 어떤 기준으로 접근하시는지 궁금합니다. ---