Vue3 + CompositionAPI + TypeScriptを使って、ポーカーゲームを作ってみた2

  • プロダクション
  • オピニオン
 Vue3 + CompositionAPI + TypeScriptを使って、ポーカーゲームを作ってみた2

目次

はじめに

前回の記事ではポーカーアプリを作る準備にあたって、Vue3+CompositionAPIの環境構築
を行いました。
今回は実際に作成したソースコードについて説明していきます。
前回の記事はこちら
作成したポーカーゲームはこちら

作成したファイルについて

プロジェクトディレクトリの"/src/"配下のパスがアプリのソースコードになっており、 今回は手を入れた以下のファイルに絞って説明していきます。

App.vue 
store
 - index.ts
assets
 - json/cardList.json
components
 - PorkerGame.vue
 - ResultGame.vue
 - StartGame.vue
 - PorkerGame
    - HandCard.vue
    - HandResult.vue

ソースコードの説明

App.vue

ルートパスアクセスで読み込まれるファイル。
Vuexで定義したstateのscreenTypeの値でスタート画面、ゲーム画面、結果画面のいずれかの画面を表示している。

<template>
  <div class="main">
    <div class="main-screen">
      <start-game v-if="store.getters.getScreenType === 0" />
      <porker-game v-if="store.getters.getScreenType === 1" />
      <result-game v-if="store.getters.getScreenType === 2" />
    </div>
  </div>
</template>
<script lang="ts"> import { useStore } from "vuex"; import { key } from "@/store/"; import { defineComponent } from "vue"; import StartGame from "@/components/StartGame.vue"; import PorkerGame from "@/components/PorkerGame.vue"; import ResultGame from "@/components/ResultGame.vue";
export default defineComponent({   components: {     StartGame,     PorkerGame,     ResultGame,   },   setup() {     const store = useStore(key);
    return {       store,     };   }, }); </script>
<style lang="scss" scoped> .main {   &-screen {     width: 800px;     height: 400px;     padding: 40px;     margin: 40px auto;     border: 3px solid lightgray;     border-radius: 6px;   } } </style>

/store/index.ts

Vuexのstoreファイル。
異なるcomponentで値を共有するために使用しており、
本アプリでは以下の4つのstateを定義しています。

1.screenType

表示画面を管理

2.selectCardIndexList

交換対象のカードを管理

3.handChangeDone

ハンドの交換判定を管理

4.resultArray

ゲーム結果を格納したリストを管理

