Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
527b4a7
[1단계 - 블랙잭 게임 실행] 망쵸(김주환) 미션 제출합니다. (#628)
3Juhwan Mar 13, 2024
f8cbf8c
docs: 베팅 관련 설계 추가
3Juhwan Mar 14, 2024
33f0fd0
feat: Balance 클래스 추가
3Juhwan Mar 14, 2024
03f3088
feat: Account 클래스에 입금 기능 구현
3Juhwan Mar 14, 2024
b7a91f8
feat: Account 클래스에 출금 기능 구현
3Juhwan Mar 14, 2024
7bb0388
rename: 돈의 단위를 Cash로 변경
3Juhwan Mar 14, 2024
d945c06
docs: 구현 사항 반영
3Juhwan Mar 14, 2024
32bbfcf
refactor: Account 클래스를 불변으로 변경
3Juhwan Mar 14, 2024
d499faa
feat: 플레이어에게 돈을 송금하는 기능 구현
3Juhwan Mar 14, 2024
edbc94e
feat: Casino로부터 Bank 분리
3Juhwan Mar 14, 2024
5a621b6
chore: 베팅 관련 클래스 삭제
3Juhwan Mar 14, 2024
7efa945
refactor: 베팅 관련 클래스 삭제
3Juhwan Mar 14, 2024
62a151d
feat: Money 클래스 생성
3Juhwan Mar 14, 2024
a733953
refactor: Money 클래스 매개변수로 문자열을 받는 생성자 삭제
3Juhwan Mar 14, 2024
3b49118
refactor: 매개변수를 Player에서 Name로 변경
3Juhwan Mar 14, 2024
56b15a0
feat: Profit 도메인 추가
3Juhwan Mar 15, 2024
100a87d
test: Profit 테스트 추가
3Juhwan Mar 15, 2024
5f0ba0d
feat: Profit과 Name에 대한 일급 컬렉션 추가
3Juhwan Mar 15, 2024
aeb4769
style: 생성자 삭제 및 범위 수정
3Juhwan Mar 15, 2024
54cd23c
feat: 이름과 베팅 금액을 묶은 일급 컬렉션 추가
3Juhwan Mar 15, 2024
38e2509
feat: 베팅 로직 추가
3Juhwan Mar 15, 2024
4cf5604
feat: 블랙잭이 되는 조건 로직 추가
3Juhwan Mar 15, 2024
51f13fe
refactor: Money 관련 연산을 객체 내부로 옮김
3Juhwan Mar 15, 2024
c7defd3
refactor: Money를 record로 변환
3Juhwan Mar 15, 2024
5d120a9
refactor: view에 final 키워드 삭제
3Juhwan Mar 15, 2024
48e3afc
refactor: Player와 Dealer에 대한 클래스 구조 변경
3Juhwan Mar 15, 2024
bf37154
refactor: 스트림 컬렉터 적용
3Juhwan Mar 15, 2024
565494d
refactor: 잘못된 변수명 수정
3Juhwan Mar 15, 2024
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
85 changes: 81 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,84 @@
# java-blackjack
# 블랙잭

블랙잭 미션 저장소

## 우아한테크코스 코드리뷰
## 기능 요구 사항

- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
### 플레이어 이름

### 플레이어

- [X] 플레이어는 이름을 갖는다
- [X] 플레이어는 카드들을 갖는다
- [X] 플레이어는 카드를 저장한다
- [X] 죽었는지 안 죽었는지 여부를 반환한다 (21을 초과하는지)


### 카드

- [X] 문자와 모양을 상태로 갖는다


### Hand(카드들)

- [X] 여러개의 카드를 갖는다
- [X] 카드의 총합 계산한다
- [X] 카드 추가한다


### 카드 게임

- [X] 각 플레이어에게 카드를 2장씩 지급한다
- [X] 플레이어마다 추가 지급한다


### 덱

- [X] 모든 카드를 1장씩 갖고 있다.
- [X] 랜덤으로 카드를 뽑는다.


### 게임 승패 결정

- [X] 딜러와 모든 플레이어의 승패 여부를 결정한다.
- [X] 딜러와 플레이어 둘다 21을 초과할 경우, 플레이어가 패배한다.
- [X] 카드 합계가 딜러는 21 이하, 플레이어는 21 초과인 경우, 플레이어가 패배한다.
- [X] 카드 합계가 딜러는 21 초과, 플레이어는 21 이하인 경우, 플레이어가 승리한다.
- [X] 카드 합계가 딜러와 플레이어 모두 21 이하인 경우, 숫자가 큰 사람이 승리한다.
- [X] 카드 합계가 딜러와 플레이어 모두 21 이하이고 동일한 경우, 무승부다.


### 인풋 뷰

- [X] 참가자의 이름을 입력받는다.
- [X] 이름은 쉼표 기준으로 분리한다.
- [X] 카드 추가 여부를 입력받는다.
- [X] y 또는 n이 아닌 경우, 예외를 발생한다.
- [X] 사용자 카드 합이 21을 초과하면, 카드 추가 여부를 묻지 않는다.
- [X] 플레이어가 n을 입력할 때까지 카드 추가 여부를 묻는다.


### 아웃풋 뷰

- [X] 딜러와 플레이어 전원에게 초기에 분배한 카드 출력한다.
- [X] 딜러의 카드는 1장만 공개한다.
- [X] 플레이어가 보유한 모든 카드를 출력한다.
- [X] 딜러가 추가 카드를 발급 받았는지 여부 출력한다.
- [X] 보유한 모든 카드의 합을 출력한다.
- [X] 최종 승패를 출력한다.


## 베팅

### 카지노

- [x] 은행과 유사한 역할로 플레이어의 계좌를 관리한다.
- [x] 계좌는 사용자 이름으로 맵핑한다.
- [x] 승패 결과에 따라 송금을 진행한다.
- [x] 플레이어의 잔고를 조회한다.


### 잔고

- [x] 사용자의 돈을 저장한다.
- [x] 입금할 수 있다.
- [x] 출금할 수 있다.
16 changes: 16 additions & 0 deletions src/main/java/blackjack/BlackjackApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package blackjack;

import blackjack.view.InputView;
import blackjack.view.MessageResolver;
import blackjack.view.OutputView;

public class BlackjackApplication {

public static void main(String[] args) {
final InputView inputView = new InputView();
final OutputView outputView = new OutputView(new MessageResolver());

final BlackjackController blackjackController = new BlackjackController(inputView, outputView);
blackjackController.run();
}
}
91 changes: 91 additions & 0 deletions src/main/java/blackjack/BlackjackController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package blackjack;

import blackjack.domain.betting.BetDetails;
import blackjack.domain.betting.ProfitDetails;
import blackjack.domain.cardgame.CardDeck;
import blackjack.domain.cardgame.CardGameResult;
import blackjack.domain.player.Dealer;
import blackjack.domain.player.Name;
import blackjack.domain.player.Participant;
import blackjack.domain.player.Player;
import blackjack.view.InputView;
import blackjack.view.OutputView;

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

public class BlackjackController {
Copy link
Member

Choose a reason for hiding this comment

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

Controller란, 어떤 요청이 들어왔을 때 그 요청을 처리할 수 있는 model 에게 그 요청의 처리를 위임하고, 처리 결과를 View에 전달하는 것을 말합니다. 하지만, 우리 미션에서는 요청을 표현하는 것이 없습니다. 그저 프로그램을 실행하면 알아서 동작을 하기 때문에 Controller라는 이름은 부적절하다고 생각합니다!

Copy link
Author

Choose a reason for hiding this comment

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

로빈의 controller 정의를 통해 새로운 인사이트를 얻습니다 👍
현재 미션에서 적용하는 MVC 패턴에서 Controller의 역할은 Model과 View를 이어주는 다리 역할이라고 생각해요!
그래도 Controller 라는 네이밍이 적절하지 않을 수도 있겠네요~
BlackjackGame 같은 네이밍을 한다면 로빈 말대로 알아서 동작하는 프로그램과 같은 의미를 담을 수 있겠어요 ✊

private final InputView inputView;
private final OutputView outputView;

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

void run() {
final List<Name> names = inputView.askPlayerNames();
final Dealer dealer = new Dealer();
final List<Player> players = names.stream().map(Player::new).toList();
final CardDeck deck = CardDeck.createShuffledDeck();

final BetDetails playersBetDetails = getPlayersBettingMoney(names);
initializeHand(deck, dealer, players);
outputView.printInitialHandOfEachPlayer(dealer, players);

for (final Player player : players) {
givePlayerMoreCardsIfWanted(deck, player);
}
giveDealerMoreCardsIfNeeded(deck, dealer);

printHandStatusOfEachPlayer(dealer, players);
final CardGameResult result = CardGameResult.of(dealer, players);
ProfitDetails profits = playersBetDetails.calculateProfit(result);
printProfit(profits);
}
Comment on lines +27 to +46
Copy link
Member

Choose a reason for hiding this comment

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

함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다.

Copy link
Author

Choose a reason for hiding this comment

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

프로그램의 진입점으로 생성과 로직 실행의 책임을 갖고 있는 메서드니까 괜찮겠지 라는 생각으로 해당 메서드는 요구사항을 벗어나게 작성했어요! 하지만 확실히 흐름이 명확하게 보이지 않네요. 요구사항이 괜히 있는 게 아니라는 생각과 함께 수정했어요!


private BetDetails getPlayersBettingMoney(final List<Name> playerNames) {
return playerNames.stream()
.collect(
Collectors.collectingAndThen(
Collectors.toMap(
name -> name,
inputView::askBettingMoney,
(x, y) -> x,
LinkedHashMap::new
), BetDetails::new));
}

private void initializeHand(final CardDeck deck, final Dealer dealer, final List<Player> players) {
deck.giveInitialCards(dealer);
for (final Participant player : players) {
deck.giveInitialCards(player);
}
}

private void givePlayerMoreCardsIfWanted(final CardDeck deck, final Player player) {
while (player.isAlive() && inputView.askForMoreCard(player.name())) {
deck.giveCard(player);
outputView.printPlayerCard(player);
}
}

private void giveDealerMoreCardsIfNeeded(final CardDeck deck, final Dealer dealer) {
while (dealer.isMoreCardNeeded()) {
deck.giveCard(dealer);
outputView.printDealerHitMessage(dealer);
}
}

private void printHandStatusOfEachPlayer(final Dealer dealer, final List<Player> players) {
outputView.printParticipantCardWithScore(dealer);
for (final Player player : players) {
outputView.printParticipantCardWithScore(player);
}
}

private void printProfit(final ProfitDetails profits) {
outputView.printPlayerProfit(profits);
}
}
41 changes: 41 additions & 0 deletions src/main/java/blackjack/domain/betting/BetDetails.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package blackjack.domain.betting;

import blackjack.domain.cardgame.CardGameResult;
import blackjack.domain.cardgame.WinningStatus;
import blackjack.domain.player.Name;

import java.util.LinkedHashMap;
import java.util.Map;

public class BetDetails {
private final Map<Name, Money> playersBettingMoney;

public BetDetails(final Map<Name, Money> playersBettingMoney) {
this.playersBettingMoney = playersBettingMoney;
}

public ProfitDetails calculateProfit(final CardGameResult result) {
final Map<Name, Profit> profitDetails = new LinkedHashMap<>();

for (final var nameAndStatus : result.totalResult().entrySet()) {
final Name name = extractName(nameAndStatus);
final WinningStatus status = extractStatus(nameAndStatus);
final Profit profit = Profit.of(findBettingMoney(name), status);
profitDetails.put(name, profit);
}

return new ProfitDetails(profitDetails);
}
Comment on lines +17 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.

1단계 미션에서 구현한 각 플레이어의 승패무 통계 관련 코드를 재사용 하기 위해 지금과 같은 구조로 수익을 계산하는 건가요? 기존 코드를 삭제하고 컴팩트하게 구현했다면 조금 더 이해하기 쉬운 코드가 될 수 있었을 것 같아요.

Copy link
Member

Choose a reason for hiding this comment

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

오 저는 오히려 잘 이용했다고 생각했는데 역시 사람마다 생각이 다르군요

Copy link
Author

Choose a reason for hiding this comment

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

로빈~ 정확하시네요 👍
제가 이번 미션을 하면서 의미를 뒀던 부분은 step1 코드를 최소한으로 건드리고 step2를 구현할 수 있는가에요!
나름 목표를 달성했다고 생각했는데, 언급해 주신 부분은 놓친 부분이었고 리팩터링을 통해 해결해야 했어요~
지금은 수정했습니다 😀


private Name extractName(final Map.Entry<Name, WinningStatus> nameAndStatus) {
return nameAndStatus.getKey();
}

private WinningStatus extractStatus(final Map.Entry<Name, WinningStatus> nameAndStatus) {
return nameAndStatus.getValue();
}

private Money findBettingMoney(final Name name) {
return playersBettingMoney.get(name);
}
}
22 changes: 22 additions & 0 deletions src/main/java/blackjack/domain/betting/Money.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package blackjack.domain.betting;

public record Money(int value) {

public Money {
validateMinimum(value);
}

private void validateMinimum(final int value) {
if (value < 0) {
throw new IllegalArgumentException("돈의 액수는 0 이상이어야 합니다.");
}
}

int minusValue() {
return -value;
}
Comment on lines +15 to +17
Copy link
Member

Choose a reason for hiding this comment

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

메서드 이름이 조금 부적절한 것 같습니다. minus는 뺀다는 의미니까요

Copy link
Author

Choose a reason for hiding this comment

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

좋은 지적 감사해요 👍
부적절하다는 코멘트에 공감하고, 리팩터링 과정에서 해당 메서드는 삭제할 수 있어서 지웠어요~


int multipleValue(final double rate) {
Copy link
Member

Choose a reason for hiding this comment

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

저는 times를 사용했어요
money.times(rate) 이렇게 보이게용

Copy link
Author

Choose a reason for hiding this comment

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

times 좋은 네이밍이네요! 👍
현재 코드는 money 내부의 메서드를 대부분 삭제하여 값만을 반환하는 VO의 형태를 갖추게 됐어요~

return (int) (value * rate);
}
}
29 changes: 29 additions & 0 deletions src/main/java/blackjack/domain/betting/Profit.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package blackjack.domain.betting;

import blackjack.domain.cardgame.WinningStatus;

import static blackjack.domain.cardgame.WinningStatus.BLACKJACK;
import static blackjack.domain.cardgame.WinningStatus.LOSE;
import static blackjack.domain.cardgame.WinningStatus.PUSH;
import static blackjack.domain.cardgame.WinningStatus.WIN;

public record Profit(int value) {
private static final double BLACKJACK_PROFIT_RATE = 1.5;

public static Profit of(final Money money, final WinningStatus status) {
if (WIN.equals(status)) {
return new Profit(money.value());
}
if (LOSE.equals(status)) {
return new Profit(money.minusValue());
}
if (PUSH.equals(status)) {
return new Profit(0);
}
if (BLACKJACK.equals(status)) {
return new Profit(money.multipleValue(BLACKJACK_PROFIT_RATE));
}
Comment on lines +14 to +25
Copy link
Member

Choose a reason for hiding this comment

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

WinningStatus를 잘 이용한다면, if문을 제거할 수 있을 것 같습니다!


throw new IllegalStateException();
}
}
26 changes: 26 additions & 0 deletions src/main/java/blackjack/domain/betting/ProfitDetails.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package blackjack.domain.betting;

import blackjack.domain.player.Name;

import java.util.Collections;
import java.util.Map;

public class ProfitDetails {
private final Map<Name, Profit> profits;

public ProfitDetails(final Map<Name, Profit> profits) {
this.profits = profits;
}

public Profit getDealerProfit() {
final int playersProfitSum = profits.values()
.stream()
.mapToInt(Profit::value)
.sum();
return new Profit(-playersProfitSum);
}

public Map<Name, Profit> details() {
return Collections.unmodifiableMap(profits);
}
}
27 changes: 27 additions & 0 deletions src/main/java/blackjack/domain/card/Card.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package blackjack.domain.card;

public class Card {
private final CardNumber cardNumber;
private final CardShape cardShape;

public Card(final CardNumber cardNumber, final CardShape cardShape) {
this.cardNumber = cardNumber;
this.cardShape = cardShape;
}

public boolean isAceCard() {
return cardNumber == CardNumber.ACE;
}

public int getScore() {
return cardNumber.getValue();
}

public CardNumber getNumber() {
return cardNumber;
}

public CardShape getShape() {
return cardShape;
}
}
27 changes: 27 additions & 0 deletions src/main/java/blackjack/domain/card/CardNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package blackjack.domain.card;

public enum CardNumber {
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 value;

CardNumber(final int value) {
this.value = value;
}

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

public enum CardShape {
SPADE,
HEART,
DIAMOND,
CLOVER
}
Loading