Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9aec21d
[1단계 - 블랙잭 게임 실행] 오잉(이하늘) 미션 제출합니다. (#415)
hanueleee Mar 8, 2023
7f42057
docs: 2단계 기능목록
hanueleee Mar 9, 2023
2041f62
feat(Player): 블랙잭인지 확인하는 기능 구현
hanueleee Mar 9, 2023
1c2cdf5
feat(Dealer): 도전자의 게임 결과를 판정하는 기능 구현
hanueleee Mar 10, 2023
baa7e32
refactor: 패키지 이동
hanueleee Mar 10, 2023
6dcdbc5
feat(GameResult): 도전자의 최종 승패 결과에 따라 수익을 계산하는 기능 구현
hanueleee Mar 10, 2023
9a91451
feat(GameResult): 딜러의 수익을 계산하는 기능 구현
hanueleee Mar 10, 2023
d1ee2d1
refactor(GameResult): 원시값 int를 포장한 Money 적용
hanueleee Mar 10, 2023
177f327
feat(InputView): 카드 추가 지급 의사 입력 기능 구현
hanueleee Mar 10, 2023
c94bf60
feat(OutputView): 최종 수익 출력 기능 구현
hanueleee Mar 10, 2023
13aab78
feat(OutputView): 최종 수익 출력 기능 구현
hanueleee Mar 10, 2023
37bf000
refactor: unmodifiableList 대신 방어적 복사 사용
hanueleee Mar 10, 2023
bae4f70
refactor: 몇몇 for를 stream으로 수정
hanueleee Mar 10, 2023
fc80dfb
refactor: 테스트 수정
hanueleee Mar 11, 2023
12f43ec
Merge branch 'step2' of https://github.com/hanueleee/java-blackjack i…
hanueleee Mar 11, 2023
a221486
refactor: TestFixture인 CardFixture 생성 및 적용
hanueleee Mar 13, 2023
2a6944d
refactor: InvalidXXX 익셉션들을 범용적인 하나의 InvalidArgumentException으로 통합
hanueleee Mar 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
### UI
- 입력
- [x] 참여할 사람의 이름 입력
- [x] 플레이어별 배팅 금액 입력
- [x] 카드 추가 지급 의사 입력

- 출력
- [x] 카드 지급 완료 문구 출력
- [x] 개인 카드 목록 출력
- [x] 딜러 카드 추가 지급 여부 출력
- [x] 최종 카드 상태 출력
- [x] 최종 수익 출력

### 기능
- BlackJackGame : 게임 관리 시스템
- [x] 모든 플레이어에게 시작 카드를 2장씩 나누어준다
- [x] 플레이어에게 카드를 1장 나누어준다
- [x] 게임 결과를 만들어 반환한다

- Player : 게임 참여자
- [x] 초기 카드 2장을 받는다
- [x] 추가 카드를 뽑는다
- [x] Bust인지 확인한다 (21이 넘는지)
- [x] BlackJack인지 확인한다 (21인지)
- Challenger : 일반 참여자 (Player를 상속받는다)
- [x] 이름을 입력받는다
- [x] 플레이어 이름이 `딜러`인 경우 예외가 발생한다
- [x] 카드의 합이 21 초과인지 확인한다
- Dealer : 딜러 (Player를 상속받는다)
- [x] 카드의 합이 16 초과인지 확인한다
- [x] 도전자의 게임 결과를 판정한다

- Players : Player 목록을 가지고 있는 일급 컬렉션
- [x] 플레이어 이름 중복 시 예외가 발생한다

- Card : 카드
- [x] 카드 숫자를 보고 점수를 반환한다 (ex. Ace -> 1 or 11 // King, Queen, Jack -> 10)

- CardDeck : 덱에 있는 카드. Card 목록을 가지고 있는 일급 컬렉션
- [x] 52장의 카드를 생성한다
- [x] 카드 순서를 무작위로 섞는다
- [x] 맨 위의 카드를 게임 참여자에게 전달한다

- HolingCards : 참여자가 소유하고 있는 카드. Card 목록을 가지고 있는 일급 컬렉션
- [x] 뽑은 카드를 저장한다
- [x] 가진 카드로 가능한 합 목록을 반환한다
- [x] 가진 카드들 중에 21보다 작은 가장 큰 값을 반환한다

- GameResult : 플레이어의 최종 수익
- [x] 도전자의 최종 승패 결과에 따라 수익을 계산한다
- [x] 딜러의 수익을 계산한다

- Money
- [x] 배팅 금액은 10000원 이상 100000원 이하여야 한다
10 changes: 10 additions & 0 deletions src/main/java/blackjack/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package blackjack;

import blackjack.controller.BlackJackController;

public class Application {
public static void main(String[] args) {
BlackJackController blackJackController = new BlackJackController();
blackJackController.run();
}
}
173 changes: 173 additions & 0 deletions src/main/java/blackjack/controller/BlackJackController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package blackjack.controller;

import blackjack.domain.card.Card;
import blackjack.domain.game.BlackJackGame;
import blackjack.domain.game.Money;
import blackjack.domain.player.Challenger;
import blackjack.domain.player.Dealer;
import blackjack.domain.player.Player;
import blackjack.domain.player.Players;
import blackjack.domain.result.GameResult;
import blackjack.dto.ChallengerResultDto;
import blackjack.dto.PlayerStatusDto;
import blackjack.exception.BlackJackGameException;
import blackjack.view.InputView;
import blackjack.view.OutputView;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class BlackJackController {

private BlackJackGame blackJackGame;

private static List<String> makeCardInfo(List<Card> inputCards) {
List<String> cardInfo = new ArrayList<>();
inputCards.stream()
.forEach(card -> cardInfo.add(card.getSymbol().getName() + card.getShape().getName()));
return cardInfo;
}

public void run() {
init();
start();
takeTurn();
showResult();
}

private void init() {
try {
List<String> playerNames = InputView.inputPlayerNames();
Players players = Players.from(playerNames);
bet(players);
} catch (BlackJackGameException e) {
OutputView.printErrorMessage(e);
init();
}
}

private void bet(Players players) {
List<Challenger> challengers = players.getChallengers();
List<Money> betAmounts = new ArrayList<>();
Copy link
Collaborator

Choose a reason for hiding this comment

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

List 말고 Map으로 관리하는건 어떻게 생각하세요?

challengers.stream()
.forEach(challenger -> betEachPlayer(challenger, betAmounts));
Copy link
Member

Choose a reason for hiding this comment

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

stream보다 for-each가 가독성이 더 좋을 것 같은데 어떻게 생각하시나요?

for (Challenger challenger: players.getChallengers()) {
    betEachPlayer(challenger, betAmounts));
}

Copy link
Author

@hanueleee hanueleee Mar 12, 2023

Choose a reason for hiding this comment

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

리뷰어께서 stream 연습삼아 만들어보라고 하셔서 수정했던 부분임미다 ,, 🦹‍♂️
근데 저도 개인적으로 for-each 가 더 가독성이 좋을 것 같다고 생각합니당

blackJackGame = BlackJackGame.from(players, betAmounts);
}

private void betEachPlayer(Challenger challenger, List<Money> betAmounts) {
try {
int inputMoney = InputView.inputPlayerBetMoney(challenger.getName());
betAmounts.add(Money.bet(inputMoney));
} catch (BlackJackGameException e) {
OutputView.printErrorMessage(e);
betEachPlayer(challenger, betAmounts);
}
}

private void start() {
blackJackGame.handOutStartCards();
showStartStatus();
}

private void showStartStatus() {
PlayerStatusDto dealerStatus = makeDealerStatus();
List<PlayerStatusDto> challengersStatus = makeChallengersStatus();
OutputView.printStartStatus(dealerStatus, challengersStatus);
}

private PlayerStatusDto makeDealerStatus() {
Player dealer = blackJackGame.getDealer();
return makePlayerStatusDto(dealer);
Copy link
Collaborator

Choose a reason for hiding this comment

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

여기에 makePlayerStatusDto가 있는것보다는, PlayerStatusDto.from() 가 더 좋지 않을까요?
컨트롤러에 있으면 메서드가 너무 많아져서 복잡해지는거같아요

}

private List<PlayerStatusDto> makeChallengersStatus() {
List<Challenger> challengers = blackJackGame.getChallengers();
return challengers.stream()
.map(challenger -> makePlayerStatusDto(challenger))
.collect(Collectors.toUnmodifiableList());
Copy link
Collaborator

Choose a reason for hiding this comment

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

굳이 toUnmodifiableList로 하신 이유가 있나요

}

private void takeTurn() {
try {
takeAllChallengersTurn();
takeDealerTurn();
} catch (BlackJackGameException e) {
OutputView.printErrorMessage(e);
}
}

private void takeAllChallengersTurn() {
for (Player player : blackJackGame.getChallengers()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

blackJackGame의 자율성을 침해하는 것 같아요

takeEachChallengerTurn(player);
}
}

private void takeEachChallengerTurn(Player player) {
if (player.canPick()) {
checkChoice(player);
}
}

private void checkChoice(Player player) {
try {
inputChoice(player);
} catch (BlackJackGameException e) {
OutputView.printErrorMessage(e);
checkChoice(player);
}
}

private void inputChoice(Player player) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

입력만 받는 줄 알았는데, hit까지 하네요?
예상하지 못했는데 inputAndHit등으로 바꾸는건 어떨까요?

boolean choice = InputView.inputPlayerChoice(player.getName());
if (choice) {
blackJackGame.hit(player);
OutputView.printChallengerStatusInGame(makePlayerStatusDto(player));
takeEachChallengerTurn(player);
}
}

private void takeDealerTurn() {
Player dealer = blackJackGame.getDealer();
boolean dealerCanPick = dealer.canPick();
Copy link
Collaborator

Choose a reason for hiding this comment

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

마찬가지로 blackJackGame의 자율성이 침해되는 것 같아요

if (dealerCanPick) {
blackJackGame.hit(dealer);
}
OutputView.printDealerTurnResult(dealerCanPick, Dealer.MAXIMUM_POINT);
}

private void showResult() {
showPoint();
showRevenue();
}

private void showPoint() {
PlayerStatusDto dealerStatus = makeDealerStatus();
Copy link
Collaborator

Choose a reason for hiding this comment

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

어디에서는 makeStatus() 어디서는 makeStatusDto()네요?
무슨차이죠?

List<PlayerStatusDto> challengersStatus = makeChallengersStatus();
OutputView.printEndStatus(dealerStatus, challengersStatus);
}

private void showRevenue() {
GameResult resultMap = blackJackGame.makeResult();

ChallengerResultDto challengerResultDto = makeChallengerResultDto(resultMap, blackJackGame.getChallengers());
OutputView.printRevenue(challengerResultDto);
}

private PlayerStatusDto makePlayerStatusDto(Player player) {
String playerName = player.getName();
List<Card> inputCards = player.getHoldingCards().getCards();
int playerPoint = player.getTotalPoint();
return new PlayerStatusDto(playerName, makeCardInfo(inputCards), playerPoint);
}
Comment on lines +158 to +163

Choose a reason for hiding this comment

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

dto로 해당 역할을 넘길 수 있을 것 같습니다!


private ChallengerResultDto makeChallengerResultDto(GameResult gameResult, List<Challenger> challengers) {
Map<String, Integer> nameAndResult = new LinkedHashMap<>();
nameAndResult.put(Dealer.NAME, gameResult.getDealerRevenue().getValue());
challengers.stream()
.forEach(challenger -> nameAndResult.put(challenger.getName(),
gameResult.getChallengerRevenue(challenger).getValue()));
return new ChallengerResultDto(nameAndResult);
}
}
47 changes: 47 additions & 0 deletions src/main/java/blackjack/domain/card/Card.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package blackjack.domain.card;

