From 40f9388f9dd7a264ed96872e918d29b48f07a6ac Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Wed, 4 Mar 2026 17:33:17 +0900 Subject: [PATCH 01/50] =?UTF-8?q?feat(Rule):=20=EB=B8=94=EB=9E=99=EC=9E=AD?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=EC=97=90=20=EB=8C=80=ED=95=9C=20Rule=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98=20=EB=B0=8F=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain/rule/BlackjackRule.java | 4 +++ .../domain/rule/DefaultBlackjackRule.java | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/main/java/team/blackjack/domain/rule/BlackjackRule.java create mode 100644 src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java diff --git a/src/main/java/team/blackjack/domain/rule/BlackjackRule.java b/src/main/java/team/blackjack/domain/rule/BlackjackRule.java new file mode 100644 index 00000000000..51d183c29d7 --- /dev/null +++ b/src/main/java/team/blackjack/domain/rule/BlackjackRule.java @@ -0,0 +1,4 @@ +package team.blackjack.domain.rule; + +public interface BlackjackRule { +} diff --git a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java new file mode 100644 index 00000000000..de1bcfbff00 --- /dev/null +++ b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java @@ -0,0 +1,25 @@ +package team.blackjack.domain.rule; + +import team.blackjack.domain.Dealer; +import team.blackjack.domain.Hand; + +public class DefaultBlackjackRule { + public static final int BLACKJACK = 21; + public static final int BLACKJACK_CARD_COUNT = 2; + public static final int DEALER_STAND_SCORE = 17; + + public boolean isBust(Hand hand) { + return hand.getScore() > BLACKJACK; + } + + public boolean isBlackjack(Hand hand) { + if (hand.getCardCount() != BLACKJACK_CARD_COUNT) { + return false; + } + return hand.getScore() == BLACKJACK; + } + + public boolean isDealerMustDraw(Dealer dealer) { + return dealer.getScore() < DEALER_STAND_SCORE; + } +} From 7038a8fa8c457beb6a5ff8b4e226abc259ed021a Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Thu, 5 Mar 2026 10:43:02 +0900 Subject: [PATCH 02/50] =?UTF-8?q?test(rule):=20=EB=B8=94=EB=9E=99=EC=9E=AD?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=EC=97=90=20=EB=8C=80=ED=95=9C=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/rule/DefaultBlackjackRuleTest.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/test/java/rule/DefaultBlackjackRuleTest.java diff --git a/src/test/java/rule/DefaultBlackjackRuleTest.java b/src/test/java/rule/DefaultBlackjackRuleTest.java new file mode 100644 index 00000000000..e2e3997b42e --- /dev/null +++ b/src/test/java/rule/DefaultBlackjackRuleTest.java @@ -0,0 +1,73 @@ +package rule; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import team.blackjack.domain.rule.DefaultBlackjackRule; + +class DefaultBlackjackRuleTest { + private static final DefaultBlackjackRule defaultBlackjackRule = new DefaultBlackjackRule(); + + @Test + void 각_핸드의_점수는_21점을_초과하는_경우_버스트이다() { + int handScore = 22; + + boolean isBust = defaultBlackjackRule.isBust(handScore); + + Assertions.assertEquals(true, isBust); + } + + @Test + void 각_핸드의_점수는_21점을_초과하지_않는_경우_버스트가_아니다() { + int handScore = 21; + + boolean isBust = defaultBlackjackRule.isBust(handScore); + + Assertions.assertEquals(false, isBust); + } + + @Test + void 각_핸드의_카드_개수가_2개이고_점수가_21점인_경우_블랙잭이다() { + int handScore = 21; + int cardCount = 2; + + boolean isBlackjack = defaultBlackjackRule.isBlackjack(handScore, cardCount); + + Assertions.assertEquals(true, isBlackjack); + } + + @Test + void 각_핸드의_카드_개수가_3개이상이고_점수가_21점인_경우_블랙잭이_아니다() { + int handScore = 21; + int cardCount = 3; + + boolean isBlackjack = defaultBlackjackRule.isBlackjack(handScore, cardCount); + + Assertions.assertEquals(false, isBlackjack); + } + + @Test + void 각_핸드의_점수가_21점이_아닌_경우_블랙잭이_아니다() { + int handScore = 20; + int cardCount = 2; + + boolean isBlackjack = defaultBlackjackRule.isBlackjack(handScore, cardCount); + + Assertions.assertEquals(false, isBlackjack); + } + + @Test + void 딜러의_점수가_17점_미만인_경우_딜러는_카드를_더_받아야한다() { + int dealerScore = 16; + boolean isDealerMustDraw = defaultBlackjackRule.isDealerMustDraw(dealerScore); + + Assertions.assertEquals(true, isDealerMustDraw); + } + + @Test + void 딜러의_점수가_17점_이상인_경우_딜러는_카드를_더_받지_않아야한다() { + int dealerScore = 17; + boolean isDealerMustDraw = defaultBlackjackRule.isDealerMustDraw(dealerScore); + + Assertions.assertEquals(false, isDealerMustDraw); + } +} From 9f743a8d13dd04d63ba8da9fe38b6a88cb3cb310 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Thu, 5 Mar 2026 17:32:19 +0900 Subject: [PATCH 03/50] =?UTF-8?q?feat(domain):=20=EB=B8=94=EB=9E=99?= =?UTF-8?q?=EC=9E=AD=20=EA=B4=80=EB=A0=A8=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/blackjack/domain/BlackjackGame.java | 40 +++++++++++++++++++ src/main/java/team/blackjack/domain/Card.java | 38 ++++++++++++++++++ .../java/team/blackjack/domain/Dealer.java | 22 ++++++++++ src/main/java/team/blackjack/domain/Deck.java | 26 ++++++++++++ src/main/java/team/blackjack/domain/Hand.java | 28 +++++++++++++ .../java/team/blackjack/domain/Player.java | 25 ++++++++++++ 6 files changed, 179 insertions(+) create mode 100644 src/main/java/team/blackjack/domain/BlackjackGame.java create mode 100644 src/main/java/team/blackjack/domain/Card.java create mode 100644 src/main/java/team/blackjack/domain/Dealer.java create mode 100644 src/main/java/team/blackjack/domain/Deck.java create mode 100644 src/main/java/team/blackjack/domain/Hand.java create mode 100644 src/main/java/team/blackjack/domain/Player.java diff --git a/src/main/java/team/blackjack/domain/BlackjackGame.java b/src/main/java/team/blackjack/domain/BlackjackGame.java new file mode 100644 index 00000000000..196cd61405e --- /dev/null +++ b/src/main/java/team/blackjack/domain/BlackjackGame.java @@ -0,0 +1,40 @@ +package team.blackjack.domain; + +import java.util.List; +import java.util.Map; + +public class BlackjackGame { + private final Dealer dealer; + private final List players; + private final Deck deck; + + public BlackjackGame(Dealer dealer, List players) { + this.dealer = dealer; + this.players = players; + this.deck = new Deck(); + } + + public Dealer getDealer() { + return dealer; + } + + public List getPlayers() { + return players; + } + + /*public Map> getAllPlayerCards(){ + for (Player player : players) { + getPlayerCardInHand() + } + } + + private Map> getPlayerCardInOneHand(Hand hand) { + for (Hand hand : player.getHands()) { + hand.getCards(); + } + }*/ + + public Deck getDeck() { + return deck; + } +} diff --git a/src/main/java/team/blackjack/domain/Card.java b/src/main/java/team/blackjack/domain/Card.java new file mode 100644 index 00000000000..1540e3ce10b --- /dev/null +++ b/src/main/java/team/blackjack/domain/Card.java @@ -0,0 +1,38 @@ +package team.blackjack.domain; + +import java.util.List; + +public enum Card { + ACE_OF_HEARTS(Suit.HEARTS, "A하트", List.of(1, 11)), + TWO_OF_HEARTS(Suit.HEARTS, "2하트", List.of(2)), + THREE_OF_HEARTS(Suit.HEARTS, "3하트", List.of(3)); + + private final Suit suit; + private final String name; + private final List values; + + Card(Suit suit, String name, List values) { + this.suit = suit; + this.name = name; + this.values = values; + } + + public enum Suit { + + HEARTS(true), DIAMONDS(true), CLUBS(false), SPADES(false); + + private final boolean isRed; + + Suit(boolean isRed) { + this.isRed = isRed; + } + + public boolean isRed() { + return isRed; + } + } + + public String getCardName() { + return this.name; + } +} diff --git a/src/main/java/team/blackjack/domain/Dealer.java b/src/main/java/team/blackjack/domain/Dealer.java new file mode 100644 index 00000000000..c7fa2fa2e52 --- /dev/null +++ b/src/main/java/team/blackjack/domain/Dealer.java @@ -0,0 +1,22 @@ +package team.blackjack.domain; + +public class Dealer { + + private final Hand hand; + + public Dealer() { + this.hand = new Hand(); + } + + public int getScore() { + return this.hand.getScore(); + } + + public Card draw(Deck deck) { + return deck.draw(); + } + + public Hand getHand() { + return this.hand; + } +} \ No newline at end of file diff --git a/src/main/java/team/blackjack/domain/Deck.java b/src/main/java/team/blackjack/domain/Deck.java new file mode 100644 index 00000000000..839144133fe --- /dev/null +++ b/src/main/java/team/blackjack/domain/Deck.java @@ -0,0 +1,26 @@ +package team.blackjack.domain; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class Deck { + private final List cards; + + public Deck() { + this.cards = Arrays.stream(Card.values()) + .collect(Collectors.toList()); + + // 카드 섞기 + Collections.shuffle(this.cards); + } + + public Card draw() { + try { + return cards.getFirst(); + } finally { + cards.removeFirst(); + } + } +} diff --git a/src/main/java/team/blackjack/domain/Hand.java b/src/main/java/team/blackjack/domain/Hand.java new file mode 100644 index 00000000000..90c9e772ef3 --- /dev/null +++ b/src/main/java/team/blackjack/domain/Hand.java @@ -0,0 +1,28 @@ +package team.blackjack.domain; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class Hand { + + private final Set cards = new HashSet<>(); + + public void addCard(Card card) { + cards.add(card); + } + + public List getCards() { + return cards.stream().toList(); + } + + public int getCardCount() { + return cards.size(); + } + + public int getScore() { + + } + +} diff --git a/src/main/java/team/blackjack/domain/Player.java b/src/main/java/team/blackjack/domain/Player.java new file mode 100644 index 00000000000..8043fcdcd7e --- /dev/null +++ b/src/main/java/team/blackjack/domain/Player.java @@ -0,0 +1,25 @@ +package team.blackjack.domain; + +import java.util.ArrayList; +import java.util.List; + +public class Player { + private final String name; + private final List hands = new ArrayList<>(); + + public Player(String name) { + this.name = name; + } + + public List getHands() { + return List.copyOf(hands); + } + + public void addHand(Hand hand) { + hands.add(hand); + } + + public String getName() { + return this.name; + } +} From b6b2ac6e0178141cbd27f4ef1e04f98f47d4195e Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Thu, 5 Mar 2026 17:33:02 +0900 Subject: [PATCH 04/50] =?UTF-8?q?feat(config):=20=EC=95=B1=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=20=EC=84=A4=EC=A0=95=20=ED=8C=8C=EC=9D=BC=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 --- .../java/team/blackjack/config/AppConfig.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/java/team/blackjack/config/AppConfig.java diff --git a/src/main/java/team/blackjack/config/AppConfig.java b/src/main/java/team/blackjack/config/AppConfig.java new file mode 100644 index 00000000000..c9a16cca3df --- /dev/null +++ b/src/main/java/team/blackjack/config/AppConfig.java @@ -0,0 +1,31 @@ +package team.blackjack.config; + +import team.blackjack.control.BlackJackController; +import team.blackjack.service.BlackJackService; + +public class AppConfig { + private static volatile AppConfig instance; + + private AppConfig() { + } + + public static AppConfig getInstance() { + if (instance == null) { + synchronized (AppConfig.class) { + if (instance == null) { + instance = new AppConfig(); + } + } + } + + return instance; + } + + public BlackJackService blackJackService() { + return new BlackJackService(); + } + + public BlackJackController blackJackController() { + return new BlackJackController(blackJackService()); + } +} From 5445549859fc407b0b6ed8b5bd3dd78ecf301c28 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Thu, 5 Mar 2026 17:34:04 +0900 Subject: [PATCH 05/50] =?UTF-8?q?refactor(Test):=20=EB=B8=94=EB=9E=99?= =?UTF-8?q?=EC=A0=9D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=8E=98=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain}/rule/DefaultBlackjackRuleTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/test/java/{ => team/blackjack/domain}/rule/DefaultBlackjackRuleTest.java (98%) diff --git a/src/test/java/rule/DefaultBlackjackRuleTest.java b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java similarity index 98% rename from src/test/java/rule/DefaultBlackjackRuleTest.java rename to src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java index e2e3997b42e..b454da82807 100644 --- a/src/test/java/rule/DefaultBlackjackRuleTest.java +++ b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java @@ -1,4 +1,4 @@ -package rule; +package team.blackjack.domain.rule; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; From 51bda21b8f58e7c1fff16a441f6a3fac4bdbe907 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Thu, 5 Mar 2026 19:45:00 +0900 Subject: [PATCH 06/50] =?UTF-8?q?feat:=20=EB=B8=94=EB=9E=99=EC=9E=AD=20?= =?UTF-8?q?=EB=9E=9C=EB=8D=A4=20=EC=B9=B4=EB=93=9C=20=EB=B0=B0=EC=A0=95=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=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/team/blackjack/Application.java | 12 ++++ .../control/BlackJackController.java | 60 +++++++++++++++++++ .../blackjack/control/dto/DrawResult.java | 11 ++++ .../team/blackjack/domain/BlackjackGame.java | 20 ++++--- src/main/java/team/blackjack/domain/Card.java | 54 ++++++++++++++++- .../java/team/blackjack/domain/Dealer.java | 4 -- src/main/java/team/blackjack/domain/Hand.java | 5 -- .../domain/rule/DefaultBlackjackRule.java | 17 +++--- .../blackjack/service/BlackJackService.java | 50 ++++++++++++++++ .../java/team/blackjack/view/InputParser.java | 11 ++++ .../java/team/blackjack/view/InputView.java | 27 +++++++++ .../java/team/blackjack/view/OutputView.java | 39 ++++++++++++ 12 files changed, 283 insertions(+), 27 deletions(-) create mode 100644 src/main/java/team/blackjack/Application.java create mode 100644 src/main/java/team/blackjack/control/BlackJackController.java create mode 100644 src/main/java/team/blackjack/control/dto/DrawResult.java create mode 100644 src/main/java/team/blackjack/service/BlackJackService.java create mode 100644 src/main/java/team/blackjack/view/InputParser.java create mode 100644 src/main/java/team/blackjack/view/InputView.java create mode 100644 src/main/java/team/blackjack/view/OutputView.java diff --git a/src/main/java/team/blackjack/Application.java b/src/main/java/team/blackjack/Application.java new file mode 100644 index 00000000000..ac0feb39faf --- /dev/null +++ b/src/main/java/team/blackjack/Application.java @@ -0,0 +1,12 @@ +package team.blackjack; + +import team.blackjack.config.AppConfig; +import team.blackjack.control.BlackJackController; + +public class Application { + public static void main(String[] args) { + AppConfig appConfig = AppConfig.getInstance(); + BlackJackController blackJackController = appConfig.blackJackController(); + blackJackController.run(); + } +} \ No newline at end of file diff --git a/src/main/java/team/blackjack/control/BlackJackController.java b/src/main/java/team/blackjack/control/BlackJackController.java new file mode 100644 index 00000000000..c77f6c0b77b --- /dev/null +++ b/src/main/java/team/blackjack/control/BlackJackController.java @@ -0,0 +1,60 @@ +package team.blackjack.control; + +import java.util.List; +import java.util.Map; +import team.blackjack.control.dto.DrawResult; +import team.blackjack.domain.BlackjackGame; +import team.blackjack.domain.Card; +import team.blackjack.domain.Player; +import team.blackjack.service.BlackJackService; +import team.blackjack.view.InputView; +import team.blackjack.view.OutputView; + +public class BlackJackController { + private BlackjackGame blackjackGame; + private final BlackJackService blackJackService; + + public BlackJackController(BlackJackService blackJackService) { + this.blackJackService = blackJackService; + } + + public void run() { + /** + * TODO: + * 1. 사용자 이름 입력(view함수 호출) + * 2. 게임 초기화(service함수 호출) + * 3. 각 플레이어, 딜러 카드 발급(service함수 호출) + * 4. 플레이어,딜러 카드 출력(view함수 호출) + * 5. 플레이어 행동 입력(view함수 호출) + * 6. 플레이어 행동에 따른 카드 발급(service함수 호출) + * 7. 딜러 카드 발급 혹은 스탠드(service함수 호출) + * 8. 딜러 카드 발급 여부 출력 (view 함수 호출) + * 9. 결과(발급 카드 및 점수) 계산 (service함수 호출) + * 10. 결과 출력 (view함수 호출) + * 11. 최종 승패 계산 (service함수 호출) + * 12. 최종 승패 출력 (view함수 호출) + */ + + OutputView.printPlayerNameRequest(); + List playerNames = InputView.readPlayerNames(); + + blackJackService.initGame(playerNames); + final BlackjackGame blackjackGame = blackJackService.getBlackjackGame(); + + blackJackService.drawInitialCards(); + OutputView.printDrawResult(convert(blackjackGame)); + + + } + + private DrawResult convert(BlackjackGame game) { + final List playerNames = game.getPlayers().stream() + .map(Player::getName) + .toList(); + final List cards = game.getDealer().getHand().getCards(); + final Map> playerCards = game.getAllPlayerCards(); + + return new DrawResult(playerNames, cards.getFirst().getCardName(), playerCards); + + } +} diff --git a/src/main/java/team/blackjack/control/dto/DrawResult.java b/src/main/java/team/blackjack/control/dto/DrawResult.java new file mode 100644 index 00000000000..f401ca3e37b --- /dev/null +++ b/src/main/java/team/blackjack/control/dto/DrawResult.java @@ -0,0 +1,11 @@ +package team.blackjack.control.dto; + +import java.util.List; +import java.util.Map; + +public record DrawResult( + List playerNames, + String dealerCard, + Map> playerCards +) { +} diff --git a/src/main/java/team/blackjack/domain/BlackjackGame.java b/src/main/java/team/blackjack/domain/BlackjackGame.java index 196cd61405e..7874f935896 100644 --- a/src/main/java/team/blackjack/domain/BlackjackGame.java +++ b/src/main/java/team/blackjack/domain/BlackjackGame.java @@ -1,5 +1,6 @@ package team.blackjack.domain; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -22,17 +23,22 @@ public List getPlayers() { return players; } - /*public Map> getAllPlayerCards(){ + public Map> getAllPlayerCards(){ + final HashMap> result = new HashMap<>(); for (Player player : players) { - getPlayerCardInHand() + result.put(player.getName(), getPlayerCardInAllHand(player)); } + + return result; } - private Map> getPlayerCardInOneHand(Hand hand) { - for (Hand hand : player.getHands()) { - hand.getCards(); - } - }*/ + private List getPlayerCardInAllHand(Player player) { + return player.getHands().stream() + .map(Hand::getCards) + .flatMap(List::stream) + .map(Card::getCardName) + .toList(); + } public Deck getDeck() { return deck; diff --git a/src/main/java/team/blackjack/domain/Card.java b/src/main/java/team/blackjack/domain/Card.java index 1540e3ce10b..5348b00e344 100644 --- a/src/main/java/team/blackjack/domain/Card.java +++ b/src/main/java/team/blackjack/domain/Card.java @@ -5,7 +5,59 @@ public enum Card { ACE_OF_HEARTS(Suit.HEARTS, "A하트", List.of(1, 11)), TWO_OF_HEARTS(Suit.HEARTS, "2하트", List.of(2)), - THREE_OF_HEARTS(Suit.HEARTS, "3하트", List.of(3)); + THREE_OF_HEARTS(Suit.HEARTS, "3하트", List.of(3)), + FOUR_OF_HEARTS(Suit.HEARTS, "4하트", List.of(4)), + FIVE_OF_HEARTS(Suit.HEARTS, "5하트", List.of(5)), + SIX_OF_HEARTS(Suit.HEARTS, "6하트", List.of(6)), + SEVEN_OF_HEARTS(Suit.HEARTS, "7하트", List.of(7)), + EIGHT_OF_HEARTS(Suit.HEARTS, "8하트", List.of(8)), + NINE_OF_HEARTS(Suit.HEARTS, "9하트", List.of(9)), + TEN_OF_HEARTS(Suit.HEARTS, "10하트", List.of(10)), + JACK_OF_HEARTS(Suit.HEARTS, "J하트", List.of(10)), + QUEEN_OF_HEARTS(Suit.HEARTS, "Q하트", List.of(10)), + KING_OF_HEARTS(Suit.HEARTS, "K하트", List.of(10)), + + ACE_OF_DIAMONDS(Suit.DIAMONDS, "A다이아", List.of(1, 11)), + TWO_OF_DIAMONDS(Suit.DIAMONDS, "2다이아", List.of(2)), + THREE_OF_DIAMONDS(Suit.DIAMONDS, "3다이아", List.of(3)), + FOUR_OF_DIAMONDS(Suit.DIAMONDS, "4다이아", List.of(4)), + FIVE_OF_DIAMONDS(Suit.DIAMONDS, "5다이아", List.of(5)), + SIX_OF_DIAMONDS(Suit.DIAMONDS, "6다이아", List.of(6)), + SEVEN_OF_DIAMONDS(Suit.DIAMONDS, "7다이아", List.of(7)), + EIGHT_OF_DIAMONDS(Suit.DIAMONDS, "8다이아", List.of(8)), + NINE_OF_DIAMONDS(Suit.DIAMONDS, "9다이아", List.of(9)), + TEN_OF_DIAMONDS(Suit.DIAMONDS, "10다이아", List.of(10)), + JACK_OF_DIAMONDS(Suit.DIAMONDS, "J다이아", List.of(10)), + QUEEN_OF_DIAMONDS(Suit.DIAMONDS, "Q다이아", List.of(10)), + KING_OF_DIAMONDS(Suit.DIAMONDS, "K다이아", List.of(10)), + + ACE_OF_CLUBS(Suit.CLUBS, "A클럽", List.of(1, 11)), + TWO_OF_CLUBS(Suit.CLUBS, "2클럽", List.of(2)), + THREE_OF_CLUBS(Suit.CLUBS, "3클럽", List.of(3)), + FOUR_OF_CLUBS(Suit.CLUBS, "4클럽", List.of(4)), + FIVE_OF_CLUBS(Suit.CLUBS, "5클럽", List.of(5)), + SIX_OF_CLUBS(Suit.CLUBS, "6클럽", List.of(6)), + SEVEN_OF_CLUBS(Suit.CLUBS, "7클럽", List.of(7)), + EIGHT_OF_CLUBS(Suit.CLUBS, "8클럽", List.of(8)), + NINE_OF_CLUBS(Suit.CLUBS, "9클럽", List.of(9)), + TEN_OF_CLUBS(Suit.CLUBS, "10클럽", List.of(10)), + JACK_OF_CLUBS(Suit.CLUBS, "J클럽", List.of(10)), + QUEEN_OF_CLUBS(Suit.CLUBS, "Q클럽", List.of(10)), + KING_OF_CLUBS(Suit.CLUBS, "K클럽", List.of(10)), + + ACE_OF_SPADES(Suit.SPADES, "A스페이드", List.of(1, 11)), + TWO_OF_SPADES(Suit.SPADES, "2스페이드", List.of(2)), + THREE_OF_SPADES(Suit.SPADES, "3스페이드", List.of(3)), + FOUR_OF_SPADES(Suit.SPADES, "4스페이드", List.of(4)), + FIVE_OF_SPADES(Suit.SPADES, "5스페이드", List.of(5)), + SIX_OF_SPADES(Suit.SPADES, "6스페이드", List.of(6)), + SEVEN_OF_SPADES(Suit.SPADES, "7스페이드", List.of(7)), + EIGHT_OF_SPADES(Suit.SPADES, "8스페이드", List.of(8)), + NINE_OF_SPADES(Suit.SPADES, "9스페이드", List.of(9)), + TEN_OF_SPADES(Suit.SPADES, "10스페이드", List.of(10)), + JACK_OF_SPADES(Suit.SPADES, "J스페이드", List.of(10)), + QUEEN_OF_SPADES(Suit.SPADES, "Q스페이드", List.of(10)), + KING_OF_SPADES(Suit.SPADES, "K스페이드", List.of(10)); private final Suit suit; private final String name; diff --git a/src/main/java/team/blackjack/domain/Dealer.java b/src/main/java/team/blackjack/domain/Dealer.java index c7fa2fa2e52..c01d1eddd8e 100644 --- a/src/main/java/team/blackjack/domain/Dealer.java +++ b/src/main/java/team/blackjack/domain/Dealer.java @@ -8,10 +8,6 @@ public Dealer() { this.hand = new Hand(); } - public int getScore() { - return this.hand.getScore(); - } - public Card draw(Deck deck) { return deck.draw(); } diff --git a/src/main/java/team/blackjack/domain/Hand.java b/src/main/java/team/blackjack/domain/Hand.java index 90c9e772ef3..8f9daccbde5 100644 --- a/src/main/java/team/blackjack/domain/Hand.java +++ b/src/main/java/team/blackjack/domain/Hand.java @@ -20,9 +20,4 @@ public List getCards() { public int getCardCount() { return cards.size(); } - - public int getScore() { - - } - } diff --git a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java index de1bcfbff00..c8b5dfcfa13 100644 --- a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java +++ b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java @@ -1,25 +1,22 @@ package team.blackjack.domain.rule; -import team.blackjack.domain.Dealer; -import team.blackjack.domain.Hand; - public class DefaultBlackjackRule { public static final int BLACKJACK = 21; public static final int BLACKJACK_CARD_COUNT = 2; public static final int DEALER_STAND_SCORE = 17; - public boolean isBust(Hand hand) { - return hand.getScore() > BLACKJACK; + public boolean isBust(int score) { + return score > BLACKJACK; } - public boolean isBlackjack(Hand hand) { - if (hand.getCardCount() != BLACKJACK_CARD_COUNT) { + public boolean isBlackjack(int score, int cardCount) { + if (cardCount != BLACKJACK_CARD_COUNT) { return false; } - return hand.getScore() == BLACKJACK; + return score == BLACKJACK; } - public boolean isDealerMustDraw(Dealer dealer) { - return dealer.getScore() < DEALER_STAND_SCORE; + public boolean isDealerMustDraw(int score) { + return score < DEALER_STAND_SCORE; } } diff --git a/src/main/java/team/blackjack/service/BlackJackService.java b/src/main/java/team/blackjack/service/BlackJackService.java new file mode 100644 index 00000000000..d92b0ab716f --- /dev/null +++ b/src/main/java/team/blackjack/service/BlackJackService.java @@ -0,0 +1,50 @@ +package team.blackjack.service; + +import java.util.List; +import team.blackjack.domain.BlackjackGame; +import team.blackjack.domain.Dealer; +import team.blackjack.domain.Deck; +import team.blackjack.domain.Hand; +import team.blackjack.domain.Player; + +public class BlackJackService { + private BlackjackGame blackjackGame; + + public void initGame(List playerNames) { + final Dealer dealer = new Dealer(); + final List players = playerNames.stream() + .map(Player::new) + .toList(); + + blackjackGame = new BlackjackGame(dealer, players); + } + + + /** + * TODO: Draw 위치에 대한 고민 다시 해보기 + */ + public void drawInitialCards() { + final Deck deck = blackjackGame.getDeck(); + final Dealer dealer = blackjackGame.getDealer(); + + // 플레이어 카드 초기화 + for (Player player : blackjackGame.getPlayers()) { + Hand hand = new Hand(); + hand.addCard(dealer.draw(deck)); + hand.addCard(dealer.draw(deck)); + + player.addHand(hand); + } + + // 딜러 카드 초기화 + Hand dealerHand = dealer.getHand(); + dealerHand.addCard(dealer.draw(deck)); + dealerHand.addCard(dealer.draw(deck)); + } + + public BlackjackGame getBlackjackGame() { + return this.blackjackGame; + } + + +} diff --git a/src/main/java/team/blackjack/view/InputParser.java b/src/main/java/team/blackjack/view/InputParser.java new file mode 100644 index 00000000000..a397c008692 --- /dev/null +++ b/src/main/java/team/blackjack/view/InputParser.java @@ -0,0 +1,11 @@ +package team.blackjack.view; + +import java.util.Scanner; + +public class InputParser { + + public static String parseNameInput(Scanner scanner) { + return scanner.nextLine(); + } + +} diff --git a/src/main/java/team/blackjack/view/InputView.java b/src/main/java/team/blackjack/view/InputView.java new file mode 100644 index 00000000000..1954a37792b --- /dev/null +++ b/src/main/java/team/blackjack/view/InputView.java @@ -0,0 +1,27 @@ +package team.blackjack.view; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +public class InputView { + private static final Scanner scanner = new Scanner(System.in); + + private static String readInput() { + return scanner.nextLine(); + } + + public static List readPlayerNames() { + return Arrays.stream(readInput().replaceAll("\s", "").split(",")) + .toList(); + } + + /** + * 플레이어가 한장의 카드를 더 받을지 여부를 입력받는다. + * @return + */ + public static boolean readHitDecision() { + return readInput().equalsIgnoreCase("y"); + } + +} diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java new file mode 100644 index 00000000000..8390c58846e --- /dev/null +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -0,0 +1,39 @@ +package team.blackjack.view; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Scanner; +import team.blackjack.control.dto.DrawResult; + +public class OutputView { + + private static void println(String message) { + System.out.println(message); + } + + private static void print(String message) { + System.out.print(message); + } + + public static void printPlayerNameRequest() { + println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); + } + + public static void printPlayerActionRequest(String playerName) { + println("%s는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)".formatted(playerName)); + } + + public static void printDrawResult(DrawResult result) { + String playerNames = String.join(", ", result.playerNames()); + println("딜러와 %s에게 2장을 나누었습니다.".formatted(playerNames)); + + println("딜러카드: %s".formatted(result.dealerCard())); + for (Entry> entry : result.playerCards().entrySet()) { + String playerCardNames = String.join(", ", entry.getValue()); + println("%s카드: %s".formatted(entry.getKey(),playerCardNames)); + } + + } +} From 36cc4274c1094c8e1e478e0f3e9d2b29980cf1a6 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Thu, 5 Mar 2026 19:48:40 +0900 Subject: [PATCH 07/50] =?UTF-8?q?docs(README.md):=20=EB=B8=94=EB=9E=99?= =?UTF-8?q?=EC=9E=AD=20=EA=B2=8C=EC=9E=84=20=EA=B8=B0=EB=B3=B8=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84=20=EB=AA=85=EC=84=B8=EC=84=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ff5f7b6790..64cf978b093 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,30 @@ # java-blackjack -블랙잭 미션 저장소 +## 기본 기능 구현 +- 플레이어 이름을 등록한다. +- 참여자(플레이어 + 딜러)에게 각각 2장의 카드를 랜덤으로 발급한다. + - 딜러 카드는 하나만 출력한다. + - 각 플레이어의 카드는 모두(2장) 출력한다. + +- 각 플레이어는 한 장의 카드를 더 받을 지 선택한다. + - 각 플레이어 별로 한 장의 카드를 더 받을지 여부를 출력한다. + - 더 받는 경우(hit): y를 입력한다. + - hit 경우는 현재 보유한 모든 카드를 출력한다. + - 더 받지 않는 경우(stand): n을 입력한다. + - 조건: stand 하기 전까지 다음 플레이어에게 턴을 넘기지 않고 계속 입력 받을 수 있다. + +- 딜러의 카드 상태에 따라 추가 카드를 자동 발급 혹은 유지한다. + - 딜러의 발급 여부를 출력한다. + - (받은 예: 딜러는 16이하라 한장의 카드를 더 받았습니다.) + - (받지 않은 예: 딜러는 17이상이라 카드를 더 받지았습니다.) + - 조건: 딜러의 카드 합이 17 이상이 될때까지 카드 발급을 반복한다. + +- 참여자(플레이어 + 딜러)의 카드 소유 현황을 출력한다. + - 각 참여자의 카드 종류와 결과 합계를 함께 출력한다. + - 조건: 출력 순서는 딜러 먼저 출력하며, 입력받은 플레이어 순서로 이어 출력한다. + +- 게임 결과를 출력한다. + - 최종 승패를 출력한다. + - 딜러는 승패전적을 출력한다. (예: 딜러: 1승 1패) + - 플레이어는 승패 여부를 출력한다. (예: pobi: 승) + - 조건: 출력 순서는 딜러 먼저 출력하며, 입력받은 플레이어 순서로 이어 출력한다. \ No newline at end of file From 9c4d316c6f293758e77cadf06d39ee9e68eb6e24 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Thu, 5 Mar 2026 21:46:37 +0900 Subject: [PATCH 08/50] =?UTF-8?q?feat(Service):=20=EB=94=9C=EB=9F=AC/?= =?UTF-8?q?=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4=20hit&stand=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../control/BlackJackController.java | 26 +++- src/main/java/team/blackjack/domain/Card.java | 127 ++++++++++-------- src/main/java/team/blackjack/domain/Hand.java | 6 + .../blackjack/domain/rule/BlackjackRule.java | 4 - .../domain/rule/DefaultBlackjackRule.java | 10 +- .../blackjack/service/BlackJackService.java | 68 ++++++++++ .../java/team/blackjack/view/InputView.java | 1 - .../java/team/blackjack/view/OutputView.java | 17 ++- 8 files changed, 186 insertions(+), 73 deletions(-) delete mode 100644 src/main/java/team/blackjack/domain/rule/BlackjackRule.java diff --git a/src/main/java/team/blackjack/control/BlackJackController.java b/src/main/java/team/blackjack/control/BlackJackController.java index c77f6c0b77b..05ddf99afa8 100644 --- a/src/main/java/team/blackjack/control/BlackJackController.java +++ b/src/main/java/team/blackjack/control/BlackJackController.java @@ -11,7 +11,6 @@ import team.blackjack.view.OutputView; public class BlackJackController { - private BlackjackGame blackjackGame; private final BlackJackService blackJackService; public BlackJackController(BlackJackService blackJackService) { @@ -44,7 +43,31 @@ public void run() { blackJackService.drawInitialCards(); OutputView.printDrawResult(convert(blackjackGame)); + readHitDecision(blackjackGame, blackjackGame.getPlayers()); + while (blackJackService.shouldDealerHit()) { + OutputView.printDealerHitMessage(); + blackJackService.hitDealer(); + } + } + + private void decideDealer(BlackjackGame blackjackGame) { + + } + + private void readHitDecision(BlackjackGame game, List players) { + players.forEach(player -> processHit(game, player)); + } + + private void processHit(BlackjackGame game, Player player) { + while (true) { + OutputView.printAskDrawCard(player.getName()); + if (!InputView.readHitDecision()) { + return; + } + blackJackService.draw(game, player); + OutputView.printPlayerCards(player.getName(), player.getHands().getFirst().getCardNames()); + } } private DrawResult convert(BlackjackGame game) { @@ -55,6 +78,5 @@ private DrawResult convert(BlackjackGame game) { final Map> playerCards = game.getAllPlayerCards(); return new DrawResult(playerNames, cards.getFirst().getCardName(), playerCards); - } } diff --git a/src/main/java/team/blackjack/domain/Card.java b/src/main/java/team/blackjack/domain/Card.java index 5348b00e344..6072c013a64 100644 --- a/src/main/java/team/blackjack/domain/Card.java +++ b/src/main/java/team/blackjack/domain/Card.java @@ -3,70 +3,72 @@ import java.util.List; public enum Card { - ACE_OF_HEARTS(Suit.HEARTS, "A하트", List.of(1, 11)), - TWO_OF_HEARTS(Suit.HEARTS, "2하트", List.of(2)), - THREE_OF_HEARTS(Suit.HEARTS, "3하트", List.of(3)), - FOUR_OF_HEARTS(Suit.HEARTS, "4하트", List.of(4)), - FIVE_OF_HEARTS(Suit.HEARTS, "5하트", List.of(5)), - SIX_OF_HEARTS(Suit.HEARTS, "6하트", List.of(6)), - SEVEN_OF_HEARTS(Suit.HEARTS, "7하트", List.of(7)), - EIGHT_OF_HEARTS(Suit.HEARTS, "8하트", List.of(8)), - NINE_OF_HEARTS(Suit.HEARTS, "9하트", List.of(9)), - TEN_OF_HEARTS(Suit.HEARTS, "10하트", List.of(10)), - JACK_OF_HEARTS(Suit.HEARTS, "J하트", List.of(10)), - QUEEN_OF_HEARTS(Suit.HEARTS, "Q하트", List.of(10)), - KING_OF_HEARTS(Suit.HEARTS, "K하트", List.of(10)), - - ACE_OF_DIAMONDS(Suit.DIAMONDS, "A다이아", List.of(1, 11)), - TWO_OF_DIAMONDS(Suit.DIAMONDS, "2다이아", List.of(2)), - THREE_OF_DIAMONDS(Suit.DIAMONDS, "3다이아", List.of(3)), - FOUR_OF_DIAMONDS(Suit.DIAMONDS, "4다이아", List.of(4)), - FIVE_OF_DIAMONDS(Suit.DIAMONDS, "5다이아", List.of(5)), - SIX_OF_DIAMONDS(Suit.DIAMONDS, "6다이아", List.of(6)), - SEVEN_OF_DIAMONDS(Suit.DIAMONDS, "7다이아", List.of(7)), - EIGHT_OF_DIAMONDS(Suit.DIAMONDS, "8다이아", List.of(8)), - NINE_OF_DIAMONDS(Suit.DIAMONDS, "9다이아", List.of(9)), - TEN_OF_DIAMONDS(Suit.DIAMONDS, "10다이아", List.of(10)), - JACK_OF_DIAMONDS(Suit.DIAMONDS, "J다이아", List.of(10)), - QUEEN_OF_DIAMONDS(Suit.DIAMONDS, "Q다이아", List.of(10)), - KING_OF_DIAMONDS(Suit.DIAMONDS, "K다이아", List.of(10)), - - ACE_OF_CLUBS(Suit.CLUBS, "A클럽", List.of(1, 11)), - TWO_OF_CLUBS(Suit.CLUBS, "2클럽", List.of(2)), - THREE_OF_CLUBS(Suit.CLUBS, "3클럽", List.of(3)), - FOUR_OF_CLUBS(Suit.CLUBS, "4클럽", List.of(4)), - FIVE_OF_CLUBS(Suit.CLUBS, "5클럽", List.of(5)), - SIX_OF_CLUBS(Suit.CLUBS, "6클럽", List.of(6)), - SEVEN_OF_CLUBS(Suit.CLUBS, "7클럽", List.of(7)), - EIGHT_OF_CLUBS(Suit.CLUBS, "8클럽", List.of(8)), - NINE_OF_CLUBS(Suit.CLUBS, "9클럽", List.of(9)), - TEN_OF_CLUBS(Suit.CLUBS, "10클럽", List.of(10)), - JACK_OF_CLUBS(Suit.CLUBS, "J클럽", List.of(10)), - QUEEN_OF_CLUBS(Suit.CLUBS, "Q클럽", List.of(10)), - KING_OF_CLUBS(Suit.CLUBS, "K클럽", List.of(10)), - - ACE_OF_SPADES(Suit.SPADES, "A스페이드", List.of(1, 11)), - TWO_OF_SPADES(Suit.SPADES, "2스페이드", List.of(2)), - THREE_OF_SPADES(Suit.SPADES, "3스페이드", List.of(3)), - FOUR_OF_SPADES(Suit.SPADES, "4스페이드", List.of(4)), - FIVE_OF_SPADES(Suit.SPADES, "5스페이드", List.of(5)), - SIX_OF_SPADES(Suit.SPADES, "6스페이드", List.of(6)), - SEVEN_OF_SPADES(Suit.SPADES, "7스페이드", List.of(7)), - EIGHT_OF_SPADES(Suit.SPADES, "8스페이드", List.of(8)), - NINE_OF_SPADES(Suit.SPADES, "9스페이드", List.of(9)), - TEN_OF_SPADES(Suit.SPADES, "10스페이드", List.of(10)), - JACK_OF_SPADES(Suit.SPADES, "J스페이드", List.of(10)), - QUEEN_OF_SPADES(Suit.SPADES, "Q스페이드", List.of(10)), - KING_OF_SPADES(Suit.SPADES, "K스페이드", List.of(10)); + ACE_OF_HEARTS(Suit.HEARTS, "A하트", true, List.of(1, 11)), + TWO_OF_HEARTS(Suit.HEARTS, "2하트", false, List.of(2)), + THREE_OF_HEARTS(Suit.HEARTS, "3하트", false, List.of(3)), + FOUR_OF_HEARTS(Suit.HEARTS, "4하트", false, List.of(4)), + FIVE_OF_HEARTS(Suit.HEARTS, "5하트", false, List.of(5)), + SIX_OF_HEARTS(Suit.HEARTS, "6하트", false, List.of(6)), + SEVEN_OF_HEARTS(Suit.HEARTS, "7하트", false, List.of(7)), + EIGHT_OF_HEARTS(Suit.HEARTS, "8하트", false, List.of(8)), + NINE_OF_HEARTS(Suit.HEARTS, "9하트", false, List.of(9)), + TEN_OF_HEARTS(Suit.HEARTS, "10하트", false, List.of(10)), + JACK_OF_HEARTS(Suit.HEARTS, "J하트", false, List.of(10)), + QUEEN_OF_HEARTS(Suit.HEARTS, "Q하트", false, List.of(10)), + KING_OF_HEARTS(Suit.HEARTS, "K하트", false, List.of(10)), + + ACE_OF_DIAMONDS(Suit.DIAMONDS, "A다이아", true, List.of(1, 11)), + TWO_OF_DIAMONDS(Suit.DIAMONDS, "2다이아", false, List.of(2)), + THREE_OF_DIAMONDS(Suit.DIAMONDS, "3다이아", false, List.of(3)), + FOUR_OF_DIAMONDS(Suit.DIAMONDS, "4다이아", false, List.of(4)), + FIVE_OF_DIAMONDS(Suit.DIAMONDS, "5다이아", false, List.of(5)), + SIX_OF_DIAMONDS(Suit.DIAMONDS, "6다이아", false, List.of(6)), + SEVEN_OF_DIAMONDS(Suit.DIAMONDS, "7다이아", false, List.of(7)), + EIGHT_OF_DIAMONDS(Suit.DIAMONDS, "8다이아", false, List.of(8)), + NINE_OF_DIAMONDS(Suit.DIAMONDS, "9다이아", false, List.of(9)), + TEN_OF_DIAMONDS(Suit.DIAMONDS, "10다이아", false, List.of(10)), + JACK_OF_DIAMONDS(Suit.DIAMONDS, "J다이아", false, List.of(10)), + QUEEN_OF_DIAMONDS(Suit.DIAMONDS, "Q다이아", false, List.of(10)), + KING_OF_DIAMONDS(Suit.DIAMONDS, "K다이아", false, List.of(10)), + + ACE_OF_CLUBS(Suit.CLUBS, "A클로버", true, List.of(1, 11)), + TWO_OF_CLUBS(Suit.CLUBS, "2클로버", false, List.of(2)), + THREE_OF_CLUBS(Suit.CLUBS, "3클로버", false, List.of(3)), + FOUR_OF_CLUBS(Suit.CLUBS, "4클로버", false, List.of(4)), + FIVE_OF_CLUBS(Suit.CLUBS, "5클로버", false, List.of(5)), + SIX_OF_CLUBS(Suit.CLUBS, "6클로버", false, List.of(6)), + SEVEN_OF_CLUBS(Suit.CLUBS, "7클로버", false, List.of(7)), + EIGHT_OF_CLUBS(Suit.CLUBS, "8클로버", false, List.of(8)), + NINE_OF_CLUBS(Suit.CLUBS, "9클로버", false, List.of(9)), + TEN_OF_CLUBS(Suit.CLUBS, "10클로버", false, List.of(10)), + JACK_OF_CLUBS(Suit.CLUBS, "J클로버", false, List.of(10)), + QUEEN_OF_CLUBS(Suit.CLUBS, "Q클로버", false, List.of(10)), + KING_OF_CLUBS(Suit.CLUBS, "K클로버", false, List.of(10)), + + ACE_OF_SPADES(Suit.SPADES, "A스페이드", true, List.of(1, 11)), + TWO_OF_SPADES(Suit.SPADES, "2스페이드", false, List.of(2)), + THREE_OF_SPADES(Suit.SPADES, "3스페이드", false, List.of(3)), + FOUR_OF_SPADES(Suit.SPADES, "4스페이드", false, List.of(4)), + FIVE_OF_SPADES(Suit.SPADES, "5스페이드", false, List.of(5)), + SIX_OF_SPADES(Suit.SPADES, "6스페이드", false, List.of(6)), + SEVEN_OF_SPADES(Suit.SPADES, "7스페이드", false, List.of(7)), + EIGHT_OF_SPADES(Suit.SPADES, "8스페이드", false, List.of(8)), + NINE_OF_SPADES(Suit.SPADES, "9스페이드", false, List.of(9)), + TEN_OF_SPADES(Suit.SPADES, "10스페이드", false, List.of(10)), + JACK_OF_SPADES(Suit.SPADES, "J스페이드", false, List.of(10)), + QUEEN_OF_SPADES(Suit.SPADES, "Q스페이드", false, List.of(10)), + KING_OF_SPADES(Suit.SPADES, "K스페이드", false, List.of(10)); private final Suit suit; private final String name; - private final List values; + private final boolean isAce; + private final List score; - Card(Suit suit, String name, List values) { + Card(Suit suit, String name, boolean isAce, List values) { this.suit = suit; this.name = name; - this.values = values; + this.isAce = isAce; + this.score = values; } public enum Suit { @@ -87,4 +89,13 @@ public boolean isRed() { public String getCardName() { return this.name; } + + public boolean isAce() { + return this.isAce; + } + + public List getScore() { + return score; + } + } diff --git a/src/main/java/team/blackjack/domain/Hand.java b/src/main/java/team/blackjack/domain/Hand.java index 8f9daccbde5..e42457a73dc 100644 --- a/src/main/java/team/blackjack/domain/Hand.java +++ b/src/main/java/team/blackjack/domain/Hand.java @@ -20,4 +20,10 @@ public List getCards() { public int getCardCount() { return cards.size(); } + + public List getCardNames() { + return cards.stream() + .map(Card::getCardName) + .toList(); + } } diff --git a/src/main/java/team/blackjack/domain/rule/BlackjackRule.java b/src/main/java/team/blackjack/domain/rule/BlackjackRule.java deleted file mode 100644 index 51d183c29d7..00000000000 --- a/src/main/java/team/blackjack/domain/rule/BlackjackRule.java +++ /dev/null @@ -1,4 +0,0 @@ -package team.blackjack.domain.rule; - -public interface BlackjackRule { -} diff --git a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java index c8b5dfcfa13..ab7f345feab 100644 --- a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java +++ b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java @@ -5,18 +5,22 @@ public class DefaultBlackjackRule { public static final int BLACKJACK_CARD_COUNT = 2; public static final int DEALER_STAND_SCORE = 17; - public boolean isBust(int score) { + public static boolean isBust(int score) { return score > BLACKJACK; } - public boolean isBlackjack(int score, int cardCount) { + public static boolean isBlackjack(int score, int cardCount) { if (cardCount != BLACKJACK_CARD_COUNT) { return false; } return score == BLACKJACK; } - public boolean isDealerMustDraw(int score) { + public static boolean isDealerMustDraw(int score) { return score < DEALER_STAND_SCORE; } + + public static boolean canUseAceAsEleven(int currentSum) { + return currentSum <= 10; + } } diff --git a/src/main/java/team/blackjack/service/BlackJackService.java b/src/main/java/team/blackjack/service/BlackJackService.java index d92b0ab716f..0be4ef0064b 100644 --- a/src/main/java/team/blackjack/service/BlackJackService.java +++ b/src/main/java/team/blackjack/service/BlackJackService.java @@ -1,11 +1,14 @@ package team.blackjack.service; import java.util.List; +import java.util.stream.Collectors; import team.blackjack.domain.BlackjackGame; +import team.blackjack.domain.Card; import team.blackjack.domain.Dealer; import team.blackjack.domain.Deck; import team.blackjack.domain.Hand; import team.blackjack.domain.Player; +import team.blackjack.domain.rule.DefaultBlackjackRule; public class BlackJackService { private BlackjackGame blackjackGame; @@ -46,5 +49,70 @@ public BlackjackGame getBlackjackGame() { return this.blackjackGame; } + public void draw(BlackjackGame blackjackGame, Player player) { + final Deck deck = blackjackGame.getDeck(); + final Card drawedCard = deck.draw(); + player.getHands().getFirst().addCard(drawedCard); + } + + public boolean shouldDealerHit() { + final Dealer dealer = blackjackGame.getDealer(); + final Hand hand = dealer.getHand(); + final int score = calculateSum(hand.getCards()); + + return DefaultBlackjackRule.isDealerMustDraw(score); + } + + public int hitDealer() { + Dealer dealer = blackjackGame.getDealer(); + Hand hand = dealer.getHand(); + hand.addCard(dealer.draw(blackjackGame.getDeck())); + return calculateSum(hand.getCards()); + } + + /** + * 모든 카드를 발급한 이후에, 최종 점수 계산시에 사용하는 함수 + */ + private int calculateSum(List cards) { + if (existAceInCards(cards)) { + var result = cards.stream() + .collect(Collectors.partitioningBy(Card::isAce)); + + final List aceCards = result.get(true); + final List nonAceCards = result.get(false); + return calculateBestSumWithAce(nonAceCards, aceCards); + } + return calucateBestSumWithoutAce(cards); + + } + + private int calculateBestSumWithAce(List cardsWithoutAces, List aceCards) { + int currentSum = calucateBestSumWithoutAce(cardsWithoutAces); + + for (Card card : aceCards) { + // 카드가 ace 이고, 11을 사용하기에 적합한경우 11을 더한다. + if (DefaultBlackjackRule.canUseAceAsEleven(currentSum)) { + currentSum += card.getScore().getLast(); + } else { + currentSum += card.getScore().getFirst(); + } + } + return currentSum; + } + + private int calucateBestSumWithoutAce(List cards) { + return cards.stream() + .mapToInt(card -> card.getScore().getFirst()) + .sum(); + } + + private boolean existAceInCards(List cards) { + for (Card card : cards) { + if (card.isAce()) { + return true; + } + } + return false; + } } diff --git a/src/main/java/team/blackjack/view/InputView.java b/src/main/java/team/blackjack/view/InputView.java index 1954a37792b..9c96a3fb1bb 100644 --- a/src/main/java/team/blackjack/view/InputView.java +++ b/src/main/java/team/blackjack/view/InputView.java @@ -23,5 +23,4 @@ public static List readPlayerNames() { public static boolean readHitDecision() { return readInput().equalsIgnoreCase("y"); } - } diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java index 8390c58846e..7d978e16c77 100644 --- a/src/main/java/team/blackjack/view/OutputView.java +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -1,10 +1,7 @@ package team.blackjack.view; -import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Map.Entry; -import java.util.Scanner; import team.blackjack.control.dto.DrawResult; public class OutputView { @@ -31,9 +28,19 @@ public static void printDrawResult(DrawResult result) { println("딜러카드: %s".formatted(result.dealerCard())); for (Entry> entry : result.playerCards().entrySet()) { - String playerCardNames = String.join(", ", entry.getValue()); - println("%s카드: %s".formatted(entry.getKey(),playerCardNames)); + printPlayerCards(entry.getKey(), entry.getValue()); } + } + + public static void printAskDrawCard(String playerName) { + println("%s는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)".formatted(playerName)); + } + + public static void printPlayerCards(String playerName, List cardNames) { + println("%s카드: %s".formatted(playerName, String.join(", ", cardNames))); + } + public static void printDealerHitMessage() { + println("딜러는 16이하라 한장의 카드를 더 받았습니다."); } } From 9c90fdd54d08442826e1a9a95c5626f77eb508b9 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Thu, 5 Mar 2026 22:04:16 +0900 Subject: [PATCH 09/50] =?UTF-8?q?feat(Service):=20=EB=94=9C=EB=9F=AC/?= =?UTF-8?q?=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../control/BlackJackController.java | 7 +++-- .../blackjack/control/dto/ScoreResult.java | 13 ++++++++ .../blackjack/service/BlackJackService.java | 31 +++++++++++++++++++ .../java/team/blackjack/view/OutputView.java | 16 ++++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 src/main/java/team/blackjack/control/dto/ScoreResult.java diff --git a/src/main/java/team/blackjack/control/BlackJackController.java b/src/main/java/team/blackjack/control/BlackJackController.java index 05ddf99afa8..3a041b92323 100644 --- a/src/main/java/team/blackjack/control/BlackJackController.java +++ b/src/main/java/team/blackjack/control/BlackJackController.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.Map; import team.blackjack.control.dto.DrawResult; +import team.blackjack.control.dto.ScoreResult; import team.blackjack.domain.BlackjackGame; import team.blackjack.domain.Card; import team.blackjack.domain.Player; @@ -49,12 +50,12 @@ public void run() { OutputView.printDealerHitMessage(); blackJackService.hitDealer(); } - } - - private void decideDealer(BlackjackGame blackjackGame) { + ScoreResult scoreResult = blackJackService.calculateAllParticipantScore(); + OutputView.printParticipantScoreResult(scoreResult); } + private void readHitDecision(BlackjackGame game, List players) { players.forEach(player -> processHit(game, player)); } diff --git a/src/main/java/team/blackjack/control/dto/ScoreResult.java b/src/main/java/team/blackjack/control/dto/ScoreResult.java new file mode 100644 index 00000000000..26257fdf7bf --- /dev/null +++ b/src/main/java/team/blackjack/control/dto/ScoreResult.java @@ -0,0 +1,13 @@ +package team.blackjack.control.dto; + +import java.util.List; +import java.util.Map; + +public record ScoreResult( + List dealerCard, + int dealerScore, + List playerNames, + Map> playerCards, + Map playerScores +) { +} diff --git a/src/main/java/team/blackjack/service/BlackJackService.java b/src/main/java/team/blackjack/service/BlackJackService.java index 0be4ef0064b..6fbc9c3a847 100644 --- a/src/main/java/team/blackjack/service/BlackJackService.java +++ b/src/main/java/team/blackjack/service/BlackJackService.java @@ -1,7 +1,9 @@ package team.blackjack.service; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; +import team.blackjack.control.dto.ScoreResult; import team.blackjack.domain.BlackjackGame; import team.blackjack.domain.Card; import team.blackjack.domain.Dealer; @@ -70,6 +72,35 @@ public int hitDealer() { return calculateSum(hand.getCards()); } + public ScoreResult calculateAllParticipantScore() { + final List playerNames = blackjackGame.getPlayers().stream() + .map(Player::getName) + .toList(); + + final Map> playerCards = blackjackGame.getPlayers().stream() + .collect(Collectors.toMap( + Player::getName, + player -> player.getHands().getFirst().getCardNames() + )); + + final Map playerScores = blackjackGame.getPlayers().stream() + .collect(Collectors.toMap( + Player::getName, + player -> calculateSum(player.getHands().getFirst().getCards()) + )); + + final Dealer dealer = blackjackGame.getDealer(); + final int dealerScore = calculateSum(dealer.getHand().getCards()); + + return new ScoreResult( + dealer.getHand().getCardNames(), + dealerScore, + playerNames, + playerCards, + playerScores + ); + } + /** * 모든 카드를 발급한 이후에, 최종 점수 계산시에 사용하는 함수 */ diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java index 7d978e16c77..aa75001e831 100644 --- a/src/main/java/team/blackjack/view/OutputView.java +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.Map.Entry; import team.blackjack.control.dto.DrawResult; +import team.blackjack.control.dto.ScoreResult; public class OutputView { @@ -43,4 +44,19 @@ public static void printPlayerCards(String playerName, List cardNames) { public static void printDealerHitMessage() { println("딜러는 16이하라 한장의 카드를 더 받았습니다."); } + + public static void printParticipantScoreResult(ScoreResult scoreResult) { + println("딜러의 최종 카드: %s - 결과: %d".formatted(String.join(", ", scoreResult.dealerCard()), scoreResult.dealerScore())); + for (String playerName : scoreResult.playerNames()) { + //pobi카드: 2하트, 8스페이드, A클로버 - 결과: 21 + + println("%s의 카드: %s - 결과: %d".formatted(playerName, + String.join(", ", scoreResult.playerCards().get(playerName)), + scoreResult.playerScores().get(playerName))); + } + } + + public static void printGameResult(String playerName, String result) { + println("%s: %s".formatted(playerName, result)); + } } From 2d76139187bac809ed84f44d4b6b1a59498ebfef Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 6 Mar 2026 11:40:12 +0900 Subject: [PATCH 10/50] =?UTF-8?q?chore:=20import=EB=AC=B8=20=EC=B5=9C?= =?UTF-8?q?=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team/blackjack/control/dto/GameResult.java | 10 ++++++++++ src/main/java/team/blackjack/domain/Hand.java | 1 - 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 src/main/java/team/blackjack/control/dto/GameResult.java diff --git a/src/main/java/team/blackjack/control/dto/GameResult.java b/src/main/java/team/blackjack/control/dto/GameResult.java new file mode 100644 index 00000000000..39a0dfc0225 --- /dev/null +++ b/src/main/java/team/blackjack/control/dto/GameResult.java @@ -0,0 +1,10 @@ +package team.blackjack.control.dto; + +import java.util.Map; + +public record GameResult( + int dealerWinCount, + int dealerFailCount, + Map WinResults +) { +} diff --git a/src/main/java/team/blackjack/domain/Hand.java b/src/main/java/team/blackjack/domain/Hand.java index e42457a73dc..cc07983904c 100644 --- a/src/main/java/team/blackjack/domain/Hand.java +++ b/src/main/java/team/blackjack/domain/Hand.java @@ -1,6 +1,5 @@ package team.blackjack.domain; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; From 7c456f27e3b969f1b3fef0f56263dbedde1ed20b Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 6 Mar 2026 16:24:16 +0900 Subject: [PATCH 11/50] =?UTF-8?q?feat:=20=EB=B8=94=EB=9E=99=EC=9E=AD=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EA=B2=B0=EA=B3=BC=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team/blackjack/Application.java | 2 +- .../java/team/blackjack/config/AppConfig.java | 2 +- .../blackjack/control/dto/GameResult.java | 10 --- .../BlackJackController.java | 36 ++++------ .../dto/DrawResult.java | 2 +- .../blackjack/controler/dto/GameResult.java | 25 +++++++ .../dto/ScoreResult.java | 2 +- src/main/java/team/blackjack/domain/Hand.java | 3 +- .../domain/rule/DefaultBlackjackRule.java | 62 ++++++++++++++++++ .../blackjack/service/BlackJackService.java | 65 +++++++++++++------ .../java/team/blackjack/view/OutputView.java | 27 ++++++-- 11 files changed, 173 insertions(+), 63 deletions(-) delete mode 100644 src/main/java/team/blackjack/control/dto/GameResult.java rename src/main/java/team/blackjack/{control => controler}/BlackJackController.java (62%) rename src/main/java/team/blackjack/{control => controler}/dto/DrawResult.java (83%) create mode 100644 src/main/java/team/blackjack/controler/dto/GameResult.java rename src/main/java/team/blackjack/{control => controler}/dto/ScoreResult.java (87%) diff --git a/src/main/java/team/blackjack/Application.java b/src/main/java/team/blackjack/Application.java index ac0feb39faf..522c7a37cca 100644 --- a/src/main/java/team/blackjack/Application.java +++ b/src/main/java/team/blackjack/Application.java @@ -1,7 +1,7 @@ package team.blackjack; import team.blackjack.config.AppConfig; -import team.blackjack.control.BlackJackController; +import team.blackjack.controler.BlackJackController; public class Application { public static void main(String[] args) { diff --git a/src/main/java/team/blackjack/config/AppConfig.java b/src/main/java/team/blackjack/config/AppConfig.java index c9a16cca3df..e43fcc3ea81 100644 --- a/src/main/java/team/blackjack/config/AppConfig.java +++ b/src/main/java/team/blackjack/config/AppConfig.java @@ -1,6 +1,6 @@ package team.blackjack.config; -import team.blackjack.control.BlackJackController; +import team.blackjack.controler.BlackJackController; import team.blackjack.service.BlackJackService; public class AppConfig { diff --git a/src/main/java/team/blackjack/control/dto/GameResult.java b/src/main/java/team/blackjack/control/dto/GameResult.java deleted file mode 100644 index 39a0dfc0225..00000000000 --- a/src/main/java/team/blackjack/control/dto/GameResult.java +++ /dev/null @@ -1,10 +0,0 @@ -package team.blackjack.control.dto; - -import java.util.Map; - -public record GameResult( - int dealerWinCount, - int dealerFailCount, - Map WinResults -) { -} diff --git a/src/main/java/team/blackjack/control/BlackJackController.java b/src/main/java/team/blackjack/controler/BlackJackController.java similarity index 62% rename from src/main/java/team/blackjack/control/BlackJackController.java rename to src/main/java/team/blackjack/controler/BlackJackController.java index 3a041b92323..167bae3bc66 100644 --- a/src/main/java/team/blackjack/control/BlackJackController.java +++ b/src/main/java/team/blackjack/controler/BlackJackController.java @@ -1,4 +1,4 @@ -package team.blackjack.control; +package team.blackjack.controler; import java.util.List; import java.util.Map; @@ -7,6 +7,7 @@ import team.blackjack.domain.BlackjackGame; import team.blackjack.domain.Card; import team.blackjack.domain.Player; +import team.blackjack.domain.rule.DefaultBlackjackRule; import team.blackjack.service.BlackJackService; import team.blackjack.view.InputView; import team.blackjack.view.OutputView; @@ -19,22 +20,6 @@ public BlackJackController(BlackJackService blackJackService) { } public void run() { - /** - * TODO: - * 1. 사용자 이름 입력(view함수 호출) - * 2. 게임 초기화(service함수 호출) - * 3. 각 플레이어, 딜러 카드 발급(service함수 호출) - * 4. 플레이어,딜러 카드 출력(view함수 호출) - * 5. 플레이어 행동 입력(view함수 호출) - * 6. 플레이어 행동에 따른 카드 발급(service함수 호출) - * 7. 딜러 카드 발급 혹은 스탠드(service함수 호출) - * 8. 딜러 카드 발급 여부 출력 (view 함수 호출) - * 9. 결과(발급 카드 및 점수) 계산 (service함수 호출) - * 10. 결과 출력 (view함수 호출) - * 11. 최종 승패 계산 (service함수 호출) - * 12. 최종 승패 출력 (view함수 호출) - */ - OutputView.printPlayerNameRequest(); List playerNames = InputView.readPlayerNames(); @@ -44,7 +29,7 @@ public void run() { blackJackService.drawInitialCards(); OutputView.printDrawResult(convert(blackjackGame)); - readHitDecision(blackjackGame, blackjackGame.getPlayers()); + readHitDecision(blackjackGame.getPlayers()); while (blackJackService.shouldDealerHit()) { OutputView.printDealerHitMessage(); @@ -53,20 +38,25 @@ public void run() { ScoreResult scoreResult = blackJackService.calculateAllParticipantScore(); OutputView.printParticipantScoreResult(scoreResult); + + GameResult gameResult = blackJackService.getGameResult(); + OutputView.printGameResult(gameResult); } - private void readHitDecision(BlackjackGame game, List players) { - players.forEach(player -> processHit(game, player)); + private void readHitDecision(List players) { + players.forEach(this::processHit); } - private void processHit(BlackjackGame game, Player player) { - while (true) { + private void processHit(Player player) { + while (!DefaultBlackjackRule.isBust(player.getScore())) { OutputView.printAskDrawCard(player.getName()); + if (!InputView.readHitDecision()) { return; } - blackJackService.draw(game, player); + + blackJackService.hitPlayer(player); OutputView.printPlayerCards(player.getName(), player.getHands().getFirst().getCardNames()); } } diff --git a/src/main/java/team/blackjack/control/dto/DrawResult.java b/src/main/java/team/blackjack/controler/dto/DrawResult.java similarity index 83% rename from src/main/java/team/blackjack/control/dto/DrawResult.java rename to src/main/java/team/blackjack/controler/dto/DrawResult.java index f401ca3e37b..d860c22568b 100644 --- a/src/main/java/team/blackjack/control/dto/DrawResult.java +++ b/src/main/java/team/blackjack/controler/dto/DrawResult.java @@ -1,4 +1,4 @@ -package team.blackjack.control.dto; +package team.blackjack.controler.dto; import java.util.List; import java.util.Map; diff --git a/src/main/java/team/blackjack/controler/dto/GameResult.java b/src/main/java/team/blackjack/controler/dto/GameResult.java new file mode 100644 index 00000000000..01b41ae615a --- /dev/null +++ b/src/main/java/team/blackjack/controler/dto/GameResult.java @@ -0,0 +1,25 @@ +package team.blackjack.controler.dto; + +import java.util.List; +import java.util.Map; +import team.blackjack.domain.Result; + +public record GameResult( + DealerResult dealerResult, + Map playerResultMap +) { + public record DealerResult( + List results + ){ + public long countBy(Result target) { + return results.stream() + .filter(result -> result == target) + .count(); + } + } + + public record PlayerResult( + Result result + ){ + } +} diff --git a/src/main/java/team/blackjack/control/dto/ScoreResult.java b/src/main/java/team/blackjack/controler/dto/ScoreResult.java similarity index 87% rename from src/main/java/team/blackjack/control/dto/ScoreResult.java rename to src/main/java/team/blackjack/controler/dto/ScoreResult.java index 26257fdf7bf..a9732749ce1 100644 --- a/src/main/java/team/blackjack/control/dto/ScoreResult.java +++ b/src/main/java/team/blackjack/controler/dto/ScoreResult.java @@ -1,4 +1,4 @@ -package team.blackjack.control.dto; +package team.blackjack.controler.dto; import java.util.List; import java.util.Map; diff --git a/src/main/java/team/blackjack/domain/Hand.java b/src/main/java/team/blackjack/domain/Hand.java index cc07983904c..2ef72a7d5b4 100644 --- a/src/main/java/team/blackjack/domain/Hand.java +++ b/src/main/java/team/blackjack/domain/Hand.java @@ -1,12 +1,13 @@ package team.blackjack.domain; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; public class Hand { - private final Set cards = new HashSet<>(); + private final Set cards = new LinkedHashSet<>(); public void addCard(Card card) { cards.add(card); diff --git a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java index ab7f345feab..92496ee4b35 100644 --- a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java +++ b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java @@ -23,4 +23,66 @@ public static boolean isDealerMustDraw(int score) { public static boolean canUseAceAsEleven(int currentSum) { return currentSum <= 10; } + + // 딜러와 플레이어의 승패를 결정하는 메서드 + public static Result judgeResult(int myScore, int targetScore) { + if(myScore > BLACKJACK){ + return Result.LOSE; + } + if(myScore > targetScore){ + return Result.WIN; + } + if(myScore < targetScore){ + return Result.LOSE; + } + return Result.DRAW; + } + + /** + * 모든 카드를 발급한 이후에, 최종 점수 계산시에 사용하는 함수 + */ + public static int calculateBestScore(List cards) { + if (existAceInCards(cards)) { + final Map> result = cards.stream() + .collect(Collectors.partitioningBy(Card::isAce)); + + final List aceCards = result.get(true); + final List nonAceCards = result.get(false); + return calculateBestSumWithAce(nonAceCards, aceCards); + } + return calculateBestSumWithoutAce(cards); + } + + private static int calculateBestSumWithAce(List cardsWithoutAces, List aceCards) { + int currentSum = calculateBestSumWithoutAce(cardsWithoutAces); + + for (Card card : aceCards) { + currentSum += aceScore(card, currentSum); + } + + return currentSum; + } + + private static int aceScore(Card card, int currentSum) { + if (DefaultBlackjackRule.canUseAceAsEleven(currentSum)) { + return card.getScore().getLast(); + } + + return card.getScore().getFirst(); + } + + private static int calculateBestSumWithoutAce(List cards) { + return cards.stream() + .mapToInt(card -> card.getScore().getFirst()) + .sum(); + } + + private static boolean existAceInCards(List cards) { + for (Card card : cards) { + if (card.isAce()) { + return true; + } + } + return false; + } } diff --git a/src/main/java/team/blackjack/service/BlackJackService.java b/src/main/java/team/blackjack/service/BlackJackService.java index 6fbc9c3a847..7fa74bd3376 100644 --- a/src/main/java/team/blackjack/service/BlackJackService.java +++ b/src/main/java/team/blackjack/service/BlackJackService.java @@ -1,15 +1,21 @@ package team.blackjack.service; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import team.blackjack.control.dto.ScoreResult; +import team.blackjack.controler.dto.GameResult; +import team.blackjack.controler.dto.GameResult.DealerResult; +import team.blackjack.controler.dto.GameResult.PlayerResult; +import team.blackjack.controler.dto.ScoreResult; import team.blackjack.domain.BlackjackGame; import team.blackjack.domain.Card; import team.blackjack.domain.Dealer; import team.blackjack.domain.Deck; import team.blackjack.domain.Hand; import team.blackjack.domain.Player; +import team.blackjack.domain.Result; import team.blackjack.domain.rule.DefaultBlackjackRule; public class BlackJackService { @@ -51,25 +57,20 @@ public BlackjackGame getBlackjackGame() { return this.blackjackGame; } - public void draw(BlackjackGame blackjackGame, Player player) { - final Deck deck = blackjackGame.getDeck(); - final Card drawedCard = deck.draw(); - player.getHands().getFirst().addCard(drawedCard); + public void hitPlayer(Player player) { + player.hit(blackjackGame.getDeck().draw()); } public boolean shouldDealerHit() { - final Dealer dealer = blackjackGame.getDealer(); - final Hand hand = dealer.getHand(); - final int score = calculateSum(hand.getCards()); + final int score = blackjackGame.getDealer().getScore(); return DefaultBlackjackRule.isDealerMustDraw(score); } - public int hitDealer() { - Dealer dealer = blackjackGame.getDealer(); - Hand hand = dealer.getHand(); - hand.addCard(dealer.draw(blackjackGame.getDeck())); - return calculateSum(hand.getCards()); + public void hitDealer() { + final Dealer dealer = blackjackGame.getDealer(); + + dealer.hit(blackjackGame.getDeck().draw()); } public ScoreResult calculateAllParticipantScore() { @@ -86,8 +87,8 @@ public ScoreResult calculateAllParticipantScore() { final Map playerScores = blackjackGame.getPlayers().stream() .collect(Collectors.toMap( Player::getName, - player -> calculateSum(player.getHands().getFirst().getCards()) - )); + Player::getScore) + ); final Dealer dealer = blackjackGame.getDealer(); final int dealerScore = calculateSum(dealer.getHand().getCards()); @@ -138,12 +139,34 @@ private int calucateBestSumWithoutAce(List cards) { .sum(); } - private boolean existAceInCards(List cards) { - for (Card card : cards) { - if (card.isAce()) { - return true; - } + public GameResult getGameResult() { + final Map playerResults = calculatePlayersResultMap(getDealerScore()); + final DealerResult dealerResult = calculateDealerResult(playerResults); + + return new GameResult(dealerResult, playerResults); + } + + private int getDealerScore(){ + return blackjackGame.getDealer().getScore(); + } + + private Map calculatePlayersResultMap(int dealerScore) { + return blackjackGame.getPlayers().stream() + .collect(Collectors.toMap( + Player::getName, + player -> new PlayerResult(DefaultBlackjackRule.judgeResult(player.getScore(), dealerScore)), + (existing, replacement) -> existing, + LinkedHashMap::new + )); + } + + private DealerResult calculateDealerResult(Map playerResults) { + final List dealerResults = new ArrayList<>(); + + for (PlayerResult playerResult : playerResults.values()) { + dealerResults.add(playerResult.result().reverse()); } - return false; + + return new DealerResult(dealerResults); } } diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java index aa75001e831..044401c68cc 100644 --- a/src/main/java/team/blackjack/view/OutputView.java +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -1,9 +1,14 @@ package team.blackjack.view; import java.util.List; +import java.util.Map; import java.util.Map.Entry; -import team.blackjack.control.dto.DrawResult; -import team.blackjack.control.dto.ScoreResult; +import team.blackjack.controler.dto.DrawResult; +import team.blackjack.controler.dto.GameResult; +import team.blackjack.controler.dto.GameResult.DealerResult; +import team.blackjack.controler.dto.GameResult.PlayerResult; +import team.blackjack.controler.dto.ScoreResult; +import team.blackjack.domain.Result; public class OutputView { @@ -56,7 +61,21 @@ public static void printParticipantScoreResult(ScoreResult scoreResult) { } } - public static void printGameResult(String playerName, String result) { - println("%s: %s".formatted(playerName, result)); + public static void printGameResult(GameResult result) { + final DealerResult dealerResult = result.dealerResult(); + final Map playeredResultMap = result.playerResultMap(); + + println("## 최종 승패:"); + println(""); + println("딜러: %d승 %d패 %d무".formatted( + dealerResult.countBy(Result.WIN), + dealerResult.countBy(Result.LOSE), + dealerResult.countBy(Result.DRAW)) + ); + + playeredResultMap.entrySet().stream() + .map(entry -> "%s: %s".formatted(entry.getKey(), entry.getValue().result().getName())) + .forEach(OutputView::println); + } } From 0dffe2e7731334755cc4527756b3dde9859a7a28 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 6 Mar 2026 16:25:41 +0900 Subject: [PATCH 12/50] =?UTF-8?q?test(BlackJackRule):=20=EB=B8=94=EB=9E=99?= =?UTF-8?q?=EC=9E=AD=20=EA=B2=8C=EC=9E=84=20=EA=B7=9C=EC=B9=99=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4,=20score=20=EA=B3=84=EC=82=B0=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/rule/DefaultBlackjackRuleTest.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java index b454da82807..c89ebacf534 100644 --- a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java +++ b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java @@ -1,5 +1,6 @@ package team.blackjack.domain.rule; +import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import team.blackjack.domain.rule.DefaultBlackjackRule; @@ -70,4 +71,22 @@ class DefaultBlackjackRuleTest { Assertions.assertEquals(false, isDealerMustDraw); } + + + @Test + void 숫자10과_6이후에_ACE가_2개_오는_경우_각각_1로_정상_해석되는지_테스트() { + List cards = List.of(Card.KING_OF_CLUBS, Card.SIX_OF_HEARTS, Card.ACE_OF_SPADES, Card.ACE_OF_HEARTS); + + int score = DefaultBlackjackRule.calculateBestScore(cards); + + Assertions.assertEquals(18, score); + } + + @Test + void Ace가_1장있는_경우_최적의_합_정상_계산_테스트() { + List cards = List.of(Card.FIVE_OF_CLUBS, Card.FIVE_OF_DIAMONDS, Card.ACE_OF_SPADES); + int score = DefaultBlackjackRule.calculateBestScore(cards); + + Assertions.assertEquals(21, score); + } } From 4232f37c1482cea24979402bbde75e299be6bccd Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 6 Mar 2026 16:45:59 +0900 Subject: [PATCH 13/50] =?UTF-8?q?fix(Result):=20=EB=88=84=EB=9D=BD?= =?UTF-8?q?=EB=90=9C=20=EC=8A=B9=ED=8C=A8=20=EC=97=B4=EA=B1=B0=ED=98=95=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team/blackjack/domain/Result.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/team/blackjack/domain/Result.java diff --git a/src/main/java/team/blackjack/domain/Result.java b/src/main/java/team/blackjack/domain/Result.java new file mode 100644 index 00000000000..e0509c7ff3a --- /dev/null +++ b/src/main/java/team/blackjack/domain/Result.java @@ -0,0 +1,28 @@ +package team.blackjack.domain; + +public enum Result { + WIN("승"), + DRAW("무"), + LOSE("패"); + + private final String name; + + Result(String name) { + + this.name = name; + } + + public String getName(){ + return this.name; + } + + public Result reverse(){ + if (this == WIN) { + return LOSE; + } + if (this == LOSE) { + return WIN; + } + return DRAW; + } +} From 919643bed95231752bb17a1e145e5cd1fe9ff9d4 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 6 Mar 2026 17:33:58 +0900 Subject: [PATCH 14/50] =?UTF-8?q?refactor:=20-=20score()=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20-=20controller=20=EA=B8=B0=EB=8A=A5=EB=B3=84=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=EB=B6=84=EB=A6=AC=20-=20dto=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- .../controler/BlackJackController.java | 34 ++++----- .../java/team/blackjack/domain/Dealer.java | 12 ++- src/main/java/team/blackjack/domain/Deck.java | 1 - src/main/java/team/blackjack/domain/Hand.java | 10 +-- .../java/team/blackjack/domain/Player.java | 25 +++++-- .../domain/rule/DefaultBlackjackRule.java | 6 ++ .../blackjack/service/BlackJackService.java | 75 +++++-------------- .../dto/DrawResult.java | 2 +- .../dto/GameResult.java | 2 +- .../dto/ScoreResult.java | 2 +- .../java/team/blackjack/view/InputParser.java | 11 --- .../java/team/blackjack/view/OutputView.java | 16 ++-- .../domain/rule/DefaultBlackjackRuleTest.java | 2 +- 14 files changed, 92 insertions(+), 111 deletions(-) rename src/main/java/team/blackjack/{controler => service}/dto/DrawResult.java (83%) rename src/main/java/team/blackjack/{controler => service}/dto/GameResult.java (93%) rename src/main/java/team/blackjack/{controler => service}/dto/ScoreResult.java (87%) delete mode 100644 src/main/java/team/blackjack/view/InputParser.java diff --git a/README.md b/README.md index 64cf978b093..add4665c21c 100644 --- a/README.md +++ b/README.md @@ -27,4 +27,7 @@ - 최종 승패를 출력한다. - 딜러는 승패전적을 출력한다. (예: 딜러: 1승 1패) - 플레이어는 승패 여부를 출력한다. (예: pobi: 승) - - 조건: 출력 순서는 딜러 먼저 출력하며, 입력받은 플레이어 순서로 이어 출력한다. \ No newline at end of file + - 조건: 출력 순서는 딜러 먼저 출력하며, 입력받은 플레이어 순서로 이어 출력한다. + +## 블랙잭 상세 규칙 +- 플레이어가 Bust인 경우에는 자동 패배 처리된다. 이때, 딜러의 카드상태에 의존되지 않고, 자동 패배한다. diff --git a/src/main/java/team/blackjack/controler/BlackJackController.java b/src/main/java/team/blackjack/controler/BlackJackController.java index 167bae3bc66..41f65838483 100644 --- a/src/main/java/team/blackjack/controler/BlackJackController.java +++ b/src/main/java/team/blackjack/controler/BlackJackController.java @@ -1,11 +1,9 @@ package team.blackjack.controler; import java.util.List; -import java.util.Map; -import team.blackjack.control.dto.DrawResult; -import team.blackjack.control.dto.ScoreResult; -import team.blackjack.domain.BlackjackGame; -import team.blackjack.domain.Card; +import team.blackjack.service.dto.DrawResult; +import team.blackjack.service.dto.GameResult; +import team.blackjack.service.dto.ScoreResult; import team.blackjack.domain.Player; import team.blackjack.domain.rule.DefaultBlackjackRule; import team.blackjack.service.BlackJackService; @@ -20,16 +18,14 @@ public BlackJackController(BlackJackService blackJackService) { } public void run() { - OutputView.printPlayerNameRequest(); - List playerNames = InputView.readPlayerNames(); - + List playerNames = readPlayerNames(); blackJackService.initGame(playerNames); - final BlackjackGame blackjackGame = blackJackService.getBlackjackGame(); - blackJackService.drawInitialCards(); - OutputView.printDrawResult(convert(blackjackGame)); - readHitDecision(blackjackGame.getPlayers()); + DrawResult drawResult = blackJackService.getHandResult(); + OutputView.printDrawResult(drawResult); + + readHitDecision(blackJackService.getPlayer()); while (blackJackService.shouldDealerHit()) { OutputView.printDealerHitMessage(); @@ -43,6 +39,10 @@ public void run() { OutputView.printGameResult(gameResult); } + private List readPlayerNames(){ + OutputView.printPlayerNameRequest(); + return InputView.readPlayerNames(); + } private void readHitDecision(List players) { players.forEach(this::processHit); @@ -59,15 +59,7 @@ private void processHit(Player player) { blackJackService.hitPlayer(player); OutputView.printPlayerCards(player.getName(), player.getHands().getFirst().getCardNames()); } - } - - private DrawResult convert(BlackjackGame game) { - final List playerNames = game.getPlayers().stream() - .map(Player::getName) - .toList(); - final List cards = game.getDealer().getHand().getCards(); - final Map> playerCards = game.getAllPlayerCards(); - return new DrawResult(playerNames, cards.getFirst().getCardName(), playerCards); + OutputView.printBustMessage(); } } diff --git a/src/main/java/team/blackjack/domain/Dealer.java b/src/main/java/team/blackjack/domain/Dealer.java index c01d1eddd8e..991952d2072 100644 --- a/src/main/java/team/blackjack/domain/Dealer.java +++ b/src/main/java/team/blackjack/domain/Dealer.java @@ -1,5 +1,7 @@ package team.blackjack.domain; +import team.blackjack.domain.rule.DefaultBlackjackRule; + public class Dealer { private final Hand hand; @@ -15,4 +17,12 @@ public Card draw(Deck deck) { public Hand getHand() { return this.hand; } -} \ No newline at end of file + + public int getScore(){ + return DefaultBlackjackRule.calculateBestScore(this.getHand().getCards()); + } + + public void hit(Card card) { + this.hand.addCard(card); + } +} diff --git a/src/main/java/team/blackjack/domain/Deck.java b/src/main/java/team/blackjack/domain/Deck.java index 839144133fe..aa54339aafa 100644 --- a/src/main/java/team/blackjack/domain/Deck.java +++ b/src/main/java/team/blackjack/domain/Deck.java @@ -12,7 +12,6 @@ public Deck() { this.cards = Arrays.stream(Card.values()) .collect(Collectors.toList()); - // 카드 섞기 Collections.shuffle(this.cards); } diff --git a/src/main/java/team/blackjack/domain/Hand.java b/src/main/java/team/blackjack/domain/Hand.java index 2ef72a7d5b4..af3b8dc0e2a 100644 --- a/src/main/java/team/blackjack/domain/Hand.java +++ b/src/main/java/team/blackjack/domain/Hand.java @@ -1,9 +1,9 @@ package team.blackjack.domain; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import team.blackjack.domain.rule.DefaultBlackjackRule; public class Hand { @@ -17,13 +17,13 @@ public List getCards() { return cards.stream().toList(); } - public int getCardCount() { - return cards.size(); - } - public List getCardNames() { return cards.stream() .map(Card::getCardName) .toList(); } + + public int getScore(){ + return DefaultBlackjackRule.calculateBestScore(this.getCards()); + } } diff --git a/src/main/java/team/blackjack/domain/Player.java b/src/main/java/team/blackjack/domain/Player.java index 8043fcdcd7e..a0a35566d35 100644 --- a/src/main/java/team/blackjack/domain/Player.java +++ b/src/main/java/team/blackjack/domain/Player.java @@ -5,21 +5,36 @@ public class Player { private final String name; - private final List hands = new ArrayList<>(); + private final List hands; public Player(String name) { this.name = name; + this.hands = initHands(); } public List getHands() { return List.copyOf(hands); } - public void addHand(Hand hand) { - hands.add(hand); - } - public String getName() { return this.name; } + + /** + * TODO: 추후 기능 확장시 한 라운드에 여러 개의 hand가 생기는 경우, 해당 메소드 수정 필요. + * @return + */ + public int getScore(){ + return this.hands.getFirst().getScore(); + } + + public void hit(Card card) { + this.hands.getFirst().addCard(card); + } + + private List initHands(){ + List hands = new ArrayList<>(); + hands.add(new Hand()); + return hands; + } } diff --git a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java index 92496ee4b35..41e3743d1c4 100644 --- a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java +++ b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java @@ -1,5 +1,11 @@ package team.blackjack.domain.rule; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import team.blackjack.domain.Card; +import team.blackjack.domain.Result; + public class DefaultBlackjackRule { public static final int BLACKJACK = 21; public static final int BLACKJACK_CARD_COUNT = 2; diff --git a/src/main/java/team/blackjack/service/BlackJackService.java b/src/main/java/team/blackjack/service/BlackJackService.java index 7fa74bd3376..9a5fb7f34ed 100644 --- a/src/main/java/team/blackjack/service/BlackJackService.java +++ b/src/main/java/team/blackjack/service/BlackJackService.java @@ -5,15 +5,15 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import team.blackjack.controler.dto.GameResult; -import team.blackjack.controler.dto.GameResult.DealerResult; -import team.blackjack.controler.dto.GameResult.PlayerResult; -import team.blackjack.controler.dto.ScoreResult; -import team.blackjack.domain.BlackjackGame; import team.blackjack.domain.Card; +import team.blackjack.service.dto.DrawResult; +import team.blackjack.service.dto.GameResult; +import team.blackjack.service.dto.GameResult.DealerResult; +import team.blackjack.service.dto.GameResult.PlayerResult; +import team.blackjack.service.dto.ScoreResult; +import team.blackjack.domain.BlackjackGame; import team.blackjack.domain.Dealer; import team.blackjack.domain.Deck; -import team.blackjack.domain.Hand; import team.blackjack.domain.Player; import team.blackjack.domain.Result; import team.blackjack.domain.rule.DefaultBlackjackRule; @@ -30,31 +30,23 @@ public void initGame(List playerNames) { blackjackGame = new BlackjackGame(dealer, players); } - - /** - * TODO: Draw 위치에 대한 고민 다시 해보기 - */ public void drawInitialCards() { final Deck deck = blackjackGame.getDeck(); final Dealer dealer = blackjackGame.getDealer(); // 플레이어 카드 초기화 for (Player player : blackjackGame.getPlayers()) { - Hand hand = new Hand(); - hand.addCard(dealer.draw(deck)); - hand.addCard(dealer.draw(deck)); - - player.addHand(hand); + player.hit(dealer.draw(deck)); + player.hit(dealer.draw(deck)); } // 딜러 카드 초기화 - Hand dealerHand = dealer.getHand(); - dealerHand.addCard(dealer.draw(deck)); - dealerHand.addCard(dealer.draw(deck)); + dealer.hit(dealer.draw(deck)); + dealer.hit(dealer.draw(deck)); } - public BlackjackGame getBlackjackGame() { - return this.blackjackGame; + public List getPlayer() { + return this.blackjackGame.getPlayers(); } public void hitPlayer(Player player) { @@ -91,7 +83,7 @@ public ScoreResult calculateAllParticipantScore() { ); final Dealer dealer = blackjackGame.getDealer(); - final int dealerScore = calculateSum(dealer.getHand().getCards()); + final int dealerScore = dealer.getScore(); return new ScoreResult( dealer.getHand().getCardNames(), @@ -102,41 +94,14 @@ public ScoreResult calculateAllParticipantScore() { ); } - /** - * 모든 카드를 발급한 이후에, 최종 점수 계산시에 사용하는 함수 - */ - private int calculateSum(List cards) { - if (existAceInCards(cards)) { - var result = cards.stream() - .collect(Collectors.partitioningBy(Card::isAce)); - - final List aceCards = result.get(true); - final List nonAceCards = result.get(false); - return calculateBestSumWithAce(nonAceCards, aceCards); - } - return calucateBestSumWithoutAce(cards); - - } - - private int calculateBestSumWithAce(List cardsWithoutAces, List aceCards) { - int currentSum = calucateBestSumWithoutAce(cardsWithoutAces); - - for (Card card : aceCards) { - // 카드가 ace 이고, 11을 사용하기에 적합한경우 11을 더한다. - if (DefaultBlackjackRule.canUseAceAsEleven(currentSum)) { - currentSum += card.getScore().getLast(); - } else { - currentSum += card.getScore().getFirst(); - } - } - - return currentSum; - } + public DrawResult getHandResult() { + final List playerNames = blackjackGame.getPlayers().stream() + .map(Player::getName) + .toList(); + final List cards = blackjackGame.getDealer().getHand().getCards(); + final Map> playerCards = blackjackGame.getAllPlayerCards(); - private int calucateBestSumWithoutAce(List cards) { - return cards.stream() - .mapToInt(card -> card.getScore().getFirst()) - .sum(); + return new DrawResult(playerNames, cards.getFirst().getCardName(), playerCards); } public GameResult getGameResult() { diff --git a/src/main/java/team/blackjack/controler/dto/DrawResult.java b/src/main/java/team/blackjack/service/dto/DrawResult.java similarity index 83% rename from src/main/java/team/blackjack/controler/dto/DrawResult.java rename to src/main/java/team/blackjack/service/dto/DrawResult.java index d860c22568b..3ef3fa77bf5 100644 --- a/src/main/java/team/blackjack/controler/dto/DrawResult.java +++ b/src/main/java/team/blackjack/service/dto/DrawResult.java @@ -1,4 +1,4 @@ -package team.blackjack.controler.dto; +package team.blackjack.service.dto; import java.util.List; import java.util.Map; diff --git a/src/main/java/team/blackjack/controler/dto/GameResult.java b/src/main/java/team/blackjack/service/dto/GameResult.java similarity index 93% rename from src/main/java/team/blackjack/controler/dto/GameResult.java rename to src/main/java/team/blackjack/service/dto/GameResult.java index 01b41ae615a..347d37163bd 100644 --- a/src/main/java/team/blackjack/controler/dto/GameResult.java +++ b/src/main/java/team/blackjack/service/dto/GameResult.java @@ -1,4 +1,4 @@ -package team.blackjack.controler.dto; +package team.blackjack.service.dto; import java.util.List; import java.util.Map; diff --git a/src/main/java/team/blackjack/controler/dto/ScoreResult.java b/src/main/java/team/blackjack/service/dto/ScoreResult.java similarity index 87% rename from src/main/java/team/blackjack/controler/dto/ScoreResult.java rename to src/main/java/team/blackjack/service/dto/ScoreResult.java index a9732749ce1..cd02dbf4eef 100644 --- a/src/main/java/team/blackjack/controler/dto/ScoreResult.java +++ b/src/main/java/team/blackjack/service/dto/ScoreResult.java @@ -1,4 +1,4 @@ -package team.blackjack.controler.dto; +package team.blackjack.service.dto; import java.util.List; import java.util.Map; diff --git a/src/main/java/team/blackjack/view/InputParser.java b/src/main/java/team/blackjack/view/InputParser.java deleted file mode 100644 index a397c008692..00000000000 --- a/src/main/java/team/blackjack/view/InputParser.java +++ /dev/null @@ -1,11 +0,0 @@ -package team.blackjack.view; - -import java.util.Scanner; - -public class InputParser { - - public static String parseNameInput(Scanner scanner) { - return scanner.nextLine(); - } - -} diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java index 044401c68cc..90cc7b3c0be 100644 --- a/src/main/java/team/blackjack/view/OutputView.java +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -3,11 +3,11 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import team.blackjack.controler.dto.DrawResult; -import team.blackjack.controler.dto.GameResult; -import team.blackjack.controler.dto.GameResult.DealerResult; -import team.blackjack.controler.dto.GameResult.PlayerResult; -import team.blackjack.controler.dto.ScoreResult; +import team.blackjack.service.dto.DrawResult; +import team.blackjack.service.dto.GameResult; +import team.blackjack.service.dto.GameResult.DealerResult; +import team.blackjack.service.dto.GameResult.PlayerResult; +import team.blackjack.service.dto.ScoreResult; import team.blackjack.domain.Result; public class OutputView { @@ -53,8 +53,6 @@ public static void printDealerHitMessage() { public static void printParticipantScoreResult(ScoreResult scoreResult) { println("딜러의 최종 카드: %s - 결과: %d".formatted(String.join(", ", scoreResult.dealerCard()), scoreResult.dealerScore())); for (String playerName : scoreResult.playerNames()) { - //pobi카드: 2하트, 8스페이드, A클로버 - 결과: 21 - println("%s의 카드: %s - 결과: %d".formatted(playerName, String.join(", ", scoreResult.playerCards().get(playerName)), scoreResult.playerScores().get(playerName))); @@ -78,4 +76,8 @@ public static void printGameResult(GameResult result) { .forEach(OutputView::println); } + + public static void printBustMessage() { + println("버스트 되었습니다. 더 이상 카드를 받을 수 없습니다."); + } } diff --git a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java index c89ebacf534..dd3f0953efc 100644 --- a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java +++ b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java @@ -3,7 +3,7 @@ import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import team.blackjack.domain.rule.DefaultBlackjackRule; +import team.blackjack.domain.Card; class DefaultBlackjackRuleTest { private static final DefaultBlackjackRule defaultBlackjackRule = new DefaultBlackjackRule(); From 16dd07af43c59154c197f2a0f4c69d29d3fe2558 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 6 Mar 2026 17:53:43 +0900 Subject: [PATCH 15/50] =?UTF-8?q?test(Player):=20=ED=94=8C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/blackjack/domain/PlayerTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/test/java/team/blackjack/domain/PlayerTest.java diff --git a/src/test/java/team/blackjack/domain/PlayerTest.java b/src/test/java/team/blackjack/domain/PlayerTest.java new file mode 100644 index 00000000000..8449071c70f --- /dev/null +++ b/src/test/java/team/blackjack/domain/PlayerTest.java @@ -0,0 +1,33 @@ +package team.blackjack.domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class PlayerTest { + @Test + void hit하면_플레이어_핸드에_카드가_추가된다() { + Player player = new Player("pobi"); + + player.hit(Card.ACE_OF_HEARTS); + + assertThat(player.getHands().getFirst().getCards()) + .containsExactly(Card.ACE_OF_HEARTS); + } + + @Test + void 킹과_에이스를_각각_1장씩_받은_플레이어의_점수는_21로_정상_계산된다() { + Player player = new Player("pobi"); + + player.hit(Card.ACE_OF_HEARTS); + player.hit(Card.KING_OF_CLUBS); + + assertThat(player.getScore()).isEqualTo(21); + } + + @Test + void 사용자가_카드를_받지않은_경우_점수는_0으로_정상_계산된다() { + Player player = new Player("pobi"); + assertThat(player.getScore()).isEqualTo(0); + } +} From 61e37fadb9c7af8036b38b3f3c20e55dc0272c3d Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 6 Mar 2026 18:02:02 +0900 Subject: [PATCH 16/50] =?UTF-8?q?test(Hand):=20=ED=95=B8=EB=93=9C,=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=EC=A0=90=EC=88=98=20=EA=B3=84=EC=82=B0=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team/blackjack/domain/HandTest.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/test/java/team/blackjack/domain/HandTest.java diff --git a/src/test/java/team/blackjack/domain/HandTest.java b/src/test/java/team/blackjack/domain/HandTest.java new file mode 100644 index 00000000000..9737076315a --- /dev/null +++ b/src/test/java/team/blackjack/domain/HandTest.java @@ -0,0 +1,68 @@ +package team.blackjack.domain; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import team.blackjack.domain.rule.DefaultBlackjackRule; + +import static org.assertj.core.api.Assertions.assertThat; + +class HandTest { + @Test + void 에이스와_킹_두_장은_블랙잭_21로_정상_계산된다() { + Hand hand = new Hand(); + hand.addCard(Card.ACE_OF_HEARTS); + hand.addCard(Card.KING_OF_HEARTS); + + assertThat(hand.getScore()).isEqualTo(21); + } + + @Test + void 숫자_카드만_있을_때_합이_정확히_계산된다() { + Hand hand = new Hand(); + hand.addCard(Card.FIVE_OF_HEARTS); + hand.addCard(Card.TEN_OF_HEARTS); + + assertThat(hand.getScore()).isEqualTo(15); + } + + @Test + void 다른_카드들의_총합이_10인_이하인_경우_에이스가_11로_계산된다() { + Hand hand = new Hand(); + hand.addCard(Card.FIVE_OF_HEARTS); + hand.addCard(Card.FOUR_OF_HEARTS); + hand.addCard(Card.ACE_OF_HEARTS); + + assertThat(hand.getScore()).isEqualTo(20); + } + + @Test + void 다른_카드들의_총합이_10인인_경우_에이스가_11로_계산된다() { + Hand hand = new Hand(); + hand.addCard(Card.SIX_OF_HEARTS); + hand.addCard(Card.FOUR_OF_HEARTS); + hand.addCard(Card.ACE_OF_HEARTS); + + assertThat(hand.getScore()).isEqualTo(21); + } + + @Test + void 다른_카드들의_총합이_11_이상인_경우_에이스가_1로_계산된다() { + Hand hand = new Hand(); + hand.addCard(Card.FIVE_OF_HEARTS); + hand.addCard(Card.SEVEN_OF_HEARTS); + hand.addCard(Card.ACE_OF_HEARTS); + + assertThat(hand.getScore()).isEqualTo(13); + } + + @Test + void 다른_카드들의_총합이_11인인_경우_에이스가_1로_계산된다() { + Hand hand = new Hand(); + hand.addCard(Card.FIVE_OF_HEARTS); + hand.addCard(Card.SIX_OF_HEARTS); + hand.addCard(Card.ACE_OF_HEARTS); + + assertThat(hand.getScore()).isEqualTo(12); + } +} From c4ac74a074a91e50dd25eb7e97538afcf66f8db1 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 6 Mar 2026 18:07:50 +0900 Subject: [PATCH 17/50] =?UTF-8?q?test(Deck):=20=EB=8D=B1,=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EB=B0=9C=EA=B8=89=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 --- .../java/team/blackjack/domain/DeckTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/test/java/team/blackjack/domain/DeckTest.java diff --git a/src/test/java/team/blackjack/domain/DeckTest.java b/src/test/java/team/blackjack/domain/DeckTest.java new file mode 100644 index 00000000000..b21fb4bf327 --- /dev/null +++ b/src/test/java/team/blackjack/domain/DeckTest.java @@ -0,0 +1,39 @@ +package team.blackjack.domain; + +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class DeckTest { + + @Test + void draw는_카드_한_장을_반환한다() { + Deck deck = new Deck(); + Card card = deck.draw(); + assertThat(card).isNotNull(); + } + + @Test + void draw를_호출하면_덱에서_카드가_제거된다() { + Deck deck = new Deck(); + deck.draw(); + deck.draw(); + // 52장 중 2장 제거 후 50장 남음 - 추가 draw 가능 + Card card = deck.draw(); + assertThat(card).isNotNull(); + } + + @Test + void 전체_52장_드로우_시_중복되지_않는다() { + Deck deck = new Deck(); + Set drawnCards = new HashSet<>(); + for (int i = 0; i < 52; i++) { + Card card = deck.draw(); + drawnCards.add(card); + } + assertThat(drawnCards).hasSize(52); + } +} From f3ecebf75acfb686a2f9d44be538dbfc87eb05b7 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 6 Mar 2026 18:13:08 +0900 Subject: [PATCH 18/50] =?UTF-8?q?test(Dealer):=20=EB=94=9C=EB=9F=AC=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/blackjack/domain/DealerTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/test/java/team/blackjack/domain/DealerTest.java diff --git a/src/test/java/team/blackjack/domain/DealerTest.java b/src/test/java/team/blackjack/domain/DealerTest.java new file mode 100644 index 00000000000..b38ab02dea8 --- /dev/null +++ b/src/test/java/team/blackjack/domain/DealerTest.java @@ -0,0 +1,33 @@ +package team.blackjack.domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class DealerTest { + + @Test + void hit하면_핸드에_카드가_추가된다() { + Dealer dealer = new Dealer(); + dealer.hit(Card.ACE_OF_HEARTS); + dealer.hit(Card.KING_OF_HEARTS); + + assertThat(dealer.getHand().getCards()) + .containsExactly(Card.ACE_OF_HEARTS, Card.KING_OF_HEARTS); + } + @Test + void 킹과_에이스를_각각_1장씩_받은_딜러의_점수는_21로_정상_계산된다() { + Dealer dealer = new Dealer(); + + dealer.hit(Card.ACE_OF_HEARTS); + dealer.hit(Card.KING_OF_CLUBS); + + assertThat(dealer.getScore()).isEqualTo(21); + } + + @Test + void 딜러가_카드를_받지않은_경우_점수는_0으로_정상_계산된다() { + Dealer dealer = new Dealer(); + assertThat(dealer.getScore()).isEqualTo(0); + } +} From de5db9ec35dcee82159c99a330b2dde628e63b41 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 6 Mar 2026 18:21:30 +0900 Subject: [PATCH 19/50] =?UTF-8?q?test(Service):=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/BlackJackServiceTest.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/test/java/team/blackjack/service/BlackJackServiceTest.java diff --git a/src/test/java/team/blackjack/service/BlackJackServiceTest.java b/src/test/java/team/blackjack/service/BlackJackServiceTest.java new file mode 100644 index 00000000000..70032187de9 --- /dev/null +++ b/src/test/java/team/blackjack/service/BlackJackServiceTest.java @@ -0,0 +1,82 @@ +package team.blackjack.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import team.blackjack.domain.Player; + +import java.util.List; +import team.blackjack.service.dto.ScoreResult; + +import static org.assertj.core.api.Assertions.assertThat; + +class BlackJackServiceTest { + + private BlackJackService blackJackService; + + @BeforeEach + void setUp() { + blackJackService = new BlackJackService(); + } + + @Test + void 플레이어_이름_목록으로_게임을_초기화한다() { + blackJackService.initGame(List.of("pobi", "jason")); + + List players = blackJackService.getPlayer(); + assertThat(players).hasSize(2); + assertThat(players.stream().map(Player::getName).toList()) + .containsExactly("pobi", "jason"); + } + + @Test + void 게임초기화_후_각_플레이어와_딜러가_카드_2장씩_가진다() { + blackJackService.initGame(List.of("pobi")); + blackJackService.drawInitialCards(); + + List players = blackJackService.getPlayer(); + assertThat(players.getFirst().getHands().getFirst().getCards()).hasSize(2); + + ScoreResult scoreResult = blackJackService.calculateAllParticipantScore(); + assertThat(scoreResult.dealerCard()).hasSize(2); + assertThat(scoreResult.playerCards().get("pobi")).hasSize(2); + } + + @Test + void 플레이어가_hit_하는_경우_카드가_한_장_추가된다() { + blackJackService.initGame(List.of("pobi")); + blackJackService.drawInitialCards(); + + Player pobi = blackJackService.getPlayer().getFirst(); + int sizeBefore = pobi.getHands().getFirst().getCards().size(); + + blackJackService.hitPlayer(pobi); + + assertThat(pobi.getHands().getFirst().getCards()).hasSize(sizeBefore + 1); + } + + @Test + void 딜러와_모든_플레이어_점수와_카드정보를_계산한다() { + blackJackService.initGame(List.of("pobi", "jason")); + blackJackService.drawInitialCards(); + + var scoreResult = blackJackService.calculateAllParticipantScore(); + + assertThat(scoreResult.playerNames()).containsExactly("pobi", "jason"); + assertThat(scoreResult.dealerCard()).isNotEmpty(); + assertThat(scoreResult.playerCards()).containsKeys("pobi", "jason"); + assertThat(scoreResult.playerScores()).containsKeys("pobi", "jason"); + + } + + @Test + void 딜러가_hit하는_경우_카드가_추가된다() { + blackJackService.initGame(List.of("pobi")); + blackJackService.drawInitialCards(); + + int dealerCardsBefore = blackJackService.calculateAllParticipantScore().dealerCard().size(); + blackJackService.hitDealer(); + int dealerCardsAfter = blackJackService.calculateAllParticipantScore().dealerCard().size(); + + assertThat(dealerCardsAfter).isEqualTo(dealerCardsBefore + 1); + } +} From 36eda30bc1e3f484d5e140f3e4a73ac12a91c741 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 6 Mar 2026 18:24:08 +0900 Subject: [PATCH 20/50] =?UTF-8?q?test(rule):=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/rule/DefaultBlackjackRuleTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java index dd3f0953efc..b1ffe2c02d5 100644 --- a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java +++ b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import team.blackjack.domain.Card; +import team.blackjack.domain.Result; class DefaultBlackjackRuleTest { private static final DefaultBlackjackRule defaultBlackjackRule = new DefaultBlackjackRule(); @@ -89,4 +90,34 @@ class DefaultBlackjackRuleTest { Assertions.assertEquals(21, score); } + + @Test + void 내_점수가_더_높으면_WIN() { + Assertions.assertEquals(Result.WIN, DefaultBlackjackRule.judgeResult(20, 18)); + } + + @Test + void 상대_점수가_더_높으면_LOSE() { + Assertions.assertEquals(Result.LOSE, DefaultBlackjackRule.judgeResult(18, 20)); + } + + @Test + void 동점이면_DRAW() { + Assertions.assertEquals(Result.DRAW, DefaultBlackjackRule.judgeResult(17, 17)); + } + + @Test + void 버스트_21초과면_LOSE() { + Assertions.assertEquals(Result.LOSE, DefaultBlackjackRule.judgeResult(22, 20)); + } + + @Test + void 합이_10이하면_Ace는_11() { + Assertions.assertTrue(DefaultBlackjackRule.canUseAceAsEleven(10)); + } + + @Test + void 합이_10초과면_Ace는_11() { + Assertions.assertFalse(DefaultBlackjackRule.canUseAceAsEleven(11)); + } } From 966d2a807d81bcceffb44f3d29c58fbee8787d61 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Mon, 9 Mar 2026 15:58:36 +0900 Subject: [PATCH 21/50] =?UTF-8?q?refactor(Players):=20BlackjackGame.player?= =?UTF-8?q?s=EB=A5=BC=20=EC=9D=BC=EA=B8=89=20=EC=BB=AC=EB=A0=89=EC=85=98?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controler/BlackJackController.java | 20 +++-- .../team/blackjack/domain/BlackjackGame.java | 23 +----- .../java/team/blackjack/domain/Player.java | 8 ++ .../java/team/blackjack/domain/Players.java | 56 +++++++++++++ .../blackjack/service/BlackJackService.java | 79 ++++++++----------- .../blackjack/service/dto/GameResult.java | 13 +++ .../service/BlackJackServiceTest.java | 29 ++++--- 7 files changed, 137 insertions(+), 91 deletions(-) create mode 100644 src/main/java/team/blackjack/domain/Players.java diff --git a/src/main/java/team/blackjack/controler/BlackJackController.java b/src/main/java/team/blackjack/controler/BlackJackController.java index 41f65838483..5d44aff90b4 100644 --- a/src/main/java/team/blackjack/controler/BlackJackController.java +++ b/src/main/java/team/blackjack/controler/BlackJackController.java @@ -4,8 +4,6 @@ import team.blackjack.service.dto.DrawResult; import team.blackjack.service.dto.GameResult; import team.blackjack.service.dto.ScoreResult; -import team.blackjack.domain.Player; -import team.blackjack.domain.rule.DefaultBlackjackRule; import team.blackjack.service.BlackJackService; import team.blackjack.view.InputView; import team.blackjack.view.OutputView; @@ -22,10 +20,10 @@ public void run() { blackJackService.initGame(playerNames); blackJackService.drawInitialCards(); - DrawResult drawResult = blackJackService.getHandResult(); + DrawResult drawResult = blackJackService.getDrawResult(); OutputView.printDrawResult(drawResult); - readHitDecision(blackJackService.getPlayer()); + readHitDecision(blackJackService.getPlayerNames()); while (blackJackService.shouldDealerHit()) { OutputView.printDealerHitMessage(); @@ -44,20 +42,20 @@ private List readPlayerNames(){ return InputView.readPlayerNames(); } - private void readHitDecision(List players) { - players.forEach(this::processHit); + private void readHitDecision(List playerNames) { + playerNames.forEach(this::processHit); } - private void processHit(Player player) { - while (!DefaultBlackjackRule.isBust(player.getScore())) { - OutputView.printAskDrawCard(player.getName()); + private void processHit(String playerName) { + while (blackJackService.shouldPlayerHit(playerName)) { + OutputView.printAskDrawCard(playerName); if (!InputView.readHitDecision()) { return; } - blackJackService.hitPlayer(player); - OutputView.printPlayerCards(player.getName(), player.getHands().getFirst().getCardNames()); + blackJackService.hitPlayer(playerName); + OutputView.printPlayerCards(playerName, blackJackService.getPlayerCardNamesByName(playerName)); } OutputView.printBustMessage(); diff --git a/src/main/java/team/blackjack/domain/BlackjackGame.java b/src/main/java/team/blackjack/domain/BlackjackGame.java index 7874f935896..cc4cf6b809d 100644 --- a/src/main/java/team/blackjack/domain/BlackjackGame.java +++ b/src/main/java/team/blackjack/domain/BlackjackGame.java @@ -6,10 +6,10 @@ public class BlackjackGame { private final Dealer dealer; - private final List players; + private final Players players; private final Deck deck; - public BlackjackGame(Dealer dealer, List players) { + public BlackjackGame(Dealer dealer, Players players) { this.dealer = dealer; this.players = players; this.deck = new Deck(); @@ -19,27 +19,10 @@ public Dealer getDealer() { return dealer; } - public List getPlayers() { + public Players getPlayers() { return players; } - public Map> getAllPlayerCards(){ - final HashMap> result = new HashMap<>(); - for (Player player : players) { - result.put(player.getName(), getPlayerCardInAllHand(player)); - } - - return result; - } - - private List getPlayerCardInAllHand(Player player) { - return player.getHands().stream() - .map(Hand::getCards) - .flatMap(List::stream) - .map(Card::getCardName) - .toList(); - } - public Deck getDeck() { return deck; } diff --git a/src/main/java/team/blackjack/domain/Player.java b/src/main/java/team/blackjack/domain/Player.java index a0a35566d35..256981cded1 100644 --- a/src/main/java/team/blackjack/domain/Player.java +++ b/src/main/java/team/blackjack/domain/Player.java @@ -37,4 +37,12 @@ private List initHands(){ hands.add(new Hand()); return hands; } + + public List getCardInAllHand() { + return hands.stream() + .map(Hand::getCards) + .flatMap(List::stream) + .map(Card::getCardName) + .toList(); + } } diff --git a/src/main/java/team/blackjack/domain/Players.java b/src/main/java/team/blackjack/domain/Players.java new file mode 100644 index 00000000000..b5f397f0b25 --- /dev/null +++ b/src/main/java/team/blackjack/domain/Players.java @@ -0,0 +1,56 @@ +package team.blackjack.domain; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class Players { + private final List players; + + public Players(List players) { + this.players = players; + } + + public List getPlayerList() { + return List.copyOf(players); + } + + // 플레이어 카드 초기화 + public void initPlayerHands(Deck deck) { + for (Player player : players) { + player.hit(deck.draw()); + player.hit(deck.draw()); + } + } + + public Map> getCardsByPlayer(){ + final HashMap> result = new HashMap<>(); + for (Player player : players) { + result.put(player.getName(), player.getCardInAllHand()); + } + + return result; + } + + public List getPlayerNames() { + return players.stream() + .map(Player::getName) + .toList(); + } + + public Map getPlayerScoresByPlayer() { + return players.stream() + .collect(Collectors.toMap( + Player::getName, + Player::getScore) + ); + } + + public Player getPlayerByName(String name) { + return players.stream() + .filter(player -> player.getName().equals(name)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("해당 이름의 플레이어가 존재하지 않습니다.")); + } +} diff --git a/src/main/java/team/blackjack/service/BlackJackService.java b/src/main/java/team/blackjack/service/BlackJackService.java index 9a5fb7f34ed..75eae2a4bf4 100644 --- a/src/main/java/team/blackjack/service/BlackJackService.java +++ b/src/main/java/team/blackjack/service/BlackJackService.java @@ -1,11 +1,11 @@ package team.blackjack.service; -import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import team.blackjack.domain.Card; +import team.blackjack.domain.Players; import team.blackjack.service.dto.DrawResult; import team.blackjack.service.dto.GameResult; import team.blackjack.service.dto.GameResult.DealerResult; @@ -15,7 +15,6 @@ import team.blackjack.domain.Dealer; import team.blackjack.domain.Deck; import team.blackjack.domain.Player; -import team.blackjack.domain.Result; import team.blackjack.domain.rule.DefaultBlackjackRule; public class BlackJackService { @@ -27,7 +26,7 @@ public void initGame(List playerNames) { .map(Player::new) .toList(); - blackjackGame = new BlackjackGame(dealer, players); + blackjackGame = new BlackjackGame(dealer, new Players(players)); } public void drawInitialCards() { @@ -35,24 +34,26 @@ public void drawInitialCards() { final Dealer dealer = blackjackGame.getDealer(); // 플레이어 카드 초기화 - for (Player player : blackjackGame.getPlayers()) { - player.hit(dealer.draw(deck)); - player.hit(dealer.draw(deck)); - } + getPlayers().initPlayerHands(deck); // 딜러 카드 초기화 dealer.hit(dealer.draw(deck)); dealer.hit(dealer.draw(deck)); } - public List getPlayer() { - return this.blackjackGame.getPlayers(); + public boolean shouldPlayerHit(String name) { + final Player player = getPlayers().getPlayerByName(name); + + return !DefaultBlackjackRule.isBust(player.getScore()); } - public void hitPlayer(Player player) { - player.hit(blackjackGame.getDeck().draw()); + public void hitPlayer(String name) { + getPlayers() + .getPlayerByName(name) + .hit(blackjackGame.getDeck().draw()); } + public boolean shouldDealerHit() { final int score = blackjackGame.getDealer().getScore(); @@ -66,57 +67,46 @@ public void hitDealer() { } public ScoreResult calculateAllParticipantScore() { - final List playerNames = blackjackGame.getPlayers().stream() - .map(Player::getName) - .toList(); - - final Map> playerCards = blackjackGame.getPlayers().stream() - .collect(Collectors.toMap( - Player::getName, - player -> player.getHands().getFirst().getCardNames() - )); - - final Map playerScores = blackjackGame.getPlayers().stream() - .collect(Collectors.toMap( - Player::getName, - Player::getScore) - ); + final List playerNames = getPlayerNames(); + final Map> playerCards = getPlayers().getCardsByPlayer(); + final Map playerScores = getPlayers().getPlayerScoresByPlayer(); final Dealer dealer = blackjackGame.getDealer(); - final int dealerScore = dealer.getScore(); return new ScoreResult( dealer.getHand().getCardNames(), - dealerScore, + dealer.getScore(), playerNames, playerCards, playerScores ); } - public DrawResult getHandResult() { - final List playerNames = blackjackGame.getPlayers().stream() - .map(Player::getName) - .toList(); + public List getPlayerNames() { + return getPlayers().getPlayerNames(); + } + + public List getPlayerCardNamesByName(String name) { + return getPlayers().getPlayerByName(name).getCardInAllHand(); + } + + public DrawResult getDrawResult() { + final List playerNames = getPlayerNames(); final List cards = blackjackGame.getDealer().getHand().getCards(); - final Map> playerCards = blackjackGame.getAllPlayerCards(); + final Map> playerCards = getPlayers().getCardsByPlayer(); return new DrawResult(playerNames, cards.getFirst().getCardName(), playerCards); } public GameResult getGameResult() { - final Map playerResults = calculatePlayersResultMap(getDealerScore()); - final DealerResult dealerResult = calculateDealerResult(playerResults); + final Map playerResults = calculatePlayersResultMap(blackjackGame.getDealer().getScore()); + final DealerResult dealerResult = DealerResult.from(playerResults.values()); return new GameResult(dealerResult, playerResults); } - private int getDealerScore(){ - return blackjackGame.getDealer().getScore(); - } - private Map calculatePlayersResultMap(int dealerScore) { - return blackjackGame.getPlayers().stream() + return getPlayers().getPlayerList().stream() .collect(Collectors.toMap( Player::getName, player -> new PlayerResult(DefaultBlackjackRule.judgeResult(player.getScore(), dealerScore)), @@ -125,13 +115,8 @@ private Map calculatePlayersResultMap(int dealerScore) { )); } - private DealerResult calculateDealerResult(Map playerResults) { - final List dealerResults = new ArrayList<>(); - for (PlayerResult playerResult : playerResults.values()) { - dealerResults.add(playerResult.result().reverse()); - } - - return new DealerResult(dealerResults); + private Players getPlayers() { + return this.blackjackGame.getPlayers(); } } diff --git a/src/main/java/team/blackjack/service/dto/GameResult.java b/src/main/java/team/blackjack/service/dto/GameResult.java index 347d37163bd..f514828eb53 100644 --- a/src/main/java/team/blackjack/service/dto/GameResult.java +++ b/src/main/java/team/blackjack/service/dto/GameResult.java @@ -1,5 +1,7 @@ package team.blackjack.service.dto; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import team.blackjack.domain.Result; @@ -8,9 +10,20 @@ public record GameResult( DealerResult dealerResult, Map playerResultMap ) { + public record DealerResult( List results ){ + public static DealerResult from(Collection playerResults) { + final List dealerResults = new ArrayList<>(); + + for (PlayerResult playerResult : playerResults) { + dealerResults.add(playerResult.result().reverse()); + } + + return new DealerResult(dealerResults); + } + public long countBy(Result target) { return results.stream() .filter(result -> result == target) diff --git a/src/test/java/team/blackjack/service/BlackJackServiceTest.java b/src/test/java/team/blackjack/service/BlackJackServiceTest.java index 70032187de9..3e8b699ec45 100644 --- a/src/test/java/team/blackjack/service/BlackJackServiceTest.java +++ b/src/test/java/team/blackjack/service/BlackJackServiceTest.java @@ -2,7 +2,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import team.blackjack.domain.Player; import java.util.List; import team.blackjack.service.dto.ScoreResult; @@ -22,36 +21,40 @@ void setUp() { void 플레이어_이름_목록으로_게임을_초기화한다() { blackJackService.initGame(List.of("pobi", "jason")); - List players = blackJackService.getPlayer(); - assertThat(players).hasSize(2); - assertThat(players.stream().map(Player::getName).toList()) + List playerNames = blackJackService.getPlayerNames(); + assertThat(playerNames).hasSize(2); + assertThat(playerNames) .containsExactly("pobi", "jason"); } @Test void 게임초기화_후_각_플레이어와_딜러가_카드_2장씩_가진다() { - blackJackService.initGame(List.of("pobi")); + String pobi = "pobi"; + blackJackService.initGame(List.of(pobi)); blackJackService.drawInitialCards(); - List players = blackJackService.getPlayer(); - assertThat(players.getFirst().getHands().getFirst().getCards()).hasSize(2); + List playerCards = blackJackService.getPlayerCardNamesByName(pobi); + assertThat(playerCards.getFirst()).hasSize(2); ScoreResult scoreResult = blackJackService.calculateAllParticipantScore(); assertThat(scoreResult.dealerCard()).hasSize(2); - assertThat(scoreResult.playerCards().get("pobi")).hasSize(2); + assertThat(scoreResult.playerCards().get(pobi)).hasSize(2); } @Test void 플레이어가_hit_하는_경우_카드가_한_장_추가된다() { - blackJackService.initGame(List.of("pobi")); + String playerName = "pobi"; + blackJackService.initGame(List.of(playerName)); blackJackService.drawInitialCards(); - Player pobi = blackJackService.getPlayer().getFirst(); - int sizeBefore = pobi.getHands().getFirst().getCards().size(); + List pobiCardNames = blackJackService.getPlayerCardNamesByName(playerName); + int sizeBefore = pobiCardNames.size(); - blackJackService.hitPlayer(pobi); + blackJackService.hitPlayer(playerName); - assertThat(pobi.getHands().getFirst().getCards()).hasSize(sizeBefore + 1); + pobiCardNames = blackJackService.getPlayerCardNamesByName(playerName); + int sizeAfter = pobiCardNames.size(); + assertThat(sizeAfter).isEqualTo(sizeBefore + 1); } @Test From 08ee405c631ddfb65d4f5c7036e9191f0e55d3f4 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Mon, 9 Mar 2026 16:13:43 +0900 Subject: [PATCH 22/50] =?UTF-8?q?refactor(OutputView):=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team/blackjack/view/OutputView.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java index 90cc7b3c0be..d624593b41e 100644 --- a/src/main/java/team/blackjack/view/OutputView.java +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -16,18 +16,10 @@ private static void println(String message) { System.out.println(message); } - private static void print(String message) { - System.out.print(message); - } - public static void printPlayerNameRequest() { println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); } - public static void printPlayerActionRequest(String playerName) { - println("%s는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)".formatted(playerName)); - } - public static void printDrawResult(DrawResult result) { String playerNames = String.join(", ", result.playerNames()); println("딜러와 %s에게 2장을 나누었습니다.".formatted(playerNames)); From c1601e1ab865b4b44869680a140db71d05ab6de8 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Mon, 9 Mar 2026 16:34:16 +0900 Subject: [PATCH 23/50] =?UTF-8?q?feat(InputView):=20Hit=20=EC=97=AC?= =?UTF-8?q?=EB=B6=80=20=EC=9E=85=EB=A0=A5=EC=97=90=20=EB=8C=80=ED=95=B4,?= =?UTF-8?q?=20=EC=9E=98=EB=AA=BB=EB=90=9C=20=EC=9E=85=EB=A0=A5=EC=9D=80=20?= =?UTF-8?q?=EC=9E=AC=EC=9E=85=EB=A0=A5=20=EB=B0=9B=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/blackjack/controler/BlackJackController.java | 10 +++++++++- src/main/java/team/blackjack/view/InputView.java | 4 ++-- src/main/java/team/blackjack/view/OutputView.java | 9 +++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/team/blackjack/controler/BlackJackController.java b/src/main/java/team/blackjack/controler/BlackJackController.java index 5d44aff90b4..8ccce61302d 100644 --- a/src/main/java/team/blackjack/controler/BlackJackController.java +++ b/src/main/java/team/blackjack/controler/BlackJackController.java @@ -50,7 +50,15 @@ private void processHit(String playerName) { while (blackJackService.shouldPlayerHit(playerName)) { OutputView.printAskDrawCard(playerName); - if (!InputView.readHitDecision()) { + String hitYn = InputView.readHitDecision(); + + while (!hitYn.equalsIgnoreCase("y") && !hitYn.equalsIgnoreCase("n")) { + OutputView.printWrongInputMessage(); + hitYn = InputView.readHitDecision(); + } + + final boolean isStand = hitYn.equalsIgnoreCase("n"); + if (isStand) { return; } diff --git a/src/main/java/team/blackjack/view/InputView.java b/src/main/java/team/blackjack/view/InputView.java index 9c96a3fb1bb..9fc949f4b8b 100644 --- a/src/main/java/team/blackjack/view/InputView.java +++ b/src/main/java/team/blackjack/view/InputView.java @@ -20,7 +20,7 @@ public static List readPlayerNames() { * 플레이어가 한장의 카드를 더 받을지 여부를 입력받는다. * @return */ - public static boolean readHitDecision() { - return readInput().equalsIgnoreCase("y"); + public static String readHitDecision() { + return readInput(); } } diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java index d624593b41e..8c707222c0f 100644 --- a/src/main/java/team/blackjack/view/OutputView.java +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -12,7 +12,7 @@ public class OutputView { - private static void println(String message) { + public static void println(String message) { System.out.println(message); } @@ -43,6 +43,7 @@ public static void printDealerHitMessage() { } public static void printParticipantScoreResult(ScoreResult scoreResult) { + println(""); println("딜러의 최종 카드: %s - 결과: %d".formatted(String.join(", ", scoreResult.dealerCard()), scoreResult.dealerScore())); for (String playerName : scoreResult.playerNames()) { println("%s의 카드: %s - 결과: %d".formatted(playerName, @@ -55,8 +56,8 @@ public static void printGameResult(GameResult result) { final DealerResult dealerResult = result.dealerResult(); final Map playeredResultMap = result.playerResultMap(); - println("## 최종 승패:"); println(""); + println("## 최종 승패:"); println("딜러: %d승 %d패 %d무".formatted( dealerResult.countBy(Result.WIN), dealerResult.countBy(Result.LOSE), @@ -72,4 +73,8 @@ public static void printGameResult(GameResult result) { public static void printBustMessage() { println("버스트 되었습니다. 더 이상 카드를 받을 수 없습니다."); } + + public static void printWrongInputMessage() { + println("잘못된 입력입니다. y, n 중 하나를 입력해주세요."); + } } From 55f8cda798dd9173371797fe95d30afdf55e79f9 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Mon, 9 Mar 2026 16:58:19 +0900 Subject: [PATCH 24/50] =?UTF-8?q?refactor(Card):=20Number=20=EC=97=B4?= =?UTF-8?q?=EA=B1=B0=ED=98=95=EC=9D=84=20=EC=B6=94=EA=B0=80=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team/blackjack/domain/Card.java | 177 ++++++++++-------- 1 file changed, 101 insertions(+), 76 deletions(-) diff --git a/src/main/java/team/blackjack/domain/Card.java b/src/main/java/team/blackjack/domain/Card.java index 6072c013a64..81925f4e5f6 100644 --- a/src/main/java/team/blackjack/domain/Card.java +++ b/src/main/java/team/blackjack/domain/Card.java @@ -3,99 +3,124 @@ import java.util.List; public enum Card { - ACE_OF_HEARTS(Suit.HEARTS, "A하트", true, List.of(1, 11)), - TWO_OF_HEARTS(Suit.HEARTS, "2하트", false, List.of(2)), - THREE_OF_HEARTS(Suit.HEARTS, "3하트", false, List.of(3)), - FOUR_OF_HEARTS(Suit.HEARTS, "4하트", false, List.of(4)), - FIVE_OF_HEARTS(Suit.HEARTS, "5하트", false, List.of(5)), - SIX_OF_HEARTS(Suit.HEARTS, "6하트", false, List.of(6)), - SEVEN_OF_HEARTS(Suit.HEARTS, "7하트", false, List.of(7)), - EIGHT_OF_HEARTS(Suit.HEARTS, "8하트", false, List.of(8)), - NINE_OF_HEARTS(Suit.HEARTS, "9하트", false, List.of(9)), - TEN_OF_HEARTS(Suit.HEARTS, "10하트", false, List.of(10)), - JACK_OF_HEARTS(Suit.HEARTS, "J하트", false, List.of(10)), - QUEEN_OF_HEARTS(Suit.HEARTS, "Q하트", false, List.of(10)), - KING_OF_HEARTS(Suit.HEARTS, "K하트", false, List.of(10)), - - ACE_OF_DIAMONDS(Suit.DIAMONDS, "A다이아", true, List.of(1, 11)), - TWO_OF_DIAMONDS(Suit.DIAMONDS, "2다이아", false, List.of(2)), - THREE_OF_DIAMONDS(Suit.DIAMONDS, "3다이아", false, List.of(3)), - FOUR_OF_DIAMONDS(Suit.DIAMONDS, "4다이아", false, List.of(4)), - FIVE_OF_DIAMONDS(Suit.DIAMONDS, "5다이아", false, List.of(5)), - SIX_OF_DIAMONDS(Suit.DIAMONDS, "6다이아", false, List.of(6)), - SEVEN_OF_DIAMONDS(Suit.DIAMONDS, "7다이아", false, List.of(7)), - EIGHT_OF_DIAMONDS(Suit.DIAMONDS, "8다이아", false, List.of(8)), - NINE_OF_DIAMONDS(Suit.DIAMONDS, "9다이아", false, List.of(9)), - TEN_OF_DIAMONDS(Suit.DIAMONDS, "10다이아", false, List.of(10)), - JACK_OF_DIAMONDS(Suit.DIAMONDS, "J다이아", false, List.of(10)), - QUEEN_OF_DIAMONDS(Suit.DIAMONDS, "Q다이아", false, List.of(10)), - KING_OF_DIAMONDS(Suit.DIAMONDS, "K다이아", false, List.of(10)), - - ACE_OF_CLUBS(Suit.CLUBS, "A클로버", true, List.of(1, 11)), - TWO_OF_CLUBS(Suit.CLUBS, "2클로버", false, List.of(2)), - THREE_OF_CLUBS(Suit.CLUBS, "3클로버", false, List.of(3)), - FOUR_OF_CLUBS(Suit.CLUBS, "4클로버", false, List.of(4)), - FIVE_OF_CLUBS(Suit.CLUBS, "5클로버", false, List.of(5)), - SIX_OF_CLUBS(Suit.CLUBS, "6클로버", false, List.of(6)), - SEVEN_OF_CLUBS(Suit.CLUBS, "7클로버", false, List.of(7)), - EIGHT_OF_CLUBS(Suit.CLUBS, "8클로버", false, List.of(8)), - NINE_OF_CLUBS(Suit.CLUBS, "9클로버", false, List.of(9)), - TEN_OF_CLUBS(Suit.CLUBS, "10클로버", false, List.of(10)), - JACK_OF_CLUBS(Suit.CLUBS, "J클로버", false, List.of(10)), - QUEEN_OF_CLUBS(Suit.CLUBS, "Q클로버", false, List.of(10)), - KING_OF_CLUBS(Suit.CLUBS, "K클로버", false, List.of(10)), - - ACE_OF_SPADES(Suit.SPADES, "A스페이드", true, List.of(1, 11)), - TWO_OF_SPADES(Suit.SPADES, "2스페이드", false, List.of(2)), - THREE_OF_SPADES(Suit.SPADES, "3스페이드", false, List.of(3)), - FOUR_OF_SPADES(Suit.SPADES, "4스페이드", false, List.of(4)), - FIVE_OF_SPADES(Suit.SPADES, "5스페이드", false, List.of(5)), - SIX_OF_SPADES(Suit.SPADES, "6스페이드", false, List.of(6)), - SEVEN_OF_SPADES(Suit.SPADES, "7스페이드", false, List.of(7)), - EIGHT_OF_SPADES(Suit.SPADES, "8스페이드", false, List.of(8)), - NINE_OF_SPADES(Suit.SPADES, "9스페이드", false, List.of(9)), - TEN_OF_SPADES(Suit.SPADES, "10스페이드", false, List.of(10)), - JACK_OF_SPADES(Suit.SPADES, "J스페이드", false, List.of(10)), - QUEEN_OF_SPADES(Suit.SPADES, "Q스페이드", false, List.of(10)), - KING_OF_SPADES(Suit.SPADES, "K스페이드", false, List.of(10)); + ACE_OF_HEARTS(Suit.HEARTS, Number.ACE), + TWO_OF_HEARTS(Suit.HEARTS, Number.TWO), + THREE_OF_HEARTS(Suit.HEARTS, Number.THREE), + FOUR_OF_HEARTS(Suit.HEARTS, Number.FOUR), + FIVE_OF_HEARTS(Suit.HEARTS, Number.FIVE), + SIX_OF_HEARTS(Suit.HEARTS, Number.SIX), + SEVEN_OF_HEARTS(Suit.HEARTS, Number.SEVEN), + EIGHT_OF_HEARTS(Suit.HEARTS, Number.EIGHT), + NINE_OF_HEARTS(Suit.HEARTS, Number.NINE), + TEN_OF_HEARTS(Suit.HEARTS, Number.TEN), + JACK_OF_HEARTS(Suit.HEARTS, Number.JACK), + QUEEN_OF_HEARTS(Suit.HEARTS, Number.QUEEN), + KING_OF_HEARTS(Suit.HEARTS, Number.KING), + + ACE_OF_DIAMONDS(Suit.DIAMONDS, Number.ACE), + TWO_OF_DIAMONDS(Suit.DIAMONDS, Number.TWO), + THREE_OF_DIAMONDS(Suit.DIAMONDS, Number.THREE), + FOUR_OF_DIAMONDS(Suit.DIAMONDS, Number.FOUR), + FIVE_OF_DIAMONDS(Suit.DIAMONDS, Number.FIVE), + SIX_OF_DIAMONDS(Suit.DIAMONDS, Number.SIX), + SEVEN_OF_DIAMONDS(Suit.DIAMONDS, Number.SEVEN), + EIGHT_OF_DIAMONDS(Suit.DIAMONDS, Number.EIGHT), + NINE_OF_DIAMONDS(Suit.DIAMONDS, Number.NINE), + TEN_OF_DIAMONDS(Suit.DIAMONDS, Number.TEN), + JACK_OF_DIAMONDS(Suit.DIAMONDS, Number.JACK), + QUEEN_OF_DIAMONDS(Suit.DIAMONDS, Number.QUEEN), + KING_OF_DIAMONDS(Suit.DIAMONDS, Number.KING), + + ACE_OF_CLUBS(Suit.CLUBS, Number.ACE), + TWO_OF_CLUBS(Suit.CLUBS, Number.TWO), + THREE_OF_CLUBS(Suit.CLUBS, Number.THREE), + FOUR_OF_CLUBS(Suit.CLUBS, Number.FOUR), + FIVE_OF_CLUBS(Suit.CLUBS, Number.FIVE), + SIX_OF_CLUBS(Suit.CLUBS, Number.SIX), + SEVEN_OF_CLUBS(Suit.CLUBS, Number.SEVEN), + EIGHT_OF_CLUBS(Suit.CLUBS, Number.EIGHT), + NINE_OF_CLUBS(Suit.CLUBS, Number.NINE), + TEN_OF_CLUBS(Suit.CLUBS, Number.TEN), + JACK_OF_CLUBS(Suit.CLUBS, Number.JACK), + QUEEN_OF_CLUBS(Suit.CLUBS, Number.QUEEN), + KING_OF_CLUBS(Suit.CLUBS, Number.KING), + + ACE_OF_SPADES(Suit.SPADES, Number.ACE), + TWO_OF_SPADES(Suit.SPADES, Number.TWO), + THREE_OF_SPADES(Suit.SPADES, Number.THREE), + FOUR_OF_SPADES(Suit.SPADES, Number.FOUR), + FIVE_OF_SPADES(Suit.SPADES, Number.FIVE), + SIX_OF_SPADES(Suit.SPADES, Number.SIX), + SEVEN_OF_SPADES(Suit.SPADES, Number.SEVEN), + EIGHT_OF_SPADES(Suit.SPADES, Number.EIGHT), + NINE_OF_SPADES(Suit.SPADES, Number.NINE), + TEN_OF_SPADES(Suit.SPADES, Number.TEN), + JACK_OF_SPADES(Suit.SPADES, Number.JACK), + QUEEN_OF_SPADES(Suit.SPADES, Number.QUEEN), + KING_OF_SPADES(Suit.SPADES, Number.KING); private final Suit suit; - private final String name; - private final boolean isAce; - private final List score; + private final Number number; - Card(Suit suit, String name, boolean isAce, List values) { + Card(Suit suit, Number number) { this.suit = suit; - this.name = name; - this.isAce = isAce; - this.score = values; + this.number = number; + } + + public String getCardName() { + return this.number.name + this.suit.name; + } + + public boolean isAce() { + return this.number == Number.ACE; + } + + public List getScore() { + return this.number.score; } public enum Suit { - HEARTS(true), DIAMONDS(true), CLUBS(false), SPADES(false); + HEARTS("하트", true), + DIAMONDS("다이아", true), + CLUBS("클로버", false), + SPADES("스페이드", false); + private final String name; private final boolean isRed; - Suit(boolean isRed) { + Suit(String name, boolean isRed) { + this.name = name; this.isRed = isRed; } - public boolean isRed() { - return isRed; + public String getName() { + return name; } } - public String getCardName() { - return this.name; - } - - public boolean isAce() { - return this.isAce; - } - public List getScore() { - return score; + public enum Number { + ACE("A", List.of(1, 11)), + TWO("2", List.of(2)), + THREE("3", List.of(3)), + FOUR("4", List.of(4)), + FIVE("5", List.of(5)), + SIX("6", List.of(6)), + SEVEN("7", List.of(7)), + EIGHT("8", List.of(8)), + NINE("9", List.of(9)), + TEN("10", List.of(10)), + JACK("J", List.of(10)), + QUEEN("Q", List.of(10)), + KING("K", List.of(10)); + + private final String name; + private final List score; + + Number(String name, List score) { + this.name = name; + this.score = score; + } } - } From 5bcf8f8f7b3217b5428ff5066b55775fc8f82e1e Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Mon, 9 Mar 2026 17:06:03 +0900 Subject: [PATCH 25/50] =?UTF-8?q?feat(Controller):=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=EB=90=9C=20=EC=9D=B4=EB=A6=84=20=EC=9E=85=EB=A0=A5=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EC=9E=AC=EC=B2=98=EB=A6=AC=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 --- .../blackjack/controler/BlackJackController.java | 14 +++++++++++++- src/main/java/team/blackjack/view/OutputView.java | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/team/blackjack/controler/BlackJackController.java b/src/main/java/team/blackjack/controler/BlackJackController.java index 8ccce61302d..a502948b41f 100644 --- a/src/main/java/team/blackjack/controler/BlackJackController.java +++ b/src/main/java/team/blackjack/controler/BlackJackController.java @@ -39,7 +39,19 @@ public void run() { private List readPlayerNames(){ OutputView.printPlayerNameRequest(); - return InputView.readPlayerNames(); + List playerNames = InputView.readPlayerNames(); + + // 중복된 이름은 허용되지 않음 + while (hasDuplicatedName(playerNames)) { + OutputView.printDuplicatedNameMessage(); + OutputView.printPlayerNameRequest(); + playerNames = InputView.readPlayerNames(); + } + return playerNames; + } + + private boolean hasDuplicatedName(List playerNames) { + return playerNames.size() != playerNames.stream().distinct().count(); } private void readHitDecision(List playerNames) { diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java index 8c707222c0f..5b72e3472fe 100644 --- a/src/main/java/team/blackjack/view/OutputView.java +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -77,4 +77,8 @@ public static void printBustMessage() { public static void printWrongInputMessage() { println("잘못된 입력입니다. y, n 중 하나를 입력해주세요."); } + + public static void printDuplicatedNameMessage() { + println("중복된 이름은 허용되지 않습니다. 다시 입력해주세요."); + } } From 34f1527fc234dcae38d24d92747bede784791d78 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Mon, 9 Mar 2026 17:22:59 +0900 Subject: [PATCH 26/50] =?UTF-8?q?refactor(GameResult):=20GameResult=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=AA=85=20MatchResult=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controler/BlackJackController.java | 10 +++++----- .../blackjack/service/BlackJackService.java | 18 +++++++++--------- .../dto/{GameResult.java => MatchResult.java} | 2 +- .../java/team/blackjack/view/OutputView.java | 8 ++++---- .../service/BlackJackServiceTest.java | 8 ++++---- 5 files changed, 23 insertions(+), 23 deletions(-) rename src/main/java/team/blackjack/service/dto/{GameResult.java => MatchResult.java} (97%) diff --git a/src/main/java/team/blackjack/controler/BlackJackController.java b/src/main/java/team/blackjack/controler/BlackJackController.java index a502948b41f..6619226724d 100644 --- a/src/main/java/team/blackjack/controler/BlackJackController.java +++ b/src/main/java/team/blackjack/controler/BlackJackController.java @@ -2,7 +2,7 @@ import java.util.List; import team.blackjack.service.dto.DrawResult; -import team.blackjack.service.dto.GameResult; +import team.blackjack.service.dto.MatchResult; import team.blackjack.service.dto.ScoreResult; import team.blackjack.service.BlackJackService; import team.blackjack.view.InputView; @@ -23,7 +23,7 @@ public void run() { DrawResult drawResult = blackJackService.getDrawResult(); OutputView.printDrawResult(drawResult); - readHitDecision(blackJackService.getPlayerNames()); + readHitDecision(blackJackService.getAllPlayerNames()); while (blackJackService.shouldDealerHit()) { OutputView.printDealerHitMessage(); @@ -33,8 +33,8 @@ public void run() { ScoreResult scoreResult = blackJackService.calculateAllParticipantScore(); OutputView.printParticipantScoreResult(scoreResult); - GameResult gameResult = blackJackService.getGameResult(); - OutputView.printGameResult(gameResult); + MatchResult matchResult = blackJackService.getGameResult(); + OutputView.printGameResult(matchResult); } private List readPlayerNames(){ @@ -75,7 +75,7 @@ private void processHit(String playerName) { } blackJackService.hitPlayer(playerName); - OutputView.printPlayerCards(playerName, blackJackService.getPlayerCardNamesByName(playerName)); + OutputView.printPlayerCards(playerName, blackJackService.findPlayerCardNamesByName(playerName)); } OutputView.printBustMessage(); diff --git a/src/main/java/team/blackjack/service/BlackJackService.java b/src/main/java/team/blackjack/service/BlackJackService.java index 75eae2a4bf4..bb2f25e3153 100644 --- a/src/main/java/team/blackjack/service/BlackJackService.java +++ b/src/main/java/team/blackjack/service/BlackJackService.java @@ -7,9 +7,9 @@ import team.blackjack.domain.Card; import team.blackjack.domain.Players; import team.blackjack.service.dto.DrawResult; -import team.blackjack.service.dto.GameResult; -import team.blackjack.service.dto.GameResult.DealerResult; -import team.blackjack.service.dto.GameResult.PlayerResult; +import team.blackjack.service.dto.MatchResult; +import team.blackjack.service.dto.MatchResult.DealerResult; +import team.blackjack.service.dto.MatchResult.PlayerResult; import team.blackjack.service.dto.ScoreResult; import team.blackjack.domain.BlackjackGame; import team.blackjack.domain.Dealer; @@ -67,7 +67,7 @@ public void hitDealer() { } public ScoreResult calculateAllParticipantScore() { - final List playerNames = getPlayerNames(); + final List playerNames = getAllPlayerNames(); final Map> playerCards = getPlayers().getCardsByPlayer(); final Map playerScores = getPlayers().getPlayerScoresByPlayer(); @@ -82,27 +82,27 @@ public ScoreResult calculateAllParticipantScore() { ); } - public List getPlayerNames() { + public List getAllPlayerNames() { return getPlayers().getPlayerNames(); } - public List getPlayerCardNamesByName(String name) { + public List findPlayerCardNamesByName(String name) { return getPlayers().getPlayerByName(name).getCardInAllHand(); } public DrawResult getDrawResult() { - final List playerNames = getPlayerNames(); + final List playerNames = getAllPlayerNames(); final List cards = blackjackGame.getDealer().getHand().getCards(); final Map> playerCards = getPlayers().getCardsByPlayer(); return new DrawResult(playerNames, cards.getFirst().getCardName(), playerCards); } - public GameResult getGameResult() { + public MatchResult getGameResult() { final Map playerResults = calculatePlayersResultMap(blackjackGame.getDealer().getScore()); final DealerResult dealerResult = DealerResult.from(playerResults.values()); - return new GameResult(dealerResult, playerResults); + return new MatchResult(dealerResult, playerResults); } private Map calculatePlayersResultMap(int dealerScore) { diff --git a/src/main/java/team/blackjack/service/dto/GameResult.java b/src/main/java/team/blackjack/service/dto/MatchResult.java similarity index 97% rename from src/main/java/team/blackjack/service/dto/GameResult.java rename to src/main/java/team/blackjack/service/dto/MatchResult.java index f514828eb53..e57da347958 100644 --- a/src/main/java/team/blackjack/service/dto/GameResult.java +++ b/src/main/java/team/blackjack/service/dto/MatchResult.java @@ -6,7 +6,7 @@ import java.util.Map; import team.blackjack.domain.Result; -public record GameResult( +public record MatchResult( DealerResult dealerResult, Map playerResultMap ) { diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java index 5b72e3472fe..70ef3749fba 100644 --- a/src/main/java/team/blackjack/view/OutputView.java +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -4,9 +4,9 @@ import java.util.Map; import java.util.Map.Entry; import team.blackjack.service.dto.DrawResult; -import team.blackjack.service.dto.GameResult; -import team.blackjack.service.dto.GameResult.DealerResult; -import team.blackjack.service.dto.GameResult.PlayerResult; +import team.blackjack.service.dto.MatchResult; +import team.blackjack.service.dto.MatchResult.DealerResult; +import team.blackjack.service.dto.MatchResult.PlayerResult; import team.blackjack.service.dto.ScoreResult; import team.blackjack.domain.Result; @@ -52,7 +52,7 @@ public static void printParticipantScoreResult(ScoreResult scoreResult) { } } - public static void printGameResult(GameResult result) { + public static void printGameResult(MatchResult result) { final DealerResult dealerResult = result.dealerResult(); final Map playeredResultMap = result.playerResultMap(); diff --git a/src/test/java/team/blackjack/service/BlackJackServiceTest.java b/src/test/java/team/blackjack/service/BlackJackServiceTest.java index 3e8b699ec45..77e3db19a74 100644 --- a/src/test/java/team/blackjack/service/BlackJackServiceTest.java +++ b/src/test/java/team/blackjack/service/BlackJackServiceTest.java @@ -21,7 +21,7 @@ void setUp() { void 플레이어_이름_목록으로_게임을_초기화한다() { blackJackService.initGame(List.of("pobi", "jason")); - List playerNames = blackJackService.getPlayerNames(); + List playerNames = blackJackService.getAllPlayerNames(); assertThat(playerNames).hasSize(2); assertThat(playerNames) .containsExactly("pobi", "jason"); @@ -33,7 +33,7 @@ void setUp() { blackJackService.initGame(List.of(pobi)); blackJackService.drawInitialCards(); - List playerCards = blackJackService.getPlayerCardNamesByName(pobi); + List playerCards = blackJackService.findPlayerCardNamesByName(pobi); assertThat(playerCards.getFirst()).hasSize(2); ScoreResult scoreResult = blackJackService.calculateAllParticipantScore(); @@ -47,12 +47,12 @@ void setUp() { blackJackService.initGame(List.of(playerName)); blackJackService.drawInitialCards(); - List pobiCardNames = blackJackService.getPlayerCardNamesByName(playerName); + List pobiCardNames = blackJackService.findPlayerCardNamesByName(playerName); int sizeBefore = pobiCardNames.size(); blackJackService.hitPlayer(playerName); - pobiCardNames = blackJackService.getPlayerCardNamesByName(playerName); + pobiCardNames = blackJackService.findPlayerCardNamesByName(playerName); int sizeAfter = pobiCardNames.size(); assertThat(sizeAfter).isEqualTo(sizeBefore + 1); } From 2589ab0a7e15a4ea512f5b8fa02386e1348fe06a Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Mon, 9 Mar 2026 17:35:16 +0900 Subject: [PATCH 27/50] =?UTF-8?q?refactor(DefaultBlackjackRuleTest):=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/rule/DefaultBlackjackRuleTest.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java index b1ffe2c02d5..d7b61f87866 100644 --- a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java +++ b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java @@ -7,13 +7,11 @@ import team.blackjack.domain.Result; class DefaultBlackjackRuleTest { - private static final DefaultBlackjackRule defaultBlackjackRule = new DefaultBlackjackRule(); - @Test void 각_핸드의_점수는_21점을_초과하는_경우_버스트이다() { int handScore = 22; - boolean isBust = defaultBlackjackRule.isBust(handScore); + boolean isBust = DefaultBlackjackRule.isBust(handScore); Assertions.assertEquals(true, isBust); } @@ -22,7 +20,7 @@ class DefaultBlackjackRuleTest { void 각_핸드의_점수는_21점을_초과하지_않는_경우_버스트가_아니다() { int handScore = 21; - boolean isBust = defaultBlackjackRule.isBust(handScore); + boolean isBust = DefaultBlackjackRule.isBust(handScore); Assertions.assertEquals(false, isBust); } @@ -32,7 +30,7 @@ class DefaultBlackjackRuleTest { int handScore = 21; int cardCount = 2; - boolean isBlackjack = defaultBlackjackRule.isBlackjack(handScore, cardCount); + boolean isBlackjack = DefaultBlackjackRule.isBlackjack(handScore, cardCount); Assertions.assertEquals(true, isBlackjack); } @@ -42,7 +40,7 @@ class DefaultBlackjackRuleTest { int handScore = 21; int cardCount = 3; - boolean isBlackjack = defaultBlackjackRule.isBlackjack(handScore, cardCount); + boolean isBlackjack = DefaultBlackjackRule.isBlackjack(handScore, cardCount); Assertions.assertEquals(false, isBlackjack); } @@ -52,7 +50,7 @@ class DefaultBlackjackRuleTest { int handScore = 20; int cardCount = 2; - boolean isBlackjack = defaultBlackjackRule.isBlackjack(handScore, cardCount); + boolean isBlackjack = DefaultBlackjackRule.isBlackjack(handScore, cardCount); Assertions.assertEquals(false, isBlackjack); } @@ -60,7 +58,7 @@ class DefaultBlackjackRuleTest { @Test void 딜러의_점수가_17점_미만인_경우_딜러는_카드를_더_받아야한다() { int dealerScore = 16; - boolean isDealerMustDraw = defaultBlackjackRule.isDealerMustDraw(dealerScore); + boolean isDealerMustDraw = DefaultBlackjackRule.isDealerMustDraw(dealerScore); Assertions.assertEquals(true, isDealerMustDraw); } @@ -68,7 +66,7 @@ class DefaultBlackjackRuleTest { @Test void 딜러의_점수가_17점_이상인_경우_딜러는_카드를_더_받지_않아야한다() { int dealerScore = 17; - boolean isDealerMustDraw = defaultBlackjackRule.isDealerMustDraw(dealerScore); + boolean isDealerMustDraw = DefaultBlackjackRule.isDealerMustDraw(dealerScore); Assertions.assertEquals(false, isDealerMustDraw); } From e9072051a011355dafdb118609451ab280993d33 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Mon, 9 Mar 2026 17:47:45 +0900 Subject: [PATCH 28/50] =?UTF-8?q?refactor(DefaultBlackjackRuleTest):=20=20?= =?UTF-8?q?@ParameterizedTest=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B0=84=EC=86=8C?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/rule/DefaultBlackjackRuleTest.java | 128 ++++++------------ 1 file changed, 45 insertions(+), 83 deletions(-) diff --git a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java index d7b61f87866..39929f3476c 100644 --- a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java +++ b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java @@ -1,74 +1,66 @@ package team.blackjack.domain.rule; +import static org.junit.jupiter.api.Assertions.assertEquals; + import java.util.List; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import team.blackjack.domain.Card; import team.blackjack.domain.Result; class DefaultBlackjackRuleTest { - @Test - void 각_핸드의_점수는_21점을_초과하는_경우_버스트이다() { - int handScore = 22; - - boolean isBust = DefaultBlackjackRule.isBust(handScore); - - Assertions.assertEquals(true, isBust); - } - @Test - void 각_핸드의_점수는_21점을_초과하지_않는_경우_버스트가_아니다() { - int handScore = 21; + @ParameterizedTest + @CsvSource({ + "22, true", + "21, false" + }) + void 버스트_판정(int handScore, boolean expected) { boolean isBust = DefaultBlackjackRule.isBust(handScore); - - Assertions.assertEquals(false, isBust); + assertEquals(expected, isBust); } - @Test - void 각_핸드의_카드_개수가_2개이고_점수가_21점인_경우_블랙잭이다() { - int handScore = 21; - int cardCount = 2; - + @ParameterizedTest + @CsvSource({ + "21, 2, true", + "21, 3, false", + "20, 2, false" + }) + void 블랙잭_판정(int handScore, int cardCount, boolean expected) { boolean isBlackjack = DefaultBlackjackRule.isBlackjack(handScore, cardCount); - - Assertions.assertEquals(true, isBlackjack); - } - - @Test - void 각_핸드의_카드_개수가_3개이상이고_점수가_21점인_경우_블랙잭이_아니다() { - int handScore = 21; - int cardCount = 3; - - boolean isBlackjack = DefaultBlackjackRule.isBlackjack(handScore, cardCount); - - Assertions.assertEquals(false, isBlackjack); - } - - @Test - void 각_핸드의_점수가_21점이_아닌_경우_블랙잭이_아니다() { - int handScore = 20; - int cardCount = 2; - - boolean isBlackjack = DefaultBlackjackRule.isBlackjack(handScore, cardCount); - - Assertions.assertEquals(false, isBlackjack); + assertEquals(expected, isBlackjack); } - @Test - void 딜러의_점수가_17점_미만인_경우_딜러는_카드를_더_받아야한다() { - int dealerScore = 16; + @ParameterizedTest + @CsvSource({ + "16, true", + "17, false" + }) + void 딜러_카드_추가_여부(int dealerScore, boolean expected) { boolean isDealerMustDraw = DefaultBlackjackRule.isDealerMustDraw(dealerScore); - - Assertions.assertEquals(true, isDealerMustDraw); + assertEquals(expected, isDealerMustDraw); } - @Test - void 딜러의_점수가_17점_이상인_경우_딜러는_카드를_더_받지_않아야한다() { - int dealerScore = 17; - boolean isDealerMustDraw = DefaultBlackjackRule.isDealerMustDraw(dealerScore); + @ParameterizedTest + @CsvSource({ + "20,18,WIN", + "18,20,LOSE", + "17,17,DRAW", + "22,20,LOSE" + }) + void 승패_판정(int myScore, int opponentScore, Result expected) { + assertEquals(expected, DefaultBlackjackRule.judgeResult(myScore, opponentScore)); + } - Assertions.assertEquals(false, isDealerMustDraw); + @ParameterizedTest + @CsvSource({ + "10, true", + "11, false" + }) + void Ace를_11로_사용_가능한지(int score, boolean expected) { + assertEquals(expected, DefaultBlackjackRule.canUseAceAsEleven(score)); } @@ -78,7 +70,7 @@ class DefaultBlackjackRuleTest { int score = DefaultBlackjackRule.calculateBestScore(cards); - Assertions.assertEquals(18, score); + assertEquals(18, score); } @Test @@ -86,36 +78,6 @@ class DefaultBlackjackRuleTest { List cards = List.of(Card.FIVE_OF_CLUBS, Card.FIVE_OF_DIAMONDS, Card.ACE_OF_SPADES); int score = DefaultBlackjackRule.calculateBestScore(cards); - Assertions.assertEquals(21, score); - } - - @Test - void 내_점수가_더_높으면_WIN() { - Assertions.assertEquals(Result.WIN, DefaultBlackjackRule.judgeResult(20, 18)); - } - - @Test - void 상대_점수가_더_높으면_LOSE() { - Assertions.assertEquals(Result.LOSE, DefaultBlackjackRule.judgeResult(18, 20)); - } - - @Test - void 동점이면_DRAW() { - Assertions.assertEquals(Result.DRAW, DefaultBlackjackRule.judgeResult(17, 17)); - } - - @Test - void 버스트_21초과면_LOSE() { - Assertions.assertEquals(Result.LOSE, DefaultBlackjackRule.judgeResult(22, 20)); - } - - @Test - void 합이_10이하면_Ace는_11() { - Assertions.assertTrue(DefaultBlackjackRule.canUseAceAsEleven(10)); - } - - @Test - void 합이_10초과면_Ace는_11() { - Assertions.assertFalse(DefaultBlackjackRule.canUseAceAsEleven(11)); + assertEquals(21, score); } } From 95e3e59d890533dd6bdbe067cdabd15476e17a11 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Tue, 10 Mar 2026 13:29:49 +0900 Subject: [PATCH 29/50] =?UTF-8?q?refactor(Service,=20Players):=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team/blackjack/domain/Players.java | 1 - src/main/java/team/blackjack/service/BlackJackService.java | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/main/java/team/blackjack/domain/Players.java b/src/main/java/team/blackjack/domain/Players.java index b5f397f0b25..5d531d86533 100644 --- a/src/main/java/team/blackjack/domain/Players.java +++ b/src/main/java/team/blackjack/domain/Players.java @@ -16,7 +16,6 @@ public List getPlayerList() { return List.copyOf(players); } - // 플레이어 카드 초기화 public void initPlayerHands(Deck deck) { for (Player player : players) { player.hit(deck.draw()); diff --git a/src/main/java/team/blackjack/service/BlackJackService.java b/src/main/java/team/blackjack/service/BlackJackService.java index bb2f25e3153..f1651d39d9f 100644 --- a/src/main/java/team/blackjack/service/BlackJackService.java +++ b/src/main/java/team/blackjack/service/BlackJackService.java @@ -33,10 +33,8 @@ public void drawInitialCards() { final Deck deck = blackjackGame.getDeck(); final Dealer dealer = blackjackGame.getDealer(); - // 플레이어 카드 초기화 getPlayers().initPlayerHands(deck); - // 딜러 카드 초기화 dealer.hit(dealer.draw(deck)); dealer.hit(dealer.draw(deck)); } From 33dbf995ffa39a0f9a8ec805d051a1bc5c5b653e Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Tue, 10 Mar 2026 13:35:23 +0900 Subject: [PATCH 30/50] =?UTF-8?q?refactor(InputView):=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team/blackjack/view/InputView.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/team/blackjack/view/InputView.java b/src/main/java/team/blackjack/view/InputView.java index 9fc949f4b8b..4d467c7b626 100644 --- a/src/main/java/team/blackjack/view/InputView.java +++ b/src/main/java/team/blackjack/view/InputView.java @@ -16,10 +16,6 @@ public static List readPlayerNames() { .toList(); } - /** - * 플레이어가 한장의 카드를 더 받을지 여부를 입력받는다. - * @return - */ public static String readHitDecision() { return readInput(); } From 7c155aa59167e33b069f4e2e400ea91e9ec8ec44 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Tue, 10 Mar 2026 13:45:02 +0900 Subject: [PATCH 31/50] =?UTF-8?q?refactor(AppConfig):=20=EC=96=B4=ED=94=8C?= =?UTF-8?q?=EB=A6=AC=EC=BC=80=EC=9D=B4=EC=85=98=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=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/team/blackjack/Application.java | 4 ++-- .../java/team/blackjack/config/AppConfig.java | 21 +++++-------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/main/java/team/blackjack/Application.java b/src/main/java/team/blackjack/Application.java index 522c7a37cca..026f5d1f667 100644 --- a/src/main/java/team/blackjack/Application.java +++ b/src/main/java/team/blackjack/Application.java @@ -6,7 +6,7 @@ public class Application { public static void main(String[] args) { AppConfig appConfig = AppConfig.getInstance(); - BlackJackController blackJackController = appConfig.blackJackController(); - blackJackController.run(); + BlackJackController controller = appConfig.blackJackController(); + controller.run(); } } \ No newline at end of file diff --git a/src/main/java/team/blackjack/config/AppConfig.java b/src/main/java/team/blackjack/config/AppConfig.java index e43fcc3ea81..b737208e5e2 100644 --- a/src/main/java/team/blackjack/config/AppConfig.java +++ b/src/main/java/team/blackjack/config/AppConfig.java @@ -4,28 +4,17 @@ import team.blackjack.service.BlackJackService; public class AppConfig { - private static volatile AppConfig instance; + private static final AppConfig instance = new AppConfig(); + private final BlackJackService blackJackService = new BlackJackService(); + private final BlackJackController blackJackController = new BlackJackController(blackJackService); - private AppConfig() { - } + private AppConfig() {} public static AppConfig getInstance() { - if (instance == null) { - synchronized (AppConfig.class) { - if (instance == null) { - instance = new AppConfig(); - } - } - } - return instance; } - public BlackJackService blackJackService() { - return new BlackJackService(); - } - public BlackJackController blackJackController() { - return new BlackJackController(blackJackService()); + return blackJackController; } } From afc73695e9cdbcf4351e0e3545775f9319ababf1 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Tue, 10 Mar 2026 13:54:02 +0900 Subject: [PATCH 32/50] =?UTF-8?q?refactor(DefaultBlackjackRule):=20?= =?UTF-8?q?=EB=B8=94=EB=9E=99=EC=9E=AD=20=EC=83=81=EC=88=98=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=20=EC=A0=9C=ED=95=9C=EC=9E=90=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/blackjack/domain/rule/DefaultBlackjackRule.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java index 41e3743d1c4..41d6a96b780 100644 --- a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java +++ b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java @@ -7,9 +7,9 @@ import team.blackjack.domain.Result; public class DefaultBlackjackRule { - public static final int BLACKJACK = 21; - public static final int BLACKJACK_CARD_COUNT = 2; - public static final int DEALER_STAND_SCORE = 17; + private static final int BLACKJACK = 21; + private static final int BLACKJACK_CARD_COUNT = 2; + private static final int DEALER_STAND_SCORE = 17; public static boolean isBust(int score) { return score > BLACKJACK; From 8462cd911dd4597dafa48808f6eca786aba82458 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Tue, 10 Mar 2026 13:57:05 +0900 Subject: [PATCH 33/50] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team/blackjack/controler/BlackJackController.java | 1 - .../java/team/blackjack/domain/rule/DefaultBlackjackRule.java | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/main/java/team/blackjack/controler/BlackJackController.java b/src/main/java/team/blackjack/controler/BlackJackController.java index 6619226724d..34aae1135c8 100644 --- a/src/main/java/team/blackjack/controler/BlackJackController.java +++ b/src/main/java/team/blackjack/controler/BlackJackController.java @@ -41,7 +41,6 @@ private List readPlayerNames(){ OutputView.printPlayerNameRequest(); List playerNames = InputView.readPlayerNames(); - // 중복된 이름은 허용되지 않음 while (hasDuplicatedName(playerNames)) { OutputView.printDuplicatedNameMessage(); OutputView.printPlayerNameRequest(); diff --git a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java index 41d6a96b780..75a7552937c 100644 --- a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java +++ b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java @@ -30,7 +30,6 @@ public static boolean canUseAceAsEleven(int currentSum) { return currentSum <= 10; } - // 딜러와 플레이어의 승패를 결정하는 메서드 public static Result judgeResult(int myScore, int targetScore) { if(myScore > BLACKJACK){ return Result.LOSE; @@ -44,9 +43,6 @@ public static Result judgeResult(int myScore, int targetScore) { return Result.DRAW; } - /** - * 모든 카드를 발급한 이후에, 최종 점수 계산시에 사용하는 함수 - */ public static int calculateBestScore(List cards) { if (existAceInCards(cards)) { final Map> result = cards.stream() From 68c89126cf5ec6b79e50ec21995f5e1646a0bd51 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Tue, 10 Mar 2026 14:00:41 +0900 Subject: [PATCH 34/50] =?UTF-8?q?refactor(Card):=20red=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team/blackjack/domain/Card.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/team/blackjack/domain/Card.java b/src/main/java/team/blackjack/domain/Card.java index 81925f4e5f6..934da36cee8 100644 --- a/src/main/java/team/blackjack/domain/Card.java +++ b/src/main/java/team/blackjack/domain/Card.java @@ -87,11 +87,11 @@ public enum Suit { SPADES("스페이드", false); private final String name; - private final boolean isRed; + private final boolean red; - Suit(String name, boolean isRed) { + Suit(String name, boolean red) { this.name = name; - this.isRed = isRed; + this.red = red; } public String getName() { From cd972c99dd7016fd5a8e8fa9a31e59273e139d4e Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Tue, 10 Mar 2026 14:17:31 +0900 Subject: [PATCH 35/50] =?UTF-8?q?refactor(Number):=20Score=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=9E=90=EB=A3=8C=ED=98=95=20Set=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=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/team/blackjack/domain/Card.java | 35 ++++++++++--------- .../domain/rule/DefaultBlackjackRule.java | 11 ++++-- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/main/java/team/blackjack/domain/Card.java b/src/main/java/team/blackjack/domain/Card.java index 934da36cee8..0f1fabbc85a 100644 --- a/src/main/java/team/blackjack/domain/Card.java +++ b/src/main/java/team/blackjack/domain/Card.java @@ -1,6 +1,7 @@ package team.blackjack.domain; import java.util.List; +import java.util.Set; public enum Card { ACE_OF_HEARTS(Suit.HEARTS, Number.ACE), @@ -75,8 +76,8 @@ public boolean isAce() { return this.number == Number.ACE; } - public List getScore() { - return this.number.score; + public Set getScore() { + return Set.copyOf(this.number.score); } public enum Suit { @@ -101,24 +102,24 @@ public String getName() { public enum Number { - ACE("A", List.of(1, 11)), - TWO("2", List.of(2)), - THREE("3", List.of(3)), - FOUR("4", List.of(4)), - FIVE("5", List.of(5)), - SIX("6", List.of(6)), - SEVEN("7", List.of(7)), - EIGHT("8", List.of(8)), - NINE("9", List.of(9)), - TEN("10", List.of(10)), - JACK("J", List.of(10)), - QUEEN("Q", List.of(10)), - KING("K", List.of(10)); + ACE("A", Set.of(1, 11)), + TWO("2", Set.of(2)), + THREE("3", Set.of(3)), + FOUR("4", Set.of(4)), + FIVE("5", Set.of(5)), + SIX("6", Set.of(6)), + SEVEN("7", Set.of(7)), + EIGHT("8", Set.of(8)), + NINE("9", Set.of(9)), + TEN("10", Set.of(10)), + JACK("J", Set.of(10)), + QUEEN("Q", Set.of(10)), + KING("K", Set.of(10)); private final String name; - private final List score; + private final Set score; - Number(String name, List score) { + Number(String name, Set score) { this.name = name; this.score = score; } diff --git a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java index 75a7552937c..cad6f4b388f 100644 --- a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java +++ b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java @@ -67,15 +67,20 @@ private static int calculateBestSumWithAce(List cardsWithoutAces, List new IllegalStateException("ACE 카드의 점수가 존재하지 않습니다.")); } - return card.getScore().getFirst(); + return card.getScore().stream() + .min(Integer::compareTo) + .orElseThrow(() -> new IllegalStateException("ACE 카드의 점수가 존재하지 않습니다.")); } private static int calculateBestSumWithoutAce(List cards) { return cards.stream() - .mapToInt(card -> card.getScore().getFirst()) + .flatMap(card -> card.getScore().stream()) + .mapToInt(Integer::intValue) .sum(); } From d6500d3b284e8acbdf0313873ecefd4f1d241522 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Tue, 10 Mar 2026 14:26:00 +0900 Subject: [PATCH 36/50] =?UTF-8?q?refactor(Deck):=20cards=20Set=20=EC=9E=90?= =?UTF-8?q?=EB=A3=8C=ED=98=95=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team/blackjack/domain/Deck.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/team/blackjack/domain/Deck.java b/src/main/java/team/blackjack/domain/Deck.java index aa54339aafa..3cc63d43ef4 100644 --- a/src/main/java/team/blackjack/domain/Deck.java +++ b/src/main/java/team/blackjack/domain/Deck.java @@ -1,25 +1,31 @@ package team.blackjack.domain; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; public class Deck { - private final List cards; + private final Set cards; public Deck() { this.cards = Arrays.stream(Card.values()) - .collect(Collectors.toList()); - - Collections.shuffle(this.cards); + .collect(Collectors.toSet()); } public Card draw() { - try { - return cards.getFirst(); - } finally { - cards.removeFirst(); + if (cards.isEmpty()) { + throw new IllegalStateException("덱에 카드가 없습니다."); } + + final List cardList = new ArrayList<>(cards); + Collections.shuffle(cardList); + + final Card drawnCard = cardList.getFirst(); + cards.remove(drawnCard); + + return drawnCard; } } From a72b9f38b6554f02009026cf0cfea5d1d1d307f8 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Tue, 10 Mar 2026 14:26:00 +0900 Subject: [PATCH 37/50] =?UTF-8?q?refactor(Deck):=20cards=20Set=20=EC=9E=90?= =?UTF-8?q?=EB=A3=8C=ED=98=95=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20-?= =?UTF-8?q?=20Deck.cards=ED=95=84=EB=93=9C=EB=A5=BC=20Set=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=84=ED=99=98=ED=95=B4=EC=84=9C=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=EC=97=90=20=EB=8C=80=ED=95=9C=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=EB=B0=A9=EC=A7=80=20-=20Deck.draw()=20=EC=97=90=EC=84=9C=20fin?= =?UTF-8?q?ally=20=EB=AC=B8=EC=9D=84=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=8C=80=EC=8B=A0=20=EC=BD=94=EB=93=9C=20=ED=9D=90?= =?UTF-8?q?=EB=A6=84=EC=9C=BC=EB=A1=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team/blackjack/domain/Deck.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/team/blackjack/domain/Deck.java b/src/main/java/team/blackjack/domain/Deck.java index aa54339aafa..3cc63d43ef4 100644 --- a/src/main/java/team/blackjack/domain/Deck.java +++ b/src/main/java/team/blackjack/domain/Deck.java @@ -1,25 +1,31 @@ package team.blackjack.domain; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; public class Deck { - private final List cards; + private final Set cards; public Deck() { this.cards = Arrays.stream(Card.values()) - .collect(Collectors.toList()); - - Collections.shuffle(this.cards); + .collect(Collectors.toSet()); } public Card draw() { - try { - return cards.getFirst(); - } finally { - cards.removeFirst(); + if (cards.isEmpty()) { + throw new IllegalStateException("덱에 카드가 없습니다."); } + + final List cardList = new ArrayList<>(cards); + Collections.shuffle(cardList); + + final Card drawnCard = cardList.getFirst(); + cards.remove(drawnCard); + + return drawnCard; } } From 881c1094f8ae2021d4441d7f7f62c761b68d0fc7 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Tue, 10 Mar 2026 14:47:20 +0900 Subject: [PATCH 38/50] =?UTF-8?q?refactor(MatchResult):=20countBy=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=EB=94=9C=EB=9F=AC=20=EC=8A=B9?= =?UTF-8?q?/=ED=8C=A8/=EB=AC=B4=20=EC=B9=B4=EC=9A=B4=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90=EB=A1=9C=20=EB=B0=9B=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/blackjack/service/BlackJackService.java | 12 +++++++++++- .../java/team/blackjack/service/dto/MatchResult.java | 9 +++------ src/main/java/team/blackjack/view/OutputView.java | 5 +---- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/java/team/blackjack/service/BlackJackService.java b/src/main/java/team/blackjack/service/BlackJackService.java index f1651d39d9f..5814e1700ee 100644 --- a/src/main/java/team/blackjack/service/BlackJackService.java +++ b/src/main/java/team/blackjack/service/BlackJackService.java @@ -6,6 +6,7 @@ import java.util.stream.Collectors; import team.blackjack.domain.Card; import team.blackjack.domain.Players; +import team.blackjack.domain.Result; import team.blackjack.service.dto.DrawResult; import team.blackjack.service.dto.MatchResult; import team.blackjack.service.dto.MatchResult.DealerResult; @@ -99,8 +100,17 @@ public DrawResult getDrawResult() { public MatchResult getGameResult() { final Map playerResults = calculatePlayersResultMap(blackjackGame.getDealer().getScore()); final DealerResult dealerResult = DealerResult.from(playerResults.values()); + final long winCnt = countBy(dealerResult.results(), Result.WIN); + final long loseCnt = countBy(dealerResult.results(), Result.LOSE); + final long drawCnt = countBy(dealerResult.results(), Result.DRAW); - return new MatchResult(dealerResult, playerResults); + return new MatchResult(dealerResult, winCnt, loseCnt, drawCnt, playerResults); + } + + private long countBy(List results, Result target) { + return results.stream() + .filter(result -> result == target) + .count(); } private Map calculatePlayersResultMap(int dealerScore) { diff --git a/src/main/java/team/blackjack/service/dto/MatchResult.java b/src/main/java/team/blackjack/service/dto/MatchResult.java index e57da347958..576c68b45db 100644 --- a/src/main/java/team/blackjack/service/dto/MatchResult.java +++ b/src/main/java/team/blackjack/service/dto/MatchResult.java @@ -8,6 +8,9 @@ public record MatchResult( DealerResult dealerResult, + long winCount, + long loseCount, + long drawCount, Map playerResultMap ) { @@ -23,12 +26,6 @@ public static DealerResult from(Collection playerResults) { return new DealerResult(dealerResults); } - - public long countBy(Result target) { - return results.stream() - .filter(result -> result == target) - .count(); - } } public record PlayerResult( diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java index 70ef3749fba..a68f76b9d00 100644 --- a/src/main/java/team/blackjack/view/OutputView.java +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -58,10 +58,7 @@ public static void printGameResult(MatchResult result) { println(""); println("## 최종 승패:"); - println("딜러: %d승 %d패 %d무".formatted( - dealerResult.countBy(Result.WIN), - dealerResult.countBy(Result.LOSE), - dealerResult.countBy(Result.DRAW)) + println("딜러: %d승 %d패 %d무".formatted(result.winCount(), result.loseCount(), result.drawCount()) ); playeredResultMap.entrySet().stream() From 2ed59ccb80df1d8de3f40b324e2d59066adc83d8 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Wed, 11 Mar 2026 15:21:02 +0900 Subject: [PATCH 39/50] =?UTF-8?q?refactor(Deck):=20draw=20=EC=97=AD?= =?UTF-8?q?=ED=95=A0=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team/blackjack/domain/Deck.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/team/blackjack/domain/Deck.java b/src/main/java/team/blackjack/domain/Deck.java index 3cc63d43ef4..1070c230f8b 100644 --- a/src/main/java/team/blackjack/domain/Deck.java +++ b/src/main/java/team/blackjack/domain/Deck.java @@ -20,12 +20,15 @@ public Card draw() { throw new IllegalStateException("덱에 카드가 없습니다."); } - final List cardList = new ArrayList<>(cards); - Collections.shuffle(cardList); - - final Card drawnCard = cardList.getFirst(); + final Card drawnCard = getShuffledCards().getFirst(); cards.remove(drawnCard); return drawnCard; } + + private List getShuffledCards() { + final List cardList = new ArrayList<>(cards); + Collections.shuffle(cardList); + return cardList; + } } From ccbf2e7b63f624583341515fbbaebffe41cba959 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Wed, 11 Mar 2026 15:30:18 +0900 Subject: [PATCH 40/50] =?UTF-8?q?refactor(BlackjackGame,=20BlackJackServic?= =?UTF-8?q?e):=20=EC=B1=85=EC=9E=84=20=EC=9D=B4=EB=8F=99=20=EB=B0=8F=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team/blackjack/config/AppConfig.java | 2 +- .../team/blackjack/domain/BlackjackGame.java | 64 +++++++++-- .../java/team/blackjack/domain/Player.java | 8 +- .../java/team/blackjack/domain/Players.java | 4 +- .../blackjack/service/BlackJackService.java | 104 ++++++++---------- .../blackjack/service/dto/MatchResult.java | 28 +---- .../java/team/blackjack/view/OutputView.java | 12 +- 7 files changed, 119 insertions(+), 103 deletions(-) diff --git a/src/main/java/team/blackjack/config/AppConfig.java b/src/main/java/team/blackjack/config/AppConfig.java index b737208e5e2..1d26fa6546c 100644 --- a/src/main/java/team/blackjack/config/AppConfig.java +++ b/src/main/java/team/blackjack/config/AppConfig.java @@ -1,6 +1,6 @@ package team.blackjack.config; -import team.blackjack.controler.BlackJackController; +import team.blackjack.control.BlackJackController; import team.blackjack.service.BlackJackService; public class AppConfig { diff --git a/src/main/java/team/blackjack/domain/BlackjackGame.java b/src/main/java/team/blackjack/domain/BlackjackGame.java index cc4cf6b809d..4a421d92cb0 100644 --- a/src/main/java/team/blackjack/domain/BlackjackGame.java +++ b/src/main/java/team/blackjack/domain/BlackjackGame.java @@ -1,8 +1,11 @@ package team.blackjack.domain; -import java.util.HashMap; + +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import team.blackjack.domain.rule.DefaultBlackjackRule; public class BlackjackGame { private final Dealer dealer; @@ -15,15 +18,62 @@ public BlackjackGame(Dealer dealer, Players players) { this.deck = new Deck(); } - public Dealer getDealer() { - return dealer; + public void drawInitialCards() { + this.players.initPlayerHands(deck); + + this.dealer.hit(dealer.draw(deck)); + this.dealer.hit(dealer.draw(deck)); + } + + public Map calculatePlayersResultMap() { + return players.getPlayerList().stream() + .collect(Collectors.toMap( + Player::getName, + player -> DefaultBlackjackRule.judgeResult(player.getScore(), dealer.getScore()), + (existing, replacement) -> existing, + LinkedHashMap::new + )); + } + + public boolean shouldPlayerHit(String name) { + return this.players.getPlayerByName(name).shouldPlayerHit(); + } + + public void hitPlayer(String name) { + this.players.getPlayerByName(name) + .hit(deck.draw()); + } + + public boolean shouldDealerHit() { + final int score = this.dealer.getScore(); + return DefaultBlackjackRule.isDealerMustDraw(score); + } + + public void hitDealer() { + this.dealer.hit(deck.draw()); + } + + public int getDealerScore() { + return this.dealer.getScore(); + } + + public Map getAllPlayerScore() { + return this.players.getPlayerScoresByPlayer(); + } + + public List getDealerCards() { + return this.dealer.getHand().getCards(); + } + + public List getPlayerCardsByName(String name) { + return this.players.getPlayerByName(name).getCardInAllHand(); } - public Players getPlayers() { - return players; + public Map> getAllPlayerCards() { + return this.players.getCardsByPlayer(); } - public Deck getDeck() { - return deck; + public List getAllPlayerNames() { + return this.players.getPlayerNames(); } } diff --git a/src/main/java/team/blackjack/domain/Player.java b/src/main/java/team/blackjack/domain/Player.java index 256981cded1..9379c39eced 100644 --- a/src/main/java/team/blackjack/domain/Player.java +++ b/src/main/java/team/blackjack/domain/Player.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import team.blackjack.domain.rule.DefaultBlackjackRule; public class Player { private final String name; @@ -38,11 +39,14 @@ private List initHands(){ return hands; } - public List getCardInAllHand() { + public List getCardInAllHand() { return hands.stream() .map(Hand::getCards) .flatMap(List::stream) - .map(Card::getCardName) .toList(); } + + public boolean shouldPlayerHit() { + return !DefaultBlackjackRule.isBust(getScore()); + } } diff --git a/src/main/java/team/blackjack/domain/Players.java b/src/main/java/team/blackjack/domain/Players.java index 5d531d86533..56c98d01f46 100644 --- a/src/main/java/team/blackjack/domain/Players.java +++ b/src/main/java/team/blackjack/domain/Players.java @@ -23,8 +23,8 @@ public void initPlayerHands(Deck deck) { } } - public Map> getCardsByPlayer(){ - final HashMap> result = new HashMap<>(); + public Map> getCardsByPlayer(){ + final HashMap> result = new HashMap<>(); for (Player player : players) { result.put(player.getName(), player.getCardInAllHand()); } diff --git a/src/main/java/team/blackjack/service/BlackJackService.java b/src/main/java/team/blackjack/service/BlackJackService.java index 5814e1700ee..aec49665bf1 100644 --- a/src/main/java/team/blackjack/service/BlackJackService.java +++ b/src/main/java/team/blackjack/service/BlackJackService.java @@ -1,22 +1,18 @@ package team.blackjack.service; -import java.util.LinkedHashMap; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import team.blackjack.domain.BlackjackGame; import team.blackjack.domain.Card; +import team.blackjack.domain.Dealer; +import team.blackjack.domain.Player; import team.blackjack.domain.Players; import team.blackjack.domain.Result; import team.blackjack.service.dto.DrawResult; import team.blackjack.service.dto.MatchResult; -import team.blackjack.service.dto.MatchResult.DealerResult; -import team.blackjack.service.dto.MatchResult.PlayerResult; import team.blackjack.service.dto.ScoreResult; -import team.blackjack.domain.BlackjackGame; -import team.blackjack.domain.Dealer; -import team.blackjack.domain.Deck; -import team.blackjack.domain.Player; -import team.blackjack.domain.rule.DefaultBlackjackRule; public class BlackJackService { private BlackjackGame blackjackGame; @@ -31,50 +27,45 @@ public void initGame(List playerNames) { } public void drawInitialCards() { - final Deck deck = blackjackGame.getDeck(); - final Dealer dealer = blackjackGame.getDealer(); - - getPlayers().initPlayerHands(deck); - - dealer.hit(dealer.draw(deck)); - dealer.hit(dealer.draw(deck)); + blackjackGame.drawInitialCards(); } public boolean shouldPlayerHit(String name) { - final Player player = getPlayers().getPlayerByName(name); - - return !DefaultBlackjackRule.isBust(player.getScore()); + return blackjackGame.shouldPlayerHit(name); } public void hitPlayer(String name) { - getPlayers() - .getPlayerByName(name) - .hit(blackjackGame.getDeck().draw()); + blackjackGame.hitPlayer(name); } - public boolean shouldDealerHit() { - final int score = blackjackGame.getDealer().getScore(); - - return DefaultBlackjackRule.isDealerMustDraw(score); + return blackjackGame.shouldDealerHit(); } public void hitDealer() { - final Dealer dealer = blackjackGame.getDealer(); - - dealer.hit(blackjackGame.getDeck().draw()); + blackjackGame.hitDealer(); } public ScoreResult calculateAllParticipantScore() { final List playerNames = getAllPlayerNames(); - final Map> playerCards = getPlayers().getCardsByPlayer(); - final Map playerScores = getPlayers().getPlayerScoresByPlayer(); + final Map playerScores = this.blackjackGame.getAllPlayerScore(); + final Map> playerCards = this.blackjackGame.getAllPlayerCards() + .entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue().stream() + .map(Card::getCardName) + .toList() + )); - final Dealer dealer = blackjackGame.getDealer(); + final int dealerScore = this.blackjackGame.getDealerScore(); + final List dealerCardNames = this.blackjackGame.getDealerCards().stream() + .map(Card::getCardName) + .toList(); return new ScoreResult( - dealer.getHand().getCardNames(), - dealer.getScore(), + dealerCardNames, + dealerScore, playerNames, playerCards, playerScores @@ -82,49 +73,44 @@ public ScoreResult calculateAllParticipantScore() { } public List getAllPlayerNames() { - return getPlayers().getPlayerNames(); + return this.blackjackGame.getAllPlayerNames(); } public List findPlayerCardNamesByName(String name) { - return getPlayers().getPlayerByName(name).getCardInAllHand(); + return this.blackjackGame.getPlayerCardsByName(name).stream() + .map(Card::getCardName) + .toList(); } public DrawResult getDrawResult() { final List playerNames = getAllPlayerNames(); - final List cards = blackjackGame.getDealer().getHand().getCards(); - final Map> playerCards = getPlayers().getCardsByPlayer(); + final List cards = this.blackjackGame.getDealerCards(); + final Map> playerCards = this.blackjackGame.getAllPlayerCards() + .entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue().stream() + .map(Card::getCardName) + .toList() + )); return new DrawResult(playerNames, cards.getFirst().getCardName(), playerCards); } public MatchResult getGameResult() { - final Map playerResults = calculatePlayersResultMap(blackjackGame.getDealer().getScore()); - final DealerResult dealerResult = DealerResult.from(playerResults.values()); - final long winCnt = countBy(dealerResult.results(), Result.WIN); - final long loseCnt = countBy(dealerResult.results(), Result.LOSE); - final long drawCnt = countBy(dealerResult.results(), Result.DRAW); + final Map playerResults = blackjackGame.calculatePlayersResultMap(); + final Collection playerResultList = playerResults.values(); + final long winCnt = countBy(playerResultList, Result.WIN); + final long loseCnt = countBy(playerResultList, Result.LOSE); + final long drawCnt = countBy(playerResultList, Result.DRAW); - return new MatchResult(dealerResult, winCnt, loseCnt, drawCnt, playerResults); + return new MatchResult(winCnt, loseCnt, drawCnt, playerResults); } - private long countBy(List results, Result target) { + private long countBy(Collection results, Result target) { return results.stream() + .map(Result::reverse) .filter(result -> result == target) .count(); } - - private Map calculatePlayersResultMap(int dealerScore) { - return getPlayers().getPlayerList().stream() - .collect(Collectors.toMap( - Player::getName, - player -> new PlayerResult(DefaultBlackjackRule.judgeResult(player.getScore(), dealerScore)), - (existing, replacement) -> existing, - LinkedHashMap::new - )); - } - - - private Players getPlayers() { - return this.blackjackGame.getPlayers(); - } } diff --git a/src/main/java/team/blackjack/service/dto/MatchResult.java b/src/main/java/team/blackjack/service/dto/MatchResult.java index 576c68b45db..f9b7e729659 100644 --- a/src/main/java/team/blackjack/service/dto/MatchResult.java +++ b/src/main/java/team/blackjack/service/dto/MatchResult.java @@ -7,29 +7,9 @@ import team.blackjack.domain.Result; public record MatchResult( - DealerResult dealerResult, - long winCount, - long loseCount, - long drawCount, - Map playerResultMap + long dealerWinCount, + long dealerLoseCount, + long dealerDrawCount, + Map playerResultMap ) { - - public record DealerResult( - List results - ){ - public static DealerResult from(Collection playerResults) { - final List dealerResults = new ArrayList<>(); - - for (PlayerResult playerResult : playerResults) { - dealerResults.add(playerResult.result().reverse()); - } - - return new DealerResult(dealerResults); - } - } - - public record PlayerResult( - Result result - ){ - } } diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java index a68f76b9d00..1f20f813beb 100644 --- a/src/main/java/team/blackjack/view/OutputView.java +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -5,8 +5,6 @@ import java.util.Map.Entry; import team.blackjack.service.dto.DrawResult; import team.blackjack.service.dto.MatchResult; -import team.blackjack.service.dto.MatchResult.DealerResult; -import team.blackjack.service.dto.MatchResult.PlayerResult; import team.blackjack.service.dto.ScoreResult; import team.blackjack.domain.Result; @@ -53,16 +51,14 @@ public static void printParticipantScoreResult(ScoreResult scoreResult) { } public static void printGameResult(MatchResult result) { - final DealerResult dealerResult = result.dealerResult(); - final Map playeredResultMap = result.playerResultMap(); - + final Map playeredResultMap = result.playerResultMap(); println(""); println("## 최종 승패:"); - println("딜러: %d승 %d패 %d무".formatted(result.winCount(), result.loseCount(), result.drawCount()) - ); + println("딜러: %d승 %d패 %d무" + .formatted(result.dealerWinCount(), result.dealerLoseCount(), result.dealerDrawCount())); playeredResultMap.entrySet().stream() - .map(entry -> "%s: %s".formatted(entry.getKey(), entry.getValue().result().getName())) + .map(entry -> "%s: %s".formatted(entry.getKey(), entry.getValue().getName())) .forEach(OutputView::println); } From 9fda00a971c92cace31775a652e6e36c85c6d96e Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Wed, 11 Mar 2026 15:30:49 +0900 Subject: [PATCH 41/50] =?UTF-8?q?chore:=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team/blackjack/Application.java | 2 +- .../blackjack/{controler => control}/BlackJackController.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/main/java/team/blackjack/{controler => control}/BlackJackController.java (98%) diff --git a/src/main/java/team/blackjack/Application.java b/src/main/java/team/blackjack/Application.java index 026f5d1f667..1a82d700dba 100644 --- a/src/main/java/team/blackjack/Application.java +++ b/src/main/java/team/blackjack/Application.java @@ -1,7 +1,7 @@ package team.blackjack; import team.blackjack.config.AppConfig; -import team.blackjack.controler.BlackJackController; +import team.blackjack.control.BlackJackController; public class Application { public static void main(String[] args) { diff --git a/src/main/java/team/blackjack/controler/BlackJackController.java b/src/main/java/team/blackjack/control/BlackJackController.java similarity index 98% rename from src/main/java/team/blackjack/controler/BlackJackController.java rename to src/main/java/team/blackjack/control/BlackJackController.java index 34aae1135c8..9ddf60866ea 100644 --- a/src/main/java/team/blackjack/controler/BlackJackController.java +++ b/src/main/java/team/blackjack/control/BlackJackController.java @@ -1,4 +1,4 @@ -package team.blackjack.controler; +package team.blackjack.control; import java.util.List; import team.blackjack.service.dto.DrawResult; From 69f2bc2615f017acd3f3d9576ef45c31b1ad5833 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Wed, 11 Mar 2026 15:59:45 +0900 Subject: [PATCH 42/50] =?UTF-8?q?refactor:=202depth=20=EC=BB=A8=EB=B2=A4?= =?UTF-8?q?=EC=85=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../control/BlackJackController.java | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/main/java/team/blackjack/control/BlackJackController.java b/src/main/java/team/blackjack/control/BlackJackController.java index 9ddf60866ea..4c47d92895b 100644 --- a/src/main/java/team/blackjack/control/BlackJackController.java +++ b/src/main/java/team/blackjack/control/BlackJackController.java @@ -23,7 +23,7 @@ public void run() { DrawResult drawResult = blackJackService.getDrawResult(); OutputView.printDrawResult(drawResult); - readHitDecision(blackJackService.getAllPlayerNames()); + readPlayerHitDecision(blackJackService.getAllPlayerNames()); while (blackJackService.shouldDealerHit()) { OutputView.printDealerHitMessage(); @@ -53,30 +53,33 @@ private boolean hasDuplicatedName(List playerNames) { return playerNames.size() != playerNames.stream().distinct().count(); } - private void readHitDecision(List playerNames) { - playerNames.forEach(this::processHit); + private void readPlayerHitDecision(List playerNames) { + playerNames.forEach(this::processPlayerHit); } - private void processHit(String playerName) { - while (blackJackService.shouldPlayerHit(playerName)) { - OutputView.printAskDrawCard(playerName); + private void processPlayerHit(String playerName) { + while (readPlayerHitDecision(playerName)) { + blackJackService.hitPlayer(playerName); + OutputView.printPlayerCards(playerName, blackJackService.findPlayerCardNamesByName(playerName)); + } + } - String hitYn = InputView.readHitDecision(); + private boolean readPlayerHitDecision(String playerName) { + final boolean isPlayerBust = blackJackService.isPlayerBust(playerName); - while (!hitYn.equalsIgnoreCase("y") && !hitYn.equalsIgnoreCase("n")) { - OutputView.printWrongInputMessage(); - hitYn = InputView.readHitDecision(); - } + if (!isPlayerBust) { + OutputView.printBustMessage(); + return false; + } - final boolean isStand = hitYn.equalsIgnoreCase("n"); - if (isStand) { - return; - } + OutputView.printAskDrawCard(playerName); + String hitYn = InputView.readHitDecision(); - blackJackService.hitPlayer(playerName); - OutputView.printPlayerCards(playerName, blackJackService.findPlayerCardNamesByName(playerName)); + while (!hitYn.equalsIgnoreCase("y") && !hitYn.equalsIgnoreCase("n")) { + OutputView.printWrongInputMessage(); + hitYn = InputView.readHitDecision(); } - OutputView.printBustMessage(); + return hitYn.equalsIgnoreCase("n"); } } From f9e30ff4dd73c79feee7d3703ae18397a23a80e4 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Wed, 11 Mar 2026 16:04:41 +0900 Subject: [PATCH 43/50] =?UTF-8?q?refactor:=20isBust=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EB=AA=85=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team/blackjack/domain/BlackjackGame.java | 2 +- src/main/java/team/blackjack/domain/Player.java | 2 +- src/main/java/team/blackjack/service/BlackJackService.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/team/blackjack/domain/BlackjackGame.java b/src/main/java/team/blackjack/domain/BlackjackGame.java index 4a421d92cb0..0579ae7de37 100644 --- a/src/main/java/team/blackjack/domain/BlackjackGame.java +++ b/src/main/java/team/blackjack/domain/BlackjackGame.java @@ -36,7 +36,7 @@ public Map calculatePlayersResultMap() { } public boolean shouldPlayerHit(String name) { - return this.players.getPlayerByName(name).shouldPlayerHit(); + return this.players.getPlayerByName(name).isBust(); } public void hitPlayer(String name) { diff --git a/src/main/java/team/blackjack/domain/Player.java b/src/main/java/team/blackjack/domain/Player.java index 9379c39eced..e678d27fedc 100644 --- a/src/main/java/team/blackjack/domain/Player.java +++ b/src/main/java/team/blackjack/domain/Player.java @@ -46,7 +46,7 @@ public List getCardInAllHand() { .toList(); } - public boolean shouldPlayerHit() { + public boolean isBust() { return !DefaultBlackjackRule.isBust(getScore()); } } diff --git a/src/main/java/team/blackjack/service/BlackJackService.java b/src/main/java/team/blackjack/service/BlackJackService.java index aec49665bf1..e810a3bc9f7 100644 --- a/src/main/java/team/blackjack/service/BlackJackService.java +++ b/src/main/java/team/blackjack/service/BlackJackService.java @@ -30,7 +30,7 @@ public void drawInitialCards() { blackjackGame.drawInitialCards(); } - public boolean shouldPlayerHit(String name) { + public boolean isPlayerBust(String name) { return blackjackGame.shouldPlayerHit(name); } From 29c2b02589dfc90cc988251c41a653ed7181394d Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Wed, 11 Mar 2026 16:24:45 +0900 Subject: [PATCH 44/50] =?UTF-8?q?refactor:=20equalsIgnoreCase=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EB=B3=80=EA=B2=BD=20fix:=20hit&Stand=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team/blackjack/control/BlackJackController.java | 6 +++--- src/main/java/team/blackjack/domain/Player.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/team/blackjack/control/BlackJackController.java b/src/main/java/team/blackjack/control/BlackJackController.java index 4c47d92895b..aeed236bf62 100644 --- a/src/main/java/team/blackjack/control/BlackJackController.java +++ b/src/main/java/team/blackjack/control/BlackJackController.java @@ -67,7 +67,7 @@ private void processPlayerHit(String playerName) { private boolean readPlayerHitDecision(String playerName) { final boolean isPlayerBust = blackJackService.isPlayerBust(playerName); - if (!isPlayerBust) { + if (isPlayerBust) { OutputView.printBustMessage(); return false; } @@ -75,11 +75,11 @@ private boolean readPlayerHitDecision(String playerName) { OutputView.printAskDrawCard(playerName); String hitYn = InputView.readHitDecision(); - while (!hitYn.equalsIgnoreCase("y") && !hitYn.equalsIgnoreCase("n")) { + while (!"y".equalsIgnoreCase(hitYn) && !"n".equalsIgnoreCase(hitYn)) { OutputView.printWrongInputMessage(); hitYn = InputView.readHitDecision(); } - return hitYn.equalsIgnoreCase("n"); + return "y".equalsIgnoreCase(hitYn); } } diff --git a/src/main/java/team/blackjack/domain/Player.java b/src/main/java/team/blackjack/domain/Player.java index e678d27fedc..251bcf60f67 100644 --- a/src/main/java/team/blackjack/domain/Player.java +++ b/src/main/java/team/blackjack/domain/Player.java @@ -47,6 +47,6 @@ public List getCardInAllHand() { } public boolean isBust() { - return !DefaultBlackjackRule.isBust(getScore()); + return DefaultBlackjackRule.isBust(getScore()); } } From 49b176b144ac46b03ef5d4937cc0a5177605b6e5 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Thu, 12 Mar 2026 13:45:30 +0900 Subject: [PATCH 45/50] =?UTF-8?q?refactor:=20Participant=20playrer/dealer?= =?UTF-8?q?=20=EA=B3=B5=ED=86=B5=EB=A1=9C=EC=A7=81=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/blackjack/domain/BlackjackGame.java | 4 +- .../java/team/blackjack/domain/Dealer.java | 21 +--------- .../team/blackjack/domain/Participant.java | 28 +++++++++++++ .../java/team/blackjack/domain/Player.java | 39 +------------------ .../java/team/blackjack/domain/Players.java | 2 +- .../team/blackjack/domain/DealerTest.java | 2 +- .../team/blackjack/domain/PlayerTest.java | 2 +- 7 files changed, 35 insertions(+), 63 deletions(-) create mode 100644 src/main/java/team/blackjack/domain/Participant.java diff --git a/src/main/java/team/blackjack/domain/BlackjackGame.java b/src/main/java/team/blackjack/domain/BlackjackGame.java index 0579ae7de37..6960c42a296 100644 --- a/src/main/java/team/blackjack/domain/BlackjackGame.java +++ b/src/main/java/team/blackjack/domain/BlackjackGame.java @@ -62,11 +62,11 @@ public Map getAllPlayerScore() { } public List getDealerCards() { - return this.dealer.getHand().getCards(); + return this.dealer.getCards(); } public List getPlayerCardsByName(String name) { - return this.players.getPlayerByName(name).getCardInAllHand(); + return this.players.getPlayerByName(name).getCards(); } public Map> getAllPlayerCards() { diff --git a/src/main/java/team/blackjack/domain/Dealer.java b/src/main/java/team/blackjack/domain/Dealer.java index 991952d2072..d10e73e8e2d 100644 --- a/src/main/java/team/blackjack/domain/Dealer.java +++ b/src/main/java/team/blackjack/domain/Dealer.java @@ -1,28 +1,9 @@ package team.blackjack.domain; -import team.blackjack.domain.rule.DefaultBlackjackRule; - -public class Dealer { - - private final Hand hand; - - public Dealer() { - this.hand = new Hand(); - } +public class Dealer extends Participant { public Card draw(Deck deck) { return deck.draw(); } - public Hand getHand() { - return this.hand; - } - - public int getScore(){ - return DefaultBlackjackRule.calculateBestScore(this.getHand().getCards()); - } - - public void hit(Card card) { - this.hand.addCard(card); - } } diff --git a/src/main/java/team/blackjack/domain/Participant.java b/src/main/java/team/blackjack/domain/Participant.java new file mode 100644 index 00000000000..f7cc8fa26cb --- /dev/null +++ b/src/main/java/team/blackjack/domain/Participant.java @@ -0,0 +1,28 @@ +package team.blackjack.domain; + +import java.util.List; +import team.blackjack.domain.rule.DefaultBlackjackRule; + +public class Participant { + private final Hand hand; + + public Participant() { + this.hand = new Hand(); + } + + public int getScore() { + return this.hand.getScore(); + } + + public void hit(Card card) { + this.hand.addCard(card); + } + + public List getCards() { + return hand.getCards(); + } + + public boolean isBust() { + return DefaultBlackjackRule.isBust(getScore()); + } +} diff --git a/src/main/java/team/blackjack/domain/Player.java b/src/main/java/team/blackjack/domain/Player.java index 251bcf60f67..86a28d3b02b 100644 --- a/src/main/java/team/blackjack/domain/Player.java +++ b/src/main/java/team/blackjack/domain/Player.java @@ -1,52 +1,15 @@ package team.blackjack.domain; -import java.util.ArrayList; -import java.util.List; -import team.blackjack.domain.rule.DefaultBlackjackRule; - -public class Player { +public class Player extends Participant { private final String name; - private final List hands; public Player(String name) { this.name = name; - this.hands = initHands(); - } - public List getHands() { - return List.copyOf(hands); } public String getName() { return this.name; } - /** - * TODO: 추후 기능 확장시 한 라운드에 여러 개의 hand가 생기는 경우, 해당 메소드 수정 필요. - * @return - */ - public int getScore(){ - return this.hands.getFirst().getScore(); - } - - public void hit(Card card) { - this.hands.getFirst().addCard(card); - } - - private List initHands(){ - List hands = new ArrayList<>(); - hands.add(new Hand()); - return hands; - } - - public List getCardInAllHand() { - return hands.stream() - .map(Hand::getCards) - .flatMap(List::stream) - .toList(); - } - - public boolean isBust() { - return DefaultBlackjackRule.isBust(getScore()); - } } diff --git a/src/main/java/team/blackjack/domain/Players.java b/src/main/java/team/blackjack/domain/Players.java index 56c98d01f46..57b9badf5ef 100644 --- a/src/main/java/team/blackjack/domain/Players.java +++ b/src/main/java/team/blackjack/domain/Players.java @@ -26,7 +26,7 @@ public void initPlayerHands(Deck deck) { public Map> getCardsByPlayer(){ final HashMap> result = new HashMap<>(); for (Player player : players) { - result.put(player.getName(), player.getCardInAllHand()); + result.put(player.getName(), player.getCards()); } return result; diff --git a/src/test/java/team/blackjack/domain/DealerTest.java b/src/test/java/team/blackjack/domain/DealerTest.java index b38ab02dea8..b9f2753df56 100644 --- a/src/test/java/team/blackjack/domain/DealerTest.java +++ b/src/test/java/team/blackjack/domain/DealerTest.java @@ -12,7 +12,7 @@ class DealerTest { dealer.hit(Card.ACE_OF_HEARTS); dealer.hit(Card.KING_OF_HEARTS); - assertThat(dealer.getHand().getCards()) + assertThat(dealer.getCards()) .containsExactly(Card.ACE_OF_HEARTS, Card.KING_OF_HEARTS); } @Test diff --git a/src/test/java/team/blackjack/domain/PlayerTest.java b/src/test/java/team/blackjack/domain/PlayerTest.java index 8449071c70f..2db952b6d37 100644 --- a/src/test/java/team/blackjack/domain/PlayerTest.java +++ b/src/test/java/team/blackjack/domain/PlayerTest.java @@ -11,7 +11,7 @@ class PlayerTest { player.hit(Card.ACE_OF_HEARTS); - assertThat(player.getHands().getFirst().getCards()) + assertThat(player.getCards()) .containsExactly(Card.ACE_OF_HEARTS); } From 62e43c504e95291ebac5995431ca10085b32e031 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Thu, 12 Mar 2026 18:06:21 +0900 Subject: [PATCH 46/50] =?UTF-8?q?feat:=20=ED=94=8C=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=20=EB=B0=B0=ED=8C=85=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20-=20DefaultBlackjackRule:=20judge=EB=A5=BC=20Partic?= =?UTF-8?q?ipant=20=EA=B8=B0=EB=B0=98=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD,=20BLACKJACK=20=EA=B2=B0=EA=B3=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20-=20=EA=B0=81=20=ED=94=8C=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=20=EB=B3=84=20=EB=B0=B0=ED=8C=85=20=EA=B8=88=EC=95=A1?= =?UTF-8?q?=20=EC=9E=85=EB=A0=A5=20=EB=B0=9B=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 +++--- .../control/BlackJackController.java | 38 +++++++++++++++++-- .../team/blackjack/domain/BlackjackGame.java | 37 +++++++++++------- src/main/java/team/blackjack/domain/Hand.java | 9 +++++ .../team/blackjack/domain/Participant.java | 11 +++++- .../java/team/blackjack/domain/Player.java | 8 ++++ .../java/team/blackjack/domain/Result.java | 22 +++++------ .../domain/rule/DefaultBlackjackRule.java | 30 ++++++++++++--- .../blackjack/service/BlackJackService.java | 24 ++++-------- .../blackjack/service/dto/RevenueResult.java | 8 ++++ .../java/team/blackjack/view/InputView.java | 4 ++ .../java/team/blackjack/view/OutputView.java | 20 +++++++++- 12 files changed, 164 insertions(+), 59 deletions(-) create mode 100644 src/main/java/team/blackjack/service/dto/RevenueResult.java diff --git a/README.md b/README.md index add4665c21c..0f89905631d 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ ## 기본 기능 구현 - 플레이어 이름을 등록한다. +- 플레이어는 배팅 금액을 입력한다. - 참여자(플레이어 + 딜러)에게 각각 2장의 카드를 랜덤으로 발급한다. - 딜러 카드는 하나만 출력한다. - 각 플레이어의 카드는 모두(2장) 출력한다. @@ -24,10 +25,11 @@ - 조건: 출력 순서는 딜러 먼저 출력하며, 입력받은 플레이어 순서로 이어 출력한다. - 게임 결과를 출력한다. - - 최종 승패를 출력한다. - - 딜러는 승패전적을 출력한다. (예: 딜러: 1승 1패) - - 플레이어는 승패 여부를 출력한다. (예: pobi: 승) - - 조건: 출력 순서는 딜러 먼저 출력하며, 입력받은 플레이어 순서로 이어 출력한다. + - 딜러의 수익률을 출력한다. + - 각 플레이어의 수익률을 출력한다. ## 블랙잭 상세 규칙 -- 플레이어가 Bust인 경우에는 자동 패배 처리된다. 이때, 딜러의 카드상태에 의존되지 않고, 자동 패배한다. +- 딜러가 버스트된 경우 살아있는 플레이어는 모두 승리한다. +- 블랙잭은 1.5배의 수익률로 승리한다. +- 딜러와 플레이어가 모두 블랙잭인 경우 무승부로 처리한다. +- 딜러와 플레이어가 모두 버스트된 경우 딜러 승리로 처리한다. diff --git a/src/main/java/team/blackjack/control/BlackJackController.java b/src/main/java/team/blackjack/control/BlackJackController.java index aeed236bf62..1e48c395171 100644 --- a/src/main/java/team/blackjack/control/BlackJackController.java +++ b/src/main/java/team/blackjack/control/BlackJackController.java @@ -2,7 +2,7 @@ import java.util.List; import team.blackjack.service.dto.DrawResult; -import team.blackjack.service.dto.MatchResult; +import team.blackjack.service.dto.RevenueResult; import team.blackjack.service.dto.ScoreResult; import team.blackjack.service.BlackJackService; import team.blackjack.view.InputView; @@ -20,6 +20,8 @@ public void run() { blackJackService.initGame(playerNames); blackJackService.drawInitialCards(); + readAllPlayerBattingMoneyRetry(blackJackService.getAllPlayerNames()); + DrawResult drawResult = blackJackService.getDrawResult(); OutputView.printDrawResult(drawResult); @@ -33,8 +35,36 @@ public void run() { ScoreResult scoreResult = blackJackService.calculateAllParticipantScore(); OutputView.printParticipantScoreResult(scoreResult); - MatchResult matchResult = blackJackService.getGameResult(); - OutputView.printGameResult(matchResult); + RevenueResult revenueResult = blackJackService.getRevenueResult(); + OutputView.printRevenueResult(revenueResult); + } + + public void readAllPlayerBattingMoneyRetry(List playerNames) { + playerNames.forEach(playerName -> { + int battingMoney = readPlayerBattingMoneyRetry(playerName); + blackJackService.batMoney(playerName, battingMoney); + }); + } + + private int readPlayerBattingMoneyRetry(String playerName) { + int battingMoney = -1; + + while (battingMoney <= 0) { + battingMoney = readPlayerBattingMoney(playerName); + } + return battingMoney; + } + + private int readPlayerBattingMoney(String playerName) { + int battingMoney = -1; + OutputView.printAskBettingMoney(playerName); + try { + battingMoney = InputView.readPlayerBattingMoney(); + } catch (NumberFormatException e) { + OutputView.printWrongDecisionInputMessage(); + } + + return battingMoney; } private List readPlayerNames(){ @@ -76,7 +106,7 @@ private boolean readPlayerHitDecision(String playerName) { String hitYn = InputView.readHitDecision(); while (!"y".equalsIgnoreCase(hitYn) && !"n".equalsIgnoreCase(hitYn)) { - OutputView.printWrongInputMessage(); + OutputView.printWrongDecisionInputMessage(); hitYn = InputView.readHitDecision(); } diff --git a/src/main/java/team/blackjack/domain/BlackjackGame.java b/src/main/java/team/blackjack/domain/BlackjackGame.java index 6960c42a296..6ccafa64107 100644 --- a/src/main/java/team/blackjack/domain/BlackjackGame.java +++ b/src/main/java/team/blackjack/domain/BlackjackGame.java @@ -1,10 +1,8 @@ package team.blackjack.domain; - import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import team.blackjack.domain.rule.DefaultBlackjackRule; public class BlackjackGame { @@ -25,17 +23,31 @@ public void drawInitialCards() { this.dealer.hit(dealer.draw(deck)); } - public Map calculatePlayersResultMap() { - return players.getPlayerList().stream() - .collect(Collectors.toMap( - Player::getName, - player -> DefaultBlackjackRule.judgeResult(player.getScore(), dealer.getScore()), - (existing, replacement) -> existing, - LinkedHashMap::new - )); + public Map calculateAllPlayerRevenue() { + final LinkedHashMap result = new LinkedHashMap<>(); + for (Player player : players.getPlayerList()) { + result.put(player.getName(), calculateMoney(player)); + } + + return result; + } + + public double calculateDealerRevenue() { + return calculateAllPlayerRevenue().values().stream() + .mapToDouble(Double::doubleValue) + .sum() * -1; + } + + private double calculateMoney(Player player) { + final Result result = DefaultBlackjackRule.judge(player, dealer); + return result.getOdds() * player.getBatMoney(); + } + + public void batMoney(String name, int money) { + this.players.getPlayerByName(name).bat(money); } - public boolean shouldPlayerHit(String name) { + public boolean isPlayerBust(String name) { return this.players.getPlayerByName(name).isBust(); } @@ -45,8 +57,7 @@ public void hitPlayer(String name) { } public boolean shouldDealerHit() { - final int score = this.dealer.getScore(); - return DefaultBlackjackRule.isDealerMustDraw(score); + return DefaultBlackjackRule.shouldDealerHit(this.dealer.getScore()); } public void hitDealer() { diff --git a/src/main/java/team/blackjack/domain/Hand.java b/src/main/java/team/blackjack/domain/Hand.java index af3b8dc0e2a..9cb0c71a6ef 100644 --- a/src/main/java/team/blackjack/domain/Hand.java +++ b/src/main/java/team/blackjack/domain/Hand.java @@ -26,4 +26,13 @@ public List getCardNames() { public int getScore(){ return DefaultBlackjackRule.calculateBestScore(this.getCards()); } + + public boolean isBust() { + return DefaultBlackjackRule.isBust(getScore()); + } + + public boolean isBlackjack() { + return DefaultBlackjackRule.isBlackjack(getScore(), cards.size()); + } + } diff --git a/src/main/java/team/blackjack/domain/Participant.java b/src/main/java/team/blackjack/domain/Participant.java index f7cc8fa26cb..51af73a34c5 100644 --- a/src/main/java/team/blackjack/domain/Participant.java +++ b/src/main/java/team/blackjack/domain/Participant.java @@ -1,7 +1,6 @@ package team.blackjack.domain; import java.util.List; -import team.blackjack.domain.rule.DefaultBlackjackRule; public class Participant { private final Hand hand; @@ -15,6 +14,10 @@ public int getScore() { } public void hit(Card card) { + if (isBust()) { + throw new IllegalStateException("이미 Bust 상태입니다."); + } + this.hand.addCard(card); } @@ -23,6 +26,10 @@ public List getCards() { } public boolean isBust() { - return DefaultBlackjackRule.isBust(getScore()); + return hand.isBust(); + } + + public boolean isBlackjack() { + return hand.isBlackjack(); } } diff --git a/src/main/java/team/blackjack/domain/Player.java b/src/main/java/team/blackjack/domain/Player.java index 86a28d3b02b..894abc3622d 100644 --- a/src/main/java/team/blackjack/domain/Player.java +++ b/src/main/java/team/blackjack/domain/Player.java @@ -2,6 +2,7 @@ public class Player extends Participant { private final String name; + private double batMoney; public Player(String name) { this.name = name; @@ -12,4 +13,11 @@ public String getName() { return this.name; } + public void bat(double money) { + this.batMoney = money; + } + + public double getBatMoney() { + return this.batMoney; + } } diff --git a/src/main/java/team/blackjack/domain/Result.java b/src/main/java/team/blackjack/domain/Result.java index e0509c7ff3a..7e354c3d1e5 100644 --- a/src/main/java/team/blackjack/domain/Result.java +++ b/src/main/java/team/blackjack/domain/Result.java @@ -1,28 +1,24 @@ package team.blackjack.domain; public enum Result { - WIN("승"), - DRAW("무"), - LOSE("패"); + BLACKJACK("승", 1.5), + WIN("승", 1), + DRAW("무", 0), + LOSE("패", -1); private final String name; + private final double odds; - Result(String name) { - + Result(String name, double odds) { this.name = name; + this.odds = odds; } public String getName(){ return this.name; } - public Result reverse(){ - if (this == WIN) { - return LOSE; - } - if (this == LOSE) { - return WIN; - } - return DRAW; + public double getOdds() { + return this.odds; } } diff --git a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java index cad6f4b388f..1709b937afc 100644 --- a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java +++ b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java @@ -1,9 +1,12 @@ package team.blackjack.domain.rule; +import static team.blackjack.domain.Result.BLACKJACK; + import java.util.List; import java.util.Map; import java.util.stream.Collectors; import team.blackjack.domain.Card; +import team.blackjack.domain.Participant; import team.blackjack.domain.Result; public class DefaultBlackjackRule { @@ -11,6 +14,9 @@ public class DefaultBlackjackRule { private static final int BLACKJACK_CARD_COUNT = 2; private static final int DEALER_STAND_SCORE = 17; + private DefaultBlackjackRule() { + } + public static boolean isBust(int score) { return score > BLACKJACK; } @@ -22,7 +28,7 @@ public static boolean isBlackjack(int score, int cardCount) { return score == BLACKJACK; } - public static boolean isDealerMustDraw(int score) { + public static boolean shouldDealerHit(int score) { return score < DEALER_STAND_SCORE; } @@ -30,16 +36,30 @@ public static boolean canUseAceAsEleven(int currentSum) { return currentSum <= 10; } - public static Result judgeResult(int myScore, int targetScore) { - if(myScore > BLACKJACK){ + public static Result judge(Participant me, Participant target) { + if (me.isBust()) { return Result.LOSE; } - if(myScore > targetScore){ + + if (target.isBust()) { return Result.WIN; } - if(myScore < targetScore){ + + final int score = me.getScore(); + final int targetScore = target.getScore(); + + if (score < targetScore) { return Result.LOSE; } + + if (me.isBlackjack() && !target.isBlackjack()) { + return Result.BLACKJACK; + } + + if (score > targetScore) { + return Result.WIN; + } + return Result.DRAW; } diff --git a/src/main/java/team/blackjack/service/BlackJackService.java b/src/main/java/team/blackjack/service/BlackJackService.java index e810a3bc9f7..1c6b5e68a85 100644 --- a/src/main/java/team/blackjack/service/BlackJackService.java +++ b/src/main/java/team/blackjack/service/BlackJackService.java @@ -1,6 +1,5 @@ package team.blackjack.service; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -9,9 +8,8 @@ import team.blackjack.domain.Dealer; import team.blackjack.domain.Player; import team.blackjack.domain.Players; -import team.blackjack.domain.Result; import team.blackjack.service.dto.DrawResult; -import team.blackjack.service.dto.MatchResult; +import team.blackjack.service.dto.RevenueResult; import team.blackjack.service.dto.ScoreResult; public class BlackJackService { @@ -31,7 +29,7 @@ public void drawInitialCards() { } public boolean isPlayerBust(String name) { - return blackjackGame.shouldPlayerHit(name); + return blackjackGame.isPlayerBust(name); } public void hitPlayer(String name) { @@ -97,20 +95,14 @@ public DrawResult getDrawResult() { return new DrawResult(playerNames, cards.getFirst().getCardName(), playerCards); } - public MatchResult getGameResult() { - final Map playerResults = blackjackGame.calculatePlayersResultMap(); - final Collection playerResultList = playerResults.values(); - final long winCnt = countBy(playerResultList, Result.WIN); - final long loseCnt = countBy(playerResultList, Result.LOSE); - final long drawCnt = countBy(playerResultList, Result.DRAW); + public RevenueResult getRevenueResult() { + final double dealerRevenue = blackjackGame.calculateDealerRevenue(); + final Map allPlayerRevenue = blackjackGame.calculateAllPlayerRevenue(); - return new MatchResult(winCnt, loseCnt, drawCnt, playerResults); + return new RevenueResult(dealerRevenue, allPlayerRevenue); } - private long countBy(Collection results, Result target) { - return results.stream() - .map(Result::reverse) - .filter(result -> result == target) - .count(); + public void batMoney(String playerName, int battingMoney) { + this.blackjackGame.batMoney(playerName, battingMoney); } } diff --git a/src/main/java/team/blackjack/service/dto/RevenueResult.java b/src/main/java/team/blackjack/service/dto/RevenueResult.java new file mode 100644 index 00000000000..c92b1592703 --- /dev/null +++ b/src/main/java/team/blackjack/service/dto/RevenueResult.java @@ -0,0 +1,8 @@ +package team.blackjack.service.dto; + +import java.util.Map; + +public record RevenueResult( + double dealerRevenue, + Map playerRevenueMap +) { } diff --git a/src/main/java/team/blackjack/view/InputView.java b/src/main/java/team/blackjack/view/InputView.java index 4d467c7b626..f15cc41a268 100644 --- a/src/main/java/team/blackjack/view/InputView.java +++ b/src/main/java/team/blackjack/view/InputView.java @@ -19,4 +19,8 @@ public static List readPlayerNames() { public static String readHitDecision() { return readInput(); } + + public static int readPlayerBattingMoney() { + return Integer.parseInt(readInput()); + } } diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java index 1f20f813beb..472b5e59f00 100644 --- a/src/main/java/team/blackjack/view/OutputView.java +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -5,6 +5,7 @@ import java.util.Map.Entry; import team.blackjack.service.dto.DrawResult; import team.blackjack.service.dto.MatchResult; +import team.blackjack.service.dto.RevenueResult; import team.blackjack.service.dto.ScoreResult; import team.blackjack.domain.Result; @@ -67,11 +68,28 @@ public static void printBustMessage() { println("버스트 되었습니다. 더 이상 카드를 받을 수 없습니다."); } - public static void printWrongInputMessage() { + public static void printWrongDecisionInputMessage() { println("잘못된 입력입니다. y, n 중 하나를 입력해주세요."); } public static void printDuplicatedNameMessage() { println("중복된 이름은 허용되지 않습니다. 다시 입력해주세요."); } + + public static void printAskBettingMoney(String playerName) { + println("%s의 의 배팅 금액은?".formatted(playerName)); + } + + public static void printWrongBattingMoneyInputMessage() { + println("잘못된 입력입니다. 양수인 숫자를 입력해주세요."); + } + + public static void printRevenueResult(RevenueResult revenueResult) { + println(""); + println("## 최종 수익:"); + println("딜러: %.2f".formatted(revenueResult.dealerRevenue())); + for (Entry entry : revenueResult.playerRevenueMap().entrySet()) { + println("%s: %.2f".formatted(entry.getKey(), entry.getValue())); + } + } } From 445c95e493738e5ee2cd4a19b169ab8d0b77b200 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 13 Mar 2026 13:19:58 +0900 Subject: [PATCH 47/50] =?UTF-8?q?test:=20BlackjackGameTest=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20DefaultBlackjackRuleTest=20=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain/BlackjackGameTest.java | 217 ++++++++++++++++++ .../domain/rule/DefaultBlackjackRuleTest.java | 80 +++++-- 2 files changed, 284 insertions(+), 13 deletions(-) create mode 100644 src/test/java/team/blackjack/domain/BlackjackGameTest.java diff --git a/src/test/java/team/blackjack/domain/BlackjackGameTest.java b/src/test/java/team/blackjack/domain/BlackjackGameTest.java new file mode 100644 index 00000000000..f67f82dba3a --- /dev/null +++ b/src/test/java/team/blackjack/domain/BlackjackGameTest.java @@ -0,0 +1,217 @@ +package team.blackjack.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class BlackjackGameTest { + + public Dealer dealer; + public Player pobi; + public Player jason; + public Players players; + public BlackjackGame game; + + @BeforeEach + void setUp() { + dealer = new Dealer(); + pobi = new Player("pobi"); + jason = new Player("jason"); + players = new Players(List.of( + pobi, + jason + )); + game = new BlackjackGame(dealer, players); + } + + @Test + void 초기화_이후에는_참여자_모두_카드_2장() { + game.drawInitialCards(); + + assertThat(game.getAllPlayerNames()).containsExactly("pobi", "jason"); + assertThat(game.getPlayerCardsByName("pobi")).hasSize(2); + assertThat(game.getPlayerCardsByName("jason")).hasSize(2); + assertThat(game.getDealerCards()).hasSize(2); + } + + @Test + void batMoney_호출시_해당_플레이어_배팅금이_설정된다() { + int pobiBatMoney = 1000; + int jasonBatMoney = 500; + game.batMoney("pobi", pobiBatMoney); + game.batMoney("jason", jasonBatMoney); + + assertThat(pobi.getBatMoney()).isEqualTo(pobiBatMoney); + assertThat(jason.getBatMoney()).isEqualTo(jasonBatMoney); + } + + @Test + void 딜러의_수익은_플레이어_수익_합의_음수이다() { + game.batMoney("pobi", 1000); + game.batMoney("jason", 1000); + game.drawInitialCards(); + + Map playerRevenue = game.calculateAllPlayerRevenue(); + double expectedDealerRevenue = -playerRevenue.values().stream() + .mapToDouble(Double::doubleValue) + .sum(); + + assertThat(game.calculateDealerRevenue()).isEqualTo(expectedDealerRevenue); + } + + + @Test + void _30과_20에_대한_버스트_여부를_반환한다() { + pobi.hit(Card.KING_OF_HEARTS); + pobi.hit(Card.QUEEN_OF_HEARTS); + pobi.hit(Card.JACK_OF_HEARTS); // 30으로 버스트 + + jason.hit(Card.TEN_OF_HEARTS); + jason.hit(Card.SIX_OF_HEARTS); // 16으로 버스트 아님 + + boolean pobiShouldHit = game.isPlayerBust("pobi"); + boolean jasonShouldHit = game.isPlayerBust("jason"); + + // shouldPlayerHit은 해당 플레이어의 버스트 여부를 반환 (현재 구현: isBust() 그대로 반환) + assertThat(pobiShouldHit).isIn(true); + assertThat(jasonShouldHit).isIn(false); + } + + @Test + void 플레이어는_hit을_하는_경우_카드가_한_장_늘어난다() { + game.drawInitialCards(); + int sizeBefore = game.getPlayerCardsByName("pobi").size(); + + game.hitPlayer("pobi"); + + assertThat(game.getPlayerCardsByName("pobi")).hasSize(sizeBefore + 1); + } + + @Test + void 딜러는_점수가_17_미만이면_Hit을_해야한다() { + game.drawInitialCards(); + int dealerScore = game.getDealerScore(); + + boolean shouldHit = game.shouldDealerHit(); + + assertThat(shouldHit).isEqualTo(dealerScore < 17); + } + + @Test + void 딜러가_hit하는경우_딜러_카드가_한_장_늘어난다() { + game.drawInitialCards(); + int sizeBefore = game.getDealerCards().size(); + + game.hitDealer(); + + assertThat(game.getDealerCards()).hasSize(sizeBefore + 1); + } + + @Test + void getDealerScore_딜러_점수_반환() { + game.drawInitialCards(); + + int score = game.getDealerScore(); + + assertThat(score).isBetween(0, 21); + } + + @Test + void 플레이어별_카드_목록_반환() { + game.drawInitialCards(); + + Map> allCards = game.getAllPlayerCards(); + + assertThat(allCards).containsKeys("pobi", "jason"); + assertThat(allCards.get("pobi")).hasSize(2); + assertThat(allCards.get("jason")).hasSize(2); + } + + @Test + void 플레이어_이름_목록_반환() { + assertThat(game.getAllPlayerNames()).containsExactly("pobi", "jason"); + } + + // --- 실제 게임 흐름 시나리오 테스트 --- + + @Test + void 한_판_진행_배팅_초기카드_수익계산_끝까지_정상_동작() { + game.batMoney("pobi", 1000); + game.batMoney("jason", 500); + + game.drawInitialCards(); + + Map playerRevenue = game.calculateAllPlayerRevenue(); + double dealerRevenue = game.calculateDealerRevenue(); + + assertThat(game.getAllPlayerNames()).containsExactly("pobi", "jason"); + assertThat(game.getPlayerCardsByName("pobi")).hasSize(2); + assertThat(game.getPlayerCardsByName("jason")).hasSize(2); + assertThat(game.getDealerCards()).hasSize(2); + assertThat(playerRevenue).containsOnlyKeys("pobi", "jason"); + assertThat(dealerRevenue).isEqualTo(-playerRevenue.values().stream().mapToDouble(Double::doubleValue).sum()); + } + + @Test + void 한_판_진행_플레이어_히트_후_수익계산() { + game.batMoney("pobi", 1000); + game.batMoney("jason", 1000); + game.drawInitialCards(); + + while (!game.isPlayerBust("pobi") && game.getPlayerCardsByName("pobi").size() < 5) { + game.hitPlayer("pobi"); + } + + Map playerRevenue = game.calculateAllPlayerRevenue(); + double dealerRevenue = game.calculateDealerRevenue(); + + assertThat(playerRevenue).containsOnlyKeys("pobi", "jason"); + assertThat(dealerRevenue).isEqualTo(-playerRevenue.values().stream().mapToDouble(Double::doubleValue).sum()); + } + + @Test + void 한_판_진행_딜러_17까지_히트_후_수익계산() { + game.batMoney("pobi", 1000); + game.batMoney("jason", 1000); + game.drawInitialCards(); + + while (game.shouldDealerHit()) { + game.hitDealer(); + } + + assertThat(game.getDealerScore()).isGreaterThanOrEqualTo(17); + Map playerRevenue = game.calculateAllPlayerRevenue(); + double dealerRevenue = game.calculateDealerRevenue(); + assertThat(dealerRevenue).isEqualTo(-playerRevenue.values().stream().mapToDouble(Double::doubleValue).sum()); + } + + @Test + void 한_게임의_전체_흐름_플레이어_히트_딜러_히트_수익계산() { + game.batMoney("pobi", 1000); + game.batMoney("jason", 1000); + + // 초기 카드 강제로 설정 (실제 게임에서는 랜덤이지만 테스트에서는 고정) + // 19 + pobi.hit(Card.ACE_OF_SPADES); + pobi.hit(Card.EIGHT_OF_HEARTS); + + // jason은 20으로 시작해서 히트해서 버스트 시도 + jason.hit(Card.TEN_OF_HEARTS); + jason.hit(Card.KING_OF_HEARTS); + jason.hit(Card.FIVE_OF_DIAMONDS); // 25로 버스트 + + // 딜러는 17로 시작해서 히트해서 18로 만듦 + dealer.hit(Card.KING_OF_CLUBS); + dealer.hit(Card.KING_OF_SPADES); + + Map playerRevenue = game.calculateAllPlayerRevenue(); + double dealerRevenue = game.calculateDealerRevenue(); + + assertThat(playerRevenue).containsOnlyKeys("pobi", "jason"); + assertThat(dealerRevenue).isEqualTo(-playerRevenue.values().stream().mapToDouble(Double::doubleValue).sum()); + } +} diff --git a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java index 39929f3476c..b529f14e0bb 100644 --- a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java +++ b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java @@ -7,11 +7,13 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import team.blackjack.domain.Card; +import team.blackjack.domain.Dealer; +import team.blackjack.domain.Participant; +import team.blackjack.domain.Player; import team.blackjack.domain.Result; class DefaultBlackjackRuleTest { - @ParameterizedTest @CsvSource({ "22, true", @@ -39,19 +41,56 @@ class DefaultBlackjackRuleTest { "17, false" }) void 딜러_카드_추가_여부(int dealerScore, boolean expected) { - boolean isDealerMustDraw = DefaultBlackjackRule.isDealerMustDraw(dealerScore); - assertEquals(expected, isDealerMustDraw); + boolean shouldHit = DefaultBlackjackRule.shouldDealerHit(dealerScore); + assertEquals(expected, shouldHit); } - @ParameterizedTest - @CsvSource({ - "20,18,WIN", - "18,20,LOSE", - "17,17,DRAW", - "22,20,LOSE" - }) - void 승패_판정(int myScore, int opponentScore, Result expected) { - assertEquals(expected, DefaultBlackjackRule.judgeResult(myScore, opponentScore)); + @Test + void 플레이어가_버스트면_패() { + Participant player = playerWithCards(Card.KING_OF_HEARTS, Card.SEVEN_OF_HEARTS, Card.FIVE_OF_HEARTS); // 22 + Participant target = dealerWithCards(Card.KING_OF_CLUBS, Card.TEN_OF_CLUBS); // 20 + + assertEquals(Result.LOSE, DefaultBlackjackRule.judge(player, target)); + } + + @Test + void 딜러가_버스트고_플레이어는_20점일경우_승() { + Participant player = playerWithCards(Card.KING_OF_HEARTS, Card.TEN_OF_HEARTS); // 20 + Participant dealer = dealerWithCards(Card.KING_OF_CLUBS, Card.SEVEN_OF_CLUBS, Card.SIX_OF_CLUBS); // 23 + + assertEquals(Result.WIN, DefaultBlackjackRule.judge(player, dealer)); + } + + @Test + void 버스트가_아닌_플레이어_점수가_높으면_승() { + Participant player = playerWithCards(Card.KING_OF_HEARTS, Card.QUEEN_OF_HEARTS); // 20 + Participant dealer = dealerWithCards(Card.KING_OF_CLUBS, Card.EIGHT_OF_CLUBS); // 18 + + assertEquals(Result.WIN, DefaultBlackjackRule.judge(player, dealer)); + } + + @Test + void 플레이어_점수가_낮으면_패() { + Participant player = playerWithCards(Card.KING_OF_HEARTS, Card.EIGHT_OF_HEARTS); // 18 + Participant dealer = dealerWithCards(Card.KING_OF_CLUBS, Card.QUEEN_OF_CLUBS); // 20 + + assertEquals(Result.LOSE, DefaultBlackjackRule.judge(player, dealer)); + } + + @Test + void 플레이어와_딜러_둘다_버스트가_아니고_동점이면_무() { + Participant player = playerWithCards(Card.KING_OF_HEARTS, Card.SEVEN_OF_HEARTS); // 17 + Participant dealer = dealerWithCards(Card.KING_OF_CLUBS, Card.SEVEN_OF_CLUBS); // 17 + + assertEquals(Result.DRAW, DefaultBlackjackRule.judge(player, dealer)); + } + + @Test + void 플레이어가_블랙잭_상대는_아니면_블랙잭_승() { + Participant player = playerWithCards(Card.ACE_OF_HEARTS, Card.KING_OF_HEARTS); // blackjack + Participant dealer = dealerWithCards(Card.KING_OF_CLUBS, Card.NINE_OF_CLUBS); // 19 + + assertEquals(Result.BLACKJACK, DefaultBlackjackRule.judge(player, dealer)); } @ParameterizedTest @@ -63,7 +102,6 @@ class DefaultBlackjackRuleTest { assertEquals(expected, DefaultBlackjackRule.canUseAceAsEleven(score)); } - @Test void 숫자10과_6이후에_ACE가_2개_오는_경우_각각_1로_정상_해석되는지_테스트() { List cards = List.of(Card.KING_OF_CLUBS, Card.SIX_OF_HEARTS, Card.ACE_OF_SPADES, Card.ACE_OF_HEARTS); @@ -80,4 +118,20 @@ class DefaultBlackjackRuleTest { assertEquals(21, score); } + + private static Player playerWithCards(Card... cards) { + Player player = new Player("test"); + for (Card card : cards) { + player.hit(card); + } + return player; + } + + private static Dealer dealerWithCards(Card... cards) { + Dealer dealer = new Dealer(); + for (Card card : cards) { + dealer.hit(card); + } + return dealer; + } } From 34c8f47d8ee80f121f518806dc88daa9cc3fdbda Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 13 Mar 2026 13:49:28 +0900 Subject: [PATCH 48/50] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=B4=EC=A7=84=20DTO=20=EB=B0=8F=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/blackjack/service/dto/MatchResult.java | 15 --------------- .../java/team/blackjack/view/OutputView.java | 17 ----------------- 2 files changed, 32 deletions(-) delete mode 100644 src/main/java/team/blackjack/service/dto/MatchResult.java diff --git a/src/main/java/team/blackjack/service/dto/MatchResult.java b/src/main/java/team/blackjack/service/dto/MatchResult.java deleted file mode 100644 index f9b7e729659..00000000000 --- a/src/main/java/team/blackjack/service/dto/MatchResult.java +++ /dev/null @@ -1,15 +0,0 @@ -package team.blackjack.service.dto; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import team.blackjack.domain.Result; - -public record MatchResult( - long dealerWinCount, - long dealerLoseCount, - long dealerDrawCount, - Map playerResultMap -) { -} diff --git a/src/main/java/team/blackjack/view/OutputView.java b/src/main/java/team/blackjack/view/OutputView.java index 472b5e59f00..f53f0fae4e6 100644 --- a/src/main/java/team/blackjack/view/OutputView.java +++ b/src/main/java/team/blackjack/view/OutputView.java @@ -1,13 +1,10 @@ package team.blackjack.view; import java.util.List; -import java.util.Map; import java.util.Map.Entry; import team.blackjack.service.dto.DrawResult; -import team.blackjack.service.dto.MatchResult; import team.blackjack.service.dto.RevenueResult; import team.blackjack.service.dto.ScoreResult; -import team.blackjack.domain.Result; public class OutputView { @@ -50,20 +47,6 @@ public static void printParticipantScoreResult(ScoreResult scoreResult) { scoreResult.playerScores().get(playerName))); } } - - public static void printGameResult(MatchResult result) { - final Map playeredResultMap = result.playerResultMap(); - println(""); - println("## 최종 승패:"); - println("딜러: %d승 %d패 %d무" - .formatted(result.dealerWinCount(), result.dealerLoseCount(), result.dealerDrawCount())); - - playeredResultMap.entrySet().stream() - .map(entry -> "%s: %s".formatted(entry.getKey(), entry.getValue().getName())) - .forEach(OutputView::println); - - } - public static void printBustMessage() { println("버스트 되었습니다. 더 이상 카드를 받을 수 없습니다."); } From 1a4fcf34e3910f44fa1827570c5b4686b43fcaaa Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Fri, 13 Mar 2026 16:01:05 +0900 Subject: [PATCH 49/50] =?UTF-8?q?refactor:=20Rule=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EB=A1=9C=EC=A7=81=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/blackjack/domain/BlackjackGame.java | 31 +++- src/main/java/team/blackjack/domain/Hand.java | 62 +++++++- .../domain/rule/DefaultBlackjackRule.java | 110 +------------- .../blackjack/domain/BlackjackGameTest.java | 83 ++++++++++- .../domain/rule/DefaultBlackjackRuleTest.java | 137 ------------------ .../service/BlackJackServiceTest.java | 2 +- 6 files changed, 170 insertions(+), 255 deletions(-) delete mode 100644 src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java diff --git a/src/main/java/team/blackjack/domain/BlackjackGame.java b/src/main/java/team/blackjack/domain/BlackjackGame.java index 6ccafa64107..7daf78d156a 100644 --- a/src/main/java/team/blackjack/domain/BlackjackGame.java +++ b/src/main/java/team/blackjack/domain/BlackjackGame.java @@ -1,11 +1,13 @@ package team.blackjack.domain; +import static team.blackjack.domain.rule.DefaultBlackjackRule.DEALER_STAND_SCORE; + import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import team.blackjack.domain.rule.DefaultBlackjackRule; public class BlackjackGame { + private final Dealer dealer; private final Players players; private final Deck deck; @@ -39,10 +41,33 @@ public double calculateDealerRevenue() { } private double calculateMoney(Player player) { - final Result result = DefaultBlackjackRule.judge(player, dealer); + final Result result = judge(player); return result.getOdds() * player.getBatMoney(); } + private Result judge(Player player) { + if (player.isBust()) { + return Result.LOSE; + } + if (dealer.isBust()) { + return Result.WIN; + } + + final int score = player.getScore(); + final int dealerScore = dealer.getScore(); + + if (score < dealerScore) { + return Result.LOSE; + } + if (player.isBlackjack() && !dealer.isBlackjack()) { + return Result.BLACKJACK; + } + if (score > dealerScore) { + return Result.WIN; + } + return Result.DRAW; + } + public void batMoney(String name, int money) { this.players.getPlayerByName(name).bat(money); } @@ -57,7 +82,7 @@ public void hitPlayer(String name) { } public boolean shouldDealerHit() { - return DefaultBlackjackRule.shouldDealerHit(this.dealer.getScore()); + return this.dealer.getScore() < DEALER_STAND_SCORE; } public void hitDealer() { diff --git a/src/main/java/team/blackjack/domain/Hand.java b/src/main/java/team/blackjack/domain/Hand.java index 9cb0c71a6ef..a2e199c9318 100644 --- a/src/main/java/team/blackjack/domain/Hand.java +++ b/src/main/java/team/blackjack/domain/Hand.java @@ -1,8 +1,13 @@ package team.blackjack.domain; +import static team.blackjack.domain.rule.DefaultBlackjackRule.BLACKJACK; +import static team.blackjack.domain.rule.DefaultBlackjackRule.BLACKJACK_CARD_COUNT; + import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import team.blackjack.domain.rule.DefaultBlackjackRule; public class Hand { @@ -23,16 +28,61 @@ public List getCardNames() { .toList(); } - public int getScore(){ - return DefaultBlackjackRule.calculateBestScore(this.getCards()); - } - public boolean isBust() { - return DefaultBlackjackRule.isBust(getScore()); + return getScore() > BLACKJACK; } public boolean isBlackjack() { - return DefaultBlackjackRule.isBlackjack(getScore(), cards.size()); + if (cards.size() != BLACKJACK_CARD_COUNT) { + return false; + } + return getScore() == BLACKJACK; + } + + + public int getScore() { + if (containsAceCard(cards)) { + final Map> result = cards.stream() + .collect(Collectors.partitioningBy(Card::isAce, Collectors.toSet())); + + final Set aceCards = result.get(true); + final Set nonAceCards = result.get(false); + return calculateBestSumWithAce(nonAceCards, aceCards); + } + return calculateBestSumWithoutAce(cards); + } + + private boolean containsAceCard(Set cards) { + return cards.stream() + .anyMatch(Card::isAce); } + private int calculateBestSumWithAce(Set cardsWithoutAces, Set aceCards) { + int currentSum = calculateBestSumWithoutAce(cardsWithoutAces); + + for (Card card : aceCards) { + currentSum += aceScore(card, currentSum); + } + + return currentSum; + } + + private int calculateBestSumWithoutAce(Set cards) { + return cards.stream() + .flatMap(card -> card.getScore().stream()) + .mapToInt(Integer::intValue) + .sum(); + } + + private int aceScore(Card card, int currentSum) { + if (currentSum <= 10) { + return card.getScore().stream() + .max(Integer::compareTo) + .orElseThrow(() -> new IllegalStateException("ACE 카드의 점수가 존재하지 않습니다.")); + } + + return card.getScore().stream() + .min(Integer::compareTo) + .orElseThrow(() -> new IllegalStateException("ACE 카드의 점수가 존재하지 않습니다.")); + } } diff --git a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java index 1709b937afc..07fff7f8c0b 100644 --- a/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java +++ b/src/main/java/team/blackjack/domain/rule/DefaultBlackjackRule.java @@ -1,115 +1,11 @@ package team.blackjack.domain.rule; -import static team.blackjack.domain.Result.BLACKJACK; - -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import team.blackjack.domain.Card; -import team.blackjack.domain.Participant; -import team.blackjack.domain.Result; - public class DefaultBlackjackRule { - private static final int BLACKJACK = 21; - private static final int BLACKJACK_CARD_COUNT = 2; - private static final int DEALER_STAND_SCORE = 17; + public static final int DEALER_STAND_SCORE = 17; + public static final int BLACKJACK = 21; + public static final int BLACKJACK_CARD_COUNT = 2; private DefaultBlackjackRule() { } - public static boolean isBust(int score) { - return score > BLACKJACK; - } - - public static boolean isBlackjack(int score, int cardCount) { - if (cardCount != BLACKJACK_CARD_COUNT) { - return false; - } - return score == BLACKJACK; - } - - public static boolean shouldDealerHit(int score) { - return score < DEALER_STAND_SCORE; - } - - public static boolean canUseAceAsEleven(int currentSum) { - return currentSum <= 10; - } - - public static Result judge(Participant me, Participant target) { - if (me.isBust()) { - return Result.LOSE; - } - - if (target.isBust()) { - return Result.WIN; - } - - final int score = me.getScore(); - final int targetScore = target.getScore(); - - if (score < targetScore) { - return Result.LOSE; - } - - if (me.isBlackjack() && !target.isBlackjack()) { - return Result.BLACKJACK; - } - - if (score > targetScore) { - return Result.WIN; - } - - return Result.DRAW; - } - - public static int calculateBestScore(List cards) { - if (existAceInCards(cards)) { - final Map> result = cards.stream() - .collect(Collectors.partitioningBy(Card::isAce)); - - final List aceCards = result.get(true); - final List nonAceCards = result.get(false); - return calculateBestSumWithAce(nonAceCards, aceCards); - } - return calculateBestSumWithoutAce(cards); - } - - private static int calculateBestSumWithAce(List cardsWithoutAces, List aceCards) { - int currentSum = calculateBestSumWithoutAce(cardsWithoutAces); - - for (Card card : aceCards) { - currentSum += aceScore(card, currentSum); - } - - return currentSum; - } - - private static int aceScore(Card card, int currentSum) { - if (DefaultBlackjackRule.canUseAceAsEleven(currentSum)) { - return card.getScore().stream() - .max(Integer::compareTo) - .orElseThrow(() -> new IllegalStateException("ACE 카드의 점수가 존재하지 않습니다.")); - } - - return card.getScore().stream() - .min(Integer::compareTo) - .orElseThrow(() -> new IllegalStateException("ACE 카드의 점수가 존재하지 않습니다.")); - } - - private static int calculateBestSumWithoutAce(List cards) { - return cards.stream() - .flatMap(card -> card.getScore().stream()) - .mapToInt(Integer::intValue) - .sum(); - } - - private static boolean existAceInCards(List cards) { - for (Card card : cards) { - if (card.isAce()) { - return true; - } - } - return false; - } } diff --git a/src/test/java/team/blackjack/domain/BlackjackGameTest.java b/src/test/java/team/blackjack/domain/BlackjackGameTest.java index f67f82dba3a..8c3b77c2ec5 100644 --- a/src/test/java/team/blackjack/domain/BlackjackGameTest.java +++ b/src/test/java/team/blackjack/domain/BlackjackGameTest.java @@ -1,7 +1,6 @@ package team.blackjack.domain; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.util.List; import java.util.Map; @@ -63,6 +62,88 @@ void setUp() { assertThat(game.calculateDealerRevenue()).isEqualTo(expectedDealerRevenue); } + @Test + void 플레이어_승_시_수익은_100프로() { + hitCards(pobi, Card.KING_OF_HEARTS, Card.QUEEN_OF_HEARTS); // 20 + hitCards(dealer, Card.KING_OF_CLUBS, Card.EIGHT_OF_CLUBS); // 18 + game.batMoney("pobi", 1000); + + Map revenue = game.calculateAllPlayerRevenue(); + + assertThat(revenue.get("pobi")).isEqualTo(1000.0); // WIN odds 1 + } + + @Test + void 플레이어_패_시_수익은_전체_마이너스_100프로() { + hitCards(pobi, Card.KING_OF_HEARTS, Card.EIGHT_OF_HEARTS); // 18 + hitCards(dealer, Card.KING_OF_CLUBS, Card.QUEEN_OF_CLUBS); // 20 + game.batMoney("pobi", 1000); + + Map revenue = game.calculateAllPlayerRevenue(); + + assertThat(revenue.get("pobi")).isEqualTo(-1000.0); // LOSE odds -1 + } + + @Test + void 동점_시_무_수익은_제로() { + hitCards(pobi, Card.KING_OF_HEARTS, Card.SEVEN_OF_HEARTS); // 17 + hitCards(dealer, Card.KING_OF_CLUBS, Card.SEVEN_OF_CLUBS); // 17 + game.batMoney("pobi", 1000); + + Map revenue = game.calculateAllPlayerRevenue(); + + assertThat(revenue.get("pobi")).isEqualTo(0.0); // DRAW odds 0 + } + + @Test + void 플레이어_버스트_할_경우_수익은_전체_손실() { + hitCards(pobi, Card.KING_OF_HEARTS, Card.SEVEN_OF_HEARTS, Card.FIVE_OF_HEARTS); // 22 + hitCards(dealer, Card.KING_OF_CLUBS, Card.QUEEN_OF_CLUBS); // 20 + game.batMoney("pobi", 1000); + + Map revenue = game.calculateAllPlayerRevenue(); + + assertThat(revenue.get("pobi")).isEqualTo(-1000.0); // LOSE (bust) + } + + @Test + void 플레이어_블랙잭_딜러는_블랙잭이_아닌경우_수익_150프로() { + hitCards(pobi, Card.ACE_OF_HEARTS, Card.KING_OF_HEARTS); // blackjack + hitCards(dealer, Card.KING_OF_CLUBS, Card.NINE_OF_CLUBS); // 19 + game.batMoney("pobi", 1000); + + Map revenue = game.calculateAllPlayerRevenue(); + + assertThat(revenue.get("pobi")).isEqualTo(1500.0); // BLACKJACK odds 1.5 + } + + @Test + void 딜러_버스트_시_버스트가_아닌_플레이어는_승리() { + hitCards(pobi, Card.KING_OF_HEARTS, Card.TEN_OF_HEARTS); // 20 + hitCards(dealer, Card.KING_OF_CLUBS, Card.SEVEN_OF_CLUBS, Card.SIX_OF_CLUBS); // 23 + game.batMoney("pobi", 1000); + + Map revenue = game.calculateAllPlayerRevenue(); + + assertThat(revenue.get("pobi")).isEqualTo(1000.0); // WIN (dealer bust) + } + + @Test + void 딜러가_버스트_여도_이미_버스트였던_플레이어는_패배() { + hitCards(jason, Card.KING_OF_SPADES,Card.KING_OF_DIAMONDS, Card.FIVE_OF_HEARTS); // 25 + hitCards(dealer, Card.KING_OF_CLUBS, Card.SEVEN_OF_CLUBS, Card.SIX_OF_CLUBS); // 23 + game.batMoney("jason", 1000); + + Map revenue = game.calculateAllPlayerRevenue(); + + assertThat(revenue.get("jason")).isEqualTo(-1000.0); // WIN (dealer bust) + } + + private void hitCards(Participant participant, Card... cards) { + for (Card card : cards) { + participant.hit(card); + } + } @Test void _30과_20에_대한_버스트_여부를_반환한다() { diff --git a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java b/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java deleted file mode 100644 index b529f14e0bb..00000000000 --- a/src/test/java/team/blackjack/domain/rule/DefaultBlackjackRuleTest.java +++ /dev/null @@ -1,137 +0,0 @@ -package team.blackjack.domain.rule; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.List; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import team.blackjack.domain.Card; -import team.blackjack.domain.Dealer; -import team.blackjack.domain.Participant; -import team.blackjack.domain.Player; -import team.blackjack.domain.Result; - -class DefaultBlackjackRuleTest { - - @ParameterizedTest - @CsvSource({ - "22, true", - "21, false" - }) - void 버스트_판정(int handScore, boolean expected) { - boolean isBust = DefaultBlackjackRule.isBust(handScore); - assertEquals(expected, isBust); - } - - @ParameterizedTest - @CsvSource({ - "21, 2, true", - "21, 3, false", - "20, 2, false" - }) - void 블랙잭_판정(int handScore, int cardCount, boolean expected) { - boolean isBlackjack = DefaultBlackjackRule.isBlackjack(handScore, cardCount); - assertEquals(expected, isBlackjack); - } - - @ParameterizedTest - @CsvSource({ - "16, true", - "17, false" - }) - void 딜러_카드_추가_여부(int dealerScore, boolean expected) { - boolean shouldHit = DefaultBlackjackRule.shouldDealerHit(dealerScore); - assertEquals(expected, shouldHit); - } - - @Test - void 플레이어가_버스트면_패() { - Participant player = playerWithCards(Card.KING_OF_HEARTS, Card.SEVEN_OF_HEARTS, Card.FIVE_OF_HEARTS); // 22 - Participant target = dealerWithCards(Card.KING_OF_CLUBS, Card.TEN_OF_CLUBS); // 20 - - assertEquals(Result.LOSE, DefaultBlackjackRule.judge(player, target)); - } - - @Test - void 딜러가_버스트고_플레이어는_20점일경우_승() { - Participant player = playerWithCards(Card.KING_OF_HEARTS, Card.TEN_OF_HEARTS); // 20 - Participant dealer = dealerWithCards(Card.KING_OF_CLUBS, Card.SEVEN_OF_CLUBS, Card.SIX_OF_CLUBS); // 23 - - assertEquals(Result.WIN, DefaultBlackjackRule.judge(player, dealer)); - } - - @Test - void 버스트가_아닌_플레이어_점수가_높으면_승() { - Participant player = playerWithCards(Card.KING_OF_HEARTS, Card.QUEEN_OF_HEARTS); // 20 - Participant dealer = dealerWithCards(Card.KING_OF_CLUBS, Card.EIGHT_OF_CLUBS); // 18 - - assertEquals(Result.WIN, DefaultBlackjackRule.judge(player, dealer)); - } - - @Test - void 플레이어_점수가_낮으면_패() { - Participant player = playerWithCards(Card.KING_OF_HEARTS, Card.EIGHT_OF_HEARTS); // 18 - Participant dealer = dealerWithCards(Card.KING_OF_CLUBS, Card.QUEEN_OF_CLUBS); // 20 - - assertEquals(Result.LOSE, DefaultBlackjackRule.judge(player, dealer)); - } - - @Test - void 플레이어와_딜러_둘다_버스트가_아니고_동점이면_무() { - Participant player = playerWithCards(Card.KING_OF_HEARTS, Card.SEVEN_OF_HEARTS); // 17 - Participant dealer = dealerWithCards(Card.KING_OF_CLUBS, Card.SEVEN_OF_CLUBS); // 17 - - assertEquals(Result.DRAW, DefaultBlackjackRule.judge(player, dealer)); - } - - @Test - void 플레이어가_블랙잭_상대는_아니면_블랙잭_승() { - Participant player = playerWithCards(Card.ACE_OF_HEARTS, Card.KING_OF_HEARTS); // blackjack - Participant dealer = dealerWithCards(Card.KING_OF_CLUBS, Card.NINE_OF_CLUBS); // 19 - - assertEquals(Result.BLACKJACK, DefaultBlackjackRule.judge(player, dealer)); - } - - @ParameterizedTest - @CsvSource({ - "10, true", - "11, false" - }) - void Ace를_11로_사용_가능한지(int score, boolean expected) { - assertEquals(expected, DefaultBlackjackRule.canUseAceAsEleven(score)); - } - - @Test - void 숫자10과_6이후에_ACE가_2개_오는_경우_각각_1로_정상_해석되는지_테스트() { - List cards = List.of(Card.KING_OF_CLUBS, Card.SIX_OF_HEARTS, Card.ACE_OF_SPADES, Card.ACE_OF_HEARTS); - - int score = DefaultBlackjackRule.calculateBestScore(cards); - - assertEquals(18, score); - } - - @Test - void Ace가_1장있는_경우_최적의_합_정상_계산_테스트() { - List cards = List.of(Card.FIVE_OF_CLUBS, Card.FIVE_OF_DIAMONDS, Card.ACE_OF_SPADES); - int score = DefaultBlackjackRule.calculateBestScore(cards); - - assertEquals(21, score); - } - - private static Player playerWithCards(Card... cards) { - Player player = new Player("test"); - for (Card card : cards) { - player.hit(card); - } - return player; - } - - private static Dealer dealerWithCards(Card... cards) { - Dealer dealer = new Dealer(); - for (Card card : cards) { - dealer.hit(card); - } - return dealer; - } -} diff --git a/src/test/java/team/blackjack/service/BlackJackServiceTest.java b/src/test/java/team/blackjack/service/BlackJackServiceTest.java index 77e3db19a74..540413e63de 100644 --- a/src/test/java/team/blackjack/service/BlackJackServiceTest.java +++ b/src/test/java/team/blackjack/service/BlackJackServiceTest.java @@ -34,7 +34,7 @@ void setUp() { blackJackService.drawInitialCards(); List playerCards = blackJackService.findPlayerCardNamesByName(pobi); - assertThat(playerCards.getFirst()).hasSize(2); + assertThat(playerCards).hasSize(2); ScoreResult scoreResult = blackJackService.calculateAllParticipantScore(); assertThat(scoreResult.dealerCard()).hasSize(2); From 9cee12ad8cf1a806a455e7e81e186a02e9469466 Mon Sep 17 00:00:00 2001 From: "wonsang.choi" Date: Mon, 16 Mar 2026 11:17:42 +0900 Subject: [PATCH 50/50] =?UTF-8?q?refactor:=20do-while=EB=AC=B8=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team/blackjack/control/BlackJackController.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/team/blackjack/control/BlackJackController.java b/src/main/java/team/blackjack/control/BlackJackController.java index 1e48c395171..4404fe6a481 100644 --- a/src/main/java/team/blackjack/control/BlackJackController.java +++ b/src/main/java/team/blackjack/control/BlackJackController.java @@ -47,11 +47,12 @@ public void readAllPlayerBattingMoneyRetry(List playerNames) { } private int readPlayerBattingMoneyRetry(String playerName) { - int battingMoney = -1; + int battingMoney; - while (battingMoney <= 0) { + do { battingMoney = readPlayerBattingMoney(playerName); - } + } while (battingMoney <= 0); + return battingMoney; }