import { InjectionKey } from "vue";
import { createStore, Store } from "vuex";
export interface State {   screenType: number; // 表示画面 0:スタート画面 1:ゲーム画面 2:結果画面   selectCardIndexList: number[]; // 交換対象のカードリスト   handChangeDone: boolean; // 交換済み判定フラグ   resultArray: string[]; // ゲーム結果を格納したリスト }
// define injection key export const key: InjectionKey<Store<State>> = Symbol();
export const store = createStore<State>({   state: {     screenType: 0,     selectCardIndexList: [],     handChangeDone: false,     resultArray: [],   },   getters: {     getScreenType(state) {       return state.screenType;     },     getSelectCardIndexList(state) {       return state.selectCardIndexList;     },     getHandChangeDone(state) {       return state.handChangeDone;     },     getResultArray(state) {       return state.resultArray;     },   },   mutations: {     setScreenType(state, val) {       state.screenType = val;     },     setSelectCardIndexList(state, val) {       state.selectCardIndexList = val;     },     setHandChangeDone(state, val) {       state.handChangeDone = val;     },     setResultArray(state, val) {       state.resultArray = val;     },   },   actions: {     setScreenType({ commit }, val) {       commit("setScreenType", val);     },     setSelectCardIndexList({ commit }, val) {       commit("setSelectCardIndexList", val);     },     setHandChangeDone({ commit }, val) {       commit("setHandChangeDone", val);     },     setResultArray({ commit }, val) {       commit("setResultArray", val);     },   }, });

/assets/json/cardList.json

全55枚のトランプを定義したJSONファイル。
マーク(ダイヤ:s,スペード:s,ハート:h,クラブ:c,ジョーカー:joker)と数値をキーとした構造となっています。
ジョーカーの数値は100に設定おりますが、13よりも大きければ問題ありません。

[
  {
    "mark": "d",
    "num": 1
  },
  {
    "mark": "d",
    "num": 2
  },
  {
    "mark": "d",
    "num": 3
  },
  {
    "mark": "d",
    "num": 4
  },
  {
    "mark": "d",
    "num": 5
  },
  {
    "mark": "d",
    "num": 6
  },
  {
    "mark": "d",
    "num": 7
  },
  {
    "mark": "d",
    "num": 8
  },
  {
    "mark": "d",
    "num": 9
  },
  {
    "mark": "d",
    "num": 10
  },
  {
    "mark": "d",
    "num": 11
  },
  {
    "mark": "d",
    "num": 12
  },
  {
    "mark": "d",
    "num": 13
  },
  {
    "mark": "s",
    "num": 1
  },
  ...
  {
    "mark": "joker",
    "num": 100
  },
  {
    "mark": "joker",
    "num": 100
  }
]

/components/StartGame.vue

ゲームタイトル、ルール説明、ゲームスタートボタンを表示しているスタート画面。
ゲームスタートボタン押下でmenuIndexのstateを更新し、ゲーム画面を表示している。

<template>
  <div class="start-game">
    <h1>ポーカーゲーム</h1>
    <div class="context">
      <h3>ルール説明</h3>
      <p>・全5回ゲームを行い、それぞれのゲームの役をポイントとして表示</p>
      <p>・手札の交換は一回のみ可能</p>
      <p>・ジョーカーは2枚</p>
    </div>
    <button type="button" class="btn btn-orange" @click="startPorkerGame()">
      ゲームスタート
    </button>
  </div>
</template>
<script lang="ts"> import { useStore } from "vuex"; import { key } from "@/store/"; import { defineComponent } from "vue";
export default defineComponent({   components: {},   setup() {     const store = useStore(key);
    // method     const startPorkerGame = (): void => {       store.dispatch("setScreenType", 1);     };
    return {       store,       startPorkerGame,     };   }, }); </script>
<style lang="scss" scoped> .start-game {   display: flex;   height: 100%;   flex-direction: column;   justify-content: center;   align-items: center;   .btn {     margin-top: 32px;   } } </style>

/components/PorkerGame.vue

ゲーム画面。
ハンドの表示や役の結果はそれぞれ別componentに定義している。
シャッフルした山札から5枚カード引き、昇順でソートしたものをハンドとして設定し、
componentに渡している。
手札の交換は選択したカードを捨て、捨てた枚数分新たに山札からカードを引いている。
次のゲームへ進むボタン押下で、再度ゲームを始められ、全5ゲームを終えると結果発表ボタンが表示され、
結果発表画面に遷移することができる。

<template>
  <div class="porker-game">
    <h2 class="game-count">
      {{ state.currentGameCount }}/{{ maxGameCount }}回目
      <span v-if="!store.getters.getHandChangeDone">交換前</span>
      <span v-else>交換後</span>
    </h2>
    <hand-card :hand-card-list="state.handCardList" />
    <hand-result :hand-card-list="state.handCardList" />
    <div class="change-hand-button">
      <button
        v-if="!store.getters.getHandChangeDone"
        type="button"
        class="btn btn-orange"
        @click="exchangeHand()"
      >
        <span v-if="store.getters.getSelectCardIndexList.length > 0"
          >選択したカードを交換</span
        ><span v-else>交換しない</span>
      </button>
      <button
        v-else-if="state.currentGameCount < maxGameCount"
        type="button"
        class="btn btn-blue"
        @click="nextGame()"
      >
        次のゲームへ
      </button>
      <button
        v-else
        type="button"
        class="btn btn-blue"
        @click="showResultGame()"
      >
        結果発表
      </button>
    </div>
  </div>
</template>
<script lang="ts"> import { defineComponent, reactive } from "vue"; import HandCard from "@/components/PorkerGame/HandCard.vue"; import HandResult from "@/components/PorkerGame/HandResult.vue"; import { useStore } from "vuex"; import { key } from "@/store/"; import cardListJson from "@/assets/json/cardList.json";
interface Card {   mark: string;   num: number; }
/**  * 全カードをシャッフル  *  * @param {Object[]} cardList 全カード  * @return {Object[]} シャッフルした全カード  */ function getShuffleCardList(cardList: Card[]) {   if (typeof window !== "undefined") {     for (let i = cardList.length - 1; i >= 0; i--) {       const j = Math.floor(Math.random() * (i + 1));
      [cardList[i], cardList[j]] = [cardList[j], cardList[i]];     }   }
  return cardList; }
/**  * 引数の数だけカードを絞り込む  *  * @param {Object[]} cardList 全カード  * @param {number} num 引くカードの枚数  * @return { [s: string]: any } 絞り込んだカードリスト  */ function getFilterCardList(cardList: Card[], num: number) {   const filterCardList = Object.create(cardList).splice(0, num);
  return filterCardList; }
/**  * 数値の昇順でソート(ジョーカーは最後)  *  * @param {Object[]} handCardList ソート前の手札  * @return {Object[]} ソート後の手札  */ function getSortHandCardList(handCardList: Card[]) {   let sortHandCardList = handCardList.slice();
  sortHandCardList = sortHandCardList.sort((a: Card, b: Card) => {     if (a.num < b.num) return -1;     if (a.num > b.num) return 1;
    return 0;   });
  return sortHandCardList; }
export default defineComponent({   components: { HandCard, HandResult },   setup() {     const store = useStore(key);     const state = reactive({       handCardList: [] as Card[],       currentGameCount: 1, // 現在のゲーム数     });     const maxGameCount = 5; // 最大ゲーム数     const isHandChange = false;     const handNum = 5;     let shuffleCardList: Card[] = [];     let deckCardList: Card[] = [];
    /**      * ゲーム開始処理      * シャッフルした山札から5枚カードを引き、昇順にソートを行い手札として設定する。      */     function initGame() {       shuffleCardList = getShuffleCardList(cardListJson); // 全カードをシャッフル       state.handCardList = getFilterCardList(shuffleCardList, handNum); // 引数の数だけカードを絞り込む       state.handCardList = getSortHandCardList(state.handCardList); // 数値の昇順でソート(ジョーカーは最後)       deckCardList = shuffleCardList.filter((card: Card) => {         let result = false;         const targetData = state.handCardList.find(           (handCard: Card) => handCard.num === card.num         );
        if (!targetData) {           result = true;         }
        return result;       }); // 山札の更新     }
    initGame(); // ゲーム開始処理
    /**      * ゲーム画面を表示      */     const startPorkerGame = (): void => {       store.dispatch("setScreenType", 1);     };
    /**      * 手札の交換処理      */     const exchangeHand = (): void => {       const selectCardIndexList = store.getters.getSelectCardIndexList;       let newHand = state.handCardList.filter((data: Card, index: number) => {         return !selectCardIndexList.includes(index);       });
      newHand = newHand.concat(         getFilterCardList(deckCardList, selectCardIndexList.length)       );       newHand = getSortHandCardList(newHand);       state.handCardList = newHand;       store.dispatch("setSelectCardIndexList", []);       store.dispatch("setHandChangeDone", true);     };
    /**      * 次のゲームへ進む      */     const nextGame = (): void => {       state.currentGameCount++;       initGame(); // ゲーム開始処理       store.dispatch("setHandChangeDone", false);     };
    /**      * 結果画面を表示      */     const showResultGame = (): void => {       store.dispatch("setScreenType", 2);       store.dispatch("setHandChangeDone", false);     };
    return {       store,       state,       maxGameCount,       isHandChange,       handNum,       deckCardList,       startPorkerGame,       exchangeHand,       nextGame,       showResultGame,     };   }, }); </script>
<style lang="scss" scoped> .porker-game {   .game-count {     text-align: center;   }   .change-hand-button {     display: flex;     align-items: center;     justify-content: center;   } } </style>

/components/PorkerGame/HandCard.vue

ハンドを画面に表示するコンポーネント。
トランプの画像をクリックで選択処理(selectCard)が実行され、
storeで管理しているselectCardIndexListを更新する。
selectCardIndexListはPorkerGame.vueで定義している手札交換処理(exchangeHand)で使用している。

<template>
  <div class="hand-card">
    <ul
      class="card-list"
      :class="{ exchanged: store.getters.getHandChangeDone }"
    >
      <li
        v-for="(card, index) in handCardList"
        :key="index"
        @click="selectCard(index)"
      >
        <img
          :class="{
            'is-selected': store.getters.getSelectCardIndexList.includes(index),
          }"
          :src="
            require(`@/assets/images/cardImages/${cardImageFileName(
              card.mark,
              card.num
            )}.png`)
          "
        />
      </li>
    </ul>
  </div>
</template>
<script lang="ts"> import { useStore } from "vuex"; import { key } from "@/store/"; import { defineComponent, computed } from "vue";
export default defineComponent({   components: {},   props: {     handCardList: {       type: Array,       default: () => [],     },   },   setup() {     const store = useStore(key);
    // computed     const cardImageFileName = computed(() => {       return (mark: string, num: number) => {         let fileName = "";
        if (mark === "joker") {           fileName = "joker";         } else {           fileName = `${mark}-${String(num)}`;         }
        return fileName;       };     });
    // method     const selectCard = (index: number): void => {       if (store.getters.getHandChangeDone) {         return;       }
      let newSelectCardIndexList = store.getters.getSelectCardIndexList;       let targetIndex = newSelectCardIndexList.indexOf(index);
      if (targetIndex >= 0) {         newSelectCardIndexList.splice(targetIndex, 1);       } else {         newSelectCardIndexList.push(index);       }
      store.dispatch("setSelectCardIndexList", newSelectCardIndexList);     };
    return {       store,       cardImageFileName,       selectCard,     };   }, }); </script>
