英語漬けDSのマラソンでの現在まで分かっている仕様について

みなさんこんにちは。ゲストCもちです。来たる9/26に英語が苦手な大人のDSトレーニング えいご漬け 浅漬け王が開催されることとなりました。(私自身は教育実習の兼ね合いがありますので残念ながら走者としての参加は難しそうです...)そこで本ゲームのマラソンTASを作った私としてこの英語漬けのマラソンにおける仕様についてかなり詳しくまとめたいと思います。ぜひこの記事を参考にして現状理論値と結論付けられている46.88秒を目指していただければなと思います。本人は逆アセンブラは初めてなので解釈とかやり方がめちゃくちゃです。

目次

1.上画面走者のスピードについて
1-1 4つの値の基本的なふるまい
1-2 SCがSMよりも小さい場合の振る舞い
1-3 SC≧SMの時の振る舞い
1-4 正解時のSMの振る舞いおよびGFの振る舞い
1-5 SASの決定、およびSMに対する振る舞い
1-6 まとめ及びこれらをC言語で表現すると

2.問題生成に関して

3.文字認識について

4.まとめ


1.上画面走者のスピードに関して
スピードに関しては、スピード現在値(格納場所:0x0218AF18 4bytes)とスピード最大値(格納場所:0x0218AF1C 4bytes)と最大値移動値(格納場所:0x0218AF20 4bytes)と猶予フレーム値(格納場所:0x0218AF24 4bytes)の計4つで管理されています。一部の値は1~2byte(s)で管理できる範囲のデータですが、バグを防ぐために4bytesで管理されていると思われます。なんかアクセスが早くなるらしい(他のTASさんの知識量バケモンでしょ...) このうちRTAで最低限必要な知識としては、
①最初は0xA0000を格納しており、この値がこのスピード最大値が取りうる最大値である。
②マラソン開始1文字目に関しては1f目からスピード最大値が減少を開始する
2文字目以降はxf目からスピード最大値が減少を開始する
④xは最大値を0x30(48)を取り、1fで1ずつ減る。正解すると0x10(16)上昇する。
⑤最大値と現在値の変動幅は違い、基本的には最大値のほうが変動幅は大きい
である。この先機械語を交えながらの解説となるので、興味がない読者は2.問題生成に関してまで読み飛ばしてもらっても構わない。(読んだ方が理解できる可能性があるが、上の知識だけで十分である)

1-1 4つの値の基本的なふるまい
長くなるのでここから先、スピード現在値=SC、スピード最大値=SM、最大値移動値=SAS、猶予フレーム値=GFとする。(英語ができないのでガバガバ宣言)マラソン開始時ではそれぞれは以下の値が格納されている。
SC=0x10000
SM=0xA0000
SAS=0x0
GF=0x0
各値の振る舞いとしては、①SCがSMよりも小さければ、SCを増やす ②GFが0の時、SMをSASの値分減らす という簡単な作りである。
これらをさらに実際の機械言語を交えながら解説していく。最後にC言語という高水準言語で簡単に表現したものを示しておくので興味があれば見てほしい。なお私自身は機械語は全く分かっておらず、雰囲気で読んでいるので雰囲気の解説となる。あんまストアとかよく分かってないので、機械語勉強できるオススメの本とかがあればぜひ教えていただきたい。絶対に買う。

1-2 SCがSMよりも小さい場合の振る舞い
まずはNDSDIS2で抜いたこの場合の機械語である↓(関係ないものは抜いている)
:0202CB50 69E069A1 unknown
:0202CB54 DA084281 ble 0223D560
:0202CB58 1808483F stmneda r8 ,{r0,r1,r2,r3,r4,r5,r11,r14}
:0202CB5C 69E161A0 unknown

