Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
8dc6e0d
Test(Money): Money 검증 ν…ŒμŠ€νŠΈ μž‘μ„±
choubung Mar 13, 2026
1680ce9
Test(Money): money 객체의 integer 값을 λ°›μ•„μ˜€λŠ” ν•¨μˆ˜μΆ”κ°€λ‘œ λ³€κ²½
choubung Mar 13, 2026
9b6dd2e
Feat(Money): money value object μž‘μ„±
choubung Mar 13, 2026
e03c568
Feat(view): InputView에 λ² νŒ… κΈˆμ•‘ μž…λ ₯ λ©”μ†Œλ“œ μΆ”κ°€
choubung Mar 13, 2026
db4d88d
Feat(view): ResultView에 μ΅œμ’… 수읡 좜λ ₯ λ©”μ†Œλ“œ μΆ”κ°€
choubung Mar 14, 2026
e325a08
Feat: 수읡 계산 둜직 μΆ”κ°€
choubung Mar 14, 2026
d43da0c
Feat(Participant): λΈ”λž™μž­ νŒλ³„ λ©”μ†Œλ“œ μΆ”κ°€
choubung Mar 14, 2026
da54a68
Refactor: μ›μ‹œκ°’μœΌλ‘œ 이름 보내지 μ•Šλ„λ‘ μˆ˜μ •
choubung Mar 14, 2026
a0e2397
Refactor: μ‚¬μ†Œν•œ λ¦¬νŒ©ν„°λ§
choubung Mar 14, 2026
bee3122
Feat: λ² νŒ… κΈ°λŠ₯ μΆ”κ°€
choubung Mar 14, 2026
14e6d8f
Feat(Player): 승패 계산에 λΈ”λž™μž­μΈ κ²½μš°λ„ κ³ λ €ν•˜λ„λ‘ μΆ”κ°€
choubung Mar 14, 2026
8d3232a
Refactor(BettingTable): μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” λ©”μ†Œλ“œ μ‚­μ œ
choubung Mar 14, 2026
df11c83
Fix(BettingTable): μŠΉνŒ¨μ— λ”°λ₯Έ 판돈 계산 둜직 μˆ˜μ •
choubung Mar 16, 2026
91a11a6
Refactor(Money): int λŒ€μ‹  BigDecimal을 μ‚¬μš©ν•˜λ„λ‘ λ³€κ²½
choubung Mar 16, 2026
a5a76a8
Refactor(Money): 였λ₯˜ λ‘œκ·Έκ°€ μ‚¬μš©μž μž…λ ₯값도 좜λ ₯ν•˜λ„λ‘ μˆ˜μ •
choubung Mar 16, 2026
ba4a1de
Refactor(Participant): ν•΄λ‹Ή 클래슀λ₯Ό 좔상 클래슀둜 λ³€κ²½
choubung Mar 16, 2026
d8a143f
Test(BettingTable): ν…ŒμŠ€νŠΈ μž‘μ„±
choubung Mar 16, 2026
dfad9b5
Refactor: bettingTable 원본 λ² νŒ… 데이터λ₯Ό κ±΄λ“œλ¦¬μ§€ μ•Šλ„λ‘ μˆ˜μ •
choubung Mar 16, 2026
1486349
Build: Mockito μ˜μ‘΄μ„± μΆ”κ°€
choubung Mar 16, 2026
959d646
Test: λ¦¬νŒ©ν„°λ§μœΌλ‘œ μΈν•œ λ³€κ²½
choubung Mar 17, 2026
0b480e7
Feat(Name): hashCode와 equals μž¬μ •μ˜
choubung Mar 17, 2026
17237df
Refactor: 클래슀 μ‚­μ œ Playerλ₯Ό BlackjackGame 도메인에 μˆ¨κΉ€
choubung Mar 17, 2026
c01dd0d
Refactor(bettingTable): λ§€κ°œλ³€μˆ˜λ₯Ό dto둜 λ³€κ²½
choubung Mar 17, 2026
24671de
Refactor(Player): 승패 κ²°κ³Όλ₯Ό μ €μž₯ν•˜λŠ” λŒ€μ‹  λ°˜ν™˜ν•˜λ„λ‘ λ³€κ²½
choubung Mar 17, 2026
8382ee9
Feat(PlayerGroup): findByName λ©”μ†Œλ“œ μΆ”κ°€
choubung Mar 17, 2026
3b4cc92
Refactor(dto): κΈ°λ³Έ 정보 dto와 κ²°κ³Ό dtoλ₯Ό 뢄리
choubung Mar 17, 2026
be765ab
Refactor: dto λ³€κ²½μœΌλ‘œ μΈν•œ λ¦¬νŒ©ν„°λ§
choubung Mar 17, 2026
52372d4
Refactor(Participant): 맀직 λ„˜λ²„λ₯Ό λ©”μ†Œλ“œλ‘œ μΆ”μƒν™”ν•˜μ—¬ 제거
choubung Mar 17, 2026
9bbd2b8
Refactor: μ „λ°˜μ μΈ λ¦¬νŒ©ν† λ§
choubung Mar 18, 2026
f14f8b1
Build: μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” μ˜μ‘΄μ„± μ‚­μ œ
choubung Mar 19, 2026
72e273e
Test: BettingTable ν…ŒμŠ€νŠΈ 보강
choubung Mar 19, 2026
cf3e8c0
Refactor(blackjackGame): λΆˆλ³€ 볡사 μˆ˜μ •
choubung Mar 19, 2026
29e0895
Refactor: equals와 hashCode μ˜€λ²„λΌμ΄λ”©
choubung Mar 19, 2026
3efb315
Refactor: μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” λ©”μ†Œλ“œ μ‚­μ œ
choubung Mar 19, 2026
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
7 changes: 1 addition & 6 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import controller.GameController;
import service.BlackjackService;
import service.PlayerManager;

