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

[2단계 - 블랙잭 베팅] 우르(김현우) 미션 제출합니다. #512

Merged
merged 45 commits into from
Mar 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
88c659b
feat : 점수가 블랙잭인지 확인
java-saeng Mar 8, 2023
daa17c8
feat : Cards의 합이 블랙잭인지 확인
java-saeng Mar 8, 2023
dd595d2
feat : Money VO 생성
java-saeng Mar 8, 2023
7f20782
feat : 게임 결과에 따라 베팅액 결정 전략 추가
java-saeng Mar 8, 2023
831a4de
feat : 참여자는 자신의 베팅 결과 상태를 결정할 수 있다
java-saeng Mar 8, 2023
75a93e8
fix : BigDecimal equals 수정
java-saeng Mar 8, 2023
ef49791
feat : 참여자는 자신의 베팅 결과에 따라 베팅액 결과 금액을 반환한다.
java-saeng Mar 8, 2023
e6a71d9
feat : CardTable은 승부에 따라 베팅액 결정 상태 요청을 Participant에게 보냄
java-saeng Mar 8, 2023
12f993e
feat : CardTable 은 경기 결과에 따라 베팅 금액을 구해준다
java-saeng Mar 8, 2023
13233d1
feat : 배팅 금액 받기
java-saeng Mar 8, 2023
f96991a
feat : 배팅금액상태 결정 되지 않음
java-saeng Mar 8, 2023
2bcdd2a
feat : Participant 수익 출력
java-saeng Mar 8, 2023
ad73162
feat : 돈 합하기
java-saeng Mar 8, 2023
23137ec
feat : 딜러 수익 구하기
java-saeng Mar 8, 2023
faadcd7
feat : 참여자와 딜러의 전체 최종 수익 구하기
java-saeng Mar 8, 2023
10d28bb
refactor : interface method 이름 변경
java-saeng Mar 9, 2023
f592f12
refactor : 참여자와 딜러 점수 비교하는 것을 Participant 에 위임
java-saeng Mar 9, 2023
3231837
feat : 처음 2장에서 승부가 나지 않을 상태 추가
java-saeng Mar 9, 2023
a0493de
refactor : 딜러와 참여자 점수를 비교를 Participant 에 위임
java-saeng Mar 9, 2023
3d20ee5
refactor : null이 아닌 승부가 나지 않은 상태인 TieState 로 초기화
java-saeng Mar 9, 2023
25d2653
refactor : first, finalMatchWith 메서드 간단하게 작성
java-saeng Mar 9, 2023
010aa44
refactor : 조건문 삭제
java-saeng Mar 9, 2023
85cd267
refactor : setter 삭제 후, 테스트 추가
java-saeng Mar 9, 2023
f41b4c9
refactor : 아직 승부가 결정이 안된 상태 null object 넣기
java-saeng Mar 9, 2023
d592ead
refactor : dealCardTo boolean 사용하지 않기 때문에 void 로 변경
java-saeng Mar 9, 2023
7380a1a
refactor : dealer 가 블랙잭이면 게임을 바로 끝내야해서 상태가 정해진 participant 는 hit 할 수 없음
java-saeng Mar 9, 2023
1f8dc61
refactor : CardArea -> Hand 로 변경
java-saeng Mar 9, 2023
5804c51
refactor : 도메인 용어로 메서드 네임, 상수 이름 변경
java-saeng Mar 9, 2023
b6c1d40
feat : 처음 두 장을 받을 때, 보여주는 방식을 추상 메서드로 변경
java-saeng Mar 10, 2023
31525fc
refactor : 카드를 보여주는 추상 메서드 존재하기 때문에 기존 코드 삭제
java-saeng Mar 10, 2023
ee39bd0
test : final match 를 테스트 할 때, first match 도 실행
java-saeng Mar 10, 2023
f3633fc
test : dealer 얻은 결과 테스트 작성
java-saeng Mar 10, 2023
839e394
style : pr 제출 전 reformatting
java-saeng Mar 10, 2023
988c4bc
refactor : 객체 메시지 목록은 관용적인 getter보다 위에 위치
java-saeng Mar 12, 2023
d2d5853
refactor : 블랙잭 조건은 카드가 2장의 합이 21이어야함
java-saeng Mar 12, 2023
b077eb1
refactor : 점수 계산할 때 softHand일 경우 바로 score 리턴
java-saeng Mar 12, 2023
a6d953a
refactor : 패키지 이동
java-saeng Mar 13, 2023
05d0da9
feat : 배팅 상태 판단하는 클래스 추가
java-saeng Mar 13, 2023
a87489c
refactor : BetResultFinder 가 베팅 결과 상태를 찾아서 Participant 가 돈을 계산
java-saeng Mar 13, 2023
9b6c0ed
test : 테스트 메서드 명 수정
java-saeng Mar 13, 2023
6cce44c
fix : DealerBust 에서 참여자가 블랙잭이 아니면서 3장 이상의 카드 합이 21일 때 진 걸로 결정
java-saeng Mar 13, 2023
69398fe
refactor : @FunctionalInterface 추가 및 NULL object 삭제
java-saeng Mar 13, 2023
fb490eb
refactor : DealerBust 조건 변경 및 사용하지 않는 메서드 삭제
java-saeng Mar 14, 2023
5f87fc6
refactor : 딜러와 참여자가 블랙잭이 아니더라도 나중에 점수가 같으면 무승부이다.
java-saeng Mar 14, 2023
23568b0
style : 리뷰 요청 전 code formatting
java-saeng Mar 14, 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
128 changes: 66 additions & 62 deletions src/main/java/controller/BlackJackController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
import domain.player.Name;
import domain.player.Player;
import domain.player.dealer.Dealer;
import domain.player.dealer.DealerResult;
import domain.player.participant.Money;
import domain.player.participant.Participant;
import domain.player.participant.ParticipantResult;
import view.InputView;
import view.OutputView;

