ゲームを作って暇つぶし

暇つぶしにやったこと

じゃんけんゲーム Ver2.0

※記事のタイトルをクリックして、個別でページを開かないと、正しく動作しません。

前バージョン

organize.hatenablog.jp

変更点

  • 掛け声に合わせて、タイミングよくボタンを押さないと、負けるようにしました。

感想

  • BattleAreaクラスが大きくなりすぎなので、もうちょっと整理して小さくしたいところ。
  • とにかく表示する文字をいい感じにするのが大変でした。
    • 出した手を、今は文字で表示していますが、画像にした方が制御しやすい気がします。
  • 要素のidに紐づけて、cssを定義しているところがありますが、idは要素の特定にだけ使って、見た目はclassだけで管理した方がいいですよとAIに言われたので、変更すると思います。
  • 結局types.tsはそのままです。どうするかまだ考えてません。
  • あいこの時は勝負を続けるようにしたいですね。
  • 勝負のログとかも見れるといいなと思いました。

次のバージョン

organize.hatenablog.jp

クラス図

index.html

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/janken.svg" />
    <link rel="stylesheet" href="./src/style.css" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>janken</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/JankenGame.ts"></script>
  </body>
</html>

style.css

h1{
  text-align: center;
}

.explanation{
  padding: 2rem;
  text-align: center;
}

#battle-area {
  width: 100%; /* ウィンドウの両端まで広げる */
    
  border: 3px solid #333; /* 四角い枠線 */
  background-color: #f0f0f0; /* 背景色(任意) */
  box-sizing: border-box; /* borderとpaddingをwidth/heightに含める */

  /* 子要素を縦に並べるためのFlexbox設定 */
  display: flex;
  flex-direction: column;
}

#timingbar {
  width: 100%; /* 親要素(#battle-area)いっぱいに広げる */
  padding: 10px;
  background-color: #222; /* バーの背景色 */
  box-sizing: border-box;

  /* 子要素(ランプ)を横に並べるためのFlexbox設定 */
  display: flex;
  align-items: center; /* ランプを垂直方向中央に揃える */
  gap: 8px; /* ランプ間のすき間を8pxに設定 */
}

.lamp {
  /* flex-grow: 1;, flex-shrink: 1;, flex-basis: 0%; */
  flex: 1; 
  height: 30px; /* 高さ */  
}

.lamp.light-off {
  background-color: #ffc10777; /* ランプの色 */
  border: 1px solid #e0a800; /* ランプの枠線 */
}

.lamp.light-on {
  background-color: hsl(59, 100%, 66%); /* ランプの色 */
  border: 1px solid #e0a800; /* ランプの枠線 */
}

/* --- 中央のランプだけ色を変える(例) --- */
#center-lamp.light-off {
  background-color: #dc354697; /* 赤色 */
  border-color: #b02a37;
}

#center-lamp.light-on {
  background-color: #ff4242; /* 赤色 */
  border-color: #b02a37;
}

/* 手を出したときのランプの色 */
.put-light{
  background-color: #00ff08;
}

#hand-area{
  position: relative;
  height: 15rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

#compute-hand{
  height:5rem;
  line-height: 5rem;
  font-size: clamp(8px, 5cqh, 300px);
  text-align: center;
}

#my-hand{
  height:5rem;
  line-height: 5rem;
  font-size: clamp(8px, 5cqh, 300px);
  text-align: center;
}

#verbal-cue{
  position: absolute; /* 通常のレイアウトから切り離して浮かす */
  top: 50%;          /* 親要素の上から50%の位置に配置 */
  left: 50%;         /* 親要素の左から50%の位置に配置 */
  transform: translate(-50%, -50%); /* 要素自身の大きさの半分だけ戻して中央揃え */
  z-index: 10;       /* 他の要素より手前に表示する */
  height: 8rem;
  width: 100%;
  line-height: 8rem;
  text-align: center;
  white-space: nowrap;
  font-size: clamp(1rem, 2rem, 3rem);
  font-weight: bold;
  color: #005f13b1;
}

.hand-buttons{
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 1rem;
  margin-bottom: 1rem;
}

.hand-button{
  width: 5em;
  height: 5em;
  margin-left: 1em;
  margin-right: 1em;
}

.start-button{
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 1rem;
  margin-left: auto;
  margin-right: auto;
}

#result.win{
  color: #ff0000;
}