<style lang="scss"> .hand-card {   .card-list {     display: flex;     align-items: center;     justify-content: center;     &.exchanged {       li {         img {           cursor: default;         }       }     }     li {       width: 150px;       &:not(:first-child) {         margin-left: 12px;       }       img {         width: 100%;         cursor: pointer;         &.is-selected {           outline: 3px solid blue;         }       }     }   } } </style>

/components/PorkerGame/HandResult.vue

ポーカーの役を表示するコンポーネント。
ハンドからジョーカーの数とジョーカーを抜いたハンドを定義してから、役の判定を行っている。
ジョーカーを抜いたハンドから「key:数値,value:マークの配列」のオブジェクト配列定義(※1)し、
以下の役が成立しているかを事前に判定。

■フラッシュ
ジョーカーを抜いたハンドに1種類のみのマークしか存在しないかどうか
■ロイヤルストレート(10,11,12,13,A)
ジョーカーを抜いたハンドが全て10からA内の数値かどうか
■ストレート
ジョーカーを抜いたハンドが階段状に1ずつインクリメントされているかどうか。
差が2以上あった場合でもジョーカーがある場合は、代用できるようにしている。
■フルハウス
※1のオブジェクト配列を使用し、スリーカードとワンペアの両方が成立しているかを判定。
ジョーカーが1枚ある場合はワンペアが2つ成立していればフルハウスとして判定する。
(ジョーカー2枚ある場合はフルハウスではなくフォーカードが成立するため、2枚あるときのパターンは考慮していない)
■ツーペア
※1のオブジェクト配列を使用し、ワンペアが2つ成立しているかどうかを判定。

