Angularでブラウザ版チェスゲームを作ってみた

前回（[Angularでブラウザ版マインスイーパを作ってみた](https://qiita.com/Hiroyasu_Sato/items/253988f30fee969056fc)）に続き、Angularゲーム制作シリーズです。

#完成したもの
![simple_chess_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431212/7a999035-4bff-4e48-ef21-b9a16bcd7082.gif)

こちらは全て手動で、コンピューター戦のような自動操作はありません。

###実装できた機能
- 駒の移動できるマスの表示
- キャスリングやポーンの特殊な動きの実装
- チェックの表示（キングを赤で囲む）
- プロモーションメニュー表示
- 駒の動きのアニメーション

###実装できなかった機能
- 引き分けの判定（難しい）

#工夫した点

##駒の動きのルールの実装
駒の種類ごとに1個1個クラスを作成してメソッドを作るのはちょっと面倒なので、PiecesMaster抽象クラスを作ります。

```typescript:PiecesMaster 
export abstract class PiecesMaster {
    // 方向と距離を決めるオブジェクト
    protected bectorInfo: {
        // 方向
        bectors: {
            file: number,
            rank: number,
        }[],
        // 距離
        infinite: boolean
    };

    protected pieceObjectInfo: Piece;

    constructor(piece: Piece) {
        this.pieceObjectInfo = piece;
        this.setBectorsInfo();
    }

    protected abstract setBectorsInfo();

    public getBectorInfo(){
        return this.bectorInfo;
    }
}
```

それぞれの駒の種類ごとのクラスでPiecesMasterクラスを継承します。
ビショップとキングの例を以下に書きます。

```typescript:Bishop
export class Bishop extends PiecesMaster{
    constructor(piece: Piece){
        super(piece);
    }

    // 斜め4方向
    protected setBectorsInfo() {
        const bectors = [
            {
                file: 1,
                rank: 1
            },
            {
                file: 1,
                rank: -1
            },
            {
                file: -1,
                rank: 1
            },
            {
                file: -1,
                rank: -1
            }
        ]

        this.bectorInfo = {
            bectors: bectors,
            // 可能な限り遠くまで動ける
            infinite: true
        };
    }
}
```

```typescript:King
export class King extends PiecesMaster{
    constructor(piece: Piece, bis: BoardInfoService){
        super(piece);
    }

    // 8方向
    protected setBectorsInfo() {
        const bectors = [
            {
                file: 1,
                rank: 0
            },
            {
                file: 1,
                rank: 1
            },
            {
                file: 0,
                rank: 1
            },
            {
                file: -1,
                rank: 1
            },
            {
                file: -1,
                rank: 0
            },
            {
                file: -1,
                rank: -1
            },
            {
                file: 0,
                rank: -1
            },
            {
                file: 1,
                rank: -1
            },
        ]

        this.bectorInfo = {
            bectors: bectors,
            // 1マスのみ進める
            infinite: false
        };
    }
}
```

駒ごとのComponentが、これらのいずれかのクラスのインスタンスを生成することで、自身の移動ルールを持っておくことができます。

##チェックの判定
チェックの判定は、駒ごとのComponent(PieceComponent)が、現在の盤面から自分の移動できるマスを判定し、それをチェック判定サービス(CheckJudger)に渡して判定してもらいます。その結果をGameMasterに渡し、GameMasterが相手のキングのComponentに結果を知らせることで、キングのComponent自身が自分を赤で囲むようになっています。
![チェックの判定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431212/d4c38de9-5777-0ebf-d9db-2d33ece9f03f.png)

また、チェスのルール上、駒が動いたことで自分のキングがチェックになるような移動は禁じられているので、システム側がそれを不可能にしておく必要があります。

以下、その動きの例。チェックを防ぐような手しか動かせないようになっています。
![simple_chess_2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431212/1197fe20-e4c6-5c31-d787-c5119fd9e3db.gif)

どの駒がどこに動けばチェックになってしまうのかをあらかじめ計算しておく必要がありますが、上のチェック判定の処理を応用します。
![駒の移動後にチェックになるかどうかの判定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431212/521e76c5-2dd5-a94c-39c6-540294226785.png)

#感想
チェスはマインスイーパ以上にルールが複雑で、キャスリングやアンパッサンなどの変わった動きが多いので、全てを実装するのが大変でした。それらを全て漏れなく一人で実装できたのは奇跡だと思います。

余裕があれば、AIの導入もしてみたいです。AI自体はOSSがあるので、それをAngularにどう埋め込むかが課題になるかもですね。