Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save na2co3-ftw/722ee0a57b32c365a8ee24bf9e5dcc1d to your computer and use it in GitHub Desktop.
Save na2co3-ftw/722ee0a57b32c365a8ee24bf9e5dcc1d to your computer and use it in GitHub Desktop.

AtCoder に登録したら解くべき精選過去問 10 問を 2003lk で解いてみた

AtCoder Beginners Selection が流行っているようなのでやってみました。

2003lk とは

2003'd ferlesyl'd lkurftless の略。

創作界隈『悠里』で創作されている架空世界上の、架空のアーキテクチャ 2003'd ferlesyl (略: 2003f) の アセンブリ言語です。

仕様は設定一覧(暫定)にまとめられています。

追加設定

2003fでは、文字列、文字コード、標準入出力についての設定がまだないので、勝手に仮定していきます。

2003fでデータをバイト単位で扱う方法が整備されていないので、文字列は、1文字32ビットでメモリ上に並べて表現することにします。文字列の終端は 0 を置くことにします。

文字コードは、ASCIIをそのまま使いましょう。

入出力は、関数の引数に渡します。 fasal という関数をエントリーポイントとして、

void fasal(unsigned int *in, unsigned int *out);

という感じにします。

そのままではAtCoderで動かないので、JavaScriptにトランスパイルして動かしました。 トランスパイラを添付しておきますが、とても雑実装なので、本来の2003lkで動くコードでも動かなくなったりします。

文字列、数値変換

数値を文字列として受け取ったり、数値を文字列として出力したりするので、数値と文字列を相互変換する関数がいりますね。

文字列→数値

'c'i
nll farces-pakda
krz f2 f5+4@
krz f0 f2@
krz f3 0
krz f1 f0@ l' farces-pakda-rinyv
fi f1 48 xylonys malkrz xx farces-pakda-situv
fi f1 58 xolonys malkrz xx farces-pakda-situv
nta f1 48
lat f3 f3 10
ata f3 f1
ata f0 4
krz xx farces-pakda-rinyv
krz f2@ f0 l' farces-pakda-situv
krz f0 f3
krz xx f5@
unsigned int parces-pakda(unsigned int **str);

という感じです。

文字列の先頭から、数字が並んでる分だけ読み進めて、その数値を返します。 同時に、文字列の先頭を表すポインタをずらして、数値の終了直後にまで移動させます。

数値→文字列

'c'i
nll krante-pakda
krz f0 f5+8@
krz f3 f5+4@
krz f1 f3@
krz f2 0 l' krante-pakda-rinyv
fi f0 10 xylonys l' krante-pakda-kak-rinyv malkrz xx krante-pakda-kak-situv
nta f0 10
ata f2 1
krz xx krante-pakda-kak-rinyv
ata f0 48 l' krante-pakda-kak-situv
krz f1@ f0
ata f1 4
fi f2 0 clo malkrz xx krante-pakda-situv
krz f0 f2
krz xx krante-pakda-rinyv

inj f0 f3@ f1 l' krante-pakda-situv
nta f1 4 l' krante-pakda-nacis-rinyv
fi f0 f1 xolonys malkrz xx krante-pakda-nacis-situv
inj f0@ f1@ f0@
ata f0 4
krz xx krante-pakda-nacis-rinyv
krz xx f5@ l' krante-pakda-nacis-situv
void krante-pakda(unsigned int value, unsigned int **str)

という感じです。

数値を文字列のところに書き込み、また文字列のポインタを書き込んだ数値の終了直後にまで移動させます。

2003fでは割算の仕様が未定なので、引き算を繰り返します。
小さい位から順に書き込み、最後に前後を反転させています。

以降のコードでは、この2つの関数は省略して書くことにします。

'c'i
nll fasal
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 krz f5@ f0

ata f5+12@ 4

nta f5 4 krz f5@ f5 ata f5@ 16
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 krz f5@ f0

ata f5+16@ 4

nta f5 4 krz f5@ f5 ata f5@ 20
nta f5 4 inj f5@ xx farces-pakda
ata f5 8

ata f5+16@ 4
ata f0 f5@ ata f5 4
ata f0 f5@ ata f5 4

nta f5 4 krz f5@ f0
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx krante-pakda
ata f5 12

krz f1 f5+4@
krz f1@ 32
ata f1 4

krz f0 f5+8@
krz f2 0
krz f1+f2@ f0+f2@ l' lyjotaleiju-rinyv
fi f0+f2@ 10 clo malkrz xx lyjotaleiju-situv
ata f2 4
krz xx lyjotaleiju-rinyv
ata f2 4 l' lyjotaleiju-situv
krz f1+f2@ 0
krz xx f5@

