Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2단계 - 블랙잭 베팅] 리브(김민주) 미션 제출합니다. #695

Merged
merged 41 commits into from
Mar 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
39a9f08
docs(README.md): 요구 사항 업데이트
Minjoo522 Mar 13, 2024
1758848
feat(Money): 베팅 금액의 원시값을 포장하는 Money 객체 구현
Minjoo522 Mar 13, 2024
48cf9fa
feat(InputView): 배팅 금액 입력 받는 기능 구현
Minjoo522 Mar 13, 2024
2dae152
feat(Money): getMoney 구현
Minjoo522 Mar 13, 2024
3714c5f
feat(Hit): Hit 상태를 표현하는 객체 구현
Minjoo522 Mar 13, 2024
4daf110
feat(BlackJack): BlackJack 상태를 표현하는 객체 구현
Minjoo522 Mar 13, 2024
629f779
feat(Bust): Bust 상태를 표현하는 객체 구현
Minjoo522 Mar 13, 2024
ebbbf67
feat(Stand): Stand 상태를 표현하는 객체 구현
Minjoo522 Mar 13, 2024
351d619
feat(State): 현재 소유한 카드의 총 점수를 계산하는 calculateScore() 메서드 추가
Minjoo522 Mar 13, 2024
1e0a610
feat(Participant): Dealer, Player의 중복 메서드 Participant로 이동
Minjoo522 Mar 13, 2024
0b7e558
refactor(State): state에서 List<Card> -> Hand를 반환하는 메서드로 변경
Minjoo522 Mar 13, 2024
4c98d73
feat(Money): 현재 금액에 전달 받은 숫자 곱하는 기능 구현
Minjoo522 Mar 13, 2024
ddffb0c
refactor(Participant): 불필요한 Hand 필드 제거
Minjoo522 Mar 13, 2024
4e88564
feat(bets): 플레이어의 베팅 금액을 저장하는 Bets 구현 및 BlackJackGame에서 사용
Minjoo522 Mar 13, 2024
8fed05d
feat(State): calculateProfit 제거 및 isBlackJack, isBust 추가
Minjoo522 Mar 13, 2024
a412a89
rename: 불필요한 클래스 제거
Minjoo522 Mar 13, 2024
222eb48
feat(ResultCommand, Referee, Rule): 블랙잭 승리 관련 조건 추가
Minjoo522 Mar 13, 2024
40fccda
feat(Bets): 플레이어의 베팅 금액을 모아둔 Bets 생성 및 Money의 validate Bets로 이동
Minjoo522 Mar 13, 2024
847eab2
feat(NameProfit): 플레이어의 이름, 수익금을 담는 NameProfit Dto 추가
Minjoo522 Mar 13, 2024
8aee5df
feat(OutputView, BlackJackGame): 딜러와 플레이어의 수익금 출력하는 기능 구현
Minjoo522 Mar 13, 2024
fe6f360
rename(state): state 관련 클래스 제거
Minjoo522 Mar 13, 2024
20388a4
refactor(Participant): Dealer와 Player에 중복 코드 추상 클래스로 이동
Minjoo522 Mar 13, 2024
608df98
rename(Bets, Money): 패키지 이동
Minjoo522 Mar 13, 2024
b758eaa
refactor(Referee): Rule -> Referee와 통합
Minjoo522 Mar 13, 2024
1a7335e
refactor(BlackJackGame): 최종 수익 출력 관련 메서드 분리
Minjoo522 Mar 13, 2024
3ede65e
docs(README.md): 요구 사항 완료 내역 체크
Minjoo522 Mar 13, 2024
d4d04c4
style(test, BlackJackGame): 불필요한 import 구문 및 파라미터 제거
Minjoo522 Mar 13, 2024
784df14
rename(ResultCommandDisplay): 사용하지 않는 display enum 제거
Minjoo522 Mar 14, 2024
eb4e010
rename(BetMoney): Money -> BetMoney로 이름 변경
Minjoo522 Mar 15, 2024
1342326
refactor(BetMoney): 각 player의 수익을 계산하는 calculatePlayerProfit에서 Map이 아…
Minjoo522 Mar 15, 2024
de58ed6
refactor(Players): 필요 없는 파라미터 제거
Minjoo522 Mar 15, 2024
cca88cf
refactor(BlackJackGame): Bets를 Players가 아닌 BlackJackGame이 담담하도록 변경
Minjoo522 Mar 15, 2024
9b85741
refactor(Profit): 당첨 수익금을 포장하는 Profit 객체 생성 및 사용
Minjoo522 Mar 15, 2024
342fbd0
feat(BetMoney): 1원 미만 1억 초과 금액을 베팅 금액으로 입력했을 때 예외가 발생하는 기능 구현
Minjoo522 Mar 15, 2024
af6a018
rename(ProfitStatement): NameProfit -> ProfitStatement로 변경
Minjoo522 Mar 15, 2024
797d6db
fit(BlackJackGame): 데이터 타입 변경 반영
Minjoo522 Mar 15, 2024
16acf7a
refactor(BetsTest): TestFixture 분리 및 ParameterizedTest 개별 테스트로 분리
Minjoo522 Mar 15, 2024
bf22dd7
refactor(Participant, Dealer): canHit을 추상 클래스에서 제거하고 Dealer에서 메서드 명을 …
Minjoo522 Mar 15, 2024
98c12c1
rename(Deck): distribute -> drawn 메서드명 변경
Minjoo522 Mar 15, 2024
d668349
refactor(test): 테스트 코드에 Fixture 추가 및 DisplayName 상세화
Minjoo522 Mar 15, 2024
ee98739
refactor(test): 예외 메시지가 변경되어도 테스트가 실패하지 않도록 수정
Minjoo522 Mar 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,22 +84,41 @@
- [x] OutputView, InputView 정적 클래스 ❌

