-
Notifications
You must be signed in to change notification settings - Fork 0
[1단계 - 블랙잭 게임 실행] 리비(이근희) 미션 제출합니다. #7
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 90 commits
60a21e8
2f32bca
49be956
768674f
6a9b3a8
e5ccb9c
2f8d112
fa90a65
888c475
b28e233
2978839
c100676
3e432f8
9f6b009
d4e3b02
3628610
ccacf3d
43b3f9f
19e1ebb
96f1d18
d324ce2
d13d6ed
12d5f01
e26f327
e368348
0e340ad
b706148
0776cfd
0f130c8
94c37a7
5b1551f
65f58a3
04003e5
45002da
96f58be
f38f988
9819609
7b36a34
2336438
89a7c36
2f8a47b
32cc01a
287ab8c
5579914
022b04a
02706a3
e9620e6
e7dec0c
101e550
83be362
3e42598
9cdd4e4
4127be1
f45b82e
59f6a93
b0ecfde
e5eb87d
b8e2c04
3fbdbcc
7e748c6
db772d4
7ebe119
087b133
e68b09a
fcf9aa7
a1b8d4b
16de5b7
eaa623f
1b9d5ba
02ee533
663bc28
38e466f
f86a043
f7593b5
563bedf
a8d1b14
60d08c4
3589843
10e20b8
5728ff5
867fcb4
a6b3eb0
b6a027a
530c89c
4abbc51
ee6ca01
86de5c1
2fd301b
a794e12
b27538d
7360053
2318ce5
40f1c66
1379611
d87d04f
1d4170b
0eb36e8
bbaca86
03c7006
cd39577
2d1e568
2b52bee
8c1c3c0
af26e10
b4e10f8
9e56e1d
35dfddd
a8a4908
97d070b
ae0fb23
a33e959
1264068
9bc37a8
d2c0c42
ce24ab4
a83dc83
2a32365
0472a19
71a4e81
b3f67a8
d5c52e8
d756ac4
1464f13
6bfeee6
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,41 @@ | ||
| # java-blackjack | ||
| # ♠️ java-blackjack ♠️ | ||
|
|
||
| 블랙잭 미션 저장소 | ||
| - 블랙잭 게임을 변형한 프로그램을 구현한다. | ||
| - 블랙잭 게임은 딜러와 플레이어 중 카드의 합이 21 또는 21에 가장 가까운 숫자를 가지는 쪽이 이기는 게임이다. | ||
| - 카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다. | ||
| - 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. | ||
| - 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. | ||
| - 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다. | ||
| - 게임을 완료한 후 각 플레이어별로 승패를 출력한다. | ||
|
|
||
| ## 우아한테크코스 코드리뷰 | ||
| # 🛠️ 기능 구현 목록 | ||
|
|
||
| - [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) | ||
| - [x] 입력 | ||
| - [x] 게임에 참여할 사람의 이름을 입력 받을 수 있다. | ||
| - [x] 한 장 더 받을지 여부를 입력 받을 수 있다. | ||
| - [x] 입력 검증 | ||
| - [x] 카드 추가 여부를 올바른 형태 (y/n)으로 입력했는지 검증할 수 있다. | ||
| - [x] 도메인 | ||
| - [x] 이름은 빈 문자열일 수 없다. | ||
| - [x] 게임 참가자의 핸드에 새로운 카드를 추가할 수 있다. | ||
| - [x] 이름이 중복되는 플레이어는 존재할 수 없다. | ||
| - [x] 플레이어가 없는 경우는 게임을 시작할 수 없다. | ||
| - [x] 게임 참가자는 딜러 제외 3명 이하여야 한다. | ||
| - [x] 카드합 비교를 통해서 플레이어의 승패를 결정할 수 있다. | ||
| - [x] 카드합 비교를 통해서 딜러의 승패를 계산할 수 있다. | ||
| - [x] 딜러는 17점 미만이면 카드를 받아야 한다. | ||
| - [x] 딜러는 17점 이상이면 카드를 그만 받아야 한다. | ||
| - [x] 핸드에서 에이스가 몇개있는지 파악할 수 있다 | ||
| - [x] 핸드의 합을 계산할 수 있다. | ||
| - [x] 저지는 핸드에서 21에 가장 가까운 합을 구할 수 있다. | ||
| - [x] 핸드의 최소합이 21을 초과하면 플레이어는 버스트한다. | ||
| - [x] 핸드의 최소합이 21 이하면 플레이어는 카드를 뽑을지 여부를 선택할 수 있다. | ||
| - [x] 핸드에 카드를 추가할 수 있다. | ||
| - [x] 카드덱에서 카드를 지정한 개수만큼 건네줄 수 있다. | ||
| - [x] 카드덱에서 카드를 한 장 뽑아서 건네줄 수 있다. | ||
| - [x] 카드덱에서 보유한 카드 개수보다 많이 뽑으면 예외가 발생한다. | ||
| - [x] 참여자의 핸드에 초기 카드를 분배할 수 있다. | ||
| - [x] 출력 | ||
| - [x] 각 참여자의 카드 정보를 출력할 수 있다. | ||
| - [x] 각 참여자의 카드 합을 출력할 수 있다. | ||
| - [x] 최종 승패를 출력할 수 있다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package blackjack; | ||
|
|
||
| import blackjack.controller.BlackJackController; | ||
| import blackjack.view.InputView; | ||
| import blackjack.view.MessageResolver; | ||
| import blackjack.view.OutputView; | ||
|
|
||
| public class Application { | ||
|
|
||
| public static void main(String[] args) { | ||
|
|
||
| InputView inputView = new InputView(); | ||
| OutputView outputView = new OutputView(new MessageResolver()); | ||
|
|
||
| BlackJackController blackJackController = new BlackJackController(inputView, outputView); | ||
| blackJackController.run(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| package blackjack.controller; | ||
|
|
||
| import static blackjack.domain.DrawDecision.YES; | ||
|
|
||
| import blackjack.domain.DealerGameResult; | ||
| import blackjack.domain.DrawDecision; | ||
| import blackjack.domain.card.Card; | ||
| import blackjack.domain.card.CardDeck; | ||
| import blackjack.domain.card.CardDeckCreator; | ||
| import blackjack.domain.player.HandCreator; | ||
| import blackjack.domain.player.Player; | ||
| import blackjack.domain.player.PlayerCreator; | ||
| import blackjack.domain.player.PlayerName; | ||
| import blackjack.domain.player.Players; | ||
| import blackjack.domain.rule.Judge; | ||
| import blackjack.domain.rule.Score; | ||
| import blackjack.domain.rule.ScoreCalculateStrategy; | ||
| import blackjack.view.InputView; | ||
| import blackjack.view.OutputView; | ||
| import java.util.List; | ||
|
|
||
| public class BlackJackController { | ||
|
|
||
| private final InputView inputView; | ||
| private final OutputView outputView; | ||
|
|
||
| public BlackJackController(InputView inputView, OutputView outputView) { | ||
| this.inputView = inputView; | ||
| this.outputView = outputView; | ||
| } | ||
|
|
||
| public void run() { | ||
| CardDeck cardDeck = initCardDeck(); | ||
| Players players = initPlayers(cardDeck); | ||
| Player dealer = initDealer(cardDeck); | ||
| printPlayersInformation(players, dealer); | ||
|
|
||
| completePlayersHand(players, cardDeck); | ||
| completeDealerHand(dealer, cardDeck); | ||
|
|
||
| printDealerPopCount(dealer); | ||
|
|
||
| printPlayerScore(dealer); | ||
| printPlayersScore(players); | ||
| printDealerGameResult(dealer, players); | ||
| printPlayersGameResult(players, dealer); | ||
| } | ||
|
|
||
| private CardDeck initCardDeck() { | ||
| CardDeckCreator cardDeckCreator = new CardDeckCreator(); | ||
| return cardDeckCreator.create(); | ||
| } | ||
|
|
||
| private Players initPlayers(CardDeck cardDeck) { | ||
| InputMapper inputMapper = new InputMapper(); | ||
|
||
| PlayerCreator playerCreator = new PlayerCreator(new HandCreator()); | ||
| List<PlayerName> playerNames = inputMapper.mapToPlayers(inputView.readNames()); | ||
|
|
||
| return new Players(playerNames.stream() | ||
| .map(playerName -> playerCreator.createPlayerFrom(playerName, cardDeck)) | ||
| .toList()); | ||
| } | ||
|
|
||
| private Player initDealer(CardDeck cardDeck) { | ||
| PlayerCreator playerCreator = new PlayerCreator(new HandCreator()); | ||
| return playerCreator.createDealerFrom(cardDeck); | ||
| } | ||
|
|
||
| private void printPlayersInformation(Players players, Player dealer) { | ||
| outputView.printHandOutEvent(players, 2); | ||
| outputView.printDealerInitialHand(dealer); | ||
| players.getPlayers().forEach(outputView::printPlayerHand); | ||
| } | ||
|
|
||
| private void completePlayersHand(Players players, CardDeck cardDeck) { | ||
| players.getPlayers().forEach(player -> completePlayerHand(player, cardDeck)); | ||
| } | ||
|
|
||
| private void completePlayerHand(Player player, CardDeck cardDeck) { | ||
| while (player.canHit() && readHitDecision(player) == YES) { | ||
| player.appendCard(cardDeck.popCard()); | ||
| outputView.printPlayerHand(player); | ||
| } | ||
| } | ||
|
|
||
| private DrawDecision readHitDecision(Player player) { | ||
| InputMapper inputMapper = new InputMapper(); | ||
| return inputMapper.mapToDrawDecision(inputView.readDrawPlan(player.getName())); | ||
| } | ||
|
|
||
| private void completeDealerHand(Player dealer, CardDeck cardDeck) { | ||
| while (dealer.canHit()) { | ||
| Card card = cardDeck.popCard(); | ||
| dealer.appendCard(card); | ||
| } | ||
| } | ||
|
|
||
| private void printDealerPopCount(Player dealer) { | ||
| int dealerPopCount = dealer.handSize() - 2; | ||
| if (dealerPopCount > 0) { | ||
| outputView.printDealerPopCount(16, dealerPopCount); | ||
| } | ||
| } | ||
|
|
||
| private void printPlayersScore(Players players) { | ||
| players.getPlayers().forEach(this::printPlayerScore); | ||
| } | ||
|
|
||
| private void printPlayerScore(Player player) { | ||
| outputView.printPlayerScore(player, player.calculateHandScore()); | ||
| } | ||
|
|
||
| private void printDealerGameResult(Player dealer, Players players) { | ||
| Score dealerScore = dealer.calculateHandScore(); | ||
| int playerWinCount = players.countPlayerWithScoreAbove(dealerScore, new ScoreCalculateStrategy()); | ||
| int dealerWinCount = players.countPlayer() - playerWinCount; | ||
|
|
||
| DealerGameResult dealerGameResult = new DealerGameResult(dealerWinCount, playerWinCount); | ||
| outputView.printDealerGameResult(dealerGameResult); | ||
| } | ||
|
|
||
| private void printPlayersGameResult(Players players, Player dealer) { | ||
| Judge judge = new Judge(); | ||
| Score dealerScore = dealer.calculateHandScore(); | ||
| players.getPlayers() | ||
| .forEach(player -> { | ||
| Score playerScore = player.calculateHandScore(); | ||
| outputView.printPlayerGameResult(player, judge.isPlayerWin(dealerScore, playerScore)); | ||
| }); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package blackjack.controller; | ||
|
|
||
| import blackjack.domain.DrawDecision; | ||
| import blackjack.domain.player.PlayerName; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
|
|
||
| public class InputMapper { | ||
|
|
||
| private static final String DELIMITER = ","; | ||
|
|
||
| public List<PlayerName> mapToPlayers(String target) { | ||
| String[] split = target.split(DELIMITER); | ||
| return Arrays.stream(split) | ||
| .map(PlayerName::new) | ||
| .toList(); | ||
| } | ||
|
|
||
| public DrawDecision mapToDrawDecision(String target) { | ||
| return DrawDecision.from(target); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package blackjack.domain; | ||
|
|
||
| public class DealerGameResult { | ||
|
|
||
| private final int winCount; | ||
| private final int loseCount; | ||
|
|
||
| public DealerGameResult(int winCount, int loseCount) { | ||
| this.winCount = winCount; | ||
| this.loseCount = loseCount; | ||
| } | ||
|
|
||
| public int getWinCount() { | ||
| return winCount; | ||
| } | ||
|
|
||
| public int getLoseCount() { | ||
| return loseCount; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package blackjack.domain; | ||
|
|
||
| import java.util.Arrays; | ||
|
|
||
| public enum DrawDecision { | ||
|
|
||
| YES("y"), | ||
| NO("n"); | ||
|
|
||
| private final String code; | ||
|
|
||
| DrawDecision(String code) { | ||
| this.code = code; | ||
| } | ||
|
|
||
| public static DrawDecision from(String code) { | ||
| return Arrays.stream(DrawDecision.values()) | ||
| .filter(drawDecision -> drawDecision.code.equals(code)) | ||
| .findFirst() | ||
| .orElseThrow(() -> new IllegalArgumentException("[ERROR] 해당하는 값을 찾지 못하였습니다.")); | ||
| } | ||
| } | ||
|
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. 적절한 클래스 분리인 것 같습니다 👍 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| package blackjack.domain.card; | ||
|
|
||
| import java.util.Objects; | ||
|
|
||
| public class Card { | ||
|
|
||
| private final CardShape cardShape; | ||
| private final CardNumber cardNumber; | ||
|
Comment on lines
+7
to
+8
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.
Member
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. 도메인에서 어느정도 노출되는 정보가 있으니 커찬의 의견 합리적인 것 같습니다. 취향의 영역이라는 생각도 드네요 🤔 |
||
|
|
||
| public Card(CardShape cardShape, CardNumber cardNumber) { | ||
| this.cardShape = cardShape; | ||
| this.cardNumber = cardNumber; | ||
| } | ||
|
|
||
| public boolean isAce() { | ||
| return cardNumber.isAce(); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) { | ||
| return true; | ||
| } | ||
| if (o == null || getClass() != o.getClass()) { | ||
| return false; | ||
| } | ||
| Card card = (Card) o; | ||
| return cardShape == card.cardShape && cardNumber == card.cardNumber; | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(cardShape, cardNumber); | ||
| } | ||
|
|
||
| public int getCardNumber() { | ||
| return cardNumber.getValue(); | ||
| } | ||
|
|
||
| public String getCardNumberName() { | ||
| return cardNumber.getName(); | ||
| } | ||
|
|
||
| public String getCardShape() { | ||
| return cardShape.getName(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package blackjack.domain.card; | ||
|
|
||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
| import java.util.stream.IntStream; | ||
|
|
||
| public class CardDeck { | ||
|
|
||
| private final List<Card> cards; | ||
|
|
||
| public CardDeck(List<Card> cards) { | ||
| this.cards = cards; | ||
| } | ||
|
|
||
| public Card popCard() { | ||
| if (cards.isEmpty()) { | ||
| throw new IllegalArgumentException("[ERROR] 남아있는 카드가 부족하여 카드를 뽑을 수 없습니다"); | ||
| } | ||
| return cards.remove(0); | ||
|
||
| } | ||
|
|
||
| public List<Card> popCards(int count) { | ||
| return IntStream.range(0, count) | ||
| .mapToObj(i -> popCard()) | ||
| .collect(Collectors.toList()); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package blackjack.domain.card; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| public class CardDeckCreator { | ||
|
|
||
| public CardDeck create() { | ||
| List<Card> cards = createCards(CardShape.values(), CardNumber.values()); | ||
| Collections.shuffle(cards); | ||
| return new CardDeck(cards); | ||
| } | ||
|
|
||
|
||
| private List<Card> createCards(CardShape[] cardShapes, CardNumber[] cardNumbers) { | ||
| return Arrays.stream(cardShapes) | ||
| .flatMap(shape -> Arrays.stream(cardNumbers) | ||
| .map(number -> new Card(shape, number))) | ||
| .collect(Collectors.toList()); | ||
| } | ||
| } | ||
|
||
Uh oh!
There was an error while loading. Please reload this page.
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.
OutputView와InputView를 만들어서Controller에 주입해주는 이유가 있을까요?Controller에서OutputView와InputView를 만들어도 되지 않을까요?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.
필드 주입의 단점을 의식한 것이긴 해요 (DI 측면)
다만 변경될 가능성이 극히 낮음에도 YAGNI원칙을 무시한 부분이 아닐까라는 생각이 들기는 하네요 😅