読者です 読者をやめる 読者になる 読者になる

ゆるめ

ゆるめなのは公開範囲です。

コードダンジョン潜伏記録

先日ゴルフの衝動にかられたので以前書いたものに追加するついでに短くしました。

しかしこのご時世、正直ブックマークレットの文字数上限なんてあってないようなものなので、可読性を下げるデメリットに打ち勝つほどのメリットがショートコーディングにあるかというと、書いてる時に楽しいとかそんなもんなんですよね。

という訳でブックマークレットのバイト削りはほどほどにして、ゴルフはゴルフ場でということで、ここしばらくゴルフ場に行ってました。

JavaScript のゴルフ場に行ったことがなかったので検索したら、コードダンジョンというブラウザゲームが出てきたので、そこで遊んでいました。レベルが上がって禁止文字の縛りが入ると、検索しても解法が直接出てくることがまずないので、思う存分脳みそしぼってあれこれ考えたり試せたりできるのが熱かったです。

ちょっとしたストーリーがあって、正しいコードの入力に成功するとストーリーが進むようになっています。コマンド入力でストーリーを進める往年のアドベンチャーゲームインタラクティブフィクションみたいで、レトロ心をくすぐられます。

あんまり面白かったものですから、プレイ料金代わりにこのゲームの攻略本みたいな本を買いました。全問解いた後に買って読んだので、メモリアルブックみたいに感じました。

元は別サイトで出されたクイズらしく、そちらも見に行ってみましたが、現在そういうテーマの問題は出されていないようです。

以下は現時点での全問 (19問) を解いた記録です。ネタバレ以外の何物でもないので、これからプレイする予定のある方は続きを読まずにまっすぐダンジョンへ向かってください。

FizzBuzzのダンジョン

以前別言語でこのテーマのゴルフに参加したことがあるのですが、コード本体より言語の無作法を極めて削っていくのがメインというか前提のような感じになっていました。

このゲームでも一文字変数とか宣言の省略とかスペース削りとかは必須なのですが、もっとこう、心が痛むようなバッドノウハウがデフォルトの感じでした。

このゲーム内ではそういう外側を削るようなことはしなくても良くて、穴埋め式でコアなところだけ書けば良いので、大分気が楽でした。

この回の穴埋め部分は、ループ内の [i, "fizz", "buzz", "fizzbuzz"] という配列の添字部分でした。0-3 のどれかを返して FizzBuzz を完成させます。

スクロールすると解説や最短文字数が出てくるのを知らず、どの辺まで頑張れば良いのかがまだつかめなくて、最初に書いたものだけですぐ離脱してしまいました。

しかも、最初のダンジョンはそこそこコードの投稿があったので、そこに載っていない解き方で解いたら投稿すべきなのかしらと思い、別段短くも何ともないコードを投稿してしまいました。後で存在を知った解説を見たらばっちりかぶっていました。恥ずかしい。穴があったらアナーキー

以降、私の書いたコードと文字数、参考値として問題初出当時の最短文字数を記していきます。私の書いたコードは Firefox53 で動作確認しました。

LV1, LV2

!(i%3)+!(i%5)*2

LV3

'300102100120100'[i%15]

LV4

'300102100120100'.repeat(7)[i]

repeat は当時の Chrome では使えなかったみたいです。

LV 私の最短 当時の最短
1 15 13
2 15 13
3 23 16
4 30 27

※「当時の最短」は「2013年11月18日 時点の Google Chrome」という縛りあり

素数のダンジョン

素数の時は true、それ以外の場合は数値を返す穴埋めです。

この問題の途中で解説と最短文字数の存在に気付いた気がします。ただ、最短文字数が 2013年の Chrome 縛り付きのものだとはまだ気付いていなかったので、コードを詰める作業が甘々でした。ちゃんとした人が今解いたらもっと短くなるかもしれません。

LV1

2**(i-1)%i==1||i

LV2

/1\.7/.test(i**4*(7+i*i*30)/21)||i

フェルマーの小定理素数のことを検索してその場で初めて知りました。なんでそうなるのかはいまいち理解しきれていませんが、フェルマーさんありがとうございます。最近使えるようになったべき乗の演算子 ** のおかげで当時の最短よりコードが縮まっています。

LV 私の最短 当時の最短
1 16 20
2 34 35

※「当時の最短」は「2013年12月05日 時点の Google Chrome」という縛りあり

