🐇

MoonBitでテトリスを実装した

に公開

この記事は MoonBit Advent Calendar 2025 6日目の記事です。
https://qiita.com/advent-calendar/2025/moonbit

はじめに

こんにちは、千葉工業大学 学部1年の @ramenha0141 です。
この記事では、MoonBit (JS backend) を用いてテトリスを実装する過程で得た知見や感想を共有していきます。

成果物


山岳積み2号→DPCの様子
SRS(Super Rotation System) を実装しているので各種スピンが可能
https://tetris-mbt.ramenhaoishii7525.workers.dev/
https://github.com/ramenha0141/tetris.mbt

使用ライブラリ

MoonBit側

moonbitlang/async

  • MoonBitで非同期処理を扱うために必要なライブラリ

mizchi/js

  • JavaScriptの各種APIのFFIバインディングを提供するライブラリ
  • Browser API / Web API だけでなく、Node.js や Cloudflare (workerd) など各種ランタイムのAPIも提供
  • Hono などの著名npmパッケージも対応拡大中
  • 今回は主に Canvas API を使用

JavaScript側

Vite

  • HTML, CSS, MoonBitをコンパイルして出力したJavaScriptをバンドルするために使用

tailwindcss

  • ほぼ装飾はしていないが、操作説明などに使用

実装

大まかなディレクトリ構造は以下のようになっています。

src/
├─ core/
│  ├─ types/
│  ├─ mino/
│  ├─ game.mbt
├─ controller/
├─ renderer/
├─ main.mbt
index.html
vite.config.ts

各ファイル/パッケージの説明

core/types

  • Vec2, Matrix[T], Cell, Rotationなど、基本的なデータ型を実装

core/mino

  • 各ミノの形状、移動・回転・ハードドロップの挙動などを定義

core/game.mbt

  • ゲームの全体的な動作を実装

controller

  • キーボード入力の処理やキー長押し時のDAS/ARR処理を担当

renderer

  • Canvas API を用いた盤面の描画を担当

実装ポイント

演算子のオーバーロード

https://github.com/ramenha0141/tetris.mbt/blob/9ae8577dbb4b9b0c7a779dfca22fcb2e6554198c/src/core/types/vec2.mbt#L33-L35

イテレータ

https://github.com/ramenha0141/tetris.mbt/blob/9ae8577dbb4b9b0c7a779dfca22fcb2e6554198c/src/core/types/vec2.mbt#L48-L58

  • ジェネレータが欲しくなる
  • 今回使用したIterの他にIteratorもあるらしい

https://github.com/ramenha0141/tetris.mbt/blob/9ae8577dbb4b9b0c7a779dfca22fcb2e6554198c/src/core/types/matrix.mbt#L23-L35

  • 使う側はこんな感じ
  • for i, e in iter { ... }でインデックスもくれる
  • 現状for inIterの組み合わせでタプルの分解はできないようなのでtなど一時的な変数に入れてあげる必要がある

パターンマッチング

https://github.com/ramenha0141/tetris.mbt/blob/9ae8577dbb4b9b0c7a779dfca22fcb2e6554198c/src/core/game.mbt#L147-L164

  • match便利

JS FFI

https://github.com/ramenha0141/tetris.mbt/blob/9ae8577dbb4b9b0c7a779dfca22fcb2e6554198c/src/main.mbt#L11-L39

  • HTMLCanvasElementKeyboardEvent@identityでキャストしてあげる必要があった
  • wait_animation_frameを使うとrequest_animation_frameasync fnの文脈で自然に扱える
  • 想像より楽にJSのAPIを扱えた

感想

全体的にかなり書き心地が良く、GCありRustと呼ぶのに相応しい言語だと感じました。
特に、演算子のオーバーロードやパターンマッチングが非常に強力で、MoonBitを使用するモチベーションにつながる機能だと感じます。
また、例の障害で取り沙汰されている.unwrap()も、いつどこでエラーがthrowされるかわからないTypeScriptと比較すると明示的であり、改善されていると考えられます。

今回実装したテトリスでは、キーイベントの取得や盤面の描画において多数のJS APIを使用しました。
繰り返しになりますが、想像より遥かに簡単に言語の壁を越えることができ、TypeScriptと大差ないコード量で記述することができました。
MoonBitのFFIやそれを利用したバインディングライブラリは、既存エコシステムの利用という面で言語の普及に大きく貢献するものであると考えます。

幾つか改善できそうな点はありますが、まだまだアーリーステージな言語であることを踏まえると非常に完成度が高いように感じました。
少なくとも基本的なテトリスの実装ができる程度の実用性は証明できたので、今後はさらに発展的なプログラムの実装に挑戦していきます。

最後に

MoonBitについてはZennやTwitterで何度か目にする機会はありましたが、今回のAdvent Calendarをきっかけに、初めて実際に触れることができました。
主催者の@mizchiさんに心から感謝を申し上げます。
また、MoonBitの開発に関わる全ての方に感謝を述べるとともに、今後も応援させていただきたいと思います。

明日は@mizchiさんの「mizchi/js.mbt で TS の代わりに Moonbit を書く」です。
https://qiita.com/advent-calendar/2025/moonbit

Discussion