-
Notifications
You must be signed in to change notification settings - Fork 0
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
[알트] 블랙잭 미션 제출합니다. #2
base: tdd-kskim
Are you sure you want to change the base?
Changes from all commits
b6dc60d
a263660
932a06b
7972852
5ada5bc
af73204
8fb0521
99cf6c3
aba0e56
a288891
6455aca
cf4a75c
0241827
8479b88
83f5847
83bc73d
9549275
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,5 +1,39 @@ | ||
# java-blackjack | ||
블랙잭 게임 미션 저장소 | ||
|
||
## 우아한테크코스 코드리뷰 | ||
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) | ||
## 기능 목록 | ||
* [X] 게임에 참여할 사람의 이름을 입력받는다. | ||
* [X] 참여자의 이름은 1 ~ 10자로 구성된다. | ||
* [X] 참여자의 이름 앞, 뒤에 오는 공백은 무시한다. | ||
* [X] 참여자의 이름은 comma(,) 단위로 구분한다. | ||
* [X] 참여자의 수는 2 ~ 8명이다. | ||
* [X] 각 참여자의 베팅 금액을 입력받는다. | ||
* [X] 베팅 금액의 최소 단위는 100으로 제한한다. | ||
* [X] 딜러와 각 참여자에게 카드를 두 장씩 분배한다. | ||
* [X] 각 참여자에게 나누어 준 카드를 출력한다. | ||
* [X] 딜러는 첫 번째 카드를 제외하고 한 장의 카드만 공개한다. | ||
* [X] 각 참여자는 두 장의 카드를 공개한다. | ||
* 딜러가 블랙잭인지 여부를 판별하고, 블랙잭이면 결과를 출력한다. | ||
* [X] 참여자가 블랙잭이라면 무승부이다. | ||
* [X] 참여자가 블랙잭이 아니라면 딜러의 승리이다. | ||
* 참여자 히트 | ||
* [X] 참여자가 블랙잭, 버스트가 아니라면 추가적으로 카드를 히트할지 여부를 입력받는다. | ||
* [X] 참여자가 스테이하면 다음 사람 턴으로 넘어간다. | ||
* [X] 참여자가 히트하면 카드 발급 후 카드를 출력한다. | ||
* 딜러 히트 | ||
* [X] 딜러는 가진 패의 합이 16 이하라면 반드시 히트해야 한다. | ||
* [X] 딜러와 참여자가 받은 모든 패를 공개하고, 결과를 출력한다. | ||
* [X] 각 참여자의 최종 수익을 출력한다. | ||
|
||
## 게임 조건 | ||
* 두 장의 패를 뽑아 합이 21인 경우 블랙잭이다. | ||
* 카드의 합이 21을 초과하는 경우 버스트이다. | ||
* 각 ACE는 1 또는 11로 계산할 수 있다. | ||
* 참여자가 버스트가 되면 딜러의 버스트 여부와 상관없이 참여자가 패배한다. | ||
|
||
### 수익 계산 | ||
* 무승부인 경우 베팅한 금액을 돌려받아 수익이 없다. | ||
* 참여자가 승리한 경우 | ||
* 블랙잭이 아니라면 베팅 금액만큼 수익을 얻는다. | ||
* 블랙잭이라면 베팅 금액의 1.5배의 수익을 얻는다. | ||
* 참여자가 패배하면 베팅한 모든 금액을 잃는다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import java.util.Scanner; | ||
|
||
import controller.GameController; | ||
import service.DrawService; | ||
import service.PlayerService; | ||
import service.ProfitService; | ||
import view.InputView; | ||
import view.OutputView; | ||
|
||
public class BlackjackApplication { | ||
private final GameController gameController; | ||
|
||
public BlackjackApplication(final GameController gameController) { | ||
this.gameController = gameController; | ||
} | ||
|
||
public static void main(String[] args) { | ||
Scanner scanner = new Scanner(System.in); | ||
InputView inputView = new InputView(scanner); | ||
OutputView outputView = new OutputView(); | ||
PlayerService playerService = new PlayerService(); | ||
DrawService drawService = new DrawService(); | ||
ProfitService profitService = new ProfitService(); | ||
|
||
GameController gameController = new GameController(inputView, outputView, playerService, drawService, | ||
profitService); | ||
BlackjackApplication blackjackApplication = new BlackjackApplication(gameController); | ||
|
||
blackjackApplication.run(); | ||
} | ||
|
||
public void run() { | ||
try { | ||
gameController.run(); | ||
} catch (Exception exception) { | ||
System.err.println(exception.getMessage()); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,87 @@ | ||||||||||||||||||||
package controller; | ||||||||||||||||||||
|
||||||||||||||||||||
import java.util.ArrayList; | ||||||||||||||||||||
import java.util.List; | ||||||||||||||||||||
|
||||||||||||||||||||
import domain.BettingMoney; | ||||||||||||||||||||
import domain.Name; | ||||||||||||||||||||
import domain.card.CardDeck; | ||||||||||||||||||||
import domain.card.CardDeckFactory; | ||||||||||||||||||||
import domain.participant.Dealer; | ||||||||||||||||||||
import domain.participant.Player; | ||||||||||||||||||||
import domain.result.ParticipantProfit; | ||||||||||||||||||||
import service.DrawService; | ||||||||||||||||||||
import service.PlayerService; | ||||||||||||||||||||
import service.ProfitService; | ||||||||||||||||||||
import view.InputView; | ||||||||||||||||||||
import view.OutputView; | ||||||||||||||||||||
|
||||||||||||||||||||
public class GameController { | ||||||||||||||||||||
private final InputView inputView; | ||||||||||||||||||||
private final OutputView outputView; | ||||||||||||||||||||
private final PlayerService playerService; | ||||||||||||||||||||
private final DrawService drawService; | ||||||||||||||||||||
private final ProfitService profitService; | ||||||||||||||||||||
|
||||||||||||||||||||
public GameController(final InputView inputView, final OutputView outputView, final PlayerService playerService, | ||||||||||||||||||||
final DrawService drawService, final ProfitService profitService) { | ||||||||||||||||||||
this.inputView = inputView; | ||||||||||||||||||||
this.outputView = outputView; | ||||||||||||||||||||
this.playerService = playerService; | ||||||||||||||||||||
this.drawService = drawService; | ||||||||||||||||||||
this.profitService = profitService; | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
public void run() { | ||||||||||||||||||||
CardDeck cardDeck = CardDeckFactory.createRandomCardDeck(); | ||||||||||||||||||||
Dealer dealer = new Dealer(); | ||||||||||||||||||||
List<Player> players = createPlayers(); | ||||||||||||||||||||
|
||||||||||||||||||||
drawCards(cardDeck, dealer, players); | ||||||||||||||||||||
|
||||||||||||||||||||
calculateProfit(dealer, players); | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
private List<Player> createPlayers() { | ||||||||||||||||||||
String namesWithComma = inputView.inputNames(); | ||||||||||||||||||||
List<Name> names = Name.fromComma(namesWithComma); | ||||||||||||||||||||
List<BettingMoney> bettingMonies = new ArrayList<>(); | ||||||||||||||||||||
for (final Name name : names) { | ||||||||||||||||||||
int money = inputView.inputBettingMoney(name); | ||||||||||||||||||||
BettingMoney bettingMoney = new BettingMoney(money); | ||||||||||||||||||||
bettingMonies.add(bettingMoney); | ||||||||||||||||||||
} | ||||||||||||||||||||
return playerService.createPlayers(names, bettingMonies); | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
private void drawCards(final CardDeck cardDeck, final Dealer dealer, final List<Player> players) { | ||||||||||||||||||||
drawService.drawInitialCards(cardDeck, dealer, players); | ||||||||||||||||||||
outputView.printHandsAtFirst(dealer, players); | ||||||||||||||||||||
|
||||||||||||||||||||
for (final Player player : players) { | ||||||||||||||||||||
drawCardToPlayer(cardDeck, player); | ||||||||||||||||||||
} | ||||||||||||||||||||
drawCardToDealer(cardDeck, dealer); | ||||||||||||||||||||
} | ||||||||||||||||||||
Comment on lines
+61
to
+65
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.
Suggested change
AllPlayers라는 객체가 있으면, 상속을 극대화하여 사용할 수 있을 것 같은데요 |
||||||||||||||||||||
|
||||||||||||||||||||
private void calculateProfit(final Dealer dealer, final List<Player> players) { | ||||||||||||||||||||
outputView.printHandsAtLast(dealer, players); | ||||||||||||||||||||
List<ParticipantProfit> participantProfits = profitService.calculateProfits(dealer, players); | ||||||||||||||||||||
outputView.printProfits(participantProfits); | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
private void drawCardToDealer(final CardDeck cardDeck, final Dealer dealer) { | ||||||||||||||||||||
while (!dealer.isFinished()) { | ||||||||||||||||||||
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.
|
||||||||||||||||||||
drawService.drawDealer(cardDeck, dealer); | ||||||||||||||||||||
outputView.printDealerHitMessage(); | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
private void drawCardToPlayer(final CardDeck cardDeck, final Player player) { | ||||||||||||||||||||
while (!player.isFinished()) { | ||||||||||||||||||||
String decision = inputView.inputPlayerDecision(player); | ||||||||||||||||||||
drawService.drawPlayer(cardDeck, player, decision); | ||||||||||||||||||||
outputView.printHand(player); | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package domain; | ||
|
||
import java.math.BigDecimal; | ||
|
||
public class BettingMoney { | ||
private static final int MIN_AMOUNT = 100; | ||
private static final int BETTING_UNIT = 100; | ||
|
||
private final BigDecimal amount; | ||
|
||
public BettingMoney(final int amount) { | ||
validate(amount); | ||
this.amount = BigDecimal.valueOf(amount); | ||
} | ||
|
||
private void validate(final int amount) { | ||
validateRange(amount); | ||
validateUnit(amount); | ||
} | ||
|
||
private void validateRange(final int amount) { | ||
if (amount < MIN_AMOUNT) { | ||
throw new IllegalArgumentException("베팅 최소 금액을 충족하지 못했습니다.\n" | ||
+ "amount: " + amount); | ||
} | ||
} | ||
|
||
private void validateUnit(final int amount) { | ||
if (amount % BETTING_UNIT != 0) { | ||
throw new IllegalArgumentException("금액의 단위가 올바르지 않습니다.\n" | ||
+ "amount: " + amount); | ||
} | ||
} | ||
|
||
public BigDecimal getAmount() { | ||
return amount; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package domain; | ||
|
||
import static java.util.stream.Collectors.toList; | ||
|
||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.stream.Stream; | ||
|
||
public class Name { | ||
private static final int MAX_LENGTH = 10; | ||
private static final int MIN_LENGTH = 1; | ||
private static final String NAME_DELIMITER = ","; | ||
|
||
private final String name; | ||
|
||
public Name(String name) { | ||
Objects.requireNonNull(name, "이름이 null입니다."); | ||
name = name.trim(); | ||
validateLength(name); | ||
this.name = name; | ||
} | ||
|
||
public static List<Name> fromComma(final String names) { | ||
return Stream.of(names.split(NAME_DELIMITER)) | ||
.map(Name::new) | ||
.collect(toList()); | ||
} | ||
Comment on lines
+16
to
+27
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 void validateLength(final String name) { | ||
int length = name.length(); | ||
if (length < MIN_LENGTH || length > MAX_LENGTH) { | ||
throw new IllegalArgumentException("이름의 길이가 올바르지 않습니다.\n" | ||
+ "name: " + name); | ||
} | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package domain.card; | ||
|
||
import static java.util.stream.Collectors.toList; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.stream.Stream; | ||
|
||
public class Card { | ||
private final Face face; | ||
private final Suit suit; | ||
|
||
private Card(final Face face, final Suit suit) { | ||
this.face = face; | ||
this.suit = suit; | ||
} | ||
|
||
public static Card fromFaceAndSuit(final Face face, final Suit suit) { | ||
return CardCache.cache | ||
.stream() | ||
.filter(card -> card.isCardOf(face, suit)) | ||
.findFirst() | ||
.orElseThrow(() -> new CardNotFoundException("카드가 존재하지 않습니다.\n" | ||
+ "face: " + face + "\n" | ||
+ "suit: " + suit)); | ||
} | ||
|
||
public static List<Card> values() { | ||
return Collections.unmodifiableList(CardCache.cache); | ||
} | ||
|
||
public boolean isAce() { | ||
return face.isAce(); | ||
} | ||
|
||
public boolean isCardOf(final Face face, final Suit suit) { | ||
return this.face == face && this.suit == suit; | ||
} | ||
|
||
public Face getFace() { | ||
return face; | ||
} | ||
|
||
public Suit getSuit() { | ||
return suit; | ||
} | ||
|
||
public String alias() { | ||
return suit.alias() + face.alias(); | ||
} | ||
|
||
private static class CardCache { | ||
public static final List<Card> cache; | ||
|
||
static { | ||
cache = Stream.of(Face.values()) | ||
.flatMap(CardCache::createByFace) | ||
.collect(toList()); | ||
} | ||
|
||
private static Stream<Card> createByFace(final Face face) { | ||
return Stream.of(Suit.values()) | ||
.map(suit -> new Card(face, suit)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package domain.card; | ||
|
||
public interface CardDeck { | ||
Card pick(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package domain.card; | ||
|
||
public class CardDeckFactory { | ||
private CardDeckFactory() { | ||
throw new AssertionError(); | ||
} | ||
|
||
public static CardDeck createRandomCardDeck() { | ||
return new RandomCardDeck(Card.values()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package domain.card; | ||
|
||
public class CardNotFoundException extends RuntimeException { | ||
public CardNotFoundException(final String message) { | ||
super(message); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package domain.card; | ||
|
||
public class EmptyCardDeckException extends RuntimeException { | ||
public EmptyCardDeckException(final String message) { | ||
super(message); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package domain.card; | ||
|
||
public enum Face { | ||
ACE(1, "A"), | ||
TWO(2), | ||
THREE(3), | ||
FOUR(4), | ||
FIVE(5), | ||
SIX(6), | ||
SEVEN(7), | ||
EIGHT(8), | ||
NINE(9), | ||
TEN(10), | ||
JACK(10, "J"), | ||
QUEEN(10, "Q"), | ||
KING(10, "K"); | ||
|
||
private final int score; | ||
private final String alias; | ||
|
||
Face(final int score) { | ||
this.score = score; | ||
this.alias = String.valueOf(score); | ||
} | ||
|
||
Face(final int score, final String alias) { | ||
this.score = score; | ||
this.alias = alias; | ||
} | ||
|
||
public boolean isAce() { | ||
return this == ACE; | ||
} | ||
|
||
public int getScore() { | ||
return score; | ||
} | ||
|
||
public String alias() { | ||
return alias; | ||
} | ||
} |
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와 List를 매치시켜서 createPlayers() 해주고 있는데요. 각 List의 요소가 1:1로 매치되는데, Map자료구조를 활용하는건 어떨까요?