#result.lose{
  color: #0000ff;
}

#result.draw{
  color: #000000;
}

.hidden{
  display: none;
}

JankenGame.ts

import { BattleArea } from "./BattleArea";
import { HandButtonArea } from "./HandButtonArea";
import { Header } from "./Header";

/**
 * ジャンケンゲームのメインクラス
 */
class JankenGame {
    /** ヘッダー */
    private header: Header;
    /** バトルエリア */
    private battleArea: BattleArea;
    /** スタートボタン */
    private startButton: HTMLButtonElement;
    /** グーチョキパーボタン */
    private handButtonArea: HandButtonArea;

    /**
     * コンストラクタ
     * @param parent ジャンケンゲームの親要素
     */
    constructor(parent: HTMLElement){
        // ヘッダー
        this.header = new Header();
        parent.appendChild(this.header.getElement());

        // バトルエリア
        this.battleArea = new BattleArea();
        // バトル終了ハンドラを設定
        this.battleArea.setEndHandler(()=>{
            this.setLady();
        })
        parent.appendChild(this.battleArea.getElement());

        // 開始ボタン
        this.startButton = document.createElement("button");
        this.startButton.classList.add('start-button', 'hand-button')
        this.startButton.textContent = "勝負!";
        this.startButton.addEventListener('click', ()=>{
            // スタートボタン非表示
            this.startButton.classList.add('hidden');
            // ハンドボタン表示
            this.handButtonArea.getElement().classList.remove('hidden');
            // ジャンケン開始
            this.battleArea.startShoot(this.rand(30, 50));
        })
        parent.appendChild(this.startButton);

        // グー・チョキ・パーボタン
        this.handButtonArea = new HandButtonArea();
        for(const handButton of this.handButtonArea.getButtons()){
            // 押されたときの処理を設定
            handButton.getElement().addEventListener('click', () => {
                // 手を出せるかどうか判定
                if(!this.battleArea.canPutHand()){
                    return;
                }

                // 出した手を設定
                const canPutNext = this.battleArea.setMyHand(handButton.getKind());

                // 次の手を出せるか判定
                if(!canPutNext){
                    // 出せない場合はハンドボタンを非表示
                    this.handButtonArea.getElement().classList.add('hidden');
                }
            });
        }
        parent.appendChild(this.handButtonArea.getElement());

        // 準備状態にする
        this.setLady();
    }

    /**
     * バトル準備
     */
    private setLady(){
        // ハンドボタンエリアを非表示
        this.handButtonArea.getElement().classList.add('hidden');
        // スタートボタンを表示
        this.startButton.classList.remove('hidden');
    }

    /**
     * 乱数取得
     * @param min 最低値 
     * @param max 最大値
     * @returns 範囲内の乱数
     */
    private rand(min: number, max: number): number {
        return (Math.floor(Math.random() * (max - min + 1)) + min);
    }
}

// app要素を取得
const appElement = document.querySelector('#app') as HTMLElement;
// ジャンケンゲーム作成
new JankenGame(appElement);

Header.ts

/**
 * タイトル・説明を表示
 */
export class Header{
    /** ルート要素 */
    private element:HTMLElement;
    /** タイトル要素 */
    private title:HTMLElement;
    /** 説明要素 */
    private explanation:HTMLElement;

    /**
     * コンストラクタ
     */
    constructor(){
        // 自身の要素を作成
        this.element = document.createElement("div");
        
        // タイトル
        this.title = document.createElement('h1');
        this.title.textContent = 'ジャンケンゲーム';
        this.element.appendChild(this.title);

        // 説明
        this.explanation = document.createElement('div');
        this.explanation.className ='explanation';
        this.explanation.innerHTML = `
            <div>勝負ボタンを押すと「さいしょはグー、じゃんけんぽん」の掛け声が始まります。</div>
            <div>「ぽん」のタイミングで出したい手のボタンを押してください。</div>
            <div>上側が相手、下側があなたが出した手になります。</div>
            <div>タイミングが早いと、超反応速度であなたの手を見られて、必ず負けてしまいます。</div>
            <div>タイミングが遅いと、後出しだといちゃもんをつけられて、負けてしまいます。</div>
            <div>タイミングよくボタンを押しましょう。</div>`;
        this.element.appendChild(this.explanation);
    }

    /**
     * 要素取得
     * @returns 要素
     */
    getElement():HTMLElement{
        return this.element;
    }
}

