-
Notifications
You must be signed in to change notification settings - Fork 0
[1단계 - 블랙잭 게임 실행] 로빈(임수빈) 미션 제출합니다. #1
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 34 commits
89a8f44
db91493
178ad20
40019ad
7196074
399fa25
19cfa4c
ab2676b
f1319fd
8656caf
9a66483
4adbb3e
b2a5a12
7106b9c
213a3f2
77439a2
453bb19
aa2a65e
5685701
c6e7564
9f15cc9
3b47a6d
a1b7720
3502c5d
89e773e
c6047ff
da5c9ce
a6ba73b
f0aae99
6322d3d
61eb780
4c5d1a4
1411054
b7ff262
0adedd5
adecadf
74d6688
15ee815
5ef0343
ea7eb45
f667755
6f1a623
4df7ab4
bc92c76
671e977
b47d4a9
92ec982
839110d
bea1e52
f7d8e73
9e305e2
0f6df51
15543db
0f008c7
0b09e12
438015e
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,59 @@ | ||
| # java-blackjack | ||
| ## 기능 요구 사항 | ||
|
|
||
| 블랙잭 미션 저장소 | ||
| ### 플레이어 이름 입력 | ||
|
|
||
| ## 우아한테크코스 코드리뷰 | ||
| 1. 플레이어의 이름은 ","을 기준으로 구분한다. | ||
| 2. 플레이어의 이름은 길이가 1 이상이어야 한다. | ||
| 3. 플레이어의 이름은 알파벳 대소문자와 숫자로만 구성되어야 한다. | ||
| 4. 플레이어의 이름은 중복되면 안된다. | ||
|
|
||
| - [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) | ||
| ### 플레이어 및 딜러 점수 계산 | ||
|
|
||
| 1. 플레이어가 소유한 카드의 점수의 합이 플레이어의 점수다. | ||
| 2. 카드의 점수는 카드의 숫자로 계산한다. | ||
| 3. Ace 카드는 1 혹은 11 중 카드의 소유자가 더 유리한 것으로 계산한다. | ||
| 4. King, Queen, Jack은 각각 10으로 계산한다. | ||
|
|
||
| ### 게임 진행 규칙 | ||
|
|
||
| 1. 모든 플레이어와 딜러는 카드를 2장씩 가지고 시작한다. | ||
| 2. 플레이어는 서로 돌아가면서 자기 턴을 가진다. | ||
| 3. 플레이어는 자신의 턴에 자신이 카드를 더 받을지 말지 선택할 수 있다. 단, 자신의 점수가 21 이상인 경우 카드를 더 받을 수 없다. | ||
| 4. 플레이어는 한 번 카드를 받지 않기로 결정한 경우, 앞으로의 턴에서 카드를 더 받을 수 없다. | ||
| 5. 모든 플레이어가 카드를 더 받을 수 없는 경우, 딜러의 턴으로 넘어간다. | ||
| 6. 딜러의 턴에서 딜러는 딜러의 점수가 16점 이하인 경우 카드를 더 받는다. 17점 이상인 경우 카드를 더 받지 않는다. | ||
| 7. 딜러의 턴이 끝나면, 최종 점수 계산 및 게임의 승패를 가린다. | ||
| 8. 최종 점수가 21점에 가장 가까우면서 21점을 넘기지 않는 사람이 승리한다. 동점인 플레이어(딜러 포함)이 나온 경우, 무승부로 판단한다. | ||
|
|
||
| ### 게임 진행 상황 출력 | ||
|
|
||
| 1. 게임이 시작 되자마자, 딜러와 플레이어가 받은 카드를 출력한다. | ||
| 2. 단, 딜러는 카드를 한장만 출력한다. | ||
| 3. 이 후 플레이어의 차례마다 플레이어가 소유한 카드를 출력한다. | ||
|
|
||
| ### 게임 결과 출력 | ||
|
|
||
| 1. 게임을 완료한 후 각 플레이어(딜러 포함)가 보유한 카드 및 점수를 출력한다. | ||
| 2. 게임을 완료한 후 각 플레이어별로 승패를 출력한다. | ||
| - 딜러는 다른 모든 플레이어에 대한 승패가 출력된다. | ||
| - 딜러가 아닌 플레이어는 딜러에 대한 승패가 출력된다. | ||
|
|
||
| ### 게임 진행 가이드 출력 | ||
|
|
||
| - 게임의 원활한 진행을 위해 가이드 문구를 출력한다. | ||
|
|
||
| ## 기능 목록 | ||
|
|
||
| - [x] 플레이어 이름 입력 기능 | ||
| - [x] 카드 점수 계산 기능 | ||
| - [x] Ace 카드 점수 보정 기능 | ||
| - [x] 플레이어 및 딜러 점수 계산 기능 | ||
| - [x] 플레이어 및 딜러간 승부 계산 기능 | ||
| - [x] 플레이어 및 딜러 손패 출력 기능 | ||
| - [x] 게임 결과 출력 기능 | ||
| - [x] 게임 승부 결과 출력 기능 | ||
| - [x] 덱 관리 기능 | ||
| - [x] 보유한 카드 점수 합계 계산 기능 | ||
| - [x] 카드 받을지 여부 입력 기능 | ||
| - [x] 전체 게임 진행 기능 | ||
| - [x] 딜러 Ace 카드 점수 보정 기능 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import controller.BlackjackController; | ||
| import domain.blackjack.Gamer; | ||
| import domain.blackjack.HoldingCards; | ||
| import domain.card.Deck; | ||
| import java.util.List; | ||
| import view.NameInputView; | ||
| import view.OutputView; | ||
|
||
|
|
||
| public class Main { | ||
| public static void main(String[] args) { | ||
| Gamer dealer = new Gamer("딜러", HoldingCards.of()); | ||
| OutputView.print("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); | ||
| List<Gamer> players = NameInputView.getNames().stream() | ||
| .map(name -> new Gamer(name, HoldingCards.of())) | ||
| .toList(); | ||
| BlackjackController blackjackController = new BlackjackController(dealer, players); | ||
| blackjackController.startBlackjackGame(Deck.fullDeck()); | ||
| } | ||
| } | ||
|
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. 로빈이 생각한 Main 클래스의 역할은 무엇인가요? 💭 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| package controller; | ||
|
|
||
| import domain.blackjack.DealerRandomCardDrawStrategy; | ||
| import domain.blackjack.GameResult; | ||
| import domain.blackjack.GameResultCalculator; | ||
| import domain.blackjack.Gamer; | ||
| import domain.blackjack.PlayerRandomCardDrawStrategy; | ||
| import domain.blackjack.SummationCardPoint; | ||
| import domain.card.Card; | ||
| import domain.card.Deck; | ||
| import dto.DealerGameResultDTO; | ||
| import dto.GamerDTO; | ||
| import dto.PlayerGameResultDTO; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.stream.Collectors; | ||
| import view.GameResultOutputView; | ||
| import view.GamerOutputView; | ||
| import view.OutputView; | ||
| import view.YesOrNoInputView; | ||
|
|
||
| public class BlackjackController { | ||
| private final Gamer dealer; | ||
| private final List<Gamer> players; | ||
|
||
|
|
||
| public BlackjackController(Gamer dealer, List<Gamer> players) { | ||
| this.dealer = dealer; | ||
| this.players = players; | ||
| } | ||
|
|
||
| public void startBlackjackGame(Deck deck) { | ||
| initDealerAndPlayers(deck); | ||
| printDealerAndPlayers(); | ||
|
|
||
| playersTryDraw(deck); | ||
| dealerTryDraw(deck); | ||
|
|
||
| printDealerAndPlayersWithPoint(); | ||
| printDealerAndPlayersGameResult(); | ||
| } | ||
|
|
||
| private void initDealerAndPlayers(Deck deck) { | ||
| dealerDraw(deck); | ||
| dealerDraw(deck); | ||
| players.forEach(player -> playerDraw(deck, player)); | ||
| players.forEach(player -> playerDraw(deck, player)); | ||
| String namesOutput = players.stream().map(Gamer::getRawName).collect(Collectors.joining(", ")); | ||
| OutputView.print("딜러와 %s에게 2장을 나누었습니다.".formatted(namesOutput)); | ||
|
||
| } | ||
|
|
||
| private void dealerDraw(Deck deck) { | ||
| dealer.draw(deck, new DealerRandomCardDrawStrategy(dealer)); | ||
| } | ||
|
|
||
| private void playerDraw(Deck deck, Gamer player) { | ||
| player.draw(deck, new PlayerRandomCardDrawStrategy(player)); | ||
| } | ||
|
||
|
|
||
| private void printDealerAndPlayers() { | ||
| printDealer(dealer); | ||
| players.forEach(BlackjackController::printPlayer); | ||
| } | ||
|
|
||
| private static void printDealer(Gamer dealer) { | ||
| List<Card> rawHoldingCards = new ArrayList<>(dealer.getRawHoldingCards()); | ||
| rawHoldingCards.remove(0); | ||
| GamerDTO gamerDTO = new GamerDTO(dealer.getRawName(), rawHoldingCards, | ||
| dealer.getRawSummationCardPoint()); | ||
|
||
| GamerOutputView.printWithoutSummationCardPoint(gamerDTO); | ||
| } | ||
|
||
|
|
||
| private static void printPlayer(Gamer player) { | ||
| GamerDTO gamerDTO = new GamerDTO(player.getRawName(), player.getRawHoldingCards(), | ||
| player.getRawSummationCardPoint()); | ||
| GamerOutputView.printWithoutSummationCardPoint(gamerDTO); | ||
| } | ||
|
|
||
| private void playersTryDraw(Deck deck) { | ||
| players.forEach(player -> playerTryDraw(deck, player)); | ||
| } | ||
|
|
||
| private void playerTryDraw(Deck deck, Gamer player) { | ||
| boolean needToDraw = true; | ||
| while (needToDraw && canDraw(player, new SummationCardPoint(21))) { | ||
| needToDraw = playerTryDrawOnce(deck, player); | ||
| } | ||
| } | ||
|
|
||
| private boolean playerTryDrawOnce(Deck deck, Gamer player) { | ||
| boolean needToDraw; | ||
| OutputView.print("%s은(는) 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)".formatted(player.getRawName())); | ||
| needToDraw = YesOrNoInputView.getYNAsBoolean(); | ||
| if (needToDraw) { | ||
| playerDraw(deck, player); | ||
| } | ||
| printPlayer(player); | ||
| return needToDraw; | ||
| } | ||
|
|
||
| private boolean canDraw(Gamer player, SummationCardPoint threshold) { | ||
| return !player.getSummationCardPoint().isBiggerThan(threshold); | ||
| } | ||
|
|
||
| private void dealerTryDraw(Deck deck) { | ||
| try { | ||
| dealerDraw(deck); | ||
| OutputView.print("딜러는 16이하라 한장의 카드를 더 받았습니다.\n"); | ||
| } catch (IllegalStateException ignored) { | ||
|
|
||
| } | ||
|
||
| } | ||
|
|
||
| private void printDealerAndPlayersWithPoint() { | ||
| GamerDTO dealerDTO = new GamerDTO(dealer.getRawName(), dealer.getRawHoldingCards(), | ||
| dealer.getRawSummationCardPoint()); | ||
| GamerOutputView.print(dealerDTO); | ||
|
|
||
| for (Gamer player : players) { | ||
| GamerDTO playerDTO = new GamerDTO(player.getRawName(), player.getRawHoldingCards(), | ||
| player.getRawSummationCardPoint()); | ||
| GamerOutputView.print(playerDTO); | ||
| } | ||
| } | ||
|
|
||
| private void printDealerAndPlayersGameResult() { | ||
| Map<GameResult, Integer> dealerGameResultCounts = players.stream() | ||
| .collect(Collectors.groupingBy(player -> GameResultCalculator.calculate(dealer, player), | ||
| Collectors.summingInt(value -> 1))); | ||
| DealerGameResultDTO dealerGameResultDTO = new DealerGameResultDTO(dealerGameResultCounts); | ||
|
|
||
| List<PlayerGameResultDTO> playerGameResultDTOS = players.stream() | ||
| .map(player -> new PlayerGameResultDTO(player.getRawName(), | ||
| GameResultCalculator.calculate(player, dealer))) | ||
| .toList(); | ||
|
|
||
| GameResultOutputView.print(dealerGameResultDTO); | ||
| playerGameResultDTOS.forEach(GameResultOutputView::print); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package domain.blackjack; | ||
|
|
||
| import domain.card.Card; | ||
| import domain.card.CardDrawStrategy; | ||
| import java.util.List; | ||
| import java.util.Random; | ||
|
|
||
| abstract class AbstractRandomCardDrawStrategy implements CardDrawStrategy { | ||
| @Override | ||
| public final Card nextCard(List<Card> cards) { | ||
| if (canDraw()) { | ||
|
||
| return cardSelectStrategy(cards); | ||
| } | ||
| throw new IllegalStateException("카드를 더이상 뽑을 수 없습니다."); | ||
| } | ||
|
|
||
| abstract boolean canDraw(); | ||
|
||
|
|
||
| private Card cardSelectStrategy(List<Card> cards) { | ||
| Random random = new Random(); | ||
| int idx = random.nextInt(cards.size()); | ||
| return cards.get(idx); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package domain.blackjack; | ||
|
|
||
| record CardPoint(int point) { | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package domain.blackjack; | ||
|
|
||
| import static domain.card.CardName.TEN; | ||
|
|
||
| import domain.card.Card; | ||
| import domain.card.CardName; | ||
|
|
||
| class CardPointCalculator { | ||
| static CardPoint calculate(Card card) { | ||
| CardName cardName = card.name(); | ||
| int cardNumber = cardName.getCardNumber(); | ||
| if (cardNumber > TEN.getCardNumber()) { | ||
| return new CardPoint(TEN.getCardNumber()); | ||
| } | ||
| return new CardPoint(cardNumber); | ||
| } | ||
|
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. 블랙잭에서 j,q,k 는 10으로 계산한다! 라는 룰이 있어 enum 값 세팅할 때 10으로 해두어도 되지 않았을까요?
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.
저는 블랙잭의 규칙은 트럼프 카드 고유의 것이라 생각하지 않았습니다. 블랙잭은 트럼프 카드를 사용하는 여러 게임 중 하나기 때문에 블랙잭의 규칙이 트럼프 카드를 추상화한 클래스 내부에 있으면 안된다고 생각했습니다. |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package domain.blackjack; | ||
|
|
||
| public class DealerRandomCardDrawStrategy extends AbstractRandomCardDrawStrategy { | ||
|
||
| private final Gamer dealer; | ||
|
||
|
|
||
| public DealerRandomCardDrawStrategy(Gamer dealer) { | ||
| this.dealer = dealer; | ||
| } | ||
|
|
||
| @Override | ||
| boolean canDraw() { | ||
| SummationCardPoint summationCardPoint = dealer.getSummationCardPoint(); | ||
| SummationCardPoint dealerDrawThresholdPoint = new SummationCardPoint(16); | ||
| return !summationCardPoint.isBiggerThan(dealerDrawThresholdPoint); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package domain.blackjack; | ||
|
|
||
| public enum GameResult { | ||
| WIN, LOSE, TIE; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package domain.blackjack; | ||
|
|
||
| public class GameResultCalculator { | ||
| /** | ||
| * baseGamer의 otherGamer 에 대한 승부 결과 반환 | ||
| * | ||
| * @param baseGamer 기준 게이머 | ||
| * @param otherGamer 상대 게이머 | ||
| * @return baseGamer의 otherGamer 에 대한 승부 결과 | ||
| */ | ||
| public static GameResult calculate(Gamer baseGamer, Gamer otherGamer) { | ||
| if (baseGamer.isDead() && otherGamer.isDead()) { | ||
| return GameResult.TIE; | ||
| } | ||
| if (baseGamer.isDead()) { | ||
| return GameResult.LOSE; | ||
| } | ||
| if (otherGamer.isDead()) { | ||
| return GameResult.WIN; | ||
| } | ||
| return getGameResultWhenNobodyDead(baseGamer, otherGamer); | ||
| } | ||
|
|
||
| private static GameResult getGameResultWhenNobodyDead(Gamer baseGamer, Gamer otherGamer) { | ||
| SummationCardPoint baseGamerSummationCardPoint = baseGamer.getSummationCardPoint(); | ||
| SummationCardPoint otherGamerSummationCardPoint = otherGamer.getSummationCardPoint(); | ||
|
|
||
| if (baseGamerSummationCardPoint.isBiggerThan(otherGamerSummationCardPoint)) { | ||
| return GameResult.WIN; | ||
| } | ||
| if (baseGamerSummationCardPoint.equals(otherGamerSummationCardPoint)) { | ||
| return GameResult.TIE; | ||
| } | ||
| return GameResult.LOSE; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| package domain.blackjack; | ||
|
|
||
| import static domain.card.CardName.TEN; | ||
|
|
||
| import domain.card.Card; | ||
| import domain.card.CardDrawStrategy; | ||
| import domain.card.Deck; | ||
| import java.util.List; | ||
|
|
||
| public class Gamer { | ||
| private final String name; | ||
| private final HoldingCards holdingCards; | ||
|
|
||
| public Gamer(String name, HoldingCards holdingCards) { | ||
| this.name = name; | ||
| this.holdingCards = holdingCards; | ||
| } | ||
|
|
||
| public void draw(Deck deck, CardDrawStrategy cardDrawStrategy) { | ||
| Card draw = deck.draw(cardDrawStrategy); | ||
| holdingCards.add(draw); | ||
| } | ||
|
|
||
| public SummationCardPoint getSummationCardPoint() { | ||
| SummationCardPoint summationCardPoint = holdingCards.calculateTotalPoint(); | ||
| if (hasAceInHoldingCards()) { | ||
| int rawPoint = fixPoint(summationCardPoint.summationCardPoint()); | ||
| return new SummationCardPoint(rawPoint); | ||
| } | ||
| return summationCardPoint; | ||
| } | ||
|
|
||
| private int fixPoint(int rawPoint) { | ||
| SummationCardPoint fixPoint = new SummationCardPoint(rawPoint + TEN.getCardNumber()); | ||
| if (!fixPoint.isDeadPoint()) { | ||
| return fixPoint.summationCardPoint(); | ||
| } | ||
| return rawPoint; | ||
| } | ||
|
|
||
| public String getRawName() { | ||
| return name; | ||
| } | ||
|
|
||
| public List<Card> getRawHoldingCards() { | ||
| return List.copyOf(holdingCards.getHoldingCards()); | ||
| } | ||
|
|
||
| public int getRawSummationCardPoint() { | ||
| return getSummationCardPoint().summationCardPoint(); | ||
| } | ||
|
|
||
| boolean isDead() { | ||
| return holdingCards.calculateTotalPoint().isDeadPoint(); | ||
| } | ||
|
|
||
| boolean hasAceInHoldingCards() { | ||
| return holdingCards.hasAce(); | ||
| } | ||
| } |
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.
모든 플레이어가 버스트가 된 경우 딜러는 카드를 뽑지 않아도 승리하기 때문에 딜러가 승이라고 생각합니다!