public class Application {
public static void main(String[] args) {
PlayerManager playerManager = new PlayerManager();
BlackjackService blackjackService = new BlackjackService();

GameController gameController = new GameController(blackjackService, playerManager);
GameController gameController = new GameController();

gameController.run();
}
Expand Down
111 changes: 81 additions & 30 deletions src/main/java/controller/GameController.java
Original file line number Diff line number Diff line change
@@ -1,64 +1,61 @@
package controller;

import domain.BettingTable;
import domain.BlackjackGame;
import domain.participant.Dealer;
import domain.participant.player.Player;
import service.BlackjackService;
import service.PlayerManager;
import domain.vo.Money;
import dto.DealerInfoDto;
import dto.DealerResultDto;
import dto.PlayerInfoDto;
import dto.PlayerResultDto;
import view.InputView;
import view.ResultView;

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

public class GameController {
private final BlackjackService blackjackService;
private final PlayerManager playerManager;

public GameController(BlackjackService blackjackService, PlayerManager playerManager) {
this.blackjackService = blackjackService;
this.playerManager = playerManager;
}
private final BlackjackGame blackjackGame = new BlackjackGame();
private final BettingTable bettingTable = new BettingTable();

public void run() {
gameSetup();
ResultView.printStartPlayersCards(blackjackService.getDealerInfo(), playerManager.getAllPlayersInfo());
askBettingMoney();

participantsHit();

finalizeGameResult();

ResultView.printCardsAndScoreResult(blackjackService.getDealerInfo(), playerManager.getAllPlayersInfo());
ResultView.printRankResult(blackjackService.getDealerInfo(), playerManager.getAllPlayersInfo());
}

private void gameSetup() {
List<String> names = InputView.askName();
playerManager.registerPlayers(names);

blackjackService.dealDealerCardsOut();
blackjackGame.registerPlayers(names);
blackjackGame.dealParticipantsCardsOut();

for (Player player : playerManager.getPlayers()) {
blackjackService.dealPlayerCardsOut(player);
ResultView.printStartPlayersCards(getDealerInfo(), getPlayerInfos());
}

private void askBettingMoney() {
for (Player player : blackjackGame.getPlayers()) {
bettingTable.placeBet(player, new Money(InputView.askBettingMoney(player.getName().getValueOf())));
}
}

private void participantsHit() {
for (Player player : playerManager.getPlayers()) {
for (Player player : blackjackGame.getPlayers()) {
playerHit(player);
}

if (blackjackService.isDealerHit()) {
if (blackjackGame.isDealerHit()) {
ResultView.printDealerOneMoreCard();
}
}

private void playerHit(Player player) {
while (canHitAndDraw(player)) {
ResultView.printPlayerCards(player.getName(), player.getCards());
}
}

private void finalizeGameResult() {
for (Player player : playerManager.getPlayers()) {
blackjackService.finalizeGameResult(player);
ResultView.printPlayerCards(getPlayerInfo(player));
}
}

Expand All @@ -67,12 +64,66 @@ private boolean canHitAndDraw(Player player) {
return false;
}

if (InputView.askHit(player.getName())) {
blackjackService.playerHit(player);
if (InputView.askHit(player.getName().getValueOf())) {
blackjackGame.playerHit(player);
return true;
}

ResultView.printPlayerCards(player.getName(), player.getCards());
ResultView.printPlayerCards(getPlayerInfo(player));
return false;
}

private void finalizeGameResult() {
blackjackGame.decideDealerResult();

DealerResultDto dealerResultDto = getDealerResult();
List<PlayerResultDto> playerResultDtos = getPlayerResultInfos();

ResultView.printCardsAndScoreResult(dealerResultDto, playerResultDtos);
ResultView.printRankResult(dealerResultDto, playerResultDtos);

ResultView.printDealerProfit(dealerResultDto);
playerResultDtos.stream()
.forEach(playerResultDto -> ResultView.printPlayerProfit(playerResultDto));
}

private DealerInfoDto getDealerInfo() {
return new DealerInfoDto(blackjackGame.getDealer().getCards());
}

private PlayerInfoDto getPlayerInfo(Player player) {
return new PlayerInfoDto(player.getName().getValueOf(), player.getCards());
}

private List<PlayerInfoDto> getPlayerInfos() {
return blackjackGame.getPlayers().stream()
.map(player -> new PlayerInfoDto(
player.getName().getValueOf(),
player.getCards()
))
.collect(Collectors.toList());
}

public DealerResultDto getDealerResult() {
Dealer dealer = blackjackGame.getDealer();
Money dealerProfit = bettingTable.getDealerProfit(blackjackGame.getPlayers(), dealer);

return new DealerResultDto(dealer.getCards(), dealer.getScore(), dealer.getRecord(), dealerProfit);
}

public List<PlayerResultDto> getPlayerResultInfos() {
List<PlayerResultDto> resultDtos = new ArrayList<>();
Dealer dealer = blackjackGame.getDealer();

for (Player player : blackjackGame.getPlayers()) {
resultDtos.add(
new PlayerResultDto(player.getName().getValueOf(),
player.getCards(),
player.getScore(),
player.decideWinStatus(dealer),
bettingTable.calculateProfit(player, dealer)));
}

return resultDtos;
}
}
45 changes: 45 additions & 0 deletions src/main/java/domain/BettingTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package domain;

import domain.participant.Dealer;
import domain.participant.WinStatus;
import domain.participant.player.Player;
import domain.vo.Money;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BettingTable {
private static final double BLACKJACK_BONUS = 1.5;
private final Map<Player, Money> bettingTable = new HashMap<>();

public void placeBet(Player player, Money money) {
bettingTable.put(player, money);
}

public Money calculateProfit(Player player, Dealer dealer) {
WinStatus playerWinStatus = player.decideWinStatus(dealer);

if (playerWinStatus == WinStatus.WIN && player.isBlackjack()) {
return new Money(bettingTable.get(player).multiplyDouble(BLACKJACK_BONUS));
}

if (playerWinStatus == WinStatus.LOSS) {
return bettingTable.get(player).negate();
}

if (playerWinStatus == WinStatus.DRAW) {
return new Money(BigDecimal.ZERO);
}

return bettingTable.get(player);
}

public Money getDealerProfit(List<Player> players, Dealer dealer) {
return players.stream()
.map(player -> calculateProfit(player, dealer))
.reduce(new Money(BigDecimal.ZERO), Money::add)
.negate();
}
}
79 changes: 79 additions & 0 deletions src/main/java/domain/BlackjackGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package domain;

import domain.card.CardDeck;
import domain.participant.Dealer;
import domain.participant.HandCards;
import domain.participant.Participant;
import domain.participant.WinStatus;
import domain.participant.player.Player;
import domain.participant.player.PlayerGroup;
import domain.vo.Name;

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

public class BlackjackGame {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

μ „λ°˜μ μœΌλ‘œ λ¦¬νŒ©ν† λ§μ΄ 잘 λ˜μ—ˆλ„€μš”.

μ €λ²ˆ pr을 받은 μƒνƒœμ—μ„œ 고민을 ν–ˆλŠ”λ°μš”,
ν˜„μ—… 관점에선 리뷰가 ν•„μš”ν–ˆλ˜ 건 λ§žμ§€λ§Œμš”

  1. 이미 큰 λ‹¨μœ„μ˜ λ¦¬νŒ©ν† λ§μ„ μ‹€ν—˜ν•΄λ³΄μ‹  μž…μž₯
  2. μš°ν…Œμ½”μ—μ„œ 사싀상 첫 λ―Έμ…˜
  3. ν•œλ²ˆμ”© μ½”λ©˜νŠΈλ‘œ λ³΄μ—¬μ£Όμ‹œλŠ” ν”λ“€λ¦¬λŠ” λͺ¨μŠ΅

μ—¬λŸ¬κ°€μ§€λ₯Ό κ³ λ €ν–ˆμ„ λ•Œ 이후 μžˆμ„ λ―Έμ…˜μ—μ„œ 고민듀을 ν•˜λ‚˜μ”© ν•΄κ²°ν•΄ λ‚˜κ°€μ‹œλŠ” 게 λ§žμ§€ μ•Šμ„κΉŒ μƒκ°ν•΄μ„œ μ–΄ν”„λ‘œλΈŒλ₯Ό ν• κΉŒ 말까 κ³ λ―Όν–ˆμ–΄μš”.
λ¦¬λ·°μ–΄λŠ” ν˜„μ§μžμ˜ μ‹œμ„ μ„ μ£ΌκΈ° μœ„ν•΄ μžˆλ‹€κ³  μƒκ°ν•˜κΈ° λ•Œλ¬Έμ— κ²°κ΅­ λ³€κ²½ μš”μ²­μ„ λ“œλ ΈλŠ”λ°μš”

잘 λ°˜μ˜ν•΄μ£Όμ…”μ„œ λ“œλ¦¬κΈΈ 잘 ν–ˆλ‹€λŠ” 생각이 λ“œλ„€μš”. LLM 이후 μ½”λ“œ μž‘λ²• λ³΄λ‹€λŠ” μ™œ ν•˜λŠ”μ§€, 무엇 λ•Œλ¬Έμ— ν•˜λŠ”μ§€κ°€ μ€‘μš”ν•΄μ§€κ³  μžˆλŠ”λ°μš”, νŒŒλ„μ²˜λŸΌ μ—¬λŸ¬κ°€μ§€λ₯Ό μ‹œλ„ν•΄λ³΄κ³  κΉ¨λ‹¬μŒμ„ μ–»μ–΄λ‚˜κ°€λŠ” μ—­λŸ‰μ΄ κ°€μž₯ μ€‘μš”ν•΄μ§ˆκ±°λΌκ³  μƒκ°ν•΄μš”. μ•žμœΌλ‘œλ„ ν™”μ΄νŒ…μž…λ‹ˆλ‹€.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

λ¦¬νŒ©ν† λ§μ„ μ—¬λŸ¬ 번 ν•΄λ³΄λ‹ˆ νž˜λ“€μ§€λ§Œ 그만큼 λ§Žμ€ κ±Έ 배울 수 μžˆμ—ˆλ˜ 것 κ°™μ•„μš”!
이둠적인 곡뢀λ₯Ό ν•΄μ•Ό 더 ν™•μ‹€νžˆ μ•Œ 수 μžˆκ² μ§€λ§Œ, 이제 μ–΄λ–€ μ½”λ“œκ°€ κΉ”λ”ν•œ μ½”λ“œλ©° κ·Έ μ½”λ“œλ₯Ό μ™œ ν•΄μ•Όν•˜λŠ”μ§€, μ–΄λ–»κ²Œ ν•΄μ•Όν•˜λŠ”μ§€λŠ” μ‘°κΈˆμ€ μ•Œ 것 κ°™μŠ΅λ‹ˆλ‹€
μ œκ°€ 쀑간에 많이 헀맀기도 ν•˜κ³  μ‹€μˆ˜ν•œ κ±Έ 또 μ‹€μˆ˜ν•˜κΈ°λ„ ν–ˆλŠ”λ° κΌΌκΌΌν•˜κ²Œ λ‹€ λ¦¬λ·°ν•΄μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€!!
웨지 덕뢄에 μ•žμœΌλ‘œ μš°ν…Œμ½” λ―Έμ…˜μ„ ν•΄λ‚˜κ°ˆ κΈ°λ°˜μ„ 잘 λ§ˆλ ¨ν•œ 것 κ°™μ•„μš”
νž˜λ“€κΈ°λ„ ν–ˆμ§€λ§Œ 또 μ½”λ“œκ°€ λ°”λ€ŒλŠ” κ±Έ λ³΄λ©΄μ„œ μž¬λ°ŒκΈ°λ„ ν–ˆμ–΄μš”γ…Žγ…Ž
거의 3μ£Ό λ™μ•ˆ 정말 많이 λ°°μ› κ³  κ°μ‚¬ν–ˆμŠ΅λ‹ˆλ‹€!
ν–‰λ³΅ν•˜μ„Έμš”πŸ˜„

private static final int START_CARD_COUNT = 2;
private final Dealer dealer = new Dealer(new HandCards());
private final CardDeck cardDeck = new CardDeck();
private PlayerGroup playerGroup;

public void registerPlayers(List<String> playerNames) {
List<Player> players = new ArrayList<>();

for (String playerName : playerNames) {
Player player = new Player(new Name(playerName), new HandCards());
players.add(player);
}

playerGroup = new PlayerGroup(players);
}

public void dealParticipantsCardsOut() {
dealCardsOut(dealer);

for (Player player : playerGroup.getPlayers()) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

μ΄ˆκΈ°μ— 객체가 playerGroup이 ν• λ‹Ήλ˜μ§€ μ•Šμ€ μƒνƒœμ˜€λ‹€λ©΄, (즉 registerPlayers이 μ‹€ν–‰λœ 적 μ—†λ‹€λ©΄, ) μ •μƒλ™μž‘ν•˜μ§€ μ•Šμ„ λ©”μ„œλ“œμ—μš”.

μΌμ’…μ˜ State machine (μƒνƒœκ°€ 있고 μƒνƒœμ— 따라 행동이 λ³€ν™”ν•˜λŠ” 객체)μΈλ°μš”, μœ μ € 미등둝 μƒνƒœμ™€ μœ μ € 등둝 μƒνƒœμš”. 이런 객체λ₯Ό λ§Œλ“€μ—ˆλ‹€λ©΄ state에 λ”°λ₯Έ μ˜ˆμ™Έμ²˜λ¦¬λ₯Ό 잘 ν•΄μ£Όμ–΄μ•Ό ν•΄μš”.

그런데 이 κ°μ²΄λŠ” μƒνƒœμ— 따라 ν–‰μœ„λ₯Ό λ°”κΏ€ ν•„μš”κ°€ μ—†μœΌλ‹ˆ, μ΄ˆκΈ°μ— playerGroupλ₯Ό ν• λ‹Ήν•˜λŠ” μ½”λ“œλ‘œ λ§Œλ“€ 수 있으면 쒀더 μ’‹κ² μ£ . (μƒμ„±μž μ£Όμž…λ“±μœΌλ‘œ)
κ°„λ‹¨ν•œ 처리둜 μƒνƒœκ°€ μ‚¬λΌμ§€λŠ” μ…ˆμ΄λ‹ˆ μœ μ§€λ³΄μˆ˜ν•˜κΈ° 쒋은 μ½”λ“œκ°€ λ©λ‹ˆλ‹€.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

생각해보면 ν”Œλ ˆμ΄μ–΄ μˆ˜λŠ” κ²Œμž„ μ‹œμž‘κ³Ό 끝 사이에 λ³€ν•˜λŠ” 일이 μ—†μœΌλ‹ˆ μ›¨μ§€μ˜ 말처럼 μ²˜λ¦¬ν•΄μ£ΌλŠ” 게 훨씬 κΉ”λ”ν•΄μ§ˆ 것 κ°™μ•„μš”. 객체가 μ–Έμ œ μ–Όλ§ˆλ‚˜ μ™„μ „ν•˜κ²Œ 생성될 수 μžˆλŠ”μ§€ 늘 고민해봐야 ν•˜λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€... κ°μ‚¬ν•©λ‹ˆλ‹€!

dealCardsOut(player);
}
}

public void playerHit(Player player) {
player.drawCard(cardDeck.getCard());
}

public boolean isDealerHit() {
if (dealer.isDealerHit()) {
dealer.drawCard(cardDeck.getCard());
return true;
}
return false;
}

public void decideDealerResult() {
playerGroup.getPlayers().stream()
.map(player -> decidePlayerResult(player))
.forEach(winstatus -> saveDealerResult(winstatus));
}

private void saveDealerResult(WinStatus winStatus) {
dealer.saveResult(winStatus);
}

public Dealer getDealer() {
return dealer;
}

private void dealCardsOut(Participant participant) {
for (int i = 0; i < START_CARD_COUNT; i++) {
participant.drawCard(cardDeck.getCard());
}
}

public WinStatus decidePlayerResult(Player player) {
return player.decideWinStatus(dealer);
}

public List<Player> getPlayers() {
return List.copyOf(playerGroup.getPlayers());
}
}
4 changes: 4 additions & 0 deletions src/main/java/domain/participant/HandCards.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public int calculateCardsScore(){
return score;
}

public int getHandCardsSize() {
return cards.size();
}

private int calculateCardsScoreExceptAce() {
int score = 0;

Expand Down
14 changes: 13 additions & 1 deletion src/main/java/domain/participant/Participant.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import java.util.List;

public class Participant {
public abstract class Participant {
protected static final int BLACKJACK_CONDITION = 21;
protected HandCards handCards;

Expand All @@ -27,4 +27,16 @@ public List<String> getCards() {
public int getScore() {
return handCards.calculateCardsScore();
}

public boolean isBlackjack() {
return isInitialHand() && isBlackjackConditionScore();
}

private boolean isInitialHand() {
return handCards.getHandCardsSize() == 2;
}

private boolean isBlackjackConditionScore() {
return getScore() == BLACKJACK_CONDITION;
}
}
Loading