パチパチ7のダンジョン

7 の倍数と 7 が含まれた数字の場合は true、それ以外は数値を返す穴埋めです。

昔ちょっと流行った NabeAtzz を思い出しましたが、自分は解いてみたかどうか思い出せませんでした。当然コードを書くときもまるっきり初めて気分です。

LV1

!/7/.test(i)*i%7?i:!0

LV2

!/7/.test(i)*i%7<1||i

LV3

/7/.test(i)||(i/7|0)==i/7||i

この辺からビット演算も取り入れないと最短コードを目指せないことに薄々気付いてきます。三問解いてようやく薄々というにぶちんです。

LV 私の最短 当時の最短
1 21 21
2 21 21
3 28 26

※「当時の最短」は「2013年12月19日 時点の Google Chrome」という縛りあり

各桁総和のダンジョン

1-9999 の数を桁ごとにバラして足していく問題です。

小学生の頃にクラスメイトがやっていた相性占いを思い出しました。名前を数値化してこんな感じで足していって、最終的な数値が大きいと相性が良いとされるものでした。

この問題は最初タブレットで遊んでいて、サンドボックスが脳内にしかなかったので、法則性に気付くまで十数分かかりました。

LV2 以降は数字をいかにして作るかがテーマになっているようです。記号プログラミングで検索したページを参考にしようと思ったのですが、あちらは文字数制限がないので、はじめに作ったゼロをのんびりインクリメントして 0-9 を作っていました。ゼロを作る部分だけ参考にしました。

どうやって数字を作ればよいのか分からなかったので、こんなコードを書いたりしていました。

i-(i/(a=Math.PI*Math.PI^[])|[])*a||a

結果の配列 arr にアクセスできることに気付いて、前の問題のようにそこをフル活用したら短くなるかなと思いやってみましたが、最短コードはそれに加え ~ をフル活用していて、私もできるようになるといいなあと思いました。

LV1

i%9||9

LV2

arr[i-0xa]||i

LV3

arr[i-`${-[]}xa`]||i

