Smalltalk-72で遊ぶOOPの原点:スラスターの火炎を描く(再び謎解き&少しアレンジ編)

アラン・ケイの“メッセージングによるプログラミング”という着想に基づき(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72に実際に触れてみるシリーズ 第2弾です(なお最新のSmalltalkについては Pharo などでお楽しみください!)。

今回は謎言語「Smalltalk-71」で書かれたスペースウォー・ゲームSmalltalk-72に移植して動かすことを目指します。前回(2019年)を含む他の記事はこちらから→Smalltalk-72で遊ぶOOPの原点 | Advent Calendar 2023 - Qiita


flame のあるべき実装を予測する

triangle が用意できたので、改めて Smalltalk-71 の flame を見ていきましょう。

スキャン画像を再掲します。

flame は、:size を引数にとるプロシージャ(Logo風に言うと。あるいは、Smalltalk-72風にはアクション)です。まず、軌跡が残らないようにペンを上げ(penup)、反対を向き(left 180)、2 + sqrt 3 だけ移動し(forward 2 + sqrt 3)描き始めるためにペンを下げています(pendown)。

はい。まずここ。ちょっと引っかかりますね^^;

:size を引数にとるこれまでのプロシージャの例に倣えば、forward 2 + sqrt 3forward (2 + sqrt 3) * :size の方がしっくりきます。

また、炎が描かれる場所までのタートルの移動と考えても、2 + sqrt 3 という中途半端な固定長より、宇宙船の胴体の半分の長さ、つまり (2 + sqrt 3) * :size 分だけ移動すると考える方が妥当です。

そのあとのtriangle size についても、size という返値有りプロシージャの存在という可能性も微レ存ながら、ここはやはり flame プロシージャが引数として得た :size を参照するのがスジだと思います。したがって:が抜けていると解釈します。

次の forward .5 については . をゴミと見ることもできますが、文字の間隔や次行の 1.5. と似た位置と大きさであることから、そのままの .50.5)が妥当と判断しました。 triangle 1.5 * :size の行の forward の後の 5 についても同様に . が消えた .50.5)でよさそうです。

最後の triangle 1 * :size の行の forward の後の空白の解釈は難しいですが、ここは素直に空白ととらえて forward :size と考えることにしましょう。

etc. は、ペンの位置と向きを元あった場所に戻す記述の省略と思われます。これは冒頭の penup の行の逆+α(triangle を描くために移動した分も戻す)を記述すればよいだけです。

したがって、あるべき Smalltalk-71 の flame の定義はこうなります。

to flame :size
  penup, left 180, forward (2 + sqrt 3) * :size, pendown
  triangle :size, forward .5 * :size
  triangle 1.5 * :size, forward .5 * :size
  triangle 2 * :size, forward .5 * :size
  triangle :size, forward :size
  penup, left 180, forward (2 + sqrt 3 + 2.5) * :size
end to

Smalltalk-72 へトランスパイルして動かしてみましょう。

to triangle size (
    @ pendn turn 90 go 0.5 * :size
        turn `120 go size
        turn `120 go size
        turn `120 go 0.5 * size turn `90
)

to flame size (
    @ penup turn 180 go ((sqrt 3) + 2) * :size pendn.
    triangle size. @ go 0.5 * size.
    triangle 1.5 * size. @ go 0.5 * size.
    triangle 2 * size. @ go 0.5 * size.
    triangle size. @ go size.
    @ penup turn 180 go ((sqrt 3) + 2.5 + 2) * size
)

disp clear
@ home penup goto 200 200 pendn. ship 30. flame 30

よい感じなのですが、炎の中の縦線がちょっと邪魔だし、最後の線分の長さも中途半端なので、ここは大胆(?)に改変して triangle後の縦線は描かないように penup を挿入することにします。

to triangle size (
    @ pendn turn 90 go 0.5 * :size
        turn `120 go size
        turn `120 go size
        turn `120 go 0.5 * size turn `90
)

to flame size (
    @ penup turn 180 go ((sqrt 3) + 2) * :size pendn.
    triangle size. @ penup go 0.5 * size.
    triangle 1.5 * size. @ penup go 0.5 * size.
    triangle 2 * size. @ penup go 0.5 * size.
    triangle size. '@ go size'.
    @ penup turn 180 go ((sqrt 3) + 1.5 + 2) * size
)

disp clear
@ home penup goto 200 200 pendn. ship 30. flame 30

[追記: よく考えると shippenup した状態で終わっているので、flametriangle もそれに倣っておけば、このアレンジは無用ですね^^; ]

to triangle size (
    @ pendn turn 90 go 0.5 * :size
        turn `120 go size
        turn `120 go size
        turn `120 go 0.5 * size turn `90
        penup
)

to flame size (
    @ penup turn 180 go ((sqrt 3) + 2) * :size pendn.
    triangle size. @ go 0.5 * size.
    triangle 1.5 * size. @ go 0.5 * size.
    triangle 2 * size. @ go 0.5 * size.
    triangle size. @ go size.
    @ penup turn 180 go ((sqrt 3) + 2.5 + 2) * size
)

disp clear
@ home penup goto 200 200 pendn. ship 30. flame 30

「sqrt」を実装する(準備編)へ続く)