## 딜러가 덱 생성을 하고, 쥐고 있으면서 경기 진행

- [x] 딜러와 플레이어 분리
- [x] 딜러에게 필요한 것
- [x] Deck
- [x] Hand
- [x] 딜러에게 필요한 역할
- [x] 덱 생성
- [x] 카드 분배
- [x] 플레이어에게 필요한 것
- [x] Name
- [x] Hand
- [x] 딜러에게 필요한 것
- [x] Deck
- [x] Hand
- [x] 딜러에게 필요한 역할
- [x] 덱 생성
- [x] 카드 분배
- [x] 플레이어에게 필요한 것
- [x] Name
- [x] Hand
- [x] 딜러, 플레이어가 생성될 때 카드를 먼저 넘기고 시작하면 안된다.(딜러가 나누어주어야 하니까)

### ACE 점수 계산

- [x] ACE가 0개일 때 0
- [x] ACE가 두 개 이상
- [x] 한 개를 제외한 모든 ACE는 1점
- [x] 나머지 한 개 ACE
- [x] 다른 카드 점수 합 + (ACE 개수 - 1)이 10보다 크면 1점, 아니면 11점
- [x] 한 개를 제외한 모든 ACE는 1점
- [x] 나머지 한 개 ACE
- [x] 다른 카드 점수 합 + (ACE 개수 - 1)이 10보다 크면 1점, 아니면 11점

---

# 2단계 - 블랙잭 베팅

- [x] 베팅 금액 전달 받기(Money)
- [x] 공백 ❌
- [x] 숫자 ❌
- [x] 음수 ❌
- [x] 베팅은 최소 1원부터 최대 1억까지 가능하다(파산 방지 🥹💰)
- [x] 입력 받은 베팅 금액 관리(BetMoney) -> 각 플레이어와 금액 정보

## 베팅 규칙

- [x] 21을 초과할 경우 베팅 금액을 모두 잃게 된다
- [x] 블랙잭 : 1.5배를 딜러에게 받는다
- [x] 딜러, 플레이어 동시에 블랙잭 : 플레이어는 베팅한 금액을 돌려 받음
- [x] 딜러가 버스트되기 전까지 `남아 있던` 플레이어들은 가지고 있는 패에 상관 없이 승리(돌려 받음)
83 changes: 56 additions & 27 deletions src/main/java/blackjack/BlackJackGame.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package blackjack;

