ブラックジャックゲーム分析-2
いずれにしても、分析したいので、長すぎるので、文章を共有しました!
ブラックジャックゲーム分析-1
domain card Card.class CardRepository.class Cards.class Deck.class Symbol.enum Type.enum controller blackjackController.class user strategy.draw DealerDrawStrategy.class PlayerDrawStrategy.class DrawStrategy.interface Dealer.class Player.class User.interface Users.class BettingMoney.class PlayerIntentionType.enum PlayerMoneys.class ResultType.enum ScoreType.enum view InputView OutputView
DrawStrategy.inteface
PlayerDrawStrategy.class
DealerDrawStrategy.class
非常に簡潔な文法ですが、後で見れば分かるように、これらの友人のおかげで条件確認文がどんなに簡単になったかがわかります.
突然私はまた私のプロジェクトがどんなに寒いかを思い出した.
BettingMoney.class
純粋なプレイヤーはいくら賭けたかしか知らない.
PlayerMoneys.class
詳しく説明してから...
ResultType.enum
まずゲーム結果をEnumで管理する
ゲームの結果は以下の通りで、それぞれ特徴があります. BLACKJACK_WIN Predicateを使用して Predicateを使用して を識別し、2枚のカードがあり、点数は21です.機能による発注金額*1.5返却 WIN Predicateを使用して
Predicate注入 ,ジェネレータにより定数trueを返す. Functionを使用して2倍の賭けを で返します.
DRAW Predicateを使用して Predicateを使用して手の点が0(分割) であるか否かを判別する.機能で賭け*0 を返却
LOSE Predicateを使用して
Predicate注入 ,ジェネレータにより定数trueを返す. 機能で賭けを返却*-1 これらのプロパティを理解し、from()メソッドを開きます.
パラメータとして
なぜこの変数名を使うのか分かりません.プレイヤーとディーラーの方が直感的だと思います.
点数差によっては、いろいろな場所が必要になるかもしれません.(BLACKJACK WINとWIN)
BLACKJACK WINとWINの違いはカード長数です.
最初の第2章のハンマーポイントで、21が完成したのが黒いジャックです.
WINの場合、BlackjackOrBurstJudgeのパラメータを使用して定数trueが得られるため、BlackJackの場合、2つのEnumが必要となる.
PlayerMoneys.getPlayerProfitsBy()メソッド.
各プレイヤーは()メソッドで結果Enumを取得し、対応するEnumのgetPrize変数で対応するボーナス結果を取得する.
ScoreType.enum
ResultTypeを理解していれば特に何もありません.
結果がBURSTの場合は0点を返します.
BlackjackController.class
コントローラはゲーム全体の流れを行うべきだと思っていましたが、
ゲームのマスタロジックだけで、ゲームの流れは外のアプリケーションにあります.
この前のprecos学習でこれについてのコメントがあったようなので、もう一度見てみます.
構造を変えたら、外でもう1階包むだけでいいです.
コントローラはメソッドの流れを参照できます.
プロセスが重要だからです.
InputView.class
しかしその出力情報はハードコーディングではありませんか???
主論理と直接関係がないから?
全てがStatic Methodなので内部ロジックが見えない…?
Application.java
プロジェクトを観察したとき、最も印象に残ったのは静的な方法がたくさんあることです.
viewのような場合は,それでも部分的に静的な方法が多い.
この部分はまだ勉強が足りないようだ.
まだまだ足りないこともたくさんありましたが、たくさん勉強になりました.
やはり達人の足どりに従うのは意味がある.
ある程度言えば、これは急速に上昇する道のようだ.
ブラックジャックゲーム分析-1
需要分析
パッケージ分析
ぶんせき
DrawStrategy.inteface
public interface DrawStrategy {
public boolean canDraw(int score);
}
トレーダーやプレイヤーが落下できるかどうかを確認する方法です.PlayerDrawStrategy.class
public class PlayerDrawStrategy implements DrawStrategy{
private static final int BLACKJACK_SCORE = 21;
private static final int BURST_SCORE = 0;
@Override
public boolean canDraw(int score) {
return score < BLACKJACK_SCORE && score != BURST_SCORE;
}
}
canDraw()を上書きし、各キャラクタに条件を追加します.DealerDrawStrategy.class
public class DealerDrawStrategy implements DrawStrategy{
private static final int BURST_SCORE = 0;
private static final int DEALER_DRAW_BOUND = 17;
@Override
public boolean canDraw(int score) {
return score < DEALER_DRAW_BOUND && score != BURST_SCORE;
}
}
プレイヤーと戦果洞.非常に簡潔な文法ですが、後で見れば分かるように、これらの友人のおかげで条件確認文がどんなに簡単になったかがわかります.
突然私はまた私のプロジェクトがどんなに寒いかを思い出した.
BettingMoney.class
public class BettingMoney {
private static final int MIN_MONEY = 0;
private final int bettingMoney;
private BettingMoney(int bettingMoney) {
if (bettingMoney <= MIN_MONEY) {
throw new IllegalArgumentException("베팅 금액은 최소 1원 이상이어야 합니다.");
}
this.bettingMoney = bettingMoney;
}
public static BettingMoney of(int bettingMoney) {
return new BettingMoney(bettingMoney);
}
public static BettingMoney of(String bettingMoney) {
return new BettingMoney(Integer.parseInt(bettingMoney));
}
public int intValue() {
return bettingMoney;
}
}
プレイヤーたちは賭け金のキャラクターの対象を担当する.純粋なプレイヤーはいくら賭けたかしか知らない.
PlayerMoneys.class
public class PlayerMoneys {
private static final int MULTIPLIER_TO_REVERSE = -1;
private final Map<Player, Integer> playerMoneys;
public PlayerMoneys(Map<Player, Integer> playerMoneys) {
this.playerMoneys = playerMoneys;
}
public Map<User, Double> getTotalPrizes(User dealer) {
Map<Player, Double> playersProfits = getPlayerProfitsBy(dealer);
Map<User, Double> totalResult = getUserProfitsOf(dealer, playersProfits);
return totalResult;
}
private Map<Player, Double> getPlayerProfitsBy(User dealer) {
Map<Player, Double> playerProfits = new HashMap<>();
playerMoneys.keySet().forEach(player ->
playerProfits.put(player, ResultType.from(player, dealer).getProfit(playerMoneys.get(player))));
return playerProfits;
}
private Map<User, Double> getUserProfitsOf(User dealer, Map<Player, Double> playerProfits) {
Map<User, Double> userProfit = new LinkedHashMap<>();
userProfit.put(dealer, playerProfits.values().stream()
.mapToDouble(playerProfit -> playerProfit * MULTIPLIER_TO_REVERSE)
.sum());
playerProfits.forEach(userProfit::put);
return userProfit;
}
}
プレイヤーの収益の計算を担当します.詳しく説明してから...
ResultType.enum
public enum ResultType {
BLACKJACK_WIN(
scoreGap -> scoreGap > 0,
cards -> cards.isInitialSize() && cards.getPoint() == 21,
money -> money.doubleValue() * 1.5
),
WIN(
scoreGap -> scoreGap > 0,
Integer::doubleValue
),
DRAW(
scoreGap -> scoreGap == 0,
cards -> cards.getPoint() != 0,
money -> money.doubleValue() * 0
),
LOSE(
scoreGap -> scoreGap <= 0,
money -> money.doubleValue() * -1
);
private final Predicate<Integer> resultJudge;
private final Predicate<Cards> blackjackOrBurstJudge;
private final Function<Integer, Double> getPrize;
ResultType(Predicate<Integer> resultJudge, Function<Integer, Double> getPrize) {
this(resultJudge, cards -> true, getPrize);
}
ResultType(Predicate<Integer> resultJudge, Predicate<Cards> blackjackOrBurstJudge, Function<Integer, Double> getPrize) {
this.resultJudge = resultJudge;
this.blackjackOrBurstJudge = blackjackOrBurstJudge;
this.getPrize = getPrize;
}
public static ResultType from(User result, User compared) {
return Arrays.stream(ResultType.values())
.filter(type -> type.resultJudge.test(result.getScoreMinusBy(compared)))
.filter(type -> type.blackjackOrBurstJudge.test(result.openAllCards()))
.findFirst()
.orElseThrow(NullPointerException::new);
}
public double getProfit(int bettingMoney) {
return getPrize.apply(bettingMoney);
}
}
ㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎまずゲーム結果をEnumで管理する
ゲームの結果は以下の通りで、それぞれ特徴があります.
scoreGap > 0
を識別ScoreGap > 0
を識別Predicate注入
ScoreGap == 0
を識別ScoreGap <= 0
を識別Predicate注入
パラメータとして
User result
(プレイヤー)とUser compared
(ディーラー)を取得します.なぜこの変数名を使うのか分かりません.プレイヤーとディーラーの方が直感的だと思います.
Arrays.stream(ResultType.values())
-->Enumのプロパティをストリームにインポートします..filter(type -> type .resultJudge.test(result.getScoreMinusBy(compared)))
-->プレイヤーとディーラーの点数差を計算し、Enumの最初の属性resultJudgeのテストを行います.点数差によっては、いろいろな場所が必要になるかもしれません.(BLACKJACK WINとWIN)
.filter(type -> type .blackjackOrBurstJudge.test(result.openAllCards()))
-->プレイヤーの手を持ってBlackjackOrBurstJudgeのテストを行います.BLACKJACK WINとWINの違いはカード長数です.
最初の第2章のハンマーポイントで、21が完成したのが黒いジャックです.
WINの場合、BlackjackOrBurstJudgeのパラメータを使用して定数trueが得られるため、BlackJackの場合、2つのEnumが必要となる.
.findFirst().orElseThrow(NullPointerException::new);
-->だから最初のEnumしか持っていないので、何も掛けていないと間違いです.PlayerMoneys.getPlayerProfitsBy()メソッド.
各プレイヤーは()メソッドで結果Enumを取得し、対応するEnumのgetPrize変数で対応するボーナス結果を取得する.
ScoreType.enum
public enum ScoreType {
BURST(point -> point > 21, point -> 0),
NORMAL(point -> point <= 21, point -> point);
private final Predicate<Integer> scoreJudge;
private final Function<Integer, Integer> getScore;
ScoreType(Predicate<Integer> scoreJudge, Function<Integer, Integer> getScore) {
this.scoreJudge = scoreJudge;
this.getScore = getScore;
}
public static ScoreType of(int point) {
return Arrays.stream(ScoreType.values())
.filter(scoreType -> scoreType.scoreJudge.test(point))
.findFirst()
.orElseThrow(NullPointerException::new);
}
public int getScore(int point) {
return getScore.apply(point);
}
}
これは最終結果点数を計算するEnumです.ResultTypeを理解していれば特に何もありません.
結果がBURSTの場合は0点を返します.
BlackjackController.class
public class BlackjackController {
public static void proceedInitialPhase(Users users, Deck deck) {
printInitialDistribution(users.getPlayers());
for (User user : users) {
user.proceedInitialPhase(deck);
printInitialStatus(user);
}
}
public static PlayerMoneys getBettingMoney(List<Player> players) {
Map<Player, Integer> result = new HashMap<>();
players.forEach(player -> result.put(player, BettingMoney.of(inputBettingMoney(player)).intValue()));
return new PlayerMoneys(result);
}
public static void proceedGame(List<Player> players, User dealer, Deck deck) {
players.forEach(player -> proceedPhaseOf(player, deck));
proceedPhaseOf(dealer, deck);
}
private static void proceedPhaseOf(Player player, Deck deck) {
while (player.canDrawMore() && PlayerIntentionType.of(inputIntentionOf(player)).isWantDraw()) {
player.receive(deck.pop());
printCardsStatusOf(player);
}
}
private static void proceedPhaseOf(User dealer, Deck deck) {
while (dealer.canDrawMore())
dealer.receive(deck.pop());
printDealerDrawing();
}
}
ブラックジャックゲームのメインコントローラ.コントローラはゲーム全体の流れを行うべきだと思っていましたが、
ゲームのマスタロジックだけで、ゲームの流れは外のアプリケーションにあります.
この前のprecos学習でこれについてのコメントがあったようなので、もう一度見てみます.
構造を変えたら、外でもう1階包むだけでいいです.
コントローラはメソッドの流れを参照できます.
プロセスが重要だからです.
InputView.class
public class InputView {
private static final Scanner SCANNER = new Scanner(System.in);
public static String inputPlayerNames() {
System.out.println("게임에 참여할 사람의 이름을 입력하시오. (쉼표로 구분)");
return SCANNER.nextLine();
}
public static String inputIntentionOf(User player) {
System.out.println(player + "는 한장의 카드를 더 받겠습니까? (예는 y, 아니오는 n)");
return SCANNER.nextLine();
}
public static String inputBettingMoney(User player) {
System.out.println(player + "의 베팅 금액은?");
return SCANNER.nextLine();
}
}
OutputView.classpublic class OutputView {
private static final String DELIMITER = ", ";
public static void printInitialDistribution(List<Player> players) {
System.out.println("딜러와 " + players.stream()
.map(Player::toString)
.collect(Collectors.joining(DELIMITER)) +
"에게 2장의 카드를 나눠줬습니다.");
}
public static void printInitialStatus(User user) {
System.out.println(user + ": " + user.openInitialCards().toList().stream()
.map(Card::toString)
.collect(Collectors.joining(DELIMITER)));
}
public static void printCardsStatusOf(Player player) {
Cards cards = player.openAllCards();
System.out.println(player + "카드: " +
cards.toList().stream()
.map(Card::toString)
.collect(Collectors.joining(DELIMITER)));
}
public static void printDealerDrawing() {
emptyLine();
System.out.println("딜러는 16이하라 한장의 카드를 더 받았습니다.");
}
public static void printResultStatus(Users users) {
for (User user : users) {
Cards cards = user.openAllCards();
System.out.println(user + "카드: " +
cards.toList().stream()
.map(Card::toString)
.collect(Collectors.joining(DELIMITER)) +
" - 결과" + cards.getPoint());
}
}
public static void printResultProfit(PlayerMoneys playerMoneys, User dealer) {
Map<User, Double> totalPrizes = playerMoneys.getTotalPrizes(dealer);
emptyLine();
System.out.println("## 최종 수익");
totalPrizes.forEach(((user, money) -> System.out.println(user + ": " + money)));
}
private static void emptyLine() {
System.out.println();
}
}
ビューに関連する部分は特にありませんが、メソッドネーミングに関連する内容に注意すべきだと思います.しかしその出力情報はハードコーディングではありませんか???
主論理と直接関係がないから?
全てがStatic Methodなので内部ロジックが見えない…?
Application.java
public static void main(String[] args) {
User dealer = new Dealer();
Deck deck = Deck.of(CardRepository.toList());
Users users = Users.of(inputPlayerNames(), dealer);
PlayerMoneys playerMoneys = BlackjackController.getBettingMoney(users.getPlayers());
BlackjackController.proceedInitialPhase(users, deck);
if(dealer.isNotBlackJack()) {
BlackjackController.proceedGame(users.getPlayers(), dealer, deck);
}
printResultStatus(users);
printResultProfit(playerMoneys, dealer);
}
ポスト
プロジェクトを観察したとき、最も印象に残ったのは静的な方法がたくさんあることです.
viewのような場合は,それでも部分的に静的な方法が多い.
この部分はまだ勉強が足りないようだ.
まだまだ足りないこともたくさんありましたが、たくさん勉強になりました.
やはり達人の足どりに従うのは意味がある.
ある程度言えば、これは急速に上昇する道のようだ.
Reference
この問題について(ブラックジャックゲーム分析-2), 我々は、より多くの情報をここで見つけました https://velog.io/@jaca/블랙잭-게임-분석-2テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol