TypeScript 学習 part8

はじめに

こちらの本でTypeScriptの学習を進めています。

前回は、ユーザーが想定外の難易度を入力した時の対処を行いました。今回は、その続きです。

コミット履歴はこちら

行ったこと

ユーザーがゲームを選択できるようにしました

新たにジャンケンゲームを追加して、ゲーム開始時にユーザーが、「ヒット・アンド・ブロー」か「ジャンケン」のどちらで遊ぶかを選択できるようにしました。

実行の様子

jankenを選んだので、ジャンケンが開始されました。

GameProcedureクラス

新たにGameProcedureというクラスを作成して、今回の機能を実装しました。

class GameProcedure {
  private currentGameTitle: GameTitle | '' = ''
  private currentGame: HitAndBlow | Janken | null = null

  constructor(private readonly gameStore: GameStore) {}

  public async start() {
    await this.select()
    await this.play()
  }

  private async select() {
    this.currentGameTitle = await promptSelect<GameTitle>('ゲームのタイトルを入力してください', gameTitles)
    this.currentGame = this.gameStore[this.currentGameTitle]
  }

  private async play() {
    if(!this.currentGame) throw new ErrorEvent('ゲームが選択されていません')
    printLine(`===\n${this.currentGameTitle} を開始します。\n==`)
    await this.currentGame.setting()
    await this.currentGame.play()
    this.currentGame.end()

    const action = await promptSelect<NextAction>('ゲームを続けますか?', nextActions)
    if (action === 'play again') {
      await this.play()
    } else if (action === 'exit') {
      this.end()
    } else {
      const neverValue: never = action
      throw new Error(`${neverValue} is an invalid action.`)
    }
  }

  private end() {
    printLine('ゲームを終了しました。')
    process.exit()
  }
}

こちらの定数の宣言と型定義も行いました。

const nextActions = ['play again', 'exit'] as const
type NextAction = typeof nextActions[number]

const gameTitles = ['hit and blow', 'janken'] as const
type GameTitle = typeof gameTitles[number]

type GameStore = {
  'hit and blow': HitAndBlow
  'janken': Janken
}

即時関数で、GameProcedureのインスタンスのstartメソッドを実行しました。

  ;(async () => {
    new GameProcedure({
      'hit and blow': new HitAndBlow(),
      'janken': new Janken(),
    }).start()
  })()

ジャンケンクラス

ジャンケンクラスを記述しました。(ソースコードはこちらからコピペしました)

const jankenOptions = ['rock', 'paper', 'scissors'] as const
type JankenOption = typeof jankenOptions[number]

class Janken {
  private rounds = 0
  private currentRound = 1
  private result = {
    win: 0,
    lose: 0,
    draw: 0,
  }

  async setting() {
    const rounds = Number(await promptInput('何本勝負にしますか?'))
    if (Number.isInteger(rounds) && 0 < rounds) {
      this.rounds = rounds
    } else {
      await this.setting()
    }
  }

  async play() {
    const userSelected = await promptSelect(`【${this.currentRound}回戦】選択肢を入力してください。`, jankenOptions)
    const randomSelected = jankenOptions[Math.floor(Math.random() * 3)]
    const result = Janken.judge(userSelected, randomSelected)
    let resultText: string

    switch (result) {
      case 'win':
        this.result.win += 1
        resultText = '勝ち'
        break
      case 'lose':
        this.result.lose += 1
        resultText = '負け'
        break
      case 'draw':
        this.result.draw += 1
        resultText = 'あいこ'
        break
    }
    printLine(`---\nあなた: ${userSelected}\n相手${randomSelected}\n${resultText}\n---`)

    if (this.currentRound < this.rounds) {
      this.currentRound += 1
      await this.play()
    }
  }

  end() {
    printLine(`\n${this.result.win}勝${this.result.lose}敗${this.result.draw}引き分けでした。`)
    this.reset()
  }

  private reset() {
    this.rounds = 0
    this.currentRound = 1
    this.result = {
      win: 0,
      lose: 0,
      draw: 0,
    }
  }

  static judge(userSelected: JankenOption, randomSelected: JankenOption) {
    if (userSelected === 'rock') {
      if (randomSelected === 'rock') return 'draw'
      if (randomSelected === 'paper') return 'lose'
      return 'win'
    } else if (userSelected === 'paper') {
      if (randomSelected === 'rock') return 'win'
      if (randomSelected === 'paper') return 'draw'
      return 'lose'
    } else {
      if (randomSelected === 'rock') return 'lose'
      if (randomSelected === 'paper') return 'win'
      return 'draw'
    }
  }
}

次回

次回は、ゲームが終わった時に、次のゲームを選択できるようにしていきます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です