diff --git a/README.md b/README.md index 8fe71120..f697156a 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,73 @@ -## [NEXTSTEP 플레이그라운드의 미션 진행 과정](https://github.com/next-step/nextstep-docs/blob/master/playground/README.md) +# 블랙잭 미션 구현 ---- -## 학습 효과를 높이기 위해 추천하는 미션 진행 방법 +## 기능 요구 사항 ---- -1. 피드백 강의 전까지 미션 진행 -> 피드백 강의 전까지 혼자 힘으로 미션 진행. 미션을 진행하면서 하나의 작업이 끝날 때 마다 add, commit -> 예를 들어 다음 숫자 야구 게임의 경우 0, 1, 2단계까지 구현을 완료한 후 push +블랙잭 게임을 변형한 프로그램을 구현한다. 블랙잭 게임은 딜러와 플레이어 중 카드의 합이 21 또는 21에 가장 가까운 숫자를 가지는 쪽이 이기는 게임이다. -![mission baseball](https://raw.githubusercontent.com/next-step/nextstep-docs/master/playground/images/mission_baseball.png) +- 플레이어는 게임을 시작할 때 배팅 금액을 정해야 한다. +- 카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다. +- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. 단, 카드를 추가로 뽑아 21을 초과할 경우 배팅 금액을 모두 잃게 된다. +- 처음 두 장의 카드 합이 21일 경우 블랙잭이 되면 베팅 금액의 1.5 배를 딜러에게 받는다. 딜러와 플레이어가 모두 동시에 블랙잭인 경우 플레이어는 베팅한 금액을 돌려받는다. +- 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다. 딜러가 21을 초과하면 그 시점까지 남아 있던 플레이어들은 가지고 있는 패에 상관 없이 승리해 베팅 금액을 받는다. ---- -2. 피드백 앞 단계까지 미션 구현을 완료한 후 피드백 강의를 학습한다. +## 이벤트 흐름 ---- -3. Git 브랜치를 master 또는 main으로 변경한 후 피드백을 반영하기 위한 새로운 브랜치를 생성한 후 처음부터 다시 미션 구현을 도전한다. +1. 참가자의 이름을 등록했다. +2. 참가자들의 배팅 금액을 등록했다. +3. 딜러와 참가자들에게 카드를 나눠줬다. +4. 카드를 확인해 진행여부를 판단했다. +5. 카드를 계속 받을지 딜러와 참여자들에게 물었다. +6. 게임 승패에 따라 분배금을 분배했다. -``` -git branch -a // 모든 로컬 브랜치 확인 -git checkout master // 기본 브랜치가 master인 경우 -git checkout main // 기본 브랜치가 main인 경우 +## 모델링 +### 참여자 +- 상태 + - 참여자는 이름과 배팅 금액 그리고 카드패를 가지고 있다. + - 이름은 1 글자 이상이어야 한다. + - 배팅 금액은 0 이상의 정수여야 한다. +- 행위 + - 카드덱에서 카드를 뽑는다. + - 자신의 카드패를 확인한다. + - 자신의 카드패의 합을 확인한다. -git checkout -b 브랜치이름 -ex) git checkout -b apply-feedback -``` +### 딜러 +- 상태 + - 딜러는 카드패를 가지고 있다. + - 딜러의 카드패 합이 16 이하이면 카드덱에서 한 장 더 뽑아야 한다. + +### 카드패(Cards) +- 상태 + - 카드패에는 카드가 포함되어 있다. + - 처음 카드패는 최소 두 장의 카드가 존재해야 한다. +- 행위 + - 카드패의 숫자 합을 출력한다. + - Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다. + - 카드패와 숫자 합을 출력준다. + +### 카드(Card) +- 상태 + - 카드는 ACE, 2, 3, 4, 5, 6, 7, 8, 9, 10, King, Queen, Jack 중 하나의 숫자를 가진다. + - 카드덱은 클로버, 하트, 다이아몬드, 스페이드 중 하나의 타입을 가진다. + +### 카드덱(OneCard) +- 상태 + - 카드덱에는 52 종류의 카드가 저장되어 있다. + - 카드덱이 생성되면 카드덱을 섞는다. +- 행위 + - 카드덱에서 카드를 뽑아 건네준다. + +### 게임 +- 행위 + - 이름 등록 + - 문자열을 받아 이름을 등록한다. + - 이름은 `,` 단위로 분리한다. + - 배팅 금액 등록 + - 순서대로 배팅 금액을 등록한다. + - 배팅 금액은 0 이상의 정수이다. + - 카드를 모두에게 분배한다. + - 카드를 참여자와 딜러에게 두 장씩 분배한다. + - 참여자에게 카드 한 장 분배 + - 카드덱에서 꺼내 참여자에게 카드를 분배한다. + - 게임 결과를 발표한다. + - 참여자들과 딜러의 카드 패를 확인한다. + - 배팅 금액 정보를 기반으로 금액을 분배한다. diff --git a/src/main/java/nextstep/blackjack/blackjack/Main.java b/src/main/java/nextstep/blackjack/blackjack/Main.java new file mode 100644 index 00000000..0f7a5e5a --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/Main.java @@ -0,0 +1,28 @@ +package nextstep.blackjack.blackjack; + +import nextstep.blackjack.blackjack.member.Dealer; +import nextstep.blackjack.blackjack.member.Participants; +import nextstep.blackjack.blackjack.service.BlackjackGameService; +import nextstep.blackjack.blackjack.service.BlackjackOutputService; +import nextstep.blackjack.blackjack.utils.IOService; + +import java.math.BigDecimal; +import java.util.Map; + +public class Main { + private final static IOService ioService = new IOService(); + private final static BlackjackGameService blackjackGame = new BlackjackGameService(ioService); + private final static BlackjackOutputService blackjackResult = new BlackjackOutputService(); + + public static void main(String[] args) { + Dealer dealer = blackjackGame.enterDealer(); + Participants participants = blackjackGame.enter(); + blackjackGame.weatherToAcceptCard(participants); + + Map scores = blackjackResult.getResult(participants, dealer); + Map result = blackjackResult.distributeMoney(participants, dealer); + + ioService.outputScores(scores); + ioService.outputResult(result); + } +} diff --git a/src/main/java/nextstep/blackjack/blackjack/member/Dealer.java b/src/main/java/nextstep/blackjack/blackjack/member/Dealer.java new file mode 100644 index 00000000..9d442cf2 --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/member/Dealer.java @@ -0,0 +1,37 @@ +package nextstep.blackjack.blackjack.member; + +import nextstep.blackjack.blackjack.onecards.Cards; +import nextstep.blackjack.blackjack.onecards.OneCards; + +import java.util.Arrays; + +public class Dealer { + private static final int CRITERION = 16; + private final Cards cards; + + public Dealer(Cards cards) { + this.cards = cards; + } + + /** + * 딜러는 카드값이 CRITERION을 넘지 않으면 카드를 한 장 더 뽑는다.* + * @param oneCards + */ + public Dealer(OneCards oneCards) { + Cards cards = new Cards(Arrays.asList(oneCards.pollCard(), oneCards.pollCard())); + + while (condition(cards)) { + cards = cards.putCard(oneCards.pollCard()); + } + + this.cards = cards; + } + + private boolean condition(Cards cards) { + return cards.sumAll() <= CRITERION; + } + + public Cards getCards() { + return cards; + } +} diff --git a/src/main/java/nextstep/blackjack/blackjack/member/Participant.java b/src/main/java/nextstep/blackjack/blackjack/member/Participant.java new file mode 100644 index 00000000..dfb50ef0 --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/member/Participant.java @@ -0,0 +1,46 @@ +package nextstep.blackjack.blackjack.member; + +import nextstep.blackjack.blackjack.onecards.Cards; +import nextstep.blackjack.blackjack.onecards.OneCards; + +import java.math.BigDecimal; + +public class Participant { + private final String username; + private final BigDecimal bettingMoney; + private Cards cards; + + public Participant(String username, BigDecimal bettingMoney, Cards cards) { + if (username == null || username.trim().isEmpty()) { + throw new IllegalArgumentException(); + } + + if (bettingMoney.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException(); + } + + this.username = username; + this.bettingMoney = bettingMoney; + this.cards = cards; + } + + public Participant(String username, BigDecimal bettingMoney, OneCards oneCards) { + this(username, bettingMoney, new Cards(oneCards)); + } + + public String getUsername() { + return username; + } + + public BigDecimal getBettingMoney() { + return bettingMoney; + } + + public Cards getCards() { + return cards; + } + + public void updateCards(Cards cards) { + this.cards = cards; + } +} diff --git a/src/main/java/nextstep/blackjack/blackjack/member/Participants.java b/src/main/java/nextstep/blackjack/blackjack/member/Participants.java new file mode 100644 index 00000000..543e7333 --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/member/Participants.java @@ -0,0 +1,31 @@ +package nextstep.blackjack.blackjack.member; + +import nextstep.blackjack.blackjack.onecards.OneCards; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +public class Participants { + + private final List participants; + + public Participants(List participants) { + this.participants = participants; + } + + public List getParticipants() { + return new ArrayList<>(participants); + } + + public void distribute(OneCards oneCards) { + participants.forEach(participant -> participant.getCards().putCard(oneCards.pollCard())); + } + + public BigDecimal sumBattingMoney() { + return getParticipants().stream() + .map(Participant::getBettingMoney) + .reduce(BigDecimal::add) + .orElse(BigDecimal.ZERO); + } +} diff --git a/src/main/java/nextstep/blackjack/blackjack/onecards/Card.java b/src/main/java/nextstep/blackjack/blackjack/onecards/Card.java new file mode 100644 index 00000000..de63ea16 --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/onecards/Card.java @@ -0,0 +1,39 @@ +package nextstep.blackjack.blackjack.onecards; + +import java.util.Objects; + +public class Card { + private final CardType cardType; + private final CardNumber cardNumber; + + public Card(CardType cardType, CardNumber cardNumber) { + this.cardType = cardType; + this.cardNumber = cardNumber; + } + + public CardNumber getCardNumber() { + return cardNumber; + } + + public String name() { + return cardNumber.number() + cardType.getType(); + } + + @Override + public String toString() { + return cardNumber.number() + cardType.getType(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Card card = (Card) o; + return cardType == card.cardType && cardNumber == card.cardNumber; + } + + @Override + public int hashCode() { + return Objects.hash(cardType, cardNumber); + } +} diff --git a/src/main/java/nextstep/blackjack/blackjack/onecards/CardNumber.java b/src/main/java/nextstep/blackjack/blackjack/onecards/CardNumber.java new file mode 100644 index 00000000..f0bf49ec --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/onecards/CardNumber.java @@ -0,0 +1,41 @@ +package nextstep.blackjack.blackjack.onecards; + +public enum CardNumber { + ACE("A", 1), + TWO("2", 2), + THREE("3", 3), + FOUR("4", 4), + FIVE("5", 5), + SIX("6", 6), + SEVEN("7", 7), + EIGHT("8", 8), + NINE("9", 9), + TEN("10", 10), + KING("K", 10), + QUEEN("Q", 10), + JACK("J", 10); + private final String cardNumber; + private final int score; + + CardNumber(String cardNumber, int score) { + this.cardNumber = cardNumber; + this.score = score; + } + + String number() { + return cardNumber; + } + + int score() { + return score; + } + + boolean isAce() { + return this.equals(CardNumber.ACE); + } + + @Override + public String toString() { + return cardNumber; + } +} diff --git a/src/main/java/nextstep/blackjack/blackjack/onecards/CardType.java b/src/main/java/nextstep/blackjack/blackjack/onecards/CardType.java new file mode 100644 index 00000000..5b0db6f4 --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/onecards/CardType.java @@ -0,0 +1,23 @@ +package nextstep.blackjack.blackjack.onecards; + +public enum CardType { + HEART("하트"), + DIAMOND("다이아몬드"), + CLOVER("클로버"), + SPADE("스페이드"); + + private final String cardType; + + CardType(String cardType) { + this.cardType = cardType; + } + + String getType() { + return cardType; + } + + @Override + public String toString() { + return cardType; + } +} diff --git a/src/main/java/nextstep/blackjack/blackjack/onecards/Cards.java b/src/main/java/nextstep/blackjack/blackjack/onecards/Cards.java new file mode 100644 index 00000000..ed3406ed --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/onecards/Cards.java @@ -0,0 +1,66 @@ +package nextstep.blackjack.blackjack.onecards; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class Cards { + private static final int DEFAULT_SIZE = 2; + private static final int MAX_SCORE = 21; + private final List cards; + + public Cards(final List cards) { + this.cards = cards; + } + + public Cards(OneCards oneCards) { + this(Arrays.asList(oneCards.pollCard(), oneCards.pollCard())); + } + + private List getCards() { + return new ArrayList<>(cards); + } + + public int sumAll() { + final int aceCount = (int) getCards().stream() + .filter(card -> card.getCardNumber().isAce()) + .count(); + + final int value = getCards().stream() + .mapToInt(card -> card.getCardNumber().score()) + .sum(); + + if (aceCount > 0 && value <= 11) { + return value + 10; + } + return value; + } + + public Cards putCard(final Card card) { + List cards = getCards(); + cards.add(getCard(card)); + return new Cards(cards); + } + + private Card getCard(Card card) { + return Optional.ofNullable(card) + .orElseThrow(() -> new IllegalArgumentException("Card는 Null일 수 없습니다.")); + } + + public boolean isCountTwo() { + return cards.size() == DEFAULT_SIZE; + } + + public boolean isOver21() { + return sumAll() > MAX_SCORE; + } + + public boolean isNotOver21() { + return sumAll() < MAX_SCORE; + } + + public boolean is21() { + return sumAll() == MAX_SCORE; + } +} diff --git a/src/main/java/nextstep/blackjack/blackjack/onecards/OneCards.java b/src/main/java/nextstep/blackjack/blackjack/onecards/OneCards.java new file mode 100644 index 00000000..54e7446b --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/onecards/OneCards.java @@ -0,0 +1,19 @@ +package nextstep.blackjack.blackjack.onecards; + +import java.util.LinkedList; + +public class OneCards { + private final LinkedList cardList; + + public OneCards(LinkedList cardList) { + this.cardList = cardList; + } + + public LinkedList getCardList() { + return new LinkedList<>(cardList); + } + + public Card pollCard() { + return cardList.pollFirst(); + } +} diff --git a/src/main/java/nextstep/blackjack/blackjack/onecards/OneCardsGenerator.java b/src/main/java/nextstep/blackjack/blackjack/onecards/OneCardsGenerator.java new file mode 100644 index 00000000..d07e8040 --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/onecards/OneCardsGenerator.java @@ -0,0 +1,29 @@ +package nextstep.blackjack.blackjack.onecards; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class OneCardsGenerator { + public static OneCards generateOneCards() { + List cards = getCards(); + shuffle(cards); + return new OneCards((LinkedList) cards); + } + + private static void shuffle(List cards) { + Collections.shuffle(cards); + } + + private static List getCards() { + List cards = new LinkedList<>(); + Arrays.stream(CardType.values()) + .forEach( + cardType -> Arrays + .stream(CardNumber.values()) + .forEach(cardNumber -> cards.add(new Card(cardType, cardNumber))) + ); + return cards; + } +} diff --git a/src/main/java/nextstep/blackjack/blackjack/service/BlackjackGameService.java b/src/main/java/nextstep/blackjack/blackjack/service/BlackjackGameService.java new file mode 100644 index 00000000..3183addc --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/service/BlackjackGameService.java @@ -0,0 +1,66 @@ +package nextstep.blackjack.blackjack.service; + +import nextstep.blackjack.blackjack.member.Dealer; +import nextstep.blackjack.blackjack.member.Participant; +import nextstep.blackjack.blackjack.member.Participants; +import nextstep.blackjack.blackjack.onecards.Cards; +import nextstep.blackjack.blackjack.onecards.OneCards; +import nextstep.blackjack.blackjack.onecards.OneCardsGenerator; +import nextstep.blackjack.blackjack.utils.IOService; + +import java.math.BigDecimal; +import java.util.Map; +import java.util.stream.Collectors; + +public class BlackjackGameService { + private final static OneCards oneCards = OneCardsGenerator.generateOneCards(); + private final IOService ioService; + + public BlackjackGameService(IOService ioService) { + this.ioService = ioService; + } + + /** + * 참여자들을 입력받고 금액을 입력받는다.* + * + * @return Participants + */ + public Participants enter() { + Map participantNameAndBatting = ioService.inputParticipantNameAndBattingMoney(); + return new Participants( + participantNameAndBatting.entrySet().stream() + .map(entry -> getParticipant(oneCards, entry.getKey(), entry.getValue())) + .collect(Collectors.toList()) + ); + } + + /** + * 참여자들에게 카드를 받을 지 묻는다. 21 이상이면 카드를 받을 수 없다.* + * + * @param participants + */ + public void weatherToAcceptCard(Participants participants) { + participants.getParticipants() + .forEach(participant -> { + while (participant.getCards().isNotOver21() && ioService.weatherToAcceptCard(participant.getUsername(), participant.getCards().sumAll())) { + Cards cards = participant.getCards().putCard(oneCards.pollCard()); + participant.updateCards(cards); + } + }); + } + + /** + * 딜러에게 카드를 준다.* + * + * @return Dealer + */ + public Dealer enterDealer() { + return new Dealer(oneCards); + } + + private Participant getParticipant(OneCards oneCards, String username, BigDecimal battingMoney) { + return new Participant(username, battingMoney, oneCards); + } + + +} diff --git a/src/main/java/nextstep/blackjack/blackjack/service/BlackjackOutputService.java b/src/main/java/nextstep/blackjack/blackjack/service/BlackjackOutputService.java new file mode 100644 index 00000000..857cd5e3 --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/service/BlackjackOutputService.java @@ -0,0 +1,125 @@ +package nextstep.blackjack.blackjack.service; + +import nextstep.blackjack.blackjack.member.Dealer; +import nextstep.blackjack.blackjack.member.Participant; +import nextstep.blackjack.blackjack.member.Participants; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class BlackjackOutputService { + + public static final String DEALER = "dealer"; + + /** + * 참여자들을 입력 받아 결과를 반환한다.* + * + * @param participants + */ + public Map distributeMoney(Participants participants, Dealer dealer) { + Map members = new HashMap<>(); + + List participants_cards_21 = participants.getParticipants().stream() + .filter(participant -> participant.getCards().is21()) + .collect(Collectors.toList()); + + int maxParticipant = participants.getParticipants().stream() + .filter(participant -> participant.getCards().sumAll() <= 21) + .max(Comparator.comparingLong(value -> value.getCards().sumAll())) + .map(participant -> participant.getCards().sumAll()) + .orElse(0); + + if (dealer.getCards().isOver21()) { + participants.getParticipants() + .forEach(participant -> members.put(participant.getUsername(), participant.getBettingMoney())); + + BigDecimal dealerProfit = participants.getParticipants().stream() + .map(Participant::getBettingMoney) + .reduce(BigDecimal::add) + .orElse(BigDecimal.ZERO) + .negate(); + + members.put(DEALER, dealerProfit); + return members; + } + + if (participants_cards_21.stream().anyMatch(participant -> participant.getCards().isCountTwo())) { + List winners = participants_cards_21.stream() + .filter(participant -> participant.getCards().isCountTwo()) + .collect(Collectors.toList()); + + List looser = participants.getParticipants().stream() + .filter(participant -> !winners.contains(participant)) + .collect(Collectors.toList()); + + winners.forEach(participant -> members.put(participant.getUsername(), participant.getBettingMoney().multiply(BigDecimal.valueOf(1.5)).setScale(0, RoundingMode.FLOOR))); + looser.forEach(participant -> members.put(participant.getUsername(), participant.getBettingMoney().negate())); + + BigDecimal dealerProfit = members.values().stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO).negate(); + members.put(DEALER, dealerProfit); + return members; + } + + if (dealer.getCards().is21() && !participants_cards_21.isEmpty()) { + participants_cards_21.forEach(participant -> + members.put(participant.getUsername(), participant.getBettingMoney()) + ); + + List participants_cards_not_21 = participants.getParticipants().stream() + .filter(participant -> !participant.getCards().is21()) + .collect(Collectors.toList()); + + participants_cards_not_21.forEach(participant -> + members.put(participant.getUsername(), participant.getBettingMoney().negate()) + ); + + BigDecimal dealerProfit = members.values().stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO).negate(); + members.put(DEALER, dealerProfit); + + return members; + } + + if ((dealer.getCards().is21() && participants_cards_21.isEmpty()) || (dealer.getCards().sumAll() > maxParticipant)) { + participants.getParticipants() + .forEach(participant -> members.put(participant.getUsername(), participant.getBettingMoney().negate())); + + BigDecimal dealerProfit = members.values().stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO).negate(); + members.put(DEALER, dealerProfit); + return members; + } + + + if (dealer.getCards().sumAll() <= maxParticipant) { + List winner = participants.getParticipants().stream() + .filter(participant -> participant.getCards().sumAll() == maxParticipant) + .collect(Collectors.toList()); + + List looser = participants.getParticipants().stream() + .filter(participant -> participant.getCards().sumAll() != maxParticipant) + .collect(Collectors.toList()); + + winner.forEach(participant -> members.put(participant.getUsername(), participant.getBettingMoney())); + looser.forEach(participant -> members.put(participant.getUsername(), participant.getBettingMoney().negate())); + + BigDecimal dealerProfit = members.values().stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO).negate(); + members.put(DEALER, dealerProfit); + + + return members; + } + return new HashMap<>(); + } + + public Map getResult(Participants participants, Dealer dealer) { + Map scores = new HashMap<>(); + participants.getParticipants().forEach(participant -> scores.put(participant.getUsername(), participant.getCards().sumAll())); + scores.put(DEALER, dealer.getCards().sumAll()); + + return scores; + } +} diff --git a/src/main/java/nextstep/blackjack/blackjack/utils/IOService.java b/src/main/java/nextstep/blackjack/blackjack/utils/IOService.java new file mode 100644 index 00000000..6c1f5cd4 --- /dev/null +++ b/src/main/java/nextstep/blackjack/blackjack/utils/IOService.java @@ -0,0 +1,52 @@ +package nextstep.blackjack.blackjack.utils; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Scanner; +import java.util.stream.Collectors; + +public class IOService { + private final Scanner scanner = new Scanner(System.in); + private static final String REGEX = ","; + + public Map inputParticipantNameAndBattingMoney() { + List names = inputName(); + return inputBattingMoney(names); + } + + private Map inputBattingMoney(List names) { + Map nameAndBattingMoney = new HashMap<>(); + for (String name : names) { + System.out.println(name + "씨는 얼마를 배팅할 것인지?"); + nameAndBattingMoney.put(name, scanner.nextBigDecimal()); + } + return nameAndBattingMoney; + } + + private List inputName() { + System.out.println("이름을 입력하시오. 단위는 `,` 입니다. "); + return Arrays.stream(scanner.nextLine().split(REGEX)) + .map(String::trim).collect(Collectors.toList()); + } + + public boolean weatherToAcceptCard(String username, int score) { + System.out.println(username + "씨는 카드를 받을건지? (y/n), score : " + score); + return Objects.equals(scanner.next(), "y"); + } + + public void outputScores(Map scores) { + for (Map.Entry entry : scores.entrySet()) { + System.out.println(entry.getKey() + " : " + entry.getValue() + "점"); + } + } + + public void outputResult(Map result) { + for (Map.Entry entry : result.entrySet()) { + System.out.println(entry.getKey() + " : " + entry.getValue() + "원"); + } + } +} diff --git a/src/main/java/nextstep/fp/Condition.java b/src/main/java/nextstep/fp/Condition.java new file mode 100644 index 00000000..bafa628f --- /dev/null +++ b/src/main/java/nextstep/fp/Condition.java @@ -0,0 +1,5 @@ +package nextstep.fp; + +public interface Condition { + boolean condition(Integer number); +} diff --git a/src/main/java/nextstep/fp/Lambda.java b/src/main/java/nextstep/fp/Lambda.java index bd68fe1c..a8072c3d 100644 --- a/src/main/java/nextstep/fp/Lambda.java +++ b/src/main/java/nextstep/fp/Lambda.java @@ -4,53 +4,51 @@ public class Lambda { public static void printAllOld(List numbers) { - System.out.println("printAllOld"); - - for (int number : numbers) { - System.out.println(number); - } + print("printAllOld"); + numbers.forEach(System.out::println); } public static void printAllLambda(List numbers) { - System.out.println("printAllLambda"); - + print("printAllLambda"); numbers.forEach(System.out::println); } public static void runThread() { - new Thread(new Runnable() { - @Override - public void run() { - System.out.println("Hello from thread"); - } - }).start(); + new Thread(() -> print("Hello from thread")).start(); } + public static int sumAll(List numbers) { - int total = 0; - for (int number : numbers) { - total += number; - } - return total; + return sumAll(numbers, Lambda::getAll); } public static int sumAllEven(List numbers) { - int total = 0; - for (int number : numbers) { - if (number % 2 == 0) { - total += number; - } - } - return total; + return sumAll(numbers, Lambda::getEven); } public static int sumAllOverThree(List numbers) { - int total = 0; - for (int number : numbers) { - if (number > 3) { - total += number; - } - } - return total; + return sumAll(numbers, Lambda::getOverThree); + } + + private static int sumAll(List numbers, Condition condition) { + return numbers.stream() + .filter(condition::condition) + .reduce(Integer::sum).orElse(0); + } + + private static void print(String message) { + System.out.println(message); + } + + private static boolean getAll(Integer integer) { + return true; + } + + private static boolean getEven(Integer integer) { + return integer % 2 == 0; + } + + private static boolean getOverThree(Integer integer) { + return integer > 3; } } diff --git a/src/main/java/nextstep/fp/StreamStudy.java b/src/main/java/nextstep/fp/StreamStudy.java index b446983a..d0936af7 100644 --- a/src/main/java/nextstep/fp/StreamStudy.java +++ b/src/main/java/nextstep/fp/StreamStudy.java @@ -5,15 +5,17 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; public class StreamStudy { + private static final String REGEX = "[\\P{L}]+"; public static long countWords() throws IOException { String contents = new String(Files.readAllBytes(Paths - .get("src/main/resources/fp/war-and-peace.txt")), StandardCharsets.UTF_8); - List words = Arrays.asList(contents.split("[\\P{L}]+")); + .get("src/main/resources/fp/war-and-peace.txt")), StandardCharsets.UTF_8); + String[] words = contents.split(REGEX); long count = 0; for (String w : words) { @@ -24,10 +26,13 @@ public static long countWords() throws IOException { public static void printLongestWordTop100() throws IOException { String contents = new String(Files.readAllBytes(Paths - .get("src/main/resources/fp/war-and-peace.txt")), StandardCharsets.UTF_8); - List words = Arrays.asList(contents.split("[\\P{L}]+")); + .get("src/main/resources/fp/war-and-peace.txt")), StandardCharsets.UTF_8); + List words = Arrays.asList(contents.split(REGEX)); - // TODO 이 부분에 구현한다. + words.stream().filter(w -> w.length() > 12) + .sorted(Comparator.comparing(String::length) + .reversed()).distinct().limit(100) + .map(String::toLowerCase).forEach(System.out::println); } public static List doubleNumbers(List numbers) { @@ -35,10 +40,10 @@ public static List doubleNumbers(List numbers) { } public static long sumAll(List numbers) { - return numbers.stream().reduce(0, (x, y) -> x + y); + return numbers.stream().reduce(0, Integer::sum); } public static long sumOverThreeAndDouble(List numbers) { - return 0; + return Lambda.sumAllOverThree(numbers) * 2L; } -} \ No newline at end of file +} diff --git a/src/main/java/nextstep/optional/Computer.java b/src/main/java/nextstep/optional/Computer.java index cc0af4d7..08e8343d 100644 --- a/src/main/java/nextstep/optional/Computer.java +++ b/src/main/java/nextstep/optional/Computer.java @@ -1,7 +1,9 @@ package nextstep.optional; +import java.util.Optional; + public class Computer { - private Soundcard soundcard; + private final Soundcard soundcard; public Computer(Soundcard soundcard) { this.soundcard = soundcard; @@ -11,8 +13,12 @@ public Soundcard getSoundcard() { return soundcard; } + public Optional getOptionalSoundcard() { + return Optional.ofNullable(soundcard); + } + public static class Soundcard { - private USB usb; + private final USB usb; public Soundcard(USB usb) { super(); @@ -22,10 +28,14 @@ public Soundcard(USB usb) { public USB getUsb() { return usb; } + + public Optional getOptionalUsb() { + return Optional.ofNullable(usb); + } } public static class USB { - private String version; + private final String version; public USB(String version) { super(); diff --git a/src/main/java/nextstep/optional/ComputerStore.java b/src/main/java/nextstep/optional/ComputerStore.java index 2695c967..51831f92 100644 --- a/src/main/java/nextstep/optional/ComputerStore.java +++ b/src/main/java/nextstep/optional/ComputerStore.java @@ -3,8 +3,13 @@ import nextstep.optional.Computer.Soundcard; import nextstep.optional.Computer.USB; +import java.util.Optional; + public class ComputerStore { - public static final String UNKNOWN_VERSION = "UNKNOWN"; + private static final String UNKNOWN_VERSION = "UNKNOWN"; + private static final USB UNKNOWN_USB = new USB(UNKNOWN_VERSION); + private static final Soundcard UNKNOWN_SOUNDCARD = new Soundcard(UNKNOWN_USB); + private static final Computer UNKNOWN_COMPUTER = new Computer(UNKNOWN_SOUNDCARD); public static String getVersion(Computer computer) { String version = UNKNOWN_VERSION; @@ -21,6 +26,9 @@ public static String getVersion(Computer computer) { } public static String getVersionOptional(Computer computer) { - return null; + return Optional.ofNullable(computer).orElse(UNKNOWN_COMPUTER) + .getOptionalSoundcard().orElse(UNKNOWN_SOUNDCARD) + .getOptionalUsb().orElse(UNKNOWN_USB) + .getVersion(); } } diff --git a/src/main/java/nextstep/optional/Expression.java b/src/main/java/nextstep/optional/Expression.java index 1c98cd6a..b335794b 100644 --- a/src/main/java/nextstep/optional/Expression.java +++ b/src/main/java/nextstep/optional/Expression.java @@ -1,5 +1,7 @@ package nextstep.optional; +import java.util.Arrays; + enum Expression { PLUS("+"), MINUS("-"), TIMES("*"), DIVIDE("/"); @@ -14,12 +16,11 @@ private static boolean matchExpression(Expression e, String expression) { } static Expression of(String expression) { - for (Expression v : values()) { - if (matchExpression(v, expression)) { - return v; - } - } - - throw new IllegalArgumentException(String.format("%s는 사칙연산에 해당하지 않는 표현식입니다.", expression)); + return Arrays.stream(values()) + .filter(expression1 -> matchExpression(expression1, expression)) + .findFirst() + .orElseThrow( + () -> new IllegalArgumentException(String.format("%s는 사칙연산에 해당하지 않는 표현식입니다.", expression)) + ); } } diff --git a/src/main/java/nextstep/optional/User.java b/src/main/java/nextstep/optional/User.java index 9614c2f4..800539f8 100644 --- a/src/main/java/nextstep/optional/User.java +++ b/src/main/java/nextstep/optional/User.java @@ -1,5 +1,7 @@ package nextstep.optional; +import java.util.Optional; + public class User { private String name; private Integer age; @@ -25,15 +27,15 @@ public static boolean ageIsInRange1(User user) { boolean isInRange = false; if (user != null && user.getAge() != null - && (user.getAge() >= 30 - && user.getAge() <= 45)) { + && (user.getAge() >= 30 + && user.getAge() <= 45)) { isInRange = true; } return isInRange; } public static boolean ageIsInRange2(User user) { - return false; + return Optional.ofNullable(user).map(User::getAge).filter(x -> x >= 30 && x <= 45).isPresent(); } @Override @@ -54,16 +56,13 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; User other = (User) obj; + if (age == null) { - if (other.age != null) - return false; + return other.age == null; } else if (!age.equals(other.age)) return false; if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - return true; + return other.name == null; + } else return name.equals(other.name); } } diff --git a/src/main/java/nextstep/optional/Users.java b/src/main/java/nextstep/optional/Users.java index 6293040d..f77f6527 100644 --- a/src/main/java/nextstep/optional/Users.java +++ b/src/main/java/nextstep/optional/Users.java @@ -2,6 +2,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Optional; public class Users { static final User DEFAULT_USER = new User("codesquad", 100); @@ -12,12 +13,7 @@ public class Users { new User("jk", 40), new User("honux", 45)); - User getUser(String name) { - for (User user : users) { - if (user.matchName(name)) { - return user; - } - } - return DEFAULT_USER; + Optional getUser(String name) { + return users.stream().filter(user -> user.matchName(name)).findFirst(); } } diff --git a/src/test/java/nextstep/blackjack/blackjack/fixture/OneCardFixture.java b/src/test/java/nextstep/blackjack/blackjack/fixture/OneCardFixture.java new file mode 100644 index 00000000..08888b40 --- /dev/null +++ b/src/test/java/nextstep/blackjack/blackjack/fixture/OneCardFixture.java @@ -0,0 +1,23 @@ +package nextstep.blackjack.blackjack.fixture; + +import nextstep.blackjack.blackjack.onecards.Card; +import nextstep.blackjack.blackjack.onecards.CardNumber; +import nextstep.blackjack.blackjack.onecards.CardType; + +import java.util.Arrays; +import java.util.List; + +public class OneCardFixture { + public static final Card 첫_번째_카드 = new Card(CardType.CLOVER, CardNumber.ACE); + public static final Card 두_번째_카드 = new Card(CardType.CLOVER, CardNumber.TWO); + public static final Card 세_번째_카드 = new Card(CardType.CLOVER, CardNumber.THREE); + public static final Card 네_번쨰_카드 = new Card(CardType.CLOVER, CardNumber.FOUR); + private static final Card 다섯_번째_카드 = new Card(CardType.CLOVER, CardNumber.JACK); + private static final Card 여섯_번째_카드 = new Card(CardType.HEART, CardNumber.JACK); + private static final Card 일곱_번째_카드 = new Card(CardType.HEART, CardNumber.ACE); + private static final Card 여덟_번째_카드 = new Card(CardType.DIAMOND, CardNumber.ACE); + private static final Card 아홉_번째_카드 = new Card(CardType.SPADE, CardNumber.ACE); + public static final List CARD_LIST = Arrays.asList(첫_번째_카드, 두_번째_카드, 세_번째_카드, 네_번쨰_카드, 다섯_번째_카드, + 여섯_번째_카드, 일곱_번째_카드, 여덟_번째_카드, 아홉_번째_카드); + +} diff --git a/src/test/java/nextstep/blackjack/blackjack/member/DealerTest.java b/src/test/java/nextstep/blackjack/blackjack/member/DealerTest.java new file mode 100644 index 00000000..b583cadb --- /dev/null +++ b/src/test/java/nextstep/blackjack/blackjack/member/DealerTest.java @@ -0,0 +1,37 @@ +package nextstep.blackjack.blackjack.member; + +import nextstep.blackjack.blackjack.onecards.OneCards; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.LinkedList; + +import static nextstep.blackjack.blackjack.fixture.OneCardFixture.CARD_LIST; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class DealerTest { + + OneCards 카드덱; + + @BeforeEach + void setUp() { + 카드덱 = new OneCards(new LinkedList<>(CARD_LIST)); + } + + @Test + @DisplayName("딜러는 카드를 받습니다.") + void dealer_create() { + assertDoesNotThrow( + () -> new Dealer(카드덱) + ); + } + + @Test + @DisplayName("딜러의 카드가 16이하면 카드를 추가로 받습니다.") + void dealer_create_add_cards() { + Dealer dealer = new Dealer(카드덱); + assertThat(dealer.getCards().sumAll()).isGreaterThan(16); + } +} diff --git a/src/test/java/nextstep/blackjack/blackjack/member/ParticipantTest.java b/src/test/java/nextstep/blackjack/blackjack/member/ParticipantTest.java new file mode 100644 index 00000000..3670b3ae --- /dev/null +++ b/src/test/java/nextstep/blackjack/blackjack/member/ParticipantTest.java @@ -0,0 +1,49 @@ +package nextstep.blackjack.blackjack.member; + +import nextstep.blackjack.blackjack.onecards.Card; +import nextstep.blackjack.blackjack.onecards.CardNumber; +import nextstep.blackjack.blackjack.onecards.CardType; +import nextstep.blackjack.blackjack.onecards.Cards; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.math.BigDecimal; +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class ParticipantTest { + private static final String USERNAME = "tis"; + private static final BigDecimal BETTING_MONEY = BigDecimal.valueOf(1000L); + private static final Card one = new Card(CardType.CLOVER, CardNumber.FOUR); + private static final Card two = new Card(CardType.CLOVER, CardNumber.JACK); + private static final Cards CARDS = new Cards(Arrays.asList(one, two)); + + @Test + @DisplayName("참여자는 이름과 배팀 금액, 카드 패를 가집니다.") + void participant_create() { + assertDoesNotThrow( + () -> new Participant(USERNAME, BETTING_MONEY, CARDS) + ); + } + + @ParameterizedTest + @NullAndEmptySource + void participant_username_is_not_blank(String username) { + assertThatThrownBy( + () -> new Participant(username, BETTING_MONEY, CARDS) + ).isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @ValueSource(ints = {-1, -1000}) + void participant_battingMoney_is_not_minus(int money) { + assertThatThrownBy( + () -> new Participant(USERNAME, BigDecimal.valueOf(money), CARDS) + ).isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/nextstep/blackjack/blackjack/member/ParticipantsTest.java b/src/test/java/nextstep/blackjack/blackjack/member/ParticipantsTest.java new file mode 100644 index 00000000..6584b2de --- /dev/null +++ b/src/test/java/nextstep/blackjack/blackjack/member/ParticipantsTest.java @@ -0,0 +1,58 @@ +package nextstep.blackjack.blackjack.member; + +import nextstep.blackjack.blackjack.onecards.Cards; +import nextstep.blackjack.blackjack.onecards.OneCards; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.LinkedList; + +import static nextstep.blackjack.blackjack.fixture.OneCardFixture.CARD_LIST; +import static org.assertj.core.api.Assertions.assertThat; + +class ParticipantsTest { + + OneCards 카드덱; + + + @BeforeEach + void setUp() { + 카드덱 = new OneCards(new LinkedList<>(CARD_LIST)); + } + + @Test + @DisplayName("게임에 참여자들이 입장한다.") + void participants_create() { + Assertions.assertDoesNotThrow( + () -> new Participants(Arrays.asList( + new Participant("tis", BigDecimal.TEN, 카드덱), + new Participant("me", BigDecimal.valueOf(20_000), 카드덱), + new Participant("yolo", BigDecimal.valueOf(30_000_000), 카드덱) + ) + ) + ); + } + + @Test + @DisplayName("참여자들에게 카드를 분배한다.") + void participants_distribute() { + // given + int size = 카드덱.getCardList().size(); + + Participants participants = new Participants(Arrays.asList( + new Participant("tis", BigDecimal.TEN, 카드덱), + new Participant("me", BigDecimal.valueOf(20_000), 카드덱), + new Participant("yolo", BigDecimal.valueOf(30_000_000), 카드덱) + )); + + // when + participants.distribute(카드덱); + + // then + assertThat(카드덱.getCardList()).hasSize(size - 9); + } +} diff --git a/src/test/java/nextstep/blackjack/blackjack/onecards/CardTest.java b/src/test/java/nextstep/blackjack/blackjack/onecards/CardTest.java new file mode 100644 index 00000000..86d4ccab --- /dev/null +++ b/src/test/java/nextstep/blackjack/blackjack/onecards/CardTest.java @@ -0,0 +1,31 @@ +package nextstep.blackjack.blackjack.onecards; + +import nextstep.blackjack.blackjack.onecards.Card; +import nextstep.blackjack.blackjack.onecards.CardNumber; +import nextstep.blackjack.blackjack.onecards.CardType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class CardTest { + + @Test + @DisplayName("카드는 숫자와 타입을 가진다.") + void card_create() { + assertDoesNotThrow( + () -> new Card(CardType.CLOVER, CardNumber.FOUR) + ); + } + + @Test + @DisplayName("자신의 정보를 반환한다.") + void card_getName() { + CardType clover = CardType.CLOVER; + CardNumber four = CardNumber.FOUR; + + Card clover_four = new Card(clover, four); + assertThat(clover_four.name()).isEqualTo(four.number() + clover.getType()); + } +} diff --git a/src/test/java/nextstep/blackjack/blackjack/onecards/CardsTest.java b/src/test/java/nextstep/blackjack/blackjack/onecards/CardsTest.java new file mode 100644 index 00000000..75c89ed0 --- /dev/null +++ b/src/test/java/nextstep/blackjack/blackjack/onecards/CardsTest.java @@ -0,0 +1,68 @@ +package nextstep.blackjack.blackjack.onecards; + +import nextstep.blackjack.blackjack.onecards.Card; +import nextstep.blackjack.blackjack.onecards.CardNumber; +import nextstep.blackjack.blackjack.onecards.CardType; +import nextstep.blackjack.blackjack.onecards.Cards; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class CardsTest { + private static final Card CLOVER_FOUR = new Card(CardType.CLOVER, CardNumber.FOUR); + private static final Card CLOVER_ACE = new Card(CardType.CLOVER, CardNumber.ACE); + private static final Card HEART_ACE = new Card(CardType.HEART, CardNumber.ACE); + private static final Card CLOVER_THREE = new Card(CardType.CLOVER, CardNumber.THREE); + private static final Card CLOVER_JACK = new Card(CardType.CLOVER, CardNumber.JACK); + private static final int SEVEN = 7; + private static final int TWELVE = 12; + + @Test + @DisplayName("카드 패는 최소 두 장이 들어간다.") + void cards_atLeast_two() { + assertDoesNotThrow( + () -> new Cards(Arrays.asList(CLOVER_FOUR, CLOVER_THREE)) + ); + } + + @Test + @DisplayName("카드패의 숫자 합을 출력한다.") + void sumCards() { + Cards cards = new Cards(Arrays.asList(CLOVER_FOUR, CLOVER_THREE)); + assertThat(cards.sumAll()).isEqualTo(SEVEN); + } + + @Test + @DisplayName("Ace가 두장이면 12이다.") + void sumCards_ace_and_ace() { + Cards cards = new Cards(Arrays.asList(CLOVER_ACE, HEART_ACE)); + assertThat(cards.sumAll()).isEqualTo(TWELVE); + } + + @Test + @DisplayName("에이스와 잭이면 합이 21이다.") + void sumCards_ace_and_jack() { + Cards cards = new Cards(Arrays.asList(CLOVER_ACE, CLOVER_JACK)); + assertThat(cards.sumAll()).isEqualTo(21); + } + + @Test + @DisplayName("에이스가 두 장이고 잭이 한 장이면 12이다.") + void sumCards_ace_and_ace_and_jack() { + Cards cards = new Cards(Arrays.asList(CLOVER_ACE, CLOVER_JACK)); + + Cards newCards = cards.putCard(HEART_ACE); + + assertThat(newCards.sumAll()).isEqualTo(12); + } + + @Test + @DisplayName("합이 21이 넘으면 더 이상 받을 수 없다.") + void cards_putCard_notSumAllOver21() { + + } +} diff --git a/src/test/java/nextstep/blackjack/blackjack/onecards/OneCardsGeneratorTest.java b/src/test/java/nextstep/blackjack/blackjack/onecards/OneCardsGeneratorTest.java new file mode 100644 index 00000000..8b797e20 --- /dev/null +++ b/src/test/java/nextstep/blackjack/blackjack/onecards/OneCardsGeneratorTest.java @@ -0,0 +1,16 @@ +package nextstep.blackjack.blackjack.onecards; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class OneCardsGeneratorTest { + private static final int ONE_CARDS_SIZE = 52; + + @Test + @DisplayName("카드덱에는 52 종류의 카드가 저장되어 있다.") + void getOneCardsSize() { + OneCards oneCards = OneCardsGenerator.generateOneCards(); + Assertions.assertThat(oneCards.getCardList()).hasSize(ONE_CARDS_SIZE); + } +} diff --git a/src/test/java/nextstep/blackjack/blackjack/onecards/OneCardsTest.java b/src/test/java/nextstep/blackjack/blackjack/onecards/OneCardsTest.java new file mode 100644 index 00000000..e7eb0a39 --- /dev/null +++ b/src/test/java/nextstep/blackjack/blackjack/onecards/OneCardsTest.java @@ -0,0 +1,44 @@ +package nextstep.blackjack.blackjack.onecards; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.LinkedList; + +import static nextstep.blackjack.blackjack.fixture.OneCardFixture.CARD_LIST; +import static nextstep.blackjack.blackjack.fixture.OneCardFixture.첫_번째_카드; + +class OneCardsTest { + + OneCards 카드덱; + + @BeforeEach + void setUp() { + 카드덱 = new OneCards(new LinkedList<>(CARD_LIST)); + } + + @Test + @DisplayName("카드덱에서 카드를 뽑으면 카드덱에서 한 장 사라진다.") + void getCard() { + // given + int 카드덱_사이즈 = 카드덱.getCardList().size(); + + // when + 카드덱.pollCard(); + + // then + Assertions.assertThat(카드덱.getCardList().size()).isEqualTo(카드덱_사이즈 - 1); + } + + @Test + @DisplayName("카드덱은 추가된 순서대로 받을 수 있다.") + void getFirstCard() { + // when + Card card = 카드덱.pollCard(); + + // then + Assertions.assertThat(card).isEqualTo(첫_번째_카드); + } +} diff --git a/src/test/java/nextstep/blackjack/blackjack/service/BlackjackOutputServiceTest.java b/src/test/java/nextstep/blackjack/blackjack/service/BlackjackOutputServiceTest.java new file mode 100644 index 00000000..9e0b951f --- /dev/null +++ b/src/test/java/nextstep/blackjack/blackjack/service/BlackjackOutputServiceTest.java @@ -0,0 +1,124 @@ +package nextstep.blackjack.blackjack.service; + +import nextstep.blackjack.blackjack.member.Dealer; +import nextstep.blackjack.blackjack.member.Participant; +import nextstep.blackjack.blackjack.member.Participants; +import nextstep.blackjack.blackjack.onecards.Card; +import nextstep.blackjack.blackjack.onecards.CardNumber; +import nextstep.blackjack.blackjack.onecards.CardType; +import nextstep.blackjack.blackjack.onecards.Cards; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class BlackjackOutputServiceTest { + + + private static final Cards 합_21_카드_3장 = new Cards(Arrays.asList(new Card(CardType.CLOVER, CardNumber.ACE), new Card(CardType.DIAMOND, CardNumber.ACE), new Card(CardType.DIAMOND, CardNumber.NINE))); + private static final Cards 합_21_카드_2장 = new Cards(Arrays.asList(new Card(CardType.CLOVER, CardNumber.JACK), new Card(CardType.DIAMOND, CardNumber.ACE))); + private static final Cards 합_24_카드 = new Cards(Arrays.asList(new Card(CardType.CLOVER, CardNumber.EIGHT), new Card(CardType.DIAMOND, CardNumber.EIGHT), new Card(CardType.HEART, CardNumber.EIGHT))); + private static final Cards 합이_18인_카드 = new Cards(Arrays.asList(new Card(CardType.CLOVER, CardNumber.JACK), new Card(CardType.DIAMOND, CardNumber.EIGHT))); + private static final Cards 합이_16인_카드 = new Cards(Arrays.asList(new Card(CardType.CLOVER, CardNumber.JACK), new Card(CardType.DIAMOND, CardNumber.SIX))); + private static final Participant 합이_21_인_사용자 = new Participant("첫 번째 참여자", BigDecimal.valueOf(20_000L), 합_21_카드_3장); + private static final Participant 한_번에_합_21_인_사용자 = new Participant("첫 번째 참여자", BigDecimal.valueOf(20_000L), 합_21_카드_2장); + private static final Participant 합이_18_인_사용자 = new Participant("세 번째 참여자", BigDecimal.valueOf(20_000L), 합이_18인_카드); + private static final Participant 합이_16_인_사용자 = new Participant("두 번째 참여자", BigDecimal.valueOf(20_000L), 합이_16인_카드); + + private BlackjackOutputService outputService; + + @BeforeEach + void setUp() { + outputService = new BlackjackOutputService(); + } + + @Test + @DisplayName("딜러와 참여자 한 명의 카드 합이 같으면 배팅 금액을 돌려받고, 나머지 참여자는 금액을 잃는다..") + void getResult_winner_dealer_and_participants() { + // given + Dealer dealer = new Dealer(합_21_카드_3장); + Participants participants = new Participants(Arrays.asList(합이_16_인_사용자, 합이_18_인_사용자, 합이_21_인_사용자)); + + // when + Map 결과 = outputService.distributeMoney(participants, dealer); + + // then + assertThat(결과.get("dealer")).isEqualTo(BigDecimal.valueOf(20_000L)); + assertThat(결과.get(합이_21_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(20_000L)); + assertThat(결과.get(합이_16_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(-20_000L)); + assertThat(결과.get(합이_18_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(-20_000L)); + } + + @Test + @DisplayName("우승자가 딜러인 경우 배팅 금액을 전부 가져가고 참여자들은 모두 잃는다.") + void getResult_winner_dealer() { + // given + Dealer dealer = new Dealer(합_21_카드_3장); + Participants participants = new Participants(Arrays.asList(합이_18_인_사용자, 합이_16_인_사용자)); + + // when + Map 결과 = outputService.distributeMoney(participants, dealer); + + // then + assertThat(결과.get("dealer")).isEqualTo(BigDecimal.valueOf(40_000L)); + assertThat(결과.get(합이_16_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(-20_000L)); + assertThat(결과.get(합이_18_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(-20_000L)); + } + + + @Test + @DisplayName("딜러 카드 합이 21을 초과하는 경우 모든 참여자에게 배팅 금액만큼 지불한다.") + void getResult_over_dealer() { + // given + Dealer dealer = new Dealer(합_24_카드); + Participants participants = new Participants(Arrays.asList(합이_16_인_사용자, 합이_18_인_사용자, 합이_21_인_사용자)); + + // when + Map 결과 = outputService.distributeMoney(participants, dealer); + + // then + assertThat(결과.get("dealer")).isEqualTo(BigDecimal.valueOf(-60_000L)); + assertThat(결과.get(합이_16_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(20_000L)); + assertThat(결과.get(합이_18_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(20_000L)); + assertThat(결과.get(합이_21_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(20_000L)); + } + + @Test + @DisplayName("참여자의 처음 두 장 합이 21인 경우 1.5 배의 배팅 금액을 받고, 그 외 참여자는 돈을 잃는다.") + void getResult_winner_first_blackjack() { + // given + Dealer dealer = new Dealer(합이_18인_카드); + Participants participants = new Participants(Arrays.asList(합이_16_인_사용자, 합이_18_인_사용자, 한_번에_합_21_인_사용자)); + + // when + Map 결과 = outputService.distributeMoney(participants, dealer); + + // then + assertThat(결과.get("dealer")).isEqualTo(BigDecimal.valueOf(10_000L)); + assertThat(결과.get(합이_16_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(-20_000L)); + assertThat(결과.get(합이_18_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(-20_000L)); + assertThat(결과.get(한_번에_합_21_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(30_000L)); + } + + @Test + @DisplayName("우승자가 참여자인 경우 배팅 금액 만큼 딜러에게 받아가고 그 외 참여자는 돈을 잃는다.") + void getResult_winner_participants() { + // given + Dealer dealer = new Dealer(합이_16인_카드); + Participants participants = new Participants(Arrays.asList(합이_16_인_사용자, 합이_18_인_사용자)); + + // when + Map 결과 = outputService.distributeMoney(participants, dealer); + + // then + assertThat(결과.get("dealer")).isEqualTo(BigDecimal.ZERO); + assertThat(결과.get(합이_16_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(-20_000L)); + assertThat(결과.get(합이_18_인_사용자.getUsername())).isEqualTo(BigDecimal.valueOf(20_000L)); + } + +} diff --git a/src/test/java/nextstep/fp/CarTest.java b/src/test/java/nextstep/fp/CarTest.java index 1ab1106f..add44143 100644 --- a/src/test/java/nextstep/fp/CarTest.java +++ b/src/test/java/nextstep/fp/CarTest.java @@ -8,24 +8,15 @@ public class CarTest { @Test public void 이동() { Car car = new Car("pobi", 0); - Car actual = car.move(new MoveStrategy() { - @Override - public boolean isMovable() { - return true; - } - }); + Car actual = car.move(() -> true); + assertThat(actual).isEqualTo(new Car("pobi", 1)); } @Test public void 정지() { Car car = new Car("pobi", 0); - Car actual = car.move(new MoveStrategy() { - @Override - public boolean isMovable() { - return false; - } - }); + Car actual = car.move(() -> false); assertThat(actual).isEqualTo(new Car("pobi", 0)); } } diff --git a/src/test/java/nextstep/optional/ComputerStoreTest.java b/src/test/java/nextstep/optional/ComputerStoreTest.java index b576253a..c8185f57 100644 --- a/src/test/java/nextstep/optional/ComputerStoreTest.java +++ b/src/test/java/nextstep/optional/ComputerStoreTest.java @@ -7,6 +7,8 @@ import static org.assertj.core.api.Assertions.assertThat; public class ComputerStoreTest { + private static final String UNKNOWN_VERSION = "UNKNOWN"; + @Test public void getVersion() { String version = "pobi's usb"; @@ -17,19 +19,19 @@ public void getVersion() { @Test public void getVersionWhenComputerIsNull() throws Exception { - assertThat(ComputerStore.getVersion(null)).isEqualTo(ComputerStore.UNKNOWN_VERSION); + assertThat(ComputerStore.getVersion(null)).isEqualTo(UNKNOWN_VERSION); } @Test public void getVersionWhenSoundcardIsNull() throws Exception { Computer computer = new Computer(null); - assertThat(ComputerStore.getVersion(computer)).isEqualTo(ComputerStore.UNKNOWN_VERSION); + assertThat(ComputerStore.getVersion(computer)).isEqualTo(UNKNOWN_VERSION); } @Test public void getVersionWhenUSBIsNull() throws Exception { Computer computer = new Computer(new Soundcard(null)); - assertThat(ComputerStore.getVersion(computer)).isEqualTo(ComputerStore.UNKNOWN_VERSION); + assertThat(ComputerStore.getVersion(computer)).isEqualTo(UNKNOWN_VERSION); } @Test @@ -42,18 +44,18 @@ public void getVersionOptional() { @Test public void getVersionOptionalWhenComputerIsNull() throws Exception { - assertThat(ComputerStore.getVersionOptional(null)).isEqualTo(ComputerStore.UNKNOWN_VERSION); + assertThat(ComputerStore.getVersionOptional(null)).isEqualTo(UNKNOWN_VERSION); } @Test public void getVersionOptionalWhenSoundcardIsNull() throws Exception { Computer computer = new Computer(null); - assertThat(ComputerStore.getVersionOptional(computer)).isEqualTo(ComputerStore.UNKNOWN_VERSION); + assertThat(ComputerStore.getVersionOptional(computer)).isEqualTo(UNKNOWN_VERSION); } @Test public void getVersionOptionalWhenUSBIsNull() throws Exception { Computer computer = new Computer(new Soundcard(null)); - assertThat(ComputerStore.getVersionOptional(computer)).isEqualTo(ComputerStore.UNKNOWN_VERSION); + assertThat(ComputerStore.getVersionOptional(computer)).isEqualTo(UNKNOWN_VERSION); } } diff --git a/src/test/java/nextstep/optional/ExpressionTest.java b/src/test/java/nextstep/optional/ExpressionTest.java index 32356261..32e0f72f 100644 --- a/src/test/java/nextstep/optional/ExpressionTest.java +++ b/src/test/java/nextstep/optional/ExpressionTest.java @@ -15,8 +15,8 @@ public void of() { @Test public void notValidExpression() { assertThatIllegalArgumentException() - .isThrownBy(() -> { - Expression.of("&"); - }); + .isThrownBy(() -> { + Expression.of("&"); + }); } } diff --git a/src/test/java/nextstep/optional/UsersTest.java b/src/test/java/nextstep/optional/UsersTest.java index ec0f7329..e4c2a88d 100644 --- a/src/test/java/nextstep/optional/UsersTest.java +++ b/src/test/java/nextstep/optional/UsersTest.java @@ -9,13 +9,17 @@ public class UsersTest { @Test public void getUser() { Users users = new Users(); - assertThat(users.getUser("crong")).isEqualTo(new User("crong", 35)); + assertThat(getUser(users, "crong")).isEqualTo(new User("crong", 35)); } @Test public void getDefaultUser() { Users users = new Users(); - assertThat(users.getUser("codesquard")).isEqualTo(Users.DEFAULT_USER); + assertThat(getUser(users, "codesquard")).isEqualTo(Users.DEFAULT_USER); + } + + private User getUser(Users users, String codesquard) { + return users.getUser(codesquard).orElse(Users.DEFAULT_USER); } }