上記の役成立結果を用いて最終的な役の結果を取得している。

<template>
  <div class="hand-result">
    <p>
      <span v-if="store.getters.getHandChangeDone">交換後の役</span
      ><span v-else>現在の役</span>:{{ checkHandResult }}
    </p>
  </div>
</template>
<script lang="ts"> import { useStore } from "vuex"; import { key } from "@/store/"; import { defineComponent, computed, PropType } from "vue";
interface Card {   mark: string;   num: number; }
/**  * フラッシュの役判定  *  * @param {Object[]} noJokerHandList ジョーカーを除いたハンド  * @return {boolean} true:フラッシュ役 false:フラッシュ役でない  */ function checkIsFlash(noJokerHandList: Card[]) {   let result = false;   const set = new Set(     noJokerHandList.map((data: Card) => {       return data.mark;     })   );
  if (Array.from(set).length === 1) {     result = true;   }
  return result; }
/**  * ストレートの役判定(ただし、ロイヤルストレートの形は除く)  *  * @param {Object[]} noJokerHandList ジョーカーを除いたハンド  * @param {number} jokerNum ジョーカーの数  * @return {boolean} true:ストレート役 false:ストレート役でない  */ function checkIsStraight(noJokerHandList: Card[], jokerNum: number) {   let result = true;   let stockJoker: number = jokerNum; // ジョーカーのカウント
  // ストレートかを判定   for (let i = 0; i < noJokerHandList.length - 1; i++) {     const diffNum = noJokerHandList[i + 1].num - noJokerHandList[i].num;
    if (diffNum !== 1) {       if (stockJoker > 0 && diffNum > 1 && diffNum - 1 <= stockJoker) {         stockJoker -= diffNum - 1;       } else {         result = false;       }     }   }
  return result; }
