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단계 - 블랙잭 베팅] 제프리(홍성호) 미션 제출합니다. #922

Merged
merged 54 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
a086f34
refactor: 생성자에 방어적 복사 적용
AppleMint98 Mar 11, 2025
915a371
refactor: 메서드 네이밍 checkDealerSumUnderThreshold -> determineDealerAddi…
AppleMint98 Mar 11, 2025
43c1438
refactor: 반복문 내부 메서드호출 분리
AppleMint98 Mar 11, 2025
9e29e31
refactor: 상수 네이밍 BUST_STANDARD -> MAX_SCORE 변경
AppleMint98 Mar 11, 2025
3f35f84
refactor: playerCanHit 변수 추출
AppleMint98 Mar 11, 2025
dc8162e
refactor: doesNotBust 메서드로 부정연산자 제거
AppleMint98 Mar 12, 2025
0f0a44d
refactor: Gambler 추상 클래스 변경
AppleMint98 Mar 12, 2025
a873203
refactor: openOneCard -> openInitialCard 네이밍 변경
AppleMint98 Mar 12, 2025
38ef468
refactor: openInitialCards 추상메서드 구현
AppleMint98 Mar 12, 2025
dd913aa
style: 패키지 이동
AppleMint98 Mar 13, 2025
c1aad90
feat: View 인터페이스 구현
AppleMint98 Mar 14, 2025
de0685f
fix: 출력 오류 수정
AppleMint98 Mar 16, 2025
bf91081
style: 패키지 구조 변경
AppleMint98 Mar 16, 2025
385b132
style: 패키지 구조 변경
AppleMint98 Mar 16, 2025
1c4b571
feat: 설정 클래스 config 통합
AppleMint98 Mar 16, 2025
3c997d5
style : 클래스 이름 변경
AppleMint98 Mar 16, 2025
1afd53d
feat: GameBoard 객체 생성 후 로직 이관
AppleMint98 Mar 16, 2025
7d8fb3a
feat: GameBoard 객체 생성 후 로직 이관
AppleMint98 Mar 16, 2025
44dbd6d
feat: 블랙잭 판별 메서드 추가
AppleMint98 Mar 16, 2025
9e9374c
feat: 배팅 금액 기능 구현
AppleMint98 Mar 16, 2025
a7fa62a
fix: y 입력 시 추가로 받지않는 에러 수정
AppleMint98 Mar 16, 2025
d00adbf
fix: 수익 반환 자료형 변경
AppleMint98 Mar 16, 2025
5b7fb50
refactor: 안쓰는 메서드 제거
AppleMint98 Mar 17, 2025
b372c39
style: 테스트 디렉터리 구조 변경
AppleMint98 Mar 17, 2025
1f8f208
test: Card 객체 테스트 작성
AppleMint98 Mar 17, 2025
182def4
test: Cards 객체 테스트 작성
AppleMint98 Mar 17, 2025
f6990ef
test: Deck 객체 테스트 작성
AppleMint98 Mar 17, 2025
6e63922
refactor: 메서드 이름 및 위치 변경
AppleMint98 Mar 17, 2025
deb4451
refactor: 변수 이름 변경
AppleMint98 Mar 17, 2025
63ac0b0
test: Player 객체 테스트 작성
AppleMint98 Mar 17, 2025
874b4c3
test: Players 객체 테스트 작성
AppleMint98 Mar 17, 2025
95c3a2a
refactor: 배팅금액 업데이트 로직 메서드 분리
AppleMint98 Mar 17, 2025
c7d2d1f
test: Dealer 객체 테스트 작성
AppleMint98 Mar 17, 2025
a498eb8
style: 개행 추가 및 메서드 정렬
AppleMint98 Mar 17, 2025
9a2f0c2
fix: 메서드 호출 수정
AppleMint98 Mar 17, 2025
5c45470
refactor: get 네이밍을 calculate로 변경
AppleMint98 Mar 17, 2025
7c10c6e
fix: 무승부시 플레이어 표기오류 수정
AppleMint98 Mar 17, 2025
fc5c299
refactor: BlackjackGame -> BlackjackController 네이밍 변경
AppleMint98 Mar 18, 2025
251a78a
refactor: BlackjackTable -> BlackjackGame 네이밍 변경
AppleMint98 Mar 18, 2025
fe9c7c5
refactor: 게임 실행 로직 이관
AppleMint98 Mar 19, 2025
897faf7
refactor: View 인터페이스 제거
AppleMint98 Mar 19, 2025
d16bdb5
refactor: 변수 이름 변경 및 메서드 접근제한자 변경
AppleMint98 Mar 19, 2025
c1a8622
refactor: 매직넘버 제거
AppleMint98 Mar 19, 2025
4fe3ea9
refactor: draw 메서드의 반환값 일급 컬렉션 변경
AppleMint98 Mar 19, 2025
4f14767
refactor: 행위를 드러내도록 메서드 네이밍 변경
AppleMint98 Mar 19, 2025
239b12d
refactor: 플레이어들의 초기 패를 드로우할 시, 파라미터로 덱을 받도록 수정
AppleMint98 Mar 19, 2025
690bc81
refactor: 배당률을 MatchResult 의 필드로 옮기고, 배당금 계산 로직을 이관
AppleMint98 Mar 19, 2025
356c58c
style: import optimize
AppleMint98 Mar 19, 2025
18d5740
refactor: CsvSource -> MethodSource 변경
AppleMint98 Mar 19, 2025
6a3e5e6
refactor: 블랙잭 승패계산로직 Dealer와 분리
AppleMint98 Mar 19, 2025
5874976
feat: 초기 배팅금액 검증로직 추가
AppleMint98 Mar 19, 2025
a47ef45
style: 패키지 변경
AppleMint98 Mar 21, 2025
2d19054
feat(GamblerStatus): 갬블러의 게임 진행 상태를 나타내는 enum 구현
AppleMint98 Mar 21, 2025
3868de1
feat(BlackjackGame): BlackjackGame
AppleMint98 Mar 21, 2025
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
19 changes: 11 additions & 8 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import controller.BlackJackController;
import view.InputView;
import view.OutputView;
import blackjack.controller.BlackjackController;
import blackjack.config.GameConfig;
import blackjack.view.InputView;
import blackjack.view.OutputView;