import java.util.Objects;

public class Card {

private final Shape shape;
private final Symbol symbol;

public Card(Shape shape, Symbol symbol) {
this.shape = shape;
this.symbol = symbol;
}

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 shape == card.shape && symbol == card.symbol;
}

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

public Shape getShape() {
return shape;
}

public Symbol getSymbol() {
return symbol;
}

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

import static java.util.stream.Collectors.toList;

import blackjack.exception.NoMoreCardException;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.List;

public class CardDeck {

private final Deque<Card> cards;

private CardDeck(Deque<Card> cards) {
this.cards = cards;
}

public static CardDeck create() {
List<Card> cards = Arrays.stream(Shape.values())
.flatMap(shape -> Arrays.stream(Symbol.values()).map(symbol -> new Card(shape, symbol)))
.collect(toList());
Collections.shuffle(cards);
return new CardDeck(new ArrayDeque<>(cards));
}

public Card pick() {
validateCardExist();
return cards.remove();
}

private void validateCardExist() {
if (cards.isEmpty()) {
throw new NoMoreCardException();
}
}
}
47 changes: 47 additions & 0 deletions src/main/java/blackjack/domain/card/HoldingCards.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package blackjack.domain.card;

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

public class HoldingCards {

private static final int MAXIMUM_SUM = 21;
private static final int ACE_BONUS = 10;

private final List<Card> cards = new ArrayList<>();

public void initialize(Card firstCard, Card secondCard) {
cards.addAll(List.of(firstCard, secondCard));
}

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

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

public int getSum() {
int aceCount = getAceCount();
int pointSum = getNotAceSum() + aceCount;
while (pointSum + ACE_BONUS <= MAXIMUM_SUM && aceCount > 0) {
pointSum += ACE_BONUS;
aceCount--;
}
return pointSum;
}

private int getNotAceSum() {
return cards.stream()
.filter(card -> !card.isAce())
.mapToInt(Card::getPoint)
.sum();
}

private int getAceCount() {
return (int) cards.stream()
.filter(Card::isAce)
.count();
}
}
18 changes: 18 additions & 0 deletions src/main/java/blackjack/domain/card/Shape.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package blackjack.domain.card;

public enum Shape {
SPADE("스페이드"),
HEART("하트"),
CLOVER("클로버"),
DIAMOND("다이아몬드");

private final String name;

Shape(String name) {
this.name = name;
}

public String getName() {
return name;
}
}
Loading