rconv - Ruby WASMでプログラム電卓を作りました

https://rconv.ongaeshi.me

Ruby on Browser に続いて Ruby WASM/WASI で電卓っぽいものを作りました。 ongaeshi.hatenablog.com

テキストボックスの内容を入力としてRubyスクリプトに渡して評価し実行結果を出力します。つまり入力→変換→出力するRubyアプリケーションがブラウザ上で簡単に作れます。コードはURLに記録されるので簡単に共有することができます。

基本

Rconv.set()メソッドに渡したブロックの中身が変換プログラムになります。ブロック引数に渡されるのはテキストボックスの内容がevalされたものです。Always string = true のときは常に文字列になります。ブロックの戻り値、もしくは標準出力に何か出力されたときはその内容が、そうでないときはブロックの戻り値が出力されます。

例えば irb 風の挙動を作りたいときはこんな感じ。(入力途中で出てくるエラーメッセージも大変分かりやすくてここはCRubyの蓄積が効いているなぁと思います)

Rconv.set(title: "Rconv", default: 1) do |e|
  p e
end

自分はRubyでいくつかのキーワードをまとめて置換するテキストフィルターみたいなやつをよく書くのですが、そういうのも得意です。

Rconv.set(title: "テキスト置換", default: 1) do |e|
  e.gsub(/\bProgress\b/, "Progressssss")
  .gsub(/@color\b/, "@colorrrr")
  .gsub(/@tty\b/, "@ttyyyyyy")
end

応用

10進数を16進数、2進数に変換したり、

def to_hex_bin(num)
  puts num
  puts "0x" + num.to_s(16)
  puts "0b" + num.to_s(2)
end

Rconv.set(title: "10進数を16進数、2進数に変換",
          default: 4660) do |e|
  to_hex_bin(e)
end

byteからKiB, MiB, GiBに変換とか。

def b2k(byte, n)
  (byte / (2 ** n).to_f).round(2)
end

def puts_b2k(byte, n, unit)
  v = b2k(byte, n)
  puts "#{v} #{unit}" if v != 0
end
  
def byte_to_kib(byte)
  puts "#{byte} byte"
  puts_b2k(byte, 10, "KiB")
  puts_b2k(byte, 20, "MiB")
  puts_b2k(byte, 30, "GiB")
  puts_b2k(byte, 40, "TiB")
  puts_b2k(byte, 50, "PiB")
  puts_b2k(byte, 60, "EiB")
end

Rconv.set(title: "byteからKiB, MiB, GiBに変換") do |e|
  byte_to_kib(e)
end

またRuby/WASMはCRubyなのでちまたにあるRubyスクリプトを移植することができます。自分は昔作ったcaseninjaというgemを移植しました。(変数名を任意のケースに変換することができるプログラム) 結構愛用しています。

(これは長いのでリンク貼ります) Caseninja -rconv

おわりに

rconv/sample.ja.md at main · ongaeshi/rconvにここで紹介したサンプルコードも置いてあるので是非遊んでみてください。よいものができたら #rconv とかで教えてくれると喜びます。

毎日簡単に更新できてサクサク閲覧できるブログシステムを作った

Jekyll + GitHub Actions で構築。ブログの開設から記事の更新まで全てWebインターフェース上で完結します。

github.com

https://day.ongaeshi.me/ が実際に自分が使っているやつなので、これを見るとどんな感じのものが作れるか分かると思います。

