-
Notifications
You must be signed in to change notification settings - Fork 6
[블랙잭 게임 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: chunghyeon-kim
Are you sure you want to change the base?
Changes from all commits
cd61a5b
9a8ad41
49385ff
5850dd0
f5e6914
c43c521
55f99ee
0d0b13c
e9ed25c
2b3f5c5
a6c12c5
b2198e8
5ec0118
f0cbf79
28c5a8d
ac18db0
b1e1a25
7b7ed47
8dab5e0
f5829e0
f0ec5f4
2cc96f3
e99f18b
9bfe228
02ba420
02b8932
6765478
d7e50f4
33dc3e5
56880bb
f3f0d12
3260c5d
b71d341
6f547a3
1177903
74e1443
3aa80e6
22f26dd
68db2ce
aa1ab86
e5a2991
4eed0a8
b21caf0
61696cb
b00b293
dc99787
8cd41f3
634acab
3818a06
49d48e7
080bc70
4b3f64b
0aebc8c
bfe22bb
13b9421
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 +1,45 @@ | ||
# java-blackjack | ||
|
||
# 구현 기능 목록 | ||
|
||
-[x] 게임 | ||
- [x] 플레이어들의 이름을 입력받는다. | ||
- [x] 최소 플레이어는 1명으로, 딜러와 게임을 진행한다. | ||
- [x] ,를 기준으로 분리 | ||
|
||
- [x] 시작시 각 플레이어가 두 장의 카드를 지급받는다. | ||
- [x] 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 뽑는다. | ||
- [x] 딜러는 처음에 받은 2장의 합계가 17이상이면 추가로 받을 수 없다. | ||
- [x] 플레이어는 카드 숫자의 합이 21이 초과하지 않는 동안 원하는 만큼 카드를 뽑을 수 있다. | ||
- [x] 카드를 추가로 뽑아 21을 초과할 경우 배팅 금액을 모두 잃게 된다. | ||
- [x] Ace를 가진 플레이어는 에이스의 숫자값으로 1 혹은 11을 선택할 수 있다. | ||
- [x] King, Queen, Jack의 숫자값은 각각 10으로 취급한다. | ||
|
||
-[x] 출력 | ||
- [x] 게임 시작 시 각 플레이어가 받은 2장의 카드를 출력한다. | ||
- [x] 각 플레이어에게 추가로 카드를 받을 것인지 묻는다. | ||
- [x] 카드를 추가로 받으면 해당 플레이어가 가진 카드 목록을 출력한다. | ||
- [x] 딜러는 카드값의 합이 16이하면 카드를 추가로 받았다는 메시지를 출력한다. | ||
|
||
- [x] 모든 플레이어가 더이상 카드를 뽑지 않으면 게임이 종료된다. | ||
- [x] 플레이어별 카드의 목록을 출력한다. | ||
- [x] 플레이어별 카드 숫자의 합을 계산하여 결과로 출력한다. | ||
|
||
- [x] 게임을 완료한 후 각 플레이어별로 승패를 출력한다. | ||
- [x] 딜러는 각 플레이어와의 승패를 누적해서 출력한다. | ||
- [x] 플레이어는 딜러와의 승패를 출력한다. | ||
|
||
|
||
- [x] 예외 | ||
- 이름 | ||
- [x] 플레이어의 이름은 공백, null 값이 될 수 없다. | ||
- [x] 플레이어의 이름은 특수문자를 포함할 수 없다. | ||
- [x] 플레이어의 이름은 "딜러"가 될 수 없다. | ||
- [x] 플레이어는 서로 중복된 이름을 가질 수 없다. | ||
|
||
- 카드 덱 | ||
- [x] 하나의 카드덱에서는 카드를 52장 초과하여 뽑을 수 없다. | ||
|
||
- 플레이어에게 카드를 더 받을 것인지 질문 | ||
- [x] y, n가 아닌 형태로 대답할 수 없다. | ||
- [x] 대문자 Y, N도 올바른 답으로 인식한다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
domain | ||
|
||
- Card, Deck, Player, Dealer, Game, Statistics | ||
- Game : 카드 분배 역할 (카드 분배 조건도 이 객체에서 판단) | ||
- Statistics : 카드분배가 완료된 후 점수계산 및 승패 결과 도출 | ||
|
||
View | ||
|
||
- 플레이어 이름을 입력 받고 결과를 출력 | ||
|
||
Controller | ||
|
||
- 게임 전체 흐름 제어 (입력, 게임실행, 출력) | ||
|
||
### Enum | ||
|
||
1. 게임컨트롤러 (카드분배- 질문) | ||
- 답(YES, NO) | ||
|
||
2. 게임결과 (결과 승패를 판단) | ||
- 승(플레이어 점수, 딜러점수, 승(플), 패(딜러)) | ||
- 패(플레이어 점수, 딜러점수, 패(플), 승(딜러)) | ||
- 무승부(플레이어 점수, 딜러점수, 무(플,딜러)) | ||
|
||
domain | ||
|
||
- Card, Deck, Player, Dealer, Score(계산), GameResult(승패도출) | ||
- Answer(질문답), Results(승패무) | ||
|
||
View | ||
|
||
- inputview: 입력받는 것(질문에 대한 답을 받는 부분) | ||
- outputview: 출력하는 것 | ||
|
||
Controller | ||
|
||
- 카드를 분배(drawCard), 더 받을지 질문, 하나카드를 분배... | ||
- 흐름제어 | ||
|
||
# Ace 카드 처리 로직 | ||
|
||
Player -> 21 한계치 Dealer -> 17 한계치 | ||
|
||
모든 게임스코어합은 -> 21이 게임의 종료 지점 | ||
|
||
Score 계산 && 카드를 더 뽑게할지말지 | ||
|
||
- 카드의 Score가 계산로직으로 21(Player) 17(Dealer) 되기전까지 카드를 더 뽑을지 질문 | ||
|
||
## 계산로직 | ||
|
||
1. 카드 목록에 포함된 에이스 카드의 숫자를 추출한다. -> n | ||
2. n+1의 길이를 가지는 배열을 생성하고, 에이스 카드를 1로 계산한 카드합과 거기에 차례대로 10을 더한 값을 배열에 입력한다. | ||
3. 배열의 숫자들 중 21을 초과하지 않으면서 가장 큰 값을 취한다. | ||
|
||
ex) Ace, Ace, 8일 경우 int[3] = {10, 20, 30} -> 20을 숫자합으로 계산 | ||
|
||
### 게임종료 | ||
|
||
카드를 한 장 뽑을 때마다 모든 경우의 수를 계산. Ace를 1로, 혹은 11로 계산하는 경우의 수를 다 생각해서 모든 경우의 수 중 카드합의 최소값이 한계치를 넘는 경우에 카드를 그만 뽑도록. | ||
|
||
Ace 7, 9 | ||
|
||
27을 취한다. 카드를 더 못뽑음. | ||
|
||
17 | ||
|
||
Ace, 7, 9, 4 | ||
|
||
-> 21 | ||
|
||
## 카드 뽑기 | ||
|
||
<딜러> | ||
처음에 2장의 카드를 받고 카드합이 16 이하이면 한 장을 더 뽑는다. 에이스 1장은 11로 계산하고 나머지 에이스는 1로 계산한다. -> 에이스가 포함되어 있을 경우 카드합에 10을 더해준다 | ||
|
||
<에이스가 포함되어 있지 않을 경우> | ||
|
||
카드합이 16 이하이면 카드를 뽑음. | ||
|
||
<에이스가 포함되어 있는 경우> | ||
|
||
카드합에 10을 더한 값이 16 이하이면 카드를 뽑음. | ||
|
||
## 카드 뽑기 및 에이스가 여러개 포함될 가능성이 있는 카드덱의 합 계산 | ||
|
||
// 카드값의 합(ace가 11) | ||
int scoreOfAceAsEleven; // 에이스의 개수 int aceCount; // Ace카드값의 차이 int DiFFERENCE_OF_ACE_VALUE = 10; | ||
|
||
calcaluateResult(){ | ||
while (canCountAceAsOne) { | ||
카드값의 합 scoreOfAceAsEleven = scoreOfAceAsOne(scoreOfAsAsEleven); | ||
aceCount--; | ||
} | ||
|
||
return scoreOfAceAsEleven; | ||
} | ||
|
||
boolean canCountAceAsOne(int 카드값의 합, 에이스의 개수){ | ||
에이스가 하나라도 있음 > 0 && 카드 합이 21(상수선언)을 넘음(ace를 1로 취급가능)} | ||
} | ||
|
||
int scoreOfAceAsOne(int 카드값의 합){ | ||
(ace를 11로 취급한) scoreOfAceAsEleven 카드값의 합 - 10 | ||
} | ||
|
||
- 17 dealer = threshould | ||
|
||
컨트롤러에서 딜러에게 boolean Drawble(){ return calculateResult() < 17 } drawCard() | ||
|
||
- 21 player 컨트롤러에서 플레이어에게 더 받을건지 묻는 질문 boolean 플레이어의 답 Yes && Drawable() { return calculateResult() < 21 } drawCard() | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package blackjack; | ||
|
||
import blackjack.controller.BlackJackController; | ||
|
||
public class BlackjackApplication { | ||
|
||
public static void main(String[] args) { | ||
try { | ||
new BlackJackController().run(); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package blackjack.controller; | ||
|
||
import blackjack.domain.card.Deck; | ||
import blackjack.domain.participant.Dealer; | ||
import blackjack.domain.participant.Player; | ||
import blackjack.domain.participant.PlayersFactory; | ||
import blackjack.domain.result.GameResult; | ||
import blackjack.dto.DrawCardRequestDto; | ||
import blackjack.dto.PlayersNameInputDto; | ||
import blackjack.view.InputView; | ||
import blackjack.view.OutputView; | ||
|
||
import java.util.List; | ||
|
||
public class BlackJackController { | ||
private final InputView inputView = new InputView(); | ||
private final OutputView outputView = new OutputView(); | ||
|
||
public void run() { | ||
PlayersNameInputDto namesInput = inputView.getPlayersName(); | ||
Dealer dealer = new Dealer(); | ||
Deck deck = new Deck(); | ||
List<Player> players = PlayersFactory.createPlayers(namesInput.getPlayersName()); | ||
|
||
drawTowCards(dealer, players, deck); | ||
drawCardToPlayers(players, deck); | ||
drawCardToDealer(dealer, deck); | ||
outputView.printCardsResult(dealer, players); | ||
outputView.printGameResult(GameResult.of(dealer, players)); | ||
} | ||
|
||
private void drawTowCards(Dealer dealer, List<Player> players, Deck deck) { | ||
for (Player player : players) { | ||
player.receiveCard(deck.drawCard()); | ||
player.receiveCard(deck.drawCard()); | ||
} | ||
dealer.receiveCard(deck.drawCard()); | ||
dealer.receiveCard(deck.drawCard()); | ||
outputView.printFirstCardsGiven(players, dealer); | ||
outputView.printDealerCard(dealer); | ||
outputView.printPlayersCard(players); | ||
} | ||
|
||
private void drawCardToPlayers(List<Player> players, Deck deck) { | ||
for (Player player : players) { | ||
drawCardToPlayer(player, deck); | ||
} | ||
} | ||
|
||
private void drawCardToPlayer(Player player, Deck deck) { | ||
DrawCardRequestDto drawCardRequest = inputView.getPlayersResponse(player); | ||
while (player.drawable() && drawCardRequest.isYes()) { | ||
player.receiveCard(deck.drawCard()); | ||
|
||
outputView.printCards(player); | ||
drawCardRequest = inputView.getPlayersResponse(player); | ||
} | ||
} | ||
|
||
private void drawCardToDealer(Dealer dealer, Deck deck) { | ||
while (dealer.drawable()) { | ||
dealer.receiveCard(deck.drawCard()); | ||
outputView.printDealerCardGiven(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package blackjack.domain.card; | ||
|
||
import lombok.Getter; | ||
|
||
import java.util.Objects; | ||
|
||
@Getter | ||
public class Card { | ||
|
||
private final Denomination denomination; | ||
private final Type type; | ||
|
||
public Card(Denomination denomination, Type type) { | ||
this.denomination = denomination; | ||
this.type = type; | ||
} | ||
|
||
public boolean isAce() { | ||
return denomination.equals(Denomination.ACE); | ||
} | ||
|
||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (!(o instanceof Card)) { | ||
return false; | ||
} | ||
Card card = (Card) o; | ||
return denomination == card.denomination && | ||
type == card.type; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(denomination, type); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return denomination.getDenominationName() + type.getType(); | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package blackjack.domain.card; | ||
|
||
import blackjack.domain.card.strategy.DrawStrategy; | ||
import blackjack.domain.card.strategy.RandomDrawStrategy; | ||
import lombok.Getter; | ||
|
||
import java.util.LinkedList; | ||
import java.util.List; | ||
|
||
public class Deck { | ||
|
||
private static final String ALERT_NO_CARD_LEFT = "사용 가능한 카드를 모두 소진하였습니다."; | ||
|
||
@Getter | ||
private final List<Card> deck; | ||
|
||
public Deck() { | ||
this.deck = new LinkedList<>(DeckFactory.createDeck()); | ||
} | ||
|
||
public Card drawCard() { | ||
if (deck.isEmpty()) { | ||
throw new RuntimeException(ALERT_NO_CARD_LEFT); | ||
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 deck.remove(generateNextDrawCardIndex(new RandomDrawStrategy())); | ||
} | ||
|
||
private int generateNextDrawCardIndex(DrawStrategy drawStrategy) { | ||
return drawStrategy.getNextIndex(deck); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package blackjack.domain.card; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
public class DeckFactory { | ||
|
||
private static final List<Card> cardsDeck; | ||
|
||
static { | ||
cardsDeck = createCardsDeck(); | ||
} | ||
|
||
private static List<Card> createCardsDeck() { | ||
List<Card> cards = new ArrayList<>(); | ||
|
||
for (Denomination denomination : Denomination.values()) { | ||
cards.addAll(createCards(denomination)); | ||
} | ||
return cards; | ||
} | ||
|
||
private static List<Card> createCards(Denomination denomination) { | ||
List<Card> cards = new ArrayList<>(); | ||
|
||
for (Type type : Type.values()) { | ||
cards.add(new Card(denomination, type)); | ||
} | ||
|
||
return cards; | ||
} | ||
|
||
public static List<Card> createDeck() { | ||
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 Collections.unmodifiableList(cardsDeck); | ||
} | ||
} | ||
|
||
|
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.
LinkedList로 선언하신 이유는 카드를 한장씩 삭제할 때 ArrayList가 비효율적이라서 그렇게 하신걸까요? 👏
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.
넵 그렇습니다.