BattleArea.ts

import { GameResult, HandButtonsDefine, HandKind, ResultNames, type PutTiming } from "./types";

/**
 * バトルエリア
 */
export class BattleArea{
    /** ルート要素 */
    private element:HTMLElement;
    /** 中央ランプ */
    private centerLamp!:HTMLElement;
    /** 左側ランプ */
    private leftLamps:HTMLElement[] = [];
    /** 右側ランプ */
    private rightLamps:HTMLElement[] = [];
    /** 点灯ランプ */
    private lightOnIndex:number = 0;
    /** フェーズ */
    private phase:number = 0;
    /** 掛け声エリア */
    private verbalCueArea:HTMLElement;
    /** 掛け声 */
    private verbalCue:HTMLElement;
    /** 勝敗結果 */
    private result:HTMLElement;
    /** タイマーID */
    private intervalId:number = 0;
    /** 自分の手 */
    private myHand:HTMLElement;
    /** 相手の手 */
    private computeHand:HTMLElement;
    /** 手を出したか */
    private isPutHand:boolean = false;
    /** 手を出したタイミング */
    private putTiming:PutTiming;
    /** ジャンケン終了時ハンドラ */
    private endHandler:Function | null = null;

    /**
     * コンストラクタ
     */
    constructor(){
        // ルート要素
        this.element = document.createElement('div');
        this.element.id = 'battle-area';

        // タイミングバー
        const timingBar = document.createElement('div');
        timingBar.id = 'timingbar';
        this.element.appendChild(timingBar);

        // 左に12個、右に12個、中央1個
        for(let index = 0; index < 25; index++){
            const lamp = document.createElement('span');
            lamp.classList.add('lamp', 'light-off');
            if(index === 12){
                lamp.id = 'center-lamp';
                this.centerLamp = lamp;
            }else if(index < 12){
                lamp.id = 'left-lamp' + (index + 1);
                this.leftLamps.push(lamp);
            }else{
                lamp.id = 'rignt-lamp' + (25- index);
                this.rightLamps.unshift(lamp);
            }

            timingBar.appendChild(lamp)
        }

        // ハンドエリア
        const handArea = document.createElement('div');
        handArea.id = 'hand-area';
        this.element.appendChild(handArea);

        // 相手の手
        this.computeHand = document.createElement('div');
        this.computeHand.id = 'compute-hand';
        handArea.appendChild(this.computeHand);

        // 自分の手
        this.myHand = document.createElement('div');
        this.myHand.id = 'my-hand';
        handArea.appendChild(this.myHand);
        
        // 掛け声エリア
        this.verbalCueArea = document.createElement('div');
        this.verbalCueArea.id = 'verbal-cue';
        handArea.appendChild(this.verbalCueArea);

        // 掛け声
        this.verbalCue = document.createElement('span');
        this.verbalCueArea.appendChild(this.verbalCue);

        // 勝敗結果
        this.result = document.createElement('span');
        this.result.id = 'result';
        this.verbalCueArea.appendChild(this.result);
        
        // 手を出したタイミング
        this.putTiming = {phase:0, lightIndex:-1}

    }

    /**
     * 要素取得
     * @returns 要素 
     */
    getElement(): HTMLElement{
        return this.element;
    }

    /**
     * ライト点灯
     * @param lamp ライト要素 
     */
    lightOn(lamp:HTMLElement){
        // 手を出した時のライトは消さない
        if(lamp.classList.contains("put-light")){
            return;
        }
        lamp.classList.remove('light-off');
        lamp.classList.add('light-on');
    }

    /**
     * ライト消灯
     * @param lamp ライト要素 
     */
    lightOff(lamp:HTMLElement){
        // 手を出した時のライトは消さない
        if(lamp.classList.contains("put-light")){
            return;
        }
        lamp.classList.remove('light-on');
        lamp.classList.add('light-off');
    }

    /**
     * インデックスを指定してライト点灯
     * @param index ライトのインデックス 
     */
    lightOnByIndex(index:number){
        this.lightOn(this.leftLamps[index]);
        this.lightOn(this.rightLamps[index]);
    }

    /**
     * インデックスを指定してライト消灯
     * @param index ライトのインデックス 
     */
    lightOffByIndex(index:number){
        this.lightOff(this.leftLamps[index]);
        this.lightOff(this.rightLamps[index]);
    }

