Skip to content
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

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
# java-blackjack
블랙잭 게임 미션 저장소

## 요구사항
+ [x] 플레이어 이름 입력
+ [x] , 기준으로 분리
+ [x] 미입력, 공백 입력 등 예외처리
+ [x] 배팅 금액 입력
+ [x] 모든 플레이어의 배팅 금액 입력
+ [x] 미입력, 문자입력, 0 이하 입력 등 예외처리
+ [x] 딜러, 플레이어 모두에게 무작위의 2장의 카드 지급
+ [x] 나누어준 카드 출력
+ [x] 딜러는 1장만 출력, 플레이어는 2장 모두 출력
+ [x] 각 플레이어의 카드 합이 21이하인 경우 카드 더 받을지 질문
+ [x] 플레이어 마다 진행, n 입력 혹은 합이 21초과인경우 중지
+ [x] y(Y),n(N) 외의 입력 예외처리
+ [x] y 입력한 경우 무작위의 카드 장한 더 지
+ [x] 카드를 받은 플레이어의 모든 카드 출력
+ [x] 딜러의 경우 16 이하인 경우 카드 한 장 더 지급
+ [x] 딜러와 플레이어의 카드, 점수 합 출력
+ [x] 카드 계산 구현
+ [x] 카드의 숫자 계산은 카드 숫자를 기본으로 함.
+ [x] 예외로 Ace는 1 또는 11로 계산 가능
+ [x] ace 제외 카드 합 <= 10 인 경우 ace 는 11로 계산
+ [x] King, Queen, Jack은 각각 10
+ [x] 수익 금액 구현
+ [x] 플레이어의 카드 합만 21 인 경우(블랙잭) 수익은 배팅금액 * 1.5
+ [x] 딜러와 플레이어 모두 블랙잭인 경우 수익은 배팅금액 * 1
+ [x] 합이 21 초과, 지는 경우면 수익은 배팅금액 * -1
+ [x] 21 이하이면서 21에 가까운 경우 수익은 배팅금액 * 1
+ [x] 플레이어와 딜러 무승부인 경우(모두 21 초과, 동점)인 경우 수익은 0


## 우아한테크코스 코드리뷰
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
11 changes: 11 additions & 0 deletions src/main/java/BlackJackApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import controller.Controller;
import view.InputView;
import view.OutputView;

public class BlackJackApplication {

public static void main(String[] args) {
Controller controller = new Controller(new InputView(), new OutputView());
controller.run();
}
}
78 changes: 78 additions & 0 deletions src/main/java/controller/Controller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package controller;

import domain.Game;
import domain.card.Deck;
import domain.money.EarningMoney;
import domain.user.*;
import view.InputView;
import view.OutputView;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Controller {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떤 역할을 하는 컨트롤러인지 명시적인 네이밍을 주는게 좋을 것 같아요.


private final InputView inputView;
private final OutputView outputView;
private Players players;
private Dealer dealer;

public Controller(final InputView inputView, final OutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
}

public void run() {
Game game = createGame();
play(game);
calculateResult();
}

private Game createGame() {
List<Name> names = inputView.inputNames()
.stream()
.map(Name::new)
.collect(Collectors.toList());
Map<Name, Integer> inputs = inputView.inputBettingMonies(names);
players = PlayerFactory.createPlayers(inputs);
dealer = new Dealer();
return new Game(new Deck());
}

private void play(Game game) {
game.drawFirst(players, dealer);
outputView.printFirstCards(dealer, players.getPlayers());
playPlayers(game);
playDealer(game);
}

private void playPlayers(Game game) {
for (Player player : players.getPlayers()) {
playPlayer(player, game);
}
}

private void playPlayer(Player player, Game game) {
while (player.canDrawCard() && inputView.inputWantMoreCard(player)) {
game.draw(player);
outputView.printCards(player);
}
if (player.canDrawCard()) {
player.stay();
}
}

private void playDealer(Game game) {
if (dealer.canDrawCard()) {
outputView.printDealerHit();
game.draw(dealer);
}
}

private void calculateResult() {
EarningMoney earningMoney = new EarningMoney();
outputView.printResultCards(dealer, players);
outputView.printEarningMoney(EarningMoney.calculateMoney(dealer, players));
}
}
33 changes: 33 additions & 0 deletions src/main/java/domain/Game.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package domain;

