初めて書いたプログラム「ロマサガ２の集気法乱数調整アイテムドロップ補助ツール」をなぜかWindowsのバッチで書いてた件

タイトルでラノベバリに出オチしておりますが、
初めてまともに書いたプログラムが
「ロマサガ２の集気法乱数調整アイテムドロップ補助ツール」でして
しかもWindowsのバッチで書いておりました。

あのツールは、今は亡きWindowMEのハードディスクとともに失われてしまっていたので、
せっかくの機会なので2x年越しに、当時を振り返って書き起こしてみることにしました。

---

 # ロマサガ２の乱数調整について前提共有

 1990年代のコンシューマーゲームの乱数は、
 しばしば乱数テーブル方式で実装されておりました。

おおむね256個サイズの配列に0～255の数値が格納されており、
乱数Index変数が指し示すテーブル値を乱数として取得する

その後、乱数Indexは何らかの方法で更新します。

何らかの更新方法は、
* 乱数Indexを固定値でいくつか進める方式 （俗に、乱数を消費すると言う）
* ゲーム内のタイマー（フレームカウンタ）を参照
などがありました。

ロマサガ２は前者で実装されております。

乱数の位置を知ることができたり、乱数の更新方法を制御できれば、
攻撃を全部クリティカルにしたり、レアドロップを確定入手できたりと思いのままで、
このような手法は乱数調整と呼ばれています。


ロマサガ２では、乱数の更新方法が比較的かんたんで、「攻撃したら１Indexを進める」のような感じで、固定で進めることができます。

これを使って、乱数位置を調整して、
敵を倒したとき、ちょうどレアドロップを入手できる乱数位置におぜん立てして、アイテムを入手する・・・

ということを補助するのが本ツールの目的です。

*夏休み２日かけて全然ドロップしなかったレアアイテム：オートクレールに対して、これ以上コストかけてらんないと思って、乱数調整の補助ツールを作りました*

---

# ツールの出力
    
    rs2.bat 800 20 40 340
    最大HP = 800
    魔  力 = 20
    体術Lv = 40
    回復量 = 340
      0 R    1      2★    3      4      5      6      7      8      9     10     11     12     13     14     15
     16     17     18     19     20     21     22     23     24     25     26     27     28     29     30     31
     32     33     34     35     36     37     38     39     40★   41     42     43     44     45     46     47
     48     49     50     51     52     53     54     55     56     57     58     59     60     61     62     63
     64     65     66     67     68     69     70 R   71     72     73     74     75     76     77     78     79 D
     80     81     82     83     84     85     86     87     88     89     90     91     92     93     94     95
     96     97     98     99    100    101    102    103    104    105    106    107    108    109    110    111
    112    113    114    115    116    117    118    119    120    121    122    123    124    125★  126    127
    128    129    130    131    132    133    134    135    136    137    138 D  139    140    141    142    143
    144    145    146    147    148    149    150    151    152    153    154    155    156    157    158★  159
    160    161    162    163    164    165    166    167    168 R  169    170    171    172    173    174    175
    176    177    178    179    180    181    182    183    184    185    186    187    188    189    190★  191
    192    193★  194    195    196    197    198    199    200    201    202    203    204    205    206    207
    208    209    210    211    212    213    214    215    216    217 D  218    219    220    221    222    223
    224    225    226    227    228    229    230    231    232    233    234    235    236    237    238    239
    240    241    242    243    244    245    246    247    248    249    250    251    252    253    254 D


バッチに、
引数で最大HP, 魔力, 体術Lv, 集気法を使って回復した値
を指定することで、
乱数テーブルの現在地候補を「★」マークで表示します。
ついでにアイテムドロップ、レアドロップ位置を、それぞれ「D」「R」マークで表示しています。

---

# 実装

