-
Notifications
You must be signed in to change notification settings - Fork 389
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
[1단계 - 블랙잭 게임 실행] 우르(김현우) 미션 제출합니다. #391
Changes from 36 commits
e538576
76aaeca
97742fc
ba8e698
ad1c3c9
cd2f4e9
1311e76
34f3580
d4a4c31
99bc87f
9f5b973
1c9277d
e49a507
542fb14
0f44385
fb25c59
328b773
68ca019
9311987
f662ffe
3e41832
5f29f3c
a9b11a2
0c81d51
390d577
48de821
79fd9e7
97492f7
5aefc5b
12fe147
8f69d8f
90940f4
d33ac4a
cb399f4
95b300e
4d88bea
94d8d7d
fca34da
61e8974
d236d9f
b09e72c
a05dfe2
30782d3
eddb77b
fef1279
648f753
4606281
76947c8
8d60d6d
254687c
4b3dbd1
d0e0387
be60c82
f53acf3
60bb6ec
7f54c2d
20fed88
4c9d00b
3ae18c8
0953389
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 |
---|---|---|
@@ -0,0 +1,26 @@ | ||
## 도메인 | ||
|
||
### 카드 값 | ||
|
||
- [x] `2~10`, `A`, `K`, `Q`, `J` 가 있다. | ||
- [x] `K`, `Q`, `J` 는 10으로 계산된다. | ||
|
||
### 카드 | ||
|
||
- [x] `다이아`, `클로버`, `하트`, `스페이드` 카드가 있다. | ||
- [x] `카드 값`을 가진다. | ||
|
||
### 카드 영역 | ||
- [x] 딜러 카드 영역과 참가자 카드 영역으로 나뉜다. | ||
- [x] 카드들을 가진다. | ||
- [x] 카드를 더 받을 수 있는 상태인지 확인한다. | ||
- [x] 카드를 더 받을 것인지 결정한다. | ||
- [x] 카드 현숫자를 계산한다. | ||
- [X] `A` 는 1 또는 11로 계산될 수 있다. | ||
- [x] 카드를 추가할 수 있다. | ||
- [x] 처음에 카드 2장을 받아 생성된다. | ||
|
||
### 카드 덱 | ||
|
||
- [x] 전체 카드를 가진다. | ||
- [x] 카드를 한장씩 준다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import controller.BlackJackController; | ||
|
||
public class BlackJackApplication { | ||
public static void main(String[] args) { | ||
new BlackJackController().run(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package controller; | ||
|
||
import domain.area.CardArea; | ||
import domain.deck.CardDeck; | ||
import domain.player.dealer.Dealer; | ||
import domain.player.dealer.DealerResult; | ||
import domain.player.Name; | ||
import domain.player.participant.Participant; | ||
import domain.player.participant.ParticipantResult; | ||
import domain.player.participant.State; | ||
import view.InputView; | ||
import view.OutputView; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
|
||
import static java.util.stream.Collectors.counting; | ||
|
||
public class BlackJackController { | ||
|
||
public void run() { | ||
|
||
final CardDeck cardDeck = CardDeck.shuffledFullCardDeck(); | ||
|
||
final List<Participant> participants = dealParticipantsCards(cardDeck); | ||
final Dealer dealer = dealDealerCards(cardDeck); | ||
|
||
printStateAfterDealtCard(participants, dealer); | ||
hittingPlayer(cardDeck, participants, dealer); | ||
printStateAfterHittedCard(participants, dealer); | ||
|
||
final Map<Participant, ParticipantResult> playersResult = determineWinner(participants, dealer); | ||
final Map<DealerResult, Long> scoreBoard = countDealerResult(playersResult); | ||
|
||
printPlayerScoreBoard(participants, playersResult, scoreBoard); | ||
} | ||
|
||
private static void printPlayerScoreBoard(final List<Participant> participants, | ||
final Map<Participant, ParticipantResult> playersResult, | ||
final Map<DealerResult, Long> scoreBoard) { | ||
OutputView.showDealerScoreBoard(scoreBoard); | ||
OutputView.showParticipantsScoreBoard(playersResult, participants); | ||
} | ||
|
||
private static void printStateAfterHittedCard(final List<Participant> participants, final Dealer dealer) { | ||
OutputView.showPlayerStateResult(dealer); | ||
OutputView.showParticipantsStateResult(participants); | ||
} | ||
|
||
private void hittingPlayer(final CardDeck cardDeck, final List<Participant> participants, final Dealer dealer) { | ||
hitForParticipants(cardDeck, participants); | ||
hitForDealer(cardDeck, dealer); | ||
} | ||
|
||
private static void printStateAfterDealtCard(final List<Participant> participants, final Dealer dealer) { | ||
OutputView.showDealtCardTo(participants); | ||
OutputView.showStateOf(dealer); | ||
OutputView.showStateOf(participants); | ||
} | ||
|
||
private static Map<DealerResult, Long> countDealerResult( | ||
final Map<Participant, ParticipantResult> playersResult) { | ||
return playersResult.keySet() | ||
.stream() | ||
.collect(Collectors.groupingBy(participant -> playersResult.get(participant) | ||
.convertToDealerResult(), | ||
counting())); | ||
} | ||
|
||
private static Map<Participant, ParticipantResult> determineWinner(final List<Participant> participants, | ||
final Dealer dealer) { | ||
return participants.stream() | ||
.collect(Collectors.toMap( | ||
Function.identity(), | ||
participant -> ParticipantResult.matchBetween(participant, dealer)) | ||
); | ||
} | ||
|
||
private void hitForDealer(final CardDeck cardDeck, final Dealer dealer) { | ||
while (dealer.canHit()) { | ||
OutputView.dealerOneMoreCard(); | ||
dealer.hit(cardDeck.draw()); | ||
} | ||
} | ||
|
||
private void hitForParticipants(final CardDeck cardDeck, final List<Participant> participants) { | ||
participants.forEach(participant -> hitForParticipant(cardDeck, participant)); | ||
} | ||
|
||
private void hitForParticipant(final CardDeck cardDeck, final Participant participant) { | ||
while (participant.canHit()) { | ||
participant.changeState(inputHitOrStay(participant)); | ||
determineHitForParticipant(cardDeck, participant); | ||
} | ||
} | ||
|
||
private void determineHitForParticipant(final CardDeck cardDeck, final Participant participant) { | ||
if (participant.wantHit()) { | ||
participant.hit(cardDeck.draw()); | ||
} | ||
OutputView.showStateOf(participant); | ||
} | ||
|
||
private State inputHitOrStay(final Participant participant) { | ||
if (InputView.readMoreCard(participant).equals("y")) { | ||
return State.HIT; | ||
} | ||
return State.STAY; | ||
} | ||
|
||
private Dealer dealDealerCards(final CardDeck cardDeck) { | ||
return new Dealer(new CardArea(cardDeck.draw(), cardDeck.draw())); | ||
} | ||
|
||
private List<Participant> dealParticipantsCards(final CardDeck cardDeck) { | ||
return InputView.readParticipantsName() | ||
.stream() | ||
.map(Name::new) | ||
.map(name -> new Participant(name, new CardArea(cardDeck.draw(), cardDeck.draw()))) | ||
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. 처음 시작할 때 두 장을 뽑는다는 건 도메인 룰인 것 같아요. 모델 내부로 로직을 이동시키는 건 어떨까요? 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.
도메인 룰에 따라서 CardTable 에 |
||
.collect(Collectors.toList()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package domain.area; | ||
|
||
import domain.card.Card; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class CardArea { | ||
|
||
private final List<Card> cards = new ArrayList<>(); | ||
|
||
public CardArea(final Card firstCard, final Card secondCard) { | ||
cards.addAll(List.of(firstCard, secondCard)); | ||
} | ||
|
||
public List<Card> cards() { | ||
return new ArrayList<>(cards); | ||
} | ||
|
||
public void addCard(final Card card) { | ||
cards.add(card); | ||
} | ||
|
||
public int calculate() { | ||
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.
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. Score 라는 값 객체를 사용해서 표현해봤습니다. int 로 나타내는 것보다 타입으로 명시적으로 보여주는게 다른 사람이 코드를 보았을 때도 무엇을 계산하는지 파악하는 것보다 점수를 반환하기 때문에 점수를 계산하는구나라고 바로 알 수 있을 것 같습니다 ~~ 2차 미션 때는 돈이 나오기 때문에 |
||
int aceCount = countAceCard(); | ||
int totalValue = sumTotalCardValue(); | ||
|
||
while (aceCount > 0) { | ||
if (totalValue <= 11) { | ||
totalValue += 10; | ||
} | ||
aceCount--; | ||
} | ||
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.
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 totalValue; | ||
} | ||
|
||
private int sumTotalCardValue() { | ||
return cards.stream() | ||
.mapToInt(card -> card.cardValue().value()) | ||
.sum(); | ||
} | ||
|
||
private int countAceCard() { | ||
return (int) cards.stream() | ||
.filter(card -> card.cardValue().isAce()) | ||
.count(); | ||
} | ||
|
||
public boolean canMoreCard() { | ||
return calculate() < 21; | ||
} | ||
|
||
public boolean isBurst() { | ||
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. 저도 처음 구현할 때는 burst인줄 알았는데 bust 더라구요. 😄 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 calculate() > 21; | ||
} | ||
|
||
public Card firstCard() { | ||
return cards.get(0); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package domain.card; | ||
|
||
import java.util.Objects; | ||
|
||
public class Card { | ||
|
||
private final CardShape cardShape; | ||
private final CardValue cardValue; | ||
|
||
public Card(final CardShape cardShape, final CardValue cardValue) { | ||
this.cardShape = cardShape; | ||
this.cardValue = cardValue; | ||
} | ||
|
||
public CardShape cardShape() { | ||
return cardShape; | ||
} | ||
|
||
public CardValue cardValue() { | ||
return this.cardValue; | ||
} | ||
|
||
@Override | ||
public boolean equals(final Object o) { | ||
if (this == o) return true; | ||
if (!(o instanceof Card)) return false; | ||
final Card card = (Card) o; | ||
return cardShape == card.cardShape && cardValue == card.cardValue; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(cardShape, cardValue); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package domain.card; | ||
|
||
public enum CardShape { | ||
DIAMOND, | ||
CLOVER, | ||
HEART, | ||
SPADE, | ||
; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package domain.card; | ||
|
||
public enum CardValue { | ||
|
||
ACE(1), | ||
|
||
TWO(2), | ||
THREE(3), | ||
FOUR(4), | ||
FIVE(5), | ||
SIX(6), | ||
SEVEN(7), | ||
EIGHT(8), | ||
NINE(9), | ||
TEN(10), | ||
|
||
KING(10), | ||
QUEEN(10), | ||
JACK(10); | ||
|
||
private final int value; | ||
|
||
CardValue(final int value) { | ||
this.value = value; | ||
} | ||
|
||
public int value() { | ||
return value; | ||
} | ||
|
||
public boolean isAce() { | ||
return this == ACE; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package domain.deck; | ||
|
||
import domain.card.Card; | ||
import domain.card.CardShape; | ||
import domain.card.CardValue; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.Stack; | ||
import java.util.stream.Collectors; | ||
|
||
public class CardDeck { | ||
|
||
private final Stack<Card> cards; | ||
|
||
private CardDeck(final Stack<Card> cards) { | ||
this.cards = cards; | ||
} | ||
|
||
public static CardDeck shuffledFullCardDeck() { | ||
|
||
Stack<Card> cards = Arrays.stream(CardShape.values()) | ||
.flatMap(cardShape -> Arrays.stream(CardValue.values()) | ||
.map(cardValue -> new Card(cardShape, cardValue))) | ||
.collect(Collectors.toCollection(Stack::new)); | ||
|
||
Collections.shuffle(cards); | ||
|
||
return new CardDeck(cards); | ||
} | ||
|
||
public Card draw() { | ||
return cards.pop(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package domain.player; | ||
|
||
public class Name { | ||
|
||
private final String value; | ||
|
||
public Name(final String name) { | ||
validateEmpty(name); | ||
this.value = name; | ||
} | ||
|
||
private void validateEmpty(final String name) { | ||
if (name == null || name.isBlank()) { | ||
throw new IllegalArgumentException("이름은 빈칸일 수 없습니다."); | ||
} | ||
} | ||
|
||
public String value() { | ||
return value; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package domain.player; | ||
|
||
import domain.area.CardArea; | ||
import domain.card.Card; | ||
|
||
public abstract class Player { | ||
|
||
protected final Name name; | ||
protected final CardArea cardArea; | ||
|
||
protected Player(final Name name, final CardArea cardArea) { | ||
this.name = name; | ||
this.cardArea = cardArea; | ||
} | ||
|
||
public Name name() { | ||
return name; | ||
} | ||
|
||
public CardArea cardArea() { | ||
return cardArea; | ||
} | ||
|
||
public boolean isBurst() { | ||
return cardArea.isBurst(); | ||
} | ||
|
||
public void hit(final Card card) { | ||
cardArea.addCard(card); | ||
} | ||
|
||
public abstract boolean canHit(); | ||
|
||
public int score() { | ||
return cardArea.calculate(); | ||
} | ||
} |
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.
want라는 개념을 뽑아내주셨네요 👍
다만, player의 상태를 바꿀 필요 없이
이런 식으로 구현해도 되지 않을까요?
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.
범블비 말씀을 듣고 굳이 State 가 필요없지 않을까라는 생각을 했어요.
제가 봤을 땐,
그래서 canHit 로 hit 할 수 있는지 없는지 판단하고, y와 n을 통해서 카드를 draw 할지 말지 결정할 수 있기 때문에 State 가 굳이 필요없다라고 판단했습니다!!