nll farces-pakda
;略

nll krante-pakda
;略

3つの数値をスタックに積んでから足していますが、スタック上で直接足し算してしまってもいいことに後で気付きました。

'c'i
nll fasal
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 krz f5@ f0

ata f5+12@ 4

nta f5 4 krz f5@ f5 ata f5@ 16
nta f5 4 inj f5@ xx farces-pakda
ata f5 8

lat f0 f0 f5@
ata f5 4
krz f1 f5+4@
ada f0 1
fi f0 0 clo malkrz xx even

krz f1@ 79
krz f1+4@ 100
krz f1+8@ 100
ata f1 12
krz xx situv

krz f1@ 69 l' even
krz f1+4@ 118
krz f1+8@ 101
krz f1+12@ 110
ata f1 16

krz f1@ 10 l' situv
krz f1+4@ 0
krz xx f5@

nll farces-pakda
;略

偶数かどうかは n & 1 == 0 で判定できますね。

even とかいう英語のラベルがありますが、リパライン語で「偶数」の言い方が分かりませんでした。(造語依頼投げました)

'c'i
nll fasal
krz f0 0
krz f1 f5+8@
ata f0 f1@
ata f0 f1+4@
ata f0 f1+8@
nta f0 96
krz f1 f5+4@
krz f1@ f0
krz f1+4@ 10
krz f1+8@ 0
krz xx f5@

0か1しかないので、つまり3文字分の合計をすれば、1の個数になりますね。
0の文字コードが48なので、一桁の数値なら48を足したり引いたりすれば文字と数字を変換できます。
3文字分の文字コードを足して、96を引けば、合計の数字の文字コードになりますね。

'c'i
nll fasal
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx farces-pakda
ata f5 8

krz f1 f5
ata f1 8
ata f1@ 4
dro f0 2
nta f5 f0
nta f5 4 krz f5@ f1
nta f5 4 krz f5@ f0
nta f5 4 krz f5@ 0
fi f5@ f5+4@ xolonys l' icve-rinyv malkrz xx icve-situv

nta f5 4 krz f5@ f5+12@
nta f5 4 inj f5@ xx farces-pakda
ata f5 8

krz f1 f5@
ata f1 12
krz f5+f1@ f0
ata f5@ 4
krz f1 f5+8@
ata f1@ 4
krz xx icve-rinyv

krz f3 0 l' icve-situv
krz f0 f5 l' rinyv
ata f0 12
krz f1 0
fi f1 f5+4@ xolonys l' ostor-rinyv malkrz xx ostor-situv
krz f2 1
ada f2 f0+f1@
fi f2 0 niv malkrz xx situv
dto f0+f1@ 1
ata f1 4
krz xx ostor-rinyv
ata f3 1 l' ostor-situv
krz xx rinyv
ata f5 f5+4@ l' situv
ata f5 12

nta f5 4 krz f5@ f3
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx krante-pakda
ata f5 12

krz f0 f5+4@
krz f0@ 10
krz f0+4@ 0
krz xx f5@

nll farces-pakda
;略

nll krante-pakda
;略

入力の数が可変なのがしんどかったです。

分かりにくいので、メイン部分(rinyv から situv あたり)でのスタックとレジスタの表を載せておきます。

場所 内容
f5+(12+4(N-1))@ 入力されたデータ(AN)
: :
f5+12@ 入力されたデータ(A1)
f5+8@ データ入力時に使用していた。入力文字列のポインタ (unsigned int**)
f5+4@ データの数(N) × 4
f5@ データ入力時にループカウンタとして使用していた (4ずつ進める)
f0 f5+12
f1 ループカウンタ (4ずつ進める)
f2 計算作業用
f3 操作の回数
'c'i
nll fasal
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 lat f0 f0 500 krz f5@ f0

ata f5+12@ 4

nta f5 4 krz f5@ f5 ata f5@ 16
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 lat f0 f0 100 krz f5@ f0

ata f5+16@ 4

nta f5 4 krz f5@ f5 ata f5@ 20
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 lat f0 f0 50 krz f5@ f0

ata f5+20@ 4

nta f5 4 krz f5@ f5 ata f5@ 24
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 krz f5@ f0