    /**
     * すべてのライトを消灯
     * 手を出した時のライトも含める。
     */
    lightOffAll(){
        for(let index = 0; index < this.leftLamps.length; index++){
            this.leftLamps[index].classList.remove('put-light');
            this.rightLamps[index].classList.remove('put-light');
            this.lightOffByIndex(index);
        }

        // 中央ライト
        this.centerLamp.classList.remove('put-light');
        this.lightOff(this.centerLamp);
    }

    /**
     * ジャンケン開始
     * @param interval ランプ点灯間隔
     */
    startShoot(interval:number){
        // すでに開始していたら何もしない
        if(this.intervalId !== 0){
            return;
        }

        // すべてのランプを消灯
        this.lightOffAll();

        // 初期化
        this.myHand.textContent = "";
        this.computeHand.textContent = "";
        this.verbalCue.textContent = "";
        this.result.textContent = "";

        this.lightOnIndex = 0;
        this.phase = 1;
        this.isPutHand = false;
        this.putTiming.phase = 0;
        this.putTiming.lightIndex = -1;

        this.intervalId = setInterval(() => {
            // 一つ前のライトを消灯
            if(this.lightOnIndex >= 1){
                this.lightOffByIndex(this.lightOnIndex-1);
            }else{
                this.lightOff(this.centerLamp);
            }

            // 現在のライトを点灯
            if(this.lightOnIndex >= this.leftLamps.length){
                this.lightOn(this.centerLamp);
            }else{
                this.lightOnByIndex(this.lightOnIndex);
            }

            // 点灯ライトのインデックスを加算
            this.lightOnIndex++;

            // 先頭ランプを点灯した場合
            if(this.lightOnIndex > this.leftLamps.length){
                // 点灯ライトのインデックスを初期化
                this.lightOnIndex = 0;

                // フェーズに合わせて掛け声を表示
                if(this.phase === 1){
                    this.verbalCue.textContent = "さい"
                }else if(this.phase === 2){
                    this.verbalCue.textContent = "しょは"
                }else if(this.phase === 3){
                    this.verbalCue.textContent = "グー"
                    this.computeHand.textContent = HandButtonsDefine[HandKind.Rock].name;
                }else if(this.phase === 4){
                    this.verbalCue.textContent = ""
                }else if(this.phase === 5){
                    this.verbalCue.textContent = "じゃん"
                    this.myHand.textContent = "";
                    this.computeHand.textContent = "";
                }else if(this.phase === 6){
                    this.verbalCue.textContent = "けん"
                }else if(this.phase === 7){
                    this.verbalCue.textContent = "ぽん"
                    // 相手の手を出す
                    this.putComputeHand();
                }else if(this.phase === 8){
                    // ジャンケン終了
                    this.stopShoot();
                    // 結果表示
                    this.setResult();
                }

                // フェーズを進める
                this.phase++;
            }

        }, interval);
    }

    /**
     * ジャンケン終了
     */
    stopShoot(){
        clearInterval(this.intervalId);
        this.intervalId = 0;
    }

    /**
     * 自分の手を表示
     * @param hand 自分の手
     * @returns 次の手を出せるかどうか
     */
    setMyHand(hand:HandKind):boolean{
        // 自分の手を表示
        this.myHand.textContent = HandButtonsDefine[hand].name;
    
        // 現在点灯しているランプの色を変更
        this.centerLamp.classList.remove('put-light');
        if(this.lightOnIndex - 1 >= this.leftLamps.length){
            this.centerLamp.classList.add('light-on');
        }else{
            this.centerLamp.classList.add('light-off');
        }

        for(let index = 0; index < this.leftLamps.length; index++){
            this.leftLamps[index].classList.remove('put-light');
            this.rightLamps[index].classList.remove('put-light');
            if(this.lightOnIndex - 1 === index){
                this.leftLamps[index].classList.add('light-on');
                this.rightLamps[index].classList.add('light-on');
            }else{
                this.leftLamps[index].classList.add('light-off');
                this.rightLamps[index].classList.add('light-off');
            }
        }


        if(this.lightOnIndex - 1 >= 0 && this.lightOnIndex - 1 < this.leftLamps.length){
            this.leftLamps[this.lightOnIndex-1].classList.add('put-light');
            this.leftLamps[this.lightOnIndex-1].classList.remove('light-on', 'light-off');
            this.rightLamps[this.lightOnIndex-1].classList.add('put-light');
            this.rightLamps[this.lightOnIndex-1].classList.remove('light-on', 'light-off');
        }else{
            this.centerLamp.classList.add('put-light');
            this.centerLamp.classList.remove('light-on', 'light-off');
        }

        // 「けん」以降に手を出したら、もう次の手は出せない
        if(this.phase >= 7 && this.phase <= 8){
            this.isPutHand = true;
            this.putTiming.phase = this.phase;
            this.putTiming.lightIndex = this.lightOnIndex - 1;
            return false;
        }

        return true;
    }

