タイピングゲーム

今回はタイピングゲームを作成

今回のあらすじとシナリオはこちら
![Untitled Diagram-Page-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481470/cbf0cb8a-1627-0f0b-dcd1-d771ec3151de.png)



![Diagram-Page-2 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481470/a558000b-f894-54ed-dd3f-e2564bbb78f2.png)

##ランダムでお題を選び出力する
次はあらかじめお題を作っておき、それを出題させる処理

お題は配列に入れておく
記事を参考に

```js
const messages = ['soccer','baseball','basletball'];

const messageNo = Math.floor( Math.random() * messages.length);
console.log(messages[messageNo]);
```
でランダムで出力されるようになった


>https://techacademy.jp/magazine/25665

##まずキー入力の取得について考える
調べていたら先人様がわかりやすく纏めてくれた[qiita](https://qiita.com/riversun/items/3ff4f5ecf5c21b0548a4)があったのでそちらなどを参考にして今回は```event.code```を使ってみたいと思う。
MDNを参考にして押したキーボードをconsoleで出力させた

```js
window.addEventListener("keydown", function(event) {
  let str = event.key
            event.code + "'";
 console.log(str)
}, true);
```
これで出力をさせる事が出来た

>mdn https://developer.mozilla.org/ja/docs/Web/API/KeyboardEvent/code

##入力した文字があっているかを確認する
恐らくここからが問題

if文で比較して合っていたらconsoleにtrueを返したい。

まずはconsoleで文字列を比較する代わりに変数を比較したい。
まだ一文字は出力できていないから、仮でsoccerにサッカーを入れてみた。
```js
const soccer ="socceer"
console.log( soccer == messages[messageNo] );
```
これで問題なくtrue,falseが出力された

if文の条件分岐の同じものを入れてみる

```js
if(soccer == messages[messageNo]) {
    console.log("true");
}
else {
    console.log('false');
}
```
こちらも問題なかった

>https://www.sejuku.net/blog/21413

##文字列を一文字ずつ取得する
お題を一文字ずつ取得してキー入力と正誤判断をする。

記事を参考にランダムでお題の頭文字をとってみる

```js
console.log(messages[messageNo].charAt(0));
```
一文字ずつ取得するのはこれで問題ない
>https://itsakura.com/javascript-charat

今は0だが入力したものが合っていたら次の文字に移れるようにしたい。

思いついた方法は
・for文でぶん回す
・if文でtrueが返ってきたら0+1をする

まずif文で書いてみたいと思う。
どうやって0を1にするか
charAt(0)を変数にしてそこに+1をしていってみる

```js
const num = 0
    if(str == messages[messageNo].charAt(num)) {
        console.log("true");
        const num = num + 1
}
else {
    console.log('false');
}
```
やってみたが```Cannot access 'num' before initialization```とエラー文が返ってきた。

[MDNの記事]()を参考にif文の中を```num =num +1```に修正したら違うエラー文が出た。
```Assignment to constant variable.```

これについての[MDNの記事](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Errors/Invalid_const_assignment)を読んだところ変数の宣言に```const```を使ったミスだった。constは再代入・再宣言が出来ないので、再代入が出来る```var```にしたらエラー文は出なくなった。しかし文字が変わらないのでconsoloで```num```を出力してみると、ずっと1のままだった。

if文だけでのループ処理見たいのはやはり厳しいのか

if文を関数にぶち込めば？
strが関数スコープに存在しているのでアクセスすることが出来ない。
どうしたら使えるか？

もう一度立ち戻って検証した結果、関数スコープに```num```を定義していたのが問題だった。

動かなったコードはこっち

```js
window.addEventListener("keydown", function (event) {
  let str = event.key
    console.log(str)
    var num = 0
    if(str == messages[messageNo].charAt(num)) {
      // console.log("true");
        num = num+ 1
        console.log(num)
}
else {
    console.log('false');
}
}, 
true);
```

正常に動いたのがこっち

```js
    var num =0
window.addEventListener("keydown", function (event) {
  let str = event.key
    console.log(str)
  
    // var num =0
    if(str == messages[messageNo].charAt(num)) {
        // num = num + 1
         // console.log("true");
         num++;
         console.log(num)
    }
           else {
           console.log('false');
        };
    
  },
true);
```
今考えればキー入力をした時に毎回```var num = 0```を定義してたらカウントされないわ、、、

今回ミスをしたので次回からは間違えないことを心に決めて次に行く
(やっぱ思った通り動くと嬉しい)


##単語が終わって次の問題をセット
最後の文字列の文字を打ち終わったらお題を切り替える必要がある。
charAtに最後の文字列を取得できる便利なものはあるか、、、

```console.log(str.charAt(str.length - 1)); // 0```
あった！！！
>http://cly7796.net/wp/javascript/extract-a-string/
置き換えて出力してみる

無事出力完了！！！

```js
console.log(messages[messageNo].charAt(messages[messageNo].length - 1)); 
///baseballの時は「l」が返ってきた
```

これを正誤判定するif文に付け加えたいと思う。

```js
window.addEventListener("keydown", function (event) {
  let str = event.key
    console.log(str)
  
    // var num =0
    if(str == messages[messageNo].charAt(num)) {
        // num = num + 1
         // console.log("true");
         num++;
         console.log(num)
    } else if(str == messages[messageNo].charAt(messages[messageNo].length - 1)) {
    console.log("終わり")
    }else {
           console.log('false');
      };
},
  true);
```
これで最後の文字を打ち込んだ時に「終わり」と返したかったが、出来なかった。
soccerの場合は最後に「r」を二回打ち込まないと「終わり」は帰ってこなかった、、、

恐らく一番上の条件に吸収されているのだと思う。(言い方があっているかわからないが)
だからその中に書いてみる

```js
if(str == messages[messageNo].charAt(num)) {
        // num = num + 1
         // console.log("true");
         num++;
      console.log(num)
      if (str == messages[messageNo].charAt(messages[messageNo].length - 1)) {
        console.log("終わり")
      }
    // } else if(str == messages[messageNo].charAt(messages[messageNo].length - 1)) {
    //   // var messageNo = Math.floor( Math.random() * messages.length);
    //   console.log("おわり")
    }else {
           console.log('false');
      };
```

これで無事に返ってきた。
しかしbaseballやbasketballなど最後に「l」が二回続くと最初のlと後のl両方で「終わり」が返ってきてしまう。
同じ文字では判別できないので文字の数で判断することにする。
[こちらの記事](https://qiita.com/JetNel0/items/0391dbf95665cbe5d220)を参考にして書いてみた。

```js
var array=[]
window.addEventListener("keydown", function (event) {
  let str = event.key
  array.push(event.key);
  let strnum = array.join("")
///以下省略
}
```
if文の条件を```strnum.length == messages[messageNo].length)```をこっちにした所無事終了させることが出来た。
後は終了したら文字列を消去するのとタイピングを間違えた時にそのキー入力が残らないようにする。
終了したときは

```js
array.length = 0;
```

間違えた時は

```js
array.pop()
```
で完了


>https://php-archive.net/javascript/js-typing/
https://murashun.jp/blog/20191110-18.html#chapter-4

後は問題を切り替える処理。

再代入するので

```js
var messageNo = Math.floor( Math.random() * messages.length);
console.log(messages[messageNo]);
```
こちらに切り替えて"終わり"の後に記述したら、
```Cannot read property 'charAt' of undefined```
と怒られた。

```js
messageNo = Math.floor( Math.random() * messages.length);
console.log(messages[messageNo]);
```
とすると次のお題がセットされたが打ち込めなかった。

```js
console.log(messages[messageNo].charAt(num))
```
これで確認した所、空欄になっていた。
やはりちゃんと定義しないといけない。

何となくletに変えたら、さっきのエラーが出なかったが、```console.log(messages[messageNo].charAt(num))```では頭文字ではなく最後の模試が取得された。これは```num```が最後のままだからだと思う。
だから```num```を0にしてみる。
これをconsoleで返したら0になって次のお題も帰ってきて、その頭文字を打ち込んでもfalseと返ってきた。

本当に変わっているかを確認するため

```js
window.addEventListener("keydown", function (event) {
  let str = event.key
    console.log(str)
  console.log(messages[messageNo]);
//以下省略
};
```
で見たらお題が変わっていなかった。
これでは何を打ってもfalseになってしまう。
falseでも確認したらお題が切り替わってなかった。

たぶんif文の中で変えてもダメな気がする
グローバルスコープで変えないといけない。

関数にいれてみた。

```js
function reset() {
  console.log("リセット")
  const messages = ['soccer', 'baseball', 'basketball'];
  var messageNo = Math.floor(Math.random() * messages.length);
  console.log(messages[messageNo])
  let num = 0
  console.log(num);
};
```
この関数を最後の文字のif文に書いたら
関数内では0になったが、また任意のキーを押したらnumもお題も戻ってしまう。

このリセット関数とlet=0に戻すのだけ通常の正誤判定の外に書いてみる。
それでもだめ

numをグローバルスコープに書くのがまずいのか？

頭が混乱してきたが要は関数内からグローバルスコープの変数を変更させたいのである。

reset関数を正誤判定のif文の一番最後に書いたらお題は変わるがnumだけ変わらなくなった。

どうしても厳しかったので[こちらの記事](https://php-archive.net/javascript/js-typing/)を参考にさせてもらいreset関数内の```messages```と```num```の定義のvarを外してグローバルで定義したら上手くいった。


##画面に表示
今まで作ったものを画面に表示させていく。
主にDOMを生成していく。
イメージとしては単語を表示させて、次に打つべき文字だけ異なる色で表示させたい。

まずは単語をまとめて表示する。
先頭に

```js
const body= document.querySelector("body")
const doc = document.createElement("p")
doc.textContent=(messages[messageNo])
body.appendChild(doc)
```
で表示できる

色はこちらでつけようとしたら

```Cannot set property 'Color' of undefined```

色々調べた再度こちらのサイトを参考にさせてもらった。
textcontentではなくinnerHTMLで出力することでプレーンテキストではなくHTMLとして出力するので様々なことが出来る。

```js
doc.innerHTML = (messages[messageNo].substr(0,num))+"<span style= 'color:red';>"+(messages[messageNo]).charAt(num)+"</span>"+(messages[messageNo].slice(num+1))
```
打つべき文字には```spanタグ```で色付けをして、それ以外は```num```を使い```substr```と```slice```でその文字以降とその文字前後を組み合わせることで反映させるようにした。

これで完成である。

>slice・substr・substringの違いはこのサイトで実際に触りながら確認できる
http://catprogram.hatenablog.com/entry/2013/05/13/231457


タイマーなどはまた今度
>https://phpjavascriptroom.com/?t=js&p=stringobject
![Diagram-Page-2 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481470/4ded1c05-6022-73e0-6a4e-17f992c8291a.png)