Smalltalk-72で遊ぶOOPの原点:「retro」と「display」の仮実装

アラン・ケイの“メッセージングによるプログラミング”という着想に基づき(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された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 が登場します。また、retroflame を修飾する副詞のような使われ方に見えます。

最後の pause until clock = :time + :framelag は、spaceshippause until clock = :time + :movelag と同様に、並列処理を意識した記述と思われますが、並列性の実現については後で考えることにして今はとりあえず無視しましょう。

retrodisplay を実装して 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 として受け取ったこの場合の shipobj 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のシステムクロック取得 へ続く)