推しアイデア
ローカルで昔ながらの遊びと、モダンな Web が組み合わさっていること。
ローカルで昔ながらの遊びと、モダンな Web が組み合わさっていること。
みんなであつまったときの盛り上がりになってほしかった。
機械学習を色々なところで使ってユーザ体験を向上させたこと。
HTML 福笑いは、リアルタイムにレンダされない ─つまり、目かくし状態でスタイルを記述してどれだけ目標に近づけられるかを競うゲームです!
ゲーム性としては、観戦やリザルトなどのシステムなどによって多くの人と盛り上がれる点、技術的にはモダンな言語・フレームワークによる堅牢なシステムと随所で機械学習を使ったゲーム体験の向上の工夫が推しポイントです。
お題作成画面です。 お題とともに部屋を作成することができます。
参加者は部屋を選んで参加できます。
するとエディタ画面に遷移し、参加者はお題を目指して HTML とCSS を描くことになります。
フロントエンドは Next.js, バックエンドは Rust, HTMLのレンダや類似度の計算を行うサーバは FastAPI で実装されています。
ゲームが終了したあと、プレイヤーとしてはどのような経過でこんな惨状になったかが気になるところです。
そこでゲーム終了後のリザルト画面を作ることにしましたが、そこでヒントにしたのがボートゲームの形勢グラフです。
ボートゲーム (例えば将棋) では、あとからその手によってどの程度評価値が変動したかを評価することができ、これの推移を見ることでゲームの流れを明確に振り返って楽しむことができます。
HTML 福笑いでも同様に、お題との類似度を評価して、その推移を描画する機能を実装しました 🙌
技術的には、類似度の計算は工夫の余地があるタスクです。
もっとも素直なのは二つの画像のピクセルごとの値の差の平均を取ることなどですが、これだと例えば「全く同じ図形だけど、左右反転している」という状況では、かなり異なった画像と評価されてしまいます。(ですが、明らかにこの二つの画像はそっくりです)
そこでこのアプリでは機械学習を使って人間の直感に近い「類似度」を計算することにしました。
より具体的には、ImageNet で事前学習した適当なモデルにそれぞれの画像を入力し、Conv層の出力のコサイン類似度によって類似度を定義しました。
(ものすごくざっくりいうと、大量の画像を食べたことによって「画像の特徴」っぽいものを出力できるようになった AI の二つの出力がどれくらい似ているかを調べました)
目標に近づけるにはもちろん色を合わせないといけませんが、これをノーヒントでやるのは大変です。そこでカラーパレットを用意して、画面に表示しておくことにしました。
事前に適当に並べたカラーパレットをおいても良いですが、例えば赤色基調のロゴでは他の色がたくさんあっても仕方がないです。
そこで、画像から代表的な色を自動で選ぶアルゴリズムを実装することにしました。
具体的には、K-Means法を使いました。事前に設定したクラスタ内平方和を下回るまでクラスタリングを繰り返してそのときの各クラスタ中心を代表色として選んでカラーパレットとすることにしました。
(ものすごくざっくりいうと、出てくる色すべてと「それなりに近い色」を設定より近さの平均が小さくなるまで選ぶ、という方法でパレットを作りました。)
openapi-generatorでコードを自動生成することによって開発を効率化しました!💪
多くのエンドポイントではHTTPメソッド、リクエストボディ、レスポンスの構造によってビジネスロジック以外の実装が定まります。これらの情報を OpenAPI に従ったスキーマとして記述することで、openapi-generatorによって多くのコードを自動生成しました。 これにより、ルーティングをはじめとした自明な部分は省略し、本質的な実装に注力できました。また、ドキュメントと実装が常に一致するのもメリットで、APIドキュメントはGitHub Actionsを利用して自動生成しています。(https://miuchi-net.github.io/hukuwarai-server)
これはサーバ側とクライアント側のやり取りをスムーズにするために役立ちました。
フロント側でレンダを行うと、パフォーマンスと偽装の懸念があります。
そのため、バックエンド側のレンダ用サーバで高速に画像化しています。
画像化には puppeteer を用いています。 リクエスト毎にいちいちブラウザを立ち上げて画像化していると、観戦画面でのリアルタイム描画に耐える速度で動作させるのが難しいので、適切に再利用して並列に捌くことで十分高速に動作させています。