From 819bff5d864a6ee4ab114282b02e1ad3b5bf57a1 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Wed, 11 Mar 2026 14:52:36 +0900 Subject: [PATCH 01/53] =?UTF-8?q?docs(README):=20=EB=A6=AC=EB=B7=B0?= =?UTF-8?q?=EB=A5=BC=20=ED=86=B5=ED=95=B4=20=EC=88=98=EC=A0=95=ED=95=9C=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=EC=9D=84=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 83 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index c8ad0d5eca2..a2a73235ee1 100644 --- a/README.md +++ b/README.md @@ -15,42 +15,67 @@ ### Card #### field - CardNumber(Enum class) + - 숫자 검증 및 반환 - CardPattern(Enum class) + - 패턴 검증 및 반환 #### method -- 숫자 검증 -- 카드 패턴 검증 --- ### Hand(1급 컬렉션) #### field -- List Card -- 카드 주인 +- cards #### method -- Append Card (카드 추가) +- append Card (카드 추가) - 전체 값(필드로 두지는 않고, 계산으로 반환) 계산 calculate +- Ace 처리 - 문양과 숫자가 중복되는지 검사 -- Ace 값을 결정? --- ### Member #### field +- name - Hand -- Role(player | dealer) #### method -- receive -- roleIsDealer 딜러인지 확인 후 카드 뽑을지 -- decideWinner(Member(역할이 dealer)) +- 총 스코어 반환 +- 모든 카드 반환 +- 카드 받기 +- 점수 비교 --- + +### Player, Dealer +#### method +- 점수 비교 재정의 + +### Members(1급 컬렉션) +#### field +- members + +#### method +- Member 추가 +- Member 카드 반환 +- Member 스코어 반환 +- Member 반환 +- 딜러 게임 결과 반환 +- 모든 플레이어 게임 결과 반환 + ### GameTable #### field -- Members(일급 컬렉션) +- Members #### method -- join : player 추가 -- CardResult playRound() -- List draw(memberName, Card) +- 초기 카드 배분 +- 플레이어 카드 뽑기 +- 딜러가 카드를 뽑아야 되는지 확인 +- Bust 체크 +- 모든 멤버 상태 반환 +- 모든 멤버 결과 반환 ### Service -- holdingPlayerCards() +- 게임 초기화 +- 멤버 게임 1라운드 실행 +- 딜러가 카드를 뽑아야 되는지 확인 +- 모든 멤버 상태 반환 +- 모든 멤버 결과 반환 + ``` 딜러카드: 3다이아몬드, 9클로버, 8다이아몬드 - 결과: 20 pobi카드: 2하트, 8스페이드, A클로버 - 결과: 21 @@ -140,17 +165,33 @@ jason: 패 토론 활동에서 정한 규칙을 의식하며 코드 작성 규칙 때문에 코드를 변경한 곳 기록 막히는 순간 기록 + ## 미션 중 기록 --- 필수 기록: -[ ] 규칙을 적용해서 변경한 코드 1곳 이상 -[ ] 테스트 작성이 어려웠던 코드 1곳 이상 -[ ] 막힌 순간 1회 이상 +- [x] 규칙을 적용해서 변경한 코드 1곳 이상 +```markdown +처음에 테스트 메소드 명을 작성할 때 우리 조가 정한 규칙은 다음과 같다. +아래의 포맷으로 테스트 메서드 작성 (한/영 상관없음) +- 메서드명_테스트상태_기대행위 +- 메서드명_기대행위_테스트상태 +그래서 기존처럼 테스트 코드를 막 지었다가 수정 했다. +``` +- [x] 테스트 작성이 어려웠던 코드 1곳 이상 +```markdown +Deck을 처음에 52장의 트럼프 카드를 넣고 shuffle을 시켜서 draw()를 테스트 하는게 어려웠다. +-> Deck을 인터페이스로 구현하고 draw 전략을 외부에서 주입해주는 방식을 사용하여 해결하였다. +``` +- [x] 막힌 순간 1회 이상 +```markdown +기존 Member 코드에서 딜러만 사용하는 메소드, 플레이어만 사용하는 메소드가 많아서 +어떻게 분리할지 고민하다가 LMS에 '변경 가능한 프로그램 만들기'를 읽고, 상속으로 해결하였다. +``` ## 미션 완료 조건 --- -[ ] 요구사항 구현 -[ ] 규칙에 의한 코드 변경 1회 이상 -[ ] 미션 중 기록 작성 +- [x] 요구사항 구현 +- [x] 규칙에 의한 코드 변경 1회 이상 +- [x] 미션 중 기록 작성 From 258ba6f5f5284a243fa581becfea03d5b03fca72 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Wed, 11 Mar 2026 16:01:14 +0900 Subject: [PATCH 02/53] =?UTF-8?q?refactor:=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=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/application/BlackjackService.java | 10 +++++----- .../exception/DuplicatedException.java | 2 +- src/main/java/domain/GameTable.java | 7 +++++-- src/main/java/domain/{ => card}/Card.java | 2 +- src/main/java/domain/{ => card}/CardNumber.java | 2 +- src/main/java/domain/{ => card}/CardPattern.java | 2 +- src/main/java/domain/{ => card}/Deck.java | 2 +- src/main/java/domain/{ => card}/StandardDeck.java | 2 +- src/main/java/domain/{ => member}/Dealer.java | 3 ++- src/main/java/domain/{ => member}/Hand.java | 6 ++++-- src/main/java/domain/{ => member}/Member.java | 4 +++- src/main/java/domain/{ => member}/Members.java | 4 +++- src/main/java/domain/{ => member}/Player.java | 4 +++- src/main/java/{domain => }/dto/GameResult.java | 2 +- src/main/java/{domain => }/dto/MemberStatus.java | 4 ++-- src/main/java/{application => }/dto/RoundResult.java | 4 ++-- src/main/java/presentation/BlackjackController.java | 6 +++--- src/main/java/presentation/ui/OutputView.java | 8 ++++---- src/test/java/domain/GameTableTest.java | 2 ++ src/test/java/domain/{ => card}/CardNumberTest.java | 2 +- src/test/java/domain/{ => card}/CardPatternTest.java | 2 +- src/test/java/domain/{ => card}/DeckTest.java | 2 +- src/test/java/domain/{ => card}/FixedDeck.java | 2 +- src/test/java/domain/{ => member}/HandTest.java | 5 +++-- src/test/java/domain/{ => member}/MemberTest.java | 4 +++- 25 files changed, 55 insertions(+), 38 deletions(-) rename src/main/java/{domain => constant}/exception/DuplicatedException.java (84%) rename src/main/java/domain/{ => card}/Card.java (97%) rename src/main/java/domain/{ => card}/CardNumber.java (97%) rename src/main/java/domain/{ => card}/CardPattern.java (96%) rename src/main/java/domain/{ => card}/Deck.java (67%) rename src/main/java/domain/{ => card}/StandardDeck.java (97%) rename src/main/java/domain/{ => member}/Dealer.java (91%) rename src/main/java/domain/{ => member}/Hand.java (91%) rename src/main/java/domain/{ => member}/Member.java (92%) rename src/main/java/domain/{ => member}/Members.java (96%) rename src/main/java/domain/{ => member}/Player.java (90%) rename src/main/java/{domain => }/dto/GameResult.java (87%) rename src/main/java/{domain => }/dto/MemberStatus.java (77%) rename src/main/java/{application => }/dto/RoundResult.java (70%) rename src/test/java/domain/{ => card}/CardNumberTest.java (98%) rename src/test/java/domain/{ => card}/CardPatternTest.java (98%) rename src/test/java/domain/{ => card}/DeckTest.java (98%) rename src/test/java/domain/{ => card}/FixedDeck.java (95%) rename src/test/java/domain/{ => member}/HandTest.java (95%) rename src/test/java/domain/{ => member}/MemberTest.java (93%) diff --git a/src/main/java/application/BlackjackService.java b/src/main/java/application/BlackjackService.java index 154b0955154..5a59bab8906 100644 --- a/src/main/java/application/BlackjackService.java +++ b/src/main/java/application/BlackjackService.java @@ -1,11 +1,11 @@ package application; -import domain.Card; +import domain.card.Card; import domain.GameTable; -import application.dto.RoundResult; -import domain.StandardDeck; -import domain.dto.GameResult; -import domain.dto.MemberStatus; +import dto.RoundResult; +import domain.card.StandardDeck; +import dto.GameResult; +import dto.MemberStatus; import java.util.List; public class BlackjackService { diff --git a/src/main/java/domain/exception/DuplicatedException.java b/src/main/java/constant/exception/DuplicatedException.java similarity index 84% rename from src/main/java/domain/exception/DuplicatedException.java rename to src/main/java/constant/exception/DuplicatedException.java index c863ff2a2ae..dc705910e83 100644 --- a/src/main/java/domain/exception/DuplicatedException.java +++ b/src/main/java/constant/exception/DuplicatedException.java @@ -1,4 +1,4 @@ -package domain.exception; +package constant.exception; public class DuplicatedException extends RuntimeException { public DuplicatedException() { diff --git a/src/main/java/domain/GameTable.java b/src/main/java/domain/GameTable.java index f81f665d22c..b21836380c6 100644 --- a/src/main/java/domain/GameTable.java +++ b/src/main/java/domain/GameTable.java @@ -1,8 +1,11 @@ package domain; import constant.Word; -import domain.dto.GameResult; -import domain.dto.MemberStatus; +import domain.card.Card; +import domain.card.Deck; +import dto.GameResult; +import dto.MemberStatus; +import domain.member.Members; import java.util.ArrayList; import java.util.List; import java.util.Map; 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 74f6c0f3563..dc824eed0d1 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/CardNumber.java b/src/main/java/domain/card/CardNumber.java similarity index 97% rename from src/main/java/domain/CardNumber.java rename to src/main/java/domain/card/CardNumber.java index 5b71704531f..2de82d7a79f 100644 --- a/src/main/java/domain/CardNumber.java +++ b/src/main/java/domain/card/CardNumber.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import java.util.Arrays; diff --git a/src/main/java/domain/CardPattern.java b/src/main/java/domain/card/CardPattern.java similarity index 96% rename from src/main/java/domain/CardPattern.java rename to src/main/java/domain/card/CardPattern.java index b955b3ac5b4..083d3f57d84 100644 --- a/src/main/java/domain/CardPattern.java +++ b/src/main/java/domain/card/CardPattern.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import java.util.Arrays; import java.util.NoSuchElementException; diff --git a/src/main/java/domain/Deck.java b/src/main/java/domain/card/Deck.java similarity index 67% rename from src/main/java/domain/Deck.java rename to src/main/java/domain/card/Deck.java index 77a74092d15..620811d98d6 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; public interface Deck { Card draw(); diff --git a/src/main/java/domain/StandardDeck.java b/src/main/java/domain/card/StandardDeck.java similarity index 97% rename from src/main/java/domain/StandardDeck.java rename to src/main/java/domain/card/StandardDeck.java index c3ecc262b82..c18f130eb87 100644 --- a/src/main/java/domain/StandardDeck.java +++ b/src/main/java/domain/card/StandardDeck.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import java.util.Collections; import java.util.LinkedList; diff --git a/src/main/java/domain/Dealer.java b/src/main/java/domain/member/Dealer.java similarity index 91% rename from src/main/java/domain/Dealer.java rename to src/main/java/domain/member/Dealer.java index 0a0396321ac..fe0d431e596 100644 --- a/src/main/java/domain/Dealer.java +++ b/src/main/java/domain/member/Dealer.java @@ -1,6 +1,7 @@ -package domain; +package domain.member; import constant.Word; +import domain.MatchResult; public class Dealer extends Member { diff --git a/src/main/java/domain/Hand.java b/src/main/java/domain/member/Hand.java similarity index 91% rename from src/main/java/domain/Hand.java rename to src/main/java/domain/member/Hand.java index e9fad1ec802..1122787c1da 100644 --- a/src/main/java/domain/Hand.java +++ b/src/main/java/domain/member/Hand.java @@ -1,6 +1,8 @@ -package domain; +package domain.member; -import domain.exception.DuplicatedException; +import domain.card.Card; +import domain.card.CardNumber; +import constant.exception.DuplicatedException; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/domain/Member.java b/src/main/java/domain/member/Member.java similarity index 92% rename from src/main/java/domain/Member.java rename to src/main/java/domain/member/Member.java index 38a67c18a54..15e67ca1b1c 100644 --- a/src/main/java/domain/Member.java +++ b/src/main/java/domain/member/Member.java @@ -1,5 +1,7 @@ -package domain; +package domain.member; +import domain.MatchResult; +import domain.card.Card; import java.util.List; public abstract class Member { diff --git a/src/main/java/domain/Members.java b/src/main/java/domain/member/Members.java similarity index 96% rename from src/main/java/domain/Members.java rename to src/main/java/domain/member/Members.java index c33584c6d91..ca4480a54fc 100644 --- a/src/main/java/domain/Members.java +++ b/src/main/java/domain/member/Members.java @@ -1,6 +1,8 @@ -package domain; +package domain.member; import constant.Word; +import domain.MatchResult; +import domain.card.Card; import java.util.ArrayList; import java.util.HashMap; import java.util.List; diff --git a/src/main/java/domain/Player.java b/src/main/java/domain/member/Player.java similarity index 90% rename from src/main/java/domain/Player.java rename to src/main/java/domain/member/Player.java index 93757e526bc..3b7599bd5af 100644 --- a/src/main/java/domain/Player.java +++ b/src/main/java/domain/member/Player.java @@ -1,4 +1,6 @@ -package domain; +package domain.member; + +import domain.MatchResult; public class Player extends Member { diff --git a/src/main/java/domain/dto/GameResult.java b/src/main/java/dto/GameResult.java similarity index 87% rename from src/main/java/domain/dto/GameResult.java rename to src/main/java/dto/GameResult.java index 5563f99294e..1ed7e419be0 100644 --- a/src/main/java/domain/dto/GameResult.java +++ b/src/main/java/dto/GameResult.java @@ -1,4 +1,4 @@ -package domain.dto; +package dto; import domain.MatchResult; import java.util.List; diff --git a/src/main/java/domain/dto/MemberStatus.java b/src/main/java/dto/MemberStatus.java similarity index 77% rename from src/main/java/domain/dto/MemberStatus.java rename to src/main/java/dto/MemberStatus.java index fab0d42e7ab..dc761cff824 100644 --- a/src/main/java/domain/dto/MemberStatus.java +++ b/src/main/java/dto/MemberStatus.java @@ -1,6 +1,6 @@ -package domain.dto; +package dto; -import domain.Card; +import domain.card.Card; import java.util.List; public record MemberStatus( diff --git a/src/main/java/application/dto/RoundResult.java b/src/main/java/dto/RoundResult.java similarity index 70% rename from src/main/java/application/dto/RoundResult.java rename to src/main/java/dto/RoundResult.java index 4eb18c2220b..7f63203a66e 100644 --- a/src/main/java/application/dto/RoundResult.java +++ b/src/main/java/dto/RoundResult.java @@ -1,6 +1,6 @@ -package application.dto; +package dto; -import domain.Card; +import domain.card.Card; import java.util.List; public record RoundResult( diff --git a/src/main/java/presentation/BlackjackController.java b/src/main/java/presentation/BlackjackController.java index 3bc93421658..d7f84d392e2 100644 --- a/src/main/java/presentation/BlackjackController.java +++ b/src/main/java/presentation/BlackjackController.java @@ -1,9 +1,9 @@ package presentation; import application.BlackjackService; -import application.dto.RoundResult; -import domain.dto.GameResult; -import domain.dto.MemberStatus; +import dto.RoundResult; +import dto.GameResult; +import dto.MemberStatus; import java.util.List; import presentation.ui.InputView; import presentation.ui.OutputView; diff --git a/src/main/java/presentation/ui/OutputView.java b/src/main/java/presentation/ui/OutputView.java index a17505d695a..3d72844881e 100644 --- a/src/main/java/presentation/ui/OutputView.java +++ b/src/main/java/presentation/ui/OutputView.java @@ -2,11 +2,11 @@ import static constant.Word.*; -import application.dto.RoundResult; -import domain.Card; +import dto.RoundResult; +import domain.card.Card; import domain.MatchResult; -import domain.dto.GameResult; -import domain.dto.MemberStatus; +import dto.GameResult; +import dto.MemberStatus; import java.util.List; import java.util.stream.Collectors; diff --git a/src/test/java/domain/GameTableTest.java b/src/test/java/domain/GameTableTest.java index 3de9eb6f2ba..9f0989ed767 100644 --- a/src/test/java/domain/GameTableTest.java +++ b/src/test/java/domain/GameTableTest.java @@ -1,5 +1,7 @@ package domain; +import domain.card.Card; +import domain.card.FixedDeck; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/domain/CardNumberTest.java b/src/test/java/domain/card/CardNumberTest.java similarity index 98% rename from src/test/java/domain/CardNumberTest.java rename to src/test/java/domain/card/CardNumberTest.java index 77cc5517b7c..9ac12c7b431 100644 --- a/src/test/java/domain/CardNumberTest.java +++ b/src/test/java/domain/card/CardNumberTest.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/domain/CardPatternTest.java b/src/test/java/domain/card/CardPatternTest.java similarity index 98% rename from src/test/java/domain/CardPatternTest.java rename to src/test/java/domain/card/CardPatternTest.java index 4b472c78861..0ebd55597b4 100644 --- a/src/test/java/domain/CardPatternTest.java +++ b/src/test/java/domain/card/CardPatternTest.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import java.util.NoSuchElementException; import org.assertj.core.api.Assertions; 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 02eaf3c27e7..676d62b31b3 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 java.util.List; import java.util.NoSuchElementException; diff --git a/src/test/java/domain/FixedDeck.java b/src/test/java/domain/card/FixedDeck.java similarity index 95% rename from src/test/java/domain/FixedDeck.java rename to src/test/java/domain/card/FixedDeck.java index cf1478054db..524c68066e8 100644 --- a/src/test/java/domain/FixedDeck.java +++ b/src/test/java/domain/card/FixedDeck.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import java.util.LinkedList; import java.util.List; diff --git a/src/test/java/domain/HandTest.java b/src/test/java/domain/member/HandTest.java similarity index 95% rename from src/test/java/domain/HandTest.java rename to src/test/java/domain/member/HandTest.java index d9f938c130f..c4411e17f4d 100644 --- a/src/test/java/domain/HandTest.java +++ b/src/test/java/domain/member/HandTest.java @@ -1,6 +1,7 @@ -package domain; +package domain.member; -import domain.exception.DuplicatedException; +import domain.card.Card; +import constant.exception.DuplicatedException; import java.util.LinkedList; import java.util.List; import java.util.Queue; diff --git a/src/test/java/domain/MemberTest.java b/src/test/java/domain/member/MemberTest.java similarity index 93% rename from src/test/java/domain/MemberTest.java rename to src/test/java/domain/member/MemberTest.java index ecbf018273c..3cb6c4df5f4 100644 --- a/src/test/java/domain/MemberTest.java +++ b/src/test/java/domain/member/MemberTest.java @@ -1,5 +1,7 @@ -package domain; +package domain.member; +import domain.MatchResult; +import domain.card.Card; import java.util.Arrays; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; From 0e0783ae6e264a990e8efc8a00e5d01000a2d76e Mon Sep 17 00:00:00 2001 From: JYL35 Date: Thu, 12 Mar 2026 15:59:25 +0900 Subject: [PATCH 03/53] =?UTF-8?q?refactor(Hand):=20=EB=B6=88=EB=B3=80=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 초기 Hand는 불변 리스트로 초기화 - appendCard 시에 불변 리스트에 카드를 추가하여 새로운 Hand 객체를 반환 --- src/main/java/domain/member/Hand.java | 16 +++++++++++----- src/main/java/domain/member/Member.java | 6 +++--- src/test/java/domain/member/HandTest.java | 8 ++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/java/domain/member/Hand.java b/src/main/java/domain/member/Hand.java index 1122787c1da..4e47b7cb781 100644 --- a/src/main/java/domain/member/Hand.java +++ b/src/main/java/domain/member/Hand.java @@ -11,16 +11,22 @@ public class Hand { private final List cards; public Hand() { - cards = new ArrayList<>(); + cards = List.of(); } - public List cards() { - return cards; + private Hand(List cards) { + this.cards = cards; } - public void appendCard(Card card) { + public List getAllCard() { + return List.copyOf(cards); + } + + public Hand appendCard(Card card) { validateDuplicate(card); - cards.add(card); + List newCards = new ArrayList<>(cards); + newCards.add(card); + return new Hand(newCards); } public int calculateTotalValue() { diff --git a/src/main/java/domain/member/Member.java b/src/main/java/domain/member/Member.java index 15e67ca1b1c..f81223e8dca 100644 --- a/src/main/java/domain/member/Member.java +++ b/src/main/java/domain/member/Member.java @@ -8,7 +8,7 @@ public abstract class Member { protected static final int BUST_CONDITION = 21; private final String name; - private final Hand hand; + private Hand hand; public Member(String name) { this.name = name; @@ -24,11 +24,11 @@ public int currentValue() { } public List currentCards() { - return hand.cards(); + return hand.getAllCard(); } public void receiveCard(Card card) { - hand.appendCard(card); + hand = hand.appendCard(card); } public abstract MatchResult compareScoreWith(Member other); diff --git a/src/test/java/domain/member/HandTest.java b/src/test/java/domain/member/HandTest.java index c4411e17f4d..679f17be18d 100644 --- a/src/test/java/domain/member/HandTest.java +++ b/src/test/java/domain/member/HandTest.java @@ -24,7 +24,7 @@ void setUp() { @DisplayName("카드 추가 시 중복 검사 예외 테스트") @Test void appendAndDuplicateTest_holdingTwoAndAppendTwo_ThrowDuplicatedException() { - hand.appendCard(new Card("2", "하트")); + hand = hand.appendCard(new Card("2", "하트")); Assertions.assertThatThrownBy( () -> hand.appendCard(new Card("2", "하트"))) @@ -34,8 +34,8 @@ void appendAndDuplicateTest_holdingTwoAndAppendTwo_ThrowDuplicatedException() { @DisplayName("카드 총합 계산 기능 테스트") @Test void calculateTest_holdTwoThree_ReturnTotalValue() { - hand.appendCard(new Card("2", "하트")); - hand.appendCard(new Card("3", "스페이드")); + hand = hand.appendCard(new Card("2", "하트")); + hand = hand.appendCard(new Card("3", "스페이드")); Assertions.assertThat(hand.calculateTotalValue()) .isEqualTo(5); @@ -57,7 +57,7 @@ void aceTest_AceAmountOneAndSum11_return12(String numbers, String names, int res Queue numberQueue = new LinkedList<>(List.of(numbers.split(":"))); Queue nameQueue = new LinkedList<>(List.of(names.split(":"))); while (!numberQueue.isEmpty()) { - hand.appendCard(new Card(numberQueue.poll(), nameQueue.poll())); + hand = hand.appendCard(new Card(numberQueue.poll(), nameQueue.poll())); } Assertions.assertThat(hand.calculateTotalValue()).isEqualTo(result); } From aeb1bd56b29d577f73b5439ea5622f67f83dfbb7 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Thu, 12 Mar 2026 17:48:02 +0900 Subject: [PATCH 04/53] =?UTF-8?q?refactor(Members):=20=ED=94=8C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=20=EA=B2=8C=EC=9E=84=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EA=B2=B0=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EC=88=98=EC=A0=95(?= =?UTF-8?q?=EA=B0=84=EC=86=8C=ED=99=94)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/GameTable.java | 25 ++++++++++++------------ src/main/java/domain/member/Members.java | 22 ++++++--------------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/main/java/domain/GameTable.java b/src/main/java/domain/GameTable.java index b21836380c6..c4f619ec8eb 100644 --- a/src/main/java/domain/GameTable.java +++ b/src/main/java/domain/GameTable.java @@ -24,9 +24,9 @@ public GameTable(List playerNames, Deck deck) { } public void distributeInitCard() { - for (String memberName : members.getAllPlayerName()) { - members.provideCard(memberName, deck.draw()); - members.provideCard(memberName, deck.draw()); + for (String memberName : members.getAllMemberName()) { + members.provideCardToMember(memberName, deck.draw()); + members.provideCardToMember(memberName, deck.draw()); } } @@ -35,20 +35,20 @@ public boolean checkBust(String memberName) { } public List drawForMember(String memberName) { - members.provideCard(memberName, deck.draw()); + members.provideCardToMember(memberName, deck.draw()); return members.findCardByName(memberName); } public boolean drawForDealer() { if (members.checkValue(Word.DEALER.getWord()) <= DEALER_DRAW_CONDITION) { - members.provideCard(Word.DEALER.getWord(), deck.draw()); + members.provideCardToMember(Word.DEALER.getWord(), deck.draw()); return true; } return false; } public List checkMemberStatuses() { - return members.getAllPlayerName() + return members.getAllMemberName() .stream() .map(name -> { List cards = members.findCardByName(name); @@ -60,14 +60,13 @@ public List checkMemberStatuses() { public List checkGameResult() { List gameResults = new ArrayList<>(); gameResults.add(new GameResult(Word.DEALER.getWord(), - members.judgeDealerGameResult())); + members.determineDealerGameResult())); - Map playerResults = members.judgePlayerGameResult(); - - for (String playerName : playerResults.keySet()) { - gameResults.add(new GameResult(playerName, - List.of(playerResults.get(playerName)))); - } + List playerResults =members.getAllMemberName().stream() + .filter(name -> !name.equals(Word.DEALER.getWord())) + .map(name -> new GameResult(name, List.of(members.determinePlayerGameResult(name)))) + .toList(); + gameResults.addAll(playerResults); return gameResults; } } diff --git a/src/main/java/domain/member/Members.java b/src/main/java/domain/member/Members.java index ca4480a54fc..03a0387a21e 100644 --- a/src/main/java/domain/member/Members.java +++ b/src/main/java/domain/member/Members.java @@ -25,7 +25,7 @@ private void join(Member member) { members.add(member); } - public void provideCard(String memberName, Card card) { + public void provideCardToMember(String memberName, Card card) { Member member = findByName(memberName); member.receiveCard(card); } @@ -47,13 +47,13 @@ private Member findByName(String memberName) { .orElseThrow(NoSuchElementException::new); } - public List getAllPlayerName() { + public List getAllMemberName() { return members.stream() .map(Member::name) .toList(); } - public List judgeDealerGameResult() { + public List determineDealerGameResult() { Member dealer = findByName(Word.DEALER.getWord()); List players = members.stream() .filter(member -> !member.name().equals(Word.DEALER.getWord())) @@ -66,19 +66,9 @@ public List judgeDealerGameResult() { return gameResult; } - public Map judgePlayerGameResult() { + public MatchResult determinePlayerGameResult(String name) { Member dealer = findByName(Word.DEALER.getWord()); - List players = members.stream() - .filter(member -> !member.name().equals(Word.DEALER.getWord())) - .toList(); - - Map gameResult = new HashMap<>(); - for (Member player : players) { - String playerName = player.name(); - gameResult.put(playerName, - player.compareScoreWith(dealer)); - } - - return gameResult; + Member player = findByName(name); + return player.compareScoreWith(dealer); } } From e2ec1e699aa024d3fe24d34fd160cce82e5e3aca Mon Sep 17 00:00:00 2001 From: JYL35 Date: Thu, 12 Mar 2026 17:49:54 +0900 Subject: [PATCH 05/53] =?UTF-8?q?test(Members):=20=EB=88=84=EB=9D=BD?= =?UTF-8?q?=EB=90=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 멤버 명단 테스트 - 카드 추가 테스트 - 딜러 게임 결과 결정 테스트 - 플레이어 게임 결과 결정 테스트 --- src/test/java/domain/member/MembersTest.java | 64 ++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/test/java/domain/member/MembersTest.java diff --git a/src/test/java/domain/member/MembersTest.java b/src/test/java/domain/member/MembersTest.java new file mode 100644 index 00000000000..9e99fdf6e62 --- /dev/null +++ b/src/test/java/domain/member/MembersTest.java @@ -0,0 +1,64 @@ +package domain.member; + +import domain.MatchResult; +import domain.card.Card; +import java.util.List; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class MembersTest { + + @DisplayName("멤버 명단을 정상적으로 불러오는지 테스트") + @Test + void getAllMemberNameTest_memberSuccessfullyCreated_containPlayerNames() { + String player1 = "pobi"; + String player2 = "jason"; + + Members members = new Members(List.of(player1, player2)); + + Assertions.assertThat(members.getAllMemberName()).contains(player1, player2); + } + + @DisplayName("해당 멤버에게 카드를 주면 정상적으로 카드를 보유하고 있는지 테스트") + @Test + void provideCardToMember_cardGivenToMember_containGivenCard() { + String playerName = "pobi"; + Members members = new Members(List.of(playerName)); + Card card = new Card("2", "하트"); + + members.provideCardToMember(playerName, card); + + Assertions.assertThat(members.findCardByName(playerName)).contains(card); + } + + @DisplayName("딜러의 게임 결과를 정상적으로 결정하는지 테스트") + @Test + void determineDealerGameResult_compareWithPlayer_returnMatchResult() { + String player = "pobi"; + Members members = new Members(List.of(player)); + List dealerCards = List.of(new Card("6", "하트"), new Card("4", "하트")); + List playerCards = List.of(new Card("2", "하트"), new Card("3", "하트")); + MatchResult expected = MatchResult.WIN; + + dealerCards.forEach(card -> members.provideCardToMember("딜러", card)); + playerCards.forEach(card -> members.provideCardToMember(player, card)); + + Assertions.assertThat(members.determineDealerGameResult()).containsExactly(expected); + } + + @DisplayName("플레이어의 게임 결과를 정상적으로 결정하는지 테스트") + @Test + void determinePlayerGameResult_compareWithDealer_returnMatchResult() { + String player = "pobi"; + Members members = new Members(List.of(player)); + List dealerCards = List.of(new Card("6", "하트"), new Card("4", "하트")); + List playerCards = List.of(new Card("2", "하트"), new Card("3", "하트")); + MatchResult expected = MatchResult.LOSE; + + dealerCards.forEach(card -> members.provideCardToMember("딜러", card)); + playerCards.forEach(card -> members.provideCardToMember(player, card)); + + Assertions.assertThat(members.determinePlayerGameResult(player)).isEqualTo(expected); + } +} From 8ef071677b889e181ec7974d46ba5c109a2fcc7b Mon Sep 17 00:00:00 2001 From: JYL35 Date: Fri, 13 Mar 2026 00:24:35 +0900 Subject: [PATCH 06/53] =?UTF-8?q?refactor(OutputView):=20=ED=94=8C?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=20=EA=B2=B0=EA=B3=BC=20=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Draw 시 출력 후 early return이 누락되어 수정 --- src/main/java/presentation/ui/OutputView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/presentation/ui/OutputView.java b/src/main/java/presentation/ui/OutputView.java index 3d72844881e..a0cb68cf48e 100644 --- a/src/main/java/presentation/ui/OutputView.java +++ b/src/main/java/presentation/ui/OutputView.java @@ -92,6 +92,7 @@ private void printPlayerResult(MatchResult playerResult, String name) { } if (playerResult == MatchResult.DRAW) { System.out.println(PLAYER_GAME_DRAW.format(name)); + return; } System.out.println(PLAYER_GAME_LOSE.format(name)); } From 5b571ddc891c45184aca3179a7dbba0d24f377e6 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Fri, 13 Mar 2026 01:04:59 +0900 Subject: [PATCH 07/53] =?UTF-8?q?refactor(Card):=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EB=B6=88=EB=B3=80=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CardNumber, CardPatter 필드 final 누락으로인해 수정 --- src/main/java/domain/card/CardNumber.java | 4 ++-- src/main/java/domain/card/CardPattern.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/domain/card/CardNumber.java b/src/main/java/domain/card/CardNumber.java index 2de82d7a79f..22cb3606bc4 100644 --- a/src/main/java/domain/card/CardNumber.java +++ b/src/main/java/domain/card/CardNumber.java @@ -17,8 +17,8 @@ public enum CardNumber { QUEEN(10, "Q"), KING(10, "K"); - private int number; - private String court; + private final int number; + private final String court; CardNumber(int number, String court) { this.number = number; diff --git a/src/main/java/domain/card/CardPattern.java b/src/main/java/domain/card/CardPattern.java index 083d3f57d84..abb8024182a 100644 --- a/src/main/java/domain/card/CardPattern.java +++ b/src/main/java/domain/card/CardPattern.java @@ -9,7 +9,7 @@ public enum CardPattern { DIAMOND("다이아몬드"), CLUB("클로버"); - private String name; + private final String name; CardPattern(String name) { this.name = name; From 6b2d824a97513f6ba99baddafb51a5f5ab79f9f4 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Fri, 13 Mar 2026 01:11:33 +0900 Subject: [PATCH 08/53] =?UTF-8?q?refactor(Service):=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=20=EC=B4=88=EA=B8=B0=ED=99=94=20=EC=A0=84=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=EA=B0=80=EB=8A=A5=EC=84=B1=EC=9D=B4=20=EC=9E=88=EC=96=B4=20?= =?UTF-8?q?=EB=B0=A9=EC=A7=80=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 - Cotroller: 메소드명 수정 및 게임 초기화 기능 사용 분기 수정 --- src/main/java/application/BlackjackService.java | 17 ++++++++++++----- .../java/presentation/BlackjackController.java | 13 ++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/java/application/BlackjackService.java b/src/main/java/application/BlackjackService.java index 5a59bab8906..1c81ff42ca6 100644 --- a/src/main/java/application/BlackjackService.java +++ b/src/main/java/application/BlackjackService.java @@ -21,22 +21,29 @@ public void initializeGame(List playerNames) { } public RoundResult startOneRound(String memberName) { - List playerCards = gameTable.drawForMember(memberName); + List playerCards = getGameTable().drawForMember(memberName); - boolean isBust = gameTable.checkBust(memberName); + boolean isBust = getGameTable().checkBust(memberName); return new RoundResult(playerCards, isBust); } public boolean checkDealerDrawable() { - return gameTable.drawForDealer(); + return getGameTable().drawForDealer(); } public List getMemberStatuses() { - return gameTable.checkMemberStatuses(); + return getGameTable().checkMemberStatuses(); } public List getGameResults() { - return gameTable.checkGameResult(); + return getGameTable().checkGameResult(); + } + + private GameTable getGameTable() { + if (gameTable == null) { + throw new IllegalStateException("게임이 초기화가 되지 않았습니다."); + } + return gameTable; } } diff --git a/src/main/java/presentation/BlackjackController.java b/src/main/java/presentation/BlackjackController.java index d7f84d392e2..25eeb4ae5eb 100644 --- a/src/main/java/presentation/BlackjackController.java +++ b/src/main/java/presentation/BlackjackController.java @@ -21,8 +21,8 @@ public BlackjackController(BlackjackService blackjackService, InputView inputVie } public void executeGame() { - List playerNames = getPlayerNames(); - InitialCards(); + List playerNames = inputView.readPlayerNames(); + setUpGame(playerNames); playGame(playerNames); checkDrawableOfDealer(); finalGameStatus(); @@ -52,17 +52,12 @@ private void playGame(List playerNames) { } } - private void InitialCards() { + private void setUpGame(List playerNames) { + blackjackService.initializeGame(playerNames); List memberStatuses = blackjackService.getMemberStatuses(); outputView.printInitialStatus(memberStatuses); } - private List getPlayerNames() { - List playerNames = inputView.readPlayerNames(); - blackjackService.initializeGame(playerNames); - return playerNames; - } - private void playAllRoundOfPlayer(String playerName) { boolean isBust = false; while (!isBust && inputView.playContinue(playerName)) { From 8528c2d22385712b55fbba9640dce377d966110b Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 15:10:39 +0900 Subject: [PATCH 09/53] =?UTF-8?q?refactor(Members):=20=EB=94=9C=EB=9F=AC?= =?UTF-8?q?=EC=99=80=20=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4=EB=A1=9C=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 - 필드와 메소드를 딜러와 플레이어로 분리 - Word: ViewMessage로 이름 변경 및 딜러 이름 제거 --- src/main/java/domain/GameTable.java | 39 +++++---- src/main/java/domain/member/Dealer.java | 12 ++- src/main/java/domain/member/Member.java | 3 +- src/main/java/domain/member/Members.java | 81 ++++++++++--------- src/main/java/domain/member/Player.java | 5 +- src/main/java/dto/MemberStatus.java | 2 +- src/main/java/presentation/ui/InputView.java | 4 +- src/main/java/presentation/ui/OutputView.java | 19 ++--- .../ui/ViewMessage.java} | 11 +-- src/test/java/domain/member/MembersTest.java | 12 +-- 10 files changed, 97 insertions(+), 91 deletions(-) rename src/main/java/{constant/Word.java => presentation/ui/ViewMessage.java} (84%) diff --git a/src/main/java/domain/GameTable.java b/src/main/java/domain/GameTable.java index c4f619ec8eb..3eefe8e5934 100644 --- a/src/main/java/domain/GameTable.java +++ b/src/main/java/domain/GameTable.java @@ -1,6 +1,5 @@ package domain; -import constant.Word; import domain.card.Card; import domain.card.Deck; import dto.GameResult; @@ -8,12 +7,10 @@ import domain.member.Members; import java.util.ArrayList; import java.util.List; -import java.util.Map; public class GameTable { private static final int BLACKJACK = 21; - private static final int DEALER_DRAW_CONDITION = 16; private final Members members; private final Deck deck; @@ -24,46 +21,48 @@ public GameTable(List playerNames, Deck deck) { } public void distributeInitCard() { - for (String memberName : members.getAllMemberName()) { - members.provideCardToMember(memberName, deck.draw()); - members.provideCardToMember(memberName, deck.draw()); + members.provideCardToDealer(deck.draw()); + members.provideCardToDealer(deck.draw()); + for (String memberName : members.getAllPlayerName()) { + members.provideCardToPlayer(memberName, deck.draw()); + members.provideCardToPlayer(memberName, deck.draw()); } } public boolean checkBust(String memberName) { - return members.checkValue(memberName) > BLACKJACK; + return members.checkPlayerScore(memberName) > BLACKJACK; } public List drawForMember(String memberName) { - members.provideCardToMember(memberName, deck.draw()); + members.provideCardToPlayer(memberName, deck.draw()); return members.findCardByName(memberName); } public boolean drawForDealer() { - if (members.checkValue(Word.DEALER.getWord()) <= DEALER_DRAW_CONDITION) { - members.provideCardToMember(Word.DEALER.getWord(), deck.draw()); + if (members.isMeetTheDrawConditionForDealer()) { + members.provideCardToDealer(deck.draw()); return true; } return false; } public List checkMemberStatuses() { - return members.getAllMemberName() - .stream() - .map(name -> { - List cards = members.findCardByName(name); - int totalValue = members.checkValue(name); - return new MemberStatus(name, cards, totalValue); - }).toList(); + List memberStatuses = new ArrayList<>(); + memberStatuses.add( + new MemberStatus(members.getDealerName(), members.findDealerCards(), members.checkDealerScore())); + + members.getAllPlayerName().stream() + .map(name -> new MemberStatus(name, members.findCardByName(name), members.checkPlayerScore(name))) + .forEach(memberStatuses::add); + return List.copyOf(memberStatuses); } public List checkGameResult() { List gameResults = new ArrayList<>(); - gameResults.add(new GameResult(Word.DEALER.getWord(), + gameResults.add(new GameResult(members.getDealerName(), members.determineDealerGameResult())); - List playerResults =members.getAllMemberName().stream() - .filter(name -> !name.equals(Word.DEALER.getWord())) + List playerResults = members.getAllPlayerName().stream() .map(name -> new GameResult(name, List.of(members.determinePlayerGameResult(name)))) .toList(); gameResults.addAll(playerResults); diff --git a/src/main/java/domain/member/Dealer.java b/src/main/java/domain/member/Dealer.java index fe0d431e596..ca8582943e5 100644 --- a/src/main/java/domain/member/Dealer.java +++ b/src/main/java/domain/member/Dealer.java @@ -1,18 +1,22 @@ package domain.member; -import constant.Word; import domain.MatchResult; public class Dealer extends Member { + private static final int DEALER_DRAW_CONDITION = 16; public Dealer() { - super(Word.DEALER.getWord()); + super(DEALER_NAME); + } + + public boolean isMeetTheDrawCondition() { + return currentScore() <= DEALER_DRAW_CONDITION; } @Override public MatchResult compareScoreWith(Member player) { - int dealerScore = currentValue(); - int playerScore = player.currentValue(); + int dealerScore = currentScore(); + int playerScore = player.currentScore(); if (playerScore > BUST_CONDITION) { return MatchResult.WIN; } diff --git a/src/main/java/domain/member/Member.java b/src/main/java/domain/member/Member.java index f81223e8dca..2eb08ad9913 100644 --- a/src/main/java/domain/member/Member.java +++ b/src/main/java/domain/member/Member.java @@ -5,6 +5,7 @@ import java.util.List; public abstract class Member { + protected static final String DEALER_NAME = "딜러"; protected static final int BUST_CONDITION = 21; private final String name; @@ -19,7 +20,7 @@ public String name() { return name; } - public int currentValue() { + public int currentScore() { return hand.calculateTotalValue(); } diff --git a/src/main/java/domain/member/Members.java b/src/main/java/domain/member/Members.java index 03a0387a21e..d33524d0d0a 100644 --- a/src/main/java/domain/member/Members.java +++ b/src/main/java/domain/member/Members.java @@ -1,74 +1,79 @@ package domain.member; -import constant.Word; import domain.MatchResult; import domain.card.Card; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.NoSuchElementException; public class Members { - private final List members; + private final Dealer dealer; + private final List players; public Members(List playerNames) { - this.members = new ArrayList<>(); - join(new Dealer()); - for (String name : playerNames) { - join(new Player(name)); - } + this.dealer = new Dealer(); + this.players = playerNames.stream() + .map(Player::new) + .toList(); } - private void join(Member member) { - members.add(member); + public void provideCardToPlayer(String playerName, Card card) { + Member player = findByPlayerName(playerName); + player.receiveCard(card); } - public void provideCardToMember(String memberName, Card card) { - Member member = findByName(memberName); - member.receiveCard(card); + public void provideCardToDealer(Card card) { + dealer.receiveCard(card); } - public List findCardByName(String memberName) { - Member member = findByName(memberName); - return member.currentCards(); + public List findCardByName(String playerName) { + Member player = findByPlayerName(playerName); + return player.currentCards(); } - public int checkValue(String memberName) { - Member member = findByName(memberName); - return member.currentValue(); + public List findDealerCards() { + return dealer.currentCards(); } - private Member findByName(String memberName) { - return members.stream() - .filter(member -> member.name().equals(memberName)) - .findAny() - .orElseThrow(NoSuchElementException::new); + public int checkPlayerScore(String playerName) { + Member player = findByPlayerName(playerName); + return player.currentScore(); + } + + public int checkDealerScore() { + return dealer.currentScore(); + } + + public boolean isMeetTheDrawConditionForDealer() { + return dealer.isMeetTheDrawCondition(); } - public List getAllMemberName() { - return members.stream() + public List getAllPlayerName() { + return players.stream() .map(Member::name) .toList(); } public List determineDealerGameResult() { - Member dealer = findByName(Word.DEALER.getWord()); - List players = members.stream() - .filter(member -> !member.name().equals(Word.DEALER.getWord())) - .toList(); - List gameResult = new ArrayList<>(); - for (Member player : players) { - gameResult.add(dealer.compareScoreWith(player)); - } - return gameResult; + players.forEach(player -> gameResult.add(dealer.compareScoreWith(player))); + return List.copyOf(gameResult); } public MatchResult determinePlayerGameResult(String name) { - Member dealer = findByName(Word.DEALER.getWord()); - Member player = findByName(name); + Member player = findByPlayerName(name); return player.compareScoreWith(dealer); } + + public String getDealerName() { + return dealer.name(); + } + + private Member findByPlayerName(String playerName) { + return players.stream() + .filter(player -> player.name().equals(playerName)) + .findAny() + .orElseThrow(NoSuchElementException::new); + } } diff --git a/src/main/java/domain/member/Player.java b/src/main/java/domain/member/Player.java index 3b7599bd5af..9c780a756f9 100644 --- a/src/main/java/domain/member/Player.java +++ b/src/main/java/domain/member/Player.java @@ -8,9 +8,10 @@ public Player(String name) { super(name); } + @Override public MatchResult compareScoreWith(Member dealer) { - int playerScore = currentValue(); - int dealerScore = dealer.currentValue(); + int playerScore = currentScore(); + int dealerScore = dealer.currentScore(); if (playerScore > BUST_CONDITION) { return MatchResult.LOSE; diff --git a/src/main/java/dto/MemberStatus.java b/src/main/java/dto/MemberStatus.java index dc761cff824..36141f20ce7 100644 --- a/src/main/java/dto/MemberStatus.java +++ b/src/main/java/dto/MemberStatus.java @@ -4,7 +4,7 @@ import java.util.List; public record MemberStatus( - String playerName, + String memberName, List cards, int totalValue ) { diff --git a/src/main/java/presentation/ui/InputView.java b/src/main/java/presentation/ui/InputView.java index a9fa838d584..20a4d7210f9 100644 --- a/src/main/java/presentation/ui/InputView.java +++ b/src/main/java/presentation/ui/InputView.java @@ -1,7 +1,7 @@ package presentation.ui; -import static constant.Word.CARD_MORD_MESSAGE; -import static constant.Word.PLAYER_NAME_MESSAGE; +import static presentation.ui.ViewMessage.CARD_MORD_MESSAGE; +import static presentation.ui.ViewMessage.PLAYER_NAME_MESSAGE; import java.io.BufferedReader; import java.io.IOException; diff --git a/src/main/java/presentation/ui/OutputView.java b/src/main/java/presentation/ui/OutputView.java index a0cb68cf48e..4b039e1de19 100644 --- a/src/main/java/presentation/ui/OutputView.java +++ b/src/main/java/presentation/ui/OutputView.java @@ -1,6 +1,6 @@ package presentation.ui; -import static constant.Word.*; +import static presentation.ui.ViewMessage.*; import dto.RoundResult; import domain.card.Card; @@ -11,6 +11,7 @@ import java.util.stream.Collectors; public class OutputView { + private static final String DEALER_NAME = "딜러"; public void printInitialStatus(List playerStatuses) { System.out.println(); @@ -42,19 +43,19 @@ public void printGameResult(List gameResults) { private void printDistributeMessage(List playerStatuses) { String playerNames = playerStatuses.stream() - .map(MemberStatus::playerName) - .filter(s -> !s.equals(DEALER.getWord())) + .map(MemberStatus::memberName) + .filter(s -> !s.equals(DEALER_NAME)) .collect(Collectors.joining(", ")); - System.out.println(DISTRIBUTE_MESSAGE.format(DEALER.getWord(), playerNames)); + System.out.println(DISTRIBUTE_MESSAGE.format(DEALER_NAME, playerNames)); } private void printMemberCurrentCard(MemberStatus playerStatus) { - if (playerStatus.playerName().equals(DEALER.getWord())) { + if (playerStatus.memberName().equals(DEALER_NAME)) { printDealerCurrentCard(playerStatus); return; } System.out.println( - playerStatus.playerName() + playerStatus.memberName() + ": " + playerStatus.cards().stream() .map(Card::cardName) @@ -64,7 +65,7 @@ private void printMemberCurrentCard(MemberStatus playerStatus) { private void printDealerCurrentCard(MemberStatus dealerStatus) { System.out.println( - dealerStatus.playerName() + dealerStatus.memberName() + ": " + dealerStatus.cards().getFirst().cardName() ); @@ -72,13 +73,13 @@ private void printDealerCurrentCard(MemberStatus dealerStatus) { private void printFinalMemberCardAndResult(MemberStatus status) { String cards = status.cards().stream().map(Card::cardName).collect(Collectors.joining(", ")); - System.out.println(CARD_STATUS.format(status.playerName(), cards) + RESULT_MESSAGE.format(status.totalValue())); + System.out.println(CARD_STATUS.format(status.memberName(), cards) + RESULT_MESSAGE.format(status.totalValue())); } private void printMemberResult(GameResult gameResult) { String name = gameResult.name(); List results = gameResult.result(); - if (name.equals(DEALER.getWord())) { + if (name.equals(DEALER_NAME)) { printDealerResult(results, name); return; } diff --git a/src/main/java/constant/Word.java b/src/main/java/presentation/ui/ViewMessage.java similarity index 84% rename from src/main/java/constant/Word.java rename to src/main/java/presentation/ui/ViewMessage.java index 857381e51c2..75b196fa4cb 100644 --- a/src/main/java/constant/Word.java +++ b/src/main/java/presentation/ui/ViewMessage.java @@ -1,7 +1,6 @@ -package constant; +package presentation.ui; -public enum Word { - DEALER("딜러"), +public enum ViewMessage { CARD_STATUS("%s카드: %s"), CARD_MORD_MESSAGE("%s는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)"), DEALER_DRAW_MESSAGE("딜러는 16이하라 한장의 카드를 더 받았습니다."), @@ -15,15 +14,11 @@ public enum Word { private final String word; - Word(String word) { + ViewMessage(String word) { this.word = word; } public String format(Object... args) { return String.format(word, args); } - - public String getWord() { - return word; - } } diff --git a/src/test/java/domain/member/MembersTest.java b/src/test/java/domain/member/MembersTest.java index 9e99fdf6e62..9514f97886f 100644 --- a/src/test/java/domain/member/MembersTest.java +++ b/src/test/java/domain/member/MembersTest.java @@ -17,7 +17,7 @@ void getAllMemberNameTest_memberSuccessfullyCreated_containPlayerNames() { Members members = new Members(List.of(player1, player2)); - Assertions.assertThat(members.getAllMemberName()).contains(player1, player2); + Assertions.assertThat(members.getAllPlayerName()).contains(player1, player2); } @DisplayName("해당 멤버에게 카드를 주면 정상적으로 카드를 보유하고 있는지 테스트") @@ -27,7 +27,7 @@ void provideCardToMember_cardGivenToMember_containGivenCard() { Members members = new Members(List.of(playerName)); Card card = new Card("2", "하트"); - members.provideCardToMember(playerName, card); + members.provideCardToPlayer(playerName, card); Assertions.assertThat(members.findCardByName(playerName)).contains(card); } @@ -41,8 +41,8 @@ void determineDealerGameResult_compareWithPlayer_returnMatchResult() { List playerCards = List.of(new Card("2", "하트"), new Card("3", "하트")); MatchResult expected = MatchResult.WIN; - dealerCards.forEach(card -> members.provideCardToMember("딜러", card)); - playerCards.forEach(card -> members.provideCardToMember(player, card)); + dealerCards.forEach(members::provideCardToDealer); + playerCards.forEach(card -> members.provideCardToPlayer(player, card)); Assertions.assertThat(members.determineDealerGameResult()).containsExactly(expected); } @@ -56,8 +56,8 @@ void determinePlayerGameResult_compareWithDealer_returnMatchResult() { List playerCards = List.of(new Card("2", "하트"), new Card("3", "하트")); MatchResult expected = MatchResult.LOSE; - dealerCards.forEach(card -> members.provideCardToMember("딜러", card)); - playerCards.forEach(card -> members.provideCardToMember(player, card)); + dealerCards.forEach(members::provideCardToDealer); + playerCards.forEach(card -> members.provideCardToPlayer(player, card)); Assertions.assertThat(members.determinePlayerGameResult(player)).isEqualTo(expected); } From 9f838fbee3815b2b8620e213ef681368db6fd9ec Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 15:28:09 +0900 Subject: [PATCH 10/53] =?UTF-8?q?refactor(Deck):=20=EB=8D=B1=20=EC=85=94?= =?UTF-8?q?=ED=94=8C=20=EC=8B=9C=20=EA=B0=95=EC=A0=9C=20=EC=BA=90=EC=8A=A4?= =?UTF-8?q?=ED=8C=85=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/card/StandardDeck.java | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/main/java/domain/card/StandardDeck.java b/src/main/java/domain/card/StandardDeck.java index c18f130eb87..47592aae0af 100644 --- a/src/main/java/domain/card/StandardDeck.java +++ b/src/main/java/domain/card/StandardDeck.java @@ -1,10 +1,12 @@ package domain.card; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import java.util.Queue; +import java.util.stream.Collectors; public class StandardDeck implements Deck { @@ -16,16 +18,12 @@ public StandardDeck() { } private void init() { - for (CardPattern cardPattern : CardPattern.values()) { - makeCard(cardPattern.getName()); - } - Collections.shuffle((List) queue); - } - - private void makeCard(String cardPattern) { - for (CardNumber cardNumber : CardNumber.values()) { - queue.add(new Card(cardNumber.getCourt(), cardPattern)); - } + List cards = Arrays.stream(CardPattern.values()) + .flatMap(pattern -> Arrays.stream(CardNumber.values()) + .map(number -> new Card(number.getCourt(), pattern.getName()))) + .collect(Collectors.toList()); + Collections.shuffle(cards); + queue.addAll(cards); } @Override From 45e3509ddd93a9fa86f360b7731d67dc1b80a5fd Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 16:32:40 +0900 Subject: [PATCH 11/53] =?UTF-8?q?feat(ValidatedInput):=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=EA=B0=92=20=EA=B2=80=EC=A6=9D=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/presentation/ui/InputView.java | 8 +++-- .../java/presentation/ui/ValidatedInput.java | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/main/java/presentation/ui/ValidatedInput.java diff --git a/src/main/java/presentation/ui/InputView.java b/src/main/java/presentation/ui/InputView.java index 20a4d7210f9..db01cc8e962 100644 --- a/src/main/java/presentation/ui/InputView.java +++ b/src/main/java/presentation/ui/InputView.java @@ -12,17 +12,21 @@ public class InputView { private final BufferedReader bufferedReader; + private final ValidatedInput validatedInput; public InputView() { this.bufferedReader = new BufferedReader(new InputStreamReader(System.in)); + this.validatedInput = new ValidatedInput(); } public List readPlayerNames() { try { System.out.println(PLAYER_NAME_MESSAGE.format()); - return Stream.of(bufferedReader.readLine().split(",")) - .map(String::trim) + List playerNames = Stream.of(bufferedReader.readLine().split(",")) + .map(String::strip) .toList(); + validatedInput.validatePlayerName(playerNames); + return playerNames; } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/java/presentation/ui/ValidatedInput.java b/src/main/java/presentation/ui/ValidatedInput.java new file mode 100644 index 00000000000..bd350b2543d --- /dev/null +++ b/src/main/java/presentation/ui/ValidatedInput.java @@ -0,0 +1,30 @@ +package presentation.ui; + +import java.util.List; + +public class ValidatedInput { + private static final String DEALER_NAME = "딜러"; + + public void validatePlayerName(List playerNames) { + playerNames.forEach(name -> { + validateIsNotNumber(name); + validateIsNotDealerName(name); + }); + } + + private void validateIsNotNumber(String playerName) { + if (isNumeric(playerName)) { + throw new IllegalArgumentException("플레이어 이름은 숫자로 설정할 수 없습니다."); + } + } + + private boolean isNumeric(String str) { + return str != null && str.matches("\\d+"); + } + + private void validateIsNotDealerName(String playerName) { + if (DEALER_NAME.equals(playerName)) { + throw new IllegalArgumentException("플레이어의 이름을 '딜러'로 설정할 수 없습니다."); + } + } +} From bc0a00aeb55598a7cd0a5ad508956f822db785e5 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 20:33:25 +0900 Subject: [PATCH 12/53] =?UTF-8?q?feat(InputView):=20=EB=B2=A0=ED=8C=85=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=20=EC=9E=85=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/presentation/ui/InputView.java | 11 +++++++++++ src/main/java/presentation/ui/ViewMessage.java | 1 + 2 files changed, 12 insertions(+) diff --git a/src/main/java/presentation/ui/InputView.java b/src/main/java/presentation/ui/InputView.java index db01cc8e962..bd4742e6b1a 100644 --- a/src/main/java/presentation/ui/InputView.java +++ b/src/main/java/presentation/ui/InputView.java @@ -1,6 +1,7 @@ package presentation.ui; import static presentation.ui.ViewMessage.CARD_MORD_MESSAGE; +import static presentation.ui.ViewMessage.PLAYER_BET_AMOUNT_MESSAGE; import static presentation.ui.ViewMessage.PLAYER_NAME_MESSAGE; import java.io.BufferedReader; @@ -32,6 +33,16 @@ public List readPlayerNames() { } } + public int readPlayerBetAmount(String name) { + try { + System.out.println(PLAYER_BET_AMOUNT_MESSAGE.format(name)); + String inputBetAmount = bufferedReader.readLine(); + return Integer.parseInt(inputBetAmount); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public Boolean playContinue(String playerName) { System.out.println(CARD_MORD_MESSAGE.format(playerName)); try { diff --git a/src/main/java/presentation/ui/ViewMessage.java b/src/main/java/presentation/ui/ViewMessage.java index 75b196fa4cb..128be152a8c 100644 --- a/src/main/java/presentation/ui/ViewMessage.java +++ b/src/main/java/presentation/ui/ViewMessage.java @@ -7,6 +7,7 @@ public enum ViewMessage { RESULT_MESSAGE(" - 결과: %d"), DISTRIBUTE_MESSAGE("%s와 %s에게 2장을 나누었습니다."), PLAYER_NAME_MESSAGE("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"), + PLAYER_BET_AMOUNT_MESSAGE("%s의 배팅 금액은?"), FINAL_GAME_RESULT_MESSAGE("## 최종 승패"), PLAYER_GAME_WIN("%s: 승"), PLAYER_GAME_DRAW("%s: 무"), From 3308686cdeefd38924a42814a5bad8227123c2b6 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 21:10:18 +0900 Subject: [PATCH 13/53] =?UTF-8?q?docs(README):=20Cycle2=20=EB=AF=B8?= =?UTF-8?q?=EC=85=98=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 --- README.md | 203 ++++++++++++++++++------------------------------------ 1 file changed, 66 insertions(+), 137 deletions(-) diff --git a/README.md b/README.md index a2a73235ee1..32c45c07c91 100644 --- a/README.md +++ b/README.md @@ -1,113 +1,59 @@ # java-blackjack 블랙잭 미션 저장소 -## 기능 요구사항 +## 구현할 기능 목록 ---- -블랙잭 게임을 변형한 프로그램을 구현한다. 블랙잭 게임은 **딜러**와 **플레이어** 중 카드의 합이 **21** 또는 **21에 가장 가까운 숫자**를 가지는 쪽이 이기는 게임이다. - -카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다. -게임을 시작하면 플레이어는 **두 장의 카드**를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. -21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. -딜러는 처음에 받은 2장의 합계가 **16이하**이면 반드시 1장의 카드를 추가로 받아야 하고, **17점 이상**이면 추가로 받을 수 없다. -게임을 완료한 후 각 플레이어별로 승패를 출력한다. - -### Card -#### field -- CardNumber(Enum class) - - 숫자 검증 및 반환 -- CardPattern(Enum class) - - 패턴 검증 및 반환 -#### method ---- -### Hand(1급 컬렉션) -#### field -- cards -#### method -- append Card (카드 추가) -- 전체 값(필드로 두지는 않고, 계산으로 반환) 계산 calculate -- Ace 처리 -- 문양과 숫자가 중복되는지 검사 ---- -### Member -#### field -- name -- Hand - -#### method -- 총 스코어 반환 -- 모든 카드 반환 -- 카드 받기 -- 점수 비교 ---- +### 1. 카드 도메인 고도화 +- [ ] **카드 캐싱(Flyweight Pattern)**: 52장의 카드를 미리 생성하고 재사용한다. +- [ ] **셔플 전략 주입**: 테스트 가능하도록 셔플 로직을 인터페이스로 분리한다. -### Player, Dealer -#### method -- 점수 비교 재정의 - -### Members(1급 컬렉션) -#### field -- members - -#### method -- Member 추가 -- Member 카드 반환 -- Member 스코어 반환 -- Member 반환 -- 딜러 게임 결과 반환 -- 모든 플레이어 게임 결과 반환 - -### GameTable -#### field -- Members - -#### method -- 초기 카드 배분 -- 플레이어 카드 뽑기 -- 딜러가 카드를 뽑아야 되는지 확인 -- Bust 체크 -- 모든 멤버 상태 반환 -- 모든 멤버 결과 반환 - -### Service -- 게임 초기화 -- 멤버 게임 1라운드 실행 -- 딜러가 카드를 뽑아야 되는지 확인 -- 모든 멤버 상태 반환 -- 모든 멤버 결과 반환 +### 2. 베팅 도메인 추가 +- [ ] **금액(Money) 객체**: 베팅 금액 원시 값을 포장하고, 수익률에 따른 계산 로직을 가진다. +- [ ] **플레이어별 베팅**: 게임 시작 시 플레이어별로 베팅 금액을 입력받는다. -``` -딜러카드: 3다이아몬드, 9클로버, 8다이아몬드 - 결과: 20 -pobi카드: 2하트, 8스페이드, A클로버 - 결과: 21 -jason카드: 7클로버, K스페이드 - 결과: 17 -``` -- finalResult() -``` - ## 최종 승패 - 딜러: 1승 1패 - pobi: 승 - jason: 패 - ``` +### 3. 상태(State) 패턴 기반 로직 리팩토링 +- [ ] **상태 추상화**: `Started`, `Running`, `Finished` 등 게임의 상태를 객체로 정의한다. +- [ ] **상태 전이 구현**: + - `Hit` 상태에서 `draw` 호출 시 점수에 따라 `Hit` 혹은 `Bust`로 전이한다. + - `Hit` 상태에서 `stay` 호출 시 `Stay` 상태로 전이한다. +- [ ] **수익률(Earnings Rate) 캡슐화**: + - `Blackjack`: 1.5배 수익률 반환 + - `Bust`: -1.0배 수익률 반환 + - `Stay`: 딜러와 비교 결과에 따라 수익률 반환 -### Controller -- inputView로 입력받는 멤버 추가 (join 호출) -- GameTable에서 멤버 리스트를 받아와서 멤버별 draw의사 판별 반복 +### 4. 게임 진행 및 정산 +- [ ] **수익 계산**: 게임 종료 후 플레이어의 상태에 따른 최종 수익을 계산한다. +- [ ] **딜러 최종 수익**: 모든 플레이어 수익의 합에 -1을 곱한 값을 도출한다. ---- -### Deck -#### method -- draw -- Card Generator 52 queue에 초기화 +### 5. UI 및 출력 +- [ ] 각 플레이어의 베팅 금액 입력 기능 +- [ ] 최종 결과 시 플레이어별/딜러 수익 합계 출력 + + +## 기능 요구사항 +블랙잭 게임을 변형한 프로그램을 구현한다. 블랙잭 게임은 딜러와 플레이어 중 카드의 합이 21 또는 21에 가장 가까운 숫자를 가지는 쪽이 이기는 게임이다. + +- 플레이어는 게임을 시작할 때 배팅 금액을 정해야 한다. +- 카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다. +- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. 단, 카드를 추가로 뽑아 21을 초과할 경우 배팅 금액을 모두 잃게 된다. +- 처음 두 장의 카드 합이 21일 경우 블랙잭이 되면 베팅 금액의 1.5 배를 딜러에게 받는다. 딜러와 플레이어가 모두 동시에 블랙잭인 경우 플레이어는 베팅한 금액을 돌려받는다. +- 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다. 딜러가 21을 초과하면 그 시점까지 남아 있던 플레이어들은 가지고 있는 패에 상관 없이 승리해 베팅 금액을 받는다. -## 실행 결과 +### 실행 결과 --- ``` 게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리) pobi,jason +pobi의 배팅 금액은? +10000 + +jason의 배팅 금액은? +20000 + 딜러와 pobi, jason에게 2장을 나누었습니다. -딜러카드: 3다이아몬드 +딜러: 3다이아몬드 pobi카드: 2하트, 8스페이드 jason카드: 7클로버, K스페이드 @@ -116,82 +62,65 @@ y pobi카드: 2하트, 8스페이드, A클로버 pobi는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) n -jason는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) +pobi카드: 2하트, 8스페이드, A클로버 +jason은 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) n jason카드: 7클로버, K스페이드 딜러는 16이하라 한장의 카드를 더 받았습니다. -딜러카드: 3다이아몬드, 9클로버, 8다이아몬드 - 결과: 20 +딜러 카드: 3다이아몬드, 9클로버, 8다이아몬드 - 결과: 20 pobi카드: 2하트, 8스페이드, A클로버 - 결과: 21 jason카드: 7클로버, K스페이드 - 결과: 17 -## 최종 승패 -딜러: 1승 1패 -pobi: 승 -jason: 패 +## 최종 수익 +딜러: 10000 +pobi: 10000 +jason: -20000 ``` ## 프로그래밍 요구 사항 --- - 자바 코드 컨벤션을 지키면서 프로그래밍한다. -- 기본적으로 Java Style Guide을 원칙으로 한다. -- indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다. 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. -힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다. + - 기본적으로 Java Style Guide을 원칙으로 한다. +- indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다. + - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. + - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다. - 3항 연산자를 쓰지 않는다. -- else 예약어를 쓰지 않는다. else 예약어를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다. -힌트: if문에서 값을 반환하는 방식으로 구현하면 else 예약어를 사용하지 않아도 된다. +- else 예약어를 쓰지 않는다. + - else 예약어를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다. + - 힌트: if문에서 값을 반환하는 방식으로 구현하면 else 예약어를 사용하지 않아도 된다. - 모든 기능을 TDD로 구현해 단위 테스트가 존재해야 한다. 단, UI(System.out, System.in) 로직은 제외 -- 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다. -- UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다. + - 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다. + - UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다. - 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다. -- 함수(또는 메소드)가 한 가지 일만 하도록 최대한 작게 만들어라. + - 함수(또는 메소드)가 한 가지 일만 하도록 최대한 작게 만들어라. - 배열 대신 컬렉션을 사용한다. - 모든 원시 값과 문자열을 포장한다. - 줄여 쓰지 않는다(축약 금지). - 일급 컬렉션을 쓴다. - -## 추가된 요구 사항 - ---- - 모든 엔티티를 작게 유지한다. - 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다. - 딜러와 플레이어에서 발생하는 중복 코드를 제거해야 한다. -## 미션 중 할 일 - ---- -토론 활동에서 정한 규칙을 의식하며 코드 작성 -규칙 때문에 코드를 변경한 곳 기록 -막히는 순간 기록 - ## 미션 중 기록 --- 필수 기록: -- [x] 규칙을 적용해서 변경한 코드 1곳 이상 +- [ ] 기능 추가로 인해 수정한 위치 개수 ```markdown -처음에 테스트 메소드 명을 작성할 때 우리 조가 정한 규칙은 다음과 같다. -아래의 포맷으로 테스트 메서드 작성 (한/영 상관없음) -- 메서드명_테스트상태_기대행위 -- 메서드명_기대행위_테스트상태 -그래서 기존처럼 테스트 코드를 막 지었다가 수정 했다. + ``` -- [x] 테스트 작성이 어려웠던 코드 1곳 이상 +- [ ] 사이클1 때보다 수정 범위가 줄었는가/늘었는가 ```markdown -Deck을 처음에 52장의 트럼프 카드를 넣고 shuffle을 시켜서 draw()를 테스트 하는게 어려웠다. --> Deck을 인터페이스로 구현하고 draw 전략을 외부에서 주입해주는 방식을 사용하여 해결하였다. + ``` -- [x] 막힌 순간 1회 이상 +- [ ] 규칙 적용으로 변경한 코드 1곳 ```markdown -기존 Member 코드에서 딜러만 사용하는 메소드, 플레이어만 사용하는 메소드가 많아서 -어떻게 분리할지 고민하다가 LMS에 '변경 가능한 프로그램 만들기'를 읽고, 상속으로 해결하였다. -``` -## 미션 완료 조건 +``` +- [ ] 테스트가 설계를 도운 순간 1회 +```markdown ---- -- [x] 요구사항 구현 -- [x] 규칙에 의한 코드 변경 1회 이상 -- [x] 미션 중 기록 작성 +``` \ No newline at end of file From 37ec0628fae0fea3c28ba87d87dd10e104659611 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 21:41:44 +0900 Subject: [PATCH 14/53] =?UTF-8?q?test(Card):=20=EA=B0=99=EC=9D=80=20?= =?UTF-8?q?=EC=A3=BC=EC=86=8C=EC=9D=98=20=EC=B9=B4=EB=93=9C=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=EC=A7=80=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=ED=95=98=EB=8A=94=20=ED=85=8C=EC=8A=A4=ED=8A=B8=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/test/java/domain/card/CardTest.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/test/java/domain/card/CardTest.java diff --git a/src/test/java/domain/card/CardTest.java b/src/test/java/domain/card/CardTest.java new file mode 100644 index 00000000000..d7d94225d19 --- /dev/null +++ b/src/test/java/domain/card/CardTest.java @@ -0,0 +1,18 @@ +package domain.card; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CardTest { + + @DisplayName("동일한 숫자와 문양의 카드를 요청하면 같은 인스턴스를 반환한다") + @Test + void staticFactoryMethod_SameInstance_ReturnTrue() { + Card card1 = Card.from("A", "스페이드"); + Card card2 = Card.from("A", "스페이드"); + + assertThat(card1).isSameAs(card2); + } +} From 63d3e9130fafb0a8310f9f24c4a7d2e264813170 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 21:44:17 +0900 Subject: [PATCH 15/53] =?UTF-8?q?feat(Card):=20=EC=A0=95=EC=A0=81=20?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A6=AC=20=EB=A9=94=EC=86=8C=EB=93=9C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 52가지 카드를 저장하도록 구현 - 팩토리 메소드는 저장소에서 get하여 반환 - 52가지 카드를 매번 생성하고 지워야할 일이 없어짐 --- src/main/java/domain/card/Card.java | 26 +++++++++++++++++++++ src/main/java/domain/card/StandardDeck.java | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/java/domain/card/Card.java b/src/main/java/domain/card/Card.java index dc824eed0d1..07a8c67185b 100644 --- a/src/main/java/domain/card/Card.java +++ b/src/main/java/domain/card/Card.java @@ -1,9 +1,23 @@ package domain.card; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; public class Card { + private static final Map CACHE = new HashMap<>(); + + static { + Arrays.stream(CardPattern.values()) + .forEach(pattern -> Arrays.stream(CardNumber.values()) + .forEach(number -> { + String key = generateKey(number.getCourt(), pattern.getName()); + CACHE.put(key, new Card(number.getCourt(), pattern.getName())); + })); + } + private final CardPattern pattern; private final CardNumber number; @@ -12,6 +26,18 @@ public Card(String number, String pattern) { this.pattern = CardPattern.matchCardPattern(pattern); } + public static Card from(String number, String pattern) { + String key = generateKey(number, pattern); + if (!CACHE.containsKey(key)) { + throw new IllegalArgumentException("존재하지 않는 카드 조합입니다: " + key); + } + return CACHE.get(key); + } + + private static String generateKey(String number, String pattern) { + return number + pattern; + } + public int number() { return number.getValue(); } diff --git a/src/main/java/domain/card/StandardDeck.java b/src/main/java/domain/card/StandardDeck.java index 47592aae0af..fc38fb78f65 100644 --- a/src/main/java/domain/card/StandardDeck.java +++ b/src/main/java/domain/card/StandardDeck.java @@ -20,7 +20,7 @@ public StandardDeck() { private void init() { List cards = Arrays.stream(CardPattern.values()) .flatMap(pattern -> Arrays.stream(CardNumber.values()) - .map(number -> new Card(number.getCourt(), pattern.getName()))) + .map(number -> Card.from(number.getCourt(), pattern.getName()))) .collect(Collectors.toList()); Collections.shuffle(cards); queue.addAll(cards); From bd434387d98343904180659abc8a51cd642bbed0 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 22:00:45 +0900 Subject: [PATCH 16/53] =?UTF-8?q?test(Money):=20VO=20=EA=B2=80=EC=A6=9D,?= =?UTF-8?q?=20=EC=98=88=EC=99=B8,=20=EC=88=98=EC=9D=B5=EA=B8=88=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8=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/test/java/domain/card/MoneyTest.java | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/test/java/domain/card/MoneyTest.java diff --git a/src/test/java/domain/card/MoneyTest.java b/src/test/java/domain/card/MoneyTest.java new file mode 100644 index 00000000000..9cd878adcd5 --- /dev/null +++ b/src/test/java/domain/card/MoneyTest.java @@ -0,0 +1,34 @@ +package domain.card; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class MoneyTest { + + @DisplayName("금액이 같으면 같은 객체로 취급한다 (VO)") + @Test + void equals_SameAmount_ReturnTrue() { + assertThat(new Money(10000)).isEqualTo(new Money(10000)); + } + + @DisplayName("베팅 금액이 0원 이하이면 예외가 발생한다") + @Test + void constructor_UnderZero_ThrowException() { + assertThatThrownBy(() -> new Money(0)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("수익률을 곱해 최종 수익금을 계산한다") + @Test + void multiply_EarningRate_ReturnProfit() { + Money bet = new Money(10000); + double earningRate = 1.5; + + int profit = bet.calculateProfit(earningRate); + + assertThat(profit).isEqualTo(15000); + } +} From e429be546b8a5169c4a57926fa98455985f90451 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 22:03:51 +0900 Subject: [PATCH 17/53] =?UTF-8?q?feat(Money):=20VO,=20=EC=98=88=EC=99=B8,?= =?UTF-8?q?=20=EC=88=98=EC=9D=B5=EA=B8=88=20=EA=B3=84=EC=82=B0=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VO: 같은 금액일 때 같은 객체로 취급하도록 구현 (같은 가치 취급) - 예외: 베팅 금액이 0원 미만이면 예외 발생 - 수익금 계산: 수익률을 받아 수익금 계산 --- src/main/java/domain/card/Money.java | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/main/java/domain/card/Money.java diff --git a/src/main/java/domain/card/Money.java b/src/main/java/domain/card/Money.java new file mode 100644 index 00000000000..d8dcc0e5f74 --- /dev/null +++ b/src/main/java/domain/card/Money.java @@ -0,0 +1,34 @@ +package domain.card; + +import java.util.Objects; + +public class Money { + + private final int amount; + + public Money(int amount) { + validateUnderZero(amount); + this.amount = amount; + } + + private void validateUnderZero(int amount) { + if (amount <= 0) { + throw new IllegalArgumentException("베팅 금액은 0보다 커야 합니다."); + } + } + + public int calculateProfit(double earningRate) { + return (int) (amount * earningRate); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Money money)) return false; + return amount == money.amount; + } + + @Override + public int hashCode() { + return Objects.hashCode(amount); + } +} From 9d85b367537ae6129fda8952e4edb3951550b5fb Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 22:45:14 +0900 Subject: [PATCH 18/53] =?UTF-8?q?test(State):=20Bust=EC=99=80=20Blackjack?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=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 --- .../java/domain/state/FinishedStateTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/test/java/domain/state/FinishedStateTest.java diff --git a/src/test/java/domain/state/FinishedStateTest.java b/src/test/java/domain/state/FinishedStateTest.java new file mode 100644 index 00000000000..60e3ff749b5 --- /dev/null +++ b/src/test/java/domain/state/FinishedStateTest.java @@ -0,0 +1,26 @@ +package domain.state; + +import domain.member.Hand; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class FinishedStateTest { + + @DisplayName("Bust 상태는 수익률 -1.0을 반환하고 종료 상태이다") + @Test + void bust_EarningRate_IsMinusOne() { + State state = new Bust(new Hand()); + assertThat(state.isFinished()).isTrue(); + assertThat(state.earningRate()).isEqualTo(-1.0); + } + + @DisplayName("Blackjack 상태는 수익률 1.5을 반환하고 종료 상태이다") + @Test + void blackjack_EarningRate_IsOnePointFive() { + State state = new Blackjack(new Hand()); + assertThat(state.isFinished()).isTrue(); + assertThat(state.earningRate()).isEqualTo(1.5); + } +} From a9798d8adb948c4a04c5a805982bfc4ba2029cdc Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 22:48:27 +0900 Subject: [PATCH 19/53] =?UTF-8?q?feat(State):=20=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=EC=A2=85=EB=A3=8C=20=EC=83=81=ED=83=9C=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20State=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 상태 패턴 도입을 위해 State 계층 및 Started, Finished 추상 클래스 구현 - 종료 상태인 Bust와 Blackjack 구현 --- src/main/java/domain/state/Blackjack.java | 15 +++++++++++++ src/main/java/domain/state/Bust.java | 15 +++++++++++++ src/main/java/domain/state/Finished.java | 26 +++++++++++++++++++++++ src/main/java/domain/state/Started.java | 16 ++++++++++++++ src/main/java/domain/state/State.java | 12 +++++++++++ 5 files changed, 84 insertions(+) create mode 100644 src/main/java/domain/state/Blackjack.java create mode 100644 src/main/java/domain/state/Bust.java create mode 100644 src/main/java/domain/state/Finished.java create mode 100644 src/main/java/domain/state/Started.java create mode 100644 src/main/java/domain/state/State.java diff --git a/src/main/java/domain/state/Blackjack.java b/src/main/java/domain/state/Blackjack.java new file mode 100644 index 00000000000..dce07ec0161 --- /dev/null +++ b/src/main/java/domain/state/Blackjack.java @@ -0,0 +1,15 @@ +package domain.state; + +import domain.member.Hand; + +public class Blackjack extends Finished { + + public Blackjack(Hand hand) { + super(hand); + } + + @Override + public double earningRate() { + return 1.5; + } +} diff --git a/src/main/java/domain/state/Bust.java b/src/main/java/domain/state/Bust.java new file mode 100644 index 00000000000..20b099601b8 --- /dev/null +++ b/src/main/java/domain/state/Bust.java @@ -0,0 +1,15 @@ +package domain.state; + +import domain.member.Hand; + +public class Bust extends Finished { + + public Bust(Hand hand) { + super(hand); + } + + @Override + public double earningRate() { + return -1.0; + } +} diff --git a/src/main/java/domain/state/Finished.java b/src/main/java/domain/state/Finished.java new file mode 100644 index 00000000000..13c60123cac --- /dev/null +++ b/src/main/java/domain/state/Finished.java @@ -0,0 +1,26 @@ +package domain.state; + +import domain.card.Card; +import domain.member.Hand; + +public abstract class Finished extends Started { + + public Finished(Hand hand) { + super(hand); + } + + @Override + public boolean isFinished() { + return true; + } + + @Override + public State draw(Card card) { + throw new IllegalArgumentException("이미 종료된 상태입니다."); + } + + @Override + public State stay() { + throw new IllegalArgumentException("이미 종료된 상태입니다."); + } +} diff --git a/src/main/java/domain/state/Started.java b/src/main/java/domain/state/Started.java new file mode 100644 index 00000000000..1030d1edacf --- /dev/null +++ b/src/main/java/domain/state/Started.java @@ -0,0 +1,16 @@ +package domain.state; + +import domain.member.Hand; + +public abstract class Started implements State { + protected final Hand hand; + + protected Started(Hand hand) { + this.hand = hand; + } + + @Override + public Hand hand() { + return hand; + } +} diff --git a/src/main/java/domain/state/State.java b/src/main/java/domain/state/State.java new file mode 100644 index 00000000000..1d003160697 --- /dev/null +++ b/src/main/java/domain/state/State.java @@ -0,0 +1,12 @@ +package domain.state; + +import domain.card.Card; +import domain.member.Hand; + +public interface State { + boolean isFinished(); + double earningRate(); + State draw(Card card); + State stay(); + Hand hand(); +} From c496ce9f988957f0cdda906e939734a4369f1a18 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 23:11:42 +0900 Subject: [PATCH 20/53] =?UTF-8?q?test(State):=20Hit=20=EC=9D=B4=ED=9B=84?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=20=EC=A0=84=ED=99=98=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/state/HitTest.java | 43 +++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/test/java/domain/state/HitTest.java diff --git a/src/test/java/domain/state/HitTest.java b/src/test/java/domain/state/HitTest.java new file mode 100644 index 00000000000..4d63fdb46c0 --- /dev/null +++ b/src/test/java/domain/state/HitTest.java @@ -0,0 +1,43 @@ +package domain.state; + +import domain.card.Card; +import domain.member.Hand; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HitTest { + + @DisplayName("Hit 상태에서 카드를 뽑아 21 이하이면 다시 Hit 상태를 반환한다") + @Test + void draw_Under21_ReturnHit() { + State state = new Hit(new Hand()); + + State nextState = state.draw(Card.from("2", "스페이드")); + + assertThat(nextState).isInstanceOf(Hit.class); + } + + @DisplayName("Hit 상태에서 카드를 뽑아 21을 초과하면 Bust 상태를 반환한다") + @Test + void draw_Over21_ReturnBust() { + Hand hand = new Hand(); + hand.appendCard(Card.from("10", "스페이드")); + hand.appendCard(Card.from("10", "하트")); + State state = new Hit(hand); + + State nextState = state.draw(Card.from("2", "클로버")); + + assertThat(nextState).isInstanceOf(Bust.class); + } + + @DisplayName("Hit 상태에서 stay를 호출하면 Stay 상태를 반환한다") + @Test + void stay_ReturnStay() { + State state = new Hit(new Hand()); + State nextState = state.stay(); + + assertThat(nextState).isInstanceOf(Stay.class); + } +} From 96fa9fe2dffc4d418f2bc3b15f7501783a7d93ae Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 23:31:32 +0900 Subject: [PATCH 21/53] =?UTF-8?q?test(State):=20Stay=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EC=9D=98=20=EC=A2=85=EB=A3=8C=20=EC=97=AC=EB=B6=80=20=EB=B0=8F?= =?UTF-8?q?=20=EC=88=98=EC=9D=B5=EB=A5=A0,=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/state/StayTest.java | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/test/java/domain/state/StayTest.java diff --git a/src/test/java/domain/state/StayTest.java b/src/test/java/domain/state/StayTest.java new file mode 100644 index 00000000000..1ff62ebb6d4 --- /dev/null +++ b/src/test/java/domain/state/StayTest.java @@ -0,0 +1,33 @@ +package domain.state; + +import domain.member.Hand; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class StayTest { + + @DisplayName("Stay 상태는 종료된 상태이다") + @Test + void isFinished_Always_ReturnTrue() { + State state = new Stay(new Hand()); + assertThat(state.isFinished()).isTrue(); + } + + @DisplayName("Stay 상태의 기본 수익률은 1.0이다") + @Test + void earningRate_Always_ReturnOnePointZero() { + State state = new Stay(new Hand()); + assertThat(state.earningRate()).isEqualTo(1.0); + } + + @DisplayName("Stay 상태에서 카드를 더 뽑으려 하면 예외가 발생한다") + @Test + void draw_WhenCalled_ThrowsException() { + State state = new Stay(new Hand()); + assertThatThrownBy(() -> state.draw(null)) + .isInstanceOf(IllegalArgumentException.class); + } +} From 880fc5d3bbbf43754115cfdae8215f255bc704a4 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 23:32:16 +0900 Subject: [PATCH 22/53] =?UTF-8?q?feat(State):=20Stay=20=EC=A2=85=EB=A3=8C?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=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/state/Stay.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/domain/state/Stay.java diff --git a/src/main/java/domain/state/Stay.java b/src/main/java/domain/state/Stay.java new file mode 100644 index 00000000000..8c92c9a9c97 --- /dev/null +++ b/src/main/java/domain/state/Stay.java @@ -0,0 +1,17 @@ +package domain.state; + +import domain.member.Hand; + +public class Stay extends Finished { + + public Stay(Hand hand) { + super(hand); + } + + @Override + public double earningRate() { + // 기본 수익률만 작성 + // 이후 딜러와 비교하는 로직을 추가할 예정 + return 1.0; + } +} From 2af5859ebbbd4c7bdc4659a6df6d77966db5ca62 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 23:32:46 +0900 Subject: [PATCH 23/53] =?UTF-8?q?test(State):=20Hit=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=20=EB=B0=8F=20Running=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/state/Hit.java | 27 +++++++++++++++++++++++++ src/main/java/domain/state/Running.java | 20 ++++++++++++++++++ src/test/java/domain/state/HitTest.java | 11 +++++----- 3 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 src/main/java/domain/state/Hit.java create mode 100644 src/main/java/domain/state/Running.java diff --git a/src/main/java/domain/state/Hit.java b/src/main/java/domain/state/Hit.java new file mode 100644 index 00000000000..61152433386 --- /dev/null +++ b/src/main/java/domain/state/Hit.java @@ -0,0 +1,27 @@ +package domain.state; + +import domain.card.Card; +import domain.member.Hand; + +public class Hit extends Running { + private static final int BUST_CONDITION = 21; + + public Hit(Hand hand) { + super(hand); + } + + @Override + public State draw(Card card) { + Hand newHand = hand.appendCard(card); + + if (newHand.calculateTotalValue() > BUST_CONDITION) { + return new Bust(newHand); + } + return new Hit(newHand); + } + + @Override + public State stay() { + return new Stay(hand); + } +} diff --git a/src/main/java/domain/state/Running.java b/src/main/java/domain/state/Running.java new file mode 100644 index 00000000000..f28c49a7bab --- /dev/null +++ b/src/main/java/domain/state/Running.java @@ -0,0 +1,20 @@ +package domain.state; + +import domain.member.Hand; + +public abstract class Running extends Started { + + public Running(Hand hand) { + super(hand); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public double earningRate() { + return 0; + } +} diff --git a/src/test/java/domain/state/HitTest.java b/src/test/java/domain/state/HitTest.java index 4d63fdb46c0..1f7cce33ae7 100644 --- a/src/test/java/domain/state/HitTest.java +++ b/src/test/java/domain/state/HitTest.java @@ -11,7 +11,7 @@ public class HitTest { @DisplayName("Hit 상태에서 카드를 뽑아 21 이하이면 다시 Hit 상태를 반환한다") @Test - void draw_Under21_ReturnHit() { + void draw_TotalScoreLessThanOrEqualTo21_ReturnHit() { State state = new Hit(new Hand()); State nextState = state.draw(Card.from("2", "스페이드")); @@ -21,10 +21,9 @@ void draw_Under21_ReturnHit() { @DisplayName("Hit 상태에서 카드를 뽑아 21을 초과하면 Bust 상태를 반환한다") @Test - void draw_Over21_ReturnBust() { - Hand hand = new Hand(); - hand.appendCard(Card.from("10", "스페이드")); - hand.appendCard(Card.from("10", "하트")); + void draw_TotalScoreGreaterThan21_ReturnBust() { + Hand hand = new Hand().appendCard(Card.from("10", "스페이드")) + .appendCard(Card.from("10", "하트")); State state = new Hit(hand); State nextState = state.draw(Card.from("2", "클로버")); @@ -34,7 +33,7 @@ void draw_Over21_ReturnBust() { @DisplayName("Hit 상태에서 stay를 호출하면 Stay 상태를 반환한다") @Test - void stay_ReturnStay() { + void stay_Always_ReturnStay() { State state = new Hit(new Hand()); State nextState = state.stay(); From 52be4081fe70a510f28a1140e2fdd9f3e5deb0f1 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sat, 14 Mar 2026 23:39:39 +0900 Subject: [PATCH 24/53] =?UTF-8?q?test(State):=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EB=AA=85=20=EB=B0=8F=20=EB=A9=94=EC=86=8C=EB=93=9C?= =?UTF-8?q?=20=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../state/{FinishedStateTest.java => FinishedTest.java} | 6 +++--- src/test/java/domain/state/StayTest.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/test/java/domain/state/{FinishedStateTest.java => FinishedTest.java} (84%) diff --git a/src/test/java/domain/state/FinishedStateTest.java b/src/test/java/domain/state/FinishedTest.java similarity index 84% rename from src/test/java/domain/state/FinishedStateTest.java rename to src/test/java/domain/state/FinishedTest.java index 60e3ff749b5..cc34cb2da2a 100644 --- a/src/test/java/domain/state/FinishedStateTest.java +++ b/src/test/java/domain/state/FinishedTest.java @@ -6,11 +6,11 @@ import static org.assertj.core.api.Assertions.assertThat; -public class FinishedStateTest { +public class FinishedTest { @DisplayName("Bust 상태는 수익률 -1.0을 반환하고 종료 상태이다") @Test - void bust_EarningRate_IsMinusOne() { + void bustEarningRate_Always_IsMinusOne() { State state = new Bust(new Hand()); assertThat(state.isFinished()).isTrue(); assertThat(state.earningRate()).isEqualTo(-1.0); @@ -18,7 +18,7 @@ void bust_EarningRate_IsMinusOne() { @DisplayName("Blackjack 상태는 수익률 1.5을 반환하고 종료 상태이다") @Test - void blackjack_EarningRate_IsOnePointFive() { + void blackjackEarningRate_Always_IsOnePointFive() { State state = new Blackjack(new Hand()); assertThat(state.isFinished()).isTrue(); assertThat(state.earningRate()).isEqualTo(1.5); diff --git a/src/test/java/domain/state/StayTest.java b/src/test/java/domain/state/StayTest.java index 1ff62ebb6d4..c220bda0398 100644 --- a/src/test/java/domain/state/StayTest.java +++ b/src/test/java/domain/state/StayTest.java @@ -18,7 +18,7 @@ void isFinished_Always_ReturnTrue() { @DisplayName("Stay 상태의 기본 수익률은 1.0이다") @Test - void earningRate_Always_ReturnOnePointZero() { + void stayEarningRate_Always_ReturnOnePointZero() { State state = new Stay(new Hand()); assertThat(state.earningRate()).isEqualTo(1.0); } From 9be9861e56622629727b6c785af91059bbcb5817 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 00:09:53 +0900 Subject: [PATCH 25/53] =?UTF-8?q?test(Member):=20Player=EC=9D=98=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EB=B3=84=20=EC=88=98=EC=9D=B5=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/member/PlayerTest.java | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/test/java/domain/member/PlayerTest.java diff --git a/src/test/java/domain/member/PlayerTest.java b/src/test/java/domain/member/PlayerTest.java new file mode 100644 index 00000000000..b45730e5314 --- /dev/null +++ b/src/test/java/domain/member/PlayerTest.java @@ -0,0 +1,35 @@ +package domain.member; + +import domain.state.Blackjack; +import domain.state.Bust; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PlayerTest { + + @DisplayName("블랙잭 상태일 때 베팅 금액의 1.5배 수익을 반환한다") + @Test + void calculateProfit_StateIsBlackjack_ReturnAccurateEarning() { + Money betMoney = new Money(10000); + Player player = new Player("pobi", betMoney, new Blackjack(new Hand())); + int expected = 15000; + + int profit = player.calculateProfit(); + + assertThat(profit).isEqualTo(expected); + } + + @DisplayName("버스트 상태일 때 베팅 금액을 모두 잃는다 (손해 금액 반환)") + @Test + void calculateProfit_StateIsBust_ReturnLostEarning() { + Money betMoney = new Money(10000); + Player player = new Player("pobi", betMoney, new Bust(new Hand())); + int expected = -10000; + + int profit = player.calculateProfit(); + + assertThat(profit).isEqualTo(expected); + } +} From bcc7fcaff0997f52e1b33444945c0555d9329384 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 01:00:45 +0900 Subject: [PATCH 26/53] =?UTF-8?q?refactor(Money):=20Money=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/{card => member}/Money.java | 2 +- src/test/java/domain/card/MoneyTest.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) rename src/main/java/domain/{card => member}/Money.java (96%) diff --git a/src/main/java/domain/card/Money.java b/src/main/java/domain/member/Money.java similarity index 96% rename from src/main/java/domain/card/Money.java rename to src/main/java/domain/member/Money.java index d8dcc0e5f74..cd5a57ce36b 100644 --- a/src/main/java/domain/card/Money.java +++ b/src/main/java/domain/member/Money.java @@ -1,4 +1,4 @@ -package domain.card; +package domain.member; import java.util.Objects; diff --git a/src/test/java/domain/card/MoneyTest.java b/src/test/java/domain/card/MoneyTest.java index 9cd878adcd5..a73320e62e4 100644 --- a/src/test/java/domain/card/MoneyTest.java +++ b/src/test/java/domain/card/MoneyTest.java @@ -1,5 +1,6 @@ package domain.card; +import domain.member.Money; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From e7d490a3b966e71ff8449b6d5f99a96977fc509d Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 01:01:27 +0900 Subject: [PATCH 27/53] =?UTF-8?q?refactor(Member):=20Member=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=83=81=ED=83=9C=20=ED=8C=A8=ED=84=B4=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20Player=20=EB=B2=A0=ED=8C=85=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/member/Dealer.java | 19 ++-------- src/main/java/domain/member/Member.java | 28 ++++++--------- src/main/java/domain/member/Player.java | 23 ++++-------- src/test/java/domain/member/MemberTest.java | 40 ++++++++++----------- 4 files changed, 41 insertions(+), 69 deletions(-) diff --git a/src/main/java/domain/member/Dealer.java b/src/main/java/domain/member/Dealer.java index ca8582943e5..26b2e137a36 100644 --- a/src/main/java/domain/member/Dealer.java +++ b/src/main/java/domain/member/Dealer.java @@ -1,28 +1,15 @@ package domain.member; -import domain.MatchResult; +import domain.state.State; public class Dealer extends Member { private static final int DEALER_DRAW_CONDITION = 16; - public Dealer() { - super(DEALER_NAME); + public Dealer(State initialState) { + super(DEALER_NAME, initialState); } public boolean isMeetTheDrawCondition() { return currentScore() <= DEALER_DRAW_CONDITION; } - - @Override - public MatchResult compareScoreWith(Member player) { - int dealerScore = currentScore(); - int playerScore = player.currentScore(); - if (playerScore > BUST_CONDITION) { - return MatchResult.WIN; - } - if (dealerScore > BUST_CONDITION) { - return MatchResult.LOSE; - } - return calculateResultFromNormalCase(dealerScore, playerScore); - } } diff --git a/src/main/java/domain/member/Member.java b/src/main/java/domain/member/Member.java index 2eb08ad9913..d7e19a152a9 100644 --- a/src/main/java/domain/member/Member.java +++ b/src/main/java/domain/member/Member.java @@ -2,6 +2,8 @@ import domain.MatchResult; import domain.card.Card; +import domain.state.State; + import java.util.List; public abstract class Member { @@ -9,11 +11,11 @@ public abstract class Member { protected static final int BUST_CONDITION = 21; private final String name; - private Hand hand; + protected State state; - public Member(String name) { + public Member(String name, State state) { this.name = name; - this.hand = new Hand(); + this.state = state; } public String name() { @@ -21,26 +23,18 @@ public String name() { } public int currentScore() { - return hand.calculateTotalValue(); + return state.hand().calculateTotalValue(); } public List currentCards() { - return hand.getAllCard(); + return state.hand().getAllCard(); } - public void receiveCard(Card card) { - hand = hand.appendCard(card); + public boolean isFinished() { + return state.isFinished(); } - public abstract MatchResult compareScoreWith(Member other); - - protected MatchResult calculateResultFromNormalCase(int myScore, int targetScore) { - if (myScore > targetScore) { - return MatchResult.WIN; - } - if (myScore < targetScore) { - return MatchResult.LOSE; - } - return MatchResult.DRAW; + public void receiveCard(Card card) { + state = state.draw(card); } } diff --git a/src/main/java/domain/member/Player.java b/src/main/java/domain/member/Player.java index 9c780a756f9..569ce1f8a3f 100644 --- a/src/main/java/domain/member/Player.java +++ b/src/main/java/domain/member/Player.java @@ -1,25 +1,16 @@ package domain.member; -import domain.MatchResult; +import domain.state.State; public class Player extends Member { + private final Money betMoney; - public Player(String name) { - super(name); + public Player(String name, Money betMoney, State initialState) { + super(name, initialState); + this.betMoney = betMoney; } - @Override - public MatchResult compareScoreWith(Member dealer) { - int playerScore = currentScore(); - int dealerScore = dealer.currentScore(); - - if (playerScore > BUST_CONDITION) { - return MatchResult.LOSE; - } - if (dealerScore > BUST_CONDITION) { - return MatchResult.WIN; - } - - return calculateResultFromNormalCase(playerScore, dealerScore); + public int calculateProfit() { + return betMoney.calculateProfit(state.earningRate()); } } diff --git a/src/test/java/domain/member/MemberTest.java b/src/test/java/domain/member/MemberTest.java index 3cb6c4df5f4..5315f2aca79 100644 --- a/src/test/java/domain/member/MemberTest.java +++ b/src/test/java/domain/member/MemberTest.java @@ -10,24 +10,24 @@ public class MemberTest { - @DisplayName("딜러와 플레이어의 승패를 가르고, 결과를 출력하는 테스트") - @ParameterizedTest - @CsvSource({ - "5:4, 3:2, WIN", - "5:4, 6:3, DRAW", - "3:2, 5:4, LOSE", - "10:9:3, 8:7:6:5, WIN" - }) - void winnerTest_CompareDealerAndPlayerScore_returnMatchResult(String dealerCardValue, String playerCardValue, - MatchResult expected) { - Member dealer = new Dealer(); - Member player = new Player("브리"); - - Arrays.stream(dealerCardValue.split(":")) - .forEach(number -> dealer.receiveCard(new Card(number, "하트"))); - Arrays.stream(playerCardValue.split(":")) - .forEach(number -> player.receiveCard(new Card(number, "하트"))); - - Assertions.assertEquals(expected, dealer.compareScoreWith(player)); - } +// @DisplayName("딜러와 플레이어의 승패를 가르고, 결과를 출력하는 테스트") +// @ParameterizedTest +// @CsvSource({ +// "5:4, 3:2, WIN", +// "5:4, 6:3, DRAW", +// "3:2, 5:4, LOSE", +// "10:9:3, 8:7:6:5, WIN" +// }) +// void winnerTest_CompareDealerAndPlayerScore_returnMatchResult(String dealerCardValue, String playerCardValue, +// MatchResult expected) { +// Member dealer = new Dealer(); +// Member player = new Player("브리"); +// +// Arrays.stream(dealerCardValue.split(":")) +// .forEach(number -> dealer.receiveCard(new Card(number, "하트"))); +// Arrays.stream(playerCardValue.split(":")) +// .forEach(number -> player.receiveCard(new Card(number, "하트"))); +// +// Assertions.assertEquals(expected, dealer.compareScoreWith(player)); +// } } From d4cc1adfc1dfca865d76289c9c661c8e54d6d51a Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 01:02:41 +0900 Subject: [PATCH 28/53] =?UTF-8?q?refactor(Members=20&=20GameTable):=20Memb?= =?UTF-8?q?ers=20=EB=B0=8F=20GameTable=EC=9D=98=20=EC=A0=90=EC=88=98=20?= =?UTF-8?q?=EC=A7=81=EC=A0=91=20=EB=B9=84=EA=B5=90=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EC=9D=84=20=EC=83=81=ED=83=9C=20=ED=99=95=EC=9D=B8=EC=9C=BC?= =?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/GameTable.java | 23 +++----- src/main/java/domain/member/Members.java | 36 ++++++++---- src/test/java/domain/GameTableTest.java | 26 +++++---- src/test/java/domain/member/MembersTest.java | 61 +++++++++----------- 4 files changed, 74 insertions(+), 72 deletions(-) diff --git a/src/main/java/domain/GameTable.java b/src/main/java/domain/GameTable.java index 3eefe8e5934..b85bdba0ad0 100644 --- a/src/main/java/domain/GameTable.java +++ b/src/main/java/domain/GameTable.java @@ -2,21 +2,21 @@ import domain.card.Card; import domain.card.Deck; +import domain.member.Money; import dto.GameResult; import dto.MemberStatus; import domain.member.Members; import java.util.ArrayList; import java.util.List; +import java.util.Map; public class GameTable { - private static final int BLACKJACK = 21; - private final Members members; private final Deck deck; - public GameTable(List playerNames, Deck deck) { - this.members = new Members(playerNames); + public GameTable(Map playerBets, Deck deck) { + this.members = new Members(playerBets); this.deck = deck; } @@ -29,8 +29,8 @@ public void distributeInitCard() { } } - public boolean checkBust(String memberName) { - return members.checkPlayerScore(memberName) > BLACKJACK; + public boolean isPlayerBust(String memberName) { + return members.isPlayerFinished(memberName); } public List drawForMember(String memberName) { @@ -58,14 +58,7 @@ public List checkMemberStatuses() { } public List checkGameResult() { - List gameResults = new ArrayList<>(); - gameResults.add(new GameResult(members.getDealerName(), - members.determineDealerGameResult())); - - List playerResults = members.getAllPlayerName().stream() - .map(name -> new GameResult(name, List.of(members.determinePlayerGameResult(name)))) - .toList(); - gameResults.addAll(playerResults); - return gameResults; + // 컴파일 에러를 막기 위해 기존 구조만 유지 + return new ArrayList<>(); } } diff --git a/src/main/java/domain/member/Members.java b/src/main/java/domain/member/Members.java index d33524d0d0a..c3dc5f6e97c 100644 --- a/src/main/java/domain/member/Members.java +++ b/src/main/java/domain/member/Members.java @@ -2,8 +2,12 @@ import domain.MatchResult; import domain.card.Card; +import domain.state.Hit; +import domain.state.Stay; + import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; public class Members { @@ -11,10 +15,10 @@ public class Members { private final Dealer dealer; private final List players; - public Members(List playerNames) { - this.dealer = new Dealer(); - this.players = playerNames.stream() - .map(Player::new) + public Members(Map playerBets) { + this.dealer = new Dealer(new Hit(new Hand())); + this.players = playerBets.entrySet().stream() + .map(entry -> new Player(entry.getKey(), entry.getValue(), new Hit(new Hand()))) .toList(); } @@ -55,15 +59,25 @@ public List getAllPlayerName() { .toList(); } - public List determineDealerGameResult() { - List gameResult = new ArrayList<>(); - players.forEach(player -> gameResult.add(dealer.compareScoreWith(player))); - return List.copyOf(gameResult); +// public List determineDealerGameResult() { +// List gameResult = new ArrayList<>(); +// players.forEach(player -> gameResult.add(dealer.compareScoreWith(player))); +// return List.copyOf(gameResult); +// } +// +// public MatchResult determinePlayerGameResult(String name) { +// Member player = findByPlayerName(name); +// return player.compareScoreWith(dealer); +// } + + public List calculatePlayerProfits() { + return players.stream() + .map(Player::calculateProfit) + .toList(); } - public MatchResult determinePlayerGameResult(String name) { - Member player = findByPlayerName(name); - return player.compareScoreWith(dealer); + public boolean isPlayerFinished(String name) { + return findByPlayerName(name).isFinished(); } public String getDealerName() { diff --git a/src/test/java/domain/GameTableTest.java b/src/test/java/domain/GameTableTest.java index 9f0989ed767..0e2f9a7f192 100644 --- a/src/test/java/domain/GameTableTest.java +++ b/src/test/java/domain/GameTableTest.java @@ -3,6 +3,9 @@ import domain.card.Card; import domain.card.FixedDeck; import java.util.List; +import java.util.Map; + +import domain.member.Money; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -17,30 +20,31 @@ class GameTableTest { @BeforeEach void setUpTest() { List cards = List.of( - new Card("10", "클로버"), - new Card("9", "클로버"), - new Card("8", "클로버"), - new Card("7", "클로버") + Card.from("10", "클로버"), + Card.from("9", "클로버"), + Card.from("8", "클로버"), + Card.from("7", "클로버") ); - this.gameTable = new GameTable(List.of(pobiName), new FixedDeck(cards)); + Map playerBets = Map.of(pobiName, new Money(10000)); + this.gameTable = new GameTable(playerBets, new FixedDeck(cards)); } - @DisplayName("카드의 총합이 21보다 크면 CurrentResult의 isBust는 true이다.") + @DisplayName("카드의 총합이 21보다 크면 isPlayerBust는 true이다.") @Test - void checkCurrentTest_playerHasCardSumOf22_returnTrue() { + void isPlayerBust_ScoreOver21_ReturnTrue() { gameTable.drawForMember(pobiName); gameTable.drawForMember(pobiName); gameTable.drawForMember(pobiName); - Assertions.assertTrue(gameTable.checkBust(pobiName)); + Assertions.assertTrue(gameTable.isPlayerBust(pobiName)); } - @DisplayName("카드의 총합이 21보다 작으면 CurrentResult의 isBust는 false이다.") + @DisplayName("카드의 총합이 21보다 작으면 isPlayerBust는 false이다.") @Test - void checkCurrentTest_playerHasCardSumOf20_returnFalse() { + void isPlayerBust_ScoreUnder21_ReturnFalse() { gameTable.drawForMember(pobiName); gameTable.drawForMember(pobiName); - Assertions.assertFalse(gameTable.checkBust(pobiName)); + Assertions.assertFalse(gameTable.isPlayerBust(pobiName)); } } diff --git a/src/test/java/domain/member/MembersTest.java b/src/test/java/domain/member/MembersTest.java index 9514f97886f..ebddcb1e758 100644 --- a/src/test/java/domain/member/MembersTest.java +++ b/src/test/java/domain/member/MembersTest.java @@ -3,62 +3,53 @@ import domain.MatchResult; import domain.card.Card; import java.util.List; -import org.assertj.core.api.Assertions; +import java.util.Map; + import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + public class MembersTest { + private final Money defaultMoney = new Money(10000); + @DisplayName("멤버 명단을 정상적으로 불러오는지 테스트") @Test - void getAllMemberNameTest_memberSuccessfullyCreated_containPlayerNames() { + void getAllPlayerName_MembersCreated_ContainsNames() { String player1 = "pobi"; String player2 = "jason"; + Map playerBets = Map.of(player1, defaultMoney, player2, defaultMoney); - Members members = new Members(List.of(player1, player2)); + Members members = new Members(playerBets); - Assertions.assertThat(members.getAllPlayerName()).contains(player1, player2); + assertThat(members.getAllPlayerName()).contains(player1, player2); } @DisplayName("해당 멤버에게 카드를 주면 정상적으로 카드를 보유하고 있는지 테스트") @Test - void provideCardToMember_cardGivenToMember_containGivenCard() { + void provideCardToPlayer_CardGiven_MemberContainsCard() { String playerName = "pobi"; - Members members = new Members(List.of(playerName)); - Card card = new Card("2", "하트"); + Members members = new Members(Map.of(playerName, defaultMoney)); + Card card = Card.from("2", "하트"); members.provideCardToPlayer(playerName, card); - Assertions.assertThat(members.findCardByName(playerName)).contains(card); - } - - @DisplayName("딜러의 게임 결과를 정상적으로 결정하는지 테스트") - @Test - void determineDealerGameResult_compareWithPlayer_returnMatchResult() { - String player = "pobi"; - Members members = new Members(List.of(player)); - List dealerCards = List.of(new Card("6", "하트"), new Card("4", "하트")); - List playerCards = List.of(new Card("2", "하트"), new Card("3", "하트")); - MatchResult expected = MatchResult.WIN; - - dealerCards.forEach(members::provideCardToDealer); - playerCards.forEach(card -> members.provideCardToPlayer(player, card)); - - Assertions.assertThat(members.determineDealerGameResult()).containsExactly(expected); + assertThat(members.findCardByName(playerName)).contains(card); } - @DisplayName("플레이어의 게임 결과를 정상적으로 결정하는지 테스트") + @DisplayName("수익 정산 로직이 정상적으로 작동하는지 테스트") @Test - void determinePlayerGameResult_compareWithDealer_returnMatchResult() { - String player = "pobi"; - Members members = new Members(List.of(player)); - List dealerCards = List.of(new Card("6", "하트"), new Card("4", "하트")); - List playerCards = List.of(new Card("2", "하트"), new Card("3", "하트")); - MatchResult expected = MatchResult.LOSE; - - dealerCards.forEach(members::provideCardToDealer); - playerCards.forEach(card -> members.provideCardToPlayer(player, card)); - - Assertions.assertThat(members.determinePlayerGameResult(player)).isEqualTo(expected); + void calculatePlayerProfits_GameOver_ReturnsCorrectProfits() { +// String player = "pobi"; +// Members members = new Members(Map.of(player, new Money(10000))); +// +// members.provideCardToDealer(Card.from("6", "하트")); +// members.provideCardToDealer(Card.from("4", "하트")); +// +// members.provideCardToPlayer(player, Card.from("Q", "하트")); +// members.provideCardToPlayer(player, Card.from("K", "하트")); +// +// assertThat(members.calculatePlayerProfits()).containsExactly(10000); } } From 132e809571b1460ed92a716e91a0b395e50fc659 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 01:03:22 +0900 Subject: [PATCH 29/53] =?UTF-8?q?refactor(Service):=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EB=90=9C=20GameTable=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/application/BlackjackService.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/application/BlackjackService.java b/src/main/java/application/BlackjackService.java index 1c81ff42ca6..aa09de1d873 100644 --- a/src/main/java/application/BlackjackService.java +++ b/src/main/java/application/BlackjackService.java @@ -2,11 +2,15 @@ import domain.card.Card; import domain.GameTable; +import domain.member.Money; import dto.RoundResult; import domain.card.StandardDeck; import dto.GameResult; import dto.MemberStatus; + +import java.util.HashMap; import java.util.List; +import java.util.Map; public class BlackjackService { @@ -15,15 +19,20 @@ public class BlackjackService { public BlackjackService() { } - public void initializeGame(List playerNames) { - this.gameTable = new GameTable(playerNames, new StandardDeck()); + public void initializeGame(Map playerBetAmounts) { + Map playerBets = new HashMap<>(); + for (String name : playerBetAmounts.keySet()) { + Money betMoney = new Money(playerBetAmounts.get(name)); + playerBets.put(name, betMoney); + } + this.gameTable = new GameTable(playerBets, new StandardDeck()); gameTable.distributeInitCard(); } public RoundResult startOneRound(String memberName) { List playerCards = getGameTable().drawForMember(memberName); - boolean isBust = getGameTable().checkBust(memberName); + boolean isBust = getGameTable().isPlayerBust(memberName); return new RoundResult(playerCards, isBust); } From ab20a64e91095d0de58f680932b4f68ab46a9c51 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 01:03:54 +0900 Subject: [PATCH 30/53] =?UTF-8?q?feat(Controller):=20=EB=B2=A0=ED=8C=85=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=20=EC=9E=85=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/presentation/BlackjackController.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/presentation/BlackjackController.java b/src/main/java/presentation/BlackjackController.java index 25eeb4ae5eb..8b50f308c39 100644 --- a/src/main/java/presentation/BlackjackController.java +++ b/src/main/java/presentation/BlackjackController.java @@ -4,7 +4,11 @@ import dto.RoundResult; import dto.GameResult; import dto.MemberStatus; + +import java.util.HashMap; import java.util.List; +import java.util.Map; + import presentation.ui.InputView; import presentation.ui.OutputView; @@ -53,7 +57,12 @@ private void playGame(List playerNames) { } private void setUpGame(List playerNames) { - blackjackService.initializeGame(playerNames); + Map playerBets = new HashMap<>(); + for (String playerName : playerNames) { + int betAmount = inputView.readPlayerBetAmount(playerName); + playerBets.put(playerName, betAmount); + } + blackjackService.initializeGame(playerBets); List memberStatuses = blackjackService.getMemberStatuses(); outputView.printInitialStatus(memberStatuses); } From dd7d869d7c16607b824631ca9f3c01869cc80479 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 02:45:26 +0900 Subject: [PATCH 31/53] =?UTF-8?q?test(State):=20=EB=94=9C=EB=9F=AC?= =?UTF-8?q?=EA=B0=80=20=EC=B9=B4=EB=93=9C=EB=A5=BC=20=EB=BD=91=EC=9D=80=20?= =?UTF-8?q?=EC=9D=B4=ED=9B=84=20=EB=B0=98=ED=99=98=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=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/test/java/domain/state/DealerHitTest.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/test/java/domain/state/DealerHitTest.java diff --git a/src/test/java/domain/state/DealerHitTest.java b/src/test/java/domain/state/DealerHitTest.java new file mode 100644 index 00000000000..a9b570aaa2a --- /dev/null +++ b/src/test/java/domain/state/DealerHitTest.java @@ -0,0 +1,45 @@ +package domain.state; + +import domain.card.Card; +import domain.member.Hand; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DealerHitTest { + + @DisplayName("딜러가 1장의 카드를 가진 상태에서 추가 카드를 뽑으면 점수에 따라 상태가 반환된다") + @ParameterizedTest + @CsvSource({ + "10, 6, domain.state.DealerHit", + "10, 7, domain.state.Stay", + "A, 10, domain.state.Blackjack" + }) + void dealerDraw_SecondCardDrawn_ReturnStatus(String initialNumber, String drawNumber, String expectedState) { + Hand hand = new Hand().appendCard(Card.from(initialNumber, "하트")); + State state = new DealerHit(hand); + + State nextState = state.draw(Card.from(drawNumber, "클로버")); + + assertThat(nextState.getClass().getName()).isEqualTo(expectedState); + } + + @DisplayName("딜러가 2장의 카드를 가진 상태(16점 이하)에서 추가 카드를 뽑으면 결과와 상관없이 종료 상태가 반환된다") + @ParameterizedTest + @CsvSource({ + "10, 4, 2, domain.state.Stay", + "10, 5, 2, domain.state.Stay", + "10, 6, 10, domain.state.Bust" + }) + void draw_ThirdCardDrawn_ReturnsFinishedState(String card1, String card2, String drawNumber, String expectedState) { + Hand hand = new Hand().appendCard(Card.from(card1, "하트")) + .appendCard(Card.from(card2, "다이아몬드")); + State state = new DealerHit(hand); + + State nextState = state.draw(Card.from(drawNumber, "클로버")); + + assertThat(nextState.getClass().getName()).isEqualTo(expectedState); + } +} From 88ecc9902a2cec075f8dca8c21bb2913105b9709 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 02:47:08 +0900 Subject: [PATCH 32/53] =?UTF-8?q?feat(State):=20=EB=94=9C=EB=9F=AC?= =?UTF-8?q?=EA=B0=80=20=EC=B9=B4=EB=93=9C=EB=A5=BC=20=EB=BD=91=EC=9D=80=20?= =?UTF-8?q?=EC=9D=B4=ED=9B=84=20=EC=83=81=ED=83=9C=20=EB=B0=98=ED=99=98=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/member/Hand.java | 4 ++ src/main/java/domain/state/DealerHit.java | 45 +++++++++++++++++++++++ src/main/java/domain/state/Hit.java | 1 - src/main/java/domain/state/Running.java | 2 + 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/main/java/domain/state/DealerHit.java diff --git a/src/main/java/domain/member/Hand.java b/src/main/java/domain/member/Hand.java index 4e47b7cb781..93f417ba437 100644 --- a/src/main/java/domain/member/Hand.java +++ b/src/main/java/domain/member/Hand.java @@ -18,6 +18,10 @@ private Hand(List cards) { this.cards = cards; } + public int size() { + return cards.size(); + } + public List getAllCard() { return List.copyOf(cards); } diff --git a/src/main/java/domain/state/DealerHit.java b/src/main/java/domain/state/DealerHit.java new file mode 100644 index 00000000000..5bfb5796c1d --- /dev/null +++ b/src/main/java/domain/state/DealerHit.java @@ -0,0 +1,45 @@ +package domain.state; + +import domain.card.Card; +import domain.member.Hand; + +public class DealerHit extends Running { + private static final int DEALER_DRAW_CONDITION = 16; + private static final int INITIAL_CARDS_COUNT = 2; + + public DealerHit(Hand hand) { + super(hand); + } + + @Override + public State draw(Card card) { + Hand newHand = hand.appendCard(card); + int score = newHand.calculateTotalValue(); + + if (score >= BUST_CONDITION) { + return new Bust(newHand); + } + if (newHand.size() < INITIAL_CARDS_COUNT) { + return new DealerHit(newHand); + } + if (newHand.size() == INITIAL_CARDS_COUNT) { + return judgeInitialState(newHand, score); + } + return new Stay(newHand); + } + + private State judgeInitialState(Hand newHand, int score) { + if (score == BLACKJACK_CONDITION) { + return new Blackjack(newHand); + } + if (score > DEALER_DRAW_CONDITION) { + return new Stay(newHand); + } + return new DealerHit(newHand); + } + + @Override + public State stay() { + throw new IllegalArgumentException("딜러는 스스로 멈출 수 없습니다."); + } +} diff --git a/src/main/java/domain/state/Hit.java b/src/main/java/domain/state/Hit.java index 61152433386..79a7e9f366a 100644 --- a/src/main/java/domain/state/Hit.java +++ b/src/main/java/domain/state/Hit.java @@ -4,7 +4,6 @@ import domain.member.Hand; public class Hit extends Running { - private static final int BUST_CONDITION = 21; public Hit(Hand hand) { super(hand); diff --git a/src/main/java/domain/state/Running.java b/src/main/java/domain/state/Running.java index f28c49a7bab..670dcba1c18 100644 --- a/src/main/java/domain/state/Running.java +++ b/src/main/java/domain/state/Running.java @@ -3,6 +3,8 @@ import domain.member.Hand; public abstract class Running extends Started { + protected static final int BUST_CONDITION = 22; + protected static final int BLACKJACK_CONDITION = 21; public Running(Hand hand) { super(hand); From 7c010583dc0b641742b2b6a08d796680a378f1a4 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 03:01:26 +0900 Subject: [PATCH 33/53] =?UTF-8?q?test(State):=20=ED=94=8C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=EA=B0=80=20=EC=B9=B4=EB=93=9C=EB=A5=BC=20?= =?UTF-8?q?=EB=BD=91=EC=95=98=EC=9D=84=20=EB=95=8C=20=ED=95=A9=EC=82=B0?= =?UTF-8?q?=EC=9D=B4=2021=EC=9D=B4=EB=A9=B4=20=EB=82=98=EC=98=A4=EB=8A=94?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=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/state/HitTest.java | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/test/java/domain/state/HitTest.java b/src/test/java/domain/state/HitTest.java index 1f7cce33ae7..5875be7c8b5 100644 --- a/src/test/java/domain/state/HitTest.java +++ b/src/test/java/domain/state/HitTest.java @@ -31,6 +31,29 @@ void draw_TotalScoreGreaterThan21_ReturnBust() { assertThat(nextState).isInstanceOf(Bust.class); } + @DisplayName("플레이어가 카드를 뽑아 21점이 되었을 때 카드 개수가 2개이면 Blackjack 상태를 반환한다") + @Test + void draw_TotalScoreIs21AndCardSizeIs2_ReturnBlackjack() { + Hand hand = new Hand().appendCard(Card.from("10", "스페이드")); + State state = new Hit(hand); + + State nextState = state.draw(Card.from("A", "클로버")); + + assertThat(nextState).isInstanceOf(Blackjack.class); + } + + @DisplayName("플레이어가 카드를 뽑아 21점이 되었을 때 카드 개수가 2개가 아니면 Stay 상태를 반환한다") + @Test + void draw_TotalScoreIs21AndCardSizeIsNot2_ReturnBlackjack() { + Hand hand = new Hand().appendCard(Card.from("10", "스페이드")) + .appendCard(Card.from("9", "하트")); + State state = new Hit(hand); + + State nextState = state.draw(Card.from("2", "클로버")); + + assertThat(nextState).isInstanceOf(Stay.class); + } + @DisplayName("Hit 상태에서 stay를 호출하면 Stay 상태를 반환한다") @Test void stay_Always_ReturnStay() { From 93b9937110fa1c6ab1c9b97fe48b8ba8f50e9eb9 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 03:02:16 +0900 Subject: [PATCH 34/53] =?UTF-8?q?feat(State):=20=ED=94=8C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=EA=B0=80=20=EC=B9=B4=EB=93=9C=EB=A5=BC=20?= =?UTF-8?q?=EB=BD=91=EC=95=98=EC=9D=84=20=EB=95=8C=20=EC=A1=B0=EA=B1=B4?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EC=83=81=ED=83=9C=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/state/DealerHit.java | 6 +++--- src/main/java/domain/state/Hit.java | 13 ++++++++++++- src/main/java/domain/state/Running.java | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/domain/state/DealerHit.java b/src/main/java/domain/state/DealerHit.java index 5bfb5796c1d..70db4c4ece9 100644 --- a/src/main/java/domain/state/DealerHit.java +++ b/src/main/java/domain/state/DealerHit.java @@ -5,7 +5,6 @@ public class DealerHit extends Running { private static final int DEALER_DRAW_CONDITION = 16; - private static final int INITIAL_CARDS_COUNT = 2; public DealerHit(Hand hand) { super(hand); @@ -15,14 +14,15 @@ public DealerHit(Hand hand) { public State draw(Card card) { Hand newHand = hand.appendCard(card); int score = newHand.calculateTotalValue(); + int newHandSize = newHand.size(); if (score >= BUST_CONDITION) { return new Bust(newHand); } - if (newHand.size() < INITIAL_CARDS_COUNT) { + if (newHandSize < INITIAL_CARDS_COUNT) { return new DealerHit(newHand); } - if (newHand.size() == INITIAL_CARDS_COUNT) { + if (newHandSize == INITIAL_CARDS_COUNT) { return judgeInitialState(newHand, score); } return new Stay(newHand); diff --git a/src/main/java/domain/state/Hit.java b/src/main/java/domain/state/Hit.java index 79a7e9f366a..c3fbf8862dd 100644 --- a/src/main/java/domain/state/Hit.java +++ b/src/main/java/domain/state/Hit.java @@ -12,13 +12,24 @@ public Hit(Hand hand) { @Override public State draw(Card card) { Hand newHand = hand.appendCard(card); + int score = newHand.calculateTotalValue(); - if (newHand.calculateTotalValue() > BUST_CONDITION) { + if (score >= BUST_CONDITION) { return new Bust(newHand); } + if (score == BLACKJACK_CONDITION) { + return judgeInitialState(newHand); + } return new Hit(newHand); } + private State judgeInitialState(Hand newHand) { + if (newHand.size() == INITIAL_CARDS_COUNT) { + return new Blackjack(newHand); + } + return new Stay(newHand); + } + @Override public State stay() { return new Stay(hand); diff --git a/src/main/java/domain/state/Running.java b/src/main/java/domain/state/Running.java index 670dcba1c18..e72635f0a52 100644 --- a/src/main/java/domain/state/Running.java +++ b/src/main/java/domain/state/Running.java @@ -5,6 +5,7 @@ public abstract class Running extends Started { protected static final int BUST_CONDITION = 22; protected static final int BLACKJACK_CONDITION = 21; + protected static final int INITIAL_CARDS_COUNT = 2; public Running(Hand hand) { super(hand); From 47725e2df99e1cba565c73ca938f439451b2922e Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 03:55:40 +0900 Subject: [PATCH 35/53] =?UTF-8?q?refactor(State):=20Blackjack,=20Stay,=20B?= =?UTF-8?q?ust=EC=9D=98=20=EC=88=98=EC=9D=B5=EB=A5=A0=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/MatchResult.java | 17 ++++++++-- src/main/java/domain/member/Player.java | 4 +-- src/main/java/domain/state/Blackjack.java | 8 +++-- src/main/java/domain/state/Bust.java | 2 +- src/main/java/domain/state/Running.java | 2 +- src/main/java/domain/state/State.java | 2 +- src/main/java/domain/state/Stay.java | 20 ++++++++--- src/test/java/domain/member/PlayerTest.java | 14 +++++--- src/test/java/domain/state/FinishedTest.java | 32 +++++++++++++----- src/test/java/domain/state/StayTest.java | 35 +++++++++++++++++--- 10 files changed, 104 insertions(+), 32 deletions(-) diff --git a/src/main/java/domain/MatchResult.java b/src/main/java/domain/MatchResult.java index 44699cba894..4e30b2557ff 100644 --- a/src/main/java/domain/MatchResult.java +++ b/src/main/java/domain/MatchResult.java @@ -1,7 +1,18 @@ package domain; public enum MatchResult { - WIN, - DRAW, - LOSE + BLACKJACK_WIN(1.5), + WIN(1.0), + DRAW(0.0), + LOSE(-1.0); + + private final double profitRate; + + MatchResult(double profitRate) { + this.profitRate = profitRate; + } + + public double profitRate() { + return profitRate; + } } diff --git a/src/main/java/domain/member/Player.java b/src/main/java/domain/member/Player.java index 569ce1f8a3f..3d93fc16e7b 100644 --- a/src/main/java/domain/member/Player.java +++ b/src/main/java/domain/member/Player.java @@ -10,7 +10,7 @@ public Player(String name, Money betMoney, State initialState) { this.betMoney = betMoney; } - public int calculateProfit() { - return betMoney.calculateProfit(state.earningRate()); + public int calculateProfit(Member member) { + return betMoney.calculateProfit(state.earningRate(member.state)); } } diff --git a/src/main/java/domain/state/Blackjack.java b/src/main/java/domain/state/Blackjack.java index dce07ec0161..f5983d9f3c1 100644 --- a/src/main/java/domain/state/Blackjack.java +++ b/src/main/java/domain/state/Blackjack.java @@ -1,5 +1,6 @@ package domain.state; +import domain.MatchResult; import domain.member.Hand; public class Blackjack extends Finished { @@ -9,7 +10,10 @@ public Blackjack(Hand hand) { } @Override - public double earningRate() { - return 1.5; + public double earningRate(State dealerState) { + if (dealerState instanceof Blackjack) { + return MatchResult.DRAW.profitRate(); + } + return MatchResult.BLACKJACK_WIN.profitRate(); } } diff --git a/src/main/java/domain/state/Bust.java b/src/main/java/domain/state/Bust.java index 20b099601b8..2771026a73e 100644 --- a/src/main/java/domain/state/Bust.java +++ b/src/main/java/domain/state/Bust.java @@ -9,7 +9,7 @@ public Bust(Hand hand) { } @Override - public double earningRate() { + public double earningRate(State dealerState) { return -1.0; } } diff --git a/src/main/java/domain/state/Running.java b/src/main/java/domain/state/Running.java index e72635f0a52..a9efa700599 100644 --- a/src/main/java/domain/state/Running.java +++ b/src/main/java/domain/state/Running.java @@ -17,7 +17,7 @@ public boolean isFinished() { } @Override - public double earningRate() { + public double earningRate(State dealerState) { return 0; } } diff --git a/src/main/java/domain/state/State.java b/src/main/java/domain/state/State.java index 1d003160697..dec0ba2e2a4 100644 --- a/src/main/java/domain/state/State.java +++ b/src/main/java/domain/state/State.java @@ -5,7 +5,7 @@ public interface State { boolean isFinished(); - double earningRate(); + double earningRate(State dealerState); State draw(Card card); State stay(); Hand hand(); diff --git a/src/main/java/domain/state/Stay.java b/src/main/java/domain/state/Stay.java index 8c92c9a9c97..3c3cc9fd968 100644 --- a/src/main/java/domain/state/Stay.java +++ b/src/main/java/domain/state/Stay.java @@ -1,5 +1,6 @@ package domain.state; +import domain.MatchResult; import domain.member.Hand; public class Stay extends Finished { @@ -9,9 +10,20 @@ public Stay(Hand hand) { } @Override - public double earningRate() { - // 기본 수익률만 작성 - // 이후 딜러와 비교하는 로직을 추가할 예정 - return 1.0; + public double earningRate(State dealerState) { + if (dealerState instanceof Bust) { + return MatchResult.WIN.profitRate(); + } + if (dealerState instanceof Blackjack) { + return MatchResult.LOSE.profitRate(); + } + return judgeScore(dealerState.hand().calculateTotalValue()); + } + + private double judgeScore(int dealerScore) { + int myScore = hand.calculateTotalValue(); + if (myScore > dealerScore) return MatchResult.WIN.profitRate(); + if (myScore < dealerScore) return MatchResult.LOSE.profitRate(); + return MatchResult.DRAW.profitRate(); } } diff --git a/src/test/java/domain/member/PlayerTest.java b/src/test/java/domain/member/PlayerTest.java index b45730e5314..7d248482f5f 100644 --- a/src/test/java/domain/member/PlayerTest.java +++ b/src/test/java/domain/member/PlayerTest.java @@ -1,7 +1,9 @@ package domain.member; +import domain.card.Card; import domain.state.Blackjack; import domain.state.Bust; +import domain.state.Stay; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -9,26 +11,28 @@ public class PlayerTest { - @DisplayName("블랙잭 상태일 때 베팅 금액의 1.5배 수익을 반환한다") + @DisplayName("플레이어가 블랙잭이고 딜러가 블랙잭이 아니면 베팅 금액의 1.5배 수익을 반환한다") @Test void calculateProfit_StateIsBlackjack_ReturnAccurateEarning() { Money betMoney = new Money(10000); Player player = new Player("pobi", betMoney, new Blackjack(new Hand())); + Member dealer = new Dealer(new Stay(new Hand().appendCard(Card.from("10", "하트")))); int expected = 15000; - int profit = player.calculateProfit(); + int profit = player.calculateProfit(dealer); assertThat(profit).isEqualTo(expected); } - @DisplayName("버스트 상태일 때 베팅 금액을 모두 잃는다 (손해 금액 반환)") + @DisplayName("플레이어가 버스트 상태이면 딜러의 상태와 상관없이 베팅 금액을 모두 잃는다") @Test void calculateProfit_StateIsBust_ReturnLostEarning() { Money betMoney = new Money(10000); - Player player = new Player("pobi", betMoney, new Bust(new Hand())); + Player player = new Player("pobi", betMoney, new Bust(new Hand().appendCard(Card.from("10", "하트")))); + Member dealer = new Dealer(new Stay(new Hand().appendCard(Card.from("2", "클로버")))); int expected = -10000; - int profit = player.calculateProfit(); + int profit = player.calculateProfit(dealer); assertThat(profit).isEqualTo(expected); } diff --git a/src/test/java/domain/state/FinishedTest.java b/src/test/java/domain/state/FinishedTest.java index cc34cb2da2a..19268be566c 100644 --- a/src/test/java/domain/state/FinishedTest.java +++ b/src/test/java/domain/state/FinishedTest.java @@ -1,5 +1,6 @@ package domain.state; +import domain.card.Card; import domain.member.Hand; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -8,19 +9,32 @@ public class FinishedTest { - @DisplayName("Bust 상태는 수익률 -1.0을 반환하고 종료 상태이다") + @DisplayName("Bust 상태는 딜러의 상태와 상관없이 항상 수익률 -1.0을 반환하고 종료 상태이다") @Test void bustEarningRate_Always_IsMinusOne() { - State state = new Bust(new Hand()); - assertThat(state.isFinished()).isTrue(); - assertThat(state.earningRate()).isEqualTo(-1.0); + State playerState = new Bust(new Hand()); + State dealerState = new Stay(new Hand().appendCard(Card.from("10", "하트"))); + + assertThat(playerState.isFinished()).isTrue(); + assertThat(playerState.earningRate(dealerState)).isEqualTo(-1.0); + } + + @DisplayName("Blackjack 상태는 딜러가 Blackjack이 아니면 수익률 1.5을 반환하고 종료 상태이다") + @Test + void blackjackEarningRate_DealerIsNotBlackjack_IsOnePointFive() { + State playerState = new Blackjack(new Hand()); + State dealerState = new Stay(new Hand()); + + assertThat(playerState.isFinished()).isTrue(); + assertThat(playerState.earningRate(dealerState)).isEqualTo(1.5); } - @DisplayName("Blackjack 상태는 수익률 1.5을 반환하고 종료 상태이다") + @DisplayName("Blackjack 상태는 딜러도 Blackjack이면 수익률 0.0(무승부)을 반환한다") @Test - void blackjackEarningRate_Always_IsOnePointFive() { - State state = new Blackjack(new Hand()); - assertThat(state.isFinished()).isTrue(); - assertThat(state.earningRate()).isEqualTo(1.5); + void blackjackEarningRate_BothBlackjack_ReturnsZero() { + State playerState = new Blackjack(new Hand()); + State dealerState = new Blackjack(new Hand()); + + assertThat(playerState.earningRate(dealerState)).isEqualTo(0.0); } } diff --git a/src/test/java/domain/state/StayTest.java b/src/test/java/domain/state/StayTest.java index c220bda0398..5dd6b578552 100644 --- a/src/test/java/domain/state/StayTest.java +++ b/src/test/java/domain/state/StayTest.java @@ -1,8 +1,11 @@ package domain.state; +import domain.card.Card; import domain.member.Hand; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -16,11 +19,35 @@ void isFinished_Always_ReturnTrue() { assertThat(state.isFinished()).isTrue(); } - @DisplayName("Stay 상태의 기본 수익률은 1.0이다") + @DisplayName("Stay 상태는 딜러가 Bust되면 점수와 상관없이 수익률 1.0을 반환한다") @Test - void stayEarningRate_Always_ReturnOnePointZero() { - State state = new Stay(new Hand()); - assertThat(state.earningRate()).isEqualTo(1.0); + void earningRate_DealerBust_ReturnsOnePointZero() { + Hand playerHand = new Hand().appendCard(Card.from("10", "하트")) + .appendCard(Card.from("5", "하트")); + State playerState = new Stay(playerHand); + + State dealerState = new Bust(new Hand()); + + assertThat(playerState.earningRate(dealerState)).isEqualTo(1.0); + } + + @DisplayName("Stay 상태는 딜러가 Stay일 때 점수 비교를 통해 수익률을 반환한다") + @ParameterizedTest + @CsvSource({ + "10, 10, 10, 9, 1.0", + "10, 8, 10, 9, -1.0", + "10, 9, 10, 9, 0.0" + }) + void earningRate_CompareScoreWithDealer_ReturnsExpectedRate( + String p1, String p2, String d1, String d2, double expectedRate) { + + Hand playerHand = new Hand().appendCard(Card.from(p1, "하트")).appendCard(Card.from(p2, "다이아몬드")); + State playerState = new Stay(playerHand); + + Hand dealerHand = new Hand().appendCard(Card.from(d1, "클로버")).appendCard(Card.from(d2, "스페이드")); + State dealerState = new Stay(dealerHand); + + assertThat(playerState.earningRate(dealerState)).isEqualTo(expectedRate); } @DisplayName("Stay 상태에서 카드를 더 뽑으려 하면 예외가 발생한다") From 96872c357d75d833867ad307f89749d6d18b8ea4 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 04:38:12 +0900 Subject: [PATCH 36/53] =?UTF-8?q?test(Members):=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=EB=A9=A4=EB=B2=84=20=EC=88=98=EC=9D=B5=20=EC=A0=95=EC=82=B0=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 --- src/test/java/domain/member/MemberTest.java | 33 ----------- src/test/java/domain/member/MembersTest.java | 61 +++++++++++++++----- 2 files changed, 47 insertions(+), 47 deletions(-) delete mode 100644 src/test/java/domain/member/MemberTest.java diff --git a/src/test/java/domain/member/MemberTest.java b/src/test/java/domain/member/MemberTest.java deleted file mode 100644 index 5315f2aca79..00000000000 --- a/src/test/java/domain/member/MemberTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package domain.member; - -import domain.MatchResult; -import domain.card.Card; -import java.util.Arrays; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -public class MemberTest { - -// @DisplayName("딜러와 플레이어의 승패를 가르고, 결과를 출력하는 테스트") -// @ParameterizedTest -// @CsvSource({ -// "5:4, 3:2, WIN", -// "5:4, 6:3, DRAW", -// "3:2, 5:4, LOSE", -// "10:9:3, 8:7:6:5, WIN" -// }) -// void winnerTest_CompareDealerAndPlayerScore_returnMatchResult(String dealerCardValue, String playerCardValue, -// MatchResult expected) { -// Member dealer = new Dealer(); -// Member player = new Player("브리"); -// -// Arrays.stream(dealerCardValue.split(":")) -// .forEach(number -> dealer.receiveCard(new Card(number, "하트"))); -// Arrays.stream(playerCardValue.split(":")) -// .forEach(number -> player.receiveCard(new Card(number, "하트"))); -// -// Assertions.assertEquals(expected, dealer.compareScoreWith(player)); -// } -} diff --git a/src/test/java/domain/member/MembersTest.java b/src/test/java/domain/member/MembersTest.java index ebddcb1e758..cdc709940cf 100644 --- a/src/test/java/domain/member/MembersTest.java +++ b/src/test/java/domain/member/MembersTest.java @@ -1,8 +1,6 @@ package domain.member; -import domain.MatchResult; import domain.card.Card; -import java.util.List; import java.util.Map; import org.junit.jupiter.api.DisplayName; @@ -38,18 +36,53 @@ void provideCardToPlayer_CardGiven_MemberContainsCard() { assertThat(members.findCardByName(playerName)).contains(card); } - @DisplayName("수익 정산 로직이 정상적으로 작동하는지 테스트") + @DisplayName("모든 멤버의 수익 정산이 정상적으로 작동하는지 테스트") @Test - void calculatePlayerProfits_GameOver_ReturnsCorrectProfits() { -// String player = "pobi"; -// Members members = new Members(Map.of(player, new Money(10000))); -// -// members.provideCardToDealer(Card.from("6", "하트")); -// members.provideCardToDealer(Card.from("4", "하트")); -// -// members.provideCardToPlayer(player, Card.from("Q", "하트")); -// members.provideCardToPlayer(player, Card.from("K", "하트")); -// -// assertThat(members.calculatePlayerProfits()).containsExactly(10000); + void calculateFinalProfits_GameOver_ReturnsCorrectResults() { + String playerName = "pobi"; + Members members = new Members(Map.of(playerName, new Money(10000))); + members.provideCardToPlayer(playerName, Card.from("Q", "하트")); + members.provideCardToPlayer(playerName, Card.from("K", "하트")); + members.changePlayerStateToStay(playerName); + members.provideCardToDealer(Card.from("6", "하트")); + members.provideCardToDealer(Card.from("4", "하트")); + members.provideCardToDealer(Card.from("7", "하트")); + Map results = members.calculateFinalProfits(); + + assertThat(results.get(playerName)).isEqualTo(10000); + assertThat(results.get(members.getDealerName())).isEqualTo(-10000); + } + + @DisplayName("딜러와 플레이어가 모두 블랙잭이면 수익은 0원이다") + @Test + void calculateFinalProfits_BothBlackjack_ReturnsZero() { + String playerName = "pobi"; + Members members = new Members(Map.of(playerName, new Money(10000))); + members.provideCardToPlayer(playerName, Card.from("A", "하트")); + members.provideCardToPlayer(playerName, Card.from("K", "하트")); + members.provideCardToDealer(Card.from("A", "스페이드")); + members.provideCardToDealer(Card.from("Q", "스페이드")); + + Map results = members.calculateFinalProfits(); + + assertThat(results.get(playerName)).isEqualTo(0); + assertThat(results.get(members.getDealerName())).isEqualTo(0); + } + + @DisplayName("딜러가 버스트되면 Bust되지 않은 플레이어는 무조건 승리한다") + @Test + void calculateFinalProfits_DealerBust_PlayerWins() { + String playerName = "pobi"; + Members members = new Members(Map.of(playerName, new Money(10000))); + members.provideCardToPlayer(playerName, Card.from("2", "하트")); + members.provideCardToPlayer(playerName, Card.from("Q", "하트")); + members.changePlayerStateToStay(playerName); + members.provideCardToDealer(Card.from("10", "하트")); + members.provideCardToDealer(Card.from("6", "하트")); + members.provideCardToDealer(Card.from("7", "하트")); + Map results = members.calculateFinalProfits(); + + assertThat(results.get(playerName)).isEqualTo(10000); + assertThat(results.get(members.getDealerName())).isEqualTo(-10000); } } From 426cc5ec2912566fd4cde954640426dc22d6488a Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 04:41:07 +0900 Subject: [PATCH 37/53] =?UTF-8?q?feat(Members):=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=EB=A9=A4=EB=B2=84=20=EC=88=98=EC=9D=B5=20=EC=A0=95=EC=82=B0=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 - 플레이어가 더 이상 카드를 받지 않고 싶어하면 상태가 Stay로 바뀌는 기능 구현 --- .../java/application/BlackjackService.java | 4 +++ src/main/java/domain/GameTable.java | 4 +++ src/main/java/domain/member/Members.java | 35 ++++++++++++++----- src/main/java/domain/member/Player.java | 5 +++ src/main/java/domain/state/Running.java | 2 +- .../presentation/BlackjackController.java | 3 ++ 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/main/java/application/BlackjackService.java b/src/main/java/application/BlackjackService.java index aa09de1d873..583f4353e23 100644 --- a/src/main/java/application/BlackjackService.java +++ b/src/main/java/application/BlackjackService.java @@ -37,6 +37,10 @@ public RoundResult startOneRound(String memberName) { return new RoundResult(playerCards, isBust); } + public void endPlayerRound(String playerName) { + getGameTable().changePlayerState(playerName); + } + public boolean checkDealerDrawable() { return getGameTable().drawForDealer(); } diff --git a/src/main/java/domain/GameTable.java b/src/main/java/domain/GameTable.java index b85bdba0ad0..924c5eb1f99 100644 --- a/src/main/java/domain/GameTable.java +++ b/src/main/java/domain/GameTable.java @@ -33,6 +33,10 @@ public boolean isPlayerBust(String memberName) { return members.isPlayerFinished(memberName); } + public void changePlayerState(String playerName) { + members.changePlayerStateToStay(playerName); + } + public List drawForMember(String memberName) { members.provideCardToPlayer(memberName, deck.draw()); return members.findCardByName(memberName); diff --git a/src/main/java/domain/member/Members.java b/src/main/java/domain/member/Members.java index c3dc5f6e97c..e40d083d103 100644 --- a/src/main/java/domain/member/Members.java +++ b/src/main/java/domain/member/Members.java @@ -1,11 +1,10 @@ package domain.member; -import domain.MatchResult; import domain.card.Card; +import domain.state.DealerHit; import domain.state.Hit; -import domain.state.Stay; -import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -16,7 +15,7 @@ public class Members { private final List players; public Members(Map playerBets) { - this.dealer = new Dealer(new Hit(new Hand())); + this.dealer = new Dealer(new DealerHit(new Hand())); this.players = playerBets.entrySet().stream() .map(entry -> new Player(entry.getKey(), entry.getValue(), new Hit(new Hand()))) .toList(); @@ -70,21 +69,39 @@ public List getAllPlayerName() { // return player.compareScoreWith(dealer); // } - public List calculatePlayerProfits() { - return players.stream() - .map(Player::calculateProfit) - .toList(); + public Map calculateFinalProfits() { + validateFinished(); + Map totalResults = new LinkedHashMap<>(); + players.forEach(player -> + totalResults.put(player.name(), player.calculateProfit(dealer))); + int totalPlayerProfit = totalResults.values() + .stream() + .mapToInt(Integer::intValue) + .sum(); + totalResults.put(dealer.name(), -1 * totalPlayerProfit); + return totalResults; + } + + private void validateFinished() { + if (!dealer.isFinished() || players.stream() + .anyMatch(player -> !player.isFinished())) { + throw new IllegalArgumentException("게임이 끝나지 않은 사람이 있습니다."); + } } public boolean isPlayerFinished(String name) { return findByPlayerName(name).isFinished(); } + public void changePlayerStateToStay(String playerName) { + findByPlayerName(playerName).changeToStay(); + } + public String getDealerName() { return dealer.name(); } - private Member findByPlayerName(String playerName) { + private Player findByPlayerName(String playerName) { return players.stream() .filter(player -> player.name().equals(playerName)) .findAny() diff --git a/src/main/java/domain/member/Player.java b/src/main/java/domain/member/Player.java index 3d93fc16e7b..2cf5757f869 100644 --- a/src/main/java/domain/member/Player.java +++ b/src/main/java/domain/member/Player.java @@ -1,6 +1,7 @@ package domain.member; import domain.state.State; +import domain.state.Stay; public class Player extends Member { private final Money betMoney; @@ -10,6 +11,10 @@ public Player(String name, Money betMoney, State initialState) { this.betMoney = betMoney; } + public void changeToStay() { + state = new Stay(state.hand()); + } + public int calculateProfit(Member member) { return betMoney.calculateProfit(state.earningRate(member.state)); } diff --git a/src/main/java/domain/state/Running.java b/src/main/java/domain/state/Running.java index a9efa700599..aec71a8e434 100644 --- a/src/main/java/domain/state/Running.java +++ b/src/main/java/domain/state/Running.java @@ -18,6 +18,6 @@ public boolean isFinished() { @Override public double earningRate(State dealerState) { - return 0; + throw new IllegalStateException("게임이 끝나지 않은 상태에서는 수익률을 계산할 수 없습니다."); } } diff --git a/src/main/java/presentation/BlackjackController.java b/src/main/java/presentation/BlackjackController.java index 8b50f308c39..a62a3df9aa3 100644 --- a/src/main/java/presentation/BlackjackController.java +++ b/src/main/java/presentation/BlackjackController.java @@ -74,5 +74,8 @@ private void playAllRoundOfPlayer(String playerName) { outputView.printCurrentCard(playerName, roundResult); isBust = roundResult.isBust(); } + if (!isBust) { + blackjackService.endPlayerRound(playerName); + } } } From 22ab1cab0d89c5317380e217ea5b31fb1da7c0f8 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 05:05:36 +0900 Subject: [PATCH 38/53] =?UTF-8?q?refactor(Member):=20Bust=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EB=A5=BC=20=EC=9D=B4=EC=9A=A9=ED=95=B4=EC=84=9C=20Bus?= =?UTF-8?q?t=EC=9D=B8=EC=A7=80=20=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/GameTable.java | 2 +- src/main/java/domain/member/Member.java | 5 +++++ src/main/java/domain/member/Members.java | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/domain/GameTable.java b/src/main/java/domain/GameTable.java index 924c5eb1f99..1b4eff46b63 100644 --- a/src/main/java/domain/GameTable.java +++ b/src/main/java/domain/GameTable.java @@ -30,7 +30,7 @@ public void distributeInitCard() { } public boolean isPlayerBust(String memberName) { - return members.isPlayerFinished(memberName); + return members.isPlayerBust(memberName); } public void changePlayerState(String playerName) { diff --git a/src/main/java/domain/member/Member.java b/src/main/java/domain/member/Member.java index d7e19a152a9..7b7267ad59a 100644 --- a/src/main/java/domain/member/Member.java +++ b/src/main/java/domain/member/Member.java @@ -2,6 +2,7 @@ import domain.MatchResult; import domain.card.Card; +import domain.state.Bust; import domain.state.State; import java.util.List; @@ -37,4 +38,8 @@ public boolean isFinished() { public void receiveCard(Card card) { state = state.draw(card); } + + public boolean isBust() { + return state instanceof Bust; + } } diff --git a/src/main/java/domain/member/Members.java b/src/main/java/domain/member/Members.java index e40d083d103..8f204b4b933 100644 --- a/src/main/java/domain/member/Members.java +++ b/src/main/java/domain/member/Members.java @@ -89,8 +89,8 @@ private void validateFinished() { } } - public boolean isPlayerFinished(String name) { - return findByPlayerName(name).isFinished(); + public boolean isPlayerBust(String name) { + return findByPlayerName(name).isBust(); } public void changePlayerStateToStay(String playerName) { From b8375dab2ee63ec2c895bccbf33f677e0dac8245 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 05:36:25 +0900 Subject: [PATCH 39/53] =?UTF-8?q?refactor(Service):=20=EB=AA=A8=EB=93=A0?= =?UTF-8?q?=20=EB=A9=A4=EB=B2=84=EC=9D=98=20=EA=B2=8C=EC=9E=84=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EB=B0=98=ED=99=98=20=EA=B8=B0=EB=8A=A5=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/application/BlackjackService.java | 7 +++++-- src/main/java/domain/GameTable.java | 8 +++----- src/main/java/dto/GameResult.java | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/application/BlackjackService.java b/src/main/java/application/BlackjackService.java index 583f4353e23..96cc1c4cd15 100644 --- a/src/main/java/application/BlackjackService.java +++ b/src/main/java/application/BlackjackService.java @@ -46,11 +46,14 @@ public boolean checkDealerDrawable() { } public List getMemberStatuses() { - return getGameTable().checkMemberStatuses(); + return getGameTable().getMemberStatuses(); } public List getGameResults() { - return getGameTable().checkGameResult(); + Map profits = gameTable.getFinalProfits(); + return profits.entrySet().stream() + .map(entry -> new GameResult(entry.getKey(), entry.getValue())) + .toList(); } private GameTable getGameTable() { diff --git a/src/main/java/domain/GameTable.java b/src/main/java/domain/GameTable.java index 1b4eff46b63..2c0572a1612 100644 --- a/src/main/java/domain/GameTable.java +++ b/src/main/java/domain/GameTable.java @@ -3,7 +3,6 @@ import domain.card.Card; import domain.card.Deck; import domain.member.Money; -import dto.GameResult; import dto.MemberStatus; import domain.member.Members; import java.util.ArrayList; @@ -50,7 +49,7 @@ public boolean drawForDealer() { return false; } - public List checkMemberStatuses() { + public List getMemberStatuses() { List memberStatuses = new ArrayList<>(); memberStatuses.add( new MemberStatus(members.getDealerName(), members.findDealerCards(), members.checkDealerScore())); @@ -61,8 +60,7 @@ public List checkMemberStatuses() { return List.copyOf(memberStatuses); } - public List checkGameResult() { - // 컴파일 에러를 막기 위해 기존 구조만 유지 - return new ArrayList<>(); + public Map getFinalProfits() { + return members.calculateFinalProfits(); } } diff --git a/src/main/java/dto/GameResult.java b/src/main/java/dto/GameResult.java index 1ed7e419be0..3be81f88a46 100644 --- a/src/main/java/dto/GameResult.java +++ b/src/main/java/dto/GameResult.java @@ -5,6 +5,6 @@ public record GameResult( String name, - List result + int result ) { } From 2b3f0e099c6581cf8d19946354ace087b39499ce Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 05:37:28 +0900 Subject: [PATCH 40/53] =?UTF-8?q?refactor(View):=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=EB=A9=A4=EB=B2=84=EC=9D=98=20=EA=B2=8C=EC=9E=84=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EC=B6=9C=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/presentation/ui/InputView.java | 1 + src/main/java/presentation/ui/OutputView.java | 46 ++++--------------- .../java/presentation/ui/ViewMessage.java | 4 +- 3 files changed, 10 insertions(+), 41 deletions(-) diff --git a/src/main/java/presentation/ui/InputView.java b/src/main/java/presentation/ui/InputView.java index bd4742e6b1a..1112b14ee78 100644 --- a/src/main/java/presentation/ui/InputView.java +++ b/src/main/java/presentation/ui/InputView.java @@ -35,6 +35,7 @@ public List readPlayerNames() { public int readPlayerBetAmount(String name) { try { + System.out.println(); System.out.println(PLAYER_BET_AMOUNT_MESSAGE.format(name)); String inputBetAmount = bufferedReader.readLine(); return Integer.parseInt(inputBetAmount); diff --git a/src/main/java/presentation/ui/OutputView.java b/src/main/java/presentation/ui/OutputView.java index 4b039e1de19..9c6bc062e37 100644 --- a/src/main/java/presentation/ui/OutputView.java +++ b/src/main/java/presentation/ui/OutputView.java @@ -38,7 +38,12 @@ public void printFinalMemberStatus(List statuses) { public void printGameResult(List gameResults) { System.out.println(FINAL_GAME_RESULT_MESSAGE.format()); - gameResults.forEach(this::printMemberResult); + gameResults.stream() + .filter(result -> result.name().equals(DEALER_NAME)) + .forEach(this::printMemberResult); + gameResults.stream() + .filter(result -> !result.name().equals(DEALER_NAME)) + .forEach(this::printMemberResult); } private void printDistributeMessage(List playerStatuses) { @@ -78,42 +83,7 @@ private void printFinalMemberCardAndResult(MemberStatus status) { private void printMemberResult(GameResult gameResult) { String name = gameResult.name(); - List results = gameResult.result(); - if (name.equals(DEALER_NAME)) { - printDealerResult(results, name); - return; - } - printPlayerResult(results.getFirst(), name); - } - - private void printPlayerResult(MatchResult playerResult, String name) { - if (playerResult == MatchResult.WIN) { - System.out.println(PLAYER_GAME_WIN.format(name)); - return; - } - if (playerResult == MatchResult.DRAW) { - System.out.println(PLAYER_GAME_DRAW.format(name)); - return; - } - System.out.println(PLAYER_GAME_LOSE.format(name)); - } - - private void printDealerResult(List results, String name) { - int win = countResult(results, MatchResult.WIN); - int draw = countResult(results, MatchResult.DRAW); - int lose = countResult(results, MatchResult.LOSE); - - StringBuilder dealerResult = new StringBuilder(); - dealerResult.append(name).append(": "); - if (win > 0) dealerResult.append(win).append("승 "); - if (draw > 0) dealerResult.append(draw).append("무 "); - if (lose > 0) dealerResult.append(lose).append("패 "); - System.out.println(dealerResult); - } - - private int countResult(List results, MatchResult target) { - return (int) results.stream() - .filter(result -> result == target) - .count(); + int result = gameResult.result(); + System.out.println(MEMBER_GAME_RESULT_MESSAGE.format(name, result)); } } diff --git a/src/main/java/presentation/ui/ViewMessage.java b/src/main/java/presentation/ui/ViewMessage.java index 128be152a8c..43016fda817 100644 --- a/src/main/java/presentation/ui/ViewMessage.java +++ b/src/main/java/presentation/ui/ViewMessage.java @@ -9,9 +9,7 @@ public enum ViewMessage { PLAYER_NAME_MESSAGE("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"), PLAYER_BET_AMOUNT_MESSAGE("%s의 배팅 금액은?"), FINAL_GAME_RESULT_MESSAGE("## 최종 승패"), - PLAYER_GAME_WIN("%s: 승"), - PLAYER_GAME_DRAW("%s: 무"), - PLAYER_GAME_LOSE("%s: 패"); + MEMBER_GAME_RESULT_MESSAGE("%s: %d"); private final String word; From 2d7bfcfdf2a7a2567aa50a3d2475082e83b0e4f0 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 05:39:23 +0900 Subject: [PATCH 41/53] =?UTF-8?q?fix(State):=20=EB=B8=94=EB=9E=99=EC=9E=AD?= =?UTF-8?q?=20=EC=8B=9C=20=EB=B2=A0=ED=8C=85=EA=B8=88=EC=97=90=201.5?= =?UTF-8?q?=EB=B0=B0=20=EC=A0=81=EC=9A=A9=EC=9D=B4=20=EC=95=88=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 각 State별 stay() 기능 추가 --- src/main/java/domain/member/Player.java | 3 +-- src/main/java/domain/state/Blackjack.java | 5 +++++ src/main/java/domain/state/Bust.java | 5 +++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/domain/member/Player.java b/src/main/java/domain/member/Player.java index 2cf5757f869..59e3313499d 100644 --- a/src/main/java/domain/member/Player.java +++ b/src/main/java/domain/member/Player.java @@ -1,7 +1,6 @@ package domain.member; import domain.state.State; -import domain.state.Stay; public class Player extends Member { private final Money betMoney; @@ -12,7 +11,7 @@ public Player(String name, Money betMoney, State initialState) { } public void changeToStay() { - state = new Stay(state.hand()); + this.state = state.stay(); } public int calculateProfit(Member member) { diff --git a/src/main/java/domain/state/Blackjack.java b/src/main/java/domain/state/Blackjack.java index f5983d9f3c1..dc0be49525a 100644 --- a/src/main/java/domain/state/Blackjack.java +++ b/src/main/java/domain/state/Blackjack.java @@ -16,4 +16,9 @@ public double earningRate(State dealerState) { } return MatchResult.BLACKJACK_WIN.profitRate(); } + + @Override + public State stay() { + return this; + } } diff --git a/src/main/java/domain/state/Bust.java b/src/main/java/domain/state/Bust.java index 2771026a73e..a5f788e3241 100644 --- a/src/main/java/domain/state/Bust.java +++ b/src/main/java/domain/state/Bust.java @@ -12,4 +12,9 @@ public Bust(Hand hand) { public double earningRate(State dealerState) { return -1.0; } + + @Override + public State stay() { + return this; + } } From 53d42bda8b83d074d7a4427ec878b1673b4c3ed2 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 05:48:16 +0900 Subject: [PATCH 42/53] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=EC=BB=A8?= =?UTF-8?q?=EB=B2=A4=EC=85=98=20=EB=A7=9E=EC=B6=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/GameTable.java | 1 + src/main/java/domain/member/Member.java | 1 - src/main/java/domain/member/Members.java | 11 ----------- src/main/java/dto/GameResult.java | 3 --- src/main/java/dto/MemberStatus.java | 1 + src/main/java/dto/RoundResult.java | 1 + src/main/java/presentation/ui/OutputView.java | 2 +- src/test/java/domain/GameTableTest.java | 1 + src/test/java/domain/card/CardPatternTest.java | 1 + src/test/java/domain/card/DeckTest.java | 1 + src/test/java/domain/member/HandTest.java | 2 ++ src/test/java/domain/member/MembersTest.java | 1 + 12 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/main/java/domain/GameTable.java b/src/main/java/domain/GameTable.java index 2c0572a1612..e5d7f524440 100644 --- a/src/main/java/domain/GameTable.java +++ b/src/main/java/domain/GameTable.java @@ -5,6 +5,7 @@ import domain.member.Money; import dto.MemberStatus; import domain.member.Members; + import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/src/main/java/domain/member/Member.java b/src/main/java/domain/member/Member.java index 7b7267ad59a..585c51cf3fa 100644 --- a/src/main/java/domain/member/Member.java +++ b/src/main/java/domain/member/Member.java @@ -1,6 +1,5 @@ package domain.member; -import domain.MatchResult; import domain.card.Card; import domain.state.Bust; import domain.state.State; diff --git a/src/main/java/domain/member/Members.java b/src/main/java/domain/member/Members.java index 8f204b4b933..445e50be02e 100644 --- a/src/main/java/domain/member/Members.java +++ b/src/main/java/domain/member/Members.java @@ -58,17 +58,6 @@ public List getAllPlayerName() { .toList(); } -// public List determineDealerGameResult() { -// List gameResult = new ArrayList<>(); -// players.forEach(player -> gameResult.add(dealer.compareScoreWith(player))); -// return List.copyOf(gameResult); -// } -// -// public MatchResult determinePlayerGameResult(String name) { -// Member player = findByPlayerName(name); -// return player.compareScoreWith(dealer); -// } - public Map calculateFinalProfits() { validateFinished(); Map totalResults = new LinkedHashMap<>(); diff --git a/src/main/java/dto/GameResult.java b/src/main/java/dto/GameResult.java index 3be81f88a46..8244995c03c 100644 --- a/src/main/java/dto/GameResult.java +++ b/src/main/java/dto/GameResult.java @@ -1,8 +1,5 @@ package dto; -import domain.MatchResult; -import java.util.List; - public record GameResult( String name, int result diff --git a/src/main/java/dto/MemberStatus.java b/src/main/java/dto/MemberStatus.java index 36141f20ce7..eb4e0e4bdfd 100644 --- a/src/main/java/dto/MemberStatus.java +++ b/src/main/java/dto/MemberStatus.java @@ -1,6 +1,7 @@ package dto; import domain.card.Card; + import java.util.List; public record MemberStatus( diff --git a/src/main/java/dto/RoundResult.java b/src/main/java/dto/RoundResult.java index 7f63203a66e..c195fa4bb81 100644 --- a/src/main/java/dto/RoundResult.java +++ b/src/main/java/dto/RoundResult.java @@ -1,6 +1,7 @@ package dto; import domain.card.Card; + import java.util.List; public record RoundResult( diff --git a/src/main/java/presentation/ui/OutputView.java b/src/main/java/presentation/ui/OutputView.java index 9c6bc062e37..a7a53e0f20a 100644 --- a/src/main/java/presentation/ui/OutputView.java +++ b/src/main/java/presentation/ui/OutputView.java @@ -4,9 +4,9 @@ import dto.RoundResult; import domain.card.Card; -import domain.MatchResult; import dto.GameResult; import dto.MemberStatus; + import java.util.List; import java.util.stream.Collectors; diff --git a/src/test/java/domain/GameTableTest.java b/src/test/java/domain/GameTableTest.java index 0e2f9a7f192..d336a52c308 100644 --- a/src/test/java/domain/GameTableTest.java +++ b/src/test/java/domain/GameTableTest.java @@ -2,6 +2,7 @@ import domain.card.Card; import domain.card.FixedDeck; + import java.util.List; import java.util.Map; diff --git a/src/test/java/domain/card/CardPatternTest.java b/src/test/java/domain/card/CardPatternTest.java index 0ebd55597b4..208d5947b09 100644 --- a/src/test/java/domain/card/CardPatternTest.java +++ b/src/test/java/domain/card/CardPatternTest.java @@ -1,6 +1,7 @@ package domain.card; import java.util.NoSuchElementException; + import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/test/java/domain/card/DeckTest.java b/src/test/java/domain/card/DeckTest.java index 676d62b31b3..ad672bfdb64 100644 --- a/src/test/java/domain/card/DeckTest.java +++ b/src/test/java/domain/card/DeckTest.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.NoSuchElementException; + import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/domain/member/HandTest.java b/src/test/java/domain/member/HandTest.java index 679f17be18d..5b394cc6c55 100644 --- a/src/test/java/domain/member/HandTest.java +++ b/src/test/java/domain/member/HandTest.java @@ -2,9 +2,11 @@ import domain.card.Card; import constant.exception.DuplicatedException; + import java.util.LinkedList; import java.util.List; import java.util.Queue; + import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/domain/member/MembersTest.java b/src/test/java/domain/member/MembersTest.java index cdc709940cf..6149bf22173 100644 --- a/src/test/java/domain/member/MembersTest.java +++ b/src/test/java/domain/member/MembersTest.java @@ -1,6 +1,7 @@ package domain.member; import domain.card.Card; + import java.util.Map; import org.junit.jupiter.api.DisplayName; From 457c5a6e8495a39569bd2e912acb50064c8c8ee1 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 05:59:48 +0900 Subject: [PATCH 43/53] =?UTF-8?q?refactor(Dealer):=20=EB=94=9C=EB=9F=AC=20?= =?UTF-8?q?=EB=BD=91=EA=B8=B0=20=EC=A1=B0=EA=B1=B4=20=EB=B9=84=EA=B5=90=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=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/GameTable.java | 2 +- src/main/java/domain/member/Dealer.java | 5 ----- src/main/java/domain/member/Members.java | 4 ++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/java/domain/GameTable.java b/src/main/java/domain/GameTable.java index e5d7f524440..c5b08500f8c 100644 --- a/src/main/java/domain/GameTable.java +++ b/src/main/java/domain/GameTable.java @@ -43,7 +43,7 @@ public List drawForMember(String memberName) { } public boolean drawForDealer() { - if (members.isMeetTheDrawConditionForDealer()) { + if (members.canTheDealerDraw()) { members.provideCardToDealer(deck.draw()); return true; } diff --git a/src/main/java/domain/member/Dealer.java b/src/main/java/domain/member/Dealer.java index 26b2e137a36..72e640f151b 100644 --- a/src/main/java/domain/member/Dealer.java +++ b/src/main/java/domain/member/Dealer.java @@ -3,13 +3,8 @@ import domain.state.State; public class Dealer extends Member { - private static final int DEALER_DRAW_CONDITION = 16; public Dealer(State initialState) { super(DEALER_NAME, initialState); } - - public boolean isMeetTheDrawCondition() { - return currentScore() <= DEALER_DRAW_CONDITION; - } } diff --git a/src/main/java/domain/member/Members.java b/src/main/java/domain/member/Members.java index 445e50be02e..d3b8fe187d8 100644 --- a/src/main/java/domain/member/Members.java +++ b/src/main/java/domain/member/Members.java @@ -48,8 +48,8 @@ public int checkDealerScore() { return dealer.currentScore(); } - public boolean isMeetTheDrawConditionForDealer() { - return dealer.isMeetTheDrawCondition(); + public boolean canTheDealerDraw() { + return !dealer.isFinished(); } public List getAllPlayerName() { From 9139edc7bc1f721f2d0eed448b2840a5aceeb6ac Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 06:01:48 +0900 Subject: [PATCH 44/53] =?UTF-8?q?refactor(Dealer):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=83=81=EC=88=98=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/member/Member.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/domain/member/Member.java b/src/main/java/domain/member/Member.java index 585c51cf3fa..7acb0288cd7 100644 --- a/src/main/java/domain/member/Member.java +++ b/src/main/java/domain/member/Member.java @@ -8,7 +8,6 @@ public abstract class Member { protected static final String DEALER_NAME = "딜러"; - protected static final int BUST_CONDITION = 21; private final String name; protected State state; From 1351ebb793ae25447e34033fb0ce6b6f3a0b0124 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 06:11:52 +0900 Subject: [PATCH 45/53] =?UTF-8?q?docs(README):=20=EC=B2=B4=ED=81=AC=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 32c45c07c91..9ffdf274951 100644 --- a/README.md +++ b/README.md @@ -4,30 +4,30 @@ ## 구현할 기능 목록 ### 1. 카드 도메인 고도화 -- [ ] **카드 캐싱(Flyweight Pattern)**: 52장의 카드를 미리 생성하고 재사용한다. -- [ ] **셔플 전략 주입**: 테스트 가능하도록 셔플 로직을 인터페이스로 분리한다. +- [x] **카드 캐싱(Flyweight Pattern)**: 52장의 카드를 미리 생성하고 재사용한다. +- [x] **셔플 전략 주입**: 테스트 가능하도록 셔플 로직을 인터페이스로 분리한다. ### 2. 베팅 도메인 추가 -- [ ] **금액(Money) 객체**: 베팅 금액 원시 값을 포장하고, 수익률에 따른 계산 로직을 가진다. -- [ ] **플레이어별 베팅**: 게임 시작 시 플레이어별로 베팅 금액을 입력받는다. +- [x] **금액(Money) 객체**: 베팅 금액 원시 값을 포장하고, 수익률에 따른 계산 로직을 가진다. +- [x] **플레이어별 베팅**: 게임 시작 시 플레이어별로 베팅 금액을 입력받는다. ### 3. 상태(State) 패턴 기반 로직 리팩토링 -- [ ] **상태 추상화**: `Started`, `Running`, `Finished` 등 게임의 상태를 객체로 정의한다. -- [ ] **상태 전이 구현**: +- [x] **상태 추상화**: `Started`, `Running`, `Finished` 등 게임의 상태를 객체로 정의한다. +- [x] **상태 전이 구현**: - `Hit` 상태에서 `draw` 호출 시 점수에 따라 `Hit` 혹은 `Bust`로 전이한다. - `Hit` 상태에서 `stay` 호출 시 `Stay` 상태로 전이한다. -- [ ] **수익률(Earnings Rate) 캡슐화**: +- [x] **수익률(Earnings Rate) 캡슐화**: - `Blackjack`: 1.5배 수익률 반환 - `Bust`: -1.0배 수익률 반환 - `Stay`: 딜러와 비교 결과에 따라 수익률 반환 ### 4. 게임 진행 및 정산 -- [ ] **수익 계산**: 게임 종료 후 플레이어의 상태에 따른 최종 수익을 계산한다. -- [ ] **딜러 최종 수익**: 모든 플레이어 수익의 합에 -1을 곱한 값을 도출한다. +- [x] **수익 계산**: 게임 종료 후 플레이어의 상태에 따른 최종 수익을 계산한다. +- [x] **딜러 최종 수익**: 모든 플레이어 수익의 합에 -1을 곱한 값을 도출한다. ### 5. UI 및 출력 -- [ ] 각 플레이어의 베팅 금액 입력 기능 -- [ ] 최종 결과 시 플레이어별/딜러 수익 합계 출력 +- [x] 각 플레이어의 베팅 금액 입력 기능 +- [x] 최종 결과 시 플레이어별/딜러 수익 합계 출력 ## 기능 요구사항 @@ -108,9 +108,11 @@ jason: -20000 --- 필수 기록: -- [ ] 기능 추가로 인해 수정한 위치 개수 +- [x] 기능 추가로 인해 수정한 위치 개수 ```markdown - +- 약 5곳 (State 클래스군, Members, Player, GameTable, BlackjackService) +- 상태 패턴 도입 후, 새로운 규칙(수익률) 추가 시 기존 클래스를 수정하지 않고 + 새로운 State 구현체만 만지면 되어 수정 범위가 제한됨. ``` - [ ] 사이클1 때보다 수정 범위가 줄었는가/늘었는가 ```markdown From ca8d35d9e1a36513de55bb76ee92723a96344cf9 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 06:44:22 +0900 Subject: [PATCH 46/53] =?UTF-8?q?feat(View):=20inputView,=20outputView?= =?UTF-8?q?=EB=A5=BC=20=EB=8B=B4=EB=8A=94=20BlackjackView=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Main.java | 4 ++- .../presentation/BlackjackController.java | 25 +++++++++---------- .../java/presentation/ui/BlackjackView.java | 7 ++++++ 3 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 src/main/java/presentation/ui/BlackjackView.java diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 159a4f427c0..8a114b80fc2 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -1,5 +1,6 @@ import application.BlackjackService; import presentation.BlackjackController; +import presentation.ui.BlackjackView; import presentation.ui.InputView; import presentation.ui.OutputView; @@ -9,7 +10,8 @@ public static void main(String[] args) { OutputView outputView = new OutputView(); BlackjackService blackjackService = new BlackjackService(); - BlackjackController blackjackController = new BlackjackController(blackjackService, inputView, outputView); + BlackjackController blackjackController = new BlackjackController(blackjackService, + new BlackjackView(inputView, outputView)); blackjackController.executeGame(); } diff --git a/src/main/java/presentation/BlackjackController.java b/src/main/java/presentation/BlackjackController.java index a62a3df9aa3..679f420fa17 100644 --- a/src/main/java/presentation/BlackjackController.java +++ b/src/main/java/presentation/BlackjackController.java @@ -9,23 +9,22 @@ import java.util.List; import java.util.Map; +import presentation.ui.BlackjackView; import presentation.ui.InputView; import presentation.ui.OutputView; public class BlackjackController { private final BlackjackService blackjackService; - private final InputView inputView; - private final OutputView outputView; + private final BlackjackView blackjackView; - public BlackjackController(BlackjackService blackjackService, InputView inputView, OutputView outputView) { + public BlackjackController(BlackjackService blackjackService, BlackjackView blackjackView) { this.blackjackService = blackjackService; - this.inputView = inputView; - this.outputView = outputView; + this.blackjackView = blackjackView; } public void executeGame() { - List playerNames = inputView.readPlayerNames(); + List playerNames = blackjackView.inputView().readPlayerNames(); setUpGame(playerNames); playGame(playerNames); checkDrawableOfDealer(); @@ -35,18 +34,18 @@ public void executeGame() { private void finalGameResult() { List gameResults = blackjackService.getGameResults(); - outputView.printGameResult(gameResults); + blackjackView.outputView().printGameResult(gameResults); } private void finalGameStatus() { List statuses = blackjackService.getMemberStatuses(); - outputView.printFinalMemberStatus(statuses); + blackjackView.outputView().printFinalMemberStatus(statuses); } private void checkDrawableOfDealer() { boolean dealerDrawable = blackjackService.checkDealerDrawable(); if (dealerDrawable) { - outputView.printDealerDrawOut(); + blackjackView.outputView().printDealerDrawOut(); } } @@ -59,19 +58,19 @@ private void playGame(List playerNames) { private void setUpGame(List playerNames) { Map playerBets = new HashMap<>(); for (String playerName : playerNames) { - int betAmount = inputView.readPlayerBetAmount(playerName); + int betAmount = blackjackView.inputView().readPlayerBetAmount(playerName); playerBets.put(playerName, betAmount); } blackjackService.initializeGame(playerBets); List memberStatuses = blackjackService.getMemberStatuses(); - outputView.printInitialStatus(memberStatuses); + blackjackView.outputView().printInitialStatus(memberStatuses); } private void playAllRoundOfPlayer(String playerName) { boolean isBust = false; - while (!isBust && inputView.playContinue(playerName)) { + while (!isBust && blackjackView.inputView().playContinue(playerName)) { RoundResult roundResult = blackjackService.startOneRound(playerName); - outputView.printCurrentCard(playerName, roundResult); + blackjackView.outputView().printCurrentCard(playerName, roundResult); isBust = roundResult.isBust(); } if (!isBust) { diff --git a/src/main/java/presentation/ui/BlackjackView.java b/src/main/java/presentation/ui/BlackjackView.java new file mode 100644 index 00000000000..2a6d452228f --- /dev/null +++ b/src/main/java/presentation/ui/BlackjackView.java @@ -0,0 +1,7 @@ +package presentation.ui; + +public record BlackjackView( + InputView inputView, + OutputView outputView +) { +} From b39a1d9eb86f0a7720567c4b292b4ab27f421d05 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Sun, 15 Mar 2026 06:55:11 +0900 Subject: [PATCH 47/53] =?UTF-8?q?fix:=2021=EC=9D=B4=20=EB=90=98=EC=96=B4?= =?UTF-8?q?=EB=8F=84=20=EC=A2=85=EB=A3=8C=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/application/BlackjackService.java | 4 ++++ src/main/java/domain/GameTable.java | 20 +++++++++++-------- src/main/java/domain/member/Members.java | 4 ++++ src/main/java/domain/state/Finished.java | 2 +- .../presentation/BlackjackController.java | 10 ++-------- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/main/java/application/BlackjackService.java b/src/main/java/application/BlackjackService.java index 96cc1c4cd15..7acf724c2d2 100644 --- a/src/main/java/application/BlackjackService.java +++ b/src/main/java/application/BlackjackService.java @@ -29,6 +29,10 @@ public void initializeGame(Map playerBetAmounts) { gameTable.distributeInitCard(); } + public boolean isFinishedByName(String playerName) { + return gameTable.isPlayerFinished(playerName); + } + public RoundResult startOneRound(String memberName) { List playerCards = getGameTable().drawForMember(memberName); diff --git a/src/main/java/domain/GameTable.java b/src/main/java/domain/GameTable.java index c5b08500f8c..32e868473f3 100644 --- a/src/main/java/domain/GameTable.java +++ b/src/main/java/domain/GameTable.java @@ -23,23 +23,27 @@ public GameTable(Map playerBets, Deck deck) { public void distributeInitCard() { members.provideCardToDealer(deck.draw()); members.provideCardToDealer(deck.draw()); - for (String memberName : members.getAllPlayerName()) { - members.provideCardToPlayer(memberName, deck.draw()); - members.provideCardToPlayer(memberName, deck.draw()); + for (String playerName : members.getAllPlayerName()) { + members.provideCardToPlayer(playerName, deck.draw()); + members.provideCardToPlayer(playerName, deck.draw()); } } - public boolean isPlayerBust(String memberName) { - return members.isPlayerBust(memberName); + public boolean isPlayerBust(String playerName) { + return members.isPlayerBust(playerName); + } + + public boolean isPlayerFinished(String playerName) { + return members.isPlayerFinishedByName(playerName); } public void changePlayerState(String playerName) { members.changePlayerStateToStay(playerName); } - public List drawForMember(String memberName) { - members.provideCardToPlayer(memberName, deck.draw()); - return members.findCardByName(memberName); + public List drawForMember(String playerName) { + members.provideCardToPlayer(playerName, deck.draw()); + return members.findCardByName(playerName); } public boolean drawForDealer() { diff --git a/src/main/java/domain/member/Members.java b/src/main/java/domain/member/Members.java index d3b8fe187d8..f26d135da52 100644 --- a/src/main/java/domain/member/Members.java +++ b/src/main/java/domain/member/Members.java @@ -48,6 +48,10 @@ public int checkDealerScore() { return dealer.currentScore(); } + public boolean isPlayerFinishedByName(String playerName) { + return findByPlayerName(playerName).isFinished(); + } + public boolean canTheDealerDraw() { return !dealer.isFinished(); } diff --git a/src/main/java/domain/state/Finished.java b/src/main/java/domain/state/Finished.java index 13c60123cac..dbb165987b6 100644 --- a/src/main/java/domain/state/Finished.java +++ b/src/main/java/domain/state/Finished.java @@ -21,6 +21,6 @@ public State draw(Card card) { @Override public State stay() { - throw new IllegalArgumentException("이미 종료된 상태입니다."); + return this; } } diff --git a/src/main/java/presentation/BlackjackController.java b/src/main/java/presentation/BlackjackController.java index 679f420fa17..d85c92c8b4e 100644 --- a/src/main/java/presentation/BlackjackController.java +++ b/src/main/java/presentation/BlackjackController.java @@ -10,8 +10,6 @@ import java.util.Map; import presentation.ui.BlackjackView; -import presentation.ui.InputView; -import presentation.ui.OutputView; public class BlackjackController { @@ -67,14 +65,10 @@ private void setUpGame(List playerNames) { } private void playAllRoundOfPlayer(String playerName) { - boolean isBust = false; - while (!isBust && blackjackView.inputView().playContinue(playerName)) { + while (!blackjackService.isFinishedByName(playerName) && blackjackView.inputView().playContinue(playerName)) { RoundResult roundResult = blackjackService.startOneRound(playerName); blackjackView.outputView().printCurrentCard(playerName, roundResult); - isBust = roundResult.isBust(); - } - if (!isBust) { - blackjackService.endPlayerRound(playerName); } + blackjackService.endPlayerRound(playerName); } } From 2a4b98867a4046f0530af5973eaa59f765d9e886 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Mon, 16 Mar 2026 16:43:42 +0900 Subject: [PATCH 48/53] =?UTF-8?q?refactor(Member):=20=EC=83=81=EC=86=8D=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=EC=97=90=EC=84=9C=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=EB=A5=BC=20=ED=99=9C=EC=9A=A9?= =?UTF-8?q?=ED=95=9C=20=EC=A1=B0=ED=95=A9=20=EB=B0=A9=EC=8B=9D=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/member/Dealer.java | 13 +++++-- .../member/{Member.java => MemberInfo.java} | 21 +++++++++-- src/main/java/domain/member/Members.java | 14 +++---- src/main/java/domain/member/Participant.java | 37 +++++++++++++++++++ src/main/java/domain/member/Player.java | 17 +++++---- src/test/java/domain/member/PlayerTest.java | 13 ++++--- 6 files changed, 86 insertions(+), 29 deletions(-) rename src/main/java/domain/member/{Member.java => MemberInfo.java} (63%) create mode 100644 src/main/java/domain/member/Participant.java diff --git a/src/main/java/domain/member/Dealer.java b/src/main/java/domain/member/Dealer.java index 72e640f151b..e4ecdf711b0 100644 --- a/src/main/java/domain/member/Dealer.java +++ b/src/main/java/domain/member/Dealer.java @@ -1,10 +1,15 @@ package domain.member; -import domain.state.State; +public class Dealer implements Participant { -public class Dealer extends Member { + private final MemberInfo dealerInfo; - public Dealer(State initialState) { - super(DEALER_NAME, initialState); + public Dealer(MemberInfo dealerInfo) { + this.dealerInfo = dealerInfo; + } + + @Override + public MemberInfo info() { + return dealerInfo; } } diff --git a/src/main/java/domain/member/Member.java b/src/main/java/domain/member/MemberInfo.java similarity index 63% rename from src/main/java/domain/member/Member.java rename to src/main/java/domain/member/MemberInfo.java index 7acb0288cd7..5a9fb95bf05 100644 --- a/src/main/java/domain/member/Member.java +++ b/src/main/java/domain/member/MemberInfo.java @@ -6,13 +6,18 @@ import java.util.List; -public abstract class Member { - protected static final String DEALER_NAME = "딜러"; +public class MemberInfo { + public static final String DEALER_NAME = "딜러"; private final String name; - protected State state; + private State state; - public Member(String name, State state) { + public MemberInfo(State state) { + this.name = DEALER_NAME; + this.state = state; + } + + public MemberInfo(String name, State state) { this.name = name; this.state = state; } @@ -21,6 +26,10 @@ public String name() { return name; } + public State state() { + return state; + } + public int currentScore() { return state.hand().calculateTotalValue(); } @@ -40,4 +49,8 @@ public void receiveCard(Card card) { public boolean isBust() { return state instanceof Bust; } + + public void changeToStay() { + this.state = state.stay(); + } } diff --git a/src/main/java/domain/member/Members.java b/src/main/java/domain/member/Members.java index f26d135da52..782cdec9494 100644 --- a/src/main/java/domain/member/Members.java +++ b/src/main/java/domain/member/Members.java @@ -15,14 +15,14 @@ public class Members { private final List players; public Members(Map playerBets) { - this.dealer = new Dealer(new DealerHit(new Hand())); + this.dealer = new Dealer(new MemberInfo(new DealerHit(new Hand()))); this.players = playerBets.entrySet().stream() - .map(entry -> new Player(entry.getKey(), entry.getValue(), new Hit(new Hand()))) + .map(entry -> new Player(new MemberInfo(entry.getKey(), new Hit(new Hand())), entry.getValue())) .toList(); } public void provideCardToPlayer(String playerName, Card card) { - Member player = findByPlayerName(playerName); + Player player = findByPlayerName(playerName); player.receiveCard(card); } @@ -31,7 +31,7 @@ public void provideCardToDealer(Card card) { } public List findCardByName(String playerName) { - Member player = findByPlayerName(playerName); + Player player = findByPlayerName(playerName); return player.currentCards(); } @@ -40,7 +40,7 @@ public List findDealerCards() { } public int checkPlayerScore(String playerName) { - Member player = findByPlayerName(playerName); + Player player = findByPlayerName(playerName); return player.currentScore(); } @@ -58,7 +58,7 @@ public boolean canTheDealerDraw() { public List getAllPlayerName() { return players.stream() - .map(Member::name) + .map(Player::name) .toList(); } @@ -66,7 +66,7 @@ public Map calculateFinalProfits() { validateFinished(); Map totalResults = new LinkedHashMap<>(); players.forEach(player -> - totalResults.put(player.name(), player.calculateProfit(dealer))); + totalResults.put(player.name(), player.calculateProfit(dealer.info()))); int totalPlayerProfit = totalResults.values() .stream() .mapToInt(Integer::intValue) diff --git a/src/main/java/domain/member/Participant.java b/src/main/java/domain/member/Participant.java new file mode 100644 index 00000000000..be3570965e7 --- /dev/null +++ b/src/main/java/domain/member/Participant.java @@ -0,0 +1,37 @@ +package domain.member; + +import domain.card.Card; +import java.util.List; + +public interface Participant { + + MemberInfo info(); + + default String name() { + return info().name(); + } + + default void receiveCard(Card card) { + info().receiveCard(card); + } + + default int currentScore() { + return info().currentScore(); + } + + default List currentCards() { + return info().currentCards(); + } + + default boolean isFinished() { + return info().isFinished(); + } + + default boolean isBust() { + return info().isBust(); + } + + default void changeToStay() { + info().changeToStay(); + } +} diff --git a/src/main/java/domain/member/Player.java b/src/main/java/domain/member/Player.java index 59e3313499d..26201f4dbb9 100644 --- a/src/main/java/domain/member/Player.java +++ b/src/main/java/domain/member/Player.java @@ -1,20 +1,21 @@ package domain.member; -import domain.state.State; +public class Player implements Participant { -public class Player extends Member { + private final MemberInfo playerInfo; private final Money betMoney; - public Player(String name, Money betMoney, State initialState) { - super(name, initialState); + public Player(MemberInfo playerInfo, Money betMoney) { + this.playerInfo = playerInfo; this.betMoney = betMoney; } - public void changeToStay() { - this.state = state.stay(); + @Override + public MemberInfo info() { + return playerInfo; } - public int calculateProfit(Member member) { - return betMoney.calculateProfit(state.earningRate(member.state)); + public int calculateProfit(MemberInfo memberInfo) { + return betMoney.calculateProfit(playerInfo.state().earningRate(memberInfo.state())); } } diff --git a/src/test/java/domain/member/PlayerTest.java b/src/test/java/domain/member/PlayerTest.java index 7d248482f5f..fb1ae3d8adb 100644 --- a/src/test/java/domain/member/PlayerTest.java +++ b/src/test/java/domain/member/PlayerTest.java @@ -15,11 +15,11 @@ public class PlayerTest { @Test void calculateProfit_StateIsBlackjack_ReturnAccurateEarning() { Money betMoney = new Money(10000); - Player player = new Player("pobi", betMoney, new Blackjack(new Hand())); - Member dealer = new Dealer(new Stay(new Hand().appendCard(Card.from("10", "하트")))); + Player player = new Player(new MemberInfo("pobi", new Blackjack(new Hand())), betMoney); + Dealer dealer = new Dealer(new MemberInfo(new Stay(new Hand().appendCard(Card.from("10", "하트"))))); int expected = 15000; - int profit = player.calculateProfit(dealer); + int profit = player.calculateProfit(dealer.info()); assertThat(profit).isEqualTo(expected); } @@ -28,11 +28,12 @@ void calculateProfit_StateIsBlackjack_ReturnAccurateEarning() { @Test void calculateProfit_StateIsBust_ReturnLostEarning() { Money betMoney = new Money(10000); - Player player = new Player("pobi", betMoney, new Bust(new Hand().appendCard(Card.from("10", "하트")))); - Member dealer = new Dealer(new Stay(new Hand().appendCard(Card.from("2", "클로버")))); + Player player = new Player(new MemberInfo("pobi", + new Bust(new Hand().appendCard(Card.from("10", "하트")))), betMoney); + Dealer dealer = new Dealer(new MemberInfo(new Stay(new Hand().appendCard(Card.from("2", "클로버"))))); int expected = -10000; - int profit = player.calculateProfit(dealer); + int profit = player.calculateProfit(dealer.info()); assertThat(profit).isEqualTo(expected); } From 7f2ca5464aa8cb229766cb11fd8ac25cca32a2da Mon Sep 17 00:00:00 2001 From: JYL35 Date: Mon, 16 Mar 2026 16:57:17 +0900 Subject: [PATCH 49/53] =?UTF-8?q?refactor(Members):=20=ED=94=8C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=EC=99=80=20=EB=94=9C=EB=9F=AC=20=EC=8A=A4?= =?UTF-8?q?=EC=BD=94=EC=96=B4=20=EB=B0=98=ED=99=98=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/GameTable.java | 4 ++-- src/main/java/domain/member/Members.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/domain/GameTable.java b/src/main/java/domain/GameTable.java index 32e868473f3..af00014123a 100644 --- a/src/main/java/domain/GameTable.java +++ b/src/main/java/domain/GameTable.java @@ -57,10 +57,10 @@ public boolean drawForDealer() { public List getMemberStatuses() { List memberStatuses = new ArrayList<>(); memberStatuses.add( - new MemberStatus(members.getDealerName(), members.findDealerCards(), members.checkDealerScore())); + new MemberStatus(members.getDealerName(), members.findDealerCards(), members.getDealerScore())); members.getAllPlayerName().stream() - .map(name -> new MemberStatus(name, members.findCardByName(name), members.checkPlayerScore(name))) + .map(name -> new MemberStatus(name, members.findCardByName(name), members.getPlayerScore(name))) .forEach(memberStatuses::add); return List.copyOf(memberStatuses); } diff --git a/src/main/java/domain/member/Members.java b/src/main/java/domain/member/Members.java index 782cdec9494..3656a03cc49 100644 --- a/src/main/java/domain/member/Members.java +++ b/src/main/java/domain/member/Members.java @@ -39,12 +39,12 @@ public List findDealerCards() { return dealer.currentCards(); } - public int checkPlayerScore(String playerName) { + public int getPlayerScore(String playerName) { Player player = findByPlayerName(playerName); return player.currentScore(); } - public int checkDealerScore() { + public int getDealerScore() { return dealer.currentScore(); } From b702593ff925b6bf0fbd140086df4d91df36ca87 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Mon, 16 Mar 2026 17:18:55 +0900 Subject: [PATCH 50/53] =?UTF-8?q?refactor(State):=20Bust=EC=99=80=20Blackj?= =?UTF-8?q?ack=20=ED=99=95=EC=9D=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/member/MemberInfo.java | 2 +- src/main/java/domain/state/Blackjack.java | 7 ++++++- src/main/java/domain/state/Bust.java | 5 +++++ src/main/java/domain/state/Started.java | 10 ++++++++++ src/main/java/domain/state/State.java | 2 ++ src/main/java/domain/state/Stay.java | 12 ++++++++---- 6 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/main/java/domain/member/MemberInfo.java b/src/main/java/domain/member/MemberInfo.java index 5a9fb95bf05..ceb28e646b3 100644 --- a/src/main/java/domain/member/MemberInfo.java +++ b/src/main/java/domain/member/MemberInfo.java @@ -47,7 +47,7 @@ public void receiveCard(Card card) { } public boolean isBust() { - return state instanceof Bust; + return state.isBust(); } public void changeToStay() { diff --git a/src/main/java/domain/state/Blackjack.java b/src/main/java/domain/state/Blackjack.java index dc0be49525a..f03ef6d44f1 100644 --- a/src/main/java/domain/state/Blackjack.java +++ b/src/main/java/domain/state/Blackjack.java @@ -9,9 +9,14 @@ public Blackjack(Hand hand) { super(hand); } + @Override + public boolean isBlackjack() { + return true; + } + @Override public double earningRate(State dealerState) { - if (dealerState instanceof Blackjack) { + if (dealerState.isBlackjack()) { return MatchResult.DRAW.profitRate(); } return MatchResult.BLACKJACK_WIN.profitRate(); diff --git a/src/main/java/domain/state/Bust.java b/src/main/java/domain/state/Bust.java index a5f788e3241..2ae2d093d0f 100644 --- a/src/main/java/domain/state/Bust.java +++ b/src/main/java/domain/state/Bust.java @@ -8,6 +8,11 @@ public Bust(Hand hand) { super(hand); } + @Override + public boolean isBust() { + return true; + } + @Override public double earningRate(State dealerState) { return -1.0; diff --git a/src/main/java/domain/state/Started.java b/src/main/java/domain/state/Started.java index 1030d1edacf..9a547634831 100644 --- a/src/main/java/domain/state/Started.java +++ b/src/main/java/domain/state/Started.java @@ -9,6 +9,16 @@ protected Started(Hand hand) { this.hand = hand; } + @Override + public boolean isBust() { + return false; + } + + @Override + public boolean isBlackjack() { + return false; + } + @Override public Hand hand() { return hand; diff --git a/src/main/java/domain/state/State.java b/src/main/java/domain/state/State.java index dec0ba2e2a4..d7fd1d9e861 100644 --- a/src/main/java/domain/state/State.java +++ b/src/main/java/domain/state/State.java @@ -5,6 +5,8 @@ public interface State { boolean isFinished(); + boolean isBust(); + boolean isBlackjack(); double earningRate(State dealerState); State draw(Card card); State stay(); diff --git a/src/main/java/domain/state/Stay.java b/src/main/java/domain/state/Stay.java index 3c3cc9fd968..61a8ff1abf2 100644 --- a/src/main/java/domain/state/Stay.java +++ b/src/main/java/domain/state/Stay.java @@ -11,10 +11,10 @@ public Stay(Hand hand) { @Override public double earningRate(State dealerState) { - if (dealerState instanceof Bust) { + if (dealerState.isBust()) { return MatchResult.WIN.profitRate(); } - if (dealerState instanceof Blackjack) { + if (dealerState.isBlackjack()) { return MatchResult.LOSE.profitRate(); } return judgeScore(dealerState.hand().calculateTotalValue()); @@ -22,8 +22,12 @@ public double earningRate(State dealerState) { private double judgeScore(int dealerScore) { int myScore = hand.calculateTotalValue(); - if (myScore > dealerScore) return MatchResult.WIN.profitRate(); - if (myScore < dealerScore) return MatchResult.LOSE.profitRate(); + if (myScore > dealerScore) { + return MatchResult.WIN.profitRate(); + } + if (myScore < dealerScore) { + return MatchResult.LOSE.profitRate(); + } return MatchResult.DRAW.profitRate(); } } From 2490ef616bb6448f6c857a59ded1772e5b6f39fc Mon Sep 17 00:00:00 2001 From: JYL35 Date: Mon, 16 Mar 2026 17:23:36 +0900 Subject: [PATCH 51/53] =?UTF-8?q?refactor:=20=EC=B2=9C=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20=EA=B5=AC=EB=B6=84=20=EA=B8=B0=ED=98=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MoneyTest: 구현 파일의 위치에 맞게 변경 --- src/test/java/domain/GameTableTest.java | 2 +- src/test/java/domain/member/MembersTest.java | 16 ++++++++-------- .../java/domain/{card => member}/MoneyTest.java | 9 ++++----- src/test/java/domain/member/PlayerTest.java | 8 ++++---- 4 files changed, 17 insertions(+), 18 deletions(-) rename src/test/java/domain/{card => member}/MoneyTest.java (81%) diff --git a/src/test/java/domain/GameTableTest.java b/src/test/java/domain/GameTableTest.java index d336a52c308..5172f4215d5 100644 --- a/src/test/java/domain/GameTableTest.java +++ b/src/test/java/domain/GameTableTest.java @@ -26,7 +26,7 @@ void setUpTest() { Card.from("8", "클로버"), Card.from("7", "클로버") ); - Map playerBets = Map.of(pobiName, new Money(10000)); + Map playerBets = Map.of(pobiName, new Money(10_000)); this.gameTable = new GameTable(playerBets, new FixedDeck(cards)); } diff --git a/src/test/java/domain/member/MembersTest.java b/src/test/java/domain/member/MembersTest.java index 6149bf22173..4a63d72909c 100644 --- a/src/test/java/domain/member/MembersTest.java +++ b/src/test/java/domain/member/MembersTest.java @@ -11,7 +11,7 @@ public class MembersTest { - private final Money defaultMoney = new Money(10000); + private final Money defaultMoney = new Money(10_000); @DisplayName("멤버 명단을 정상적으로 불러오는지 테스트") @Test @@ -41,7 +41,7 @@ void provideCardToPlayer_CardGiven_MemberContainsCard() { @Test void calculateFinalProfits_GameOver_ReturnsCorrectResults() { String playerName = "pobi"; - Members members = new Members(Map.of(playerName, new Money(10000))); + Members members = new Members(Map.of(playerName, new Money(10_000))); members.provideCardToPlayer(playerName, Card.from("Q", "하트")); members.provideCardToPlayer(playerName, Card.from("K", "하트")); members.changePlayerStateToStay(playerName); @@ -50,15 +50,15 @@ void calculateFinalProfits_GameOver_ReturnsCorrectResults() { members.provideCardToDealer(Card.from("7", "하트")); Map results = members.calculateFinalProfits(); - assertThat(results.get(playerName)).isEqualTo(10000); - assertThat(results.get(members.getDealerName())).isEqualTo(-10000); + assertThat(results.get(playerName)).isEqualTo(10_000); + assertThat(results.get(members.getDealerName())).isEqualTo(-10_000); } @DisplayName("딜러와 플레이어가 모두 블랙잭이면 수익은 0원이다") @Test void calculateFinalProfits_BothBlackjack_ReturnsZero() { String playerName = "pobi"; - Members members = new Members(Map.of(playerName, new Money(10000))); + Members members = new Members(Map.of(playerName, new Money(10_000))); members.provideCardToPlayer(playerName, Card.from("A", "하트")); members.provideCardToPlayer(playerName, Card.from("K", "하트")); members.provideCardToDealer(Card.from("A", "스페이드")); @@ -74,7 +74,7 @@ void calculateFinalProfits_BothBlackjack_ReturnsZero() { @Test void calculateFinalProfits_DealerBust_PlayerWins() { String playerName = "pobi"; - Members members = new Members(Map.of(playerName, new Money(10000))); + Members members = new Members(Map.of(playerName, new Money(10_000))); members.provideCardToPlayer(playerName, Card.from("2", "하트")); members.provideCardToPlayer(playerName, Card.from("Q", "하트")); members.changePlayerStateToStay(playerName); @@ -83,7 +83,7 @@ void calculateFinalProfits_DealerBust_PlayerWins() { members.provideCardToDealer(Card.from("7", "하트")); Map results = members.calculateFinalProfits(); - assertThat(results.get(playerName)).isEqualTo(10000); - assertThat(results.get(members.getDealerName())).isEqualTo(-10000); + assertThat(results.get(playerName)).isEqualTo(10_000); + assertThat(results.get(members.getDealerName())).isEqualTo(-10_000); } } diff --git a/src/test/java/domain/card/MoneyTest.java b/src/test/java/domain/member/MoneyTest.java similarity index 81% rename from src/test/java/domain/card/MoneyTest.java rename to src/test/java/domain/member/MoneyTest.java index a73320e62e4..2d2e68848e1 100644 --- a/src/test/java/domain/card/MoneyTest.java +++ b/src/test/java/domain/member/MoneyTest.java @@ -1,6 +1,5 @@ -package domain.card; +package domain.member; -import domain.member.Money; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -12,7 +11,7 @@ public class MoneyTest { @DisplayName("금액이 같으면 같은 객체로 취급한다 (VO)") @Test void equals_SameAmount_ReturnTrue() { - assertThat(new Money(10000)).isEqualTo(new Money(10000)); + assertThat(new Money(10_000)).isEqualTo(new Money(10_000)); } @DisplayName("베팅 금액이 0원 이하이면 예외가 발생한다") @@ -25,11 +24,11 @@ void constructor_UnderZero_ThrowException() { @DisplayName("수익률을 곱해 최종 수익금을 계산한다") @Test void multiply_EarningRate_ReturnProfit() { - Money bet = new Money(10000); + Money bet = new Money(10_000); double earningRate = 1.5; int profit = bet.calculateProfit(earningRate); - assertThat(profit).isEqualTo(15000); + assertThat(profit).isEqualTo(15_000); } } diff --git a/src/test/java/domain/member/PlayerTest.java b/src/test/java/domain/member/PlayerTest.java index fb1ae3d8adb..cb51b234bac 100644 --- a/src/test/java/domain/member/PlayerTest.java +++ b/src/test/java/domain/member/PlayerTest.java @@ -14,10 +14,10 @@ public class PlayerTest { @DisplayName("플레이어가 블랙잭이고 딜러가 블랙잭이 아니면 베팅 금액의 1.5배 수익을 반환한다") @Test void calculateProfit_StateIsBlackjack_ReturnAccurateEarning() { - Money betMoney = new Money(10000); + Money betMoney = new Money(10_000); Player player = new Player(new MemberInfo("pobi", new Blackjack(new Hand())), betMoney); Dealer dealer = new Dealer(new MemberInfo(new Stay(new Hand().appendCard(Card.from("10", "하트"))))); - int expected = 15000; + int expected = 15_000; int profit = player.calculateProfit(dealer.info()); @@ -27,11 +27,11 @@ void calculateProfit_StateIsBlackjack_ReturnAccurateEarning() { @DisplayName("플레이어가 버스트 상태이면 딜러의 상태와 상관없이 베팅 금액을 모두 잃는다") @Test void calculateProfit_StateIsBust_ReturnLostEarning() { - Money betMoney = new Money(10000); + Money betMoney = new Money(10_000); Player player = new Player(new MemberInfo("pobi", new Bust(new Hand().appendCard(Card.from("10", "하트")))), betMoney); Dealer dealer = new Dealer(new MemberInfo(new Stay(new Hand().appendCard(Card.from("2", "클로버"))))); - int expected = -10000; + int expected = -10_000; int profit = player.calculateProfit(dealer.info()); From 3c24d3ffdcd4d70ea3df270dafa239d8162543f3 Mon Sep 17 00:00:00 2001 From: JYL35 Date: Mon, 16 Mar 2026 17:28:02 +0900 Subject: [PATCH 52/53] =?UTF-8?q?refactor:=20=EC=9E=98=EB=AA=BB=EB=90=9C?= =?UTF-8?q?=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=9E=91=EB=AA=85=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/member/Money.java | 4 ++-- src/test/java/domain/state/HitTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/domain/member/Money.java b/src/main/java/domain/member/Money.java index cd5a57ce36b..07f9b7e79f7 100644 --- a/src/main/java/domain/member/Money.java +++ b/src/main/java/domain/member/Money.java @@ -7,11 +7,11 @@ public class Money { private final int amount; public Money(int amount) { - validateUnderZero(amount); + validateMoreThanZero(amount); this.amount = amount; } - private void validateUnderZero(int amount) { + private void validateMoreThanZero(int amount) { if (amount <= 0) { throw new IllegalArgumentException("베팅 금액은 0보다 커야 합니다."); } diff --git a/src/test/java/domain/state/HitTest.java b/src/test/java/domain/state/HitTest.java index 5875be7c8b5..6cefa770e9c 100644 --- a/src/test/java/domain/state/HitTest.java +++ b/src/test/java/domain/state/HitTest.java @@ -44,7 +44,7 @@ void draw_TotalScoreIs21AndCardSizeIs2_ReturnBlackjack() { @DisplayName("플레이어가 카드를 뽑아 21점이 되었을 때 카드 개수가 2개가 아니면 Stay 상태를 반환한다") @Test - void draw_TotalScoreIs21AndCardSizeIsNot2_ReturnBlackjack() { + void draw_TotalScoreIs21AndCardSizeIsNot2_ReturnStay() { Hand hand = new Hand().appendCard(Card.from("10", "스페이드")) .appendCard(Card.from("9", "하트")); State state = new Hit(hand); From d88d116bef1ee8c6731ff09ee83dcc579c6e265b Mon Sep 17 00:00:00 2001 From: JYL35 Date: Mon, 16 Mar 2026 17:56:39 +0900 Subject: [PATCH 53/53] =?UTF-8?q?refactor(Service):=20GameTable=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=8B=9C=EC=A0=90=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/Main.java | 5 +-- .../java/application/BlackjackService.java | 24 ++++--------- .../presentation/BlackjackController.java | 34 ++++++++++--------- 3 files changed, 26 insertions(+), 37 deletions(-) diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 8a114b80fc2..694458d3bea 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -1,4 +1,3 @@ -import application.BlackjackService; import presentation.BlackjackController; import presentation.ui.BlackjackView; import presentation.ui.InputView; @@ -9,9 +8,7 @@ public static void main(String[] args) { InputView inputView = new InputView(); OutputView outputView = new OutputView(); - BlackjackService blackjackService = new BlackjackService(); - BlackjackController blackjackController = new BlackjackController(blackjackService, - new BlackjackView(inputView, outputView)); + BlackjackController blackjackController = new BlackjackController(new BlackjackView(inputView, outputView)); blackjackController.executeGame(); } diff --git a/src/main/java/application/BlackjackService.java b/src/main/java/application/BlackjackService.java index 7acf724c2d2..3367ba86786 100644 --- a/src/main/java/application/BlackjackService.java +++ b/src/main/java/application/BlackjackService.java @@ -14,12 +14,9 @@ public class BlackjackService { - private GameTable gameTable; + private final GameTable gameTable; - public BlackjackService() { - } - - public void initializeGame(Map playerBetAmounts) { + public BlackjackService(Map playerBetAmounts) { Map playerBets = new HashMap<>(); for (String name : playerBetAmounts.keySet()) { Money betMoney = new Money(playerBetAmounts.get(name)); @@ -34,23 +31,23 @@ public boolean isFinishedByName(String playerName) { } public RoundResult startOneRound(String memberName) { - List playerCards = getGameTable().drawForMember(memberName); + List playerCards = gameTable.drawForMember(memberName); - boolean isBust = getGameTable().isPlayerBust(memberName); + boolean isBust = gameTable.isPlayerBust(memberName); return new RoundResult(playerCards, isBust); } public void endPlayerRound(String playerName) { - getGameTable().changePlayerState(playerName); + gameTable.changePlayerState(playerName); } public boolean checkDealerDrawable() { - return getGameTable().drawForDealer(); + return gameTable.drawForDealer(); } public List getMemberStatuses() { - return getGameTable().getMemberStatuses(); + return gameTable.getMemberStatuses(); } public List getGameResults() { @@ -59,11 +56,4 @@ public List getGameResults() { .map(entry -> new GameResult(entry.getKey(), entry.getValue())) .toList(); } - - private GameTable getGameTable() { - if (gameTable == null) { - throw new IllegalStateException("게임이 초기화가 되지 않았습니다."); - } - return gameTable; - } } diff --git a/src/main/java/presentation/BlackjackController.java b/src/main/java/presentation/BlackjackController.java index d85c92c8b4e..44ca1f822f4 100644 --- a/src/main/java/presentation/BlackjackController.java +++ b/src/main/java/presentation/BlackjackController.java @@ -13,58 +13,60 @@ public class BlackjackController { - private final BlackjackService blackjackService; private final BlackjackView blackjackView; - public BlackjackController(BlackjackService blackjackService, BlackjackView blackjackView) { - this.blackjackService = blackjackService; + public BlackjackController(BlackjackView blackjackView) { this.blackjackView = blackjackView; } public void executeGame() { List playerNames = blackjackView.inputView().readPlayerNames(); - setUpGame(playerNames); - playGame(playerNames); - checkDrawableOfDealer(); - finalGameStatus(); - finalGameResult(); + BlackjackService blackjackService = new BlackjackService(readPlayerInitStatus(playerNames)); + setUpGame(blackjackService); + playGame(blackjackService, playerNames); + checkDrawableOfDealer(blackjackService); + finalGameStatus(blackjackService); + finalGameResult(blackjackService); } - private void finalGameResult() { + private void finalGameResult(BlackjackService blackjackService) { List gameResults = blackjackService.getGameResults(); blackjackView.outputView().printGameResult(gameResults); } - private void finalGameStatus() { + private void finalGameStatus(BlackjackService blackjackService) { List statuses = blackjackService.getMemberStatuses(); blackjackView.outputView().printFinalMemberStatus(statuses); } - private void checkDrawableOfDealer() { + private void checkDrawableOfDealer(BlackjackService blackjackService) { boolean dealerDrawable = blackjackService.checkDealerDrawable(); if (dealerDrawable) { blackjackView.outputView().printDealerDrawOut(); } } - private void playGame(List playerNames) { + private void playGame(BlackjackService blackjackService, List playerNames) { for (String playerName : playerNames) { - playAllRoundOfPlayer(playerName); + playAllRoundOfPlayer(blackjackService, playerName); } } - private void setUpGame(List playerNames) { + private Map readPlayerInitStatus(List playerNames) { Map playerBets = new HashMap<>(); for (String playerName : playerNames) { int betAmount = blackjackView.inputView().readPlayerBetAmount(playerName); playerBets.put(playerName, betAmount); } - blackjackService.initializeGame(playerBets); + return playerBets; + } + + private void setUpGame(BlackjackService blackjackService) { List memberStatuses = blackjackService.getMemberStatuses(); blackjackView.outputView().printInitialStatus(memberStatuses); } - private void playAllRoundOfPlayer(String playerName) { + private void playAllRoundOfPlayer(BlackjackService blackjackService, String playerName) { while (!blackjackService.isFinishedByName(playerName) && blackjackView.inputView().playContinue(playerName)) { RoundResult roundResult = blackjackService.startOneRound(playerName); blackjackView.outputView().printCurrentCard(playerName, roundResult);