` が禁止文字に入っていなかったので遠慮なく使わせてもらいましたが、今リニューアルしたら間違いなく禁止されている文字だと思います。

LV 私の最短 当時の最短
1 6 6
2 13 12
3 20 24

※「当時の最短」は「2013年12月26日 時点の Google Chrome」という縛りあり

スーパー楕円のダンジョン

スーパー楕円なんて初めて聞きましたが、数式がちゃんと載っていて、親切だなあと思いました。

LV3 で掛け算と割り算とついでにべき乗も封じられて途方に暮れたため、ものすごく迷走しているコードがメモに残っています。

(s=t=>(``+t).split`-`)(lzbase62.decompress(`sLxBQsJxBPsIxBDsFxlkxm9xmwxlFxmgxlGxmdxjHxmbxiKxmGsKxBOxfOxmIxlkxm9xmfxlWxmPxlXxmOxlYxmLxjZxmFxecxmJxlkxmn`)).slice(s(y).pop()).shift().charAt(s(x).pop())-0
(a=>{p=i=0;for(i=a(y);i--;)p+=3;for(n=4;--n;)if((i+=parseInt(`_g0ig0hg0hg0hf0he0hb0h70h0f20g00g00g00f00e00d00a0020000000`.slice(p).charAt(n),36))>a(x))return n;return 0})(t=>(``+t).split`-`.pop())

どちらも正解の配列を圧縮してループに合わせて取り出す方式です。文字数上限ギリギリです。

解説を見たとき、載っていた別解に舌を巻きました。あんな 15年前の IE みたいな書き方で動くのにびっくりです。

別解二位の人のコードが動かなくなっていたので、動くようにしてみましたが、結構伸びてしまいました。バッククォートはやっぱチートかなと思って二種類書いてました。

+(new Text).ownerDocument.getElementById((sample=(_=>{})).name).innerText.charAt((y<<6)+(y<<3)+y+y+x+1368)
+(new Text).ownerDocument.getElementById(`sample`).innerText.charAt((y<<6)+(y<<3)+y+y+x+1368)

LV1

(a=Math.abs,w=a(x**3),h=a(y**3),w+h<4097)*2|w/64+h<=512

べき乗の短縮が効いているので、当時の最短より縮まっています。

LV2

(a=n=>(n>>31|1)*n**3,w=a(x),h=a(y),w+h<4097)*2|w/64+h<=512

n>>31|1 は検索して出てきた絶対値の出し方そのままですが、解説を見たら n>>9|1 でも大丈夫だったみたいで、56文字まで縮まりました。

((w=(x>>9|1)*x**3)+(h=(y>>9|1)*y**3)<4097)*2|w/64+h<=512

LV3

(a=n=>{if(n<0)n=-n;h=0;for(i=n;i--;)for(j=n;j--;)h+=n},a(x),w=h,a(y),w+h<4097)<<1^w<=512-h<<6

参考までに、解説にあった二位の人のコードを今風の書き方にすると、80文字まで縮みました。アロー関数つよい。

(z=n=>(d=parseInt(1e3+n%n,n-(t=n<0)^-t)^1%n<1),z(y)+z(x)<4097)<<1^d<=512-z(y)<<6
LV 私の最短 当時の最短
1 55 57
2 58 60
3 93 94

※「当時の最短」は「2014年01月29日 時点の Google Chrome」という縛りあり

5乗のダンジョン

どっちの五乗か判断して 0 or 1 を返す問題です。

本来は Math.pow() という長めの文字を避けて、別の判定方法を考えるやつです。

LV1

b**5==c|0

LV2

-`${a-c}`.slice(-1)&&1

ごめんなさいな感じの回答です。もうちょっと縛った回答も考えて楽しむべきでした。

LV 私の最短 当時の最短
1 9 11
2 22 24(15)

※「当時の最短」は「2014年02月20日 時点の Google Chrome」という縛りあり

あべこべのダンジョン

8桁の数字を桁ごとにバラして、「1」を「9」に、「2」を「8」に…と変換していく問題です。

「これ、e をはさむ数値の書き方で短くするやつだな…」と思って早々に削るのを諦めたので、スコアはそれなりでした。

Lv3 で -1 を出す際、各桁総和のダンジョンと同じ轍を踏んで、結局遠回りしてしまいました。三角関数のメソッドを片っ端から for で回してこのチョイスになりました。

55555555*2+n*(Math.tan(2)|3)

LV1

111111110-n

LV2

111111111+~n

LV3

55555555*2+n*''.indexOf()

他のところは私の実力ではしょうがないとして、''q にして削れることに気付けなかったのは悔しいです。

LV 私の最短 当時の最短
1 11 10
2 12 10
3 25 20

※「当時の最短」は「2014年03月26日 時点の Google Chrome」という縛りあり

バグハンターのダンジョン

「bug」を「000」に置換していく問題です。replace はしょっぱなから封じられています。

LV2 の回答がゆるぎなきチート感を放っていたのでバッククォートに頼らない解法も考えました。解説に同じ発想のコードが載っていてちょっと嬉しかったです。

t.split(console.debug.name.slice(2)).join([0]+0+0)

最終的に当時の最短と同じ文字数になって良かったなあと思いました。

t.split(11517 .toString(31)).join([0]+0+0)

アロー関数がありならもう少し削れます。

t.split((bug=_=>_).name).join([0]+0+0)

LV1

t.split('bug').join('000')

LV2

t.split(`bug`).join(`000`)

LV3

(bug=i=>{for(;i--;)t=t.substr(i,3)==bug.name?t.slice(0,i)+0+0+0+t.slice(i+3):t})(14),t

一位のコードの一つをアロー関数に直すと 82文字まで縮まったので、私のコードは 4文字ほど冗長です。

(j=>{for(;j<18;t+=a+i+(q=t.slice(j++,j))in{bug:0}?i=q=0:a)a=i,i=q})(0),t.slice(34)
LV 私の最短 当時の最短
1 26 26
2 26 42
3 86 88

※「当時の最短」は「2014年04月28日 時点の Google Chrome」という縛りあり

ダブル数列のダンジョン

等差数列と等比数列を見分ける問題です。最初の説明を読んで「まっすぐのグラフかまがったグラフかってことだな」という雑な理解をしました。

ビット演算下手だから上位コードには並べないな…と諦めた問題ですが、次のあたりから自分でもビット演算試行錯誤してみようと思ったりもした問題です。解説の上位コードは今見ても仕組みは分かれどどうしてここに思い至れるのか分からないものばかりで、至らなさを感じます。

LV1, LV2

(b/a^0)*(b%a+b)

LV3

(b/a^0)*Array(b%a).push(...Array(b))

スプレッド演算子は当時なかったやつです。

LV 私の最短 当時の最短
1 15 13
2 15 13
3 36 22

※「当時の最短」は「2014年05月26日 時点の Google Chrome」という縛りあり

3番目のダンジョン

五つの数字の中で三番目に小さい値を返す問題です。大きい方から数えても良いあたりに親切さを感じました。

最初は Math.max で削りだそうと思ったのですが、括弧がたくさん必要になったので、すぐソートする方針に変えました。

(l=[a,b,c,d,e])[(f=k=>l.indexOf(Math.max(...l)))()]=0,l[f()]=0,l[f()]

[...arguments] より [a,b,c,d,e] の方が短いことを確認してすぐ候補から外してしまったのですが、上位のコードはすべて arguments を利用していました。ビット演算以上に再帰は避けがちです。

この辺からあれこれ試行錯誤してテキストファイルがどんどん大きくなっていきます。

LV1

[a,b,c,d,e].sort()[2]

LV2

(l=[a,b,c,d,e]).map(k=>l[k]=k),l.filter(k=>k)[7]

filter の中身は当時でも四文字 (Date) でいけたそうです。

LV 私の最短 当時の最短
1 21 21
2 48 53

※「当時の最短」は「2014年07月14日 時点の Google Chrome」という縛りあり

そろばんのダンジョン

与えられた数を桁ごとに分割してゼロパディングした五進数で返す問題です。

この問題から答えを変数 r に入れて戻すタイプの回答方法に変わって、for が大活躍するようになります。今まではワンライナーの中に収めるために function で囲む必要があったので、不利でした。

LV1

for(d of(r=q)+i)r+=[d/5|0]+d%5

何故か空の文字列が引数として渡されて q に入っていました。

LV2

i+=q;f=n=>i[n]+1?[i[n]/5|0]+i[n]%5:q;r=f`0`+f`1`+f`2`

アロー関数とバッククォートのダブルチートです。i[n] が縮められそうで縮められませんでした。

LV 私の最短 当時の最短
1 30 30
2 53 59

※「当時の最短」は「2014年12月01日 時点の Google Chrome」という縛りあり

2015のダンジョン

「2015」を生成する問題です。この問題から => が全面禁止になってしょんぼりしました。

LV1

r=a*b*c

LV2

r=a|b|c;r+=r<<-~(a&b&c)

ここはビット演算だな!と張り切ってみたのですが、上位コードは別のアプローチ法で辿り着くものでした。

LV 私の最短 当時の最短
1 7 7
2 23 16

※「当時の最短」は「2014年12月22日 時点の Google Chrome」という縛りあり

ゾンビのダンジョン

一定の法則で配列を変化させた場合の「2」の最大数を答える問題です。

最初どうしても最短に近い長さにならず、しばらく悩んでいたのですが、解法がヒントに載っていました。見逃していました。

悩んでいた時のコードです。96文字です。

for(r=i=8;i--;){t=[...f];t[i]||(t[i]=3);r=r<(a=t.join``.replace(/1?31?|2/g,'').length)?r:a}r=8-r

「このるてんしとさんて人、今のところ全問コード投稿してるな…熱心なファンなのかな…」と思って貼られている URL を見に行ったら実は作成者さんご本人で、申し訳ない気持ちになりました。ちょうどこのタイミングで解説ページに投稿コードと同じものが自作のものという説明付きで紹介されていて、遅かれ早かれこの問題でるてんしとさんの正体に気付くことになったと思うのですが、自力で気付けて良かったです。

十何年か前、自作フリーソフトがちょくちょく CD-ROM 付き雑誌に収録されたり、お礼としてその収録された雑誌を貰ったりしていました。その際同じく収録されていたソフトの中にめもりーくりーなーをよく見かけていたので、昔から見知っているような気持ちになっていましたが、ハンドルはこの時点で初めて知りました。雑誌に載っていた作者名が多分本名のみだったんだと思います。

今 HDD を漁ったら 1999年製のめもりーくりーなーなんかも出てきました。readme.txt もやっぱり本名でした。

LV1

r=/1,0,1/.test(f)+/1,0|0,1/.test(f)+/0/.test(f);for(i of f)r+=i>1
for(i of[/0/,/1,0|0,1/,/1,0,1/])r=i.test(f)+~~r;for(i of f)r+=i>1

LV2

r=/1,0,1/.test(f)+/1,0/.test(f+','+f.reverse())+/0/.test(f);for(i of f)r+=i>1

LV1 で悩み切って LV2 は力尽きた感があります。

LV 私の最短 当時の最短
1 65 64
2 77 68

※「当時の最短」は「2015年02月03日 時点の Google Chrome」という縛りあり

ホワイト・デイのダンジョン

変数の型を判別して、それぞれに合った値を返す問題です。

この辺からなかなか最短に辿り着けなくなりました。もともとそんなに辿り着けてないのですが、上位の人との差がより一層開いてる感覚がありました。

LV1

r=q[0]?q.concat(q,q):1/[q]?q*3:!q

LV2

return q[0]?q.concat(q,q):1/[q]?q*3:!q

最短まであと一文字だったのですが、力及ばずでした。

LV 私の最短 当時の最短
1 33 32
2 38 37

※「当時の最短」は「2015年03月03日 時点の Google Chrome」という縛りあり

エイプリルフールのダンジョン

数式の計算結果を検証する問題です。珍しく eval 解禁です。

LV2 は、setTimeoutasync/await を組み合わせて何とかならないかしらと試してみましたが、いじれる部分の外側に async をつけないと動かないので、無理でした。

LV1

r=+eval(q.replace(/=/,'=='))

LV2

ga.constructor('x='+q.replace('=','=='))();r=+x

多分 GoogleAnalytics の何かだと思うのですが、ga という関数がグローバルなところに置いてありました。初出の時にはなかったのではないかと思われます。

LV 私の最短 当時の最短
1 28 28
2 47 41

※「当時の最短」は「2015年04月09日 時点の Google Chrome」という縛りあり

母の日のダンジョン

文字列から最短一致する「mother」の文字を浮かび上がらせる問題です。

テキストファイルに試行錯誤の軌跡が割ときれいに残っていたので載せます。

q.replace(/(.*?)(m.*?)(o.*?)(t.*?)(h.*?)(e.*?)(r.*)/,function(_,a,b,c,d,e,f,g){a=[a,b,c,d,e,f,g];a[0]=' '+a[0];r='';for(i of a)r+=i[0].padEnd(i.length,'_');r=r.slice(1)})
r=''.padEnd(36,'_');for(s of 'mother')i=q.indexOf(s,i),r=r.slice(0,i)+s+r.slice(i+1)
r='';for(s of 'mother')i=q.indexOf(s,i),r=r.padEnd(i,'_'),r+=s;r=r.padEnd(36,'_')
r='';for(s of 'mother ')i=q.indexOf(s,i),r=r.padEnd(~i?i:36,'_')+s.trim()
r='';m='mother'.split``;for(s of q)r+=s==m[0]?m.shift():'_'
r='';[...m]='mother';for(s of q)r+=s==m[0]?m.shift():'_'
r='';m='mother';for(s of q)r+=s==m[0]?(m=m.slice(1),s):'_'
r='';i=0;m='mother';for(s of q)r+=s==m[i]?m[i++]:'_'
r='';i=0;for(s of q)r+=s=='mother'[i]?(i++,s):'_'
i=0;for(s of q)r+=s=='mother'[i]?++i&&s:'_'
for(s of q)r+=s=='mother'[i|=0]?++i&&s:'_'


q.replace(/./g,function(s){r+=s.split('mother'[i|=0])[0]?'_':++i&&s})
q.split``.map(function(s){r+=s.split('mother'[i|=0])[0]?'_':++i&&s})
q.split``.map(function(s){r+=s.match('mothers'[i|=0])?++i&&s:'_'})
[...a]=q;a.map(function(s){r+={[s]:1}['mothers'[i|=0]]?++i&&s:'_'})
i=0;q.split``.map(function(s){r+=i^'mother'.search(s)?'_':++i&&s})

他の問題はもっと伸びたり縮んだりが激しくてごちゃごちゃしています。このファイルも下の方にごちゃごちゃした部品が色々書かれていました。

最初に比較用の配列を作るために、とにかく動くコードを書くのですが、この最初のやつめっちゃ長いですね。残余引数使っても良かったのに。

LV1

for(s of q)r+=s=='mother'[i|=0]?++i&&s:'_'

LV2

q.split``.map(function(s){r+=s.match('mothers'[i|=0])?++i&&s:'_'})
i=0;q.split``.map(function(s){r+=i^'mother'.search(s)?'_':++i&&s})

問題を解いているとき、「これ最短文字数の人はきっと function 以外の方法で関数作ってる…」と思っていたのですが、うまく調べられず、歯がゆい思いをしました。Property Method Assignment という方法で短くできるんだそうです。

縮めても 63文字だったので最短には届きませんでしたが。

q.split``.map({f(s){r+=s.match('mothers'[i|=0])?++i&&s:'_'}}.f)
LV 私の最短 当時の最短
1 42 42
2 66 62(59)

※「当時の最短」は「2015年05月15日 時点の Google Chrome」という縛りあり

雨降りのダンジョン

配列の抜けを補って雨の日を割り出す問題です。

この辺で in じゃなく of を使った方が削り率が高くなる、添え字はできる限り避けるという攻略法に気付きました。例によって遅めの気付きです。

LV1

for(n of q)a=n+q[r.push((n||a-2)>>2)+1]

LV2

for(n of q)i=n+q[r.push(n/4^0+i/6*!n^0)+1]

毎回ビット演算頑張ろうと思っても上位の人に追いつけません。

LV 私の最短 当時の最短
1 39 37
2 42 38

※「当時の最短」は「2015年06月19日 時点の Google Chrome」という縛りあり

太陽のダンジョン

3*3 のマスで縦横、逆順ありで「sun」の文字を検知して、検知した文字の数を返す問題です。

この問題は難航したので一旦後回しにして次の問題を先に解きました。そしたら自分の中でラスボスのような印象がついてしまいました。

縦横のヒット数を出して計算する以外の解き方がずっと思いつかなくて、その解き方のコードばかり書いていました。89文字から縮まず絶望していました。

m=/nus|sun/;for(i in q)r+=m.test(q[i-8]+q[i-4]+q[i]);i=q.split(m).length-1;r=(r+i)*3-r*i

LV2 では Computed property names を使ってどうにかならないかと思い試してみましたが、どうにもなりませんでした。

a={[atob('IG51cw').trim()]:1,[atob('ICBzdW4').trim()]:1};for(i=12;i--;)r+=(a[q[v=i%4]+q[v+4]+q[v+8]]|a[q[h=i-v]+q[h+1]+q[h+2]])&v<3
t=!1+''+$;a={[t[3]+t[5]+t[6]]:1,[t[6]+t[5]+t[3]]:1};for(i=12;i--;)r+=(a[q[v=i%4]+q[v+4]+q[v+8]]|a[q[h=i-v]+q[h+1]+q[h+2]])&v<3

LV1

for(i in q)r+=/nus|sun/.test(q[v=i%4]+q[v+4]+q[v+8]+i+q.substr(i-v,3))&v<3

LV2

for(i=12;i--;)r+=!!RegExp(atob('c1VOfE5Vcw'),'i').exec(q[v=i%4]+q[v+4]+q[v+8]+i+q[h=i-v]+q[h+1]+q[h+2])&v<3

LV2 は割と早々に諦めました。

LV 私の最短 当時の最短
1 74 72
2 107 79

※「当時の最短」は「2015年07月17日 時点の Google Chrome」という縛りあり

海のダンジョン

二次元配列内の数値を計算して最大値の添え字を返す問題です。

問題を解いているとき何故か頭の中でずっと宙船が流れていました。

entriesi の管理を省けるかなと思ったら、entries ってフレーズ自体が長いせいで、短くなりませんでした。

for([i,[a,b]]of q.entries())a<4|b<4|a/b<q||(q=a/b,r=i)

LV1

i=0;for([a,b]of q)a<4|b<4|a/b<q||(q=a/b,r=i),i++

LV2

b=i=0;for([a,c]of q)a=6%a&&6%c&&a*c**-1,b%a==b&&(r=i,b=a),i++

分割代入とべき乗演算子のおかげで当時の最短より縮まってます。ちなみに一位のコードをいじったら 57文字まで縮まりました。

i=0;for([a,b]of q)a*=b**(4%a%b*2-9),a%q!=a&&(q=a,r=i),i++
LV 私の最短 当時の最短
1 48 53
2 61 68

※「当時の最短」は「2015年08月14日 時点の Google Chrome」という縛りあり

総括

Firefox53(/ES201[567]/) つよい。自分の腕はあんまつよくない。

競うこと全般が苦手なのですが、このゲームは競う部分も楽しめました。最短コードをお手本とか正解とかそんな風に見ていたので、競り合うというよりは辿り着く感覚でいたおかげかもしれません。