Smalltalk-72で遊ぶOOPの原点:魚雷を実装する
アラン・ケイの“メッセージングによるプログラミング”という着想に基づき(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72に実際に触れてみるシリーズ 第2弾です(なお最新のSmalltalkについては Pharo などでお楽しみください!)。
今回は謎言語「Smalltalk-71」で書かれたスペースウォー・ゲームを Smalltalk-72に移植して動かすことを目指します。前回(2019年)を含む他の記事はこちらから→Smalltalk-72で遊ぶOOPの原点 | Advent Calendar 2023 - Qiita
torpedo
と torp
魚雷 torpedo
とその描画用プロシージャの torp
は、宇宙船 spaceship
に対する ship
のペアと同じ関係にあります。
継承があれば torpedo
はきっと spaceship
との共通部分を抽象クラス化してそれを継承して作ると少し楽ができそう(Smalltalk-72版ではコードが膨れ上がったので…)ですが、Smalltalk-72 同様に Smalltalk-71 にも継承機構は想定されていなかったようで、一部重複するコードで実装する必要があります。
まず描画用の torp
ですが、これは例によって省略されているので、ship
から尾翼を省いて少し小さめにした処理で済ませました。
to torp size ( @ penup turn 180 go :size turn 90 go 0.5 * size turn 90 pendn go 2 * size turn 30 go size turn 120 go size turn 30 go 2 * size turn 30 go size turn 120 go size penup turn 120 go 0.5 * size turn `90 go size)
魚雷 torpedo
は、前述のとおり spaceship
と基本的なところは同じなのですが、
- パイロット名
pilot
は無い - 速度
speed
と位置locx
locy
(Smalltalk-71版ではlocation
)、そして方向direction
は射出された時点のspaceship
のそれに従う - 推進力
thrust
は常に0
(つまり、速度speed
はそのまま)
という点で異なることが Smalltlak-71版の元コードから読み取れます。
書かれてはいませんが、向き direction
も変わらず一定であるべきなので、舵 steer
も当然 0
であるべきでしょう。
位置の更新に moveship
を、描画用の torp
を呼ぶ際に display torp
を使っているので、魚雷としては不要なはずの thrust
や steer
に加え、Smalltalk-72版で追加した直近描画の位置等情報の llocx
llocy
lldir
lthr
も宣言と初期化が必要になります。
射出時の初期位置 locx
locy
は元コードのままだと spaceship
と重なっており、これでは crash?
が反応してしまうので、direction
の方向に SSIZE * 3
ほど移動して現れるように変えています。後述の時限のしくみに倣って、生成直後から一定時間 crash?
の実行を行わないというやり方でも良いかもしれませんね。
Smalltalk-71の元のコードでは、画面をまたぐと消滅するようですが、moveship
で位置は画面をまたぐように正規化されてしまっており、またいだことを知る方法もないため、Smalltalk-72版では時限を設けて一定時間(グローバル変数 TORPLIFE
)で無効化して消滅することにしました。
時限を迎えたり他のオブジェクトと接触した時の消滅 finish SELF
の際には、発射した spaceship
の numtorps
のデクリメントを行う必要があるのですが、発射した spaceship
の numtorps
にアクセスできるコンテキストから外れてしまうため spaceship
に numtorps
をデクリメントする debumptorps
(と、必要ないですがインクリメントする bumptorps
も)用意しこれをコールしています。なお torpedo
インスンタス生成時に、それを射出した spaceship
(自身)を launcher
として渡すような変更も加えています。
魚雷が時限を迎えて消滅するときのために、display
アクションに消去だけする erase
オプションも用意しました。
to torpedo : thrust steer locx locy speed direction time ftime llocx llocy ldir lthr launcher endlife ( isnew ? (:launcher. :speed. :locx. :locy. "ldir _ :direction. "locx _ "llocx _ locx + (cos direction) * SSIZE * 10. "locy _ "llocy _ locy + (sin direction) * SSIZE * 10. launcher bumptorps. "thrust _ "lthru _ "steer _ 0. "time _ "ftime _ clock. "endlife _ clock + TORPLIFE) %release ? ( stick delete thrust. stick delete steer. stick delete trigger) %locx ? (!locx) %locy ? (!locy) %step ? ( 0 < clock - time + MOVELAG ? ( "time _ clock. 0 < clock - endlife ? ( launcher debumptorps. display torp erase. finish SELF) moveship. crash~ SELF. display torp)) %is ? (ISIT eval) )
to display obj (
:#obj.
0 < clock - ftime + FRAMELAG ? (
"ftime _ clock.
@ penup goto llocx llocy up turn ldir + 90 pendn white.
obj SSIZE.
(0 < lthr ? (flame SSIZE)
0 > lthr ? (retro flame SSIZE)).
@ penup goto locx locy up turn direction + 90 pendn black.
%erase ? ()
obj SSIZE.
(0 < thrust ? (flame SSIZE)
0 > thrust ? (retro flame SSIZE))
"llocx _ locx. "llocy _ locy. "ldir _ direction ."lthr _ thrust))
to spaceship newtorp : pilot thrust steer trigger numtorps locx locy speed direction time ftime llocx llocy ldir lthr ( isnew ? (:pilot. "lthr _ :#thrust. :#steer :#trigger. "numtorps _ "speed _ 0. "direction _ "ldir _ 0 + rand * 360. "locx _ "llocx _ rand between 50 462. "locy _ "llocy _ rand between 50 462. "time _ "ftime _ clock) %release ? ( stick delete thrust. stick delete steer. stick delete trigger) %locx ? (!locx) %locy ? (!locy) %step ? ( 0 < clock - time + MOVELAG ? ( "time _ clock. (trigger ? (3 > numtorps ? ( "newtorp _ torpedo SELF speed locx locy direction. spacewar schedule newtorp)) moveship. crash~ SELF. display ship))) %is ? (ISIT eval) %bumptorps ? ("numtorps _ numtorps + 1) %debumptorps ? ("numtorps _ numtorps - 1) )
こちらが、停止している宇宙船(敵)対して魚雷を発射、一発外して二発目で当てたときの様子です。しつこいようですが^^; 航跡が残るように display
の残像を消す処理はコメントアウトしてあります。
( 「ask」「start」の実装 へ続く )