アラン・ケイの“メッセージングによるプログラミング”という着想に基づき(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72に実際に触れてみるシリーズ 第2弾です(なお最新のSmalltalkについては Pharo などでお楽しみください!)。
今回は謎言語「Smalltalk-71」で書かれたスペースウォー・ゲームを Smalltalk-72に移植して動かすことを目指します。前回(2019年)を含む他の記事はこちらから→Smalltalk-72で遊ぶOOPの原点 | Advent Calendar 2023 - Qiita
obset を活用して簡易スケジューラー spacewar を定義
前回の動作チェックで使った repeat の代わりに、新たに spacewar アクションを定義し、これにその役割を担わせることにしましょう。
こんな仕組みと動きを想定します。
objectsというセットもどき(obset)をクラス変数として持ち、spacewar schedule objで宇宙船や、将来は魚雷などのオブジェクトをobjectsに追加spacewar delete objで指定したオブジェクト、spacewar delete allで全てのオブジェクトのスケジュールを放棄spacewar runでobjects内のオブジェクトに順に制御を渡す
クラス変数 objects に代入される obset は、ALLDEFS(ブートストラップ)で定義されている、要素をユニークなもののみに保つセットの性質も有するシンプルな動的配列オブジェクトです。
☞set ← obset. でインスタンスを生成し、set ← elem とすることで、elem がはじめてなら追加、すでに追加済みなら無視します。
obset は、この elem のユニーク性を維持する ← elem だけでなく、無条件で追加する add elem もあるので、こちらを使えば要素の重複を許す通常の動的配列としても使えます。
ただし、動的“配列”とはいうものの、インデックスでのアクセスなど配列の基本的な機能はいっさい用意されていないので、そういう用途には vec で内部表現のベクター vector のインスタンスを引っ張り出して使うという割り切った仕様になっています。
前述のとおり、ALLDEFSウインドウ、もしくは show obset でその定義を見ることができます。
後の Smalltalk-80 で導入される do: や Ruby の each に相当する map も素朴な実装ながら備えられています。

ところで、組み込みの配列 vector にも map はあるにはあるのですが、この obset の map とは違って、各要素に map の引数となる配列内に収めたシンボルをメッセージとして送るだけという別の仕様になっています。こちらは、Smalltalk の do: や Ruby の each でブロックの代わりにシンボルを与えたときの使い方に似ています。同じ map でもぶれがある雑さがいいですね^^;

spacewarアクションはこんなふうに定義してみました。

to spacewar x y z : : objects (
(null objects ? ("objects _ obset))
%schedule ? (objects _ :#)
%delete ? (%all ? ("objects _ nil) objects delete :#)
%run ? (repeat (objects map "("x _ vec[i]. x)))
)
disp clear
"s1 _ spaceship 'Jimmy' 5 30 false.
"s2 _ spaceship 'Beth' `5 `20 false.
spacewar schedule s1. spacewar schedule s2.
spacewar run
前回同様に2艇の宇宙船 s1 s2 を定義して schedule 後、run してみましょう。

escキーで停止できます。