nta f5 4 krz f5@ 0
krz f0 0
fi f0 f5+16@ llonys l' rinyv-500 malkrz xx situv-500
krz f1 0
fi f1 f5+12@ llonys l' rinyv-100 malkrz xx situv-100
krz f2 0
fi f2 f5+8@ llonys l' rinyv-50 malkrz xx situv-50
krz f3 f0
ata f3 f1
ata f3 f2
fi f3 f5+4@ niv malkrz xx xeu
ata f5@ 1
ata f2 50 l' xeu
krz xx rinyv-50
ata f1 100 l' situv-50
krz xx rinyv-100
ata f0 500 l' situv-100
krz xx rinyv-500

krz f0 f5@ l' situv-500
ata f5 20

nta f5 4 krz f5@ f0
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx krante-pakda
ata f5 12

krz f0 f5+4@
krz f0@ 10
krz f0+4@ 0
krz xx f5@

nll farces-pakda
;略

nll krante-pakda
;略

3重ループです。

メイン部分(rinyv-500 から situv-500 あたり)でのスタックフレーム等

場所 内容
f5+16@ 500円玉の最大枚数(A) × 500
f5+12@ 100円玉の最大枚数(B) × 100
f5+8@ 50円玉の最大枚数(C) × 50
f5+4@ 合計金額(X)
f5@ 見つかった組み合わせの数
f0 500円玉ループカウンタ (500ずつ進める)
f1 100円玉ループカウンタ (100ずつ進める)
f2 50円玉ループカウンタ (50ずつ進める)
f3 計算作業用
'c'i
nll fasal
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 krz f5@ f0

ata f5+12@ 4

nta f5 4 krz f5@ f5 ata f5@ 16
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 krz f5@ f0

ata f5+16@ 4

nta f5 4 krz f5@ f5 ata f5@ 20
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 krz f5@ f0

nta f5 4 krz f5@ 0
nta f5 24
krz f5@ 0
krz f5+20@ 0
fi f5@ 10 xolonys l' rinyv-1000 malkrz xx situv-1000
krz f0 0
krz f5+16@ 0
fi f0 10 xolonys l' rinyv-100 malkrz xx situv-100
krz f1 0
krz f5+12@ 0
fi f1 10 xolonys l' rinyv-10 malkrz xx situv-10
krz f2 0
krz f5+8@ 0
fi f2 10 xolonys l' rinyv-1 malkrz xx situv-1
krz f3 f5+20@
ata f3 f5+16@
ata f3 f5+12@
ata f3 f5+8@
fi f3 f5+36@ llonys malkrz xx situv-1000
krz f5+4@ f3
krz f3 f5@
ata f3 f0
ata f3 f1
ata f3 f2
fi f3 f5+32@ xylonys malkrz xx xeu
fi f3 f5+28@ llonys malkrz xx xeu
ata f5+24@ f5+4@
ata f2 1 l' xeu
ata f5+8@ 1
krz xx rinyv-1
ata f1 1 l' situv-1
ata f5+12@ 10
krz xx rinyv-10
ata f0 1 l' situv-10
ata f5+16@ 100
krz xx rinyv-100
ata f5@ 1 l' situv-100
ata f5+20@ 1000
krz xx rinyv-1000
krz f0 f5+24@ l' situv-1000
fi f5+36@ 10000 niv malkrz xx situv
fi f5+32@ 1 niv malkrz xx situv
ata f0 10000
ata f5 40 l' situv

nta f5 4 krz f5@ f0
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx krante-pakda
ata f5 12

krz f0 f5+4@
krz f0@ 10
krz f0+4@ 0
krz xx f5@

nll farces-pakda
;略

nll krante-pakda
;略

最大10000なので、0から9までのループを各桁ごとにやる4重ループのあと、個別に10000だけ処理しています。

メイン部分(rinyv-1000 から situv あたり)でのスタックフレーム等

場所 内容
f5+36@ 探索する数値の最大(N)
f5+32@ 各桁合計の上限(A)
f5+28@ 各桁合計の下限(B)
f5+24@ 数値の合計 (最終的な計算結果)
f5+20@ 1000の位の数値 (0, 1000, 2000…)
f5+16@ 100の位の数値 (0, 100, 200…)
f5+12@ 10の位の数値 (0, 10, 20…)
f5+8@ 1の位の数値 (0, 1, 2…)
f5+4@ 数値 (f5+8@〜f5+20@の合計)
f5@ 1000の位のループカウンタ (0, 1, 2…)
f0 100の位のループカウンタ (0, 1, 2…)
f1 10の位のループカウンタ (0, 1, 2…)
f2 1の位のループカウンタ (0, 1, 2…)
f3 計算作業用

あ、これf2とf5+8@の内容同じですね。今気づきました。

'c'i
nll fasal
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx farces-pakda
ata f5 8