/**  * ロイヤルストレートの役判定(10, 11, 12, 13, Aの形)  *  * @param {Object[]} noJokerHandList ジョーカーを除いたハンド  * @param {number} jokerNum ジョーカーの数  * @return {boolean} true:ロイヤルストレート役 false:ロイヤルストレート役でない  */ function checkIsRoyalStraight(noJokerHandList: Card[], jokerNum: number) {   let result = false;   const handCardNum: number[] = noJokerHandList.map((data: Card) => {     return data.num;   });   const royalStraightHandNum: number[] = [1, 10, 11, 12, 13];   const diffHand: number[] = royalStraightHandNum.filter((data: number) => {     return handCardNum.indexOf(data) >= 0;   });
  if (diffHand.length + jokerNum >= 5) {     result = true;   }
  return result; }
/**  * 手札内で一番重複しているカードの枚数の取得  *  * @param {Object} numKeyHand ハンドの数値をkeyとしてオブジェクト  * @return {number} 手札内で一番重複しているカードの枚数  */ function getMaxSameCardNum(numKeyHand: { [n: number]: Card[] }) {   let maxSameCard = 0;
  for (const key in numKeyHand) {     if (key === "j") {       continue;     }
    if (numKeyHand[key].length > maxSameCard) {       maxSameCard = numKeyHand[key].length;     }   }
  return maxSameCard; }
/**  * フルハウスの役判定  *  * @param {Object} numKeyHand ハンドの数値をkeyとしてオブジェクト  * @param {number} jokerNum ジョーカーの数  * @return {boolean} true:フルハウス役 false:フルハウス役でない  */ function checkIsFullHouse(   numKeyHand: { [n: number]: Card[] },   jokerNum: number ) {   let result = false;
  if (jokerNum === 0) {     let threeCard = false;     let twoCard = false;
    for (const key in numKeyHand) {       if (numKeyHand[key].length === 3) {         threeCard = true;       } else if (numKeyHand[key].length === 2) {         twoCard = true;       }     }
    if (threeCard && twoCard) {       result = true;     }   } else if (jokerNum === 1) {     let twoCardPair = 0;
    for (const key in numKeyHand) {       if (numKeyHand[key].length === 2) {         twoCardPair++;       }     }
    if (twoCardPair === 2) {       result = true;     }   }
  return result; }
/**  * ツーペアの役判定  *  * @param {Object} numKeyHand ハンドの数値をkeyとしてオブジェクト  * @param {number} jokerNum ジョーカーの数  * @return {boolean} true:フルハウス役 false:フルハウス役でない  */ function checkIsTwoPair(numKeyHand: { [n: number]: Card[] }, jokerNum: number) {   let result = false;
  if (jokerNum === 0) {     let twoCardPair = 0;
    for (const key in numKeyHand) {       if (numKeyHand[key].length === 2) {         twoCardPair++;       }     }
    if (twoCardPair === 2) {       result = true;     }   }
  return result; }
/**  * 数字をkeyとしたオブジェクトを取得  *  * @param {Object[]} noJokerHandList ジョーカーを除いたハンド  * @return {Object} 数字をkeyとしたオブジェクト  */ function getNumKeyHand(noJokerHandList: Card[]) {   const numKeyHand: { [n: number]: Card[] } = {};
  for (let i = 0; i < noJokerHandList.length; i++) {     if (!numKeyHand[noJokerHandList[i].num]) {       numKeyHand[noJokerHandList[i].num] = [];     }
    numKeyHand[noJokerHandList[i].num].push(noJokerHandList[i]);   }
  return numKeyHand; }
/**  * ポーカーの役の取得  *  * @param {Object[]} handCardList ハンド  * @return {string} ポーカーの役  */ function getHandResult(handCardList: Card[]) {   let handResult = "役なし";   const noJokerHandList: Card[] = handCardList.filter(     (data: Card) => data.mark !== "joker"   );   const jokerNum: number = handCardList.filter(     (data: Card) => data.mark === "joker"   ).length;   const numKeyHand: { [n: number]: Card[] } = getNumKeyHand(noJokerHandList);   const maxPairNum: number = getMaxSameCardNum(numKeyHand);   const isFlash = checkIsFlash(noJokerHandList);   const isRoyalStraight = checkIsRoyalStraight(noJokerHandList, jokerNum);   const isStraight = checkIsStraight(noJokerHandList, jokerNum);   const isFullHouse = checkIsFullHouse(numKeyHand, jokerNum);   const isTwoPair = checkIsTwoPair(numKeyHand, jokerNum);
  // フラッシュかどうかの判定   if (isFlash) {     if (isRoyalStraight) {       // ロイヤルストレートフラッシュかどうかの判定       handResult = "ロイヤルストレートフラッシュ";     } else if (isStraight) {       // ストレートフラッシュかどうかの判定       handResult = "ストレートフラッシュ";     } else {       // フラッシュかどうかの判定       handResult = "フラッシュ";     }   } else if (isRoyalStraight || isStraight) {     // ストレートかどうかの判定     handResult = "ストレート";   } else if (maxPairNum + jokerNum === 5) {     handResult = "ファイブカード";   } else if (maxPairNum + jokerNum === 4) {     handResult = "フォーカード";   } else if (isFullHouse) {     handResult = "フルハウス";   } else if (maxPairNum + jokerNum === 3) {     handResult = "スリーカード";   } else if (isTwoPair) {     handResult = "ツーペア";   } else if (maxPairNum + jokerNum === 2) {     handResult = "ワンペア";   }
  return handResult; }