改めて、書いてみるとかなり癖の強いプログラムが出来上がりました。
（バッチで書くもんじゃないと少し後悔しました）

    @echo off
    setlocal enabledelayedexpansion
    
    set randomTable[0]=0
    set randomTable[1]=229
    set randomTable[2]=59
    set randomTable[3]=208
    　・・・（中略）・・・
    set randomTable[251]=209
    set randomTable[252]=51
    set randomTable[253]=246
    set randomTable[254]=96
    
    
    set hp=%1
    set mag=%2
    set lv=%3
    set heal=%4
    echo 最大HP = %hp%
    echo 魔  力 = %mag%
    echo 体術Lv = %lv%
    echo 回復量 = %heal%
    
    
    for /L %%i in (0, 1, 15) do (
        set lineMessage=
        for /L %%j in (0, 1, 15) do (
            set "mark=  "
            set /A table_index=%%i*16+%%j
            if !table_index! neq 255 (
                call set randVal=%%randomTable[!table_index!]%%
                set /A tmp=%lv% + %mag% + !randVal! %% %mag% + 1
                set /A shukihoValue=%hp% / 8 + 3 * !tmp!
    
                REM 乱数テーブルindex候補を★で記載
                REM 64で割り切れたらレアドロップ、32なら通常ドロップ
                set /A tmp=%heal%
                set /A div64=!randVal! %% 64
                set /A div32=!randVal! %% 32
                if !shukihoValue! equ !tmp! (
                    set mark=★
                ) else if !div64! equ 0 (
                    set mark= R
                ) else if !div32! equ 0 (
                    set mark= D
                )
    
                set /A table_index=%%i*16+%%j
                set "formatted=   !table_index!"
                set "formatted=!formatted:~-3!"
                set "lineMessage=!lineMessage!!formatted!!mark!  "
            )
        )
        echo !lineMessage!
    )
    
    endlocal
　　
一個ずつ、実装を見ていきます。  

    @echo off
コマンドがいちいち出力されるのをOFFにします。
  

    setlocal enabledelayedexpansion
遅延環境変数を有効にします。
これをやっとかないとfor分がえらく大変になります。
    
    set randomTable[0]=0
    set randomTable[1]=229
    set randomTable[2]=59
    set randomTable[3]=208
    　・・・（中略）・・・
    set randomTable[251]=209
    set randomTable[252]=51
    set randomTable[253]=246
    set randomTable[254]=96
バッチって配列ないらしいので、
愚直に１行ずつ定義しました・・・
  
    set hp=%1
    set mag=%2
    set lv=%3
    set heal=%4
    echo 最大HP = %hp%
    echo 魔  力 = %mag%
    echo 体術Lv = %lv%
    echo 回復量 = %heal%
入力引数のsetと表示をしています。
  
    
    for /L %%i in (0, 1, 15) do (
        set lineMessage=
        for /L %%j in (0, 1, 15) do (
            set "mark=  "
            set /A table_index=%%i*16+%%j

set 変数名=  ...で空の環境変数を定義します
set "mark=  "  ... 半角スペース＊２を設定したいのでダブルクオーテーションで囲ってます。
set /A  ... で数値演算できるようになります。

            if !table_index! neq 255 (
ロマサガ２の乱数テーブルは０～２５４なので、２５５の時に実行しないようif文でガードしています。

                call set randVal=%%randomTable[!table_index!]%%
配列のような書き方をしたとき、
callで呼ぶと変数が展開されることを利用して、randamTableの指定Indexの値=乱数値を参照しています。
                
                set /A tmp=%lv% + %mag% + !randVal! %% %mag% + 1
                set /A shukihoValue=%hp% / 8 + 3 * !tmp!

この辺は集気法の回復量の計算をしています。
    
                REM 乱数テーブルindex候補を★で記載
                REM 64で割り切れたらレアドロップ、32なら通常ドロップ
                set /A tmp=%heal%
                set /A div64=!randVal! %% 64
                set /A div32=!randVal! %% 32
                if !shukihoValue! equ !tmp! (
                    set mark=★
                ) else if !div64! equ 0 (
                    set mark= R
                ) else if !div32! equ 0 (
                    set mark= D
                )
if文の中で数値演算のやり方がわからんかったので、div64のような変数を定義しております。。。
    
                set /A table_index=%%i*16+%%j
                set "formatted=   !table_index!"
                set "formatted=!formatted:~-3!"
頭に最初空白３つ付けた後、「:~-3」という書き方をすると先頭から３文字引いてくれます。
ここでは数値の桁数が少なければ、先頭を空白で埋めるフォーマット整形処理をやっています。

                
                set "lineMessage=!lineMessage!!formatted!!mark!  "
            )
        )
        echo !lineMessage!
    )
lineMessageに行の出力テキストを積んで、for文の最後で１行分出力しています。

    
    endlocal
つかった環境変数をリセットします。

---

# 感想

このプログラムは、私のプログラムやっている理由の原点（効率よくゲームを攻略したい）であるので、
今回の機会に、記憶の中からサルページできてよかったです。