krz f1 f5
ata f1 8
ata f1@ 4
dro f0 2
nta f5 f0
nta f5 4 krz f5@ f1
nta f5 4 krz f5@ f0
nta f5 4 krz f5@ 0
fi f5@ f5+4@ xolonys l' icve-rinyv malkrz xx icve-situv

nta f5 4 krz f5@ f5+12@
nta f5 4 inj f5@ xx farces-pakda
ata f5 8

krz f1 f5@
ata f1 12
krz f5+f1@ f0
ata f5@ 4
krz f1 f5+8@
ata f1@ 4
krz xx icve-rinyv

nta f5 4 l' icve-situv krz f5@ f5 ata f5@ 16
nta f5 4 krz f5@ 0
nta f5 4 krz f5@ f5+16@ dto f5@ 2 nta f5@ 1
nta f5 4 inj f5@ xx ycax
ata f5 16

krz f0 f5
ata f0 12
krz f1 f5+4@
krz f2 0
krz f3 0
nta f1 4 l' rinyv
fi f3 0 niv malkrz xx bob
ata f2 f0+f1@
krz xx xeu
nta f2 f0+f1@ l' bob
fi f1 0 clo l' xeu malkrz xx situv
nac f3
krz xx rinyv
ata f5 f5+4@ l' situv
ata f5 12

nta f5 4 krz f5@ f2
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx krante-pakda
ata f5 12

krz f0 f5+4@
krz f0@ 10
krz f0+4@ 0
krz xx f5@

nll farces-pakda
;略

nll krante-pakda
;略

nll ycax
krz f2 f5+4@
krz f1 f5+8@
krz f0 f5+12@
fi f1 f2 xolo   malkrz xx lus
krz f3 f1
ata f3 1
fi f3 f5+4@ llo   l' panqa   malkrz xx fistir
krz f2 f5+8@
dro f2 2   krz f2 f0+f2@
dro f3 2   krz f5+4294967292@ f0+f3@   dto f3 2
fi f2 f5+4294967292@ xtlo   malkrz xx iska
ata f1 1
dro f1 2   dro f3 2   inj f0+f3@ f0+f1@ f0+f3@   dto f1 2   dto f3 2
ata f3 1  l' iska
krz xx panqa

krz f2 f5+8@  l' fistir
dro f1 2   dro f2 2  inj f0+f1@ f0+f2@ f0+f1@   dto f1 2  dto f2 2
nta f5 4   krz f5@ f1
nta f5 4   krz f5@ f5+20@
nta f5 4   krz f5@ f5+20@
nta f5 4   krz f1 f5+12@   nta f1 1   krz f5@ f1
nta f5 4   inj f5@ xx ycax  ata f5 8
           krz f1 f5+ 8@   ata f1 1   krz f5@ f1
nta f5 4   krz f5@ f5+20@
nta f5 4   inj f5@ xx ycax  ata f5 20
krz xx f5@   l' lus

クイックソートが既に書かれていたので利用しました。
データ入力部分は3問目からコピペです。

メイン部分(rinyv から situv あたり)でのスタックフレーム等

場所 内容
f5+(12+4(N-1))@ 入力されたデータ(aN)
: :
f5+12@ 入力されたデータ(a1)
f5+8@ データ入力時に使用していた。入力文字列のポインタ (unsigned int**)
f5+4@ データの数(N) × 4
f5@ データ入力時にループカウンタとして使用していた (4ずつ進める)
f0 f5+12
f1 ループカウンタ (4ずつ減らす)
f2 点数 (Aliceが取ったときは加算、Bobが取ったときは減算)
f3 Aliceの番の時0、Bobの番のとき0xffffffff

点数の差を、加算と減算で考えるのはAtCoder に登録したら解くべき精選過去問 10 問を Haskell で解いてみた経由でcojnaさんのものを参考にさせていただきました。

'c'i
nll fasal
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx farces-pakda
ata f5 8

krz f1 f5
ata f1 8
ata f1@ 4
dro f0 2
nta f5 f0
nta f5 4 krz f5@ f1
nta f5 4 krz f5@ f0
nta f5 4 krz f5@ 0
fi f5@ f5+4@ xolonys l' icve-rinyv malkrz xx icve-situv

nta f5 4 krz f5@ f5+12@
nta f5 4 inj f5@ xx farces-pakda
ata f5 8

krz f1 f5@
ata f1 12
krz f5+f1@ f0
ata f5@ 4
krz f1 f5+8@
ata f1@ 4
krz xx icve-rinyv