public class Application {
public static void main(String[] args) {

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

BlackJackController controller = new BlackJackController(inputView, outputView);
controller.run();
GameConfig gameConfig = new GameConfig(
new InputView(),
new OutputView()
);
BlackjackController blackJackController = new BlackjackController(gameConfig);
blackJackController.run();
}
}
24 changes: 24 additions & 0 deletions src/main/java/blackjack/config/GameConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package blackjack.config;

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

public class GameConfig {

private final InputView inputView;
private final OutputView outputView;

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

public InputView getInputView() {
return inputView;
}

public OutputView getOutputView() {
return outputView;
}

}
9 changes: 9 additions & 0 deletions src/main/java/blackjack/constant/GamblerStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package blackjack.constant;
Copy link

Choose a reason for hiding this comment

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

enum은 모두 constant 패키지에 위치한 것 같은데요. 👀
정말로 상수의 역할 정도만 한다면 constant 패키지에 두는 것도 나쁘지 않지만, 대다수는 블랙잭이라는 도메인에 필수적인
도메인 객체로 보이는데요. 😃

MatchResult, TrumpRank, TrumpSuit, GamblerStatus 등은 domain 패키지에 두는게 더 좋지 않을까 생각되는데 제프리는 어떻게 생각하시나요?

Copy link
Author

@AppleMint98 AppleMint98 Mar 24, 2025

Choose a reason for hiding this comment

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

맞습니다! constant 에 있기에는, 도메인 객체에 더 가까운 것 같아요!

TrumpRank, TrunkSuit는 card 도메인에 더 가까운거 같고,
GamblerStatus 는 gambler 도메인에 더 가까운거 같아요 😄

