ruby.wasm を読み込んでいます(初回は 8MB ほど)

Ruby Tanks

ruby.wasm でブラウザ上のCRubyが動いています。setup / draw を書いて戦車AIを操り、BGMも Ruby の DSL で鳴らします。

Arena TICK 0
BLUE — きみ
HP 100 / 命中 0 / 発射 0
VS
LV1 ROTARY
HP 80 / 命中 0 / 発射 0
API リファレンス

動かす(1tickあたりの上限つき)

forward(v) / back(v)
前後に進む。最大 4px
turn(deg)
車体を回す。最大 ±9°
turn_gun(deg)
砲塔を回す。最大 ±22°
turn_radar(deg)
レーダーを回す。最大 ±45°
fire(power)
0.5〜3.0。強いほど遅く重く、次弾まで長い
say(text) / beep(:c4)
吹き出しと効果音

見る

enemy?
今このtickでレーダーが敵を捉えているか
enemy_distance / enemy_angle / enemy_hp
捉えていれば値、いなければ nil
gun_offset
砲塔と敵の角度差。0に近いほど当たる
ready?
次弾が撃てる状態か
x, y, heading, gun, radar, hp, time
自分の状態
enemy_x / enemy_y
敵の絶対座標。自分が動いてもぶれないので、速度を測るならこちら
flight_time(dist, power)
その弾が dist 進むのにかかる tick 数
wall_ahead?(dist)
その距離先が壁か
angle_to(x,y) / center_angle
指定座標・中央への絶対角

寄せる

aim_at_enemy
砲塔を敵へ。角度差を返す
turn_to_enemy / strafe
車体を敵へ/敵に対して真横へ
norm(deg) / clamp(v,lo,hi)
角度を-180..180に/値を範囲に収める

勝ち上がりのヒント

お手本の blue.rb では LV1 にも勝てません。段ごとに必要な発想があります。
LV1・LV2 — 横に走る
turn_to_enemy を strafe に替えるだけで被弾が激減します
LV3 — 敵の速度を測る
enemy_x / enemy_y を毎tick覚えておき、差を取れば敵の速度が出ます。 そこへ flight_time ぶん進んだ先を angle_to で狙います。 角度の変化量では自分の動きが混ざって当たりません
LV4・LV5 — 読まれない
同じ向きに回り続けると先読みされます。 数十tickごとに forward の符号を反転すると急に当たらなくなります
倒した相手のコードはそのまま読めます。写して改造するのが一番早い上達法です。

bgm.rb(Sonic Pi 風)

use_bpm(110)
テンポ
live_loop(:bass) { ... }
そのブロックの長さで延々ループ
play(:c4, dur:, amp:, wave:)
wave は :saw :square :triangle :sine
chord(:c3,:ds3,:g3)
同時に鳴らす
drum(:kick) / drum(:hat) / drum(:snare)
打楽器
sleep(1)
1拍進める。これで時間が流れます

仕組み

ruby.wasm
CRuby 3.4 を WASI ビルドしたもの。CDNから wasm を落として WebAssembly で実行しています
setup / draw
Processing と同じ考え方。setup は1回、draw は毎tick(30fps)
物理と描画
JS側の担当。Ruby は「意図」だけを返し、移動量・命中判定はエンジンが決めます。Robocode と同じ分担です