と書いても僕自身よく分かんないので(これガチ)、NO$GBAさんに書いてあるのを書いていくと、
①0202CB50 69A1 ldr r1,[r4,18h] 
②0202CB52 69E0 ldr r0,[r4,1Ch]
③0202CB54 4281 cmp r1,r0
④0202CB56 DA08 bge Lxx_202CB6Ah  ;↓
⑤0202CB58 483F ldr r0,=Lxx_400h
⑥0202CB5A 1808 adds r0,r1,r0
⑦0202CB5C 61A0 str r0,[r4,18h] 

①r4+18のアドレスにある4byteをr1に代入する。省いたが、この時のr4には0x0218AF00が格納されているので、0x0218AF18すなわちSCの値をr1に入れましょうという意味と理解している
②同様にr0に0x0218AF1C、すなわちSMの値をr0に入れましょうという意味と理解している。
③r1とr0を比べ、その結果をフラグに示す。フラグとかよく分からんけどまあ比較してるね。今回はSCがSMよりも小さい場合をみるので、r1が小さいことを想定する。
④さっきの結果を使うのか、一緒になってるのかはよく分からないけど条件分岐。今回はr1がr0と等しいかそれ以上の場合は0202CB6Aに分岐するというような感じ。今回は小さいのでfalseで分岐せずに無視して次に行く。
⑤r0に0x400を代入する。
⑥r0にr1とr0を足した値を代入する。演算結果に応じて条件フラグのビットを更新する
⑦r4+18のアドレスにr0の値を代入する。
と言った具合。これが、SC<SMの時は0x400ずつ上昇するといった根拠である。
なおこの後もう一度比較を行い、SC<SMだった場合はSCを格納させ、SC>SMだった場合はSMの値を格納させる。

1-3 SC≧SMの時の振る舞い
例の如く機械語が、(上の分岐後部分で必要なやつ)
:0202CB68 483BE007 ldmmida r11!,{r0,r1,r2,r13,r14,r15}
:0202CB6C 61A01A08 movvs r1,r8,lsl #0x14 ;r1=0(0x0)

で、NO$GBAで書かれているのが、
①0202CB6A 483B ldr r0,=Lxx_400h
②0202CB6C 1A08 subs r0,r1,r0
③0202CB6E 61A0 str r0,[r4,18h]

①は1-2の⑤と同じ
②はr0にr1-r0の値を代入する。ただし、答えが負になる場合は0とし、フラグを0にする。
③は1-2の⑦と同じ
これが、SC≧SMの時は0x400ずつ減少する根拠である。

1-4 正解時のSMの振る舞いおよびGFの振る舞い
機械語さん↓
:0202CB28 69E1D012 unknown
:0202CB2C 18084848 stmneda r8 ,{r3,r6,r11,r14}
:0202CB30 69E161E0 unknown
:0202CB34 42814847 addmi r4,r1,#0x470000
:0202CB38 61E0DD00 mvnvs r13,r0,lsl #0x1a
:0202CB3C 30106A60 andccs r6,r0,r0,ror #0x14
:0202CB40 6A606260 bvs 038454C8
:0202CB44 DD012830 unknown
:0202CB48 62602030 rsbvs r2,r0,#0x30
:0202CB4C 62202000 eorvs r2,r0,#0x0

同じようにNO$GBAさんに書いてあるやつを書いていくと...
①0202CB2A 69E1 ldr r1,[r4,1Ch]
②0202CB2C 4848 ldr r0,=Lxx_8000h
③0202CB2E 1808 adds r0,r1,r0
④0202CB30 61E0 str r0,[r4,1Ch]
⑤0202CB32 69E1 ldr r1,[r4,1Ch]
⑥0202CB34 4847 ldr r0,=Lxx_0A0000h
⑦0202CB36 4281 cmp r1,r0
⑧0202CB38 DD00 ble Lxx_202CB3Ch
⑨0202CB3A 61E0 str r0,[r4,1Ch]
⑩0202CB3C 6A60 ldr r0,[r4,24h]
⑪0202CB3E 3010 adds r0,10h
⑫0202CB40 6260 str r0[r4,24h]
⑬0202CB42 6AA60 ldr r0,[r4,24h]
⑭0202CB44 2830 cmp r0,30h
⑮0202CB46 D001 ble Lxx_202CB4Ch
⑯0202CB48 2030 movs r0,30h
⑰0202CB4A 6260 str r0,[r4,24h]
⑱0202CB4C 2000 movs r0,0h
⑲0202CB4E 6220 str r0,[r4,20h]
以下1-2へ続く

