#1 TypeScriptでTA○IR○Nを作ってみた ~タイル配置編~

## 始めに
前回の記事はこちらになります
[#0 TypeScriptでTA○IR○Nを作ってみた ~環境構築編~](https://qiita.com/sts11142/items/90ac4af41ac7eb0b14ff)  
  
前回で環境構築が終了したので、今回からいよいよゲーム本体の制作に取り掛かります。
と言いたいところですが、前回の設定では微妙にエラーが起きてしまうので、今回はその部分の訂正から始めます。  
  

## TSのコンパイルに関わるエラー： 
### モジュール選択

作業を進めていく中で、前回時点の`tsconfig.json`のままでコンパイルしたところ、ブラウザ上でこのようなエラーが出現しました。

```json:tssonfig.json
{
    "compilerOptions": {
        "module": "commonjs",
    }
}
```


```
Uncaught ReferenceError: exports is not defined
```

調べてみると、どうやら原因は自分のモジュール管理方式への認識の誤りにあるようでした。  
JavaScriptでは、異なるファイルに定義された関数などを別ファイルで使用するために`import`や`require`という方法が用意されています。
このよく似た二つの式ですが、どうやら明確に使用方法が異なっているようで、ここの勘違いがエラーを引き起こしていたようです。  

```
commonjs => require()
es6      => import/expoort
```  

先ほどの`tsconfig.json`内では、`"module": "commonjs"`としていました。
しかし自分が普段使用する（今回も使用する）のは`import/export`であり、正しくは`"module": "es6"`と記述しなくてはなりません。

```json:./tsconfig.ts
{
    "compilerOptions": {
        "lib": ["ES2015", "DOM"],
        "module": "es6",
        "outDir": "dist",
        "sourceMap": true,
        "strict": true,
        "target": "es2015"
    },
    "include": [
        "src"
    ]
}
```

### import文のファイル名指定
これは気付きにくいエラーでした。実際にコードで見た方が分かりやすいので、下に例を載せます。  

```typescript:hoge.ts
import { hoge } from './hoge' // これだとエラーが出る
```

```
GET http://127.0.0.1:5500/hoge net::ERR_ABORTED 404 (Not Found)
```

正しくは

```typescript:hoge.ts
import { hoge } from './hoge.js' // .tsファイルでも拡張子は.jsで。
// import { hoge } from './hoge.ts'   // エラーが出る
```


CORS対策にLiveServerを使用していますが、拡張子まで丁寧に記述しないとファイルを認識してくれないようです。
また上の例のように拡張子が`.ts`の状態でコンパイルしてもファイル中の`import`文自体は書き直されないため、コンパイル後のJSファイルでも拡張子が`~ from './hoge.ts'`のままになり正しく認識してくれません。


## ゲーム制作開始
### 描画部分
```html:index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>TAGIRON</title>
    <link rel="stylesheet" href="node_modules/normalize.css/normalize.css" />
    <link rel="stylesheet" href="./src/css/index.css" />
    <link rel="stylesheet" href="src/css/gameScreen.css" />
  </head>
  <body>
    <div id="gameScreen">
      <div class="tileCase tileCase_enem">
        <div class="numberTile"></div>
        <div class="numberTile"></div>
        <div class="numberTile"></div>
        <div class="numberTile"></div>
        <div class="numberTile"></div>
      </div>
      <dic id="field"></dic>
      <div class="tileCase">
        <div class="numberTile"></div>
        <div class="numberTile"></div>
        <div class="numberTile"></div>
        <div class="numberTile"></div>
        <div class="numberTile"></div>
      </div>
    </div>
    <script type="module" src="./dist/index.js"></script>
  </body>
</html>
```

```css:./src/css/index.css
html,
body
{
	font-size: 62.5%;
}
body
{
	width: 100vw;
	height: 100vh;
	/* background: #000; */
	display: flex;
}
```

```css:./src/css/gameScreen.css
#gameScreen {
  width: 100%;
  height: 80%;
  margin: auto 0;
  background: rgb(214, 209, 209);
  position: relative;
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 10rem 1fr 10rem;
  grid-template-areas:
    'tileCase_enum'
    'field'
    'tileCase';
}
.tileCase_enum {
  grid-area: 'tileCase_enum';
}
.tileCase {
  grid-area: 'tileCase';
  position: relative;
  width: 60%;
  height: 10rem;
  margin: 0 auto;
  background: rgb(66, 10, 10);
  display: flex;
  justify-content: space-between;
  padding: 0 6rem;
  align-items: center;
}
.numberTile {
  width: 6rem;
  height: 8rem;
  background: rgb(224, 81, 81);

  font-size: 1.7rem;
  line-height: 8rem;
  text-align: center;
}
#field {
  grid-area: 'field';
  width: 60%;
  margin: 0 auto;
}

```

### タイルカードが定義されたデッキファイル

```ts:./src/TileDeck.ts
export type TileType = {
    color: number,
    num: number,
    isSelected: boolean,
    location: number
}

export const TILEDECK = [
    /** 
     *  color: number
     *      0: 赤色のカード
     *      1: 青色のカード
     *  num: number
     *      カード番号
     *  isSelected: boolean
     *      false: 未配布のカード。配布可能。
     *      ture : 配布済みのカード。配布不可。
     *  location: number
     *      カードの位置(インデックス)。HTMLタグに対応し、0~9まで。
     *      使わない可能性あり。
     */
    {
        color: 0,
        num: 1,
        isSelected: false,
        location: 0
    },
    {
        color: 0,
        num: 2,
        isSelected: false,
        location: 0
    },
    {
        color: 0,
        num: 3,
        isSelected: false,
        location: 0
    },
    {
        color: 0,
        num: 4,
        isSelected: false,
        location: 0
    },
    {
        color: 0,
        num: 5,
        isSelected: false,
        location: 0
    },
    {
        color: 0,
        num: 6,
        isSelected: false,
        location: 0
    },
    {
        color: 0,
        num: 7,
        isSelected: false,
        location: 0
    },
    {
        color: 0,
        num: 8,
        isSelected: false,
        location: 0
    },
    {
        color: 0,
        num: 9,
        isSelected: false,
        location: 0
    },
    {
        color: 1,
        num: 1,
        isSelected: false,
        location: 0
    },
    {
        color: 1,
        num: 2,
        isSelected: false,
        location: 0
    },
    {
        color: 1,
        num: 3,
        isSelected: false,
        location: 0
    },
    {
        color: 1,
        num: 4,
        isSelected: false,
        location: 0
    },
    {
        color: 1,
        num: 5,
        isSelected: false,
        location: 0
    },
    {
        color: 1,
        num: 6,
        isSelected: false,
        location: 0
    },
    {
        color: 1,
        num: 7,
        isSelected: false,
        location: 0
    },
    {
        color: 1,
        num: 8,
        isSelected: false,
        location: 0
    },
    {
        color: 1,
        num: 9,
        isSelected: false,
        location: 0
    }
]
```

タイルカードは、0~9までの数字が書かれた赤色または青色のカードのことです（ただし5が書かれたカードのみ黄色）。これをランダムに5枚ずつ両プレイヤーに割り振ります。相手カードの数字・色の並びを全て当てることで勝利することができます。  
このファイルでは、タイルの型エイリアスと割り振る前のタイルデッキを定義しています。`color`と`num`は固定値で、`isSelected`と`location`が可変値になります（この状態ではどの値の書き換えも出来てしまうのが難点です...）。  

### タイルをデッキから手札にセットする

```ts:./src/SetTiles.ts
import { TileType, TILEDECK } from "./TileDeck.js";
import { swapTiles, showTiles } from "./SwapAndShow.js";


export function SetTiles(elems: NodeListOf<HTMLDivElement>): void {
  let i: number // カウンタ変数
  let TileCase: [TileType[], TileType[]] = [[], []] // TileCase[0]: 相手, [1]: 自分
  const NUMOFTILES = 18 // 数字カード山札の総数 9枚 * 2

  // 山札からランダムにカードを選んで相手・自分に振り分ける
  for (i = 0; i < elems.length; i++) {
    while(true) {
      let rnd = Math.floor(Math.random() * NUMOFTILES)
      let card = TILEDECK[rnd]
      if(!card.isSelected) {
        if(i < 5) TileCase[0].push(card)
        else TileCase[1].push(card)

        card.location = i // どのカードかを記録
        card.isSelected = true // 選択済みにする

        break
      }
    }
  }

  // 手札を適切な形に並び替える
  swapTiles(TileCase)
  // 手札を表示する
  showTiles(TileCase, elems)
}
```

```ts:./src/SwapAndShow.ts
import { TileType } from "./TileDeck";

export function swapTiles(TileCase: TileType[][]): TileType[][] {
  // 手札を適切な形に並び替える
  //    小さい数字が左側
  //    数字が同じ時は赤色(color = 0)が左側
  let i: number

  for(i = 0; i < TileCase.length; i++) {
    TileCase[i].sort((a, b) => {
      if(a.num > b.num) {
        return 1
      }
      else if(a.num === b.num) {
        if(a.color > b.color) {
          return 1
        }
      }
      return -1
    })
  }

  return TileCase
}

export function showTiles(TileCase: TileType[][], elems: NodeListOf<HTMLDivElement>): void {
  let i: number, j: number // カウンタ変数
  let l: number = 0 // インデックス記憶変数
  let tile: TileType


  for(i = 0; i < 2; i++) {
    for(j = 0; j < 5; j++) {
      // elem[]: index 0~9 に対応させる
      if(i === 0) {
        l = j
      } else {
        l = j + 5
      }

      tile = TileCase[i][j]

      // 手札のタイルに数字を表記する
      elems[l].innerHTML = tile.num.toString()

      /**
       * タイルの色を適切な形に変更する
       *    tile.color === 0: 赤
       *               === 1: 青 (default)
       *    tile.num   === 5: 黄
       */ 
      if(tile.color === 1) {
        elems[l].style.background = 'rgb(81, 126, 224)'
      }
      if(tile.num === 5) {
        elems[l].style.background = 'rgb(214, 224, 81)'
      }
    }
  }
}
```

これでタイルの割り当てと表示が上手くいきました。

![スクリーンショット 2021-09-28 20.54.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/946670/abf3c129-90e7-cba8-60e4-9bbb51465276.png)


## 今回のまとめ
今回はコンパイルの際に発生したエラーの解消と、タイルの定義・プレイヤーへの配布・描画までを行いました。
次回は質問カードの定義と場への配布、描画まで行いたいと思います。

## 関連記事
[#0 TypeScriptでTA○IR○Nを作ってみた ~環境構築編~](https://qiita.com/sts11142/items/90ac4af41ac7eb0b14ff)  

\#2 TypeScriptでTA○IR○Nを作ってみた ~質問カード編~