export default defineComponent({   components: {},   props: {     handCardList: {       type: Array as PropType<Array<Card>>,       default: () => [],     },   },   setup(props) {     const store = useStore(key);     const checkHandResult = computed(() => {       let handResult = getHandResult(props.handCardList); // ポーカーの役の取得
      if (store.getters.getHandChangeDone) {         const newResultArray = store.state.resultArray;
        newResultArray.push(handResult);
        store.dispatch("setResultArray", newResultArray);       }
      return handResult;     });
    return {       store,       checkHandResult,     };   }, }); </script>
<style lang="scss"> .hand-result {   p {     font-size: 28px;     font-weight: bold;     color: red;     text-align: center;   } } </style>

/components/resultGame.vue

ゲームの結果を表示する画面。
役毎にポイントを設定し、5ゲームの集計結果を算出する。
最初に戻るボタンを押下で、スタート画面に遷移する。

<template>
  <div class="result-game">
    <h2>結果発表</h2>
    <div class="result-list">
      <ul>
        <li
          v-for="(result, index) in store.getters.getResultArray"
          :key="index"
        >
          <p>{{ index + 1 }}回目: {{ result }} ({{ pointList[result] }}点)</p>
        </li>
      </ul>
    </div>
    <div class="point">
      <p>点数: {{ resultPoint }}点</p>
    </div>
    <div class="reset">
      <button type="button" class="btn btn-blue" @click="resetGame()">
        最初に戻る
      </button>
    </div>
  </div>
</template>
<script lang="ts"> import { useStore } from "vuex"; import { key } from "@/store/"; import { defineComponent } from "vue";
export default defineComponent({   components: {},   setup() {     const store = useStore(key);     let resultPoint = 0;     const pointList: { [s: string]: number } = {       ワンペア: 10,       ツーペア: 20,       スリーカード: 30,       ストレート: 40,       フラッシュ: 50,       フルハウス: 60,       フォーカード: 70,       ストレートフラッシュ: 80,       ロイヤルストレートフラッシュ: 90,       ファイブカード: 100,       役なし: 0,     };
    /**      * 結果ポイントの計算      */     function calcResultPoint() {       const resultArray = store.getters.getResultArray;
      for (let i = 0; i < resultArray.length; i++) {         resultPoint += pointList[resultArray[i]];       }     }
    calcResultPoint(); // 結果ポイントの計算
    /**      * ゲームのリセット      */     const resetGame = (): void => {       store.dispatch("setScreenType", 0);       store.dispatch("setResultArray", []);     };
    return {       store,       resultPoint,       pointList,       resetGame,     };   }, }); </script>
<style lang="scss" scoped> .result-game {   display: flex;   height: 100%;   flex-direction: column;   justify-content: center;   align-items: center;   .point {     font-size: 32px;     color: red;     font-weight: bold;   } } </style>

まとめ

工夫した点としては最大2枚のジョーカーを考慮したロジックを考えたことです。
ジョーカーを抜いたハンドとジョーカーの数を使用することで、複雑にならずにプログラムを書くことができました。
Vue3のCompositionAPIは初めて触ってみた感想としては、data,computed,methodオプションを全てsetupオプション内に記述するため、可読性の考慮が求められるなと感じました。
リアクティブな変数を定義する際にreactiveやrefを使用するなどいった変更はありますが、基本的にはVue2までの書き方と似ているため、
Vue2をやられていた方であればCompositionAPIの学習コストはそこまで高くないと感じました。

この記事の執筆者

Y.M

テクノロジーソリューション事業部

この記事に関するご相談やご質問など、お気軽にお問い合わせください。

お問い合わせ

タグ一覧