①r1にr4+1Cのアドレスの4bytesの値を代入する。この辺はずっとr4は0x0218AF00が格納されっぱなしなので、r1に0x0218AF1CすなわちSMを代入する。
②r0に0x8000を代入する
③r0にr1とr0を足した値を代入する。演算結果に応じて条件フラグのビットを更新する
④r4+1Cのアドレスにr0の値を代入する。すなわちSM+0x8000の値を代入する。
⑤r1にr4+1Cのアドレスの4bytesの値を代入する。新しいSMの値を入れる。
⑥r0に0x0A0000を代入する。この値はMAX値
⑦r1とr0を比較し、その結果をフラグに示す。たぶん比較系は⑧みたいな条件分岐系と一緒に使うっぽい?(しょしんしゃ)
⑧r1がr0より小さいか等しい場合は0203CB3Cへ行く。すなわちA0000より大きい場合のみ⑨が実行され、それ以外は⑩へ行く
⑨r4+1Cのアドレスにr0の値を代入する。すなわち計算後のSMが0xA0000より大きい場合は0xA0000を代入するということである。
⑩r0にr4+24のアドレスの4bytesの値を代入する。ここでr0にGFを代入する。
⑪r0に0x10を足す。すなわち正解すると猶予フレームが0x10増える
⑫r4+24のアドレスにr0の値を代入する。
⑬r0にr4+24のアドレスの4bytesの値を代入する。ここでr0に計算後のGFを代入する。
⑭r0と0x30を比較する。
⑮⑧と同等。行き先は0202CB4C。すなわち30より大きい場合のみ⑯と⑰が実行される。
⑯r0に30を代入する。
⑰r4+24のアドレスにr0の値を代入する。すなわち計算後の猶予フレームが0x30より大きい場合は0x30が強制的に代入される
⑱r0に0を代入する。
⑲r4+20のアドレスにr0の値を代入する。すなわちSAS=0としSMが動かないようにしている
このような仕組みになっている。

1-5 SASの決定、およびSMに対する振る舞い
機械語さん↓
:0202CAE0 69E1D11E unknown
:0202CAE4 1A086A20 bne 0224736C
:0202CAE8 69E061E0 unknown
:0202CAEC DA012800 ble 02076AF4
:0202CAF0 61E02000 mvnvs r2,r0
:0202CAF4 485369E1 ldmmida r3 ,{r0,r5,r6,r7,r8,r11,r13,r14}^
:0202CAF8 DD084281 unknown
:0202CAFC 30406A20 subcc r6,r0,r0,lsr #0x14
:0202CB00 6A216220 bvs 02885388
:0202CB04 42814850 addmi r4,r1,#0x500000
:0202CB08 6220DD0C eorvs r13,r0,#0x300
:0202CB0C 6A20E00A bvs 02864B3C
:0202CB10 62203840 eorvs r3,r0,#0x400000
:0202CB14 484D6A21 stmmida r13 ,{r0,r5,r9,r11,r13,r14}^
:0202CB18 DA034281 ble 020FD524
:0202CB1C E0016220 and r6,r1,r0,lsr #0x4

