-
Notifications
You must be signed in to change notification settings - Fork 0
[2단계 - 블랙잭 베팅] 망쵸(김주환) 미션 제출합니다. #16
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
base: main
Are you sure you want to change the base?
Changes from all commits
527b4a7
f8cbf8c
33f0fd0
03f3088
b7a91f8
7bb0388
d945c06
32bbfcf
d499faa
edbc94e
5a621b6
7efa945
62a151d
a733953
3b49118
56b15a0
100a87d
5f0ba0d
aeb4769
54cd23c
38e2509
4cf5604
51f13fe
c7defd3
5d120a9
48e3afc
bf37154
565494d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,84 @@ | ||
| # java-blackjack | ||
| # 블랙잭 | ||
|
|
||
| 블랙잭 미션 저장소 | ||
|
|
||
| ## 우아한테크코스 코드리뷰 | ||
| ## 기능 요구 사항 | ||
|
|
||
| - [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) | ||
| ### 플레이어 이름 | ||
|
|
||
| ### 플레이어 | ||
|
|
||
| - [X] 플레이어는 이름을 갖는다 | ||
| - [X] 플레이어는 카드들을 갖는다 | ||
| - [X] 플레이어는 카드를 저장한다 | ||
| - [X] 죽었는지 안 죽었는지 여부를 반환한다 (21을 초과하는지) | ||
|
|
||
|
|
||
| ### 카드 | ||
|
|
||
| - [X] 문자와 모양을 상태로 갖는다 | ||
|
|
||
|
|
||
| ### Hand(카드들) | ||
|
|
||
| - [X] 여러개의 카드를 갖는다 | ||
| - [X] 카드의 총합 계산한다 | ||
| - [X] 카드 추가한다 | ||
|
|
||
|
|
||
| ### 카드 게임 | ||
|
|
||
| - [X] 각 플레이어에게 카드를 2장씩 지급한다 | ||
| - [X] 플레이어마다 추가 지급한다 | ||
|
|
||
|
|
||
| ### 덱 | ||
|
|
||
| - [X] 모든 카드를 1장씩 갖고 있다. | ||
| - [X] 랜덤으로 카드를 뽑는다. | ||
|
|
||
|
|
||
| ### 게임 승패 결정 | ||
|
|
||
| - [X] 딜러와 모든 플레이어의 승패 여부를 결정한다. | ||
| - [X] 딜러와 플레이어 둘다 21을 초과할 경우, 플레이어가 패배한다. | ||
| - [X] 카드 합계가 딜러는 21 이하, 플레이어는 21 초과인 경우, 플레이어가 패배한다. | ||
| - [X] 카드 합계가 딜러는 21 초과, 플레이어는 21 이하인 경우, 플레이어가 승리한다. | ||
| - [X] 카드 합계가 딜러와 플레이어 모두 21 이하인 경우, 숫자가 큰 사람이 승리한다. | ||
| - [X] 카드 합계가 딜러와 플레이어 모두 21 이하이고 동일한 경우, 무승부다. | ||
|
|
||
|
|
||
| ### 인풋 뷰 | ||
|
|
||
| - [X] 참가자의 이름을 입력받는다. | ||
| - [X] 이름은 쉼표 기준으로 분리한다. | ||
| - [X] 카드 추가 여부를 입력받는다. | ||
| - [X] y 또는 n이 아닌 경우, 예외를 발생한다. | ||
| - [X] 사용자 카드 합이 21을 초과하면, 카드 추가 여부를 묻지 않는다. | ||
| - [X] 플레이어가 n을 입력할 때까지 카드 추가 여부를 묻는다. | ||
|
|
||
|
|
||
| ### 아웃풋 뷰 | ||
|
|
||
| - [X] 딜러와 플레이어 전원에게 초기에 분배한 카드 출력한다. | ||
| - [X] 딜러의 카드는 1장만 공개한다. | ||
| - [X] 플레이어가 보유한 모든 카드를 출력한다. | ||
| - [X] 딜러가 추가 카드를 발급 받았는지 여부 출력한다. | ||
| - [X] 보유한 모든 카드의 합을 출력한다. | ||
| - [X] 최종 승패를 출력한다. | ||
|
|
||
|
|
||
| ## 베팅 | ||
|
|
||
| ### 카지노 | ||
|
|
||
| - [x] 은행과 유사한 역할로 플레이어의 계좌를 관리한다. | ||
| - [x] 계좌는 사용자 이름으로 맵핑한다. | ||
| - [x] 승패 결과에 따라 송금을 진행한다. | ||
| - [x] 플레이어의 잔고를 조회한다. | ||
|
|
||
|
|
||
| ### 잔고 | ||
|
|
||
| - [x] 사용자의 돈을 저장한다. | ||
| - [x] 입금할 수 있다. | ||
| - [x] 출금할 수 있다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package blackjack; | ||
|
|
||
| import blackjack.view.InputView; | ||
| import blackjack.view.MessageResolver; | ||
| import blackjack.view.OutputView; | ||
|
|
||
| public class BlackjackApplication { | ||
|
|
||
| public static void main(String[] args) { | ||
| final InputView inputView = new InputView(); | ||
| final OutputView outputView = new OutputView(new MessageResolver()); | ||
|
|
||
| final BlackjackController blackjackController = new BlackjackController(inputView, outputView); | ||
| blackjackController.run(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| package blackjack; | ||
|
|
||
| import blackjack.domain.betting.BetDetails; | ||
| import blackjack.domain.betting.ProfitDetails; | ||
| import blackjack.domain.cardgame.CardDeck; | ||
| import blackjack.domain.cardgame.CardGameResult; | ||
| import blackjack.domain.player.Dealer; | ||
| import blackjack.domain.player.Name; | ||
| import blackjack.domain.player.Participant; | ||
| import blackjack.domain.player.Player; | ||
| import blackjack.view.InputView; | ||
| import blackjack.view.OutputView; | ||
|
|
||
| import java.util.LinkedHashMap; | ||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| public class BlackjackController { | ||
| private final InputView inputView; | ||
| private final OutputView outputView; | ||
|
|
||
| BlackjackController(final InputView inputView, final OutputView outputView) { | ||
| this.inputView = inputView; | ||
| this.outputView = outputView; | ||
| } | ||
|
|
||
| void run() { | ||
| final List<Name> names = inputView.askPlayerNames(); | ||
| final Dealer dealer = new Dealer(); | ||
| final List<Player> players = names.stream().map(Player::new).toList(); | ||
| final CardDeck deck = CardDeck.createShuffledDeck(); | ||
|
|
||
| final BetDetails playersBetDetails = getPlayersBettingMoney(names); | ||
| initializeHand(deck, dealer, players); | ||
| outputView.printInitialHandOfEachPlayer(dealer, players); | ||
|
|
||
| for (final Player player : players) { | ||
| givePlayerMoreCardsIfWanted(deck, player); | ||
| } | ||
| giveDealerMoreCardsIfNeeded(deck, dealer); | ||
|
|
||
| printHandStatusOfEachPlayer(dealer, players); | ||
| final CardGameResult result = CardGameResult.of(dealer, players); | ||
| ProfitDetails profits = playersBetDetails.calculateProfit(result); | ||
| printProfit(profits); | ||
| } | ||
|
Comment on lines
+27
to
+46
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 프로그램의 진입점으로 생성과 로직 실행의 책임을 갖고 있는 메서드니까 괜찮겠지 라는 생각으로 해당 메서드는 요구사항을 벗어나게 작성했어요! 하지만 확실히 흐름이 명확하게 보이지 않네요. 요구사항이 괜히 있는 게 아니라는 생각과 함께 수정했어요! |
||
|
|
||
| private BetDetails getPlayersBettingMoney(final List<Name> playerNames) { | ||
| return playerNames.stream() | ||
| .collect( | ||
| Collectors.collectingAndThen( | ||
| Collectors.toMap( | ||
| name -> name, | ||
| inputView::askBettingMoney, | ||
| (x, y) -> x, | ||
| LinkedHashMap::new | ||
| ), BetDetails::new)); | ||
| } | ||
|
|
||
| private void initializeHand(final CardDeck deck, final Dealer dealer, final List<Player> players) { | ||
| deck.giveInitialCards(dealer); | ||
| for (final Participant player : players) { | ||
| deck.giveInitialCards(player); | ||
| } | ||
| } | ||
|
|
||
| private void givePlayerMoreCardsIfWanted(final CardDeck deck, final Player player) { | ||
| while (player.isAlive() && inputView.askForMoreCard(player.name())) { | ||
| deck.giveCard(player); | ||
| outputView.printPlayerCard(player); | ||
| } | ||
| } | ||
|
|
||
| private void giveDealerMoreCardsIfNeeded(final CardDeck deck, final Dealer dealer) { | ||
| while (dealer.isMoreCardNeeded()) { | ||
| deck.giveCard(dealer); | ||
| outputView.printDealerHitMessage(dealer); | ||
| } | ||
| } | ||
|
|
||
| private void printHandStatusOfEachPlayer(final Dealer dealer, final List<Player> players) { | ||
| outputView.printParticipantCardWithScore(dealer); | ||
| for (final Player player : players) { | ||
| outputView.printParticipantCardWithScore(player); | ||
| } | ||
| } | ||
|
|
||
| private void printProfit(final ProfitDetails profits) { | ||
| outputView.printPlayerProfit(profits); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package blackjack.domain.betting; | ||
|
|
||
| import blackjack.domain.cardgame.CardGameResult; | ||
| import blackjack.domain.cardgame.WinningStatus; | ||
| import blackjack.domain.player.Name; | ||
|
|
||
| import java.util.LinkedHashMap; | ||
| import java.util.Map; | ||
|
|
||
| public class BetDetails { | ||
| private final Map<Name, Money> playersBettingMoney; | ||
|
|
||
| public BetDetails(final Map<Name, Money> playersBettingMoney) { | ||
| this.playersBettingMoney = playersBettingMoney; | ||
| } | ||
|
|
||
| public ProfitDetails calculateProfit(final CardGameResult result) { | ||
| final Map<Name, Profit> profitDetails = new LinkedHashMap<>(); | ||
|
|
||
| for (final var nameAndStatus : result.totalResult().entrySet()) { | ||
| final Name name = extractName(nameAndStatus); | ||
| final WinningStatus status = extractStatus(nameAndStatus); | ||
| final Profit profit = Profit.of(findBettingMoney(name), status); | ||
| profitDetails.put(name, profit); | ||
| } | ||
|
|
||
| return new ProfitDetails(profitDetails); | ||
| } | ||
|
Comment on lines
+17
to
+28
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1단계 미션에서 구현한 각 플레이어의 승패무 통계 관련 코드를 재사용 하기 위해 지금과 같은 구조로 수익을 계산하는 건가요? 기존 코드를 삭제하고 컴팩트하게 구현했다면 조금 더 이해하기 쉬운 코드가 될 수 있었을 것 같아요.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 저는 오히려 잘 이용했다고 생각했는데 역시 사람마다 생각이 다르군요
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 로빈~ 정확하시네요 👍 |
||
|
|
||
| private Name extractName(final Map.Entry<Name, WinningStatus> nameAndStatus) { | ||
| return nameAndStatus.getKey(); | ||
| } | ||
|
|
||
| private WinningStatus extractStatus(final Map.Entry<Name, WinningStatus> nameAndStatus) { | ||
| return nameAndStatus.getValue(); | ||
| } | ||
|
|
||
| private Money findBettingMoney(final Name name) { | ||
| return playersBettingMoney.get(name); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package blackjack.domain.betting; | ||
|
|
||
| public record Money(int value) { | ||
|
|
||
| public Money { | ||
| validateMinimum(value); | ||
| } | ||
|
|
||
| private void validateMinimum(final int value) { | ||
| if (value < 0) { | ||
| throw new IllegalArgumentException("돈의 액수는 0 이상이어야 합니다."); | ||
| } | ||
| } | ||
|
|
||
| int minusValue() { | ||
| return -value; | ||
| } | ||
|
Comment on lines
+15
to
+17
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 메서드 이름이 조금 부적절한 것 같습니다. minus는 뺀다는 의미니까요
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋은 지적 감사해요 👍 |
||
|
|
||
| int multipleValue(final double rate) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 times를 사용했어요
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| return (int) (value * rate); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package blackjack.domain.betting; | ||
|
|
||
| import blackjack.domain.cardgame.WinningStatus; | ||
|
|
||
| import static blackjack.domain.cardgame.WinningStatus.BLACKJACK; | ||
| import static blackjack.domain.cardgame.WinningStatus.LOSE; | ||
| import static blackjack.domain.cardgame.WinningStatus.PUSH; | ||
| import static blackjack.domain.cardgame.WinningStatus.WIN; | ||
|
|
||
| public record Profit(int value) { | ||
| private static final double BLACKJACK_PROFIT_RATE = 1.5; | ||
|
|
||
| public static Profit of(final Money money, final WinningStatus status) { | ||
| if (WIN.equals(status)) { | ||
| return new Profit(money.value()); | ||
| } | ||
| if (LOSE.equals(status)) { | ||
| return new Profit(money.minusValue()); | ||
| } | ||
| if (PUSH.equals(status)) { | ||
| return new Profit(0); | ||
| } | ||
| if (BLACKJACK.equals(status)) { | ||
| return new Profit(money.multipleValue(BLACKJACK_PROFIT_RATE)); | ||
| } | ||
|
Comment on lines
+14
to
+25
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WinningStatus를 잘 이용한다면, if문을 제거할 수 있을 것 같습니다! |
||
|
|
||
| throw new IllegalStateException(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package blackjack.domain.betting; | ||
|
|
||
| import blackjack.domain.player.Name; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.Map; | ||
|
|
||
| public class ProfitDetails { | ||
| private final Map<Name, Profit> profits; | ||
|
|
||
| public ProfitDetails(final Map<Name, Profit> profits) { | ||
| this.profits = profits; | ||
| } | ||
|
|
||
| public Profit getDealerProfit() { | ||
| final int playersProfitSum = profits.values() | ||
| .stream() | ||
| .mapToInt(Profit::value) | ||
| .sum(); | ||
| return new Profit(-playersProfitSum); | ||
| } | ||
|
|
||
| public Map<Name, Profit> details() { | ||
| return Collections.unmodifiableMap(profits); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package blackjack.domain.card; | ||
|
|
||
| public class Card { | ||
| private final CardNumber cardNumber; | ||
| private final CardShape cardShape; | ||
|
|
||
| public Card(final CardNumber cardNumber, final CardShape cardShape) { | ||
| this.cardNumber = cardNumber; | ||
| this.cardShape = cardShape; | ||
| } | ||
|
|
||
| public boolean isAceCard() { | ||
| return cardNumber == CardNumber.ACE; | ||
| } | ||
|
|
||
| public int getScore() { | ||
| return cardNumber.getValue(); | ||
| } | ||
|
|
||
| public CardNumber getNumber() { | ||
| return cardNumber; | ||
| } | ||
|
|
||
| public CardShape getShape() { | ||
| return cardShape; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package blackjack.domain.card; | ||
|
|
||
| public enum CardNumber { | ||
| ACE(1), | ||
| TWO(2), | ||
| THREE(3), | ||
| FOUR(4), | ||
| FIVE(5), | ||
| SIX(6), | ||
| SEVEN(7), | ||
| EIGHT(8), | ||
| NINE(9), | ||
| TEN(10), | ||
| JACK(10), | ||
| QUEEN(10), | ||
| KING(10); | ||
|
|
||
| private final int value; | ||
|
|
||
| CardNumber(final int value) { | ||
| this.value = value; | ||
| } | ||
|
|
||
| int getValue() { | ||
| return value; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package blackjack.domain.card; | ||
|
|
||
| public enum CardShape { | ||
| SPADE, | ||
| HEART, | ||
| DIAMOND, | ||
| CLOVER | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Controller란, 어떤 요청이 들어왔을 때 그 요청을 처리할 수 있는 model 에게 그 요청의 처리를 위임하고, 처리 결과를 View에 전달하는 것을 말합니다. 하지만, 우리 미션에서는 요청을 표현하는 것이 없습니다. 그저 프로그램을 실행하면 알아서 동작을 하기 때문에 Controller라는 이름은 부적절하다고 생각합니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로빈의 controller 정의를 통해 새로운 인사이트를 얻습니다 👍
현재 미션에서 적용하는 MVC 패턴에서 Controller의 역할은 Model과 View를 이어주는 다리 역할이라고 생각해요!
그래도 Controller 라는 네이밍이 적절하지 않을 수도 있겠네요~
BlackjackGame같은 네이밍을 한다면 로빈 말대로 알아서 동작하는 프로그램과 같은 의미를 담을 수 있겠어요 ✊