From d121e5271f062acf7f205b16ed995a46c1da1e17 Mon Sep 17 00:00:00 2001 From: minuyim Date: Tue, 30 Jun 2020 18:12:18 +0900 Subject: [PATCH 01/17] =?UTF-8?q?docs:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3bcfc25784..8bb9976516 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,27 @@ # java-blackjack -블랙잭 게임 미션 저장소 - -## 우아한테크코스 코드리뷰 -* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) \ No newline at end of file +## 기능 구현 + - 플레이어 이름을 입력받는다 + - 영문자 이외의 값이 입력되면 안된다.(대소문자 구별 안한다) + - 첫턴에는 딜러와 등록된 플레이어에게 2장의 카드를 나눠준다. + - 딜러는 한장의 카드만 출력한다. + - 플레이어 들은 모든 카드를 출력한다. + - 모든 플레이어들은 원하는 만큼 카드를 입력받는다. + - 21이 넘어갈경우 더이상 카드를 받지 못하고 버스트, 다음 플레이어로 넘어간다. + - 딜러는 정해진 규칙에 따라 카드를 추가 지급 받는다. + - 16이하일 경우 한장의 카드를 추가 지급 받는다. + - 21초과할 경우 딜러는 버스트한다. + - 17이상일 경우 더이상 지급받지 않는다. + - 각 플레이어별 점수를 계산한다. + - ACE는 1또는 11로 계산한다. + - 11로 계산했을때 21이 초과하지 않으면 11로 계산한다. + - 21이 초과하면 1로 계산한다. + - 승패를 정한다. + - 플레이어가 버스트 될경우 무조건 플레이어가 패배한다. + - 플레이어가 버스트 되지 않고 딜러가 버스트 되면 플레이어 승리한다. + - 둘다 아닐 경우 점수를 계산하여 승패를 정한다. + - 플레이어에게 배팅할 금액을 입력받는다. + - 숫자 이외의 값을 읽으면 예외처리 + - 음수를 입력받을 경우 예외처리 + - 플레이어 배팅액을 참고하여 게임 승패에 따른 수익을 보여준다. + - 블랙잭으로 이길 경우 배팅액의 1.5배, 정상적으로 이길 경우 1배, 비길 경우 0배, 질 경우 -1배를 출력한다. + - 딜러는 플레이어의 수익 결과를 합한 값의 - 값을 출력한다. From 70353097adb18773df516ba7d52c421ceb983fc2 Mon Sep 17 00:00:00 2001 From: minuyim Date: Tue, 30 Jun 2020 18:41:40 +0900 Subject: [PATCH 02/17] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=EC=97=90=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=A0=20Card=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/card/Card.java | 11 +++++++++++ src/main/java/domain/card/Rank.java | 25 +++++++++++++++++++++++++ src/main/java/domain/card/Suit.java | 18 ++++++++++++++++++ src/test/java/domain/card/CardTest.java | 14 ++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 src/main/java/domain/card/Card.java create mode 100644 src/main/java/domain/card/Rank.java create mode 100644 src/main/java/domain/card/Suit.java create mode 100644 src/test/java/domain/card/CardTest.java diff --git a/src/main/java/domain/card/Card.java b/src/main/java/domain/card/Card.java new file mode 100644 index 0000000000..922bdf7234 --- /dev/null +++ b/src/main/java/domain/card/Card.java @@ -0,0 +1,11 @@ +package domain.card; + +public class Card { + private final Rank rank; + private final Suit suit; + + public Card(Rank rank, Suit suit) { + this.rank = rank; + this.suit = suit; + } +} diff --git a/src/main/java/domain/card/Rank.java b/src/main/java/domain/card/Rank.java new file mode 100644 index 0000000000..409816ccd1 --- /dev/null +++ b/src/main/java/domain/card/Rank.java @@ -0,0 +1,25 @@ +package domain.card; + +public enum Rank { + ACE(1, "A"), + TWO(2, "2"), + THREE(3, "3"), + FOUR(4, "4"), + FIVE(5, "5"), + SIX(6, "6"), + SEVEN(7, "7"), + EIGHT(8, "8"), + NINE(9, "9"), + TEN(10, "10"), + JACK(10, "J"), + QUEEN(10, "Q"), + KING(10, "K"); + + private final int value; + private final String pattern; + + Rank(int value, String pattern) { + this.value = value; + this.pattern = pattern; + } +} diff --git a/src/main/java/domain/card/Suit.java b/src/main/java/domain/card/Suit.java new file mode 100644 index 0000000000..bb569db1b4 --- /dev/null +++ b/src/main/java/domain/card/Suit.java @@ -0,0 +1,18 @@ +package domain.card; + +public enum Suit { + CLUB("♣"), + DIAMOND("♦"), + HEART("♥"), + SPADE("♠"); + + private final String pattern; + + Suit(String pattern) { + this.pattern = pattern; + } + + public String getPattern() { + return pattern; + } +} diff --git a/src/test/java/domain/card/CardTest.java b/src/test/java/domain/card/CardTest.java new file mode 100644 index 0000000000..ec4998bcf8 --- /dev/null +++ b/src/test/java/domain/card/CardTest.java @@ -0,0 +1,14 @@ +package domain.card; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class CardTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + assertThat(new Card(Rank.ACE, Suit.SPADE)).isNotNull(); + } +} From 797a8d2cee2a10ca1bc6f4f769552701b683ebc7 Mon Sep 17 00:00:00 2001 From: minuyim Date: Tue, 30 Jun 2020 23:26:05 +0900 Subject: [PATCH 03/17] =?UTF-8?q?feat:=20=EB=B8=94=EB=9E=99=EC=9E=AD?= =?UTF-8?q?=EC=97=90=20=ED=95=84=EC=9A=94=ED=95=9C=20Deck=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/card/Card.java | 18 +++++++++++++++ src/main/java/domain/card/deck/Deck.java | 17 ++++++++++++++ src/test/java/domain/card/deck/DeckTest.java | 24 ++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 src/main/java/domain/card/deck/Deck.java create mode 100644 src/test/java/domain/card/deck/DeckTest.java diff --git a/src/main/java/domain/card/Card.java b/src/main/java/domain/card/Card.java index 922bdf7234..bc20f3edd0 100644 --- a/src/main/java/domain/card/Card.java +++ b/src/main/java/domain/card/Card.java @@ -1,5 +1,7 @@ package domain.card; +import java.util.Objects; + public class Card { private final Rank rank; private final Suit suit; @@ -8,4 +10,20 @@ public Card(Rank rank, Suit suit) { this.rank = rank; this.suit = suit; } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Card card = (Card)o; + return rank == card.rank && + suit == card.suit; + } + + @Override + public int hashCode() { + return Objects.hash(rank, suit); + } } diff --git a/src/main/java/domain/card/deck/Deck.java b/src/main/java/domain/card/deck/Deck.java new file mode 100644 index 0000000000..266b465eab --- /dev/null +++ b/src/main/java/domain/card/deck/Deck.java @@ -0,0 +1,17 @@ +package domain.card.deck; + +import java.util.Stack; + +import domain.card.Card; + +public class Deck { + private final Stack cards; + + public Deck(Stack cards) { + this.cards = cards; + } + + public Card pop() { + return cards.pop(); + } +} diff --git a/src/test/java/domain/card/deck/DeckTest.java b/src/test/java/domain/card/deck/DeckTest.java new file mode 100644 index 0000000000..85849a81f7 --- /dev/null +++ b/src/test/java/domain/card/deck/DeckTest.java @@ -0,0 +1,24 @@ +package domain.card.deck; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Stack; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; + +class DeckTest { + @Test + @DisplayName("가장 마지막 카드를 내보내는 지 확인") + void pop() { + Stack cards = new Stack<>(); + cards.push(new Card(Rank.EIGHT, Suit.SPADE)); + cards.push(new Card(Rank.NINE, Suit.CLUB)); + Deck deck = new Deck(cards); + assertThat(deck.pop()).isEqualTo(new Card(Rank.NINE, Suit.CLUB)); + } +} \ No newline at end of file From acdf32a882ffcc08c68edf3420ea2ee5e4c6f12b Mon Sep 17 00:00:00 2001 From: minuyim Date: Tue, 30 Jun 2020 23:26:30 +0900 Subject: [PATCH 04/17] =?UTF-8?q?feat:=20=EB=9E=9C=EB=8D=A4=ED=95=98?= =?UTF-8?q?=EA=B2=8C=20=EC=84=9E=EC=9D=80=20Deck=EC=9D=84=20=EB=A7=8C?= =?UTF-8?q?=EB=93=A4=EC=96=B4=20=EC=A3=BC=EB=8A=94=20RandomShuffledDeckFac?= =?UTF-8?q?tory=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/card/deck/AbstractDeckFactory.java | 30 +++++++++++++++++++ .../java/domain/card/deck/CardFactory.java | 9 ++++++ .../java/domain/card/deck/DeckFactory.java | 5 ++++ .../card/deck/RandomShuffledDeckFactory.java | 14 +++++++++ .../domain/card/deck/CardFactoryTest.java | 15 ++++++++++ .../domain/card/deck/DeckFactoryTest.java | 27 +++++++++++++++++ .../domain/card/deck/TestDeckFactory.java | 15 ++++++++++ 7 files changed, 115 insertions(+) create mode 100644 src/main/java/domain/card/deck/AbstractDeckFactory.java create mode 100644 src/main/java/domain/card/deck/CardFactory.java create mode 100644 src/main/java/domain/card/deck/DeckFactory.java create mode 100644 src/main/java/domain/card/deck/RandomShuffledDeckFactory.java create mode 100644 src/test/java/domain/card/deck/CardFactoryTest.java create mode 100644 src/test/java/domain/card/deck/DeckFactoryTest.java create mode 100644 src/test/java/domain/card/deck/TestDeckFactory.java diff --git a/src/main/java/domain/card/deck/AbstractDeckFactory.java b/src/main/java/domain/card/deck/AbstractDeckFactory.java new file mode 100644 index 0000000000..8b50fadf3b --- /dev/null +++ b/src/main/java/domain/card/deck/AbstractDeckFactory.java @@ -0,0 +1,30 @@ +package domain.card.deck; + +import java.util.Arrays; +import java.util.List; +import java.util.Stack; +import java.util.stream.Collectors; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; + +public abstract class AbstractDeckFactory implements DeckFactory, CardFactory { + @Override + public List createCards() { + return Arrays.stream(Rank.values()) + .flatMap((rank -> Arrays.stream(Suit.values()) + .map(suit -> new Card(rank, suit)))) + .collect(Collectors.toList()); + } + + @Override + public Deck create() { + List cards = handleCards(createCards()); + Stack newCards = new Stack<>(); + newCards.addAll(cards); + return new Deck(newCards); + } + + public abstract List handleCards(List cards); +} diff --git a/src/main/java/domain/card/deck/CardFactory.java b/src/main/java/domain/card/deck/CardFactory.java new file mode 100644 index 0000000000..f64e38e55c --- /dev/null +++ b/src/main/java/domain/card/deck/CardFactory.java @@ -0,0 +1,9 @@ +package domain.card.deck; + +import java.util.List; + +import domain.card.Card; + +public interface CardFactory { + List createCards(); +} diff --git a/src/main/java/domain/card/deck/DeckFactory.java b/src/main/java/domain/card/deck/DeckFactory.java new file mode 100644 index 0000000000..3e864234ef --- /dev/null +++ b/src/main/java/domain/card/deck/DeckFactory.java @@ -0,0 +1,5 @@ +package domain.card.deck; + +public interface DeckFactory { + Deck create(); +} diff --git a/src/main/java/domain/card/deck/RandomShuffledDeckFactory.java b/src/main/java/domain/card/deck/RandomShuffledDeckFactory.java new file mode 100644 index 0000000000..13ce935c85 --- /dev/null +++ b/src/main/java/domain/card/deck/RandomShuffledDeckFactory.java @@ -0,0 +1,14 @@ +package domain.card.deck; + +import java.util.Collections; +import java.util.List; + +import domain.card.Card; + +public class RandomShuffledDeckFactory extends AbstractDeckFactory { + @Override + public List handleCards(List cards) { + Collections.shuffle(cards); + return cards; + } +} diff --git a/src/test/java/domain/card/deck/CardFactoryTest.java b/src/test/java/domain/card/deck/CardFactoryTest.java new file mode 100644 index 0000000000..02a3db737c --- /dev/null +++ b/src/test/java/domain/card/deck/CardFactoryTest.java @@ -0,0 +1,15 @@ +package domain.card.deck; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class CardFactoryTest { + @Test + @DisplayName("52장의 카드를 만드는 지 확인") + void createCards() { + CardFactory testDeckFactory = new TestDeckFactory(); + assertThat(testDeckFactory.createCards()).hasSize(52); + } +} diff --git a/src/test/java/domain/card/deck/DeckFactoryTest.java b/src/test/java/domain/card/deck/DeckFactoryTest.java new file mode 100644 index 0000000000..b6e1822747 --- /dev/null +++ b/src/test/java/domain/card/deck/DeckFactoryTest.java @@ -0,0 +1,27 @@ +package domain.card.deck; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; + +class DeckFactoryTest { + @Test + @DisplayName("Deck을 만드는 지 확인") + void create() { + DeckFactory testDeckFactory = new TestDeckFactory(); + assertThat(testDeckFactory.create()).isNotNull(); + } + + @Test + @DisplayName("DeckFactory가 카드를 원하는 방식으로 조절할 수 있는 지 확인") + void handleCards() { + DeckFactory testDeckFactory = new TestDeckFactory(); + Deck deck = testDeckFactory.create(); + assertThat(deck.pop()).isEqualTo(new Card(Rank.ACE, Suit.CLUB)); + } +} \ No newline at end of file diff --git a/src/test/java/domain/card/deck/TestDeckFactory.java b/src/test/java/domain/card/deck/TestDeckFactory.java new file mode 100644 index 0000000000..3947666f33 --- /dev/null +++ b/src/test/java/domain/card/deck/TestDeckFactory.java @@ -0,0 +1,15 @@ +package domain.card.deck; + +import java.util.Arrays; +import java.util.List; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; + +public class TestDeckFactory extends AbstractDeckFactory { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.ACE, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)); + } +} From 68ef1a3ffc8fff0b8cc4b27ad2089d46a68dac40 Mon Sep 17 00:00:00 2001 From: minuyim Date: Wed, 1 Jul 2020 00:43:48 +0900 Subject: [PATCH 05/17] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=EA=B0=80=EC=A7=80=EA=B3=A0=20=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=EB=A5=BC=20=EB=82=98=ED=83=80=EB=82=B4?= =?UTF-8?q?=EB=8A=94=20Hand=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/gamer/Hand.java | 21 ++++++++++++++++++ src/test/java/domain/gamer/HandTest.java | 27 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/main/java/domain/gamer/Hand.java create mode 100644 src/test/java/domain/gamer/HandTest.java diff --git a/src/main/java/domain/gamer/Hand.java b/src/main/java/domain/gamer/Hand.java new file mode 100644 index 0000000000..5f73733607 --- /dev/null +++ b/src/main/java/domain/gamer/Hand.java @@ -0,0 +1,21 @@ +package domain.gamer; + +import java.util.List; + +import domain.card.Card; + +public class Hand { + private final List cards; + + public Hand(List cards) { + this.cards = cards; + } + + public void add(Card card) { + cards.add(card); + } + + public List getCards() { + return cards; + } +} diff --git a/src/test/java/domain/gamer/HandTest.java b/src/test/java/domain/gamer/HandTest.java new file mode 100644 index 0000000000..88a00d1ed0 --- /dev/null +++ b/src/test/java/domain/gamer/HandTest.java @@ -0,0 +1,27 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; + +public class HandTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + assertThat(new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB)))).isNotNull(); + } + + @Test + void addCard() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB))); + hand.add(new Card(Rank.ACE, Suit.SPADE)); + assertThat(hand.getCards()).hasSize(3); + } +} From 16b9c2222bd45465e68cfe1d276118bc1e0efc90 Mon Sep 17 00:00:00 2001 From: minuyim Date: Wed, 1 Jul 2020 00:54:24 +0900 Subject: [PATCH 06/17] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=EA=B0=80=EC=A7=80=EA=B3=A0=20=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9D=84=20=EB=82=98=ED=83=80=EB=82=B4?= =?UTF-8?q?=EB=8A=94=20Name=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/gamer/Name.java | 16 ++++++++++++++++ src/test/java/domain/gamer/NameTest.java | 21 +++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/main/java/domain/gamer/Name.java create mode 100644 src/test/java/domain/gamer/NameTest.java diff --git a/src/main/java/domain/gamer/Name.java b/src/main/java/domain/gamer/Name.java new file mode 100644 index 0000000000..0c12031c25 --- /dev/null +++ b/src/main/java/domain/gamer/Name.java @@ -0,0 +1,16 @@ +package domain.gamer; + +public class Name { + private final String name; + + public Name(String name) { + validate(name); + this.name = name; + } + + private void validate(String name) { + if (name.matches(".*\\s.*")) { + throw new IllegalArgumentException("이름에 공백이 들어가면 안됩니다."); + } + } +} diff --git a/src/test/java/domain/gamer/NameTest.java b/src/test/java/domain/gamer/NameTest.java new file mode 100644 index 0000000000..4c5944d14d --- /dev/null +++ b/src/test/java/domain/gamer/NameTest.java @@ -0,0 +1,21 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class NameTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + assertThat(new Name("hi")).isNotNull(); + } + + @Test + void constructorException() { + assertThatThrownBy(() -> new Name("h i")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("공백"); + } +} From 6eac343998d27c1e016b6cdfc0374019801a754c Mon Sep 17 00:00:00 2001 From: minuyim Date: Wed, 1 Jul 2020 00:59:29 +0900 Subject: [PATCH 07/17] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=EA=B0=80=EC=A7=80=EA=B3=A0=20=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EB=B0=B0=ED=8C=85=EC=95=A1=EC=9D=84=20=EB=82=98=ED=83=80?= =?UTF-8?q?=EB=82=B4=EB=8A=94=20BattingMoney=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/gamer/BattingMoney.java | 16 +++++++++++++ .../java/domain/gamer/BattingMoneyTest.java | 23 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/main/java/domain/gamer/BattingMoney.java create mode 100644 src/test/java/domain/gamer/BattingMoneyTest.java diff --git a/src/main/java/domain/gamer/BattingMoney.java b/src/main/java/domain/gamer/BattingMoney.java new file mode 100644 index 0000000000..3301f1f3fd --- /dev/null +++ b/src/main/java/domain/gamer/BattingMoney.java @@ -0,0 +1,16 @@ +package domain.gamer; + +public class BattingMoney { + private final int money; + + public BattingMoney(int money) { + validate(money); + this.money = money; + } + + private void validate(int money) { + if (money < 0) { + throw new IllegalArgumentException("음수는 허용되지 않습니다. input : " + money); + } + } +} diff --git a/src/test/java/domain/gamer/BattingMoneyTest.java b/src/test/java/domain/gamer/BattingMoneyTest.java new file mode 100644 index 0000000000..bce643d6a1 --- /dev/null +++ b/src/test/java/domain/gamer/BattingMoneyTest.java @@ -0,0 +1,23 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class +BattingMoneyTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + assertThat(new BattingMoney(10)).isNotNull(); + } + + @Test + @DisplayName("생성 테스트 - 음수 시 예외") + void constructorException() { + assertThatThrownBy(() -> new BattingMoney(-10)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("음수"); + } +} From 7eebdd8b950111ebc5de7f9081e0e8503bcb9039 Mon Sep 17 00:00:00 2001 From: minuyim Date: Wed, 1 Jul 2020 16:53:12 +0900 Subject: [PATCH 08/17] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EB=A5=BC=20=EB=82=98=ED=83=80=EB=82=B4=EB=8A=94=20Gamer=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/gamer/Gamer.java | 35 +++++++++++++++++++++++ src/test/java/domain/gamer/GamerTest.java | 35 +++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/main/java/domain/gamer/Gamer.java create mode 100644 src/test/java/domain/gamer/GamerTest.java diff --git a/src/main/java/domain/gamer/Gamer.java b/src/main/java/domain/gamer/Gamer.java new file mode 100644 index 0000000000..aaeb5dae88 --- /dev/null +++ b/src/main/java/domain/gamer/Gamer.java @@ -0,0 +1,35 @@ +package domain.gamer; + +import domain.card.deck.Deck; + +public class Gamer { + private final Name name; + private final Hand hand; + private final BattingMoney battingMoney; + + private Gamer(Name name, Hand hand, BattingMoney battingMoney) { + this.name = name; + this.hand = hand; + this.battingMoney = battingMoney; + } + + public static Gamer of(String name, Hand hand, int battingMoney) { + return new Gamer(new Name(name), hand, new BattingMoney(battingMoney)); + } + + public void hit(Deck deck) { + hand.add(deck.pop()); + } + + public Name getName() { + return name; + } + + public Hand getHand() { + return hand; + } + + public BattingMoney getBattingMoney() { + return battingMoney; + } +} diff --git a/src/test/java/domain/gamer/GamerTest.java b/src/test/java/domain/gamer/GamerTest.java new file mode 100644 index 0000000000..e37b27ff51 --- /dev/null +++ b/src/test/java/domain/gamer/GamerTest.java @@ -0,0 +1,35 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.DeckFactory; +import domain.card.deck.TestDeckFactory; + +public class GamerTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + List cards = Arrays.asList(new Card(Rank.ACE, Suit.SPADE), new Card(Rank.EIGHT, Suit.SPADE)); + assertThat(Gamer.of("사람", new Hand(cards), 10_000)).isNotNull(); + } + + @Test + @DisplayName("게이머가 히트할 수 있는지 확인") + void hit() { + List cards = new ArrayList<>(Arrays.asList(new Card(Rank.ACE, Suit.SPADE), new Card(Rank.EIGHT, Suit.SPADE))); + Gamer gamer = Gamer.of("사람", new Hand(cards), 10_000); + DeckFactory testDeckFactory = new TestDeckFactory(); + gamer.hit(testDeckFactory.create()); + assertThat(gamer.getHand().getCards()).hasSize(3); + } +} From 7248635f71b363354951540bac007463aa5f3cc8 Mon Sep 17 00:00:00 2001 From: minuyim Date: Wed, 1 Jul 2020 18:19:25 +0900 Subject: [PATCH 09/17] =?UTF-8?q?feat:=20Hand=20Score=20=EA=B3=84=EC=82=B0?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EB=B0=8F=20=EC=8A=A4=EC=BD=94=EC=96=B4?= =?UTF-8?q?=20=EC=A0=90=EC=88=98=20=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/card/Card.java | 8 ++++ src/main/java/domain/card/Rank.java | 4 ++ src/main/java/domain/gamer/Hand.java | 33 ++++++++++++++- src/test/java/domain/card/CardTest.java | 6 +++ src/test/java/domain/gamer/HandTest.java | 54 +++++++++++++++++++++++- 5 files changed, 103 insertions(+), 2 deletions(-) diff --git a/src/main/java/domain/card/Card.java b/src/main/java/domain/card/Card.java index bc20f3edd0..b2193c5625 100644 --- a/src/main/java/domain/card/Card.java +++ b/src/main/java/domain/card/Card.java @@ -11,6 +11,14 @@ public Card(Rank rank, Suit suit) { this.suit = suit; } + public boolean isAce() { + return rank == Rank.ACE; + } + + public int getScore() { + return rank.getValue(); + } + @Override public boolean equals(Object o) { if (this == o) diff --git a/src/main/java/domain/card/Rank.java b/src/main/java/domain/card/Rank.java index 409816ccd1..006d0b8a84 100644 --- a/src/main/java/domain/card/Rank.java +++ b/src/main/java/domain/card/Rank.java @@ -22,4 +22,8 @@ public enum Rank { this.value = value; this.pattern = pattern; } + + public int getValue() { + return value; + } } diff --git a/src/main/java/domain/gamer/Hand.java b/src/main/java/domain/gamer/Hand.java index 5f73733607..8f827af310 100644 --- a/src/main/java/domain/gamer/Hand.java +++ b/src/main/java/domain/gamer/Hand.java @@ -1,10 +1,14 @@ package domain.gamer; +import java.util.Collections; import java.util.List; import domain.card.Card; public class Hand { + private static final int BLACKJACK_SCORE = 21; + private static final int ACE_BONUS = 10; + private final List cards; public Hand(List cards) { @@ -15,7 +19,34 @@ public void add(Card card) { cards.add(card); } + public int calculateScore() { + return cards.stream() + .filter(Card::isAce) + .reduce(calculateRawScore(), (subTotal, card) -> addAceBonus(subTotal), Integer::sum); + } + + private int addAceBonus(int subTotal) { + if (subTotal + ACE_BONUS <= BLACKJACK_SCORE) { + return subTotal + ACE_BONUS; + } + return subTotal; + } + + private int calculateRawScore() { + return cards.stream() + .mapToInt(Card::getScore) + .sum(); + } + + public boolean isBust() { + return calculateScore() > BLACKJACK_SCORE; + } + + public boolean isBlackJackScore() { + return calculateScore() == BLACKJACK_SCORE; + } + public List getCards() { - return cards; + return Collections.unmodifiableList(cards); } } diff --git a/src/test/java/domain/card/CardTest.java b/src/test/java/domain/card/CardTest.java index ec4998bcf8..ccb5b05556 100644 --- a/src/test/java/domain/card/CardTest.java +++ b/src/test/java/domain/card/CardTest.java @@ -11,4 +11,10 @@ public class CardTest { void constructor() { assertThat(new Card(Rank.ACE, Suit.SPADE)).isNotNull(); } + + @Test + @DisplayName("Ace인지 확인") + void isAce() { + assertThat(new Card(Rank.ACE, Suit.SPADE).isAce()).isTrue(); + } } diff --git a/src/test/java/domain/gamer/HandTest.java b/src/test/java/domain/gamer/HandTest.java index 88a00d1ed0..ef4e920c0c 100644 --- a/src/test/java/domain/gamer/HandTest.java +++ b/src/test/java/domain/gamer/HandTest.java @@ -15,13 +15,65 @@ public class HandTest { @Test @DisplayName("생성 테스트") void constructor() { - assertThat(new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB)))).isNotNull(); + assertThat( + new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB)))).isNotNull(); } @Test + @DisplayName("카드를 더할 수 있는 지 확인") void addCard() { Hand hand = new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB))); hand.add(new Card(Rank.ACE, Suit.SPADE)); assertThat(hand.getCards()).hasSize(3); } + + @Test + @DisplayName("현재 가지고 있는 카드의 점수를 계산하는지 확인") + void calculateScore1() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB))); + assertThat(hand.calculateScore()).isEqualTo(16); + } + + @Test + @DisplayName("현재 가지고 있는 카드의 점수를 계산하는지 확인 - 에이스 보너스") + void calculateScore2() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.ACE, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB))); + assertThat(hand.calculateScore()).isEqualTo(19); + } + + @Test + @DisplayName("현재 가지고 있는 카드의 점수를 계산하는지 확인 - 에이스 보너스 적용 X") + void calculateScore3() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.ACE, Suit.SPADE), + new Card(Rank.TEN, Suit.CLUB), + new Card(Rank.TEN, Suit.SPADE))); + assertThat(hand.calculateScore()).isEqualTo(21); + } + + @Test + @DisplayName("현재 가지고 있는 카드의 점수를 계산하는지 확인 - 에이스 보너스 일부 적용") + void calculateScore4() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.ACE, Suit.SPADE), + new Card(Rank.ACE, Suit.CLUB), + new Card(Rank.NINE, Suit.SPADE))); + assertThat(hand.calculateScore()).isEqualTo(21); + } + + @Test + @DisplayName("버스트 여부 확인") + void isBust() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.TEN, Suit.SPADE), + new Card(Rank.EIGHT, Suit.CLUB), + new Card(Rank.NINE, Suit.SPADE))); + assertThat(hand.isBust()).isTrue(); + } + + @Test + @DisplayName("블랙잭 스코어와 같은 지 확인") + void isBlackJackScore() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.TEN, Suit.SPADE), + new Card(Rank.TWO, Suit.CLUB), + new Card(Rank.NINE, Suit.SPADE))); + assertThat(hand.isBlackJackScore()).isTrue(); + } } From 5de127b403e17b5919e02f354e02dedc7f7b5d06 Mon Sep 17 00:00:00 2001 From: minuyim Date: Wed, 1 Jul 2020 22:26:38 +0900 Subject: [PATCH 10/17] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EC=9D=98=20=ED=95=B8=EB=93=9C=20=EC=83=81=ED=83=9C=EB=A5=BC=20?= =?UTF-8?q?=EB=82=98=ED=83=80=EB=82=B4=EB=8A=94=20State=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/gamer/Gamer.java | 18 +++--- src/main/java/domain/gamer/Name.java | 3 +- src/main/java/domain/state/BlackJack.java | 17 ++++++ src/main/java/domain/state/Bust.java | 14 +++++ src/main/java/domain/state/Finished.java | 25 +++++++++ src/main/java/domain/state/GamerState.java | 20 +++++++ src/main/java/domain/state/Playing.java | 35 ++++++++++++ src/main/java/domain/state/State.java | 18 ++++++ src/main/java/domain/state/StateFactory.java | 25 +++++++++ src/main/java/domain/state/Stay.java | 16 ++++++ src/test/java/domain/gamer/GamerTest.java | 9 ++- src/test/java/domain/state/BlackJackTest.java | 24 ++++++++ src/test/java/domain/state/BustTest.java | 24 ++++++++ src/test/java/domain/state/FinishedTest.java | 42 ++++++++++++++ src/test/java/domain/state/PlayingTest.java | 55 +++++++++++++++++++ .../java/domain/state/StateFactoryTest.java | 39 +++++++++++++ src/test/java/domain/state/StayTest.java | 24 ++++++++ 17 files changed, 394 insertions(+), 14 deletions(-) create mode 100644 src/main/java/domain/state/BlackJack.java create mode 100644 src/main/java/domain/state/Bust.java create mode 100644 src/main/java/domain/state/Finished.java create mode 100644 src/main/java/domain/state/GamerState.java create mode 100644 src/main/java/domain/state/Playing.java create mode 100644 src/main/java/domain/state/State.java create mode 100644 src/main/java/domain/state/StateFactory.java create mode 100644 src/main/java/domain/state/Stay.java create mode 100644 src/test/java/domain/state/BlackJackTest.java create mode 100644 src/test/java/domain/state/BustTest.java create mode 100644 src/test/java/domain/state/FinishedTest.java create mode 100644 src/test/java/domain/state/PlayingTest.java create mode 100644 src/test/java/domain/state/StateFactoryTest.java create mode 100644 src/test/java/domain/state/StayTest.java diff --git a/src/main/java/domain/gamer/Gamer.java b/src/main/java/domain/gamer/Gamer.java index aaeb5dae88..43c074a43c 100644 --- a/src/main/java/domain/gamer/Gamer.java +++ b/src/main/java/domain/gamer/Gamer.java @@ -1,32 +1,34 @@ package domain.gamer; import domain.card.deck.Deck; +import domain.state.State; +import domain.state.StateFactory; public class Gamer { private final Name name; - private final Hand hand; + private State state; private final BattingMoney battingMoney; - private Gamer(Name name, Hand hand, BattingMoney battingMoney) { + private Gamer(Name name, State state, BattingMoney battingMoney) { this.name = name; - this.hand = hand; + this.state = state; this.battingMoney = battingMoney; } - public static Gamer of(String name, Hand hand, int battingMoney) { - return new Gamer(new Name(name), hand, new BattingMoney(battingMoney)); + public static Gamer of(String name, Deck deck, int battingMoney) { + return new Gamer(new Name(name), StateFactory.create(deck), new BattingMoney(battingMoney)); } public void hit(Deck deck) { - hand.add(deck.pop()); + state = state.hit(deck); } public Name getName() { return name; } - public Hand getHand() { - return hand; + public State getState() { + return state; } public BattingMoney getBattingMoney() { diff --git a/src/main/java/domain/gamer/Name.java b/src/main/java/domain/gamer/Name.java index 0c12031c25..4137ba5d89 100644 --- a/src/main/java/domain/gamer/Name.java +++ b/src/main/java/domain/gamer/Name.java @@ -1,6 +1,7 @@ package domain.gamer; public class Name { + private static final String SPACE_PATTERN = ".*\\s.*"; private final String name; public Name(String name) { @@ -9,7 +10,7 @@ public Name(String name) { } private void validate(String name) { - if (name.matches(".*\\s.*")) { + if (name.matches(SPACE_PATTERN)) { throw new IllegalArgumentException("이름에 공백이 들어가면 안됩니다."); } } diff --git a/src/main/java/domain/state/BlackJack.java b/src/main/java/domain/state/BlackJack.java new file mode 100644 index 0000000000..311cc8403d --- /dev/null +++ b/src/main/java/domain/state/BlackJack.java @@ -0,0 +1,17 @@ +package domain.state; + +import domain.gamer.Hand; + +public class BlackJack extends Finished { + + private static final double BLACKJACK_RATE = 1.5; + + public BlackJack(Hand hand) { + super(hand); + } + + @Override + public double getEarningRate() { + return BLACKJACK_RATE; + } +} diff --git a/src/main/java/domain/state/Bust.java b/src/main/java/domain/state/Bust.java new file mode 100644 index 0000000000..fd36ee1480 --- /dev/null +++ b/src/main/java/domain/state/Bust.java @@ -0,0 +1,14 @@ +package domain.state; + +import domain.gamer.Hand; + +public class Bust extends Finished { + public Bust(Hand hand) { + super(hand); + } + + @Override + public double getEarningRate() { + return 0; + } +} diff --git a/src/main/java/domain/state/Finished.java b/src/main/java/domain/state/Finished.java new file mode 100644 index 0000000000..0bd53c6f33 --- /dev/null +++ b/src/main/java/domain/state/Finished.java @@ -0,0 +1,25 @@ +package domain.state; + +import domain.card.deck.Deck; +import domain.gamer.Hand; + +public abstract class Finished extends GamerState { + public Finished(Hand hand) { + super(hand); + } + + @Override + public State hit(Deck deck) { + throw new UnsupportedOperationException(); + } + + @Override + public State stay() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isFinished() { + return true; + } +} diff --git a/src/main/java/domain/state/GamerState.java b/src/main/java/domain/state/GamerState.java new file mode 100644 index 0000000000..ca4885ba33 --- /dev/null +++ b/src/main/java/domain/state/GamerState.java @@ -0,0 +1,20 @@ +package domain.state; + +import java.util.Collections; +import java.util.List; + +import domain.card.Card; +import domain.gamer.Hand; + +public abstract class GamerState implements State { + protected final Hand hand; + + public GamerState(Hand hand) { + this.hand = hand; + } + + @Override + public List getCards() { + return Collections.unmodifiableList(hand.getCards()); + } +} diff --git a/src/main/java/domain/state/Playing.java b/src/main/java/domain/state/Playing.java new file mode 100644 index 0000000000..d914b7dcae --- /dev/null +++ b/src/main/java/domain/state/Playing.java @@ -0,0 +1,35 @@ +package domain.state; + +import domain.card.deck.Deck; +import domain.gamer.Hand; + +public class Playing extends GamerState { + public Playing(Hand hand) { + super(hand); + } + + @Override + public State hit(Deck deck) { + hand.add(deck.pop()); + if (hand.isBust()) { + new Bust(hand); + } + return new Playing(hand); + } + + @Override + public State stay() { + return new Stay(hand); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public double getEarningRate() { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/main/java/domain/state/State.java b/src/main/java/domain/state/State.java new file mode 100644 index 0000000000..4ee547933e --- /dev/null +++ b/src/main/java/domain/state/State.java @@ -0,0 +1,18 @@ +package domain.state; + +import java.util.List; + +import domain.card.Card; +import domain.card.deck.Deck; + +public interface State { + State hit(Deck deck); + + State stay(); + + boolean isFinished(); + + double getEarningRate(); + + List getCards(); +} diff --git a/src/main/java/domain/state/StateFactory.java b/src/main/java/domain/state/StateFactory.java new file mode 100644 index 0000000000..f9f0340166 --- /dev/null +++ b/src/main/java/domain/state/StateFactory.java @@ -0,0 +1,25 @@ +package domain.state; + +import java.util.ArrayList; +import java.util.Arrays; + +import domain.card.Card; +import domain.card.deck.Deck; +import domain.gamer.Hand; + +public class StateFactory { + private StateFactory() { + } + + public static State create(Deck deck) { + return createState(deck.pop(), deck.pop()); + } + + private static State createState(Card first, Card second) { + Hand hand = new Hand(new ArrayList<>(Arrays.asList(first, second))); + if (hand.isBlackJackScore()) { + return new BlackJack(hand); + } + return new Playing(hand); + } +} diff --git a/src/main/java/domain/state/Stay.java b/src/main/java/domain/state/Stay.java new file mode 100644 index 0000000000..7b4d53d1ba --- /dev/null +++ b/src/main/java/domain/state/Stay.java @@ -0,0 +1,16 @@ +package domain.state; + +import domain.gamer.Hand; + +public class Stay extends Finished { + private static final int NORMAL_RATE = 1; + + public Stay(Hand hand) { + super(hand); + } + + @Override + public double getEarningRate() { + return NORMAL_RATE; + } +} diff --git a/src/test/java/domain/gamer/GamerTest.java b/src/test/java/domain/gamer/GamerTest.java index e37b27ff51..3721b64352 100644 --- a/src/test/java/domain/gamer/GamerTest.java +++ b/src/test/java/domain/gamer/GamerTest.java @@ -19,17 +19,16 @@ public class GamerTest { @Test @DisplayName("생성 테스트") void constructor() { - List cards = Arrays.asList(new Card(Rank.ACE, Suit.SPADE), new Card(Rank.EIGHT, Suit.SPADE)); - assertThat(Gamer.of("사람", new Hand(cards), 10_000)).isNotNull(); + assertThat(Gamer.of("사람", new TestDeckFactory().create(), 10_000)).isNotNull(); } @Test - @DisplayName("게이머가 히트할 수 있는지 확인") + @DisplayName("게이머가 hit할 수 있는지 확인") void hit() { List cards = new ArrayList<>(Arrays.asList(new Card(Rank.ACE, Suit.SPADE), new Card(Rank.EIGHT, Suit.SPADE))); - Gamer gamer = Gamer.of("사람", new Hand(cards), 10_000); DeckFactory testDeckFactory = new TestDeckFactory(); + Gamer gamer = Gamer.of("사람", testDeckFactory.create(), 10_000); gamer.hit(testDeckFactory.create()); - assertThat(gamer.getHand().getCards()).hasSize(3); + assertThat(gamer.getState().getCards()).hasSize(3); } } diff --git a/src/test/java/domain/state/BlackJackTest.java b/src/test/java/domain/state/BlackJackTest.java new file mode 100644 index 0000000000..5556349b0d --- /dev/null +++ b/src/test/java/domain/state/BlackJackTest.java @@ -0,0 +1,24 @@ +package domain.state; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.gamer.Hand; + +public class BlackJackTest { + @Test + @DisplayName("승리시 얻는 상금 비율을 확인") + void getEarningRate() { + assertThat(new BlackJack( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).getEarningRate()).isEqualTo(1.5); + } +} diff --git a/src/test/java/domain/state/BustTest.java b/src/test/java/domain/state/BustTest.java new file mode 100644 index 0000000000..57ce40df3e --- /dev/null +++ b/src/test/java/domain/state/BustTest.java @@ -0,0 +1,24 @@ +package domain.state; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.gamer.Hand; + +public class BustTest { + @Test + @DisplayName("승리시 얻는 상금 비율을 확인") + void getEarningRate() { + assertThat(new Bust( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).getEarningRate()).isEqualTo(0); + } +} diff --git a/src/test/java/domain/state/FinishedTest.java b/src/test/java/domain/state/FinishedTest.java new file mode 100644 index 0000000000..c0eef5203a --- /dev/null +++ b/src/test/java/domain/state/FinishedTest.java @@ -0,0 +1,42 @@ +package domain.state; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.TestDeckFactory; +import domain.gamer.Hand; + +public class FinishedTest { + @Test + @DisplayName("종료 상태인지 확인") + void isFinished() { + assertThat(new BlackJack( + new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).isFinished()).isTrue(); + } + + @Test + @DisplayName("hit 요청 시 예외 처리") + void hit() { + assertThatThrownBy(() -> new BlackJack( + new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).hit(new TestDeckFactory().create())) + .isInstanceOf(UnsupportedOperationException.class); + } + + @Test + @DisplayName("stay 요청 시 예외 처리") + void stay() { + assertThatThrownBy(() -> new BlackJack( + new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).stay()) + .isInstanceOf(UnsupportedOperationException.class); + } +} diff --git a/src/test/java/domain/state/PlayingTest.java b/src/test/java/domain/state/PlayingTest.java new file mode 100644 index 0000000000..fd3af2d48f --- /dev/null +++ b/src/test/java/domain/state/PlayingTest.java @@ -0,0 +1,55 @@ +package domain.state; + +import static org.assertj.core.api.Assertions.*; + +import java.util.ArrayList; +import java.util.Arrays; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.Deck; +import domain.card.deck.TestDeckFactory; +import domain.gamer.Hand; + +public class PlayingTest { + @Test + @DisplayName("종료 상태인지 확인") + void isFinished() { + assertThat(new Playing( + new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).isFinished()).isFalse(); + } + + @Test + @DisplayName("hit 시 다음 상태를 제대로 가지고 오는 지 확인") + void hit() { + Deck deck = new TestDeckFactory().create(); + State state = new Playing( + new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))).hit(deck); + assertThat(state).isInstanceOf(Playing.class); + } + + @Test + @DisplayName("stay 시 다음 상태를 제대로 가지고 오는 지 확인") + void stay() { + Deck deck = new TestDeckFactory().create(); + State state = new Playing( + new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))).stay(); + assertThat(state).isInstanceOf(Stay.class); + } + + @Test + @DisplayName("EarningRate 요청 시 예외 처리") + void getEarningRate() { + assertThatThrownBy(() -> new Playing( + new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))).getEarningRate()) + .isInstanceOf(UnsupportedOperationException.class); + } +} diff --git a/src/test/java/domain/state/StateFactoryTest.java b/src/test/java/domain/state/StateFactoryTest.java new file mode 100644 index 0000000000..15b6bcc2ad --- /dev/null +++ b/src/test/java/domain/state/StateFactoryTest.java @@ -0,0 +1,39 @@ +package domain.state; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.AbstractDeckFactory; +import domain.card.deck.Deck; +import domain.card.deck.TestDeckFactory; + +public class StateFactoryTest { + @Test + @DisplayName("Deck을 이용해 초기 카드 상태를 만들어주는 지 확인") + void create1() { + Deck deck = new TestDeckFactory().create(); + State state = StateFactory.create(deck); + assertThat(state).isInstanceOf(Playing.class); + } + + @Test + @DisplayName("Deck을 이용해 초기 카드 상태를 만들어주는 지 확인 - 블랙잭") + void create2() { + Deck deck = new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.ACE, Suit.CLUB), new Card(Rank.TEN, Suit.CLUB)); + } + }.create(); + State state = StateFactory.create(deck); + assertThat(state).isInstanceOf(BlackJack.class); + } +} diff --git a/src/test/java/domain/state/StayTest.java b/src/test/java/domain/state/StayTest.java new file mode 100644 index 0000000000..c28e44212d --- /dev/null +++ b/src/test/java/domain/state/StayTest.java @@ -0,0 +1,24 @@ +package domain.state; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.gamer.Hand; + +public class StayTest { + @Test + @DisplayName("승리시 얻는 상금 비율을 확인") + void getEarningRate() { + assertThat(new Stay( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).getEarningRate()).isEqualTo(1); + } +} From d1c9d02d4a07e095c2ea1d86f628a5d9b7525323 Mon Sep 17 00:00:00 2001 From: minuyim Date: Wed, 1 Jul 2020 23:47:36 +0900 Subject: [PATCH 11/17] =?UTF-8?q?feat:=20=EB=B8=94=EB=9E=99=EC=9E=AD?= =?UTF-8?q?=EC=97=90=20=ED=95=84=EC=9A=94=ED=95=9C=20=EB=94=9C=EB=9F=AC?= =?UTF-8?q?=EC=99=80=20=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/gamer/Dealer.java | 15 ++++++++++ src/main/java/domain/gamer/Gamer.java | 26 +++++++--------- src/main/java/domain/gamer/Player.java | 18 +++++++++++ src/test/java/domain/gamer/DealerTest.java | 16 ++++++++++ src/test/java/domain/gamer/GamerTest.java | 35 ++++++++++++---------- src/test/java/domain/gamer/HandTest.java | 3 +- src/test/java/domain/gamer/PlayerTest.java | 14 +++++++++ 7 files changed, 96 insertions(+), 31 deletions(-) create mode 100644 src/main/java/domain/gamer/Dealer.java create mode 100644 src/main/java/domain/gamer/Player.java create mode 100644 src/test/java/domain/gamer/DealerTest.java create mode 100644 src/test/java/domain/gamer/PlayerTest.java diff --git a/src/main/java/domain/gamer/Dealer.java b/src/main/java/domain/gamer/Dealer.java new file mode 100644 index 0000000000..8a98fddba2 --- /dev/null +++ b/src/main/java/domain/gamer/Dealer.java @@ -0,0 +1,15 @@ +package domain.gamer; + +import domain.card.deck.Deck; +import domain.state.State; +import domain.state.StateFactory; + +public class Dealer extends Gamer { + private Dealer(State state) { + super(new Name("딜러"), state); + } + + public static Dealer of(Deck deck) { + return new Dealer(StateFactory.create(deck)); + } +} diff --git a/src/main/java/domain/gamer/Gamer.java b/src/main/java/domain/gamer/Gamer.java index 43c074a43c..4b8eaef4bd 100644 --- a/src/main/java/domain/gamer/Gamer.java +++ b/src/main/java/domain/gamer/Gamer.java @@ -2,27 +2,27 @@ import domain.card.deck.Deck; import domain.state.State; -import domain.state.StateFactory; -public class Gamer { - private final Name name; - private State state; - private final BattingMoney battingMoney; +public abstract class Gamer { + protected final Name name; + protected State state; - private Gamer(Name name, State state, BattingMoney battingMoney) { + protected Gamer(Name name, State state) { this.name = name; this.state = state; - this.battingMoney = battingMoney; - } - - public static Gamer of(String name, Deck deck, int battingMoney) { - return new Gamer(new Name(name), StateFactory.create(deck), new BattingMoney(battingMoney)); } public void hit(Deck deck) { state = state.hit(deck); } + public void stay() { + state = state.stay(); + } + + public boolean isFinished() { + return state.isFinished(); + } public Name getName() { return name; } @@ -30,8 +30,4 @@ public Name getName() { public State getState() { return state; } - - public BattingMoney getBattingMoney() { - return battingMoney; - } } diff --git a/src/main/java/domain/gamer/Player.java b/src/main/java/domain/gamer/Player.java new file mode 100644 index 0000000000..3e81ece706 --- /dev/null +++ b/src/main/java/domain/gamer/Player.java @@ -0,0 +1,18 @@ +package domain.gamer; + +import domain.card.deck.Deck; +import domain.state.State; +import domain.state.StateFactory; + +public class Player extends Gamer { + private final BattingMoney battingMoney; + + private Player(Name name, State state, BattingMoney battingMoney) { + super(name, state); + this.battingMoney = battingMoney; + } + + public static Player of(String name, Deck deck, int battingMoney) { + return new Player(new Name(name), StateFactory.create(deck), new BattingMoney(battingMoney)); + } +} diff --git a/src/test/java/domain/gamer/DealerTest.java b/src/test/java/domain/gamer/DealerTest.java new file mode 100644 index 0000000000..634080ac41 --- /dev/null +++ b/src/test/java/domain/gamer/DealerTest.java @@ -0,0 +1,16 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +import domain.card.deck.Deck; +import domain.card.deck.TestDeckFactory; + +public class DealerTest { + @Test + void constructor() { + Deck deck = new TestDeckFactory().create(); + assertThat(Dealer.of(deck)).isNotNull(); + } +} diff --git a/src/test/java/domain/gamer/GamerTest.java b/src/test/java/domain/gamer/GamerTest.java index 3721b64352..d07984bef8 100644 --- a/src/test/java/domain/gamer/GamerTest.java +++ b/src/test/java/domain/gamer/GamerTest.java @@ -2,33 +2,38 @@ import static org.assertj.core.api.Assertions.*; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import domain.card.Card; -import domain.card.Rank; -import domain.card.Suit; import domain.card.deck.DeckFactory; import domain.card.deck.TestDeckFactory; +import domain.state.Stay; public class GamerTest { @Test - @DisplayName("생성 테스트") - void constructor() { - assertThat(Gamer.of("사람", new TestDeckFactory().create(), 10_000)).isNotNull(); + @DisplayName("게이머가 hit 할 수 있는지 확인") + void hit() { + DeckFactory testDeckFactory = new TestDeckFactory(); + Gamer gamer = Player.of("사람", testDeckFactory.create(), 10_000); + gamer.hit(testDeckFactory.create()); + assertThat(gamer.getState().getCards()).hasSize(3); } @Test - @DisplayName("게이머가 hit할 수 있는지 확인") - void hit() { - List cards = new ArrayList<>(Arrays.asList(new Card(Rank.ACE, Suit.SPADE), new Card(Rank.EIGHT, Suit.SPADE))); + @DisplayName("게이머가 stay 할 수 있는지 확인") + void stay() { DeckFactory testDeckFactory = new TestDeckFactory(); - Gamer gamer = Gamer.of("사람", testDeckFactory.create(), 10_000); + Gamer gamer = Player.of("사람", testDeckFactory.create(), 10_000); + gamer.stay(); + assertThat(gamer.getState()).isInstanceOf(Stay.class); + } + + @Test + @DisplayName("게이머의 턴이 끝났는지 확인") + void isFinished() { + DeckFactory testDeckFactory = new TestDeckFactory(); + Gamer gamer = Player.of("사람", testDeckFactory.create(), 10_000); gamer.hit(testDeckFactory.create()); - assertThat(gamer.getState().getCards()).hasSize(3); + assertThat(gamer.isFinished()).isFalse(); } } diff --git a/src/test/java/domain/gamer/HandTest.java b/src/test/java/domain/gamer/HandTest.java index ef4e920c0c..f46f01a359 100644 --- a/src/test/java/domain/gamer/HandTest.java +++ b/src/test/java/domain/gamer/HandTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.*; +import java.util.ArrayList; import java.util.Arrays; import org.junit.jupiter.api.DisplayName; @@ -22,7 +23,7 @@ void constructor() { @Test @DisplayName("카드를 더할 수 있는 지 확인") void addCard() { - Hand hand = new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB))); + Hand hand = new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB)))); hand.add(new Card(Rank.ACE, Suit.SPADE)); assertThat(hand.getCards()).hasSize(3); } diff --git a/src/test/java/domain/gamer/PlayerTest.java b/src/test/java/domain/gamer/PlayerTest.java new file mode 100644 index 0000000000..7d22ab6f62 --- /dev/null +++ b/src/test/java/domain/gamer/PlayerTest.java @@ -0,0 +1,14 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +import domain.card.deck.TestDeckFactory; + +public class PlayerTest { + @Test + void constructor() { + assertThat(Player.of("사람", new TestDeckFactory().create(), 10_000)).isNotNull(); + } +} From 87b62b57bd48e296c9b751ce80bad38b08a750cf Mon Sep 17 00:00:00 2001 From: minuyim Date: Thu, 2 Jul 2020 15:57:38 +0900 Subject: [PATCH 12/17] =?UTF-8?q?feat:=20=EB=B8=94=EB=9E=99=EC=9E=AD=20Sta?= =?UTF-8?q?te=20=EA=B0=84=20=EB=B9=84=EA=B5=90=20=EC=97=B0=EC=82=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/gamer/Player.java | 10 ++++ src/main/java/domain/state/BlackJack.java | 11 ++++- src/main/java/domain/state/Bust.java | 10 ++++ src/main/java/domain/state/Finished.java | 4 ++ src/main/java/domain/state/Playing.java | 11 ++++- src/main/java/domain/state/State.java | 4 ++ src/main/java/domain/state/Stay.java | 13 ++++++ src/test/java/domain/state/BlackJackTest.java | 44 +++++++++++++++++- src/test/java/domain/state/BustTest.java | 28 ++++++++++- src/test/java/domain/state/FinishedTest.java | 11 ++++- src/test/java/domain/state/PlayingTest.java | 21 +++++++++ src/test/java/domain/state/StayTest.java | 46 +++++++++++++++++++ 12 files changed, 206 insertions(+), 7 deletions(-) diff --git a/src/main/java/domain/gamer/Player.java b/src/main/java/domain/gamer/Player.java index 3e81ece706..f63abdbe60 100644 --- a/src/main/java/domain/gamer/Player.java +++ b/src/main/java/domain/gamer/Player.java @@ -15,4 +15,14 @@ private Player(Name name, State state, BattingMoney battingMoney) { public static Player of(String name, Deck deck, int battingMoney) { return new Player(new Name(name), StateFactory.create(deck), new BattingMoney(battingMoney)); } + + public int calculateEarning(Dealer dealer) { + if (state.isSameResult(dealer.state)) { + return 0; + } + if (state.isWin(dealer.state)) { + return (int)(battingMoney.getMoney() * state.getEarningRate()); + } + return -1 * battingMoney.getMoney(); + } } diff --git a/src/main/java/domain/state/BlackJack.java b/src/main/java/domain/state/BlackJack.java index 311cc8403d..463591a647 100644 --- a/src/main/java/domain/state/BlackJack.java +++ b/src/main/java/domain/state/BlackJack.java @@ -3,13 +3,22 @@ import domain.gamer.Hand; public class BlackJack extends Finished { - private static final double BLACKJACK_RATE = 1.5; public BlackJack(Hand hand) { super(hand); } + @Override + public boolean isWin(State state) { + return !isSameResult(state); + } + + @Override + public boolean isSameResult(State state) { + return state instanceof BlackJack; + } + @Override public double getEarningRate() { return BLACKJACK_RATE; diff --git a/src/main/java/domain/state/Bust.java b/src/main/java/domain/state/Bust.java index fd36ee1480..157e43a774 100644 --- a/src/main/java/domain/state/Bust.java +++ b/src/main/java/domain/state/Bust.java @@ -7,6 +7,16 @@ public Bust(Hand hand) { super(hand); } + @Override + public boolean isWin(State state) { + return false; + } + + @Override + public boolean isSameResult(State state) { + return state instanceof Bust; + } + @Override public double getEarningRate() { return 0; diff --git a/src/main/java/domain/state/Finished.java b/src/main/java/domain/state/Finished.java index 0bd53c6f33..ded303dfa4 100644 --- a/src/main/java/domain/state/Finished.java +++ b/src/main/java/domain/state/Finished.java @@ -22,4 +22,8 @@ public State stay() { public boolean isFinished() { return true; } + + public int calculateScore() { + return hand.calculateScore(); + } } diff --git a/src/main/java/domain/state/Playing.java b/src/main/java/domain/state/Playing.java index d914b7dcae..5177d5444e 100644 --- a/src/main/java/domain/state/Playing.java +++ b/src/main/java/domain/state/Playing.java @@ -28,8 +28,17 @@ public boolean isFinished() { } @Override - public double getEarningRate() { + public boolean isWin(State state) { throw new UnsupportedOperationException(); } + @Override + public boolean isSameResult(State state) { + throw new UnsupportedOperationException(); + } + + @Override + public double getEarningRate() { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/domain/state/State.java b/src/main/java/domain/state/State.java index 4ee547933e..2ff893dcdb 100644 --- a/src/main/java/domain/state/State.java +++ b/src/main/java/domain/state/State.java @@ -12,6 +12,10 @@ public interface State { boolean isFinished(); + boolean isWin(State state); + + boolean isSameResult(State state); + double getEarningRate(); List getCards(); diff --git a/src/main/java/domain/state/Stay.java b/src/main/java/domain/state/Stay.java index 7b4d53d1ba..a5a697c787 100644 --- a/src/main/java/domain/state/Stay.java +++ b/src/main/java/domain/state/Stay.java @@ -9,6 +9,19 @@ public Stay(Hand hand) { super(hand); } + @Override + public boolean isWin(State state) { + if (state instanceof Stay) { + return calculateScore() > ((Stay)state).calculateScore(); + } + return !state.isWin(this); + } + + @Override + public boolean isSameResult(State state) { + return state instanceof Stay && calculateScore() == ((Stay)state).calculateScore(); + } + @Override public double getEarningRate() { return NORMAL_RATE; diff --git a/src/test/java/domain/state/BlackJackTest.java b/src/test/java/domain/state/BlackJackTest.java index 5556349b0d..811eed39fe 100644 --- a/src/test/java/domain/state/BlackJackTest.java +++ b/src/test/java/domain/state/BlackJackTest.java @@ -18,7 +18,47 @@ public class BlackJackTest { void getEarningRate() { assertThat(new BlackJack( new Hand( - Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), - new Card(Rank.NINE, Suit.CLUB)))).getEarningRate()).isEqualTo(1.5); + Arrays.asList(new Card(Rank.TEN, Suit.SPADE), + new Card(Rank.ACE, Suit.CLUB)))).getEarningRate()).isEqualTo(1.5); + } + + @Test + @DisplayName("블랙잭이 이기는 경우 확인") + void isWin1() { + BlackJack blackJack = new BlackJack( + new Hand(Arrays.asList(new Card(Rank.TEN, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)))); + Stay stay = new Stay(new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.NINE, Suit.CLUB)))); + assertThat(blackJack.isWin(stay)).isTrue(); + } + + @Test + @DisplayName("블랙잭이 이기는게 아닌 경우 확인") + void isWin2() { + BlackJack blackJack1 = new BlackJack( + new Hand(Arrays.asList(new Card(Rank.TEN, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)))); + BlackJack blackJack2 = new BlackJack( + new Hand(Arrays.asList(new Card(Rank.KING, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)))); + + assertThat(blackJack1.isWin(blackJack2)).isFalse(); + } + + @Test + @DisplayName("블랙잭과 동일 결과가 아닌지 확인") + void isSameResult1() { + BlackJack blackJack = new BlackJack( + new Hand(Arrays.asList(new Card(Rank.TEN, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)))); + Stay stay = new Stay(new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.NINE, Suit.CLUB)))); + assertThat(blackJack.isSameResult(stay)); + } + + @Test + @DisplayName("블랙잭과 동일 결과인지 확인") + void isSameResult2() { + BlackJack blackJack1 = new BlackJack( + new Hand(Arrays.asList(new Card(Rank.TEN, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)))); + BlackJack blackJack2 = new BlackJack( + new Hand(Arrays.asList(new Card(Rank.KING, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)))); + + assertThat(blackJack1.isSameResult(blackJack2)).isTrue(); } } diff --git a/src/test/java/domain/state/BustTest.java b/src/test/java/domain/state/BustTest.java index 57ce40df3e..a7e27f7e83 100644 --- a/src/test/java/domain/state/BustTest.java +++ b/src/test/java/domain/state/BustTest.java @@ -18,7 +18,33 @@ public class BustTest { void getEarningRate() { assertThat(new Bust( new Hand( - Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), new Card(Rank.NINE, Suit.CLUB)))).getEarningRate()).isEqualTo(0); } + + @Test + @DisplayName("bust가 지는 경우 확인") + void isWin() { + Bust bust = new Bust( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))); + assertThat(bust.isWin(new Bust( + new Hand( + Arrays.asList(new Card(Rank.SEVEN, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))))).isFalse(); + } + + @Test + @DisplayName("bust가 같은 결과가 아닌 경우 확인") + void isSameResult() { + Bust bust = new Bust( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))); + assertThat(bust.isWin(new Bust( + new Hand( + Arrays.asList(new Card(Rank.SEVEN, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))))).isFalse(); + } } diff --git a/src/test/java/domain/state/FinishedTest.java b/src/test/java/domain/state/FinishedTest.java index c0eef5203a..b91bf01a44 100644 --- a/src/test/java/domain/state/FinishedTest.java +++ b/src/test/java/domain/state/FinishedTest.java @@ -25,7 +25,7 @@ void isFinished() { @Test @DisplayName("hit 요청 시 예외 처리") void hit() { - assertThatThrownBy(() -> new BlackJack( + assertThatThrownBy(() -> new Stay( new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.NINE, Suit.CLUB)))).hit(new TestDeckFactory().create())) .isInstanceOf(UnsupportedOperationException.class); @@ -34,9 +34,16 @@ void hit() { @Test @DisplayName("stay 요청 시 예외 처리") void stay() { - assertThatThrownBy(() -> new BlackJack( + assertThatThrownBy(() -> new Stay( new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.NINE, Suit.CLUB)))).stay()) .isInstanceOf(UnsupportedOperationException.class); } + + @Test + void calculateScore() { + assertThat(new Stay( + new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).calculateScore()).isEqualTo(17); + } } diff --git a/src/test/java/domain/state/PlayingTest.java b/src/test/java/domain/state/PlayingTest.java index fd3af2d48f..15e7c700b0 100644 --- a/src/test/java/domain/state/PlayingTest.java +++ b/src/test/java/domain/state/PlayingTest.java @@ -52,4 +52,25 @@ void getEarningRate() { new Card(Rank.NINE, Suit.CLUB))))).getEarningRate()) .isInstanceOf(UnsupportedOperationException.class); } + + @Test + @DisplayName("isSameResult 요청 시 예외 발생") + void isSameResult() { + assertThatThrownBy(() -> new Playing( + new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))).isSameResult( + new Stay(new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))))) + .isInstanceOf(UnsupportedOperationException.class); + } + @Test + @DisplayName("isSameResult 요청 시 예외 발생") + void isWin() { + assertThatThrownBy(() -> new Playing( + new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))).isWin( + new Stay(new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))))) + .isInstanceOf(UnsupportedOperationException.class); + } } diff --git a/src/test/java/domain/state/StayTest.java b/src/test/java/domain/state/StayTest.java index c28e44212d..9e9a4dc964 100644 --- a/src/test/java/domain/state/StayTest.java +++ b/src/test/java/domain/state/StayTest.java @@ -21,4 +21,50 @@ void getEarningRate() { Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.NINE, Suit.CLUB)))).getEarningRate()).isEqualTo(1); } + + @Test + @DisplayName("stay가 이기는 경우 확인") + void isWin1() { + Stay stay = new Stay( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.KING, Suit.CLUB)))); + assertThat(stay.isWin(new Bust( + new Hand( + Arrays.asList(new Card(Rank.SEVEN, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))))).isTrue(); + } + + @Test + @DisplayName("stay가 지는 경우 확인") + void isWin2() { + Stay stay = new Stay( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.KING, Suit.CLUB)))); + assertThat(stay.isWin(new BlackJack( + new Hand( + Arrays.asList(new Card(Rank.ACE, Suit.SPADE), new Card(Rank.KING, Suit.CLUB)))))).isFalse(); + } + + @Test + @DisplayName("stay가 stay에게 지는 경우 확인") + void isWin3() { + Stay stay = new Stay( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.KING, Suit.CLUB)))); + assertThat(stay.isWin(new Stay( + new Hand( + Arrays.asList(new Card(Rank.TEN, Suit.SPADE), new Card(Rank.KING, Suit.CLUB)))))).isFalse(); + } + + @Test + @DisplayName("자신과 동일한 결과일지 확인") + void isSameResult() { + Stay stay = new Stay( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.NINE, Suit.CLUB)))); + assertThat(stay.isSameResult(new Stay( + new Hand( + Arrays.asList(new Card(Rank.SEVEN, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))))).isFalse(); + } } From 79838786ea917a86874119a90b80d8d206671f40 Mon Sep 17 00:00:00 2001 From: minuyim Date: Thu, 2 Jul 2020 16:21:32 +0900 Subject: [PATCH 13/17] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=EB=A5=BC=20=EC=B6=9C=EB=A0=A5=ED=95=98=EB=8A=94=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/domain/gamer/BattingMoney.java | 4 + src/main/java/domain/gamer/Gamer.java | 1 + src/test/java/domain/gamer/PlayerTest.java | 81 ++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/src/main/java/domain/gamer/BattingMoney.java b/src/main/java/domain/gamer/BattingMoney.java index 3301f1f3fd..386056860d 100644 --- a/src/main/java/domain/gamer/BattingMoney.java +++ b/src/main/java/domain/gamer/BattingMoney.java @@ -13,4 +13,8 @@ private void validate(int money) { throw new IllegalArgumentException("음수는 허용되지 않습니다. input : " + money); } } + + public int getMoney() { + return money; + } } diff --git a/src/main/java/domain/gamer/Gamer.java b/src/main/java/domain/gamer/Gamer.java index 4b8eaef4bd..ab08cf462c 100644 --- a/src/main/java/domain/gamer/Gamer.java +++ b/src/main/java/domain/gamer/Gamer.java @@ -23,6 +23,7 @@ public void stay() { public boolean isFinished() { return state.isFinished(); } + public Name getName() { return name; } diff --git a/src/test/java/domain/gamer/PlayerTest.java b/src/test/java/domain/gamer/PlayerTest.java index 7d22ab6f62..5afdfc3069 100644 --- a/src/test/java/domain/gamer/PlayerTest.java +++ b/src/test/java/domain/gamer/PlayerTest.java @@ -2,13 +2,94 @@ import static org.assertj.core.api.Assertions.*; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.AbstractDeckFactory; import domain.card.deck.TestDeckFactory; public class PlayerTest { @Test + @DisplayName("생성 테스트") void constructor() { assertThat(Player.of("사람", new TestDeckFactory().create(), 10_000)).isNotNull(); } + + @Test + @DisplayName("게임 후 수익 결과 계산 - 무승부") + void calculateEarning1() { + Player player = Player.of("사람", new TestDeckFactory().create(), 10_000); + Dealer dealer = Dealer.of(new TestDeckFactory().create()); + + player.stay(); + dealer.stay(); + + assertThat(player.calculateEarning(dealer)).isEqualTo(0); + } + + @Test + @DisplayName("게임 후 수익 결과 계산 - 이기는 경우") + void calculateEarning2() { + Player player = Player.of("사람", new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.KING, Suit.CLUB), new Card(Rank.TEN, Suit.CLUB)); + } + }.create(), 10_000); + Dealer dealer = Dealer.of(new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.ACE, Suit.CLUB), new Card(Rank.SEVEN, Suit.CLUB)); + } + }.create()); + + player.stay(); + dealer.stay(); + + assertThat(player.calculateEarning(dealer)).isEqualTo(10_000); + } + + @Test + @DisplayName("게임 후 수익 결과 계산 - 이기는 경우") + void calculateEarning3() { + Player player = Player.of("사람", new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.KING, Suit.CLUB), new Card(Rank.ACE, Suit.CLUB)); + } + }.create(), 10_000); + Dealer dealer = Dealer.of(new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.ACE, Suit.CLUB), new Card(Rank.SEVEN, Suit.CLUB)); + } + }.create()); + + dealer.stay(); + + assertThat(player.calculateEarning(dealer)).isEqualTo(15_000); + } + + @Test + @DisplayName("게임 후 수익 결과 계산 - 지는 경우") + void calculateEarning4() { + Player player = Player.of("사람", new TestDeckFactory().create(), 10_000); + Dealer dealer = Dealer.of(new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.TEN, Suit.SPADE), new Card(Rank.TEN, Suit.CLUB)); + } + }.create()); + + player.stay(); + dealer.stay(); + + assertThat(player.calculateEarning(dealer)).isEqualTo(-10_000); + } } From 58a6d48c1e5ac54e996d0087760cd7bd4a65cfe8 Mon Sep 17 00:00:00 2001 From: minuyim Date: Thu, 2 Jul 2020 18:32:24 +0900 Subject: [PATCH 14/17] =?UTF-8?q?feat:=20hit=20=EA=B0=80=EB=8A=A5=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=EB=A5=BC=20=ED=99=95=EC=9D=B8=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/gamer/Dealer.java | 7 +++++++ src/main/java/domain/gamer/Gamer.java | 2 ++ src/main/java/domain/gamer/Player.java | 5 +++++ src/main/java/domain/state/Finished.java | 4 ---- src/main/java/domain/state/GamerState.java | 5 +++++ src/main/java/domain/state/State.java | 2 ++ src/main/java/domain/state/Stay.java | 4 ++-- src/main/java/empty.txt | 0 src/test/java/empty.txt | 0 9 files changed, 23 insertions(+), 6 deletions(-) delete mode 100644 src/main/java/empty.txt delete mode 100644 src/test/java/empty.txt diff --git a/src/main/java/domain/gamer/Dealer.java b/src/main/java/domain/gamer/Dealer.java index 8a98fddba2..e412d667b0 100644 --- a/src/main/java/domain/gamer/Dealer.java +++ b/src/main/java/domain/gamer/Dealer.java @@ -5,6 +5,8 @@ import domain.state.StateFactory; public class Dealer extends Gamer { + private static final int HIT_THRESHOLD = 17; + private Dealer(State state) { super(new Name("딜러"), state); } @@ -12,4 +14,9 @@ private Dealer(State state) { public static Dealer of(Deck deck) { return new Dealer(StateFactory.create(deck)); } + + @Override + public boolean canHit() { + return !state.isFinished() && state.calculateScore() < HIT_THRESHOLD; + } } diff --git a/src/main/java/domain/gamer/Gamer.java b/src/main/java/domain/gamer/Gamer.java index ab08cf462c..688eba9508 100644 --- a/src/main/java/domain/gamer/Gamer.java +++ b/src/main/java/domain/gamer/Gamer.java @@ -31,4 +31,6 @@ public Name getName() { public State getState() { return state; } + + public abstract boolean canHit(); } diff --git a/src/main/java/domain/gamer/Player.java b/src/main/java/domain/gamer/Player.java index f63abdbe60..6d00ddab25 100644 --- a/src/main/java/domain/gamer/Player.java +++ b/src/main/java/domain/gamer/Player.java @@ -25,4 +25,9 @@ public int calculateEarning(Dealer dealer) { } return -1 * battingMoney.getMoney(); } + + @Override + public boolean canHit() { + return !state.isFinished(); + } } diff --git a/src/main/java/domain/state/Finished.java b/src/main/java/domain/state/Finished.java index ded303dfa4..0bd53c6f33 100644 --- a/src/main/java/domain/state/Finished.java +++ b/src/main/java/domain/state/Finished.java @@ -22,8 +22,4 @@ public State stay() { public boolean isFinished() { return true; } - - public int calculateScore() { - return hand.calculateScore(); - } } diff --git a/src/main/java/domain/state/GamerState.java b/src/main/java/domain/state/GamerState.java index ca4885ba33..dd292d12ce 100644 --- a/src/main/java/domain/state/GamerState.java +++ b/src/main/java/domain/state/GamerState.java @@ -17,4 +17,9 @@ public GamerState(Hand hand) { public List getCards() { return Collections.unmodifiableList(hand.getCards()); } + + public int calculateScore() { + return hand.calculateScore(); + } + } diff --git a/src/main/java/domain/state/State.java b/src/main/java/domain/state/State.java index 2ff893dcdb..2b04de28ff 100644 --- a/src/main/java/domain/state/State.java +++ b/src/main/java/domain/state/State.java @@ -10,6 +10,8 @@ public interface State { State stay(); + int calculateScore(); + boolean isFinished(); boolean isWin(State state); diff --git a/src/main/java/domain/state/Stay.java b/src/main/java/domain/state/Stay.java index a5a697c787..8c3846dc1e 100644 --- a/src/main/java/domain/state/Stay.java +++ b/src/main/java/domain/state/Stay.java @@ -12,14 +12,14 @@ public Stay(Hand hand) { @Override public boolean isWin(State state) { if (state instanceof Stay) { - return calculateScore() > ((Stay)state).calculateScore(); + return calculateScore() > state.calculateScore(); } return !state.isWin(this); } @Override public boolean isSameResult(State state) { - return state instanceof Stay && calculateScore() == ((Stay)state).calculateScore(); + return state instanceof Stay && calculateScore() == state.calculateScore(); } @Override diff --git a/src/main/java/empty.txt b/src/main/java/empty.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/test/java/empty.txt b/src/test/java/empty.txt deleted file mode 100644 index e69de29bb2..0000000000 From 7520fb1281b167b8f978ea9c973d5bc5564350f0 Mon Sep 17 00:00:00 2001 From: minuyim Date: Thu, 2 Jul 2020 19:16:25 +0900 Subject: [PATCH 15/17] =?UTF-8?q?feat:=20Players=EC=9D=98=20totalEarning?= =?UTF-8?q?=20=EA=B3=84=EC=82=B0=20=EB=B0=8F=20Dealer=20Earning=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/gamer/Dealer.java | 4 ++ src/main/java/domain/gamer/Players.java | 24 ++++++++++ src/test/java/domain/gamer/DealerTest.java | 29 +++++++++++ src/test/java/domain/gamer/PlayersTest.java | 53 +++++++++++++++++++++ 4 files changed, 110 insertions(+) create mode 100644 src/main/java/domain/gamer/Players.java create mode 100644 src/test/java/domain/gamer/PlayersTest.java diff --git a/src/main/java/domain/gamer/Dealer.java b/src/main/java/domain/gamer/Dealer.java index e412d667b0..0b7f22bb3d 100644 --- a/src/main/java/domain/gamer/Dealer.java +++ b/src/main/java/domain/gamer/Dealer.java @@ -19,4 +19,8 @@ public static Dealer of(Deck deck) { public boolean canHit() { return !state.isFinished() && state.calculateScore() < HIT_THRESHOLD; } + + public int calculateEarning(Players players) { + return -1 * players.calculateTotalEarning(this); + } } diff --git a/src/main/java/domain/gamer/Players.java b/src/main/java/domain/gamer/Players.java new file mode 100644 index 0000000000..ed273ffd63 --- /dev/null +++ b/src/main/java/domain/gamer/Players.java @@ -0,0 +1,24 @@ +package domain.gamer; + +import java.util.List; + +public class Players { + private final List players; + + public Players(List players) { + validate(players); + this.players = players; + } + + private void validate(List players) { + if (players.isEmpty()) { + throw new IllegalArgumentException("플레이어가 비어있습니다."); + } + } + + public int calculateTotalEarning(Dealer dealer) { + return players.stream() + .mapToInt(player -> player.calculateEarning(dealer)) + .sum(); + } +} diff --git a/src/test/java/domain/gamer/DealerTest.java b/src/test/java/domain/gamer/DealerTest.java index 634080ac41..e9d06eebcd 100644 --- a/src/test/java/domain/gamer/DealerTest.java +++ b/src/test/java/domain/gamer/DealerTest.java @@ -2,15 +2,44 @@ import static org.assertj.core.api.Assertions.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.AbstractDeckFactory; import domain.card.deck.Deck; import domain.card.deck.TestDeckFactory; public class DealerTest { @Test + @DisplayName("생성 테스트") void constructor() { Deck deck = new TestDeckFactory().create(); assertThat(Dealer.of(deck)).isNotNull(); } + + @Test + @DisplayName("플레이어들의 총 수익을 계산") + void calculateEarning() { + Deck deck = new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.ACE, Suit.CLUB), new Card(Rank.TEN, Suit.CLUB), + new Card(Rank.ACE, Suit.DIAMOND), new Card(Rank.ACE, Suit.CLUB)); + } + }.create(); + Player player = Player.of("사람", deck, 10_000); + player.stay(); + Players players = new Players( + Collections.singletonList(player)); + Dealer dealer = Dealer.of(deck); + + assertThat(dealer.calculateEarning(players)).isEqualTo(10_000); + } } diff --git a/src/test/java/domain/gamer/PlayersTest.java b/src/test/java/domain/gamer/PlayersTest.java new file mode 100644 index 0000000000..e75ddc650f --- /dev/null +++ b/src/test/java/domain/gamer/PlayersTest.java @@ -0,0 +1,53 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.AbstractDeckFactory; +import domain.card.deck.Deck; +import domain.card.deck.TestDeckFactory; + +public class PlayersTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + assertThat(new Players( + Collections.singletonList(Player.of("사람", new TestDeckFactory().create(), 10_000)))).isNotNull(); + } + + @Test + @DisplayName("생성 테스트 - 리스트가 비어있는 경우 예외 처리") + void constructorException() { + assertThatThrownBy(() -> new Players(Collections.emptyList())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("비어"); + } + + @Test + @DisplayName("플레이어들의 총 수익을 계산") + void calculateTotalEarning() { + Deck deck = new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.ACE, Suit.CLUB), new Card(Rank.TEN, Suit.CLUB), + new Card(Rank.ACE, Suit.DIAMOND), new Card(Rank.ACE, Suit.CLUB)); + } + }.create(); + Player player = Player.of("사람", deck, 10_000); + player.stay(); + Players players = new Players( + Collections.singletonList(player)); + Dealer dealer = Dealer.of(deck); + + assertThat(players.calculateTotalEarning(dealer)).isEqualTo(-10_000); + } +} From 66df98d46af7f02fd79e1489e9da8ed6abedcb5c Mon Sep 17 00:00:00 2001 From: minuyim Date: Thu, 2 Jul 2020 23:58:00 +0900 Subject: [PATCH 16/17] =?UTF-8?q?feat:=20Controller,=20View,=20Dto=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 13 +++++ .../java/controller/BlackJackController.java | 57 ++++++++++++++++++ src/main/java/controller/YesOrNo.java | 31 ++++++++++ src/main/java/domain/card/Card.java | 8 +++ src/main/java/domain/card/Rank.java | 4 ++ src/main/java/domain/gamer/Dealer.java | 7 +++ src/main/java/domain/gamer/Gamer.java | 15 ++++- src/main/java/domain/gamer/Name.java | 4 ++ src/main/java/domain/gamer/Players.java | 5 ++ src/main/java/domain/state/Playing.java | 2 +- src/main/java/dto/CardDto.java | 34 +++++++++++ src/main/java/dto/GamerDto.java | 38 ++++++++++++ src/main/java/dto/GamerEarningDto.java | 38 ++++++++++++ src/main/java/dto/GamerWithScoreDto.java | 46 +++++++++++++++ src/main/java/dto/PlayerInfo.java | 19 ++++++ src/main/java/view/InputView.java | 44 ++++++++++++++ src/main/java/view/OutputView.java | 58 +++++++++++++++++++ 17 files changed, 420 insertions(+), 3 deletions(-) create mode 100644 src/main/java/Application.java create mode 100644 src/main/java/controller/BlackJackController.java create mode 100644 src/main/java/controller/YesOrNo.java create mode 100644 src/main/java/dto/CardDto.java create mode 100644 src/main/java/dto/GamerDto.java create mode 100644 src/main/java/dto/GamerEarningDto.java create mode 100644 src/main/java/dto/GamerWithScoreDto.java create mode 100644 src/main/java/dto/PlayerInfo.java create mode 100644 src/main/java/view/InputView.java create mode 100644 src/main/java/view/OutputView.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 0000000000..0a4db9f428 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,13 @@ +import java.util.Scanner; + +import controller.BlackJackController; +import view.InputView; +import view.OutputView; + +public class Application { + public static void main(String[] args) { + BlackJackController blackJackController = new BlackJackController(new InputView(new Scanner(System.in)), + new OutputView()); + blackJackController.run(); + } +} diff --git a/src/main/java/controller/BlackJackController.java b/src/main/java/controller/BlackJackController.java new file mode 100644 index 0000000000..0d42eb03b9 --- /dev/null +++ b/src/main/java/controller/BlackJackController.java @@ -0,0 +1,57 @@ +package controller; + +import static java.util.stream.Collectors.*; + +import java.util.List; + +import domain.card.deck.Deck; +import domain.card.deck.DeckFactory; +import domain.card.deck.RandomShuffledDeckFactory; +import domain.gamer.Dealer; +import domain.gamer.Player; +import domain.gamer.Players; +import dto.GamerDto; +import dto.GamerEarningDto; +import dto.GamerWithScoreDto; +import dto.PlayerInfo; +import view.InputView; +import view.OutputView; + +public class BlackJackController { + private final InputView inputView; + private final OutputView outputView; + + public BlackJackController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + List playerInfos = inputView.inputPlayerInfos(); + DeckFactory randomShuffledDeckFactory = new RandomShuffledDeckFactory(); + Deck deck = randomShuffledDeckFactory.create(); + + Players players = playerInfos.stream() + .map(playerInfo -> Player.of(playerInfo.getName(), deck, playerInfo.getBattingMoney())) + .collect(collectingAndThen(toList(), Players::new)); + Dealer dealer = Dealer.of(deck); + + outputView.printGamersCards(GamerDto.listOf(dealer, players.getPlayers())); + + for (Player player : players.getPlayers()) { + while (player.canHit()) { + YesOrNo yesOrNo = YesOrNo.findByShortName(inputView.inputYesOrNo(player.getName())); + yesOrNo.execute(player, deck); + outputView.printGamerCards(GamerDto.of(player)); + } + } + + while (dealer.canHit()) { + outputView.printDealerHit(); + dealer.hit(deck); + } + + outputView.printGamersCardsWithScore(GamerWithScoreDto.listOf(dealer, players.getPlayers())); + outputView.printEarning(GamerEarningDto.listOf(dealer, players)); + } +} diff --git a/src/main/java/controller/YesOrNo.java b/src/main/java/controller/YesOrNo.java new file mode 100644 index 0000000000..5906159963 --- /dev/null +++ b/src/main/java/controller/YesOrNo.java @@ -0,0 +1,31 @@ +package controller; + +import java.util.Arrays; +import java.util.function.BiConsumer; + +import domain.card.deck.Deck; +import domain.gamer.Player; + +public enum YesOrNo { + YES("y", Player::hit), + NO("n", ((player, deck) -> player.stay())); + + private final String shortName; + private final BiConsumer playerBehavior; + + YesOrNo(String shortName, BiConsumer playerBehavior) { + this.shortName = shortName; + this.playerBehavior = playerBehavior; + } + + public static YesOrNo findByShortName(String name) { + return Arrays.stream(values()) + .filter(yesOrNo -> yesOrNo.shortName.equalsIgnoreCase(name)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("잘못된 명령어입니다")); + } + + public void execute(Player player, Deck deck) { + playerBehavior.accept(player, deck); + } +} diff --git a/src/main/java/domain/card/Card.java b/src/main/java/domain/card/Card.java index b2193c5625..c2e195988e 100644 --- a/src/main/java/domain/card/Card.java +++ b/src/main/java/domain/card/Card.java @@ -19,6 +19,14 @@ public int getScore() { return rank.getValue(); } + public String getRank() { + return rank.getPattern(); + } + + public String getSuit() { + return suit.getPattern(); + } + @Override public boolean equals(Object o) { if (this == o) diff --git a/src/main/java/domain/card/Rank.java b/src/main/java/domain/card/Rank.java index 006d0b8a84..f78f091c71 100644 --- a/src/main/java/domain/card/Rank.java +++ b/src/main/java/domain/card/Rank.java @@ -26,4 +26,8 @@ public enum Rank { public int getValue() { return value; } + + public String getPattern() { + return pattern; + } } diff --git a/src/main/java/domain/gamer/Dealer.java b/src/main/java/domain/gamer/Dealer.java index 0b7f22bb3d..6c31f87dad 100644 --- a/src/main/java/domain/gamer/Dealer.java +++ b/src/main/java/domain/gamer/Dealer.java @@ -1,5 +1,8 @@ package domain.gamer; +import java.util.List; + +import domain.card.Card; import domain.card.deck.Deck; import domain.state.State; import domain.state.StateFactory; @@ -23,4 +26,8 @@ public boolean canHit() { public int calculateEarning(Players players) { return -1 * players.calculateTotalEarning(this); } + + public List getOneCards() { + return getState().getCards().subList(0, 1); + } } diff --git a/src/main/java/domain/gamer/Gamer.java b/src/main/java/domain/gamer/Gamer.java index 688eba9508..01e3e7f360 100644 --- a/src/main/java/domain/gamer/Gamer.java +++ b/src/main/java/domain/gamer/Gamer.java @@ -1,5 +1,8 @@ package domain.gamer; +import java.util.List; + +import domain.card.Card; import domain.card.deck.Deck; import domain.state.State; @@ -24,13 +27,21 @@ public boolean isFinished() { return state.isFinished(); } - public Name getName() { - return name; + public int calculateScore() { + return state.calculateScore(); + } + + public String getName() { + return name.getName(); } public State getState() { return state; } + public List getCards() { + return state.getCards(); + } + public abstract boolean canHit(); } diff --git a/src/main/java/domain/gamer/Name.java b/src/main/java/domain/gamer/Name.java index 4137ba5d89..e19b40d460 100644 --- a/src/main/java/domain/gamer/Name.java +++ b/src/main/java/domain/gamer/Name.java @@ -14,4 +14,8 @@ private void validate(String name) { throw new IllegalArgumentException("이름에 공백이 들어가면 안됩니다."); } } + + public String getName() { + return name; + } } diff --git a/src/main/java/domain/gamer/Players.java b/src/main/java/domain/gamer/Players.java index ed273ffd63..bd718f2a5d 100644 --- a/src/main/java/domain/gamer/Players.java +++ b/src/main/java/domain/gamer/Players.java @@ -1,5 +1,6 @@ package domain.gamer; +import java.util.Collections; import java.util.List; public class Players { @@ -21,4 +22,8 @@ public int calculateTotalEarning(Dealer dealer) { .mapToInt(player -> player.calculateEarning(dealer)) .sum(); } + + public List getPlayers() { + return Collections.unmodifiableList(players); + } } diff --git a/src/main/java/domain/state/Playing.java b/src/main/java/domain/state/Playing.java index 5177d5444e..11bd229499 100644 --- a/src/main/java/domain/state/Playing.java +++ b/src/main/java/domain/state/Playing.java @@ -12,7 +12,7 @@ public Playing(Hand hand) { public State hit(Deck deck) { hand.add(deck.pop()); if (hand.isBust()) { - new Bust(hand); + return new Bust(hand); } return new Playing(hand); } diff --git a/src/main/java/dto/CardDto.java b/src/main/java/dto/CardDto.java new file mode 100644 index 0000000000..97c8dc2661 --- /dev/null +++ b/src/main/java/dto/CardDto.java @@ -0,0 +1,34 @@ +package dto; + +import java.util.List; +import java.util.stream.Collectors; + +import domain.card.Card; + +public class CardDto { + private String rank; + private String suit; + + public CardDto(String rank, String suit) { + this.rank = rank; + this.suit = suit; + } + + public static List listOf(List cards) { + return cards.stream() + .map(CardDto::of) + .collect(Collectors.toList()); + } + + private static CardDto of(Card card) { + return new CardDto(card.getRank(), card.getSuit()); + } + + public String getRank() { + return rank; + } + + public String getSuit() { + return suit; + } +} diff --git a/src/main/java/dto/GamerDto.java b/src/main/java/dto/GamerDto.java new file mode 100644 index 0000000000..a73d4e502a --- /dev/null +++ b/src/main/java/dto/GamerDto.java @@ -0,0 +1,38 @@ +package dto; + +import java.util.List; +import java.util.stream.Collectors; + +import domain.gamer.Dealer; +import domain.gamer.Gamer; +import domain.gamer.Player; + +public class GamerDto { + private String name; + private List cards; + + public GamerDto(String name, List cards) { + this.name = name; + this.cards = cards; + } + + public static GamerDto of(Gamer gamer) { + return new GamerDto(gamer.getName(), CardDto.listOf(gamer.getCards())); + } + + public static List listOf(Dealer dealer, List players) { + List gamers = players.stream() + .map(GamerDto::of) + .collect(Collectors.toList()); + gamers.add(0, new GamerDto(dealer.getName(), CardDto.listOf(dealer.getOneCards()))); + return gamers; + } + + public String getName() { + return name; + } + + public List getCards() { + return cards; + } +} diff --git a/src/main/java/dto/GamerEarningDto.java b/src/main/java/dto/GamerEarningDto.java new file mode 100644 index 0000000000..b73adf68c2 --- /dev/null +++ b/src/main/java/dto/GamerEarningDto.java @@ -0,0 +1,38 @@ +package dto; + +import java.util.List; +import java.util.stream.Collectors; + +import domain.gamer.Dealer; +import domain.gamer.Player; +import domain.gamer.Players; + +public class GamerEarningDto { + private String name; + private int earningMoney; + + public GamerEarningDto(String name, int earningMoney) { + this.name = name; + this.earningMoney = earningMoney; + } + + public static List listOf(Dealer dealer, Players players) { + List gamers = players.getPlayers().stream() + .map(player -> GamerEarningDto.of(player, dealer)) + .collect(Collectors.toList()); + gamers.add(0, new GamerEarningDto(dealer.getName(), dealer.calculateEarning(players))); + return gamers; + } + + public static GamerEarningDto of(Player player, Dealer dealer) { + return new GamerEarningDto(player.getName(), player.calculateEarning(dealer)); + } + + public String getName() { + return name; + } + + public int getEarningMoney() { + return earningMoney; + } +} diff --git a/src/main/java/dto/GamerWithScoreDto.java b/src/main/java/dto/GamerWithScoreDto.java new file mode 100644 index 0000000000..d3e4934546 --- /dev/null +++ b/src/main/java/dto/GamerWithScoreDto.java @@ -0,0 +1,46 @@ +package dto; + +import java.util.List; +import java.util.stream.Collectors; + +import domain.gamer.Dealer; +import domain.gamer.Gamer; +import domain.gamer.Player; + +public class GamerWithScoreDto { + private String name; + private List cards; + private int score; + + public GamerWithScoreDto(String name, List cards, int score) { + this.name = name; + this.cards = cards; + this.score = score; + } + + public static List listOf(Dealer dealer, List players) { + List gamers = players.stream() + .map(GamerWithScoreDto::of) + .collect(Collectors.toList()); + gamers.add(0, new GamerWithScoreDto(dealer.getName(), CardDto.listOf(dealer.getCards()), + dealer.calculateScore())); + return gamers; + } + + private static GamerWithScoreDto of(Gamer gamer) { + return new GamerWithScoreDto(gamer.getName(), CardDto.listOf(gamer.getCards()), + gamer.calculateScore()); + } + + public String getName() { + return name; + } + + public List getCards() { + return cards; + } + + public int getScore() { + return score; + } +} diff --git a/src/main/java/dto/PlayerInfo.java b/src/main/java/dto/PlayerInfo.java new file mode 100644 index 0000000000..940c5d61f1 --- /dev/null +++ b/src/main/java/dto/PlayerInfo.java @@ -0,0 +1,19 @@ +package dto; + +public class PlayerInfo { + private String name; + private int battingMoney; + + public PlayerInfo(String name, int battingMoney) { + this.name = name; + this.battingMoney = battingMoney; + } + + public String getName() { + return name; + } + + public int getBattingMoney() { + return battingMoney; + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 0000000000..7fe98761e9 --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,44 @@ +package view; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; + +import dto.PlayerInfo; + +public class InputView { + private static final String DELIMITER = ","; + private final Scanner scanner; + + public InputView(Scanner scanner) { + this.scanner = scanner; + } + + public List inputPlayerInfos() { + List playerInfos = new ArrayList<>(); + List names = inputName(); + for (String name : names) { + playerInfos.add(new PlayerInfo(name, inputBattingMoney(name))); + } + return playerInfos; + } + + private List inputName() { + System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); + return Arrays.stream(scanner.nextLine().split(DELIMITER)) + .map(String::trim) + .collect(Collectors.toList()); + } + + private int inputBattingMoney(String name) { + System.out.println(name + "의 배팅 금액은?"); + return Integer.parseInt(scanner.nextLine()); + } + + public String inputYesOrNo(String name) { + System.out.println(name + "은 한장의 카드를 더 받으시겠습니까?"); + return scanner.nextLine(); + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 0000000000..6dbb58878c --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,58 @@ +package view; + +import java.util.List; +import java.util.stream.Collectors; + +import dto.CardDto; +import dto.GamerDto; +import dto.GamerEarningDto; +import dto.GamerWithScoreDto; + +public class OutputView { + private static final String DELIMITER = ", "; + + public void printGamersCards(List gamers) { + System.out.println(transferGamerNamesToString(gamers) + "에게 2장의 카드를 나누어 주었습니다."); + for (GamerDto gamer : gamers) { + printGamerCards(gamer); + } + } + + public void printGamerCards(GamerDto gamer) { + System.out.println(gamer.getName() + "카드: " + transferGamerCardsToString(gamer.getCards())); + } + + private String transferGamerNamesToString(List gamers) { + return gamers.stream() + .map(GamerDto::getName) + .collect(Collectors.joining(DELIMITER)); + } + + private String transferGamerCardsToString(List cards) { + return cards.stream() + .map(cardDto -> cardDto.getRank() + cardDto.getSuit()) + .collect(Collectors.joining(DELIMITER)); + } + + public void printDealerHit() { + System.out.println("딜러는 17 미만이라 한 장의 카드를 더 뽑았습니다."); + } + + public void printGamersCardsWithScore(List gamers) { + for (GamerWithScoreDto gamer : gamers) { + printGamerCardsWithScore(gamer); + } + } + + private void printGamerCardsWithScore(GamerWithScoreDto gamer) { + System.out.println( + gamer.getName() + "카드: " + transferGamerCardsToString(gamer.getCards()) + " - 결과 : " + gamer.getScore()); + } + + public void printEarning(List gamers) { + System.out.println("## 최종 수익"); + for (GamerEarningDto gamer : gamers) { + System.out.println(gamer.getName() + ": " + gamer.getEarningMoney()); + } + } +} From 02d7f867c297e5733823a9c866d20a27a128d83c Mon Sep 17 00:00:00 2001 From: minuyim Date: Fri, 3 Jul 2020 00:12:51 +0900 Subject: [PATCH 17/17] =?UTF-8?q?refactor:=20=EB=88=84=EB=9D=BD=EB=90=9C?= =?UTF-8?q?=20test=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20=EB=B0=8F=20Co?= =?UTF-8?q?ntroller=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/controller/BlackJackController.java | 45 +++++++++++++------ src/test/java/domain/gamer/GamerTest.java | 8 ++++ 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/main/java/controller/BlackJackController.java b/src/main/java/controller/BlackJackController.java index 0d42eb03b9..e7ba703fe7 100644 --- a/src/main/java/controller/BlackJackController.java +++ b/src/main/java/controller/BlackJackController.java @@ -27,30 +27,49 @@ public BlackJackController(InputView inputView, OutputView outputView) { } public void run() { - List playerInfos = inputView.inputPlayerInfos(); - DeckFactory randomShuffledDeckFactory = new RandomShuffledDeckFactory(); - Deck deck = randomShuffledDeckFactory.create(); - - Players players = playerInfos.stream() - .map(playerInfo -> Player.of(playerInfo.getName(), deck, playerInfo.getBattingMoney())) - .collect(collectingAndThen(toList(), Players::new)); + Deck deck = getDeck(); + Players players = getPlayers(deck); Dealer dealer = Dealer.of(deck); + playBlackJack(deck, players, dealer); + printResult(players, dealer); + } + private void playBlackJack(Deck deck, Players players, Dealer dealer) { outputView.printGamersCards(GamerDto.listOf(dealer, players.getPlayers())); - for (Player player : players.getPlayers()) { - while (player.canHit()) { - YesOrNo yesOrNo = YesOrNo.findByShortName(inputView.inputYesOrNo(player.getName())); - yesOrNo.execute(player, deck); - outputView.printGamerCards(GamerDto.of(player)); - } + decideHitOrStay(deck, player); } + decideDealerHit(deck, dealer); + } + private void decideDealerHit(Deck deck, Dealer dealer) { while (dealer.canHit()) { outputView.printDealerHit(); dealer.hit(deck); } + } + + private void decideHitOrStay(Deck deck, Player player) { + while (player.canHit()) { + YesOrNo yesOrNo = YesOrNo.findByShortName(inputView.inputYesOrNo(player.getName())); + yesOrNo.execute(player, deck); + outputView.printGamerCards(GamerDto.of(player)); + } + } + + private Players getPlayers(Deck deck) { + List playerInfos = inputView.inputPlayerInfos(); + return playerInfos.stream() + .map(playerInfo -> Player.of(playerInfo.getName(), deck, playerInfo.getBattingMoney())) + .collect(collectingAndThen(toList(), Players::new)); + } + + private Deck getDeck() { + DeckFactory randomShuffledDeckFactory = new RandomShuffledDeckFactory(); + return randomShuffledDeckFactory.create(); + } + private void printResult(Players players, Dealer dealer) { outputView.printGamersCardsWithScore(GamerWithScoreDto.listOf(dealer, players.getPlayers())); outputView.printEarning(GamerEarningDto.listOf(dealer, players)); } diff --git a/src/test/java/domain/gamer/GamerTest.java b/src/test/java/domain/gamer/GamerTest.java index d07984bef8..b79f9030ab 100644 --- a/src/test/java/domain/gamer/GamerTest.java +++ b/src/test/java/domain/gamer/GamerTest.java @@ -36,4 +36,12 @@ void isFinished() { gamer.hit(testDeckFactory.create()); assertThat(gamer.isFinished()).isFalse(); } + + @Test + @DisplayName("게이머의 카드 스코어를 계산하는 지 확인") + void calculateScore() { + DeckFactory testDeckFactory = new TestDeckFactory(); + Gamer gamer = Player.of("사람", testDeckFactory.create(), 10_000); + assertThat(gamer.calculateScore()).isEqualTo(12); + } }