krz f0 0 l' icve-situv
nta f5 4 l' farnenes-rinyv
krz f5@ 0
ata f0 1
fi f0 100 xylonys malkrz xx farnenes-rinyv

krz f0 f5
ata f0 412
krz f1 0
fi f1 f5+404@ xolonys l' rinyv malkrz xx situv
krz f2 f0+f1@
nta f2 1
dro f2 2
krz f5+f2@ 1
ata f1 4
krz xx rinyv

krz f1 0 l' situv
krz f0 0
fi f5+f0@ 0 clo l' kinfit-rinyv malkrz xx kinfit-xeu
ata f1 1
ata f0 4 l' kinfit-xeu
fi f0 400 xylonys malkrz xx kinfit-rinyv
ata f5 f5+404@
ata f5 412

nta f5 4 krz f5@ f1
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx krante-pakda
ata f5 12

krz f0 f5+4@
krz f0@ 10
krz f0+4@ 0
krz xx f5@

nll farces-pakda
;略

nll krante-pakda
;略

データ入力部分は3問目からコピペです。
スタックに要素100のint32配列を確保してバケット法します。

繰り返し回数が確定で1回以上なら、ラベル定義1つでループできることに気付きましたが、既にあるコードを書き換えるのも面倒なので混在しています。

バケットに入れる時(rinyv から situv あたり)でのスタックフレーム等

場所 内容
f5+(412+4(N-1))@ 入力されたデータ(dN)
: :
f5+412@ 入力されたデータ(d1)
f5+408@ データ入力時に使用していた。入力文字列のポインタ (unsigned int**)
f5+404@ データの数(N) × 4
f5+400@ データ入力時にループカウンタとして使用していた (4ずつ進める)
f5+396@ 直系100cmの餅があったら1、なければ0
: :
f5+0@ 直系1cmの餅があったら1、なければ0
f0 f5+412
f1 ループカウンタ (4ずつ進める)
f2 計算作業用
'c'i
nll fasal
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 krz f5@ f0

ata f5+12@ 4

nta f5 4 krz f5@ f5 ata f5@ 16
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 krz f5@ f0

nta f5 8
krz f0 0
krz f5+4@ 0
krz f1 0 l' rinyv-10000
krz f5@ 0
krz f2 f5+12@ l' rinyv-5000
nta f2 f0
nta f2 f1
krz f3 f2
lat f3 f3 1000
ata f3 f5@
ata f3 f5+4@
fi f3 f5+8@ clo malkrz xx jel
ata f1 1
ata f5@ 5000
krz f3 f5+12@
nta f3 f0
fi f1 9 xolonys malkrz xx situv-5000
fi f1 f3 xtlonys malkrz xx rinyv-5000
ata f0 1 l' situv-5000
ata f5+4@ 10000
fi f5+4@ f5+8@ llonys malkrz xx situv-10000
fi f0 f5+12@ xtlonys malkrz xx rinyv-10000

ata f5 16 l' situv-10000
krz f0 f5+4@
krz f0@ 45
krz f0+4@ 49
krz f0+8@ 32
krz f0+12@ 45
krz f0+16@ 49
krz f0+20@ 32
krz f0+24@ 45
krz f0+28@ 49
krz f0+32@ 10
krz f0+36@ 0
krz xx f5@

ata f5 8 l' jel
krz f5+4@ f1
krz f5@ f2

nta f5 4 krz f5@ f0
nta f5 4 krz f5@ f5 ata f5@ 20
nta f5 4 inj f5@ xx krante-pakda
ata f5 12

krz f0 f5+12@
krz f0@ 32
ata f5+12@ 4

nta f5 4 krz f5@ f5+8@
nta f5 4 krz f5@ f5 ata f5@ 20
nta f5 4 inj f5@ xx krante-pakda
ata f5 12

krz f0 f5+12@
krz f0@ 32
ata f5+12@ 4

nta f5 4 krz f5@ f5+4@
nta f5 4 krz f5@ f5 ata f5@ 20
nta f5 4 inj f5@ xx krante-pakda
ata f5 12

ata f5 8
krz f0 f5+4@
krz f0@ 10
krz f0+4@ 0
krz xx f5@

nll farces-pakda
;略

nll krante-pakda
;略

10000円札の枚数と5000円札の枚数の2重ループにして、1000円札の枚数は引き算で求めるようにしてもTLEしました。Cのようなコンパイラ言語にトランスパイルしていれば通っていたかもしれませんね。

ということで、5000円札を9枚以上使うパターンは探索しないようにしました。5000円札を9枚以上使う場合、10000円札を1枚増やし、5000円札を9枚減らし、1000円札を5枚増やしても合計金額は同じだからです。これでO(N2)からO(N)になると思います。