    /**
     * ジャンケン中かどうか
     * @returns じゃんけん中かどうか
     */
    isFighting():boolean{
        if(this.phase >= 1){
            return true;
        }
        return false;
    }

    /**
     * 手を出せるかどうか
     * @returns 手を出せるかどうか
     */
    canPutHand():boolean{
        if(this.phase <= 0 || this.phase > 8){
            return false;
        }

        if(this.isPutHand){
            return false;
        }
        return true;
    }

    /**
     * 相手の手を出す
     */
    putComputeHand(){
        // ランダムで手を取得
        let computeHand = this.getRandomHand();

        // 「ぽん」のタイミングより早く、自分が手を出していたら
        if(this.myHand.textContent.length !== 0){
            // 自分の手を取得
            const myHand = this.getHandKindByName(this.myHand.textContent)
            if(myHand){
                // 勝てる手を取得
                computeHand = this.getWindHand(myHand);
            }
        }

        // 相手の手を表示
        this.computeHand.textContent = HandButtonsDefine[computeHand].name;        
    }

    /**
     * 手の表示名からHandKindを取得
     * @param name 手の表示名
     * @returns HandKind
     */
    getHandKindByName(name:string):HandKind | undefined{
        const keys = Object.keys(HandButtonsDefine) as HandKind[];
        return keys.find(key => HandButtonsDefine[key].name === name);
    }

    /**
     * 結果表示
     */
    setResult(){
        // 掛け声を削除
        this.verbalCue.textContent = ""

        // 手を取得
        const myHand = this.getHandKindByName(this.myHand.textContent);
        const computeHand = this.getHandKindByName(this.computeHand.textContent);
        
        // エラー判定
        if(!computeHand){
            alert("コンピュータがまだ手を出していないのに、結果を表示しようとしています。")
            console.error("コンピュータがまだ手を出していません。")
            return;
        }

        // ハンドボタンが押されていない
        if(!myHand){
            // 負け
            this.verbalCue.textContent = ResultNames[GameResult.Lose];
        }else{
            // 勝敗を取得
            const result = this.getResult(myHand, computeHand);
            // 勝敗を表示
            this.result.textContent = ResultNames[result];
            // 勝敗に合わせて色を設定
            this.setResultColor(result);

            // 出すタイミングが遅かった場合
            if(this.putTiming.phase >= 8 && this.putTiming.lightIndex >= 0){
                this.verbalCue.textContent = '後出しなので';
                this.setResultColor(GameResult.Lose);
                this.result.textContent = ResultNames[GameResult.Lose];
            }else if(this.putTiming.phase <= 7){
                // 出すタイミングが早かった場合
                this.result.textContent = ResultNames[result];
                this.setResultColor(GameResult.Lose);
                this.verbalCue.textContent = "(出すのが速い)"
            }
        }

        // ジャンケン終了時ハンドラを実行
        if(this.endHandler){
            this.endHandler();
        }
    }

    /**
     * 勝敗に合わせた文字色を設定
     * @param result 勝敗
     */
    setResultColor(result:GameResult){
        for(const gameResult of Object.values(GameResult)){
            if(result === gameResult){
                this.result.classList.add(result);
            }else{
                this.result.classList.remove(gameResult);
            }
        }
    }

    /**
     * 手をランダムに取得
     * @returns グー or チョキ or パー
     */
    private getRandomHand(): HandKind {
        const hands = Object.values(HandKind);
        const randomIndex = Math.floor(Math.random() * hands.length);
        return hands[randomIndex];
    }

    /**
     * 勝てる手を取得
     * @param hand HandKind
     * @returns HandKind
     */
    private getWindHand(hand: HandKind): HandKind{
        if(hand == HandKind.Rock){
            return HandKind.Paper;
        }else if(hand == HandKind.Scissors){
            return HandKind.Rock;
        }else{
            return HandKind.Scissors;
        }
    }

