wasmbotsをRubyで遊ぼう
まず、wasmbotsとは
という話をしないといけません。これはwasmバイナリを作って、ダンジョン探索をするゲームです。
具体的には?
ダンジョンを探索するためのプログラムを書いて、それを特定のインタフェースを満たしたWASMファイルにコンパイルすれば、あとはそれをアップロードすることでプレイヤーに自動でダンジョン探索をさせることができる、というブラウザゲームです。
プロジェクトのサンプルダンジョンにアクセスしてみれば即イメージが掴めると思います。
アクセスしたら、いくつかプリセットでアルゴリズムが用意されていることがわかります。
適当に1つ選んだら ▶️ のボタンを押すと探索が始まります。
demo用 pic.twitter.com/SEPfMyWcH2
— Uchio Kondo💥 (@udzura) 2025年2月23日
同じように、ダンジョン探索のアルゴリズムを任意の言語で書いて、WASMにコンパイルしてアップロードしたら、自分のコードでプレイヤーにダンジョンを探索させられます。
ただ、任意の言語と言っても、以下の条件を満たしたWASMを生成できないといけません*1。
- 特定の関数をexportするよう指定しなければならない
- 以下の4つ
void clientInitialize(void)size_t setup(size_t requestReserve)bool receiveGameParams(size_t offset)void tick(size_t offset)
- 以下の4つ
- 特定の関数をimportして利用することも可能である
int32_t getRandomInt(int32_t min, int32_t max),void logFunction(int logLevel, unsigned int msgPtr, unsigned int msgLen),void shutdown(void)が用意されている
この条件では例えばGo(GOARCH=wasm などの場合)、そしてRuby.wasmではちょっと難しいかもしれません。また、WASM GCの環境に依存した言語だと動くブラウザと動かないブラウザが出てきそうに思います。
公式のサンプルでは以下の言語が用意されています。
- C
- Rust
- Zig
- AssemblyScript
- tinygo
これらのサンプルをベースにコードを少しずつ変えて遊んでみることができそうです。WASMの素振りにも良い題材ではないでしょうか。
ところで、せっかくなので...
wasmbotsをRubyで遊ぶ手順を紹介しますね...。
*1:インタフェースの表現はC関数に合わせました
mruby/edge バージョン 1.0.3 の話【募集あり】
mruby/edge バージョン 1.0.3 を出した。
これは mruby/edge バージョン 1.0.0 ではないのかという話だが、ちょっと前に 1.0.0 を出したのだが、その時点では Hash と Object#== がなかったりしたので後から追加していたらバージョンを上げることになったため。
RubyスクリプトからComponent Model対応WASMバイナリを作った(実験的バージョン)
まずは実験的ツールの紹介
componentize_any というコマンドラインツールを作りました。Rubyで書いたので以下の方法でインストールしてください。
$ gem install componentize_any ## もしくは $ git clone https://github.com/udzura/componentize_any.git && cd componentize_any $ bundle install
以下のようなスクリプトを用意します。(wittyファイルとでも名付けました)
witty do world do export "wasi:cli/run@0.2.0" end package "wasi:cli@0.2.0" do interface "run" do define "run", :func, {[] => :result}, counterpart: "component_run" end end end
以下のようなRubyとRBSのファイルを用意し、 mec コマンドをインストールしていわゆる普通の(WASI p1依存なしの)WASMバイナリを用意。
# run.rb def component_run 0 end
# run.export.rbs def component_run: () -> Integer
$ cargo install mec --version=1.0.0-rc3 $ mec --no-wasi run.rb $ file run.wasm run.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)
mec とはRubyのスクリプトをCore WASMのバイナリにコンパイルするコマンドです。以下の記事などで解説しています。
これらが揃ったら componentize_any でComponentを作ることができます。
$ bundle exec componentize_any \
-witty-file witty.rb \
--input run.wasm \
--output out.wasm
Writing WAT to /var/folders/sv/...
Compiling WAT to WASM0: /var/folders/sv/...
joining WASM0 files with run.wasm
created out.wasm
run to check: `wasm-tools dump out.wasm 2>&1 | less`
ちゃんとWIT表現を取り出せることを確認。
$ wasm-tools component wit out.wasm
package root:component;
world root {
export wasi:cli/run@0.2.0;
}
package wasi:cli@0.2.0 {
interface run {
run: func() -> result;
}
}
このComponentは wasi:cli/run@0.2.0 を実装しているので、現在の wasmtime ならそのままファイルを渡して実行可能です。実行が(何も警告を吐かずに)成功することを確認します。
$ wasmtime out.wasm $ echo $? 0
この時、Rubyスクリプトの Kernel#component_run で 1 を返すようにして再度この手順を踏めば、実行は正しく失敗します。