Expand All @@ -16,8 +15,6 @@
import java.util.Map;
import java.util.stream.Collectors;

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

public class BlackJackController {

public void run() {
Expand All @@ -29,96 +26,103 @@ public void run() {
final Dealer dealer = createDealer();
final List<Player> players = createPlayers(participants, dealer);

deal(cardTable, players);

printStateAfterDeal(participants, dealer);
hittingPlayer(cardTable, participants, dealer);
printStateAfterHit(participants, dealer);
firstDeal(cardTable, players);
printStateAfterFirstDeal(participants, dealer);

final Map<Participant, ParticipantResult> participantsResult = cardTable.determineWinner(participants, dealer);
final Map<DealerResult, Long> scoreBoard = countDealerResult(participantsResult);

printPlayerScoreBoard(participants, participantsResult, scoreBoard);
}

private static List<Player> createPlayers(final List<Participant> participants, final Dealer dealer) {
List<Player> players = new ArrayList<>(participants);
players.add(dealer);
finalDeal(cardTable, participants, dealer);
printStateAfterFinalDeal(participants, dealer);

return players;
printPlayersMoneyBoard(cardTable, participants, dealer);
}

private static void deal(final CardTable cardTable, final List<Player> players) {
private void firstDeal(final CardTable cardTable, final List<Player> players) {
dealCard(cardTable, players);
dealCard(cardTable, players);
}

private static void dealCard(final CardTable cardTable, final List<Player> players) {
private void dealCard(final CardTable cardTable, final List<Player> players) {
players.forEach(cardTable::dealCardTo);
}

private static Dealer createDealer() {
return new Dealer();
private void printStateAfterFirstDeal(final List<Participant> participants, final Dealer dealer) {
OutputView.showDealtCardTo(participants);
OutputView.showStateOf(dealer);
OutputView.showStateOf(participants);
}

private static List<Participant> createParticipants() {
return InputView.readParticipantsName()
.stream()
.map(Name::new)
.map(Participant::new)
.collect(Collectors.toList());
private void finalDeal(final CardTable cardTable, final List<Participant> participants, final Dealer dealer) {
hitForParticipants(cardTable, participants);
hitForDealer(cardTable, dealer);
}

private static void printPlayerScoreBoard(final List<Participant> participants,
final Map<Participant, ParticipantResult> playersResult,
final Map<DealerResult, Long> scoreBoard) {
OutputView.showDealerScoreBoard(scoreBoard);
OutputView.showParticipantsScoreBoard(playersResult, participants);
private void hitForParticipants(final CardTable cardTable, final List<Participant> participants) {
participants.stream()
.filter(Participant::isNotBlackjack)
.forEach(participant -> hitForParticipant(cardTable, participant));

}

private static void printStateAfterHit(final List<Participant> participants, final Dealer dealer) {
private void hitForParticipant(final CardTable cardTable, final Participant participant) {
while (participant.canHit() && selectHitOrStand(participant)) {
cardTable.dealCardTo(participant);
OutputView.showStateOf(participant);
}
}

private void hitForDealer(final CardTable cardTable, final Dealer dealer) {
do {
OutputView.dealerOneMoreCard();
cardTable.dealCardTo(dealer);
} while (dealer.canHit());
}

private void printStateAfterFinalDeal(final List<Participant> participants, final Dealer dealer) {
OutputView.showPlayerStateResult(dealer);
OutputView.showParticipantsStateResult(participants);
}

private static void printStateAfterDeal(final List<Participant> participants, final Dealer dealer) {
OutputView.showDealtCardTo(participants);
OutputView.showStateOf(dealer);
OutputView.showStateOf(participants);
private void printPlayersMoneyBoard(final CardTable cardTable, final List<Participant> participants,
final Dealer dealer) {
printParticipantsMoneyBoard(cardTable, participants, dealer);
printDealerMoneyBoard(cardTable, participants, dealer);
}

private static Map<DealerResult, Long> countDealerResult(
final Map<Participant, ParticipantResult> playersResult) {
return playersResult.keySet()
.stream()
.collect(Collectors.groupingBy(participant -> playersResult.get(participant)
.convertToDealerResult(),
counting()));
private void printParticipantsMoneyBoard(final CardTable cardTable, final List<Participant> participants,
final Dealer dealer) {
final Map<Participant, Money> participantsResult = cardTable.determineParticipantsBettingMoney(participants,
dealer);
OutputView.showBettingMoneyBoard(participantsResult);
}

private void hittingPlayer(final CardTable cardTable, final List<Participant> participants, final Dealer dealer) {
hitForParticipants(cardTable, participants);
hitForDealer(cardTable, dealer);
private void printDealerMoneyBoard(final CardTable cardTable, final List<Participant> participants,
final Dealer dealer) {
final Money money = cardTable.determineDealerMoney(participants, dealer);
OutputView.showDealerMoneyBoard(money);
}

private void hitForDealer(final CardTable cardTable, final Dealer dealer) {
do {
OutputView.dealerOneMoreCard();
} while (cardTable.dealCardTo(dealer));
private List<Player> createPlayers(final List<Participant> participants, final Dealer dealer) {
List<Player> players = new ArrayList<>(participants);
players.add(dealer);

return players;
}

private void hitForParticipants(final CardTable cardTable, final List<Participant> participants) {
participants.forEach(participant -> hitForParticipant(cardTable, participant));
private Dealer createDealer() {
return new Dealer();
}

private void hitForParticipant(final CardTable cardTable, final Participant participant) {
while (inputHitOrStay(participant)) {
cardTable.dealCardTo(participant);
OutputView.showStateOf(participant);
}
private List<Participant> createParticipants() {
return InputView.readParticipantsName()
.stream()
.map(Name::new)
.map(name -> {
final int bettingMoney = InputView.readBettingMoney(name);
return new Participant(name, Money.wons(bettingMoney));
})
.collect(Collectors.toList());
}

private boolean inputHitOrStay(final Participant participant) {
return InputView.readMoreCard(participant).equals("y");
private boolean selectHitOrStand(final Participant participant) {
return InputView.readMoreCard(participant);
}
}
16 changes: 12 additions & 4 deletions src/main/java/domain/Score.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class Score {

private static final Score UPPER_LIMIT_SCORE = new Score(21);

private static final Score REMAIN_SCORE_ACE = new Score(10);
private static final Score SOFT_HAND_SCORE = new Score(10);
Copy link

Choose a reason for hiding this comment

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

Score는 점수를 담는 값 객체,, 그래서 한정지으면 안된다??

보통 Score라는 객체를 만들 때 모든 점수값을 담을 수 있는 범용적인 Score 객체로 만들지는 않습니다.
이 객체 같은 경우 좀 더 분명하게 이름을 짓는다면 BlackjackScore 정도 되겠죠.
다만, 지금은 블랙잭을 다루는 어플리케이션이기 때문에 굳이 이렇게 지을 필요는 없는 것 같아요.
대부분의 리팩토링은 정말 필요할 때 해도 됩니다. 나중에 다른 게임이 추가되고 이 객체의 이름이 헷갈린다는 생각이 들면 그 때 해도 될거에요.

Copy link
Author

Choose a reason for hiding this comment

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

그렇군요,, 저는 Score나 Money 와 같은 객체들은 범용적으로 사용한다는 생각을 당연하게 하고 있었습니다
그래서 위와 같은 고민이 생겼구요,,
범블비 말씀대로 범용적은 Score를 만드는게 일반적이지 않다면 필요할 때 리팩토링하는 식으로 진행하는게 합리적으로 보입니다~~


private final int value;

Expand Down Expand Up @@ -36,11 +36,11 @@ public boolean isGreaterThan(final Score other) {
return value > other.value;
}

public Score plusTenIfNotBurst() {
final Score plusScore = this.plus(REMAIN_SCORE_ACE);
public Score plusSoftHand() {
final Score plusScore = this.plus(SOFT_HAND_SCORE);

if (plusScore.isLessEqualThan(UPPER_LIMIT_SCORE)) {
return this.plus(REMAIN_SCORE_ACE);
return this.plus(SOFT_HAND_SCORE);
}

return this;
Expand All @@ -50,6 +50,14 @@ public Score plus(Score other) {
return new Score(value + other.value);
}

public boolean isSumTwentyOne() {
return this.equals(UPPER_LIMIT_SCORE);
}

public boolean isSame(final Score other) {
return this.equals(other);
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
Expand Down
8 changes: 6 additions & 2 deletions src/main/java/domain/card/Card.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ public CardValue cardValue() {

@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof Card)) return false;
if (this == o) {
return true;
}
if (!(o instanceof Card)) {
return false;
}
final Card card = (Card) o;
return cardShape == card.cardShape && cardValue == card.cardValue;
}
Expand Down
41 changes: 19 additions & 22 deletions src/main/java/domain/cardtable/CardTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import domain.deck.CardDeck;
import domain.player.Player;
import domain.player.dealer.Dealer;
import domain.player.participant.Money;
import domain.player.participant.Participant;
import domain.player.participant.ParticipantResult;
import domain.player.participant.betresult.resultfinder.BetResultFinder;

import java.util.List;
import java.util.Map;
Expand All @@ -14,45 +15,41 @@
public class CardTable {

private final CardDeck cardDeck;
private final BetResultFinder betResultFinder;

private CardTable(final CardDeck cardDeck) {
this.cardDeck = cardDeck;
this.betResultFinder = new BetResultFinder();
}

public static CardTable readyToPlayBlackjack(final CardDeck cardDeck) {
return new CardTable(cardDeck);
}

public Map<Participant, ParticipantResult> determineWinner(final List<Participant> participants,
final Dealer dealer) {
public Map<Participant, Money> determineParticipantsBettingMoney(final List<Participant> participants,
final Dealer dealer) {

return participants.stream()
.collect(Collectors.toMap(
Function.identity(),
participant -> matchBetween(participant, dealer))
);
participant -> participant.determineBetMoney(
betResultFinder.findStateOf(participant, dealer))
));
}

private ParticipantResult matchBetween(final Participant participant, final Dealer dealer) {
if (participant.isBust()) {
return ParticipantResult.LOSER;
}
if (dealer.isBust()) {
return ParticipantResult.WINNER;
}
if (participant.score().isGreaterThan(dealer.score())) {
return ParticipantResult.WINNER;
}
if (participant.score().equals(dealer.score())) {
return ParticipantResult.DRAWER;
}
return ParticipantResult.LOSER;
public Money determineDealerMoney(final List<Participant> participants, final Dealer dealer) {

final Map<Participant, Money> participantsResultMoney = determineParticipantsBettingMoney(participants, dealer);

return participantsResultMoney.values()
.stream()
.reduce(Money.MIN, Money::plus)
.lose();
}

public boolean dealCardTo(Player player) {
public void dealCardTo(Player player) {
if (player.canHit()) {
player.hit(cardDeck.draw());
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package domain.area;
package domain.hand;

import domain.Score;
import domain.card.Card;

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

public class CardArea {
public class Hand {

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

Expand All @@ -21,8 +21,8 @@ public void addCard(final Card card) {
public Score calculate() {
Score score = score();

if (hasAce()) {
score = score.plusTenIfNotBurst();
if (isSoftHand()) {
return score.plusSoftHand();
}

return score;
Expand All @@ -35,7 +35,7 @@ private Score score() {

}

private boolean hasAce() {
private boolean isSoftHand() {
return cards.stream()
.anyMatch(card -> card.cardValue().isAce());
}
Expand All @@ -48,7 +48,15 @@ public boolean isBust() {
return calculate().isBust();
}

public boolean isBlackjack() {
return cards.size() == 2 && calculate().isSumTwentyOne();
}

public Card firstCard() {
return cards.get(0);
}

public Card secondCard() {
return cards.get(1);
}
}
Loading