MatchResult를 옮길 패키지가 애매해서, 관련 역할을 찾아보다가
BlackjackRule 이라는 기존의 객체가 evaluate() 메서드를 사용해 MatchResult 를 결정한다고 생각해서,
Rule 대신 Judge 라는 네이밍으로 바꾸고 judgment 라는 새로운 패키지를 만들어 옮겼습니다!


public enum GamblerStatus {

IN_PROGRESS,
END,
;

}
29 changes: 29 additions & 0 deletions src/main/java/blackjack/constant/MatchResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package blackjack.constant;

public enum MatchResult {

WIN("승", 1.0),
LOSE("패", -1.0),
PUSH("무", 0),
BLACKJACK_WIN("블랙잭 승", 1.5);

private final String message;
private final double payoutMultiplier;

MatchResult(String message, double payoutMultiplier) {
this.message = message;
this.payoutMultiplier = payoutMultiplier;
}

public double calculatePayout(double betAmount) {
return betAmount * payoutMultiplier;
}

public String getMessage() {
return message;
}

public double getPayoutMultiplier() {
return payoutMultiplier;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package domain.constant;
package blackjack.constant;

public enum TrumpRank {

ACE(11, "A"),
TWO(2, "2"),
THREE(3, "3"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package domain.constant;
package blackjack.constant;

public enum TrumpSuit {

SPADE("스페이드"),
CLOVER("클로버"),
HEART("하트"),
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/blackjack/constant/UserAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package blackjack.constant;

import java.util.Arrays;

public enum UserAction {

HIT("y"),
STAND("n"),
;

private final String command;

UserAction(String command) {
this.command = command;
}

public static UserAction from(String input) {
return Arrays.stream(values())
.filter(action -> action.command.equals(input))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("[ERROR] 올바른 기능을 입력해 주세요."));
}

}
61 changes: 61 additions & 0 deletions src/main/java/blackjack/controller/BlackjackController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package blackjack.controller;

import blackjack.config.GameConfig;
import blackjack.constant.UserAction;
import blackjack.domain.BlackjackGame;
import blackjack.view.InputView;
import blackjack.view.OutputView;
import java.util.List;

public class BlackjackController {

private final InputView inputView;
private final OutputView outputView;

public BlackjackController(GameConfig gameConfig) {
this.inputView = gameConfig.getInputView();
this.outputView = gameConfig.getOutputView();
}

public void run() {
List<String> playerNames = inputView.readParticipantsNames();
BlackjackGame blackjackGame = new BlackjackGame(playerNames);

for (String playerName : blackjackGame.getPlayerNames()) {
int betAmount = inputView.readParticipantsBetAmount(playerName);
blackjackGame.updateBetAmount(playerName, betAmount);
}
outputView.printInitialGameSettings(blackjackGame);

playGame(blackjackGame);

outputView.printGameSummary(blackjackGame);
outputView.printGameResult(blackjackGame);
}


public void playGame(BlackjackGame blackjackGame) {
while (blackjackGame.isPlaying()) {
playPlayerTurn(blackjackGame);
}
playDealerTurn(blackjackGame);
}


private void playPlayerTurn(BlackjackGame blackjackGame) {
String playerName = blackjackGame.findCurrentTurnPlayerName();
while (inputView.readOneMoreCardResponse(playerName).equals(UserAction.HIT)) {
blackjackGame.addCardTo(playerName);
outputView.printPlayerCards(blackjackGame, playerName);
}
blackjackGame.endPlayerTurn(playerName);
Comment on lines +46 to +51
Copy link

Choose a reason for hiding this comment

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

여기서 꼭 while로 둘 필요가 없을 것 같아요. 턴을 종료한게 아니라면 findCurrentTurnPlayer()의 결과로 동일한 플레이어가 반환되지 않을까 싶어요.

여기서는 단순히 if 문으로 처리를 해주는게 좋을 것 같아요. 😃

또한 PlayerName을 BlackjackGame 객체에게 전달해줄 필요 없이, 스스로 현재 턴의 유저를 조회해서 처리하도록해도 되지 않을까 싶어요.

Suggested change
String playerName = blackjackGame.findCurrentTurnPlayerName();
while (inputView.readOneMoreCardResponse(playerName).equals(UserAction.HIT)) {
blackjackGame.addCardTo(playerName);
outputView.printPlayerCards(blackjackGame, playerName);
}
blackjackGame.endPlayerTurn(playerName);
Player player = blackjackGame.findCurrentTurnPlayer();
if (inputView.readOneMoreCardResponse(player.getPlayerName()).equals(UserAction.HIT)) {
blackjackGame.drawCurrentTurn(); // 내부에서 findCurrentTurnPlayer() 하여 player.changeStatusToEnd() 진행.
outputView.printPlayerCards(blackjackGame, playerName);
return;
}
blackjackGame.endCurrentPlayerTurn(); // 내부에서 findCurrentTurnPlayer() 하여 player.endPlayerTurn()

Copy link
Author

Choose a reason for hiding this comment

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

오 생각하지 못했습니다!
플레이어가 N을 누를때 까지, 해당 플레이어에게 계속 카드를 더 받을지(Y/N) 를 물어봐야 된다고만 생각해서 while로 구현했는데,
플레이어가 상태를 바꾸기 전까지 findCurrentTurnPlayer() 로 찾아서 더 받을지(Y/N)을 물어보면
while을 안쓰고도 구현이 가능할 것 같아요!!
감사합니다 👍 👍

}

private void playDealerTurn(BlackjackGame blackjackGame) {
if (blackjackGame.isDealerShouldDrawCard()) {
outputView.printDealerOneMoreCardMessage();
}
blackjackGame.processDealerTurn();
}

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

import blackjack.domain.card.Deck;
import blackjack.domain.gambler.Dealer;
import blackjack.domain.gambler.Player;
import blackjack.domain.gambler.PlayerName;
import blackjack.domain.gambler.Players;
import java.util.List;

public class BlackjackGame {

private final Deck deck;
private final Dealer dealer;
private final Players players;

public BlackjackGame(List<String> playerNames) {
validatePlayerCount(playerNames);
this.deck = Deck.initialize();
this.dealer = new Dealer();
this.players = registerPlayers(playerNames);
distributeStartingHands();
}
Comment on lines +16 to +22
Copy link

Choose a reason for hiding this comment

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

지금처럼 BlackjackGame 객체를 생성하게되면, BlackjackGame 객체에 대한 테스트 작성이 어려울 것 같아요. 🤔
또한 객체를 생성하는 시점에 바로 distributeStartingHands() 하기보다는, 생성자에서는 BlackjackGame 객체를 생성만 하고, 이후에 blackjackGame 객체에게 distributeStartingHands() 메시지를 보내면 어떨까요?

추가적으로 processDealerTurn, updateBetAmount, findPlayer, findCurrentTurnPlayerName, addCardTo, isPlaying 등등

BlackjackGmae의 public 메서드에 대한 테스트가 없어보이는데요.
이 부분 테스트 추가 부탁드릴게요.

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 패턴의 이해가 부족한 상태에서, 주변을 따라가기에 급급했던것 같아요.

왜 컨트롤러는 테스트코드를 따로 작성하지 않지? 라는 고민이 부족했던것 같아요. 컨트롤러는 테스트 코드를 작성하지 않아도 된다는 법칙이 있는것도 아닌데, 그냥 페어분들이 컨트롤러는 따로 작성하지 않아서 안하는 것이 맞다고 생각했어요. 🙇
지금 생각나는 바로는, 컨트롤러는 비즈니스 로직을 수행하기 보다는, 입, 출력과 서비스를 연결하는 역할을 하기 때문에, 중요도가 상대적으로 떨어져서 라고 생각합니다!

현재 BlackjackGame 객체같은 경우는 View와 Domain을 연결해주는 Controller의 역할이 아니라, 도메인들간의 비즈니스 로직을 수행하는 서비스 느낌의 도메인이므로 테스트 작성이 필수적인것 같아요!

BlackjackGame 이외에도 누락된 테스트 코드도 보완해서 올리겠습니다!


public void processDealerTurn() {
if (isDealerShouldDrawCard()) {
dealer.addCard(deck.drawCard());
}
calculateTotalPayout();
}

public void updateBetAmount(String playerName, int betAmount) {
findPlayer(playerName).updateBetAmount(betAmount);
}

public Player findPlayer(String playerName) {
return players.findPlayer(playerName);
}

public String findCurrentTurnPlayerName() {
return players.findCurrentPlayer().getPlayerName();
}

public void addCardTo(String playerName) {
findPlayer(playerName).addCard(deck.drawCard());
}

public boolean isPlaying() {
return players.countInProgressPlayers() != 0;
Copy link

Choose a reason for hiding this comment

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

총 몇명의 진행중 플레이어가 존재하지는를 물을 뒤, 카운팅하기보다는
players 객체에게는 진행중인 player가 있는지 여부를 물어봐도 되지 않을까요?

}

public boolean isDealerShouldDrawCard() {
return dealer.shouldDrawCard();
}

public void endPlayerTurn(String playerName) {
players.endPlayerTurn(playerName);
}

private void calculateTotalPayout() {
dealer.applyBetAmounts(players);
}

private void distributeStartingHands() {
dealer.drawInitializeHand(deck.drawInitialCards());
players.drawInitializeHands(deck);
}

private Players registerPlayers(List<String> names) {
return new Players(names.stream().map(PlayerName::new).map(Player::new).toList());
}

private void validatePlayerCount(List<String> playerNames) {
if (playerNames.isEmpty() || playerNames.size() > 6) {
Copy link

Choose a reason for hiding this comment

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

6은 매직넘버로 보여요. 👀

throw new IllegalArgumentException("[ERROR] 플레이어는 1명 이상, 6명 이하로 지정해야 합니다.");
}
}

public List<String> getPlayerNames() {
return players.getPlayerNames();
}

public Players getPlayers() {
return players;
}

public Dealer getDealer() {
return dealer;
}

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

import blackjack.constant.MatchResult;
import blackjack.domain.card.Cards;
import blackjack.domain.gambler.Dealer;
import blackjack.domain.gambler.Player;

public class BlackjackRule {

public static final int MAX_SCORE = 21;

public static MatchResult evaluate(Dealer dealer, Player player) {
int dealerScore = dealer.sumCardScores();
int playerScore = player.sumCardScores();
Cards playerCards = player.getCards();
Cards dealerCards = dealer.getCards();

if (dealerCards.isBlackjack() || playerCards.isBlackjack()) {
return calculateMatchResultWhenBlackjack(dealerCards, playerCards);
}
if (dealerScore > MAX_SCORE || playerScore > MAX_SCORE) {
return calculateMatchResultWhenOverBustStandard(playerScore);
}
return calculateMatchResult(dealerScore, playerScore);
}

private static MatchResult calculateMatchResultWhenBlackjack(Cards dealerCards, Cards playerCards) {
if (playerCards.isBlackjack() && dealerCards.isBlackjack()) {
return MatchResult.PUSH;
}
if (playerCards.isBlackjack()) {
return MatchResult.BLACKJACK_WIN;
}
return MatchResult.LOSE;
}

private static MatchResult calculateMatchResultWhenOverBustStandard(int playerScore) {
if (playerScore > MAX_SCORE) {
return MatchResult.LOSE;
}
return MatchResult.WIN;
}

private static MatchResult calculateMatchResult(int dealerScore, int playerScore) {
if (playerScore == dealerScore) {
return MatchResult.PUSH;
}
if (playerScore > dealerScore) {
return MatchResult.WIN;
}
return MatchResult.LOSE;
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package domain;
package blackjack.domain.card;

import domain.constant.TrumpSuit;
import domain.constant.TrumpRank;
import blackjack.constant.TrumpRank;
import blackjack.constant.TrumpSuit;

public class Card {

Expand Down
Loading