見出し画像

#15(最終回) 【React.jsで、BlackJackを作る】:カードが少なくなったらシャッフルする。あとがき。

これまで

前回は、スコアの計算や表示の見直しをしました。

BlackJack アプリの sandbox

BlackJack アプリの sandbox は以下です。

この sandbox は、連載が進むのと同時に更新されていきます
「私も作りたい!」という方は、フォークして一緒に作り始めることができます。

はじめから作りたい方はこちら

バグ修正

プレイヤーのスコアがちょうど 21 になると無条件で勝ちになってしまうバグがありました。
本来なら、プレイヤーのスコアが 21 になればディーラーもカードを引いて勝敗をジャッジするというのが正しい処理です。

src/BlackJack.js の関数 reducer()

switch文の case "check": の箇所を見つけてください。

case "check": {
 // ディーラーまたはプレイヤーがブラックジャック
 if (
   BlackJackUtilities.isBlackJack(state.dealersHand) ||
   BlackJackUtilities.isBlackJack(state.playersHand)
 ) {
   return { ...state, isTurnEnd: true };
 }
 // プレイヤーのスコアが 21 になったらディーラーがカードを引いてターンは終わり
 if (BlackJackUtilities.getTotal(state.playersHand) === 21) {
   const [newDeck, newHand] = dealForDealer(state.deck, state.dealersHand);
   return {
     ...state,
     deck: newDeck,
     dealersHand: newHand,
     isTurnEnd: true
   };
 }
 // プレイヤーがバーストしたらターンは終わり
 if (BlackJackUtilities.getTotal(state.playersHand) > 21) {
   return { ...state, isTurnEnd: true };
 }
 return { ...state };
}

元々、プレイヤーのスコアが「21 以上であれば」という条件分岐のみでしたが、「21 ちょうどであれば」「21 を超えたら」という条件分岐に分けました。

デッキの数が少なくなったらシャッフルする

これまで特に触れてきませんでしたが、現状ではカードがなくなると例外が発生します
それもそのはず。デッキがなくなっても補充していないですからね。

実際のブラックジャックゲームでは、カードがある程度少なくなった時点でデッキをシャッフルします。
その際の使用量を Penetration といい、「Penetration 80%」といえば「デッキのカードの 80% を使用したらシャッフルする」ということになります。

ここでも、その仕様を採用することにします。

カードの最少枚数を求める

src/BlackJack.js で最少枚数を格納する state minimumNumber を定義します。

const penetration = 0.8;

function getMinimumNumber(initialDeck, penetration) {
 return initialDeck.length - Math.floor(initialDeck.length * penetration);
}

次に、初期 state 定義を以下のように修正します。

const initialState = {
 deck: initialDeck,
 minimumNumber: getMinimumNumber(initialDeck, penetration),
 dealersHand: [],
 playersHand: [],
 isTurnEnd: false
};

デッキを複数セット用意できるようにする

現状はデッキを1セットしか作れませんので、複数セット用意できるようにします。
引数で作成したいデッキのセット数を渡すことで可変にできるようにしました。

src/utilities/BlackJackUtilities.js

export function getDeck(numberOfDeck = 1) {
 const suits = ["♠", "♣", "❤", "♦"];
 // prettier-ignore
 const ranks = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"];
 const deck = [];
 for (let i = 0; i < numberOfDeck; i++) {
   suits.forEach((suit) =>
     ranks.forEach((rank) => deck.push({ suit: suit, rank: rank }))
   );
 }
 return deck;
}

シャッフルする処理を追加する

フック useEffect() を使用して、デッキの数が変わる度にシャッフルが必要かをチェックするようにしました。

useEffect(() => {
 if (state.deck.length <= state.minimumNumber) {
   dispatch({ type: "shuffle" });
   toast("Shuffled!!", {
     style: {
       borderRadius: "10px",
       background: "#737373",
       color: "#ffffff"
     }
   });
 }
}, [state.deck, state.minimumNumber]);

また、デッキがシャッフルされたことをユーザーに知らせるため、react-hot-toast というパッケージを使用して、画面下部にトーストを表示するようにしました。
すぐに使えていい感じでした。

現在の画面

今回の修正で画面は以下のようになりました。

画像1

詳しい変更はこちら

Codesandbox は GitHub連携ができます。
以下は今回のコミットによる差分です。

あとがき

今回でブラックジャックは完成です!
まだ jsDoc が古いままですが、追々修正してプログラミング・プラモデル Vol.3 に繋げていきたいと思います。

さて、15回に渡る連載で Border7 を BlackJack に作り変えてきましたが、いかがだったでしょうか?
「私の選択を紹介する」というオレオレなテーマで連載してきましたが、単なる製作日記になってしまった感が否めません。
そういった意味では課題が残りましたが、フック useReducer() を紹介できるいい機会にもなったのでひとまずは良しとしておきます。

もし「一緒にブラックジャック作ってたよ!」という方がいらっしゃいましたら、ぜひあなたの作ったコードをご紹介頂ければ嬉しいです。

さて、あとがきの冒頭で「プログラミング・プラモデル Vol.3」について触れた通り、次の連載では「プログラミング・プラモデル Vol.3 で BlackJack を作る」というテーマでお送りしたいと思います。

フック useReducer() の詳しい使い方util 処理の作り方など、「Vol.2 Border7 」のときとはまた違った処理を一緒に組み立てていきます。
お楽しみに!

この記事があなたのお役に立ちましたら、よろしければサポートをお願いいたします! より良い記事をお届けできるよう、活動費に充てさせていただきます。