んで本当に分からないので(ポンコツ)NO$GBAさんのやつを見ると、
①0202CAE2 69E1 ldr r1,[r4,1Ch]
②0202CAE4 6A20 ldr r0,[r4,20h]
③0202CAE6 1A08 subs r0,r1,r0
④0202CAE8 61E0 str r0,[r4,1Ch]
⑤0202CAEA 69E0 ldr r0,[r4,1Ch]
⑥0202CAEC 2800 cmp r0,0h
⑦0202CAEE DA01 bge Lxx_202CAF4h  ;↓
⑧0202CAF0 2000 movs r0,0h
⑨0202CAF2 61E0 str r0,[r4,1Ch]
⑩0202CAF4 69E1 ldr r1,[r4,1Ch]
⑪0202CAF6 4853 ldr r0,=Lxx_28000h
⑫0202CAF8 4281 cmp r1,r0
⑬0202CAFA D008 ble Lxx_202CB0Eh
⑭0202CAFC 6A20 ldr r0,[r4,20h]
⑮0202CAFE 3040 adds r0,40h
⑯0202CB00 6220 str r0,[r4,20h]
⑰0202CB02 6A21 ldr r1,[r4,20h]
⑱0202CB04 4850 ldr r0,Lxx_800h
⑲0202CB06 4281 cmp r1,r0
⑳0202CB08 D00C ble Lxx_202CB24h ;↓
㉑0202CB0A 6220 str r0,[r4,20h]
㉒0202CB0C E00A b Lxx_202CB24h ;↓
㉓0202CB0E 6A20 ldr r0,[r4,20h]
㉔0202CB10 3840 subs r0,40h
㉕0202CB12 6220 str r0,[r4,20h]
㉖0202CB14 6A21 ldr r1,[r4,20h]
㉗0202CB16 484D ldr r0,=Lxx_100h
㉘0202CB18 4281 cmp r1,r0
㉙0202CB1A DA03 bge Lxx_202CB24h
㉚0202CB1C 6220 str r0,[r4,20h]
㉛0202CB1E E001 b Lxx_202CB24h


①r1にr4+1Cのアドレスの4bytesの値を代入する。すなわちSMの値を代入する。
②r0にr4+20のアドレスの4bytesの値を代入する。すなわちSASの値を代入する。
③r0にr1とr0を引いた値を代入する。演算結果に応じて条件フラグのビットを更新する。
④r4+1Cのアドレスにr0の値を代入する。すなわちSMにSM-SASの値を代入する
⑤r0にr4+20のアドレスの4bytesの値を代入する。すなわち計算後のSMの値を代入する。
⑥r0と0を比較し、その結果をフラグに示す。
⑦r0と0が等しいか、それ以上の場合は0202CAF4に行く。すなわち⑩
⑧r0に0を代入する。
⑨r4+1Cのアドレスにr0の値を代入する。負の値になっていたら0に戻す処理なのかな。
⑩r1にr4+1Cのアドレスの4bytesの値を代入する。すなわちSMの値を代入する。
⑪r0に0x28000を代入する。
⑫r1とr0を比較し、その結果をフラグに示す。
⑬r1がr0より小さいか等しい場合は0203CB0Eへ行く。すなわちSMが0x28000より小さい場合、0203CB0Eへ処理が行く㉓。
⑭r0にr4+20のアドレスの4bytesの値を代入する。すなわちSASの値を代入する。
⑮r0に0x40を加える。
⑯r4+20のアドレスにr0の値を代入する。すなわちSMが0x28000より大きい場合はSAS=SAS+0x40ということになる。
⑰r1にr4+20のアドレスの4bytesの値を代入する。すなわち計算後のSASを代入する。
⑱r0に800を代入する。
⑲r1とr0を比較し、その結果をフラグに示す。
⑳r1がr0より小さいか等しい場合は020C2B24へ行く。
㉑r4+20のアドレスにr0の値を代入する。すなわちSASの最大値は0x800である。
㉒たぶん無条件分岐? 020C2B24へ行く。
㉓r0にr4+20のアドレスの4bytesの値を代入する。すなわちSASの値を代入する。
㉔r0-0x40を行う。
㉕r4+20のアドレスにr0の値を代入する。すなわちSMが0x28000より小さい場合はSAS=SAS-0x40ということになる。
㉖r1にr4+20のアドレスの4bytesの値を代入する。すなわち計算後のSASを代入する
㉗r0に0x100を代入する。
㉘r1とr0を比較し、その結果をフラグに示す。
㉙r1とr0が等しいか、それ以上の場合は0202CB24に行く。
㉚r4+20のアドレスにr0の値を代入する。すなわちそれよち小さい場合はSAS=0x100ということになる。
㉛たぶん無条件分岐?020C2B24へ行く。


