TypeScript 学習 part7

はじめに

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

前回は、ユーザーがゲームの難易度を選択できるようにしました。今回は、その続きです。

コミット履歴はこちら

行ったこと

ユーザーが想定外の難易度を入力した時の対処

ゲームの設定では、normalとhardモードしか選ぶことが出来ません。仮にユーザーがvery hardと入力してしまった場合は、エラーが発生してしまう状態でした。

完成後の画面

まずnormalかhardを選ぶことが明示され、ユーザーがvery hardを選んでしまったら、再入力を促すようになりました。

promptSelect関数の作成

ユーザーにモードの選択肢を表示するpromptSelect関数を作成しました。赤字の関数(printLine, readLineについて、この後解説します)

const promptSelect = async (text: string, values: readonly string[]): Promise<string> => {
  printLine(`\n${text}`)
  values.forEach((value) => {
    printLine(`- ${value}`)
  })
  printLine(`> `, false)

  const input = await readLine()
  if (values.includes(input)) {
    return input
  } else {
    return promptSelect(text, values)
  }
}

printLine関数はこちらです。

const printLine = (text: string, breakLine: boolean = true) => {
  process.stdout.write(text + (breakLine ? '\n' : ''))
}

また、readLine関数はこちらです。

const readLine = async () => {
  const input: string = await new Promise((resolve) =>
                          process.stdin.once('data', (data) => resolve(data.toString())))
  return input.trim()
}

promptSelect関数は、settingメソッド内で使用しました。

  async setting() {
    this.mode = await promptSelect('モードを入力してください。', ['normal', 'hard']) as Mode
    const answerLength = this.getAnswerLength()

    while (this.answer.length < answerLength) {
      const randNum = Math.floor(Math.random() * this.answerSource.length)
      const selectedItem = this.answerSource[randNum]
      if (!this.answer.includes(selectedItem)) {
        this.answer.push(selectedItem)
      }
    }
  }

ジェネリクスの使用

型を動的に扱えるジェネリクスを使用しました。

const promptSelect = async <T extends string>(text: string, values: readonly string[]): Promise<T> => {
  printLine(`\n${text}`)
  values.forEach((value) => {
    printLine(`- ${value}`)
  })
  printLine(`> `, false)

  const input = (await readLine()) as T
  if (values.includes(input)) {
    return input
  } else {
    return promptSelect<T>(text, values)
  }
}
  async setting() {
    this.mode = await promptSelect<Mode>('モードを入力してください。', ['normal', 'hard'])
    const answerLength = this.getAnswerLength()

    while (this.answer.length < answerLength) {
      const randNum = Math.floor(Math.random() * this.answerSource.length)
      const selectedItem = this.answerSource[randNum]
      if (!this.answer.includes(selectedItem)) {
        this.answer.push(selectedItem)
      }
    }
  }

DRYなコードにした

DRY(Don’t repeat yourself)なコードにしました。(繰り返しを避けたコード)

const modes = ['normal', 'hard'] as const
type Mode = typeof modes[number]

class HitAndBlow {
  private readonly answerSource = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
  private answer: string[] = []
  private tryCount = 0
  private mode: Mode = 'normal'

  async setting() {
    this.mode = await promptSelect<Mode>('モードを入力してください。', modes)
    const answerLength = this.getAnswerLength()

    while (this.answer.length < answerLength) {
      const randNum = Math.floor(Math.random() * this.answerSource.length)
      const selectedItem = this.answerSource[randNum]
      if (!this.answer.includes(selectedItem)) {
        this.answer.push(selectedItem)
      }
    }
  }
// 以下省略

次回

次回は、新しいブランチを切って、hit and blow以外も遊べるようなゲームにしていきます。

コメントを残す

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