Open14

オンライン対戦フェアリーチェスウェブアプリをつくる

みどりみちみどりみち

コマンドラインから起動するフェアリーチェスアプリを前に作ったのですが、ブラウザで通信対戦ができるようにしたいので作りたいなという気持ち…

Image from Gyazo

https://github.com/midorimici/fairy-chess

フェアリーチェスができるサービスって実はあまりないような気もする。

みどりみちみどりみち

もとのコードも結構大きくてぐちゃぐちゃなので、この機会に画像ファイルとかゲームや駒のデータを分離してきれいに書き直したいという動機もある。

みどりみちみどりみち

とりあえず

画像ファイル(駒・動き説明): Storage
駒・ゲームのデータ : Cloud Firestore
通信 : Realtime Database

で考えています。

みどりみちみどりみち

Alice Chess のほうはゲームが一種しかないけど、今回はゲームがたくさんあるので、SPA ではなくなりそう。

みどりみちみどりみち

Next, Chakra UI, Storybook, ESLint, Jest + React Testing Library になりました。ちゃんと環境整えようとするとセットアップが割と大変ですね…
前作ったアプリ(このチェスアプリも含む)を改修するときに大変な想いをしたので、今回は可読性・保守性を高くしようと欲張りセットです。

みどりみちみどりみち

駒やゲームのデータは更新することがほとんどなくだいたい参照するだけだから Firestore
対戦中のデータは頻繁に更新されるから Realtime Database
というふうに分けていたけど、データ量多くないし両方 Realtime Database に入れても無料枠に収まりそう…とか思い始めた。
いや、そもそも駒とゲームのデータはリアルタイム性不要だし自分しか書き込みしないし、CMS で管理してもいいよなあ

みどりみちみどりみち

いちおう Storage に JSON 入れる方法もあるらしいけど、管理がつらそうなので CMS でいきたいと思います。
画像ファイルも入れられるので Storage も使わずに
駒・ゲームのデータ・画像を CMS で

みどりみちみどりみち

調べたら locize 以外にもいろいろあるらしい…

https://zenn.dev/terrierscript/scraps/8039918a4d5bdb

こういうサービスを使って API でリソースを取得して表示をしようとしていろいろ試行錯誤したのですが、結局うまく行かず断念しました。
CLI でローカルにダウンロードして、それを next-i18next から使うというのはできるので、
ローカルで JSON 書かなくてよくなるだけでもいいやと思い i18nexus を使いました。

https://i18nexus.com/

みどりみちみどりみち

さて、駒の情報を CMS に登録します。
駒の名前とか価値はいいものの、駒の動きをどう扱えばいいのか…
現行だと Python のコードで関数を作って使っているんだけど、関数は登録できないのでちょっと考えないといけないですね。

みどりみちみどりみち

関数には駒の位置を渡していたりします。平行移動してあげればいいので、駒の位置を (0, 0) として、駒が原点にあるときの移動可能な動きの情報を JSON で管理することにしました。

Pawn の例
{
  "moves": [
    {
      "move": {
        "type": "rider",
        "direction": [0, 1],
        "length": 2,
        "option": "move-only",
      },
      "when": "piece.moveCount === 0",
    },
    {
      "move": {
        "type": "leaper",
        "direction": [0, 1],
        "option": "move-only",
      },
    },
    {
      "move": {
        "type": "leaper",
        "direction": [1, 1],
        "option": "capture-only",
      },
    },
    {
      "move": {
        "type": "leaper",
        "direction": [-1, 1],
        "option": "capture-only",
      },
    },
  ],
}

CMS で JSON をフィールドとして持たせてもバリデーションができなかったりするのでそこのつらさはある🥺

みどりみちみどりみち

人力で JSON をいちいち指定するのは本当につらいので、できるだけ楽したい気持ちがある。
なのできれいにすぐに使えるデータを入れるのではなく、クライアント側で parse してしまおうかという。

{
  "id": "N",
  "color": "W"
}

とかではなくて WN としてしまってもいいよなあ。
とか考えながら CMS にコンテンツを作成していく。

みどりみちみどりみち

GraphCMS からデータを取得するのは比較的楽にできました!

graphqlgraphql-request を入れてからこのように書いて

lib/graphcms.ts
import { GraphQLClient } from 'graphql-request';

const graphcms = new GraphQLClient(process.env.GRAPHCMS_URL ?? '');

export const fetchGql = async <T>(query: string) => graphcms.request<T>(query);

getStaticProps でこんな感じに書けばデータを取得できます。

pages/games.ts
export const getStaticProps: GetStaticProps = async () => {
  const data = await fetchGql<GamesResponse>(`
    {
      games {
        id
        name
        description
      }
    }
  `);

  return {
    props: {
      games: data.games,
    },
  };
};

権限とか Publish 状態とかは気を付けないとデータが取得できないので注意しましょう😇