1-6 まとめ及びこれらをC言語で表現すると
これらの情報を全てまとめてC言語で表すと以下の通りになる。ただし私の専門は情報ではないので、ここでもガバガバプログラムであるので注意して欲しい。

#include <stdio.h>
void main()
{
    int SC = 0x10000;
    int SM = 0xA0000;
    int SAS = 0x0;
    int GF = 0x0;
    int judge = 0/*とりあえずでおいた 1が正解した 0が不正解とする*/

    while(1){
        if(judge == 1){            /*正解したら*/
            SM = SM + 0x8000
            if(SM >= 0xA0000){
                SM=0xA0000;
            }  
            GF=GF+0x10;
            if(GF >= 0x30){
                GF=0x30;
            }
            SAS=0;              /*正解処理終わり*/
            judge=0;
        }
        if(SC<SM){              /*スピード処理*/
            SC=SC+0x400;
        }   else    {
            SC=SC-0x400;
        }
        SM=SM-SAS;              /*スピード最大値処理*/
        if(SM<0){
            SM=0;
        }
        if(SM<0x28000){         /*0x28000より小さい場合*/
            SAS=SAS-0x40;
            if(SAS<0x100){
                SAS=0x100;
            }
        }   else    {           /*0x28000以上の場合*/
            SAS=SAS+0x40;
            if(SAS>=0x800){
                SAS=0x800;
            }
        }
    }
}

たぶんこんな感じだと思います。

2.問題生成に関して
問題生成の詳細なアルゴリズムは現状分かってはいませんが、
①乱数値(格納場所:0x020A6B00)の値を参照し問題が決定されている。
②一度生成されると、マラソン中は中断をしてやめない限りどんなことをしても問題は変わらない
ということが分かっています。こちらに関しては現状調査中ですので分かれば別記事を書きます(機械語が関わるため追記は向いていない)

3.文字認識について
文字の認識は始点座標と終点座標および各辺のベクトルではないかと考えられます。座標は調べてませんが、このゲームは以下の表1のようなベクトル成分で反応させることができます。
表1 アルファベットとベクトル分解
文字反応できるベクトル順文字反応できるベクトル順文字反応できるベクトル順文字反応できるベクトル順
a↙↘↗↘h↓↗↓o↙↗↖v↘↗
b↓↗↓ip↓↑↘↙w↘↗
c↖↙↘↗j↓↙↖q↓↑↙↘x未調査 たぶん↘↙か↘↗↙
d↓↑←↓→k↓↑↘r↓↗y↘↗↙
e→↖↙↘ls←↘←z→↓→
f↖↓+→m↗↓↗↓t←↓  
g↙↗↓←n↑↘↑u↘↗  
である。ここでRTAにおいて最も重要なことは、
  • iとlは同じく↓だけで反応するため区別の必要がない上に点を書く必要がない。
  • bとhは同じく ↓↗↓だけで反応するため区別の必要はない
  • r,u,v,wは全て↘↗(rは↓成分が含まれれば反応する)ので、チェックを書くだけでよい(TAS語のチェックはこれ)
である。簡略化も可能なのでできるだけ文字を書く時間は少なくするべきである。(SASは1文字正解でも16fしか増えないためあまりのんびりはできない)



4.まとめ
以上が英語漬けで現状分かっている仕様である。その他分かり次第追記・追加の記事を書いていく。問い合わせは@gestoC_motiまで。





この記事へのコメント

2021年01月
               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