メイン部分(rinyv-10000 から situv-10000 あたり)でのスタックフレーム等

場所 内容
f5+12@ 合計枚数(N)
f5+8@ 合計金額(Y)
f5+4@ 10000円札の金額 (0, 10000, 20000…)
f5@ 5000円札の金額 (0, 5000, 10000…)
f0 10000円札のループカウンタ (0, 1, 2…)
f1 5000円札のループカウンタ (0, 1, 2…)
f2 1000円札の枚数
f3 計算作業用
'c'i
nll fasal
krz f0 f5+8@
krz f1 f5
nta f5 4
krz f5@ 0
nta f5 4 l' krz-lyjotaleiju
krz f5@ f0@
ata f0 4
fi f0@ 10 niv malkrz xx krz-lyjotaleiju

fi f5@ 0 clo l' rinyv malkrz xx yes
fi f5@ 109 clo malkrz xx drea
fi f5@ 101 clo malkrz xx eras
fi f5@ 114 niv malkrz xx no
ata f5 4
fi f5@ 101 niv malkrz xx no
ata f5 4
fi f5@ 109 clo malkrz xx drea
fi f5@ 115 clo malkrz xx era
krz xx no

ata f5 4 l' drea
fi f5@ 97 niv malkrz xx no
ata f5 4
fi f5@ 101 niv malkrz xx no
ata f5 4
fi f5@ 114 niv malkrz xx no
ata f5 4
fi f5@ 100 niv malkrz xx no
ata f5 4
krz xx rinyv

ata f5 4 l' eras
fi f5@ 115 niv malkrz xx no
ata f5 4 l' era
fi f5@ 97 niv malkrz xx no
ata f5 4
fi f5@ 114 niv malkrz xx no
ata f5 4
fi f5@ 101 niv malkrz xx no
ata f5 4
krz xx rinyv

krz f5 f1 l' no
krz f0 f5+4@
krz f0@ 78
krz f0+4@ 79
krz f0+8@ 10
krz f0+12@ 0
krz xx f5@

krz f5 f1 l' yes
krz f0 f5+4@
krz f0@ 89
krz f0+4@ 69
krz f0+8@ 83
krz f0+12@ 10
krz f0+16@ 0
krz xx f5@

後ろから検証していきます。後ろから見ていったときに終端(元の文字列だと先頭の位置)に0が欲しかったので、一旦スタックにコピーしています。
スタックからポップしながら検証していってます。スタックの使い方が雑ですね。

'c'i
nll fasal
nta f5 4 krz f5@ f5 ata f5@ 12
nta f5 4 inj f5@ xx farces-pakda
ata f5 4 krz f5@ f0

ata f5+12@ 4
nta f5 4 krz f5@ 0
nta f5 4 krz f5@ 0
nta f5 4 krz f5@ 0
nta f5 4 krz f5@ 0
nta f5 8

nta f5 4 l' rinyv krz f5@ f5 ata f5@ 40
nta f5 4 inj f5@ xx farces-pakda
ata f5 8 krz f5+4@ f0

ata f5+36@ 4

nta f5 4 krz f5@ f5 ata f5@ 40
nta f5 4 inj f5@ xx farces-pakda
ata f5 8 krz f5@ f0

ata f5+36@ 4

nta f5 4 krz f5@ f5 ata f5@ 40
nta f5 4 inj f5@ xx farces-pakda
ata f5 8

fi f5+12@ f5@ llonys malkrz xx snakxaz-x
krz f1 f5@
nta f1 f5+12@
krz xx y
krz f1 f5+12@ l' snakxaz-x
nta f1 f5@

fi f5+8@ f0 llonys l' y malkrz xx snakxaz-y
ata f1 f0
nta f1 f5+8@
krz xx t
ata f1 f5+8@ l' snakxaz-y
nta f1 f0

krz f2 f5+4@ l' t
nta f2 f5+16@
fi f1 f2 llonys malkrz xx no
ada f1 1
ada f2 1
fi f1 f2 niv malkrz xx no

krz f5+16@ f5+4@
krz f5+12@ f5@
krz f5+8@ f0
ata f5+36@ 4
ata f5+20@ 1
fi f5+20@ f5+24@ xylonys malkrz xx rinyv

ata f5 28
krz f0 f5+4@
krz f0@ 89
krz f0+4@ 101
krz f0+8@ 115
krz f0+12@ 10
krz f0+16@ 0
krz xx f5@

