13
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

BASICのソースをElixirに移植してみよう! #1 序文がただのポエムになったわ

Last updated at Posted at 2023-12-17

1. OverView

Space, the final frontier...宇宙、それは人類に残された最後の開拓地

大昔、もう僕が子供の頃、宇宙戦艦ヤマトも放送されてない時に、アメリカでは宇宙大作戦(スタートレック)のゲームをコンピューター上で再現しようとしていた人がいたそうです。

ゲームの文書の翻訳はこちら
https://www.nakajim.net/trek/trek.html

この時代のコンピューターのスペックに合わせて、このゲームはかなりコンパクトなソースだったわけですが、
当時のマイコンのメモリは非常に少なく動かない機種も多かったそうです。
そこで、このゲームを小型のコンピュータでも、簡略化し、より小さいメモリで動かせる様にしたのが、Tiny Trekです。

後述の理由で、こいつをElixirに移植してみようと思い立った訳ですが、こんなもん読めるかい!と言うくらいに
メモリ節約のテクニックが詰まったソースです。詳しくはおいおい書きますが、こんな感じで何が何だかわからん。

45 F.I=71TO152:@(I)=0:N.I:@(8*X+Y+62)=4,M=A.(@(8*U+V-9)),N=M/100

これ、省略形で書かれておりますが、実際にはこういうソースです。

FOR I=71 TO 152
    @(I)=0
NEXT I
@(8*X+Y+62)=4
M=A.(@(8*U+V-9))
N=M/100

F.とかN.とIとか省略形で命令を書くとメモリを節約出来る、もうそこから節約しているソースなのです。
えー、これ読むの?awkで変換せんとダメか?と思ってたところに、このソースをPC-6001にわかりやすく移植されたナナオさんと言う方がいまして。

これならっ、これなら俺にも読める!移植してくださった!ナナオさんありがとうございます!と移植を開始した次第です。

本当はちゃんとして移植を完了させ、完全なものを出すべきかと思ったのですが、試行錯誤、様々な言語の壁を乗り越える様、どんどんリファクタリングをする過程を、記事にしていきたいと思います。

2. 動機と最初の意識転換について

さらにポエムが続きますが、僕と同じハマり方をしている人がいたら楽しいので、書いておきます。

言い訳は抜きにして、どうもElixirの勉強が捗らない、Elixirと言うのは非常にパワフルな言語で、見よう見まねでコピペしていれば、スマホの掲示板アプリが、1.5時間のハンズオンで、誰でも出来てしまいます。

しかし、どうも、ほかの言語の様にさくっと書けない…俺は阿呆か?と卑下したところで始まらないので、コードを読んでいるうちに、関数型言語やElixirの書き方の細部にこだわって、ロジック自体をないがしろにしたからではないかと思う様になりました。

と言うのも、僕の得意なプログラミング言語からの置き換えElixirの機能を語れば、あれがない、これがない、と不便に見えるのですが、別に処理をする上で、ループも分岐(if文)もすべてある。

要するにプログラム全体の構造を考え直し、やりたいことに立ち戻らないとダメだろう。

例えば、今回のEasy Trekは全部で64個のクォドラントそれぞれに、100面ダイス(乱数)を振って5以下の場合、宇宙基地を配置します。

BASICのソースだと、以下の様な処理になります。

    FOR I= 0 TO 63
        # 1d99を振る。5以下でベースが存在する。
        J=-(FNR(99)<5)
        B=B+J       # Baseの数を増やす、追加されるのは、FNR(99)<5の結果である事に注意
        A(I)=-10*J
    NEXT I

これ、変数A(0) - A(63)に、宇宙基地(Star base)をランダムで配置しているのですが、私は以下の様にしました。

# あまり綺麗な書き方ではないのですが…
starbase_list = Enum.reduce(0..63, [], fn _x, acc ->
        if :rand.uniform(100) <= 5 do
            starbase = [Set_objects.set_item(acc)]
            [starbase | acc]
        else
            acc
        end
    end)

データ構造が違うので、単純な比較は出来ませんが、BASICのLoopと同等の文法を求めるのではなく、元の目的を考えてロジックを組んでコードを書けばよいのではないのだろうか?そう言う蓄積がないから、コードが書けないのではないだろうか?と考えた次第です。

上のElixirのコードも読んでしまえば

  1. Enum.reduceで、0から63までループする
  2. :rand.uniform(100) の結果が5以下なら、starbaseと言うリストにオブジェクトを追加していく。

それほど、大きな差異があるわけでもない。要は「何をするか」を決めてなく、漠然と既存の自分の知っている言語の面影を追っていただけだったのではないかと反省しました。

そこで、何か手ごたえのあるソースを読もうとおもっていたのですが、ちょうど古いゲームのコードを別件で探していた際に、Tiny Trekのコードが、確かに記法やメモリ節約のため、読みにくい部分があるももの、ロジック自体は非常によく練られていて簡潔であり、勉強鵜や移植に向いているんじゃないかと気づいたわけです。

そう思って、気楽に始めました。
さて、ここまでデカい口を叩いた成果が出るかは…やれんのか?やれません。
いやもう、このソース、すっごくよく出来てるんですよ。TinyBasicの記事を書きたくなるくらいにw(本末転倒)

13
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?