記事の投稿

  • マークダウンに日付を付けた見出し(# 2022-07-19)を作ると中身がその日の投稿になります(例: _days/2022.md)
  • # 2022-07-19 今日の出来事のように日付の後ろに空白を開けて記事にタイトルを付けることもできます
  • _days/ディレクトリ以下の全ての.mdファイルが投稿対象になります(ファイルの分割単位は自由です)

記事の投稿に新規ファイルの追加が不要なため、編集用のURLをブックマークしておいて一番上に見出しを追加すれば記事の投稿が完了します。 1ファイルから複数の記事を発行できるので、コンテンツの移動や統合も簡単です。

新規ブログの開設の仕方

https://github.com/ongaeshi/day#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4 の手順に沿って進めます。

GitHubにtemplateという仕組みを使っているため、Use this templateボタンをクリックして自分のレポジトリを作って、あとは自分の設定をしてください。

カスタマイズ

中身は Jekyll なので好きにやってください。

うまく動かない、使ってみたなどありましたら@ongaeshiまで教えていただけると喜びます。

Ruby on Browser 1.0 リリース

窓の杜で取り上げていただいた後も実装は少しずつ続けていて、ひとまずブラウザ上で最新のRubyを試すのに必要な機能は一通り実装できたんじゃないかと思う。リファレンスマニュアルへのリンクを貼ってシンタックスハイライトを入れたりCtrl+Enterで実行できるようにした。(自分が書いたサンプルコードはScrapboxにあるのでコピペして試せます)

https://rubyonbrowser.ongaeshi.me/

f:id:tuto0621:20220417001111p:plain

モバイルでも簡単なコードだったら書けるように色々工夫したのでちょっとしたコードを書きたいときにぜひ試してみてほしい。(Select Allボタンは結構こだわった)

f:id:tuto0621:20220417001229p:plain

他のブラウザ言語処理系と大きく違うこととして「ファイルを読み書きするAPIも使える」ということがある。元々WASIがWASMにファイルIOや通信を持せたることを目的にしたものなのでRuby WASM/WASI自体がファイルIOを持っており、そこにWasmerFSというブラウザ上に仮想ファイルシステムを持たせることができるライブラリと組み合わせて実現している。

おかげで以下のようなサンプルコードをブラウザ上で実行できる!PC上で実行するよりもはるかに安全に色々試せるのはとてもよい。(ファイルはブラウザリロードで消える)

https://docs.ruby-lang.org/ja/latest/class/File.html#I_TRUNCATE

f:id:tuto0621:20220417001133g:plain

まだまだ伸びしろを感じているのでもうしばらくRuby WASIで遊ぶ予定。

Ruby On BrowserとRuby WASM/WASIの雑感

Ruby WASM/WASI の発表にえらくテンションが上がったので、勢いで作ったものが窓の杜で紹介されてびっくりしました。(それだけ注目されているということですね)

Ruby On Browserは51行しかないHTMLでまだまだ荒削りなのでもっとちゃんとしたものを試したい方は是非TryRuby playgroundのCRuby 3.2.0dev をお試しください。

Ruby On Browser自体もまだまだ発展させていくつもりですが、現状Ruby WASM/WASIを触ってみていいなあと思ったことです。

1. 簡単に自分好みのブラウザRubyが作れる

Try Rubyのようにブラウザ上でプログラミング言語が試せること自体は現在はそこまで珍しくないですが、クライアントサイドだけで(しかもとても短いコードで)動かせるのは大変魅力的です。個人のPCやイントラネット上に好みのカスタマイズを加えたブラウザRubyを簡単に構築することができます。

2. wasmファイルの入れ替えが簡単

Webアプリ自体はそのままにバイナリファイルの ruby.wasm を入れ替えるだけで別バージョンのRubyを試すことができます。

3. 最新版がすでにNightlyビルドされている

すでにCRubyのレポジトリに取り込まれているため常に最新の.wasmが動くのも大きいです。Nightlyビルド を使えば一番簡単に最新版を試せる環境となりそうです。(2022-03-25現在のruby.wasmは9.8MiBなので1週間分保管しておいても70MiB程度)

f:id:tuto0621:20220326231418p:plain

4. ファイルAPIが使える

内部で使っている @wasmer/wasmfs はメモリ上に仮想ファイルシステムを構築できるため、File.read, File.write のようなファイルAPIもブラウザ上で動かすことができます。(今実験中です)

WASI自体はファイルシステム以外にソケット通信などもサポートしているため、将来的には require "net/socket"require "net/http"もできるようになるかもしれません。夢が広がりますね。

gifアニメにお絵描きできるGifDrawerをリリースしました

リモートワークでスクショやgifアニメに手書きコメントを付けて共有する機会が増えたのですが、 手書きコメントにもアニメーションがつけられたら便利なんじゃないかと思い作りました。

左クリックで書くf:id:tuto0621:20220308011626g:plainf:id:tuto0621:20220308011651g:plain
f:id:tuto0621:20220308012355g:plainf:id:tuto0621:20220308011726g:plain
f:id:tuto0621:20220308011740g:plainf:id:tuto0621:20220308011752g:plain

インストール

https://github.com/ongaeshi/GifDrawertagsから最新版をダウンロードして適当なところへ展開してexeをダブルクリックすれば動きます。gitレポジトリを直接cloneしてもよいです。

お絵描きしながらタイムラインを動かすことでアニメーションがつけられます。コマ送りを使うと書きやすいです。ペンタブで書きたい人はWindows InkをOFFにしてください。

gifや画像をドラッグ&ドロップするとそれを背景にしてお絵描きできます。

ソフトウェア構成

ClipScriptというタイムラインに連動したアニメーションをスクリプトで記述できるアプリケーションの上で作っています。 ClipScriptはOpenSiv3DのAPIをmrubyにバインドして動いています。

大部分がスクリプトで動いているためmain.rbのパラメータを変更すると色セットやペンの太さを変更することができます(本当はもっと色々できます)。

# 調整用パラメータ

# ペンの色
# "navy", "blue", "aqua", "teal", "olive", "green", "lime", "yellow", "orange"
# "red", "fuchsia", "purple", "maroon", "white", "silver", "gray", "black"
PEN_COLORS = ["red", "blue", "green", "black"]

# ペンの太さ
PEN_THICKNESSES = [1, 2, 4, 8]

# 消しゴムの太さ
ERASER_THICKNESS = 32

# gifアニメが未設定のときの終了時間
DEFAULT_END_TIME = 3

# コマ送りの再生レート(1が60fps、3で20fps)
FRAME_ADVANCE_RATE = 3

.
.

おわりに

感想や質問など #gifdrawer ハッシュタグを付けたりしてつぶやいてもらえたら嬉しいです。

残りゲーム体力が少なくてもブラウザゲームをRubyで簡単に書きたい

crisp-game-libというゲーム制作体力が残り10%でもゲームが書けるJavaScriptのライブラリがあるのだが、それをOpal に移植してRubyで書けるようにした。(画面クリックで遊べます)

https://ongaeshi.github.io/crisp-opal-test/fall/

2色のりんご🍎が落ちてくるので、同じ色を取ると得点、違う色は避ける、画面タップでプレイヤーの色が変わるので状況によって使い分けよう。モバイルの場合は両手プレイで移動と色替えの操作の手を分けると遊びやすいかも。

コード

https://github.com/ongaeshi/crisp-opal-test/blob/master/fall/main.rb

setup(
  title: "FALL",
  description: <<~EOS,
    [SWIPE] Move
    [TAP] Color
  EOS
  characters: [
    <<~EOS,
      llllll
      ll l l
      ll l l
      llllll
       l  l
       l  l
    EOS
    <<~EOS,
       pp
       pp
     rrrrrr
     rrrrrr
     rrrrrr
     rrrrrr
    EOS
    <<~EOS,
       yy
       yy
     llllll
     llllll
     llllll
     llllll
    EOS
  ],
  options: {
    isPlayingBgm: true,
    isReplayEnabled: true,
    seed: 200
  }
)
 
class Falling
  def initialize(string, x, y)
    @string = string
    @pos = vec(x, y)
  end

  def update(player_color)
    @pos.y += 1
  
    if char(@string, @pos).is_colliding.char.a
      if player_color == "red" && @string == "b" ||
        player_color == "black" && @string == "c"
        add_score(1, @pos)
        return true
      else
        end_game
      end
    end

    @pos.y > 102
  end
end

@player_color = nil
@fallings = nil

def update
  if ticks == 0
    @player_color = "red"
    @fallings = []
  end

  if input.is_just_pressed
    @player_color = @player_color == "red" ? "black" : "red"
  end

  color(@player_color)
  char("a", input.pos.x, 95)

  color("black")
  @fallings.push(Falling.new(rndi(2) == 1 ? "b" : "c", rnd(100), 0)) if ticks % 5 == 0
  @fallings.delete_if do |e|
    e.update(@player_color)
  end
end

crisp-game-libはupdateという関数の中に処理を書いてゲームが作れる。初期化処理はif ticks==0の中に書く。

crisp-game-libの特徴として描画処理と当たり判定が一体化しているところがある。対象と重なっているかは描画したときの戻り値を使って判定する(まだ描画されていない対象との当たり判定は当然取得できないので描画順に気をつける必要がある)。これは極力短く書くための仕組みで、ゲームは通常ロジックと描画が分かれて記述することが多いが確かにこれならコード量が半分になり余計なことを考えずに済むと思った。テクスチャも貼れない(ドットは配列作れば打てる)、サウンドは自動生成されたものを番号指定するだけ、効果音組み込みで数種類の中から選択、とライブラリ全体としてゲームを高速に作るために特化していて書いていると心地よい。(このゲームも書き始めてから30分位で書けたと思う) 後リソースを一切要求しないのでコードだけで配布できるのもよい。

もっとたくさんのゲームを触ってみたい方は、crisp-game-lib作者のABAさんがすでに大量の面白いゲームを作られているので是非遊んでみて欲しい。ほぼ全てのAPIがOpalに移植できていると思うのでブラウザゲームRubyで記述したい、という趣味の方がいましたら是非お使いください。