import domain.card.Card;
import domain.card.Deck;
import domain.user.Dealer;
import domain.user.Participant;
import domain.user.Players;

import java.util.ArrayList;
import java.util.List;

public class Game {
public static final int FIRST_DRAW_NUMBER = 2;

private Deck deck;

public Game(Deck deck) {
this.deck = deck;
}

public void drawFirst(Players players, Dealer dealer) {
List<Card> drawCards = new ArrayList<>();
for (int index = 0; index < players.size() * FIRST_DRAW_NUMBER; index++) {
drawCards.add(deck.deal());
}
players.firstDraw(drawCards);
dealer.drawFirst(deck.deal(), deck.deal());
Comment on lines +22 to +27
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Game 클래스가 Deck에서 모든 카드를 꺼내 Players에 전달해줘야 할까요? PlayersDeck간의 협력 관계를 통해 풀어갈 수 있는 부분이라고 생각합니다.

}
Comment on lines +21 to +28
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public void drawFirst(Players players, Dealer dealer) {
List<Card> drawCards = new ArrayList<>();
for (int index = 0; index < players.size() * FIRST_DRAW_NUMBER; index++) {
drawCards.add(deck.deal());
}
players.firstDraw(drawCards);
dealer.drawFirst(deck.deal(), deck.deal());
}
public void drawFirst(Players players) {
List<Card> drawCards = new ArrayList<>();
for (int index = 0; index < players.size() * FIRST_DRAW_NUMBER; index++) {
drawCards.add(deck.deal());
}
players.firstDraw(drawCards);
}

Players에 딜러도 포함된다면 깔끔한 코드를 가져갈 수 있을 것 같아요.


public void draw(Participant participant) {
participant.draw(deck.deal());
}
}
41 changes: 41 additions & 0 deletions src/main/java/domain/card/Card.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package domain.card;

import java.util.Objects;

public class Card {
private final Symbol symbol;

private final Type type;
Comment on lines +6 to +8
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

별건 아니지만,, 둘이 사이가 안좋나요..?


public Card(Symbol symbol, Type type) {
this.symbol = symbol;
this.type = type;
}

public boolean isAce() {
return symbol == Symbol.ACE;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Card card = (Card) o;
return symbol == card.symbol &&
type == card.type;
}

@Override
public int hashCode() {
return Objects.hash(symbol, type);
}

@Override
public String toString() {
return symbol + " " + type;
}

public int getScore() {
return symbol.getScore();
}
}
23 changes: 23 additions & 0 deletions src/main/java/domain/card/CardFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package domain.card;

import java.util.ArrayList;
import java.util.List;