import blackjack.dto.DealerResultCount;
import blackjack.dto.NameCards;
import blackjack.dto.NameCardsScore;
import blackjack.dto.PlayerNameFinalResult;
import blackjack.dto.ProfitStatement;
import blackjack.model.deck.Deck;
import blackjack.model.money.Bets;
import blackjack.model.money.Profit;
import blackjack.model.participant.Dealer;
import blackjack.model.participant.Player;
import blackjack.model.participant.Players;
import blackjack.model.result.Referee;
import blackjack.model.result.Rule;
import blackjack.model.result.ResultCommand;
import blackjack.view.InputView;
import blackjack.view.OutputView;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class BlackJackGame {
Expand All @@ -29,16 +32,15 @@ public BlackJackGame(InputView inputView, OutputView outputView) {
public void run() {
final Dealer dealer = new Dealer(new Deck());
final Players players = registerPlayers();
final Referee referee = new Referee(new Rule(dealer));
final Referee referee = new Referee(dealer);
final Bets bets = registerBets(players);

registerInitialCards(dealer, players);
outputView.printDistributionSubject(players.getNames());
printInitialCards(dealer, players);

playPlayersTurn(players, dealer);
playDealerTurn(dealer);

printFinalResult(dealer, players, referee);
playParticipantsTurn(players, dealer);
printFinalCardsAndScores(dealer, players);
printResultProfits(referee, players, bets);
}

private Players registerPlayers() {
Expand All @@ -51,9 +53,34 @@ private Players registerPlayers() {
}
}

private Bets registerBets(final Players players) {
final Bets bets = new Bets();
final Map<Player, Integer> playersBetMoney = readPlayersBetMoney(players);
for (Player player: playersBetMoney.keySet()) {
int money = playersBetMoney.get(player);
bets.addBet(player, money);
}
return bets;
}

private Map<Player, Integer> readPlayersBetMoney(final Players players) {
return players.stream()
.collect(Collectors.toMap(player -> player, player -> readBetMoney(player.getName())));
}

private int readBetMoney(final String name) {
try {
return inputView.readBetMoney(name);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
return readBetMoney(name);
}
}

private void registerInitialCards(final Dealer dealer, final Players players) {
dealer.receiveInitialCards(dealer.distributeInitialCard());
players.receiveInitialCards(dealer::distributeInitialCard);
outputView.printDistributionSubject(players.getNames());
}

private void printInitialCards(final Dealer dealer, final Players players) {
Expand All @@ -72,6 +99,11 @@ private List<NameCards> createDealerPlayersNameCards(final Dealer dealer, final
.toList();
}

private void playParticipantsTurn(final Players players, final Dealer dealer) {
playPlayersTurn(players, dealer);
playDealerTurn(dealer);
}

private void playPlayersTurn(final Players players, final Dealer dealer) {
players.playEachPlayerTurns(this::playPlayerTurn, dealer);
}
Expand Down Expand Up @@ -100,17 +132,12 @@ private void distributeIfPlayerWant(final boolean isHit, final Player player, fi
}

private void playDealerTurn(final Dealer dealer) {
while (dealer.canHit()) {
while (dealer.shouldHit()) {
outputView.printDealerHit();
dealer.draw(dealer.distributeCard());
}
}

private void printFinalResult(final Dealer dealer, final Players players, final Referee referee) {
printFinalCardsAndScores(dealer, players);
printFinalResultCommand(referee, players);
}

private void printFinalCardsAndScores(final Dealer dealer, final Players players) {
outputView.println();
NameCardsScore dealerNameCardsScore = new NameCardsScore(OutputView.DEALER_NAME, dealer.openCards(),
Expand All @@ -120,19 +147,21 @@ private void printFinalCardsAndScores(final Dealer dealer, final Players players
outputView.printFinalCardsAndScore(playerNameCardsScore);
}

private void printFinalResultCommand(final Referee referee, final Players players) {
printDealerFinalResultCount(referee, players);
List<PlayerNameFinalResult> playerNameFinalResults = players.stream()
.map(player -> PlayerNameFinalResult.from(player, referee.judgePlayerResult(player)))
.toList();
outputView.printFinalResults(playerNameFinalResults);
private void printResultProfits(final Referee referee, final Players players, final Bets bets) {
final Map<Player, ResultCommand> playerResultCommands = players.matchPlayerResultCommands(referee);
printDealerResultProfit(playerResultCommands, bets);
printPlayersResultProfit(playerResultCommands, bets);
}

private void printDealerFinalResultCount(final Referee referee, final Players players) {
List<DealerResultCount> dealerResults = referee.judgeDealerResult(players)
.entrySet().stream()
.map(dealerResult -> new DealerResultCount(dealerResult.getKey(), dealerResult.getValue()))
.toList();
outputView.printDealerFinalResult(dealerResults);
private void printDealerResultProfit(final Map<Player, ResultCommand> playerResultCommands,
final Bets bets) {
final Profit dealerProfit = bets.calculateDealerProfit(playerResultCommands);
outputView.printDealerProfit(dealerProfit.getProfit());
}

private void printPlayersResultProfit(final Map<Player, ResultCommand> playerResultCommands, final Bets bets) {
final Map<Player, Profit> playerProfits = bets.calculatePlayersProfit(playerResultCommands);
final List<ProfitStatement> profitStatements = ProfitStatement.createNameProfits(playerProfits);
outputView.printPlayersProfit(profitStatements);
}
}
7 changes: 0 additions & 7 deletions src/main/java/blackjack/dto/DealerResultCount.java

This file was deleted.

11 changes: 0 additions & 11 deletions src/main/java/blackjack/dto/PlayerNameFinalResult.java

This file was deleted.

19 changes: 19 additions & 0 deletions src/main/java/blackjack/dto/ProfitStatement.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package blackjack.dto;

import blackjack.model.money.Profit;
import blackjack.model.participant.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public record ProfitStatement(String name, int profit) {

public static List<ProfitStatement> createNameProfits(final Map<Player, Profit> playerProfit) {
final List<ProfitStatement> profitStatements = new ArrayList<>();
for (Player player : playerProfit.keySet()) {
final int profit = playerProfit.get(player).getProfit();
profitStatements.add(new ProfitStatement(player.getName(), profit));
}
return profitStatements;
}
}
8 changes: 5 additions & 3 deletions src/main/java/blackjack/model/deck/Deck.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

public class Deck {
private static final int BLACKJACK_CARD_COUNT = 52;
static final String DECK_SIZE_IS_NOT_ENOUGH = "카드의 수가 52개가 아닙니다.";
static final String CAN_NOT_DRAWN_CARD = "카드가 부족합니다.";

private final Deque<Card> deck;

Expand All @@ -24,7 +26,7 @@ private Deck(final Deque<Card> deck) {

private void validateSize(final List<Card> deck) {
if (deck.size() != BLACKJACK_CARD_COUNT) {
throw new IllegalStateException("카드의 수가 52개가 아닙니다.");
throw new IllegalStateException(DECK_SIZE_IS_NOT_ENOUGH);
}
}

Expand All @@ -46,11 +48,11 @@ private Deque<Card> shuffleDeck(final List<Card> originDeck) {
return new ArrayDeque<>(originDeck);
}

public Card distribute() {
public Card drawn() {
try {
return deck.removeFirst();
} catch (NoSuchElementException e) {
throw new NoSuchElementException("카드가 부족합니다.");
throw new NoSuchElementException(CAN_NOT_DRAWN_CARD);
}
}
}
47 changes: 47 additions & 0 deletions src/main/java/blackjack/model/money/BetMoney.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package blackjack.model.money;

import java.util.Objects;

public class BetMoney {
private static final int MIN_BET_MONEY = 1;
private static final int MAX_BET_MONEY = 100_000_000;
public static final String OUT_OF_BET_MONEY_BOUND = "1원부터 1억원까지만 베팅 가능합니다.";

private final int betMoney;

public BetMoney(final int betMoney) {
validateBetMoneyInBound(betMoney);
this.betMoney = betMoney;
}

private void validateBetMoneyInBound(int betMoney) {
if (betMoney < MIN_BET_MONEY || betMoney > MAX_BET_MONEY) {
throw new IllegalArgumentException(OUT_OF_BET_MONEY_BOUND);
}
}

public int multiply(final double multiplier) {
return (int) (betMoney * multiplier);
}

public int getBetMoney() {
return betMoney;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BetMoney betMoney1 = (BetMoney) o;
return betMoney == betMoney1.betMoney;
}

@Override
public int hashCode() {
return Objects.hash(betMoney);
}
}
40 changes: 40 additions & 0 deletions src/main/java/blackjack/model/money/Bets.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package blackjack.model.money;

import blackjack.model.participant.Player;
import blackjack.model.result.ResultCommand;
import java.util.LinkedHashMap;
import java.util.Map;

public class Bets {
private final Map<Player, BetMoney> bets;

public Bets() {
this.bets = new LinkedHashMap<>();
}

public void addBet(final Player player, final int money) {
bets.put(player, new BetMoney(money));
}

public Map<Player, Profit> calculatePlayersProfit(final Map<Player, ResultCommand> result) {
final Map<Player, Profit> playersProfit = new LinkedHashMap<>();
for (Player player : result.keySet()) {
final double rate = result.get(player).getRate();
playersProfit.put(player, calculatePlayerProfit(rate, player));
}
return playersProfit;
}

private Profit calculatePlayerProfit(final double rate, final Player player) {
int profitMoney = bets.get(player).multiply(rate);
return new Profit(profitMoney);
}

public Profit calculateDealerProfit(final Map<Player, ResultCommand> result) {
final Map<Player, Profit> playersProfit = calculatePlayersProfit(result);
final int playerTotalProfit = playersProfit.values().stream()
.mapToInt(Profit::getProfit)
.sum();
return new Profit(playerTotalProfit * -1);
}
}
32 changes: 32 additions & 0 deletions src/main/java/blackjack/model/money/Profit.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package blackjack.model.money;

import java.util.Objects;

public class Profit {
private final int profit;

public Profit(int profit) {
this.profit = profit;
}

public int getProfit() {
return profit;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Profit profit1 = (Profit) o;
return profit == profit1.profit;
}

@Override
public int hashCode() {
return Objects.hash(profit);
}
}
Loading