アラン・ケイの“メッセージングによるプログラミング”という着想に基づき(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72に実際に触れてみるシリーズ 第2弾です(なお最新のSmalltalkについては Pharo などでお楽しみください!)。
今回は謎言語「Smalltalk-71」で書かれたスペースウォー・ゲームを Smalltalk-72に移植して動かすことを目指します。前回(2019年)を含む他の記事はこちらから→Smalltalk-72で遊ぶOOPの原点 | Advent Calendar 2023 - Qiita
内容が不明の retro
は何か?を意識しつつdisplay
を眺める
moveship
がうまく機能しているのかを宇宙船を表示させて確認したいので、プロシージャ display
を実装します。内容を見てみましょう。
to display ":obj penup, moveto :location, turn :direction create :obj :size if :thrust > 0 then create flame :size if :thrust < 0 then create retro flame :size pause until clock = :time + :framelag end to
":obj
により、引数を通常の方法とは違う方法で受け取っているようです。
コールする側の shpaceship
では display ship
のように記述していますが、このとき ship
プロシージャをコールしてしまってはおかしなことになるので、 ship
プロシージャそのものを引数として渡していると考えるのが自然です。
display
プロシージャ本体では、まず、penup, moveto :location, turn direction
でタートルを :location
の場所に移動し、:direction
の向きに変更しています。
そして続く create :obj :size
で、指定した大きさの宇宙船を描いている(ship :size
)みたいなのですが、ここでの create
の役割が謎ですね。
spaceship
の魚雷(torpedo
)生成はこれがクラスなのでインスタンス生成だろうと想像していたのですが、:obj
として渡された ship
はオブジェクトではなくプロシージャなのでまったく当てが外れてしまったみたいです^^;
次で、:thrust
が正なら通常の場所に、負ならおそらくは逆噴射の場所に flame
を :size
で描きます。ここにも create
が登場します。また、retro
は flame
を修飾する副詞のような使われ方に見えます。
最後の pause until clock = :time + :framelag
は、spaceship
の pause until clock = :time + :movelag
と同様に、並列処理を意識した記述と思われますが、並列性の実現については後で考えることにして今はとりあえず無視しましょう。
retro
と display
を実装して moveship
の動きを再び確認
create
を気にしなければ、retro
にはタートルの向きを逆にする操作を書くだけで、本来、宇宙船の尾部に描くはずのスラスタの炎(flame
)を宇宙船の頭に描いてやることができます。
とりあえず今は、そのような解釈で display
とともに Smalltalk-72 で書いてみることにします。
to retro (@ turn 180) to display obj ( @ penup goto location x location y up turn direction + 90 pendn. :#obj size. (0 < thrust ? (flame size) 0 > thrust ? (retro flame size)) )
Smalltalk-72 May30版のタートルには、その向きをダイレクトに指定する方法が無いので、up
により、あらかじめ決められた向き(270
度)に変更してから、宇宙船の向きdirection
にさらに 90
(=360
-270
)度を足す角度を回転 turn
させることで Smalltalk-71版と同じ効果を出しています。
Smalltalk-71 の display
で引数として(Smalltalk-72 ではメッセージとして)特殊な受け取り方をしている obj
については、create
の役割が分からない状況では判断が難しいところですが、要は obj
として受け取ったこの場合の ship
を obj size
のように記述することで ship size
と同じようにコールできればよい…と割り切れば、Smalltalk-72 ではメッセージとして送られてきた ship
を参照としてフェッチして obj
に代入(:#obj
)することで目的は達成できます。
余談ですが、obj
はこの一度しか使わないので、Smalltalk-72 の場合、obj
というテンポラリ変数の宣言を含め obj
を取り除いてしまうことも可能です(参照フェッチ :#
を括る括弧は新たに必要になりますが…)。
to display (
@ penup goto location x location y up turn direction + 90 pendn.
(:#) size.
(0 < thrust ? (flame size)
0 > thrust ? (retro flame size))
)
最後の thrust
の正負で分岐する処理は、今のところdisplay
アクションのメソッドの最後の処理なので、全体を括る括弧はあっても無くても大丈夫ですが、この後に(thrust
の正負にかかわらず)実行しなければならない処理があるときは括弧は必須です。念のため。
前回の moveship
の動作確認のためのコードに組み込んで実行してみましょう。
disp clear "location _ point 200 200. "speed _ 10. "spscale _ 1.0. "thrust _ 10. "direction _ 270. "dirscale _ 0.5. "steer _ 90. "lscale _ 1.0. "size _ 6. do 10 (moveship. display ship).
逆噴射版も。
disp clear
"location _ point 200 200. "speed _ 10. "spscale _ 1.0. "thrust _ `10. "direction _ 270. "dirscale _ 0.5. "steer _ 90. "lscale _ 1.0. "size _ 6.
do 10 (moveship. display ship).
描いた宇宙船はいつどうやって消しているんだよ…という新たな謎が生じますが、とりあえずはこれでよしとしましょう。
(Altoのシステムクロック取得 へ続く)