public class CardFactory {

public static List<Card> create() {
List<Card> cards = new ArrayList<>();
Symbol[] symbols = Symbol.values();
for (Symbol symbol : symbols) {
createByType(cards, symbol);
}
return cards;
}

private static void createByType(final List<Card> cards, final Symbol symbol) {
Type[] types = Type.values();
for (Type type : types) {
cards.add(new Card(symbol, type));
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/domain/card/Deck.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package domain.card;

import java.util.Collections;
import java.util.List;

public class Deck {
private final List<Card> cards;

public Deck() {
cards = CardFactory.create();
Collections.shuffle(cards);
}

public Card deal() {
return cards.remove(0);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CardFactory를 보니 자료구조가 ArrayList네요.
0번째 인덱스 값을 지운다면 deal 메서드가 O(N)의 시간복잡도를 가지게 될 것 같은데, 아래와 같이 바꿔보는건 어떨까요?

Suggested change
return cards.remove(0);
return cards.remove(cards.size() - 1);

}
}
71 changes: 71 additions & 0 deletions src/main/java/domain/card/Hands.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package domain.card;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static domain.Game.FIRST_DRAW_NUMBER;

public class Hands {
Copy link

@KS-KIM KS-KIM Jul 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

블랙잭에서 한 명의 플레이어가 가지는 패를 Hand라고 부릅니다. 일급 컬렉션이라서 복수형 네이밍을 선택하신 것 같은데, Hand가 이미 복수형의 의미를 내포하고 있으니 아래와 같이 바꾸는게 좋지 않을까요?

Suggested change
public class Hands {
public class Hand {

40 CARDS [countable]
a) the playing cards given to one person in a game

참고: https://www.ldoceonline.com/ko/dictionary/hand

public static final int TEN = 10;
public static final int ELEVEN = 11;
public static final int BLACKJACK = 21;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

21이 블랙잭이냐? 라고 했을 때 아닐수도 있다고 생각되는 네이밍이네요.

public static final int DEALER_BUST_NUMBER = 17;
Copy link
Member

@hongbin-dev hongbin-dev Jul 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public static final int DEALER_BUST_NUMBER = 17; 딜러의 책임 아닐까요?

Copy link

@KS-KIM KS-KIM Jul 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

17이 딜러가 버스트 하는 기준인가요? 제가 알고있는 바로는 딜러가 추가적인 패를 드로우할지 여부를 결정하는 기준입니다. 의미가 잘못 전달될 여지가 있으니 네이밍을 바꿔보는게 어떨까요?


private final List<Card> cards;

public Hands() {
this.cards = new ArrayList<>();
}

public List<Card> getCards() {
return Collections.unmodifiableList(cards);
}

public void add(final Card card) {
cards.add(card);
}

public boolean isBust() {
return sum() > BLACKJACK;
}

public int sum() {
int sum = cards.stream()
.mapToInt(Card::getScore)
.sum();
if (isAceNotExist() || isAceOne(sum)) {
return sum;
}
return sum + TEN;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ace를 1 또는 11이 아니라 1 또는 20으로 볼 수 있다면? TEN이라는 네이밍은 변경에 유연하지 못할 것 같아요. 이름을 다시 고민해보는게 어떨까요?

}

private boolean isAceOne(int sum) {
return isAceExist() && sum > ELEVEN;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

버스트 기준이 25로 바뀐다면 ELEVEN의 값은 바뀌지 않을까요? 보다 유연한 네이밍을 하는게 좋을 것 같아요.

}

private boolean isAceExist() {
return cards.stream()
.anyMatch(Card::isAce);
}

private boolean isAceNotExist() {
return !isAceExist();
}

public boolean isBlackjack() {
return cards.size() == FIRST_DRAW_NUMBER && sum() == BLACKJACK;
}

public boolean isDealerBust() {
return sum() >= DEALER_BUST_NUMBER;
}
Comment on lines +60 to +62
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

딜러도 버스트는 21이 아닐까요..?


public boolean isBiggerThan(Hands hands) {
return sum() > hands.sum();
}

public boolean isSame(Hands hands) {
return sum() == hands.sum();
}
}
27 changes: 27 additions & 0 deletions src/main/java/domain/card/Symbol.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package domain.card;

public enum Symbol {
ACE(1),
TWO(2),
THREE(3),
FOUR(4),
FIVE(5),
SIX(6),
SEVEN(7),
EIGHT(8),
NINE(9),
TEN(10),
JACK(10),
QUEEN(10),
KING(10);

private final int score;

Symbol(final int score) {
this.score = score;
}

public int getScore() {
return score;
}
}
8 changes: 8 additions & 0 deletions src/main/java/domain/card/Type.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package domain.card;

public enum Type {
SPADE,
DIAMOND,
HEART,
CLUB
}
23 changes: 23 additions & 0 deletions src/main/java/domain/money/BettingMoney.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package domain.money;

public class BettingMoney {

public static final int MIN_BETTING_MONEY = 1;
Copy link

@KS-KIM KS-KIM Jul 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

블랙잭이 가지는 earningRate가 1.5인데 최소 금액을 1로 설정해도 괜찮을까요? 1.5배를 적용했을 때 profit이 소수점 단위로 나오겠네요. 금액에 관한 부분은 무엇보다 정확도가 중요하니 입력할 수 있는 금액의 단위를 조정하는 것을 고려해보아요:)


private final int bettingMoney;

public BettingMoney(final int bettingMoney) {
validate(bettingMoney);
this.bettingMoney = bettingMoney;
}

private void validate(final int bettingMoney) {
if (bettingMoney < MIN_BETTING_MONEY) {
throw new IllegalArgumentException("배팅금액은 1원 이상이어야 합니다. 입력금액 : " + bettingMoney);
}
}

public int getBettingMoney() {
return bettingMoney;
}
}
Loading