From b89e3fbc433867bd01202fb1f2074d40845e0033 Mon Sep 17 00:00:00 2001 From: rueun Date: Sun, 26 May 2024 13:05:01 +0900 Subject: [PATCH 01/20] =?UTF-8?q?docs:=20README.md=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?(=EA=B8=B0=EB=8A=A5=20=EB=AA=85=EC=84=B8=EC=84=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index a05a3c0ce7..d09cdb1f05 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,26 @@ 블랙잭 미션 저장소 ## [미션 리드미](https://github.com/talmood/private-mission-README/tree/main/%EB%AF%B8%EC%85%98%204%20-%20%EB%B8%94%EB%9E%99%EC%9E%AD) + + +## 기능 명세서 +- 카드 + - [ ] 카드는 조커를 제외한 52장의 카드로 구성된다. + - [ ] King, Queen, Jack은 각각 10점으로 계산한다. + - [ ] Ace는 1점 또는 11점으로 계산한다. + +- 딜러 + - [ ] 딜러는 처음에 받은 카드 2장의 합이 16점 이하이면 반드시 1장의 카드를 더 받아야 한다. + - [ ] 딜러는 17점 이상이면 추가로 카드를 받을 수 없다. + +- 플레이어 + - [ ] 플레이어는 카드의 합이 21점을 초과하면 패배한다. + - [ ] 플레이어는 카드의 합이 21점을 넘지 않으면 원하는 만큼 카드를 추가로 더 받을 수 있다. + - [ ] 플레이어는 게임 종료 시 딜러와 자신의 카드를 비교하여 승패를 가린다. + +- 게임 + - [ ] 딜러와 플레이어 중 카드의 합이 21에 가장 가까운 쪽이 승리한다. + - [ ] 딜러와 플레이어의 카드의 합이 같으면 무승부가 된다. + - [ ] 게임을 시작하면 딜러와 플레이어에게 각각 2장의 카드를 나눠준다. + - [ ] 게임을 종료하면 승패를 가린다. + - [ ] 게임을 다시 시작하거나 종료할 수 있다. From 5a4aec143aec73cfc00093605491d0938b1476ef Mon Sep 17 00:00:00 2001 From: rueun Date: Mon, 27 May 2024 10:45:11 +0900 Subject: [PATCH 02/20] =?UTF-8?q?feat:=20console,=20StringUtil=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=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/blackjack/utils/Console.java | 29 +++++++++++++++++++ src/main/java/blackjack/utils/StringUtil.java | 13 +++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/main/java/blackjack/utils/Console.java create mode 100644 src/main/java/blackjack/utils/StringUtil.java diff --git a/src/main/java/blackjack/utils/Console.java b/src/main/java/blackjack/utils/Console.java new file mode 100644 index 0000000000..2a2e57f022 --- /dev/null +++ b/src/main/java/blackjack/utils/Console.java @@ -0,0 +1,29 @@ +package blackjack.utils; + +import java.util.Scanner; + +public class Console { + + private static Scanner scanner; + + private Console() { + } + + public static String readLine() { + return getInstance().nextLine(); + } + + public static void close() { + if (scanner != null) { + scanner.close(); + scanner = null; + } + } + + private static Scanner getInstance() { + if (scanner == null) { + scanner = new Scanner(System.in); + } + return scanner; + } +} diff --git a/src/main/java/blackjack/utils/StringUtil.java b/src/main/java/blackjack/utils/StringUtil.java new file mode 100644 index 0000000000..80289805e1 --- /dev/null +++ b/src/main/java/blackjack/utils/StringUtil.java @@ -0,0 +1,13 @@ +package blackjack.utils; + +import java.util.Objects; + +public abstract class StringUtil { + + private StringUtil() { + } + + public static boolean isBlank(final String str) { + return Objects.isNull(str) || str.trim().isEmpty(); + } +} From 0c49ea666f351450e12e385d331f39f02a9e2a94 Mon Sep 17 00:00:00 2001 From: rueun Date: Mon, 27 May 2024 10:46:00 +0900 Subject: [PATCH 03/20] =?UTF-8?q?feat:=20Application.java=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=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/blackjack/Application.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/blackjack/Application.java diff --git a/src/main/java/blackjack/Application.java b/src/main/java/blackjack/Application.java new file mode 100644 index 0000000000..018c112d77 --- /dev/null +++ b/src/main/java/blackjack/Application.java @@ -0,0 +1,9 @@ +package blackjack; + + +public class Application { + + public static void main(String[] args) { + // TODO 구현 진행 + } +} From f031641e67e9fe9fbb4dba112d27ae0b9d0f3c69 Mon Sep 17 00:00:00 2001 From: rueun Date: Wed, 29 May 2024 00:16:13 +0900 Subject: [PATCH 04/20] =?UTF-8?q?feat:=20splitter=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/utils/CommaStringSplitter.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/blackjack/utils/CommaStringSplitter.java diff --git a/src/main/java/blackjack/utils/CommaStringSplitter.java b/src/main/java/blackjack/utils/CommaStringSplitter.java new file mode 100644 index 0000000000..2311912b07 --- /dev/null +++ b/src/main/java/blackjack/utils/CommaStringSplitter.java @@ -0,0 +1,21 @@ +package blackjack.utils; + +import java.util.Arrays; +import java.util.List; + +public class CommaStringSplitter { + private static final String DELIMITER = ","; + + private CommaStringSplitter() { + } + + public static List split(final String str) { + if (StringUtil.isBlank(str)) { + throw new IllegalArgumentException("split string must not be blank"); + } + + return Arrays.stream(str.split(DELIMITER)) + .map(String::trim) + .toList(); + } +} From 0e8a45547fbe131dd23ec7c380a09bc4cf06e1ec Mon Sep 17 00:00:00 2001 From: rueun Date: Wed, 29 May 2024 00:35:04 +0900 Subject: [PATCH 05/20] =?UTF-8?q?feat:=20card=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/card/Card.java | 15 +++++++ src/main/java/blackjack/domain/card/Rank.java | 41 +++++++++++++++++++ src/main/java/blackjack/domain/card/Suit.java | 27 ++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 src/main/java/blackjack/domain/card/Card.java create mode 100644 src/main/java/blackjack/domain/card/Rank.java create mode 100644 src/main/java/blackjack/domain/card/Suit.java diff --git a/src/main/java/blackjack/domain/card/Card.java b/src/main/java/blackjack/domain/card/Card.java new file mode 100644 index 0000000000..77aba288af --- /dev/null +++ b/src/main/java/blackjack/domain/card/Card.java @@ -0,0 +1,15 @@ +package blackjack.domain.card; + +public class Card { + private final Suit suit; + private final Rank rank; + + private Card(final Suit suit, final Rank rank) { + this.suit = suit; + this.rank = rank; + } + + public static Card of(final String suit, final String rank) { + return new Card(Suit.of(suit), Rank.of(rank)); + } +} diff --git a/src/main/java/blackjack/domain/card/Rank.java b/src/main/java/blackjack/domain/card/Rank.java new file mode 100644 index 0000000000..9417c0282b --- /dev/null +++ b/src/main/java/blackjack/domain/card/Rank.java @@ -0,0 +1,41 @@ +package blackjack.domain.card; + +public enum Rank { + ACE("A", 1), + TWO("2", 2), + THREE("3", 3), + FOUR("4", 4), + FIVE("5", 5), + SIX("6", 6), + SEVEN("7", 7), + EIGHT("8", 8), + NINE("9", 9), + TEN("10", 10), + JACK("J", 10), + QUEEN("Q", 10), + KING("K", 10); + + private final String name; + private final int score; + + Rank(final String name, final int score) { + this.name = name; + this.score = score; + } + + public static Rank of(final String name) { + return Rank.valueOf(name); + } + + public String getName() { + return name; + } + + public int getScore() { + return score; + } + + public boolean isAce() { + return this == ACE; + } +} diff --git a/src/main/java/blackjack/domain/card/Suit.java b/src/main/java/blackjack/domain/card/Suit.java new file mode 100644 index 0000000000..12e53f34a2 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Suit.java @@ -0,0 +1,27 @@ +package blackjack.domain.card; + +import java.util.Arrays; + +public enum Suit { + SPADE("스페이드"), + HEART("하트"), + DIAMOND("다이아몬드"), + COLVER("클로버"); + + private final String name; + + Suit(final String name) { + this.name = name; + } + + public static Suit of(final String name) { + return Arrays.stream(Suit.values()) + .filter(suit -> suit.name.equals(name)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 카드 모양입니다.")); + } + + public String getName() { + return name; + } +} From 7c9dc566a8754fc10d11877f709bf9312eb913c1 Mon Sep 17 00:00:00 2001 From: rueun Date: Wed, 29 May 2024 01:12:59 +0900 Subject: [PATCH 06/20] =?UTF-8?q?feat:=20=EC=B9=B4=EB=93=9C=20Deck=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/card/Card.java | 26 +++++++ src/main/java/blackjack/domain/card/Deck.java | 69 +++++++++++++++++++ .../java/blackjack/domain/card/DeckTest.java | 34 +++++++++ 3 files changed, 129 insertions(+) create mode 100644 src/main/java/blackjack/domain/card/Deck.java create mode 100644 src/test/java/blackjack/domain/card/DeckTest.java diff --git a/src/main/java/blackjack/domain/card/Card.java b/src/main/java/blackjack/domain/card/Card.java index 77aba288af..845c5b4631 100644 --- a/src/main/java/blackjack/domain/card/Card.java +++ b/src/main/java/blackjack/domain/card/Card.java @@ -1,5 +1,7 @@ package blackjack.domain.card; +import java.util.Objects; + public class Card { private final Suit suit; private final Rank rank; @@ -12,4 +14,28 @@ private Card(final Suit suit, final Rank rank) { public static Card of(final String suit, final String rank) { return new Card(Suit.of(suit), Rank.of(rank)); } + + public static Card of(final Suit suit, final Rank rank) { + return new Card(suit, rank); + } + + public Suit getSuit() { + return suit; + } + + public Rank getRank() { + return rank; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Card card)) return false; + return suit == card.suit && rank == card.rank; + } + + @Override + public int hashCode() { + return Objects.hash(suit, rank); + } } diff --git a/src/main/java/blackjack/domain/card/Deck.java b/src/main/java/blackjack/domain/card/Deck.java new file mode 100644 index 0000000000..b90fced1e9 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Deck.java @@ -0,0 +1,69 @@ +package blackjack.domain.card; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class Deck { + private static final int DECK_SIZE = 52; + private static final String DECK_EMPTY_MESSAGE = "덱이 비었습니다."; + private static final String DECK_SIZE_MESSAGE = String.format("덱의 크기는 %d 입니다.", DECK_SIZE); + private static final String DUPLICATE_CARD_MESSAGE = "중복된 카드가 존재합니다."; + + private final List cards; + + private Deck() { + final List initializedCards = initialize(); + validate(initializedCards); + this.cards = initializedCards; + } + + public static Deck create() { + // TODO: 5/29/24 deck 생성 시, 셔플 작업 추가 + return new Deck(); + } + + private static List initialize() { + return Arrays.stream(Suit.values()) + .flatMap(suit -> Arrays.stream(Rank.values()) + .map(rank -> Card.of(suit, rank))) + .collect(Collectors.toList()); + } + + private static void validate(final List cards) { + if (cards.isEmpty()) { + throw new IllegalArgumentException(DECK_EMPTY_MESSAGE); + } + if (cards.size() != DECK_SIZE) { + throw new IllegalArgumentException(DECK_SIZE_MESSAGE); + } + if (cards.stream().distinct().count() != DECK_SIZE) { + throw new IllegalArgumentException(DUPLICATE_CARD_MESSAGE); + } + } + + public List getCards() { + return List.copyOf(cards); + } + + + /** + * 초기 카드 2장을 뽑고, 덱에서 제거 + * @return List 초기 카드 2장 + */ + public List drawInitialCards() { + return List.of(draw(), draw()); + } + + /** + * 카드를 뽑고, 덱에서 제거 + * @return Card 뽑은 카드 + */ + public Card draw() { + if (cards.isEmpty()) { + throw new IllegalArgumentException(DECK_EMPTY_MESSAGE); + } + // 마지막 카드를 뽑아내고, 덱에서 제거 + return cards.remove(cards.size() - 1); + } +} diff --git a/src/test/java/blackjack/domain/card/DeckTest.java b/src/test/java/blackjack/domain/card/DeckTest.java new file mode 100644 index 0000000000..7b997ef03f --- /dev/null +++ b/src/test/java/blackjack/domain/card/DeckTest.java @@ -0,0 +1,34 @@ +package blackjack.domain.card; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class DeckTest { + + @DisplayName("덱을 생성한다.") + @Test + void create() { + final Deck deck = Deck.create(); + System.out.println(deck); + assertEquals(52, deck.getCards().size()); + } + + @DisplayName("초기 카드 2장을 뽑고, 덱에서 제거") + @Test + void drawInitialCards() { + final Deck deck = Deck.create(); + assertEquals(2, deck.drawInitialCards().size()); + } + + @DisplayName("카드를 뽑고, 덱에서 제거") + @Test + void draw() { + final Deck deck = Deck.create(); + final Card card = deck.draw(); + assertEquals(Suit.CLOVER, card.getSuit()); + assertEquals(Rank.KING, card.getRank()); + assertEquals(51, deck.getCards().size()); + } +} \ No newline at end of file From 16e422eec54607b8ce00937f6a7d2c06987eb8d4 Mon Sep 17 00:00:00 2001 From: rueun Date: Wed, 29 May 2024 01:13:34 +0900 Subject: [PATCH 07/20] =?UTF-8?q?fix:=20=ED=81=B4=EB=A1=9C=EB=B2=84=20enum?= =?UTF-8?q?=20=EA=B0=92=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/blackjack/domain/card/Suit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/blackjack/domain/card/Suit.java b/src/main/java/blackjack/domain/card/Suit.java index 12e53f34a2..67860ac2e0 100644 --- a/src/main/java/blackjack/domain/card/Suit.java +++ b/src/main/java/blackjack/domain/card/Suit.java @@ -6,7 +6,7 @@ public enum Suit { SPADE("스페이드"), HEART("하트"), DIAMOND("다이아몬드"), - COLVER("클로버"); + CLOVER("클로버"); private final String name; From 80b09ce148cc73aa592b9dba6e56772d660843d1 Mon Sep 17 00:00:00 2001 From: rueun Date: Wed, 29 May 2024 01:42:40 +0900 Subject: [PATCH 08/20] =?UTF-8?q?feat:=20cards=20=EC=9D=BC=EA=B8=89=20?= =?UTF-8?q?=EC=BB=AC=EB=A0=89=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/blackjack/domain/card/Cards.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/blackjack/domain/card/Cards.java diff --git a/src/main/java/blackjack/domain/card/Cards.java b/src/main/java/blackjack/domain/card/Cards.java new file mode 100644 index 0000000000..4a4fa7a2b2 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Cards.java @@ -0,0 +1,24 @@ +package blackjack.domain.card; + +import java.util.ArrayList; +import java.util.List; + +public class Cards { + private static final int INITIAL_CARDS_SIZE = 2; + private final List cards; + + public Cards() { + this.cards = new ArrayList<>(); + } + + public void receiveInitialCards(final List cards) { + validateInitialCards(cards); + this.cards.addAll(cards); + } + + private void validateInitialCards(final List cards) { + if (cards.size() != INITIAL_CARDS_SIZE) { + throw new IllegalArgumentException("초기 카드는 %d장 이어야 합니다.".formatted(INITIAL_CARDS_SIZE)); + } + } +} From ebca59ba31c5c3c9df4feb99f2fe48b765c15d22 Mon Sep 17 00:00:00 2001 From: rueun Date: Wed, 29 May 2024 01:50:50 +0900 Subject: [PATCH 09/20] =?UTF-8?q?feat:=20Name,=20Participant=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain/participant/Name.java | 38 +++++++++++++++++++ .../domain/participant/Participant.java | 10 +++++ 2 files changed, 48 insertions(+) create mode 100644 src/main/java/blackjack/domain/participant/Name.java create mode 100644 src/main/java/blackjack/domain/participant/Participant.java diff --git a/src/main/java/blackjack/domain/participant/Name.java b/src/main/java/blackjack/domain/participant/Name.java new file mode 100644 index 0000000000..a733bfbcdf --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Name.java @@ -0,0 +1,38 @@ +package blackjack.domain.participant; + +import java.util.Objects; + +public class Name { + private final String name; + + private Name(final String name) { + validate(name); + this.name = name; + } + + public static Name of(final String name) { + return new Name(name); + } + + private static void validate(final String name) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("이름은 null 이거나 빈 문자열일 수 없습니다."); + } + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Name name1)) return false; + return Objects.equals(getName(), name1.getName()); + } + + @Override + public int hashCode() { + return Objects.hash(getName()); + } +} diff --git a/src/main/java/blackjack/domain/participant/Participant.java b/src/main/java/blackjack/domain/participant/Participant.java new file mode 100644 index 0000000000..577866fce5 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -0,0 +1,10 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Cards; + +public abstract class Participant { + private Cards cards; + private Name name; + + +} From 18497927844e4cfcd51b1fa237a4ad8810944044 Mon Sep 17 00:00:00 2001 From: rueun Date: Wed, 29 May 2024 11:29:20 +0900 Subject: [PATCH 10/20] =?UTF-8?q?feat:=20InputView=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/blackjack/view/InputView.java | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/blackjack/view/InputView.java diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java new file mode 100644 index 0000000000..7f5c1eeae6 --- /dev/null +++ b/src/main/java/blackjack/view/InputView.java @@ -0,0 +1,33 @@ +package blackjack.view; + +import blackjack.utils.CommaStringSplitter; +import blackjack.utils.Console; + +import java.util.List; + +public class InputView { + private static final String INPUT_PLAYER_NAME = "게임에 참여할 사람의 이름을 입력하세요. (쉼표 기준으로 분리)"; + private static final String ANSWER_YES = "y"; + private static final String ANSWER_NO = "n"; + private static final String NOT_YES_OR_NO = "y 또는 n 중 하나를 입력해주세요."; + + public List inputPlayerNames() { + System.out.println(INPUT_PLAYER_NAME); + final List names = CommaStringSplitter.split(Console.readLine()); + System.out.println(); + return names; + } + + public boolean inputMoreCard(final String name) { + System.out.printf("%s는 카드를 더 받겠습니까? (예는 y, 아니오는 n)%n", name); + final String answer = Console.readLine(); + if (!isYesOrNo(answer)) { + throw new IllegalArgumentException(NOT_YES_OR_NO); + } + return ANSWER_YES.equalsIgnoreCase(answer); + } + + private boolean isYesOrNo(final String answer) { + return ANSWER_YES.equalsIgnoreCase(answer) || ANSWER_NO.equalsIgnoreCase(answer); + } +} From 84bc97f15ce61e1f666297eb5953816cc9013ae2 Mon Sep 17 00:00:00 2001 From: rueun Date: Sun, 2 Jun 2024 12:23:29 +0900 Subject: [PATCH 11/20] =?UTF-8?q?docs:=20README.md=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d09cdb1f05..c01babb7ff 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ - [ ] 플레이어는 게임 종료 시 딜러와 자신의 카드를 비교하여 승패를 가린다. - 게임 - - [ ] 딜러와 플레이어 중 카드의 합이 21에 가장 가까운 쪽이 승리한다. - - [ ] 딜러와 플레이어의 카드의 합이 같으면 무승부가 된다. + - [ ] 딜러와 플레이어 중 카드의 합이 21을 초과하지 않으면서 21에 가장 가까운 쪽이 승리한다. + - [ ] 딜러와 플레이어의 카드의 합이 같으면 딜러가 승리한다. + - [ ] 딜러와 플레이어가 블랙잭인 경우 무승부로 처리한다. - [ ] 게임을 시작하면 딜러와 플레이어에게 각각 2장의 카드를 나눠준다. - [ ] 게임을 종료하면 승패를 가린다. - - [ ] 게임을 다시 시작하거나 종료할 수 있다. From 774e264bd0d41dd9060463cf8159bba9d8f419ae Mon Sep 17 00:00:00 2001 From: rueun Date: Sun, 2 Jun 2024 23:30:34 +0900 Subject: [PATCH 12/20] =?UTF-8?q?feat:=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=85=94=ED=94=8C=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 --- .../domain/card/{Deck.java => CardDeck.java} | 14 +++++++++----- .../card/{DeckTest.java => CardDeckTest.java} | 18 +++++++++--------- 2 files changed, 18 insertions(+), 14 deletions(-) rename src/main/java/blackjack/domain/card/{Deck.java => CardDeck.java} (89%) rename src/test/java/blackjack/domain/card/{DeckTest.java => CardDeckTest.java} (55%) diff --git a/src/main/java/blackjack/domain/card/Deck.java b/src/main/java/blackjack/domain/card/CardDeck.java similarity index 89% rename from src/main/java/blackjack/domain/card/Deck.java rename to src/main/java/blackjack/domain/card/CardDeck.java index b90fced1e9..83eefee0f9 100644 --- a/src/main/java/blackjack/domain/card/Deck.java +++ b/src/main/java/blackjack/domain/card/CardDeck.java @@ -1,10 +1,11 @@ package blackjack.domain.card; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -public class Deck { +public class CardDeck { private static final int DECK_SIZE = 52; private static final String DECK_EMPTY_MESSAGE = "덱이 비었습니다."; private static final String DECK_SIZE_MESSAGE = String.format("덱의 크기는 %d 입니다.", DECK_SIZE); @@ -12,15 +13,14 @@ public class Deck { private final List cards; - private Deck() { + private CardDeck() { final List initializedCards = initialize(); validate(initializedCards); this.cards = initializedCards; } - public static Deck create() { - // TODO: 5/29/24 deck 생성 시, 셔플 작업 추가 - return new Deck(); + public static CardDeck create() { + return new CardDeck(); } private static List initialize() { @@ -42,6 +42,10 @@ private static void validate(final List cards) { } } + public void shuffle() { + Collections.shuffle(cards); + } + public List getCards() { return List.copyOf(cards); } diff --git a/src/test/java/blackjack/domain/card/DeckTest.java b/src/test/java/blackjack/domain/card/CardDeckTest.java similarity index 55% rename from src/test/java/blackjack/domain/card/DeckTest.java rename to src/test/java/blackjack/domain/card/CardDeckTest.java index 7b997ef03f..ce89a301b4 100644 --- a/src/test/java/blackjack/domain/card/DeckTest.java +++ b/src/test/java/blackjack/domain/card/CardDeckTest.java @@ -5,30 +5,30 @@ import static org.junit.jupiter.api.Assertions.*; -class DeckTest { +class CardDeckTest { @DisplayName("덱을 생성한다.") @Test void create() { - final Deck deck = Deck.create(); - System.out.println(deck); - assertEquals(52, deck.getCards().size()); + final CardDeck cardDeck = CardDeck.create(); + System.out.println(cardDeck); + assertEquals(52, cardDeck.getCards().size()); } @DisplayName("초기 카드 2장을 뽑고, 덱에서 제거") @Test void drawInitialCards() { - final Deck deck = Deck.create(); - assertEquals(2, deck.drawInitialCards().size()); + final CardDeck cardDeck = CardDeck.create(); + assertEquals(2, cardDeck.drawInitialCards().size()); } @DisplayName("카드를 뽑고, 덱에서 제거") @Test void draw() { - final Deck deck = Deck.create(); - final Card card = deck.draw(); + final CardDeck cardDeck = CardDeck.create(); + final Card card = cardDeck.draw(); assertEquals(Suit.CLOVER, card.getSuit()); assertEquals(Rank.KING, card.getRank()); - assertEquals(51, deck.getCards().size()); + assertEquals(51, cardDeck.getCards().size()); } } \ No newline at end of file From 00e408768cb31903f4e4edff320259c34c893bb7 Mon Sep 17 00:00:00 2001 From: rueun Date: Sun, 2 Jun 2024 23:32:13 +0900 Subject: [PATCH 13/20] =?UTF-8?q?feat:=20Participant=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain/participant/Dealer.java | 12 +++++++ .../domain/participant/Participant.java | 22 ++++++++++-- .../blackjack/domain/participant/Player.java | 12 +++++++ .../blackjack/domain/participant/Players.java | 34 +++++++++++++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 src/main/java/blackjack/domain/participant/Dealer.java create mode 100644 src/main/java/blackjack/domain/participant/Player.java create mode 100644 src/main/java/blackjack/domain/participant/Players.java diff --git a/src/main/java/blackjack/domain/participant/Dealer.java b/src/main/java/blackjack/domain/participant/Dealer.java new file mode 100644 index 0000000000..908485fcca --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Dealer.java @@ -0,0 +1,12 @@ +package blackjack.domain.participant; + +public class Dealer extends Participant { + + private Dealer(final Name name) { + super(name); + } + + public static Dealer create() { + return new Dealer(Name.of("딜러")); + } +} diff --git a/src/main/java/blackjack/domain/participant/Participant.java b/src/main/java/blackjack/domain/participant/Participant.java index 577866fce5..919fee963e 100644 --- a/src/main/java/blackjack/domain/participant/Participant.java +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -1,10 +1,28 @@ package blackjack.domain.participant; +import blackjack.domain.card.CardDeck; import blackjack.domain.card.Cards; +import java.util.ArrayList; + public abstract class Participant { - private Cards cards; - private Name name; + protected Name name; + protected Cards cards; + + protected Participant(final Name name) { + this.name = name; + this.cards = Cards.of(new ArrayList<>()); + } + + public void drawInitialCards(final CardDeck cardDeck) { + cardDeck.drawInitialCards().forEach(cards::add); + } + public String getName() { + return name.getName(); + } + public Cards getCards() { + return cards; + } } diff --git a/src/main/java/blackjack/domain/participant/Player.java b/src/main/java/blackjack/domain/participant/Player.java new file mode 100644 index 0000000000..b02de6b363 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Player.java @@ -0,0 +1,12 @@ +package blackjack.domain.participant; + +public class Player extends Participant { + + private Player(final Name name) { + super(name); + } + + public static Player of(final Name name) { + return new Player(name); + } +} diff --git a/src/main/java/blackjack/domain/participant/Players.java b/src/main/java/blackjack/domain/participant/Players.java new file mode 100644 index 0000000000..95818f3965 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Players.java @@ -0,0 +1,34 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.CardDeck; + +import java.util.List; + +public class Players { + private final List players; + + private Players(final List players) { + if (players.isEmpty()) { + throw new IllegalArgumentException("플레이어는 최소 한 명 이상이어야 합니다."); + } + this.players = players; + } + + public static Players of(final List players) { + return new Players(players); + } + + public void drawInitialCards(final CardDeck cardDeck) { + players.forEach(player -> player.drawInitialCards(cardDeck)); + } + + public List getPlayers() { + return players; + } + + public List getPlayerNames() { + return players.stream() + .map(Player::getName) + .toList(); + } +} From ad60b202ee2f37bd003e252418f5b366b4a9e862 Mon Sep 17 00:00:00 2001 From: rueun Date: Sun, 2 Jun 2024 23:35:07 +0900 Subject: [PATCH 14/20] =?UTF-8?q?feat:=20InputView.java=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=20=EC=B9=B4=EB=93=9C=20=EC=B6=9C=EB=A0=A5=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/view/InputView.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java index 7f5c1eeae6..7e772c3448 100644 --- a/src/main/java/blackjack/view/InputView.java +++ b/src/main/java/blackjack/view/InputView.java @@ -9,6 +9,7 @@ public class InputView { private static final String INPUT_PLAYER_NAME = "게임에 참여할 사람의 이름을 입력하세요. (쉼표 기준으로 분리)"; private static final String ANSWER_YES = "y"; private static final String ANSWER_NO = "n"; + private static final String ADDITIONAL_DRAW_CARD = "%s는 한장의 카드를 더 받겠습니까? (예는 y, 아니오는 n)"; private static final String NOT_YES_OR_NO = "y 또는 n 중 하나를 입력해주세요."; public List inputPlayerNames() { @@ -18,11 +19,12 @@ public List inputPlayerNames() { return names; } - public boolean inputMoreCard(final String name) { - System.out.printf("%s는 카드를 더 받겠습니까? (예는 y, 아니오는 n)%n", name); + public boolean inputMoreCard(final String playerName) { + System.out.println(ADDITIONAL_DRAW_CARD.formatted(playerName)); final String answer = Console.readLine(); if (!isYesOrNo(answer)) { - throw new IllegalArgumentException(NOT_YES_OR_NO); + System.out.println(NOT_YES_OR_NO); + return inputMoreCard(playerName); } return ANSWER_YES.equalsIgnoreCase(answer); } From 50394858fdd71b29a4f8a9a6a36871a755620bec Mon Sep 17 00:00:00 2001 From: rueun Date: Sun, 2 Jun 2024 23:35:27 +0900 Subject: [PATCH 15/20] =?UTF-8?q?feat:=20=EC=B4=88=EA=B8=B0=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EC=B6=9C=EB=A0=A5=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=802?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/view/ResultView.java | 50 ++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/main/java/blackjack/view/ResultView.java diff --git a/src/main/java/blackjack/view/ResultView.java b/src/main/java/blackjack/view/ResultView.java new file mode 100644 index 0000000000..b40da720fb --- /dev/null +++ b/src/main/java/blackjack/view/ResultView.java @@ -0,0 +1,50 @@ +package blackjack.view; + +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Participant; +import blackjack.domain.participant.Players; + +import java.util.List; + +public class ResultView { + private static final String SHARE_INITIAL_CARDS = "딜러와 %s에게 2장을 나누었습니다."; + private static final String PLAYER_DRAW_CARD = "%s는 카드를 더 받겠습니까? (예는 y, 아니오는 n)"; + private static final String DEALER_DRAW_CARD = "딜러는 16이하라 한장의 카드를 더 받았습니다."; + private static final String CARD_INFO = "%s 카드: %s"; + + public static void printShareInitialCards(final Players players, final Dealer dealer) { + final String playerNames = String.join(", ", players.getPlayerNames()); + System.out.println(String.format(SHARE_INITIAL_CARDS, playerNames)); + + printDealerInitialCards(dealer); + printPlayerInitialCards(players); + } + + private static void printDealerInitialCards(final Dealer dealer) { + final String cardsInfo = dealer.getCards().showCardsInfo().get(0); + System.out.println(String.format(CARD_INFO, dealer.getName(), cardsInfo)); + } + + private static void printPlayerInitialCards(final Players players) { + for (Participant player : players.getPlayers()) { + final String cardsInfo = String.join(", ", player.getCards().showCardsInfo()); + System.out.println(String.format(CARD_INFO, player.getName(), cardsInfo)); + } + } + + public static void printPlayerDrawCard(final String playerName) { + System.out.println(String.format(PLAYER_DRAW_CARD, playerName)); + } + + public static void printDealerDrawCard() { + System.out.println(DEALER_DRAW_CARD); + } + + public static void printResult(final List participants) { + System.out.println("## 최종 승패"); + for (Participant participant : participants) { + System.out.println(participant); + } + + } +} From d44f6b32a089fdc13f33e8e98c88fd987126a15a Mon Sep 17 00:00:00 2001 From: rueun Date: Sun, 2 Jun 2024 23:36:19 +0900 Subject: [PATCH 16/20] =?UTF-8?q?feat:=20game=20Controller=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/Application.java | 5 ++- .../controller/BlackJackGameController.java | 37 +++++++++++++++++ .../blackjack/domain/game/BlackJackGame.java | 41 +++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 src/main/java/blackjack/controller/BlackJackGameController.java create mode 100644 src/main/java/blackjack/domain/game/BlackJackGame.java diff --git a/src/main/java/blackjack/Application.java b/src/main/java/blackjack/Application.java index 018c112d77..4c4ae07f74 100644 --- a/src/main/java/blackjack/Application.java +++ b/src/main/java/blackjack/Application.java @@ -1,9 +1,12 @@ package blackjack; +import blackjack.controller.BlackJackGameController; + public class Application { public static void main(String[] args) { - // TODO 구현 진행 + final BlackJackGameController blackJackGameController = new BlackJackGameController(); + blackJackGameController.run(); } } diff --git a/src/main/java/blackjack/controller/BlackJackGameController.java b/src/main/java/blackjack/controller/BlackJackGameController.java new file mode 100644 index 0000000000..65840a7a1e --- /dev/null +++ b/src/main/java/blackjack/controller/BlackJackGameController.java @@ -0,0 +1,37 @@ +package blackjack.controller; + +import blackjack.domain.game.BlackJackGame; +import blackjack.domain.card.CardDeck; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Name; +import blackjack.domain.participant.Player; +import blackjack.domain.participant.Players; +import blackjack.view.InputView; + +import java.util.List; +import java.util.stream.Collectors; + +public class BlackJackGameController { + + private final InputView inputView = new InputView(); + + public void run() { + final CardDeck cardDeck = CardDeck.create(); + cardDeck.shuffle(); + + final Dealer dealer = Dealer.create(); + final Players players = initPlayers(); + + final BlackJackGame blackJackGame = new BlackJackGame(cardDeck, players, dealer); + blackJackGame.play(); + } + + private Players initPlayers() { + final List playerNames = inputView.inputPlayerNames(); + final List players = playerNames.stream() + .map(name -> Player.of(Name.of(name))) + .collect(Collectors.toList()); + + return Players.of(players); + } +} diff --git a/src/main/java/blackjack/domain/game/BlackJackGame.java b/src/main/java/blackjack/domain/game/BlackJackGame.java new file mode 100644 index 0000000000..da659261aa --- /dev/null +++ b/src/main/java/blackjack/domain/game/BlackJackGame.java @@ -0,0 +1,41 @@ +package blackjack.domain.game; + +import blackjack.domain.card.CardDeck; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Players; +import blackjack.view.ResultView; + +public class BlackJackGame { + private final CardDeck cardDeck; + private final Players players; + private final Dealer dealer; + + public BlackJackGame(final CardDeck cardDeck, final Players players, final Dealer dealer) { + this.cardDeck = cardDeck; + this.players = players; + this.dealer = dealer; + } + + public void play() { + drawInitialCards(cardDeck); + playersTurn(); + dealerTurn(); + judge(); + } + + private void drawInitialCards(final CardDeck cardDeck) { + dealer.drawInitialCards(cardDeck); + players.drawInitialCards(cardDeck); + ResultView.printShareInitialCards(players, dealer); + } + + private void judge() { + } + + private void dealerTurn() { + } + + private void playersTurn() { + + } +} From c628c78b5ac1905e003b22b2faa6db380e64af03 Mon Sep 17 00:00:00 2001 From: rueun Date: Mon, 3 Jun 2024 00:06:46 +0900 Subject: [PATCH 17/20] =?UTF-8?q?feat:=20=EC=B9=B4=EB=93=9C=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20=EB=A1=9C=EC=A7=81=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/blackjack/domain/card/Card.java | 4 ++ .../java/blackjack/domain/card/Cards.java | 52 +++++++++++++++---- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/main/java/blackjack/domain/card/Card.java b/src/main/java/blackjack/domain/card/Card.java index 845c5b4631..e936d0a0c1 100644 --- a/src/main/java/blackjack/domain/card/Card.java +++ b/src/main/java/blackjack/domain/card/Card.java @@ -27,6 +27,10 @@ public Rank getRank() { return rank; } + public String showCardInfo() { + return rank.getName() + suit.getName(); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/blackjack/domain/card/Cards.java b/src/main/java/blackjack/domain/card/Cards.java index 4a4fa7a2b2..46c83af584 100644 --- a/src/main/java/blackjack/domain/card/Cards.java +++ b/src/main/java/blackjack/domain/card/Cards.java @@ -1,24 +1,54 @@ package blackjack.domain.card; -import java.util.ArrayList; import java.util.List; public class Cards { - private static final int INITIAL_CARDS_SIZE = 2; private final List cards; - public Cards() { - this.cards = new ArrayList<>(); + private Cards(final List cards) { + this.cards = cards; } - - public void receiveInitialCards(final List cards) { - validateInitialCards(cards); - this.cards.addAll(cards); + + public static Cards of(final List cards) { + return new Cards(cards); + } + + public void add(final Card card) { + cards.add(card); + } + + public List showCardsInfo() { + return cards.stream() + .map(Card::showCardInfo) + .toList(); + } + + public boolean isBlackjack() { + return calculateScore() == 21 && cards.size() == 2; } - private void validateInitialCards(final List cards) { - if (cards.size() != INITIAL_CARDS_SIZE) { - throw new IllegalArgumentException("초기 카드는 %d장 이어야 합니다.".formatted(INITIAL_CARDS_SIZE)); + public int calculateScore() { + + int totalScore = cards.stream() + .mapToInt(card -> card.getRank().getScore()) + .sum(); + + int aceCount = (int) cards.stream() + .map(Card::getRank) + .filter(Rank.ACE::equals) + .count(); + + return calculateAceScore(totalScore, aceCount); + } + + private int calculateAceScore(final int totalScore, int aceCount) { + int score = totalScore; + + while (aceCount > 0 && score + 10 <= 21) { + score += 10; + aceCount--; } + + return score; } } From 7f71b9c76eb52d8981457f2c292971d6aee61b74 Mon Sep 17 00:00:00 2001 From: rueun Date: Mon, 3 Jun 2024 00:07:10 +0900 Subject: [PATCH 18/20] =?UTF-8?q?feat:=20=EC=B9=B4=EB=93=9C=EB=A5=BC=20?= =?UTF-8?q?=EB=BD=91=EC=9D=84=20=EC=88=98=20=EC=9E=88=EB=8A=94=EC=A7=80=20?= =?UTF-8?q?=EC=B2=B4=ED=81=AC=ED=95=98=EB=8A=94=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/participant/Dealer.java | 7 +++++++ .../java/blackjack/domain/participant/Participant.java | 10 ++++++++++ src/main/java/blackjack/domain/participant/Player.java | 7 +++++++ 3 files changed, 24 insertions(+) diff --git a/src/main/java/blackjack/domain/participant/Dealer.java b/src/main/java/blackjack/domain/participant/Dealer.java index 908485fcca..7da533bc37 100644 --- a/src/main/java/blackjack/domain/participant/Dealer.java +++ b/src/main/java/blackjack/domain/participant/Dealer.java @@ -2,10 +2,17 @@ public class Dealer extends Participant { + private static final int DEALER_DRAW_THRESHOLD = 17; + private Dealer(final Name name) { super(name); } + @Override + public boolean canDrawCard() { + return cards.calculateScore() < DEALER_DRAW_THRESHOLD; + } + public static Dealer create() { return new Dealer(Name.of("딜러")); } diff --git a/src/main/java/blackjack/domain/participant/Participant.java b/src/main/java/blackjack/domain/participant/Participant.java index 919fee963e..83defd61a0 100644 --- a/src/main/java/blackjack/domain/participant/Participant.java +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -18,6 +18,16 @@ public void drawInitialCards(final CardDeck cardDeck) { cardDeck.drawInitialCards().forEach(cards::add); } + public void drawCard(final CardDeck cardDeck) { + cards.add(cardDeck.draw()); + } + + public boolean isBlackjack() { + return cards.isBlackjack(); + } + + public abstract boolean canDrawCard(); + public String getName() { return name.getName(); } diff --git a/src/main/java/blackjack/domain/participant/Player.java b/src/main/java/blackjack/domain/participant/Player.java index b02de6b363..f8dfebe582 100644 --- a/src/main/java/blackjack/domain/participant/Player.java +++ b/src/main/java/blackjack/domain/participant/Player.java @@ -2,10 +2,17 @@ public class Player extends Participant { + private static final int PLAYER_DRAW_THRESHOLD = 21; + private Player(final Name name) { super(name); } + @Override + public boolean canDrawCard() { + return cards.calculateScore() < PLAYER_DRAW_THRESHOLD; + } + public static Player of(final Name name) { return new Player(name); } From 963e373578a09d0c978b42a2f93b4bade9f9c3cd Mon Sep 17 00:00:00 2001 From: rueun Date: Mon, 3 Jun 2024 02:27:21 +0900 Subject: [PATCH 19/20] =?UTF-8?q?docs:=20=EA=B2=B0=EA=B3=BC=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20=EC=A1=B0=EA=B1=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index c01babb7ff..ce68b6ffbb 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,18 @@ - [ ] 딜러와 플레이어가 블랙잭인 경우 무승부로 처리한다. - [ ] 게임을 시작하면 딜러와 플레이어에게 각각 2장의 카드를 나눠준다. - [ ] 게임을 종료하면 승패를 가린다. +- 결과 + - [ ] 게임 종료 후 승패 결과를 보여준다. + - 계산 + - 플레이어가 이기는 경우 + - [ ] 플레이어가 블랙잭이면서 딜러가 블랙잭이 아닌 경우 + - [ ] 플레이어가 버스트가 아니면서 딜러가 버스트인 경우 + - [ ] 플레이어, 딜러 모두 버스트가 아니면서 플레이어가 딜러보다 점수가 높은 경우 + - 플레이어가 지는 경우 + - [ ] 딜러가 블랙잭이면서 플레이어가 블랙잭이 아닌 경우 + - [ ] 딜러가 버스트가 아니면서 플레이어가 버스트인 경우 + - [ ] 플레이어, 딜러 모두 버스트가 아니면서 플레이어가 딜러보다 점수가 낮은 경우 + - 무승부 + - [ ] 플레이어, 딜러 모두 블랙잭인 경우 + - [ ] 플레이어, 딜러 모두 버스트인 경우 + - [ ] 플레이어, 딜러 모두 버스트가 아니면서 플레이어와 딜러의 점수가 같은 경우 From 12f992d1cfcae70f8c7b98825beafb1be91cd7ac Mon Sep 17 00:00:00 2001 From: rueun Date: Mon, 3 Jun 2024 02:28:14 +0900 Subject: [PATCH 20/20] =?UTF-8?q?feat:=20=EA=B2=B0=EA=B3=BC=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20=EC=B6=9C=EB=A0=A5=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/BlackJackGameController.java | 6 +- .../blackjack/domain/game/BlackJackGame.java | 47 +++++++++- .../blackjack/domain/participant/Dealer.java | 9 ++ .../domain/participant/Participant.java | 7 ++ .../blackjack/domain/participant/Player.java | 8 ++ .../blackjack/domain/participant/Players.java | 12 +++ .../blackjack/domain/result/DealerResult.java | 30 +++++++ .../domain/result/ParticipantResult.java | 21 +++++ .../blackjack/domain/result/PlayerResult.java | 23 +++++ .../java/blackjack/domain/result/Result.java | 41 +++++++++ .../domain/result/ResultDeterminer.java | 8 ++ src/main/java/blackjack/view/InputView.java | 12 +-- src/main/java/blackjack/view/ResultView.java | 66 +++++++++++--- .../java/blackjack/view/InputViewTest.java | 88 +++++++++++++++++++ 14 files changed, 352 insertions(+), 26 deletions(-) create mode 100644 src/main/java/blackjack/domain/result/DealerResult.java create mode 100644 src/main/java/blackjack/domain/result/ParticipantResult.java create mode 100644 src/main/java/blackjack/domain/result/PlayerResult.java create mode 100644 src/main/java/blackjack/domain/result/Result.java create mode 100644 src/main/java/blackjack/domain/result/ResultDeterminer.java create mode 100644 src/test/java/blackjack/view/InputViewTest.java diff --git a/src/main/java/blackjack/controller/BlackJackGameController.java b/src/main/java/blackjack/controller/BlackJackGameController.java index 65840a7a1e..9dfa218163 100644 --- a/src/main/java/blackjack/controller/BlackJackGameController.java +++ b/src/main/java/blackjack/controller/BlackJackGameController.java @@ -1,7 +1,7 @@ package blackjack.controller; -import blackjack.domain.game.BlackJackGame; import blackjack.domain.card.CardDeck; +import blackjack.domain.game.BlackJackGame; import blackjack.domain.participant.Dealer; import blackjack.domain.participant.Name; import blackjack.domain.participant.Player; @@ -13,8 +13,6 @@ public class BlackJackGameController { - private final InputView inputView = new InputView(); - public void run() { final CardDeck cardDeck = CardDeck.create(); cardDeck.shuffle(); @@ -27,7 +25,7 @@ public void run() { } private Players initPlayers() { - final List playerNames = inputView.inputPlayerNames(); + final List playerNames = InputView.inputPlayerNames(); final List players = playerNames.stream() .map(name -> Player.of(Name.of(name))) .collect(Collectors.toList()); diff --git a/src/main/java/blackjack/domain/game/BlackJackGame.java b/src/main/java/blackjack/domain/game/BlackJackGame.java index da659261aa..cfe00a2b91 100644 --- a/src/main/java/blackjack/domain/game/BlackJackGame.java +++ b/src/main/java/blackjack/domain/game/BlackJackGame.java @@ -2,9 +2,17 @@ import blackjack.domain.card.CardDeck; import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; import blackjack.domain.participant.Players; +import blackjack.domain.result.DealerResult; +import blackjack.domain.result.PlayerResult; +import blackjack.view.InputView; import blackjack.view.ResultView; +import java.util.List; + +import static blackjack.view.ResultView.printBlankLine; + public class BlackJackGame { private final CardDeck cardDeck; private final Players players; @@ -18,24 +26,55 @@ public BlackJackGame(final CardDeck cardDeck, final Players players, final Deale public void play() { drawInitialCards(cardDeck); - playersTurn(); - dealerTurn(); + if (!isGameOver()) { + playersTurn(); + dealerTurn(); + } + printResult(); judge(); } + private boolean isGameOver() { + return dealer.isBlackjack() || players.isAllBlackjack(); + } + private void drawInitialCards(final CardDeck cardDeck) { dealer.drawInitialCards(cardDeck); players.drawInitialCards(cardDeck); ResultView.printShareInitialCards(players, dealer); } - private void judge() { + private void playersTurn() { + for (final Player player : players.getPlayers()) { + playerTurn(player); + } + } + + private void playerTurn(final Player player) { + printBlankLine(); + while (player.canDrawCard() && InputView.printOrDrawOrStop(player.getName())) { + player.drawCard(cardDeck); + ResultView.printCardInfo(player); + } } private void dealerTurn() { + printBlankLine(); + while (dealer.canDrawCard()) { + dealer.drawCard(cardDeck); + ResultView.printDealerDrawCard(); + } } - private void playersTurn() { + private void printResult() { + printBlankLine(); + ResultView.printCardInfoWithScore(dealer); + players.getPlayers().forEach(ResultView::printCardInfoWithScore); + } + private void judge() { + final List playersResults = players.createResults(dealer); + final DealerResult dealerResult = dealer.createResult(playersResults); + ResultView.printResult(playersResults, dealerResult); } } diff --git a/src/main/java/blackjack/domain/participant/Dealer.java b/src/main/java/blackjack/domain/participant/Dealer.java index 7da533bc37..8507fb4dab 100644 --- a/src/main/java/blackjack/domain/participant/Dealer.java +++ b/src/main/java/blackjack/domain/participant/Dealer.java @@ -1,5 +1,10 @@ package blackjack.domain.participant; +import blackjack.domain.result.DealerResult; +import blackjack.domain.result.PlayerResult; + +import java.util.List; + public class Dealer extends Participant { private static final int DEALER_DRAW_THRESHOLD = 17; @@ -16,4 +21,8 @@ public boolean canDrawCard() { public static Dealer create() { return new Dealer(Name.of("딜러")); } + + public DealerResult createResult(final List playerResults) { + return new DealerResult(playerResults); + } } diff --git a/src/main/java/blackjack/domain/participant/Participant.java b/src/main/java/blackjack/domain/participant/Participant.java index 83defd61a0..e043aa280b 100644 --- a/src/main/java/blackjack/domain/participant/Participant.java +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -26,6 +26,13 @@ public boolean isBlackjack() { return cards.isBlackjack(); } + public boolean isBust() { + return cards.calculateScore() > 21; + } + + public int calculateScore() { + return cards.calculateScore(); + } public abstract boolean canDrawCard(); public String getName() { diff --git a/src/main/java/blackjack/domain/participant/Player.java b/src/main/java/blackjack/domain/participant/Player.java index f8dfebe582..f06578ef0b 100644 --- a/src/main/java/blackjack/domain/participant/Player.java +++ b/src/main/java/blackjack/domain/participant/Player.java @@ -1,5 +1,8 @@ package blackjack.domain.participant; +import blackjack.domain.result.PlayerResult; +import blackjack.domain.result.Result; + public class Player extends Participant { private static final int PLAYER_DRAW_THRESHOLD = 21; @@ -16,4 +19,9 @@ public boolean canDrawCard() { public static Player of(final Name name) { return new Player(name); } + + public PlayerResult createResult(final Dealer dealer) { + final Result result = Result.checkResult(this, dealer); + return PlayerResult.of(name.getName(), result); + } } diff --git a/src/main/java/blackjack/domain/participant/Players.java b/src/main/java/blackjack/domain/participant/Players.java index 95818f3965..6c28eca5eb 100644 --- a/src/main/java/blackjack/domain/participant/Players.java +++ b/src/main/java/blackjack/domain/participant/Players.java @@ -1,6 +1,7 @@ package blackjack.domain.participant; import blackjack.domain.card.CardDeck; +import blackjack.domain.result.PlayerResult; import java.util.List; @@ -31,4 +32,15 @@ public List getPlayerNames() { .map(Player::getName) .toList(); } + + public boolean isAllBlackjack() { + return players.stream() + .allMatch(Player::isBlackjack); + } + + public List createResults(final Dealer dealer) { + return players.stream() + .map(player -> player.createResult(dealer)) + .toList(); + } } diff --git a/src/main/java/blackjack/domain/result/DealerResult.java b/src/main/java/blackjack/domain/result/DealerResult.java new file mode 100644 index 0000000000..5c83f0fa35 --- /dev/null +++ b/src/main/java/blackjack/domain/result/DealerResult.java @@ -0,0 +1,30 @@ +package blackjack.domain.result; + +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +public class DealerResult { + private final Map result; + + public DealerResult(final List playerResults) { + this.result = new EnumMap<>(Result.class); + calculateResultCount(playerResults); + } + + private void calculateResultCount(final List playerResults) { + for (final PlayerResult playerResult : playerResults) { + addCount(Result.reverse(playerResult.getResult())); + } + } + + private void addCount(final Result result) { + this.result.put(result, this.result.getOrDefault(result, 0) + 1); + } + + public Map getResult() { + return Collections.unmodifiableMap(result); + } + +} diff --git a/src/main/java/blackjack/domain/result/ParticipantResult.java b/src/main/java/blackjack/domain/result/ParticipantResult.java new file mode 100644 index 0000000000..e9ae3c9db8 --- /dev/null +++ b/src/main/java/blackjack/domain/result/ParticipantResult.java @@ -0,0 +1,21 @@ +package blackjack.domain.result; + +public class ParticipantResult { + + private final String name; + private final Result result; + + public ParticipantResult(final String name, final Result result) { + this.name = name; + this.result = result; + } + + public String getName() { + return name; + } + + public Result getResult() { + return result; + } + +} diff --git a/src/main/java/blackjack/domain/result/PlayerResult.java b/src/main/java/blackjack/domain/result/PlayerResult.java new file mode 100644 index 0000000000..c085e343bd --- /dev/null +++ b/src/main/java/blackjack/domain/result/PlayerResult.java @@ -0,0 +1,23 @@ +package blackjack.domain.result; + +public class PlayerResult { + private final String name; + private final Result result; + + private PlayerResult(final String name, final Result result) { + this.name = name; + this.result = result; + } + + public static PlayerResult of(final String name, final Result result) { + return new PlayerResult(name, result); + } + + public String getName() { + return name; + } + + public Result getResult() { + return result; + } +} diff --git a/src/main/java/blackjack/domain/result/Result.java b/src/main/java/blackjack/domain/result/Result.java new file mode 100644 index 0000000000..0684232778 --- /dev/null +++ b/src/main/java/blackjack/domain/result/Result.java @@ -0,0 +1,41 @@ +package blackjack.domain.result; + +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; + +import java.util.Arrays; + +public enum Result { + WIN("승", (player, dealer) -> (player.isBlackjack() && !dealer.isBlackjack()) || (player.isBust() && !dealer.isBust()) || (player.calculateScore() > dealer.calculateScore() && !dealer.isBust())), + LOSE("패", (player, dealer) -> (!player.isBlackjack() && dealer.isBlackjack()) || (!player.isBust() && dealer.isBust() && player.calculateScore() < dealer.calculateScore())), + DRAW("무", (player, dealer) -> (player.calculateScore() == dealer.calculateScore()) || (player.isBust() && dealer.isBust()) || (player.isBlackjack() && dealer.isBlackjack())); + + private final String name; + private final ResultDeterminer determiner; + + Result(final String name, final ResultDeterminer determiner) { + this.name = name; + this.determiner = determiner; + } + + public static Result checkResult(final Player player, final Dealer dealer) { + return Arrays.stream(values()) + .filter(result -> result.determiner.compare(player, dealer)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("결과를 판단할 수 없습니다.")); + } + + public static Result reverse(final Result result) { + if (result == WIN) { + return LOSE; + } + if (result == LOSE) { + return WIN; + } + return DRAW; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/blackjack/domain/result/ResultDeterminer.java b/src/main/java/blackjack/domain/result/ResultDeterminer.java new file mode 100644 index 0000000000..88dd5628dc --- /dev/null +++ b/src/main/java/blackjack/domain/result/ResultDeterminer.java @@ -0,0 +1,8 @@ +package blackjack.domain.result; + +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; + +public interface ResultDeterminer { + boolean compare(Player player, Dealer dealer); +} \ No newline at end of file diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java index 7e772c3448..0b5f4c416c 100644 --- a/src/main/java/blackjack/view/InputView.java +++ b/src/main/java/blackjack/view/InputView.java @@ -9,27 +9,27 @@ public class InputView { private static final String INPUT_PLAYER_NAME = "게임에 참여할 사람의 이름을 입력하세요. (쉼표 기준으로 분리)"; private static final String ANSWER_YES = "y"; private static final String ANSWER_NO = "n"; - private static final String ADDITIONAL_DRAW_CARD = "%s는 한장의 카드를 더 받겠습니까? (예는 y, 아니오는 n)"; + private static final String PLAYER_ADDITIONAL_DRAW_CARD = "%s는 한장의 카드를 더 받겠습니까? (예는 y, 아니오는 n)"; private static final String NOT_YES_OR_NO = "y 또는 n 중 하나를 입력해주세요."; - public List inputPlayerNames() { + public static List inputPlayerNames() { System.out.println(INPUT_PLAYER_NAME); final List names = CommaStringSplitter.split(Console.readLine()); System.out.println(); return names; } - public boolean inputMoreCard(final String playerName) { - System.out.println(ADDITIONAL_DRAW_CARD.formatted(playerName)); + public static boolean printOrDrawOrStop(final String playerName) { + System.out.println(PLAYER_ADDITIONAL_DRAW_CARD.formatted(playerName)); final String answer = Console.readLine(); if (!isYesOrNo(answer)) { System.out.println(NOT_YES_OR_NO); - return inputMoreCard(playerName); + return printOrDrawOrStop(playerName); } return ANSWER_YES.equalsIgnoreCase(answer); } - private boolean isYesOrNo(final String answer) { + private static boolean isYesOrNo(final String answer) { return ANSWER_YES.equalsIgnoreCase(answer) || ANSWER_NO.equalsIgnoreCase(answer); } } diff --git a/src/main/java/blackjack/view/ResultView.java b/src/main/java/blackjack/view/ResultView.java index b40da720fb..457b2bd4a0 100644 --- a/src/main/java/blackjack/view/ResultView.java +++ b/src/main/java/blackjack/view/ResultView.java @@ -3,14 +3,21 @@ import blackjack.domain.participant.Dealer; import blackjack.domain.participant.Participant; import blackjack.domain.participant.Players; +import blackjack.domain.result.DealerResult; +import blackjack.domain.result.PlayerResult; +import blackjack.domain.result.Result; import java.util.List; +import java.util.Map; public class ResultView { private static final String SHARE_INITIAL_CARDS = "딜러와 %s에게 2장을 나누었습니다."; - private static final String PLAYER_DRAW_CARD = "%s는 카드를 더 받겠습니까? (예는 y, 아니오는 n)"; private static final String DEALER_DRAW_CARD = "딜러는 16이하라 한장의 카드를 더 받았습니다."; private static final String CARD_INFO = "%s 카드: %s"; + private static final String CARD_INFO_WITH_SCORE = "%s 카드: %s - 결과: %d"; + private static final String FINAL_RESULT = "## 최종 승패"; + private static final String PARTICIPANT_RESULT = "%s: %s"; + public static void printShareInitialCards(final Players players, final Dealer dealer) { final String playerNames = String.join(", ", players.getPlayerNames()); @@ -19,7 +26,6 @@ public static void printShareInitialCards(final Players players, final Dealer de printDealerInitialCards(dealer); printPlayerInitialCards(players); } - private static void printDealerInitialCards(final Dealer dealer) { final String cardsInfo = dealer.getCards().showCardsInfo().get(0); System.out.println(String.format(CARD_INFO, dealer.getName(), cardsInfo)); @@ -27,24 +33,60 @@ private static void printDealerInitialCards(final Dealer dealer) { private static void printPlayerInitialCards(final Players players) { for (Participant player : players.getPlayers()) { - final String cardsInfo = String.join(", ", player.getCards().showCardsInfo()); - System.out.println(String.format(CARD_INFO, player.getName(), cardsInfo)); + printCardInfo(player); } } - public static void printPlayerDrawCard(final String playerName) { - System.out.println(String.format(PLAYER_DRAW_CARD, playerName)); - } - public static void printDealerDrawCard() { System.out.println(DEALER_DRAW_CARD); } - public static void printResult(final List participants) { - System.out.println("## 최종 승패"); - for (Participant participant : participants) { - System.out.println(participant); + public static void printCardInfo(final Participant participant) { + final String cardsInfo = String.join(", ", participant.getCards().showCardsInfo()); + System.out.println(String.format(CARD_INFO, participant.getName(), cardsInfo)); + } + + public static void printCardInfoWithScore(final Participant participant) { + final String cardsInfo = String.join(", ", participant.getCards().showCardsInfo()); + System.out.println(String.format(CARD_INFO_WITH_SCORE, participant.getName(), cardsInfo, participant.getCards().calculateScore())); + } + + public static void printResult(final List playerResults, final DealerResult dealerResult) { + printBlankLine(); + System.out.println(FINAL_RESULT); + printDealerResult(dealerResult); + printPlayerResults(playerResults); + } + + private static void printDealerResult(final DealerResult dealerResult) { + final Map result = dealerResult.getResult(); + final StringBuilder resultString = new StringBuilder(); + + int winCount = result.getOrDefault(Result.WIN, 0); + int loseCount = result.getOrDefault(Result.LOSE, 0); + int drawCount = result.getOrDefault(Result.DRAW, 0); + + if (winCount > 0) { + resultString.append(winCount).append("승 "); + } + if (loseCount > 0) { + resultString.append(loseCount).append("패 "); + } + if (drawCount > 0) { + resultString.append(drawCount).append("무"); + } + + System.out.println(String.format(PARTICIPANT_RESULT, "딜러", resultString)); + } + + private static void printPlayerResults(final List playerResults) { + for (final PlayerResult playerResult : playerResults) { + System.out.println(String.format(PARTICIPANT_RESULT, playerResult.getName(), playerResult.getResult().getName())); } + } + + public static void printBlankLine() { + System.out.println(); } } diff --git a/src/test/java/blackjack/view/InputViewTest.java b/src/test/java/blackjack/view/InputViewTest.java new file mode 100644 index 0000000000..e3d1efa756 --- /dev/null +++ b/src/test/java/blackjack/view/InputViewTest.java @@ -0,0 +1,88 @@ +package blackjack.view; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; + +class InputViewTest { + + private final InputView inputView = new InputView(); + private InputStream originalIn; + + @BeforeEach + void setUp() { + // 현재 입력 스트림을 originalIn에 보관한다. + originalIn = System.in; + } + + @AfterEach + void tearDown() { + // 테스트가 끝나면 System.in을 원래의 originalIn으로 복구한다. + System.setIn(originalIn); + } + + @DisplayName("게임에 참여할 사람의 이름을 입력받는다.") + @Test + void inputPlayerNames() { + // Given + String input = "은정, 금정"; + System.setIn(new ByteArrayInputStream(input.getBytes())); + + // When + List names = inputView.inputPlayerNames(); + + // Then + assertEquals(2, names.size()); + assertEquals("은정", names.get(0)); + assertEquals("금정", names.get(1)); + } + + @Test + void inputMoreCard_yes() { + // Given + String input = "y"; + System.setIn(new ByteArrayInputStream(input.getBytes())); + + // When + boolean result = inputView.printOrDrawOrStop("은정"); + + // Then + assertTrue(result); + } + + @Test + void inputMoreCard_no() { + + // Given + String input = "n"; + System.setIn(new ByteArrayInputStream(input.getBytes())); + + // When + boolean result = inputView.printOrDrawOrStop("은정"); + + // Then + assertFalse(result); + } + + @Test + void inputMoreCard_invalidInput() { + String input = "invalid"; + System.setIn(new ByteArrayInputStream(input.getBytes())); + + Exception exception = assertThrows(IllegalArgumentException.class, () -> { + inputView.printOrDrawOrStop("은정"); + }); + + assertEquals("y 또는 n 중 하나를 입력해주세요.", exception.getMessage()); + } +}