-
Notifications
You must be signed in to change notification settings - Fork 0
[STEP 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 all commits
4117d5c
e1a6bdf
19d6093
8523956
c5a433b
80e5408
ca39c4b
700ddc0
f36578b
4939d97
1a19a26
e5be6ae
5e03b1a
80ab9f0
9039353
33584bd
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,3 +1,27 @@ | ||
| # java-blackjack | ||
|
|
||
| 블랙잭 미션 저장소 | ||
|
|
||
| ## 참가자 입력 | ||
| - [ ] 쉼표 기준으로 분리 | ||
|
|
||
| ## 카드 배분 | ||
| - [ ] 카드는 52장을 사용 | ||
| - 숫자 계산은 카드 숫자를 기본 | ||
| - Ace는 1 or 11로 계산 가능 | ||
| - King, Queen, Jack은 각각 10으로 계산 | ||
| - [ ] 카드는 게임이 시작되기 전에 순서를 섞는다. | ||
| - [ ] 참가자에게 카드를 2장씩 분배 | ||
| - [ ] 각 참가자에게 분배된 카드 출력 | ||
|
|
||
| ## 게임 진행 | ||
| - [ ] 딜러를 제외한 참가자에게 Hit/Stand 여부 확인 | ||
| - 참가자의 카드 합이 21을 넘지 않는지 Bust 검증 | ||
| - draw 할 카드가 존재하는지 확인 | ||
| - [ ] 딜러 Hit/Stand 여부 확인 | ||
| - 16 이하라면 카드를 draw, 아니라면 멈춘다. | ||
|
|
||
| ## 게임 종료 | ||
| - [ ] 참가자의 카드 패와 결과 출력 | ||
| - [ ] 결과를 바탕으로 각 참가자들의 승패 출력 | ||
| - 점수가 같을 때를 고려 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import controller.GameController; | ||
| import ui.InputView; | ||
| import ui.ResultView; | ||
|
|
||
| public class Application { | ||
| public static void main(String[] args){ | ||
| GameController gameController = new GameController(new ResultView(), new InputView()); | ||
| gameController.run(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| package controller; | ||
|
|
||
| import domain.deck.Deck; | ||
| import domain.participant.Dealer; | ||
| import domain.participant.Participant; | ||
| import domain.participant.Player; | ||
| import domain.result.ResultJudge; | ||
| import ui.InputView; | ||
| import ui.ResultView; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| public class GameController { | ||
| private final ResultView resultView; | ||
| private final InputView inputView; | ||
|
|
||
| public GameController(ResultView resultView, InputView inputView){ | ||
| this.resultView = resultView; | ||
| this.inputView = inputView; | ||
| } | ||
|
|
||
| public void run(){ | ||
| Deck deck = new Deck(); | ||
| ResultJudge judge = new ResultJudge(); | ||
| // 참가자 입력 | ||
| List<Participant> participants = joinParticipant(); | ||
|
|
||
| // 초기 패 설정 | ||
| initialAllPlayersHand(participants, deck); | ||
|
|
||
| // 플레이어마다 Hit/Stand 여부 확인 | ||
| askHitOrStand(participants, deck); | ||
|
|
||
| // 참가자 패와 점수 공개 | ||
| resultView.printScores(participants); | ||
|
|
||
| // 참가자 승/패 공개 | ||
| resultView.printGameResult(participants, judge); | ||
|
|
||
| } | ||
|
|
||
| /*** | ||
| * 1. 모든 플레이어들에게 Hit/Stand 여부를 묻기 위한 함수 실행 | ||
| * 2. 딜러 draw 여부 확인 (16점 이하인지) | ||
| */ | ||
| private void askHitOrStand(List<Participant> participants, Deck deck){ | ||
| Participant dealer = participants.getFirst(); | ||
| for(Participant player : participants.subList(1, participants.size())){ | ||
| askPlayerHit(player, deck); | ||
| } | ||
|
|
||
| while(dealer.canReceiveCard()){ | ||
| dealer.receive(deck.draw()); | ||
| resultView.printDealerDraw(); | ||
| } | ||
| } | ||
|
|
||
| /*** | ||
| * 플레이어에게 Hit/Stand 여부 확인 | ||
| */ | ||
| private void askPlayerHit(Participant player, Deck deck){ | ||
| while(inputView.askHit(player.getName())){ | ||
|
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. 플레이어가 더 이상 카드를 받을 수 없는 상태가 되면 입력을 다시 묻지 않고 턴을 종료하는 흐름을 의도하신 걸까요? 제안: |
||
| canHit(player, deck); | ||
| } | ||
| } | ||
|
|
||
| /*** | ||
| * Hit 선택시 실제로 플레이어가 Bust가 아닌지 확인 후 카드 지급 | ||
| */ | ||
| private void canHit(Participant player, Deck deck){ | ||
| if(player.canReceiveCard()){ | ||
| player.receive(deck.draw()); | ||
| resultView.printHand(player); | ||
| return; | ||
| } | ||
| resultView.printIsBust(); | ||
| } | ||
|
|
||
| /*** | ||
| * 딜러와 입력 받은 플레이어 추가 | ||
| */ | ||
| private List<Participant> joinParticipant(){ | ||
| Participant dealer = new Dealer("딜러"); | ||
| List<String> playerNames; | ||
| List<Participant> participants = new ArrayList<>(); | ||
|
|
||
| participants.add(dealer); | ||
|
|
||
| playerNames = inputView.inputPlayers(); | ||
|
|
||
| for(String playerName : playerNames){ | ||
| Participant player = new Player(playerName); | ||
| participants.add(player); | ||
| } | ||
|
|
||
| return participants; | ||
| } | ||
|
|
||
| /*** | ||
| * 모든 참가자에게 2장의 카드를 분배하기 위한 함수 호출 및 패 공개 | ||
| */ | ||
| private void initialAllPlayersHand(List<Participant> participants, Deck deck){ | ||
| for(Participant participant : participants){ | ||
| initialHand(participant, deck); | ||
| } | ||
| resultView.printInitialDeal(participants); | ||
| resultView.printAllHands(participants); | ||
| } | ||
|
|
||
| /** | ||
| * 카드를 2장씩 분배 | ||
| */ | ||
| private void initialHand(Participant participant, Deck deck){ | ||
| for(int i = 0; i < 2; i++){ | ||
| participant.receive(deck.draw()); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package domain.card; | ||
|
|
||
| import domain.card.enums.Rank; | ||
| import domain.card.enums.Suit; | ||
|
|
||
| public class Card { | ||
| private Rank rank; | ||
|
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. 카드를 불변 객체로 두려는 의도였다면 필드에도 그 의도를 표현해보는 것은 어떨까요? 제안: |
||
| private Suit suit; | ||
|
|
||
| public Card(Rank rank, Suit suit){ | ||
| this.rank = rank; | ||
| this.suit = suit; | ||
| } | ||
|
|
||
| public boolean isAce() { | ||
| return this.rank == Rank.ACE; | ||
| } | ||
|
|
||
| public int score(){ | ||
| return this.rank.getScore(); | ||
| } | ||
|
|
||
| public String displayName(){ | ||
| return rank.getName() + suit.getName(); | ||
| } | ||
|
Comment on lines
+23
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. Card가 자신의 표현 방식까지 책임지는 것이 자연스러울까요? |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package domain.card.enums; | ||
|
|
||
| public enum Rank { | ||
| ACE("A", 1), | ||
| TWO("2", 2), | ||
| THREE("3", 3), | ||
| FOUR("4", 4), | ||
| FIVE("5", 5), | ||
| SIX("6", 6), | ||
| SEVEN("7", 7), | ||
| EIGHT("8", 8), | ||
| NINE("9", 9), | ||
| TEN("10", 10), | ||
| JACK("J", 10), | ||
| QUEEN("Q", 10), | ||
| KING("K", 10); | ||
|
|
||
| private final String name; | ||
| private final int score; | ||
|
|
||
| Rank(String name, int score) { | ||
| this.name = name; | ||
| this.score = score; | ||
| } | ||
|
|
||
| public int getScore() { | ||
| return score; | ||
| } | ||
|
|
||
| public String getName() { | ||
| return name; | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package domain.card.enums; | ||
|
|
||
| public enum Suit { | ||
| CLUB("클로버"), | ||
| HEART("하트"), | ||
| SPADE("스페이드"), | ||
| DIAMOND("다이아몬드"); | ||
|
|
||
| private final String name; | ||
|
|
||
| Suit(String name){ | ||
| this.name = name; | ||
| } | ||
|
|
||
| public String getName(){ | ||
| return name; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| package domain.deck; | ||
|
|
||
| import domain.card.Card; | ||
| import domain.card.enums.Rank; | ||
| import domain.card.enums.Suit; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| public class Deck { | ||
| private final List<Card> cards = new ArrayList<>(); | ||
|
|
||
| public Deck() { | ||
|
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. 덱의 생성과 셔플을 한 번에 묶은 것은 '게임 시작 전 항상 섞인 덱'이라는 요구사항을 강제하기 위한 의도였을까요? 제안: 운영용으로는 |
||
| createdDeck(); | ||
| shuffle(); | ||
| } | ||
|
|
||
| private void createdDeck(){ | ||
| for (Suit suit : Suit.values()){ | ||
| addCardsBySuit(suit); | ||
| } | ||
| } | ||
|
|
||
| private void addCardsBySuit(Suit suit) { | ||
| for(Rank rank : Rank.values()){ | ||
| this.cards.add(new Card(rank, suit)); | ||
| } | ||
| } | ||
|
|
||
| public Card firstCard(){ | ||
| return cards.get(0); | ||
| } | ||
|
|
||
| public Card draw(){ | ||
| if(cards.isEmpty()){ | ||
|
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. 카드가 부족한 상황을 게임 진행 불가 상태로 다루려는 의도였을까요, 아니면 자동으로 새 덱을 준비하는 것을 룰로 보신 걸까요? 제안: |
||
| createdDeck(); | ||
| this.shuffle(); | ||
| } | ||
| return cards.removeFirst(); | ||
| } | ||
|
|
||
| public void shuffle(){ | ||
| Collections.shuffle(cards); | ||
| } | ||
|
|
||
| public List<Card> cards(){ | ||
| return List.copyOf(cards); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| package domain.hand; | ||
|
|
||
| import domain.card.Card; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| public class Hand { | ||
| private final List<Card> cards = new ArrayList<>(); | ||
|
|
||
| public void add(Card card) { | ||
| cards.add(card); | ||
| } | ||
|
|
||
| public int score() { | ||
| int score = basicScore(); | ||
| int aceCount = aceCount(); | ||
|
|
||
| return bestScore(score, aceCount); | ||
| } | ||
|
|
||
| public boolean isBust() { | ||
| return score() > 21; | ||
|
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. 만약 블랙잭이 21이 아니라 22가 된다면, 몇 군데를 수정해야 할까요? |
||
| } | ||
|
|
||
| public String displayCards(){ | ||
| return cards.stream() | ||
| .map(Card::displayName) | ||
| .collect(Collectors.joining(", ")); | ||
| } | ||
|
|
||
| public String displayFirstCard(){ | ||
| return cards.getFirst().displayName(); | ||
| } | ||
|
|
||
| private int basicScore() { | ||
| int score = 0; | ||
| for (Card card : cards) { | ||
| score += card.score(); | ||
| } | ||
| return score; | ||
| } | ||
|
|
||
| private int aceCount() { | ||
| return (int) cards.stream().filter(Card::isAce).count(); | ||
| } | ||
|
|
||
| private int bestScore(int score, int aceCount) { | ||
| while (aceCount > 0 && score + 10 <= 21) { | ||
| score += 10; | ||
| aceCount--; | ||
| } | ||
| return score; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package domain.participant; | ||
|
|
||
| public class Dealer extends Participant{ | ||
|
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. 추상 클래스는 어떤 단점이 있을까요? |
||
| public Dealer(String name){ | ||
| super(name); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean canReceiveCard(){ | ||
| return checkScore() <= 16; | ||
| } | ||
| } | ||
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.
딜러와 플레이어 목록을 하나의
List<Participant>로 묶은 이유는 '동일하게 카드를 받는 참가자'라는 공통점을 활용하기 위한 선택이었을까요?제안:
Participants또는BlackjackGame같은 객체가Dealer dealer,List<Player> players를 명시적으로 보유하고, 공통 순회가 필요할 때만allParticipants()를 제공하는 구조는 어떨까요? 그러면 딜러 위치 불변식을 한 곳에서 관리할 수 있습니다.