Skip to content

Commit

Permalink
[2단계 - 블랙잭(베팅)] 성하(김성훈) 미션 제출합니다. (#531)
Browse files Browse the repository at this point in the history
* docs(README.md): 기능 요구 사항 및 구현할 기능 목록 작성

* refactor(ResultView): 상수 유틸 클래스 딜러 이름 사용하도록 리팩토링

* refactor: 컨트롤러에 있는 도메인 로직 BlackJackGame으로 위임

* refactor(Deck): DeckGenreator 기능들 Deck으로 위임

* docs(README.md): 기능목록 업데이트

* test(BettingMoneyTest): 배팅 머니가 0 이하의 금액이면 예외 처리 테스트

* feat(BettingMoney): 0 이하의 금액 예외 처리 기능 구현

* test(DealerTest): 플레이어의 배팅 머니 저장 기능 테스트

* feat(Dealer): 플레이어의 배팅 금액 저장 기능 구현

* test(DealerTest): 수익률 계산 기능 테스트

* feat: 수익률 계산 기능 구현

* test(DealerTest): 딜러 블랙잭 판단 기능 테스트

* feat : 딜러 블랙잭 판단 기능 구현

* feat(InputView): 배팅 금액 입력 기능 구현

* test(InputValiddatorTest): 배팅 금액이 숫자가 아니면 예외 처리 테스트

* feat(InputValidator): 배팅 금액 검증 기능 구현

* feat(OutputView): 배팅 금액 출력 기능 구현

* feat(BlackJackGame): 배팅 금액 저장 & 수익 계산 기능 구현

* feat(ResultView): 최종 수익 출력 기능 구현

* test(TotalSumValueTest): 모든 요소에 수를 더하는 기능 테스트

* feat(TotalSumValues): 모든 요소에 수를 더하는 기능 구현

* test(HandCardsTest): 최적의 합계 구하는 기능 테스트

* refactor: 최적 합계 구하는 기능 HandCards로 위임

* rename: HandCards -> Hand로 네이밍 변경

* refactor: 핸드 카드 반환 기능 리팩토링

* refactor: 참가자 자신이 최적 합계 계산하도록 리팩토링

* test: DeckGeneratorTest DeckTest로 위임

* test(HandTest): 블랙잭 체크 기능 테스트

* feat(Hand): 블랙잭 체크 기능 구현

* test(BettingMoneyByPlayerTest): 플레이어의 배팅 금액을 찾아서 반환 기능 테스트

* feat(BettingMoneyByPlayer): 플레이어의 배팅 금액 찾아서 반환 기능 구현

* test(FinalProfitByParticipantTest): 딜러의 최종 수익 계산 테스트

* feat(FinalProfitByParticipant): 참가자별 최종 수익 관련 기능 구현

* test(FinalProfitTest): 최종 수익 더하는 기능 테스트

* feat(FinalProfit): 최종 수익 더하는 기능 구현

* test(FinalProfitTest): 최종 수익 부호를 바꾸는 기능 테스트

* feat(FinalProfit): 최종 수익 부호를 바꾸는 기능 구현

* test(HandTest): 버스트 체크 기능 테스트

* feat(Hand): 버스트 체크 기능 구현

* test(HandTest): 스테이 체크 기능 테스트

* feat(Hand): 스테이 체크 기능 구현

* feat(BettingManage): 참가자별 배팅 금액, 수익을 관리하는 BettingManager 구현

* docs(README.md): 참가자별 배팅 금액 저장, 참가자별 수익을 계산할 수 있다.

* refactor(FinalProfitByParticipant): 순서 보장하도록 LinkedHashMap으로 변경

* refactor(BettingManager): 수익 계산 시 스테이 분기 처리 추가

* refactor(ResultView): 최종 수익 딜러 따로 출력하도록 리팩토링

* feat: 블랙잭 게임 실행되도록 컨트롤러 실행 로직 조립

* chore: 사용하지 않는 메소드 삭제

* remove: 안 쓰는 Result 관련 객체 삭제

* refactor(FinalProfit): 딜러 최종 수익이 0일 때, -1 곱하지 않도록 리팩토링

* refactor: 딜러가 블랙잭일 시 플레이어 추가 카드 요청 단계 Skip 후 결과 출력하도록 리팩토링

* refactor: BettingMoney 변환 검증 로직 도메인으로 위임

* refactor: ParticipantResultDto를 생성하여 View에서 사용하도록 리팩토링

* refactor(Hand): 카드 합이 21이어도 카드 장 수가 3장 이상이면 Stay가 되도록 리팩토링

* refactor: ParticipantFinalResultDto를 생성하여 View에서 사용하도록 리팩토링

* refactor: FinalProfitDto를 생성하여 View에서 사용하도록 리팩토링

* refactor: 불필요한 메소드 삭제

* refactor: FinalProfitDto를 생성하여 View에서 사용하도록 리팩토링

* refactor(BlackJackController): 게임 단계 분리 리팩토링

* refactor(BettingMoney): getter 네이밍 멤버 변수명과 일치하도록 리팩토링

* refactor(Hand): 코드 구조 개선 리팩토링

* test(PlayerBettingResultTest): 배팅 결과에 따라 최종 수익을 계산하는 기능 테스트

* feat(PlayerBettingResult): 배팅 결과에 따라 최종 수익을 계산하는 기능 구현

* test(PlayerTest): 플레이어와 딜러의 상태를 비교하여 배팅 결과를 반환하는 기능 테스트

* feat(Player): 플레이어와 딜러의 상태를 비교하여 배팅 결과를 반환하는 기능 구현

* refactor(BettingManager): 책임 분리 및 구조 개선 리팩토링
  • Loading branch information
sh111-coder committed Mar 13, 2023
1 parent 16bc60a commit 32502d7
Show file tree
Hide file tree
Showing 50 changed files with 1,168 additions and 747 deletions.
15 changes: 12 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@
- [x] 추가로 카드를 요청할 수 있다.
- [x] 가지고 있는 카드의 합이 21을 넘지 않을 때 가능하다.
- [x] 카드 요청 여부를 입력받을 수 있다.

- [x] 배팅 금액을 입력할 수 있다.
- [x] 0 초과인 숫자만 가능하다.
- [x] 카드 합이 21을 넘으면 배팅 금액을 모두 잃는다.
- [x] 처음 두 장의 카드 합이 21이면 배팅 금액의 1.5배를 얻는다.

- 딜러
- [x] 카드를 요청할 수 있다.
- 처음에 받은 카드의 합이 17 미만일 때 요청한다.
- 소프트 17의 경우에는 요청하지 않는다.
- [x] 플레이어의 배팅 금액을 저장한다.
- [x] 딜러와 플레이어들의 최종 수익을 계산할 수 있다.

-
- [x] 이름을 가지고 있다.
Expand All @@ -43,7 +49,6 @@
- 시작 시점, 딜러와 플레이어에게 각각 두 장의 카드를 지급한다.
- [x] 플레이어와 딜러에게 추가적인 카드 요청을 받을 수 있다.
- [x] 플레이어와 딜러의 결과를 계산할 수 있다.
- [x] 결과를 통해 승패를 판단할 수 있다.
- 승패는 딜러와 플레이어의 비교로만 이루어진다.
- [x] 카드가 의미하는 숫자의 합이 21 또는 21에 가장 가까운 쪽이 이긴다.
- [x] 숫자의 합이 21을 넘어가면 Bust 처리 한다.
Expand All @@ -55,4 +60,8 @@
- 딜러는 두 장 중 첫 번째 카드의 정보만 출력한다.
- [x] 카드 추가 요청을 받은 후 플레이어의 보유 카드 이름을 출력한다.
- [x] 결과 계산 후 딜러와 모든 플레이어의 보유 카드 이름과 계산 결과를 출력한다.
- [x] 최종 승패를 출력한다.
- [x] 딜러와 플레이어들의 최종 수익을 출력할 수 있다.

- 배팅 매니저
- [x] 참가자별 배팅 금액을 저장한다.
- [x] 참가자별 수익을 계산할 수 있다.
110 changes: 82 additions & 28 deletions src/main/java/controller/BlackJackController.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package controller;

import java.util.List;
import java.util.Map;

import domain.AdditionalDrawStatus;
import domain.BlackJackGame;
import domain.betting.BettingManager;
import domain.betting.BettingMoney;
import domain.participant.ParticipantFinalResultDto;
import domain.participant.ParticipantResultDto;
import domain.participant.Player;
import domain.participant.Players;
import util.Constants;
import view.InputView;
import view.OutputView;
Expand All @@ -16,11 +18,15 @@ public class BlackJackController {

public void run() {
BlackJackGame blackJackGame = generateBlackJackGame();
printParticipantInitCardsStep(blackJackGame, blackJackGame.getPlayerNames());
playerDrawCardStep(blackJackGame);
dealerDrawCardStep(blackJackGame);
printParticipantFinalCardsStep(blackJackGame, blackJackGame.getPlayerNames());
printFinalFightResultStep(blackJackGame);
BettingManager bettingManager = new BettingManager();
startGameStep(blackJackGame, bettingManager);
additionalCardDrawStep(blackJackGame);
printResultStep(blackJackGame, bettingManager);
}

private void startGameStep(BlackJackGame blackJackGame, BettingManager bettingManager) {
saveBettingMoney(blackJackGame, bettingManager);
initDistributeCards(blackJackGame);
}

private BlackJackGame generateBlackJackGame() {
Expand All @@ -39,50 +45,98 @@ private List<String> initPlayerNames() {
}
}

private void printParticipantInitCardsStep(BlackJackGame blackJackGame, List<String> playerNames) {
private void saveBettingMoney(BlackJackGame blackJackGame, BettingManager bettingManager) {
List<String> playerNames = blackJackGame.getPlayerNames();
for (String playerName : playerNames) {
BettingMoney bettingMoney = generateBettingMoney(playerName);
Player findPlayer = blackJackGame.findPlayerByPlayerName(playerName);
bettingManager.savePlayerBettingMoney(findPlayer, bettingMoney);
}
}

private void initDistributeCards(BlackJackGame blackJackGame) {
List<String> playerNames = blackJackGame.getPlayerNames();
ResultView.printInitMessage(playerNames);
ResultView.printParticipantResult(Constants.DEALER_NAME, blackJackGame.findCardNamesByParticipantName(Constants.DEALER_NAME));
ParticipantResultDto dealerResultDto = blackJackGame.generateParticipantResultByParticipantName(Constants.DEALER_NAME);
ResultView.printParticipantResult(dealerResultDto);
for (String playerName : playerNames) {
List<String> findCardNames = blackJackGame.findCardNamesByParticipantName(playerName);
ResultView.printParticipantResult(playerName, findCardNames);
ParticipantResultDto playerResultDto = blackJackGame.generateParticipantResultByParticipantName(playerName);
ResultView.printParticipantResult(playerResultDto);
}
}

private void additionalCardDrawStep(BlackJackGame blackJackGame) {
if (!isDealerBlackjack(blackJackGame)) {
additionalCardDraw(blackJackGame);
}
}

private boolean isDealerBlackjack(BlackJackGame blackJackGame) {
return blackJackGame.isDealerBlackjack();
}

private void additionalCardDraw(BlackJackGame blackJackGame) {
askPlayerAdditionalCardDrawStep(blackJackGame);
dealerAdditionalCardDrawStep(blackJackGame);
}

private BettingMoney generateBettingMoney(String playerName) {
try {
OutputView.printInputBettingMoney(playerName);
String bettingMoney = InputView.inputBettingMoney();
return new BettingMoney(bettingMoney);
} catch (IllegalArgumentException e) {
OutputView.printMessage(e.getMessage());
return generateBettingMoney(playerName);
}
}

public void playerDrawCardStep(BlackJackGame blackJackGame) {
Players players = blackJackGame.getPlayers();
for (Player player : players.getPlayers()) {
drawCardOrPass(blackJackGame, player);
private void askPlayerAdditionalCardDrawStep(BlackJackGame blackJackGame) {
List<String> playerNames = blackJackGame.getPlayerNames();
for (String playerName : playerNames) {
askPlayerAdditionalCardDraw(blackJackGame, playerName);
}
}

private void drawCardOrPass(BlackJackGame blackJackGame, Player player) {
private void askPlayerAdditionalCardDraw(BlackJackGame blackJackGame, String playerName) {
AdditionalDrawStatus additionalDrawStatus = AdditionalDrawStatus.DRAW;
while (AdditionalDrawStatus.isDrawable(additionalDrawStatus) && blackJackGame.canPlayerDrawCard(player)) {
String playerName = player.getName();
while (AdditionalDrawStatus.isDrawable(additionalDrawStatus) && blackJackGame.canPlayerDrawCard(playerName)) {
OutputView.printInputReceiveYesOrNotMessage(playerName);
String receiveOrNot = InputView.inputReceiveOrNot();
additionalDrawStatus = blackJackGame.distributePlayerCardOrPass(player, receiveOrNot);
ResultView.printParticipantResult(playerName, blackJackGame.findCardNamesByParticipantName(playerName));
additionalDrawStatus = blackJackGame.distributePlayerCardOrPass(playerName, receiveOrNot);
ParticipantResultDto playerResultDto = blackJackGame.generateParticipantResultByParticipantName(playerName);
ResultView.printParticipantResult(playerResultDto);
}
}

private void dealerDrawCardStep(BlackJackGame blackJackGame) {
private void dealerAdditionalCardDrawStep(BlackJackGame blackJackGame) {
while (blackJackGame.canDealerDrawCard()) {
OutputView.printDealerReceivedMessage();
blackJackGame.distributeDealerCard();
}
}

private void printParticipantFinalCardsStep(BlackJackGame blackJackGame, List<String> playerNames) {
ResultView.printParticipantFinalResult(Constants.DEALER_NAME, blackJackGame.findCardNamesByParticipantName(Constants.DEALER_NAME), blackJackGame.getDealerCardValueSum());
private void printResultStep(BlackJackGame blackJackGame, BettingManager bettingManager) {
printFinalCardResult(blackJackGame);
calculateFinalProfit(blackJackGame, bettingManager);
printFinalProfit(bettingManager);
}

private void printFinalCardResult(BlackJackGame blackJackGame) {
ParticipantFinalResultDto dealerFinalResultDto = blackJackGame.generateParticipantFinalResultByParticipantName(Constants.DEALER_NAME);
ResultView.printParticipantFinalResult(dealerFinalResultDto);
List<String> playerNames = blackJackGame.getPlayerNames();
for (String playerName : playerNames) {
List<String> findCardNames = blackJackGame.findCardNamesByParticipantName(playerName);
ResultView.printParticipantFinalResult(playerName, findCardNames, blackJackGame.findPlayerCardValueSumByPlayerName(playerName));
ParticipantFinalResultDto playerFinalResultDto = blackJackGame.generateParticipantFinalResultByParticipantName(playerName);
ResultView.printParticipantFinalResult(playerFinalResultDto);
}
}

private void printFinalFightResultStep(BlackJackGame blackJackGame) {
Map<String, String> resultByPlayerName = blackJackGame.calculateResult();
ResultView.printFinalFightResult(resultByPlayerName);
private void calculateFinalProfit(BlackJackGame blackJackGame, BettingManager bettingManager) {
bettingManager.calculateParticipantFinalProfit(blackJackGame.getRawPlayers(), blackJackGame.getDealer());
}

private void printFinalProfit(BettingManager bettingManager) {
ResultView.printFinalProfit(bettingManager.generateFinalProfitDto());
}
}
61 changes: 34 additions & 27 deletions src/main/java/domain/BlackJackGame.java
Original file line number Diff line number Diff line change
@@ -1,44 +1,62 @@
package domain;

import java.util.List;
import java.util.Map;

import domain.betting.BettingMoney;
import domain.card.Deck;
import domain.participant.*;
import domain.result.ResultCalculator;

public class BlackJackGame {

private static final String ADDITIONAL_DRAW_CARD_OK_SIGN = "y";

private final Deck deck;
private final Participants participants;

public BlackJackGame(List<String> playerNames) {
this.deck = InitGameSetter.generateDeck();
this.deck = new Deck();
Players players = InitGameSetter.generatePlayers(deck, playerNames);
Dealer dealer = InitGameSetter.generateDealer(deck);
this.participants = new Participants(players, dealer);
}

public boolean isDealerBlackjack() {
Dealer dealer = participants.getDealer();
return dealer.isBlackjack();
}

public List<String> getPlayerNames() {
return participants.getPlayerNames();
}

public List<String> findCardNamesByParticipantName(String participantName) {
return participants.findCardNamesByParticipantName(participantName);
public Player findPlayerByPlayerName(String playerName) {
return participants.findPlayerByPlayerName(playerName);
}

public Players getPlayers() {
return participants.getPlayers();
public ParticipantResultDto generateParticipantResultByParticipantName(String participantName) {
List<String> findCardNames = participants.findCardNamesByParticipantName(participantName);
return new ParticipantResultDto(participantName, findCardNames);
}

public boolean canPlayerDrawCard(Player player) {
return player.checkCardsCondition();
public ParticipantFinalResultDto generateParticipantFinalResultByParticipantName(String participantName) {
ParticipantResultDto participantResultDto = generateParticipantResultByParticipantName(participantName);

Participant findParticipant = participants.findParticipantByParticipantName(participantName);
int totalValueSum = findParticipant.calculateOptimalCardValueSum();

return new ParticipantFinalResultDto(participantResultDto, totalValueSum);
}

public AdditionalDrawStatus distributePlayerCardOrPass(Player player, String receiveOrNot) {
public boolean canPlayerDrawCard(String playerName) {
Player findPlayer = participants.findPlayerByPlayerName(playerName);
return findPlayer.canHit();
}

public AdditionalDrawStatus distributePlayerCardOrPass(String playerName, String receiveOrNot) {
AdditionalDrawStatus additionalDrawStatus = AdditionalDrawStatus.PASS;
if (receiveOrNot.equals("y")) {
player.takeCard(deck.drawCard());
Player findPlayer = participants.findPlayerByPlayerName(playerName);
if (receiveOrNot.equals(ADDITIONAL_DRAW_CARD_OK_SIGN)) {
findPlayer.takeCard(deck.drawCard());
additionalDrawStatus = AdditionalDrawStatus.DRAW;
}
return additionalDrawStatus;
Expand All @@ -53,22 +71,11 @@ public boolean canDealerDrawCard() {
return participants.canDealerDrawCard();
}

public int getDealerCardValueSum() {
return participants.getDealerCardValueSum();
public Dealer getDealer() {
return participants.getDealer();
}

public int findPlayerCardValueSumByPlayerName(String playerName) {
Player findPlayer = participants.findPlayerByPlayerName(playerName);
return findPlayer.getOptimalCardValueSum();
}

public Map<String, String> calculateResult() {
Players players = participants.getPlayers();
Dealer dealer = participants.getDealer();
ResultCalculator resultCalculator = new ResultCalculator(dealer, players);
for (Player player : players.getPlayers()) {
resultCalculator.calculate(player, dealer);
}
return resultCalculator.getFinalFightResults();
public List<Player> getRawPlayers() {
return participants.getRawPlayers();
}
}
10 changes: 2 additions & 8 deletions src/main/java/domain/InitGameSetter.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,24 @@

import domain.card.Card;
import domain.card.Deck;
import domain.card.DeckGenerator;
import domain.participant.*;

public class InitGameSetter {

public static Deck generateDeck() {
DeckGenerator deckGenerator = new DeckGenerator();
return deckGenerator.generate();
}

public static Players generatePlayers(Deck deck, List<String> playerNames) {
List<Player> generatedPlayers = new ArrayList<>();
for (String playerName : playerNames) {
List<Card> initCards = new ArrayList<>();
drawTwoCard(deck, initCards);
generatedPlayers.add(new Player(new Name(playerName), new HandCards(initCards)));
generatedPlayers.add(new Player(new Name(playerName), new Hand(initCards)));
}
return new Players(generatedPlayers);
}

public static Dealer generateDealer(Deck deck) {
List<Card> initCards = new ArrayList<>();
drawTwoCard(deck, initCards);
return new Dealer(new HandCards(initCards));
return new Dealer(new Hand(initCards));
}

private static void drawTwoCard(Deck deck, List<Card> initCards) {
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/domain/betting/BettingManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package domain.betting;

import java.util.List;

import domain.participant.Dealer;
import domain.participant.Hand;
import domain.participant.Player;
import domain.profit.FinalProfit;
import domain.profit.FinalProfitByParticipant;
import domain.profit.FinalProfitDto;

public class BettingManager {

private final BettingMoneyByPlayer bettingMoneyByPlayer;
private final FinalProfitByParticipant finalProfitByParticipant;

public BettingManager() {
this.bettingMoneyByPlayer = new BettingMoneyByPlayer();
this.finalProfitByParticipant = new FinalProfitByParticipant();
}

public void savePlayerBettingMoney(Player player, BettingMoney bettingMoney) {
bettingMoneyByPlayer.putPlayerBettingMoney(player, bettingMoney);
}

public void calculateParticipantFinalProfit(List<Player> players, Dealer dealer) {
for (Player player : players) {
calculatePlayerFinalProfit(player, dealer);
}
}

private void calculatePlayerFinalProfit(Player player, Dealer dealer) {
Hand dealerHand = dealer.getHand();
BettingMoney bettingMoney = bettingMoneyByPlayer.findBettingMoneyByPlayer(player);
PlayerBettingResult playerBettingResult = player.calculateBettingResult(dealerHand);
saveProfit(player, bettingMoney, playerBettingResult);
}

private void saveProfit(Player player, BettingMoney bettingMoney, PlayerBettingResult playerBettingResult) {
finalProfitByParticipant.putParticipantFinalProfit(player, new FinalProfit(playerBettingResult.calculateFinalProfit(bettingMoney.getMoney())));
}

public FinalProfitDto generateFinalProfitDto() {
return new FinalProfitDto(
finalProfitByParticipant.calculateDealerFinalProfit(),
finalProfitByParticipant.getFinalProfitByParticipant());
}
}
Loading

0 comments on commit 32502d7

Please sign in to comment.