-
Notifications
You must be signed in to change notification settings - Fork 313
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
[Step4] 블랙잭(베팅) 구현 #816
base: seokho-ham
Are you sure you want to change the base?
[Step4] 블랙잭(베팅) 구현 #816
Changes from all commits
a663c20
bfeaf5f
3cbfa99
6cab392
859e7eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,9 @@ import blackjack.domain.Deck | |
import blackjack.domain.Player | ||
import blackjack.domain.PlayerGameResult | ||
import blackjack.domain.PlayerStatus | ||
import blackjack.domain.PlayerWinLoseResult | ||
import blackjack.domain.Players | ||
import blackjack.view.PlayerInfo | ||
|
||
class BlackJackGame private constructor( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이전 단계에서 딜러와 플레이어의 중복 코드에 대한 고려 + 차이가 나는 로직에 대해 적절히 분리하는 작업이 선행되어서 이번 단계에서는 큰 변경사항이 없었네요! 이 부분이 사실 블랙잭 미션의 핵심 중 하나인데요, 딜러와 플레이어는 언뜻 보면 같은 참가자로 묶였을 때 더 직관적인 것처럼 보이나, 사실 분리했을 때 오히려 구조가 더 깔끔해집니다. 4단계를 진행하면서 이 구조의 이점이 잘 와닿으셨으면 좋겠네요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 어제의 수업을 듣고 상태 패턴을 적용해볼 계획도 있으신가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 마지막이니까 한번 도전해보고 다시 리뷰 요청 드리겠습니다:) |
||
val dealer: Dealer, | ||
|
@@ -40,7 +42,14 @@ class BlackJackGame private constructor( | |
} | ||
|
||
fun getGameResult(): GameResult { | ||
val playerGameResults = players.members.map { PlayerGameResult(it.name, it.compareWithDealer(dealer)) } | ||
val playerGameResults = | ||
players.members.map { | ||
PlayerGameResult( | ||
it.name, | ||
it.betAmount, | ||
PlayerWinLoseResult.compareResult(dealer, it), | ||
) | ||
} | ||
val dealerResult = DealerResult(playerGameResults) | ||
return GameResult(dealerResult, playerGameResults) | ||
} | ||
|
@@ -62,19 +71,19 @@ class BlackJackGame private constructor( | |
private const val DEFAULT_CARD_COUNT = 2 | ||
|
||
fun createGame( | ||
playerNames: List<String>, | ||
playerInfos: List<PlayerInfo>, | ||
deck: Deck, | ||
): BlackJackGame { | ||
val dealer = createDealer(deck) | ||
val players = createPlayers(playerNames, deck) | ||
val players = createPlayers(playerInfos, deck) | ||
return BlackJackGame(dealer, players, deck) | ||
} | ||
|
||
private fun createPlayers( | ||
playerNames: List<String>, | ||
playerInfos: List<PlayerInfo>, | ||
deck: Deck, | ||
): Players { | ||
val players = Players.from(playerNames) | ||
val players = Players.from(playerInfos) | ||
players.drawDefaultCards(deck, DEFAULT_CARD_COUNT) | ||
return players | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,15 @@ | ||
package blackjack | ||
|
||
import blackjack.domain.CalculateEarnAmount | ||
import blackjack.domain.PlayerGameResult | ||
import blackjack.domain.PlayerWinLoseResult | ||
|
||
class DealerResult(playerGameResults: List<PlayerGameResult>) { | ||
var winCount: Int = 0 | ||
private set | ||
var loseCount: Int = 0 | ||
private set | ||
var pushCount: Int = 0 | ||
private set | ||
class DealerResult(playerGameResults: List<PlayerGameResult>) : CalculateEarnAmount { | ||
private var earnAmount: Int = | ||
playerGameResults | ||
.map { it.getEarnAmount() } | ||
.fold(0) { total, amount -> total + amount }.toInt() | ||
|
||
init { | ||
playerGameResults.forEach { | ||
when (it.result) { | ||
PlayerWinLoseResult.WIN -> loseCount++ | ||
PlayerWinLoseResult.LOSE -> winCount++ | ||
PlayerWinLoseResult.PUSH -> pushCount++ | ||
} | ||
} | ||
override fun getEarnAmount(): Int { | ||
return -earnAmount | ||
Comment on lines
+6
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. earnAmount가 variable이 될 필요가 있을까요~? |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package blackjack.domain | ||
|
||
data class BetAmount(val amount: Double) { | ||
init { | ||
require(amount > ZERO) { "베팅 금액은 0원 이상이어야 합니다." } | ||
} | ||
|
||
companion object { | ||
private const val ZERO = 0 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package blackjack.domain | ||
|
||
fun interface CalculateEarnAmount { | ||
fun getEarnAmount(): Int | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 어떤 의도로 만드신 인터페이스인지 이해했습니다! 다만 현재는 인터페이스의 |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,13 @@ | ||
package blackjack.domain | ||
|
||
import kotlin.math.roundToInt | ||
|
||
data class PlayerGameResult( | ||
val name: String, | ||
val betAmount: BetAmount, | ||
val result: PlayerWinLoseResult, | ||
) | ||
) : CalculateEarnAmount { | ||
override fun getEarnAmount(): Int { | ||
return (betAmount.amount * result.odds).roundToInt() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,25 @@ | ||
package blackjack.domain | ||
|
||
enum class PlayerWinLoseResult { | ||
WIN, | ||
LOSE, | ||
PUSH, | ||
enum class PlayerWinLoseResult(val odds: Double) { | ||
WIN(1.0), | ||
LOSE(-1.0), | ||
PUSH(0.0), | ||
BLACKJACK(1.5), ; | ||
|
||
companion object { | ||
fun compareResult( | ||
dealer: Dealer, | ||
player: Player, | ||
): PlayerWinLoseResult { | ||
return when { | ||
dealer.isBlackJack() -> LOSE | ||
dealer.isBust() -> WIN | ||
player.isBust() -> LOSE | ||
player.isBlackJack() && !dealer.isBlackJack() -> BLACKJACK | ||
dealer.getCardSum() > player.hand.calculateCardsMaxSum() -> LOSE | ||
dealer.getCardSum() == player.hand.calculateCardsMaxSum() -> PUSH | ||
else -> WIN | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package blackjack.view | ||
|
||
data class PlayerInfo( | ||
val name: String, | ||
val amount: Double, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package blackjack | ||
|
||
import blackjack.domain.BetAmount | ||
import blackjack.domain.PlayerGameResult | ||
import blackjack.domain.PlayerWinLoseResult | ||
import io.kotest.core.spec.style.StringSpec | ||
import io.kotest.matchers.shouldBe | ||
import kotlin.math.roundToInt | ||
|
||
class PlayerGameResultTest : StringSpec({ | ||
"최종 수익을 계산한다." { | ||
// given | ||
val result = PlayerGameResult("player1", BetAmount(10000.0), PlayerWinLoseResult.WIN) | ||
|
||
// when | ||
val earnAmount = result.getEarnAmount() | ||
|
||
// then | ||
earnAmount shouldBe (10000.0 * 1.0).roundToInt() | ||
} | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from: #809 (comment)
예를 들어 Cards의 add 함수가 바뀐다거나, Cards의 테스트 과정에서 많은 카드 더미가 있어야 하는 전제 조건이 있는 경우 이점을 얻을 수 있을 것 같아요.
저도 테스트 코드만을 위한 코드 변경은 최대한 보수적으로 접근하려고 하는데요, (예를 들어 테스트코드만을 위해 private -> public으로 전환한다던지?) 예외적으로 생성자 파라미터의 경우 객체를 초기화하는 전제 조건 주입 목적으로는 허용하고 있습니다! 외부에서 변경 가능하지 않도록 설계할 수 있기도 하구요!
이 부분은 재성님이 수업에서도 언급하셔서 제안드렸으나 개발자마다 주관적인 의견이 있을 것이라고 생각합니다 🙂