緊急事態宣言下だし、人狼ゲームを実装してみた【Docker・JavaScript】

:::note warn
GitHubを整理したくて、DockerイメージをGitHub Container Regitryから削除しました。アーカイブ状態ですが、コードは引き続き公開しています。
:::

# 動機
春先の緊急事態宣言下ではZoom飲みが大流行したせいか、オンラインの人狼が重くなって困りました。また発令されたし、 **みんなが自分で人狼ゲームを起動できたら幸せなのでは？** というノリで実装＆公開したので紹介します。

# 紹介
以下のような簡単なUIのチャット人狼です。ホストの一人がDockerでゲームを立ち上げたら、ほかの人は**ブラウザ（スマホでもOK）からアクセスするだけ**で簡単に遊べます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/609b289c-abc0-9a16-7947-b77c3c9c1a6a.png)

## 遊び方（ホスト）
### Dockerコンテナの起動
まず以下のコマンドでDockerコンテナを起動します。初回はGitHub Container Registryからイメージがpullされます。

```
docker container run -it --rm -p 3000:3000 ghcr.io/dr666m1/werewolf
```
起動後、画面の指示に従ってゲームの設定を行ってください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/04c2be11-2bde-be61-1cea-1a8449678b80.png)

### ポートの公開
3000番ポートでゲームが起動しているので、パブリックIPアドレスの**ある**環境（GCEなど）ならみんなにIPアドレスを伝えるだけです。注意点としては、`http://...:3000`のようにポート番号を明示する必要があるかもしれないことと、ファイアウォールの設定を確認しておくことです。

パブリックIPアドレスの**ない**環境では[ngrok](https://ngrok.com/)を利用するのが便利です。初期設定が完了すると`ngrok http 3000`というコマンドで3000番ポートを公開できるようになります。コマンド後にURLが表示されるので、それをみんなに伝えてください。無料枠での制限は[こちら](https://ngrok.com/pricing)ご確認ください。

## 遊び方（全員）
ホストから教えてもらったURLにブラウザでアクセスしましょう。以下のような画面が表示されたら名前を決めて入場してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/80a9dd7a-a484-3dfa-e0a6-82c60e935048.png)

その後は普通の人狼ゲームです。役職は現状「市民」「人狼」「占い師」「霊媒師」「狩人」のみ[^1]と標準的なので、迷うことはないと思います。

[^1]: 要望があれば「狂人」とかいろいろ追加するかもしれません。

# 実装
Node.jsで実装しています。[GitHub](https://github.com/dr666m1/docker_werewolf)に全てのコードが置いてあるので興味があればご覧ください。似たようなものを作る人の参考に、利用したパッケージを簡単に紹介します。全部`npm install`で使えるはずです。

### Next.js
[Next.js](https://nextjs.org/)は[React](https://ja.reactjs.org/)でUIを作成するためのフレームワークです。SSG（static generation）やSSR（server-side rendering）を利用できるのが特徴です。今回は`next export`で静的なHTMLファイルを出力するところまでNext.jsで実装しました[^2]。GitHubリポジトリの[client](https://github.com/dr666m1/docker_werewolf/tree/main/client)ディレクトリ以下が関連ファイルです。

[^2]: [custom server](https://nextjs.org/docs/advanced-features/custom-server)を利用すればNext.jsの枠組み内で実装を完了できそうでしたが、UIに集中したくて分離しました。[Dockerfile](https://github.com/dr666m1/docker_werewolf/blob/main/Dockerfile)を見ての通り、多段階ビルドになっています

### bulma
[bulma](https://bulma.io/)はCSSフレームワークです。DOMのクラスを設定するだけで見た目をいい感じに整えてくれます（例えば以下は`notification`クラス）。bulmaのおかげで、**今回CSSはほとんど書いていません**[^3]。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/4cb8940f-6f5c-0f0a-0939-a5d13b84a3c0.png)

Next.jsの枠組みで利用するには、`_app.js`の冒頭に一行追記するだけです。

```javascript:_app.js

import "bulma/css/bulma.css" // 追記

export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}
```


[^3]: JavaScriptの中で多少スタイルをいじった程度です。

### socket.io
[socket.io](https://socket.io/)はウェブソケットを扱うパッケージで、チャットの実装に利用しました。[ws](https://github.com/websockets/ws)というパッケージもあるのですが、socket.ioの方が多機能なので今回はそちらを使っています（例：ウェブソケットが使えない状況でロングポーリングに切り替えられる、[room](https://socket.io/docs/v3/rooms/#Default-room)や[namespace](https://socket.io/docs/v3/namespaces/#Default-namespace)という概念でクライアントを整理できる）。ちょっと今回の実装では活かしきれなかった部分も多いのですが。

# 後書き
JavaScriptは全くの初心者なので、バグがあったらすみません。Zoom飲みとかにご活用ください。noteにも稀に投稿しているのでよければご覧ください（[直近の記事](https://note.com/dr666m1/n/nac7c70055a50)）。