From 589415351b7e73ade7fde60e2a0753d16bfff5d8 Mon Sep 17 00:00:00 2001 From: DK Date: Thu, 23 Nov 2023 16:27:17 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20=EC=99=9C=20=EC=96=B4=EA=B8=80?= =?UTF-8?q?=EB=A6=AC=ED=95=9C=EA=B1=B4=20=EB=A7=8C=EB=93=A4=EA=B8=B0=20?= =?UTF-8?q?=EC=8B=AB=EC=9D=84=EA=B9=8C=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/doc/TODOS.md | 111 ++++++++++++++++++++++++++ src/main/java/card/Card.java | 19 +++++ src/main/java/card/Deck.java | 23 ++++++ src/main/java/card/Number.java | 33 ++++++++ src/main/java/card/Shape.java | 18 +++++ src/main/java/console/InputView.java | 78 ++++++++++++++++++ src/main/java/console/OutputView.java | 33 ++++++++ src/main/java/game/Controller.java | 10 +++ src/main/java/hand/Asset.java | 26 ++++++ src/main/java/hand/Hand.java | 48 +++++++++++ src/main/java/user/Dealer.java | 11 +++ src/main/java/user/Player.java | 7 ++ src/main/java/user/User.java | 36 +++++++++ src/test/java/card/CardTest.java | 13 +++ src/test/java/card/DeckTest.java | 15 ++++ 15 files changed, 481 insertions(+) create mode 100644 src/doc/TODOS.md create mode 100644 src/main/java/card/Card.java create mode 100644 src/main/java/card/Deck.java create mode 100644 src/main/java/card/Number.java create mode 100644 src/main/java/card/Shape.java create mode 100644 src/main/java/console/InputView.java create mode 100644 src/main/java/console/OutputView.java create mode 100644 src/main/java/game/Controller.java create mode 100644 src/main/java/hand/Asset.java create mode 100644 src/main/java/hand/Hand.java create mode 100644 src/main/java/user/Dealer.java create mode 100644 src/main/java/user/Player.java create mode 100644 src/main/java/user/User.java create mode 100644 src/test/java/card/CardTest.java create mode 100644 src/test/java/card/DeckTest.java diff --git a/src/doc/TODOS.md b/src/doc/TODOS.md new file mode 100644 index 00000000..3d20b7d9 --- /dev/null +++ b/src/doc/TODOS.md @@ -0,0 +1,111 @@ +게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리) +pobi,jason + +pobi의 배팅 금액은? +10000 + +jason의 배팅 금액은? +20000 + +딜러와 pobi, jason에게 2장의 나누었습니다. +딜러: 3다이아몬드 +pobi카드: 2하트, 8스페이드 +jason카드: 7클로버, K스페이드 + +pobi는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) +y +pobi카드: 2하트, 8스페이드, A클로버 +pobi는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) +n +jason은 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) +n +jason카드: 7클로버, K스페이드 + +딜러는 16이하라 한장의 카드를 더 받았습니다. + +딜러 카드: 3다이아몬드, 9클로버, 8다이아몬드 - 결과: 20 +pobi카드: 2하트, 8스페이드, A클로버 - 결과: 21 +jason카드: 7클로버, K스페이드 - 결과: 17 + +## 최종 수익 +딜러: 10000 +pobi: 10000 +jason: -20000 + +--- + +# 순서대로 정리 + +1. 플레이어 이름을 입력받는다. + - 플레이어의 이름은 쉼표로 구분(,) +- 이 때 딜러도 생성된다 + +2. 플레이어는 베팅 금액을 입력한다. + +3. 카드기계는 카드를 두장씩 넘겨준다. +- 이 때 플레이어들은 카드덱에 카드를 두 장 지급받는다.(딜러 포함) + - 이 때, 딜러의 카드 합이 16 이하면 반드시 한 장 더뽑고, 17 이상이면 뽑지 않는다. +- 지급받은 카드들을 출력한다 + +4. 플레이어들은 한 장을 더 받을지를 계산해서, y나 n 으로 대답한다. 원하는 만큼 작업을 반복한다. + +5. 최종 수익을 계산한다 + +### 카드 판별 + +1. 딜러 +- 딜러는 2장 뽑았을 때 카드 합이 16 이하면 한장 더, 아니면 뽑지 않는다. + +2. 플레이어는 21이 되면 1.5배를 돌려받는다. 대신 딜러또한 21이면 베팅금액만 돌려받는다. + +--- + +# 도메인별 정리 + +# 플레이어 + +## 1. 상태 + +- 베팅 금액 +- 카드 뭉치 (덱? 스택?) + +## 2. 동작 + +- 베팅 금액을 설정해서 초기화된다. (이 때 카드뭉치는 비어있다) +- 손패의 합을 계산한다 + +# 딜러 (플레이어 상속?) + +## 1. 상태 + +- 베팅 금액 +- 카드 뭉치 + +## 2. 동작 + +- 플레이어의 동작들... +- 손패의 합이 16 이하인지 초과인지 계산하는 로직 + +# 카드 + +## 상태 + +- 이름 +- 숫자 값 + +## 동작 + +- 게터매서드 + +# 카드 뭉치 + +## 상태 + +- 카드들 + +### 동작 + +- 카드들을 생성한다. (홀 카드) +- 카드들을 랜덤으로 셔플한다 +- 셔플한 카드들을 리턴한다 + diff --git a/src/main/java/card/Card.java b/src/main/java/card/Card.java new file mode 100644 index 00000000..258d7a35 --- /dev/null +++ b/src/main/java/card/Card.java @@ -0,0 +1,19 @@ +package card; + +public class Card { + private final Number number; + private final Shape shape; + + public Card(Shape shape, Number number) { + this.number = number; + this.shape = shape; + } + + public String getCardInfo(){ + return number.getName() + shape.getName(); + } + + public Number getNumber() { + return number; + } +} \ No newline at end of file diff --git a/src/main/java/card/Deck.java b/src/main/java/card/Deck.java new file mode 100644 index 00000000..442f9266 --- /dev/null +++ b/src/main/java/card/Deck.java @@ -0,0 +1,23 @@ +package card; + +import java.util.*; + +public class Deck { + private static final Deque cards = new ArrayDeque<>(); + static { + Arrays.stream(Shape.values()).forEach( + shape -> Arrays.stream(Number.values()) + .forEach(number -> cards.add(new Card(shape, number))) + ); + } + public void shuffle() { + List cardList = new ArrayList<>(cards); + Collections.shuffle(cardList); + cards.clear(); + cards.addAll(cardList); + } + + public Card getCard(){ + return cards.pop(); + } +} \ No newline at end of file diff --git a/src/main/java/card/Number.java b/src/main/java/card/Number.java new file mode 100644 index 00000000..9709c47e --- /dev/null +++ b/src/main/java/card/Number.java @@ -0,0 +1,33 @@ +package card; + +public enum Number { + + ACE("ACE", 1), + TWO("2", 2), + THREE("3", 3), + FOUR("4", 4), + FIVE("5", 5), + SIX("6", 6), + SEVEN("7", 7), + EIGHT("8", 8), + NINE("9", 9), + JACK("잭", 10), + QUEEN("퀸", 10), + KING("킹", 10); + + private final String name; + private final int value; + + Number(String name, int value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public int getValue() { + return value; + } +} \ No newline at end of file diff --git a/src/main/java/card/Shape.java b/src/main/java/card/Shape.java new file mode 100644 index 00000000..35af8e7f --- /dev/null +++ b/src/main/java/card/Shape.java @@ -0,0 +1,18 @@ +package card; + +public enum Shape { + HEART("하트"), + SPADE("스페이드"), + CLOVER("클로버"), + DIAMOND("다이아몬드"); + + private final String name; + + Shape(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/src/main/java/console/InputView.java b/src/main/java/console/InputView.java new file mode 100644 index 00000000..ac0f8008 --- /dev/null +++ b/src/main/java/console/InputView.java @@ -0,0 +1,78 @@ +package console; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Arrays; + +public class InputView { + private final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private String getInput(){ + try { + return br.readLine(); + } catch (IOException e){ + throw new IllegalArgumentException(e.getMessage()); + } + } + public String[] getName() { + System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); + String[] userNames = getInput().split(","); + try{ + validate(userNames); + } catch (IllegalArgumentException e){ + System.out.println(e.getMessage()); + return getName(); + } + return userNames; + } + private void validate(String[] userNames){ + if (userNames.length == 0 || isDuplicatedNames(userNames) || isContainsBlank(userNames)){ + throw new IllegalArgumentException("[Error] 잘못된 입력"); + } + } + + private boolean isContainsBlank(String[] userNames) { + return Arrays.stream(userNames) + .anyMatch(s -> s.trim().isEmpty()); + } + + private boolean isDuplicatedNames(String[] userNames) { + return Arrays.stream(userNames) + .distinct() + .count() == userNames.length; + } + + private int getMoney(String name){ + System.out.println(name + "의 배팅 금액은?"); + String input = getInput(); + try { + return Integer.parseInt(input); + } catch (NumberFormatException e){ + System.out.println("[ERROR] 잘못된 입력"); + return getMoney(name); + } + } + + public boolean askUserForCardDecision(String name){ + System.out.println(name + "는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)"); + String input = getInput(); + try { + checkInput(input); + return name.equals("y"); + } catch (IllegalArgumentException e){ + System.out.println("[ERROR] 잘못된 입력"); + return askUserForCardDecision(name); + } + } + + private void checkInput(String input) { + input = input.trim().toLowerCase(); + if (input.isEmpty() || isNotValid(input)) { + throw new IllegalArgumentException(); + } + } + + private boolean isNotValid(String input) { + return !(input.equals("y") || input.equals("n")); + } +} \ No newline at end of file diff --git a/src/main/java/console/OutputView.java b/src/main/java/console/OutputView.java new file mode 100644 index 00000000..3be8ece4 --- /dev/null +++ b/src/main/java/console/OutputView.java @@ -0,0 +1,33 @@ +package console; + +import card.Card; +import user.User; + +import java.util.Arrays; +import java.util.Deque; + +public class OutputView { + public void printNames(String[] usernames){ + String names = getNames(usernames); + System.out.println(names); + } + + private String getNames(String[] usernames) { + StringBuilder sb = new StringBuilder(); + Arrays.stream(usernames).forEach(s -> sb.append(s).append(",")); + return sb.substring(0, sb.length() - 1); + } + + public void distributedTwoCards(String[] usernames){ + System.out.println("딜러와 " + getNames(usernames) + "에게 2장의 나누었습니다."); + } + + public void printUsersCardInfo(User user){ + String name = user.getName(); + StringBuilder sb = new StringBuilder(); + Deque cardsInHand = user.getCardsInHand(); + cardsInHand.forEach(card -> sb.append(card.getCardInfo()).append(", ")); + String cardInfo = sb.substring(0, sb.length() - 2); + System.out.println(name + "카드: " + cardInfo); + } +} diff --git a/src/main/java/game/Controller.java b/src/main/java/game/Controller.java new file mode 100644 index 00000000..a19cb8f0 --- /dev/null +++ b/src/main/java/game/Controller.java @@ -0,0 +1,10 @@ +package game; + +import console.InputView; +import console.OutputView; + +public class Controller { + private final InputView inputView = new InputView(); + private final OutputView outputView = new OutputView(); + +} diff --git a/src/main/java/hand/Asset.java b/src/main/java/hand/Asset.java new file mode 100644 index 00000000..d2dfb699 --- /dev/null +++ b/src/main/java/hand/Asset.java @@ -0,0 +1,26 @@ +package hand; + +public class Asset { + private int money; + + public void setMoney(int money) { + this.money = money; + } + + private void winMoney(int money){ + this.money += money; + } + + private void payMoney(int money){ + this.money -= money; + } + + public int getMoney() { + return money; + } + + public void sendMoney(Asset asset, int money){ + this.payMoney(money); + asset.winMoney(money); + } +} \ No newline at end of file diff --git a/src/main/java/hand/Hand.java b/src/main/java/hand/Hand.java new file mode 100644 index 00000000..83f1e5d6 --- /dev/null +++ b/src/main/java/hand/Hand.java @@ -0,0 +1,48 @@ +package hand; + +import card.Card; +import card.Number; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class Hand { + private final Deque cardsInHand = new ArrayDeque<>(); + + public void addCard(Card card){ + cardsInHand.add(card); + } + public int getAceQuantity(){ + return (int) cardsInHand.stream() + .filter(card -> card.getNumber().equals(Number.ACE)) + .count(); + } + public int calculateValueWithoutACE(){ + return this.cardsInHand.stream() + .map(Card::getNumber) + .mapToInt(Number::getValue) + .sum(); + } + public int getHandValue() { + int valueWithoutACE = calculateValueWithoutACE(); + int aceQuantity = getAceQuantity(); + + if (aceQuantity > 0 && isACEAddable(valueWithoutACE)) { + return getAceContainedValue(valueWithoutACE, aceQuantity); + } + return valueWithoutACE; + } + private int getAceContainedValue(int valueWithoutACE, int aceQuantity) { + while (aceQuantity > 0 && isACEAddable(valueWithoutACE)) { + valueWithoutACE += 10; + aceQuantity--; + } + return valueWithoutACE; + } + private boolean isACEAddable(int valueWithoutACE) { + return valueWithoutACE + 10 <= 21; + } + public Deque getCardsInHand() { + return cardsInHand; + } +} \ No newline at end of file diff --git a/src/main/java/user/Dealer.java b/src/main/java/user/Dealer.java new file mode 100644 index 00000000..53ec4920 --- /dev/null +++ b/src/main/java/user/Dealer.java @@ -0,0 +1,11 @@ +package user; + +public class Dealer extends User{ + protected Dealer(String name) { + super(name); + } + + public boolean shouldDrawCard() { + return getHandValue() <= 16; + } +} \ No newline at end of file diff --git a/src/main/java/user/Player.java b/src/main/java/user/Player.java new file mode 100644 index 00000000..0336cf95 --- /dev/null +++ b/src/main/java/user/Player.java @@ -0,0 +1,7 @@ +package user; + +public class Player extends User{ + protected Player(String name) { + super(name); + } +} diff --git a/src/main/java/user/User.java b/src/main/java/user/User.java new file mode 100644 index 00000000..9b1a7e17 --- /dev/null +++ b/src/main/java/user/User.java @@ -0,0 +1,36 @@ +package user; + +import card.Card; +import hand.Asset; +import hand.Hand; + +import java.util.Deque; + +public abstract class User { + private final Hand hand = new Hand(); + private final Asset asset = new Asset(); + private final String name; + + protected User(String name) { + this.name = name; + } + public void addHand(Card card){ + this.hand.addCard(card); + } + public int getHandValue(){ + return this.hand.getHandValue(); + } + public void sendMoney(User user, int money){ + this.asset.sendMoney(user.asset, money); + } + + public Deque getCardsInHand(){ + return this.hand.getCardsInHand(); + } + public int getMoney() { + return this.asset.getMoney(); + } + public String getName() { + return this.name; + } +} \ No newline at end of file diff --git a/src/test/java/card/CardTest.java b/src/test/java/card/CardTest.java new file mode 100644 index 00000000..182b51e4 --- /dev/null +++ b/src/test/java/card/CardTest.java @@ -0,0 +1,13 @@ +package card; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class CardTest { + @Test + void initTest() { + Card card = new Card(Shape.DIAMOND, Number.ACE); + assertThat(card.getCardInfo()).isEqualTo(Number.ACE.getName() + Shape.DIAMOND.getName()); + } +} \ No newline at end of file diff --git a/src/test/java/card/DeckTest.java b/src/test/java/card/DeckTest.java new file mode 100644 index 00000000..42f4670c --- /dev/null +++ b/src/test/java/card/DeckTest.java @@ -0,0 +1,15 @@ +package card; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class DeckTest { + @Test + void getCard() { + Deck deck = new Deck(); + Card card = deck.getCard(); + // 항상 ACE HEART (셔플 아직 안했기때문) + assertThat(card.getCardInfo()).isEqualTo(Number.ACE.getName() + Shape.HEART.getName()); + } +} \ No newline at end of file