    /**
     * 自分と相手の手から勝敗を判定
     * @param playerHand 自分の手 
     * @param computerHand 相手の手
     * @returns 勝敗
     */
    private getResult(playerHand: HandKind, computerHand: HandKind): GameResult {
        if (playerHand === computerHand) {
            return GameResult.Draw;
        } else if (
            (playerHand === HandKind.Rock && computerHand === HandKind.Scissors) ||
            (playerHand === HandKind.Scissors && computerHand === HandKind.Paper) ||
            (playerHand === HandKind.Paper && computerHand === HandKind.Rock)
        ) {
            return GameResult.Win;
        } else {
            return GameResult.Lose;
        }
    }

    /**
     * ジャンケン終了時ハンドラ設定
     * @param handler ジャンケン終了時ハンドラ
     */
    setEndHandler(handler:Function){
        this.endHandler = handler;
    }
}

HandButtonArea.ts

import { HandButton } from "./HandButton";
import { HandButtonsDefine } from "./types";

/**
 * グー・チョキ・パーボタン表示エリア
 */
export class HandButtonArea{
    /** ルート要素 */
    private element:HTMLElement;
    /** グー・チョキ・パーボタン */
    private handButtons:HandButton[] = [];

    /**
     * コンストラクタ
     */
    constructor(){
        // ルート要素
        this.element = document.createElement('div');
        this.element.className = 'hand-buttons';
        
        // グーチョキパーボタンを作成
        Object.values(HandButtonsDefine).forEach(value => {
            // ボタン作成
            const handButton = new HandButton(value);

            // 格納場所に追加
            this.element.appendChild(handButton.getElement());

            // ボタンを保持
            this.handButtons.push(handButton);
        });
    }

    /**
     * 要素取得
     * @returns 要素
     */
    getElement():HTMLElement{
        return this.element;
    }

    /**
     * グー・チョキ・パーボタン取得
     * @returns グー・チョキ・パーボタン
     */
    getButtons():HandButton[]{
        return this.handButtons;
    }
}

HandButton.ts

import type { HandButtonOption, HandKind } from "./types";

/**
 * グー・チョキ・パーボタン
 */
export class HandButton{
    /** 種別 */
    private kind: HandKind;
    /** ルート要素 */
    private element: HTMLElement;

    /**
     * コンストラクタ
     * @param buttonDefine ボタン定義
     */
    constructor(buttonDefine: HandButtonOption){
        // ボタン要素を作成
        this.element = document.createElement('button');
        // idを設定
        this.element.id = buttonDefine.id;
        // classを設定
        this.element.className = "hand-button";
        // 表示名を設定
        this.element.textContent = buttonDefine.name;
        
        // 種別を設定
        this.kind = buttonDefine.id;
    }

    /**
     * 種別取得
     * @returns ボタンの種別 
     */
    getKind(): HandKind {
        return this.kind;
    }

    /**
     * 要素取得
     * @returns 要素
     */
    getElement(): HTMLElement {
        return this.element;
    }

}

types.ts

/** グーチョキパーボタンのオプション */
export interface HandButtonOption {
    id: HandKind;
    name: string;
}

/** グーチョキーパー種別 */
export const HandKind = {
    Rock: "rock",
    Scissors: "scissors",
    Paper: "paper"
} as const

export type HandKind = typeof HandKind[keyof typeof HandKind];

/** グーチョキパーボタン定義 */
export const HandButtonsDefine: Record<HandKind, HandButtonOption> = {
    [HandKind.Rock]: {id:HandKind.Rock, name:"グー"},
    [HandKind.Scissors]: {id:HandKind.Scissors, name:"チョキ"},
    [HandKind.Paper]: {id:HandKind.Paper, name:"パー"},
} as const

/** 勝敗種別 */
export const GameResult = {
    Win: "win",
    Lose: "lose",
    Draw: "draw"
} as const

export type GameResult = typeof GameResult[keyof typeof GameResult];

/** 勝敗の表示名 */
export const ResultNames: Record<GameResult, string> = {
    [GameResult.Win]: "勝ち",
    [GameResult.Lose]: "負け",
    [GameResult.Draw]: "あいこ",
} as const

/** 手を出したタイミング */
export interface PutTiming{
    /** フェーズ */
    phase:number;
    /** 点灯ライト */
    lightIndex:number;
}