ata f5 28 l' no
krz f0 f5+4@
krz f0@ 78
krz f0+4@ 111
krz f0+8@ 10
krz f0+12@ 0
krz xx f5@

nll farces-pakda
;略

TLEしました。クソ雑魚JSトランスパイラには無理なようです。

入力を読みとりながら順次処理しています。

メイン部分(rinyv から malkrz xx rinyv あたり)でのスタックフレーム等

場所 内容
f5+36@ 入力文字列のポインタ
f5+32@ 出力文字列のポインタ
f5+28@ リターンアドレス
f5+24@ データの数(N)
f5+20@ ループカウンタ(i-1)
f5+16@ 前回の時刻(ti-1)
f5+12@ 前回のx位置(xi-1)
f5+8@ 前回のy位置(yi-1)
f5+4@ 時刻(ti)
f5@ x位置(xi)
f0 y位置(yi)
f1 前回との距離(|xi-xi-1|+|yi-yi-1|)
f2 前回からの時間(ti-ti-1)

おわりに

普通のアセンブリ言語でやってる人さえ居なさそうなのに、まさかの架空アセンブリ言語でした。コードとても読みにくいと思います。

あと、こういうことする時はコンパイラ言語を使いましょうね。

// usage: node transpile.js [filename]
const fs = require("fs");
let injCount = 0;
const source = fs.readFileSync(process.argv[2], "utf8")
.replace(/;.*\r?\n?/g, "")
.replace(/-/g, "_")
.replace(/\s*\+\s*/g, ",")
.replace(/(\S+)@/g, "[$1]")
.replace(/(?:\bfen|'c'i)\b/g, "")
.replace(/\bnac\s+(\S+)/g, "nac($1);")
.replace(/\b(krz|malkrz|ata|nta|ada|ekc|dal|dto|dro|dtosna)\s+(\S+)\s+(\S+)/g, "$1($2, $3);")
.replace(/\b(inj|lat|latsna)\s+(\S+)\s+(\S+)\s+(\S+)/g, "$1($2, $3, $4);")
.replace(/\bfi\s+(\S+)\s+(\S+)\s+(\S+)/g, "$3($1, $2);")
.replace(/\bnll\s+(\S+)/g, "case $1:")
.replace(/([^;\s][^;]*;)\s+l'\s+(\S+)/g, "case $2: $1")
.replace(/\bkrz\(xx, (\S+)\);/g, "krzXX($1); break;")
.replace(/\bmalkrz\(xx, (\S+)\);/g, "if (cpu.flag) { krzXX($1); break; }")
.replace(/\binj\((\S+), xx, (\S+)\);/g, (_, a, c) => `injXX(${a}, i${++injCount}, ${c}); break; case i${injCount}:` );
const labels = [];
const exp = /case (\S+):/g;
let match;
while ((match = exp.exec(source)) !== null) {
labels.push(match[1]);
}
const js = `// transpiled from 2003lk
"use strict";
const stdinAddress = 0x00000000;
const stdoutAddress = 0x00010000;
const initialXX = 0x14830000|0;
const initialF5 = 0x6d7aa0f8|0;
const outermostRetAddress = 0xbda574b8|0;
const cpu = {
f0: 0x82ebfc85|0,
f1: 0xfc73c497|0,
f2: 0x9cf84b9d|0,
f3: 0x92c073e6|0,
f5: initialF5,
xx: initialXX,
flag: false
};
const memory = new Map([
[initialF5, outermostRetAddress],
[initialF5 + 4 | 0, stdoutAddress],
[initialF5 + 8 | 0, stdinAddress],
]);
const f0 = "f0", f1 = "f1", f2 = "f2", f3 = "f3", f5 = "f5";
function write(operand, value) {
if (typeof operand == "string") {
cpu[operand] = value;
}
if (Array.isArray(operand)) {
memory.set(operand.map(read).reduce((a, b) => a + b) | 0, value);
}
}
function read(operand) {
if (typeof operand == "string") {
return cpu[operand];
}
if (Array.isArray(operand)) {
const value = memory.get(operand.map(read).reduce((a, b) => a + b) | 0);
if (typeof value == "undefined") console.log("Uninitialized memory access");
return value;
}
return operand | 0;
}
function ata(dst, src) { write(dst, read(dst) + read(src) | 0); }
function nta(dst, src) { write(dst, read(dst) - read(src) | 0); }
function ada(dst, src) { write(dst, read(dst) & read(src)); }
function ekc(dst, src) { write(dst, read(dst) | read(src)); }
function dal(dst, src) { write(dst, ~(read(dst) ^ read(src))); }
function dto(dst, src) {
const amout = read(src);
write(dst, (amout & 0xffffffe0) == 0 ? read(dst) >>> amout | 0 : 0);
}
function dro(dst, src) {
const amout = read(src);
write(dst, (src & 0xffffffe0) == 0 ? read(dst) << src : 0);
}
function dtosna(dst, src) {
const amout = read(src);
if ((amout & 0xffffffe0) == 0) {
write(dst, read(dst) >> amout);
} else {
write(dst, (read(dst) & 0x80000000) == 0 ? 0 : -1);
}
}
function nac(dst) { write(dst, ~read(dst)); }
function lat(dstl, dsth, src) {
const a_ = read(src), a = [a_ & 0xffff, a_ >>> 16 & 0xffff];
const b_ = read(dstl), b = [b_ & 0xffff, b_ >>> 16 & 0xffff];
const dst = [0, 0, 0, 0];
for (let i = 0; i < 2; i++) {
if (a[i] == 0) {
continue;
}
let current = 0;
for (let j = 0; j < 2; j++) {
current += a[i] * b[j] + dst[i + j];
dst[i + j] = current & 0xffff;
current = current >>> 16 & 0xffff;
}
dst[i + 2] = current;
}
write(dsth, dst[2] + (dst[3] << 16));
write(dstl, dst[0] + (dst[1] << 16));
}
function latsna(dstl, dsth, src) {
const a_ = read(src), a = [Math.abs(a_) & 0xffff, Math.abs(a_) >>> 16 & 0xffff];
const b_ = read(dstl), b = [Math.abs(b_) & 0xffff, Math.abs(b_) >>> 16 & 0xffff];
const dst = [0, 0, 0, 0];
for (let i = 0; i < 2; i++) {
if (a[i] == 0) {
continue;
}
let current = 0;
for (let j = 0; j < 2; j++) {
current += a[i] * b[j] + dst[i + j];
dst[i + j] = current & 0xffff;
current = current >>> 16 & 0xffff;
}
dst[i + 2] = current;
}
let dstlValue = dst[0] + (dst[1] << 16);
let dsthValue = dst[2] + (dst[3] << 16);
if ((a_ < 0) != (b_ < 0)) {
dstlValue = -dstlValue | 0;
if (dstlValue == 0) {
dsthValue = -dsthValue | 0;
} else {
dsthValue = ~dsthValue;
}
}
write(dsth, dsthValue);
write(dstl, dstlValue);
}
function krz(dst, src) { write(dst, read(src)); }
function malkrz(dst, src) { if (cpu.flag) write(dst, read(dst)); }
function xtlo(a, b) { cpu.flag = read(a) <= read(b); }
function xylo(a, b) { cpu.flag = read(a) < read(b); }
function clo(a, b) { cpu.flag = read(a) == read(b); }
function xolo(a, b) { cpu.flag = read(a) >= read(b); }
function llo(a, b) { cpu.flag = read(a) > read(b); }
function niv(a, b) { cpu.flag = read(a) != read(b); }
function xtlonys(a, b) { cpu.flag = (read(a) >>> 0) <= (read(b) >>> 0); }
function xylonys(a, b) { cpu.flag = (read(a) >>> 0) < (read(b) >>> 0); }
function xolonys(a, b) { cpu.flag = (read(a) >>> 0) >= (read(b) >>> 0); }
function llonys(a, b) { cpu.flag = (read(a) >>> 0) > (read(b) >>> 0); }
function inj(a, b, c) {
const tmp = read(b);
write(b, read(c));
write(a, tmp);
}
function krzXX(value) { cpu.xx = read(value); }
function injXX(a, b, c) {
write(a, read(b));
cpu.xx = read(c);
}
${labels.map((label, i) => `const ${label} = ${0x14830000 + i + 1 | 0};`).join(" ")}
let stdin = require("fs").readFileSync("/dev/stdin", "utf8");
for (let i = 0; i < stdin.length; i++) {
memory.set(stdinAddress + i * 4 | 0, stdin.charCodeAt(i));
}
memory.set(stdinAddress + stdin.length * 4 | 0, 0);
while (cpu.xx != outermostRetAddress) {
switch (cpu.xx) {
case initialXX:
${source}
}
}
let stdout = "";
let i = 0;
while (true) {
const value = memory.get(stdoutAddress + i | 0);
if (typeof value == "undefined") console.log("Uninitialized memory access");
if (!value) break;
stdout += String.fromCharCode(value);
i += 4;
}
process.stdout.write(stdout);
`;
fs.writeFileSync(process.argv[2] + ".js", js);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment