simbelmynë :: diary

小児科医療 & 趣味はコンピュータいじりです

スーパー楕円のダンジョンLV3でコードゴルフ

こんにちは、仕事中にコード書いてる小児科医、simbelmyn@simbelmyncomです。
院長に知れたら首かもしれません。

さて、codeIQというITエンジニアのためのスキル評価+転職支援サイトがありまして、
我々小児科医には誠に縁もゆかりも無い筈なのですが、趣味で参加しています。

プログラマへの転職意図はありません。いまのところ。
でも電子カルテの糞さが身にしみてるので、電子カルテ開発だったら転職するな。

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

さておき、「スーパー楕円のダンジョン(注:リンク先では問題の内容はみられなくなっています)」とは、
クロノス・クラウン合同会社 柳井政和様@rutenが出題された問題で、
問題の条件をみたすようなJavascriptのコードを書いて提出するものです。

この文章は、私がこの問題に対して提出した解答について、どのように考えてそのコードに至ったかを、記録したものです。
2月4日に書いていますが、公開は2月17日に解答が締め切られた後にする予定です。
一位になったら、この記事アップするんだ・・・俺。


とまあ、こんなふうな偉そうな記事を書いている私ですが、
結城浩@hyukiセンセイが先日出題された問題、通称ジェムストリング問題は盛大に間違えました。
そんなレベルの人間の書く文章ですが、ご容赦ください。

さて、その問題ですが

要約しますと、魔王の復活を阻止するため、「スーパー楕円の封印」を解く必要がある、と。
えー・・・なんだそりゃ?いやまあ続きを読みます。
「スーパー楕円の封印」をとくためには、

・X(-36~36)、Y(-18~18)が与えられるので、
・スーパー楕円1:(|x|/32)^3 + (|y|/ 8)^3 <= 1 の内部であれば1を
・スーパー楕円2:(|x|/16)^3 + (|y|/16)^3 <= 1 の内部であれば2を
・1と2の両方の重なる部分であれば3を

返すようなプログラムを、javascriptで書く必要があると、いうわけです。
いわゆるFizzBuzz問題です。
そして、もうこれ以降は魔王もダンジョンも関係なくなってしまいます。
なんで魔王がスーパー楕円なのかとか、理屈をこねる余裕すら与えない、
なかなか潔いフレーバーですな、ふむふむ、などと思うわけです。魔王哀れ。


で、FizzBuzzではあるんですが、

function yourCode() {
    var arr = [];
    var RNG_X = 36;
    var RNG_Y = 18;
    for (var y = -RNG_Y; y <= RNG_Y; y ++) {
        for (var x = -RNG_X; <= RNG_X; x ++) {
            arr.push(【ここの部分】);
        }
    }
    return arr;
}

この、【ここの部分】に入るコードを答えるというのが問題となっておりました。
なので制御構文が使いにくいですね。


さらに、このスーパー楕円のダンジョン問題ですが、レベルが1~3まであって、別の問題として登録されています。
各レベルで何が違うのか、というと、文字数制限と、「使ってはいけない文字・構文」があるのです。見てみますと、

・レベル1:200文字以内、全文字が使用可能
・レベル2:150文字以内、以下使用禁止:
 Math ' " String Array [ ] $ jQuery eval this window ? :
・レベル3:200文字以内、以下使用禁止:
 Math ' " String Array [ ] $ jQuery eval this window ? : * / & |

こうなっております。

制限内で意図した結果が得られるコードが書ければ正解なのですが、
なんとなく短いコードを追求したくなりますね。なりますよね?

レベル1とレベル2

この問題レベル1と2で、私の提出した解答は以下のとおりです。
・レベル1:

((A=(x<0?-x:x)*x*x)+(B=(y<0?-y:y)*y*y)<4097)*2|A/64+B<=512(58文字)

・レベル2:

(p=x<0,q=y<0,p+=x*x*x^-p,q+=y*y*y^-q,p+q<4097)*2|p/64+q<=512(60文字)

レベル1では、絶対値の計算にMath.abs()が使用可能ですが、三項演算子を使用したほうが文字数が少なくなるので、そちらを使用しております。

2/19追記:LV1では、どうもMath.absを使うほうが主流だったようです。
というか1位のなかでMath.abs使ってないの俺だけだった・・・。

レベル2では、Mathも三項演算子も使用できなくなりましたので、|x|を計算するために、
 |x| = (x<0) + ( x xor -(x<0) )
という演算を行っています。

(x<0)はxが0未満ならtrue、0以上ならfalseです。javascriptでは、trueは1、falseは0と解釈されるらしいです。
その結果、
 xが0以上:|x| = 0 + (x xor 0)
 xが0未満:|x| = 1 + (x xor-1)
という計算式になり、xの絶対値が計算できています。

レベル3

さて、問題本番はレベル3です。「* / & |」の4文字が追加で使用禁止になりました。
乗算ができないのは大きな大きな悩みどころです。

もう一度スーパー楕円1と2の式を示しますが、

・X(-36~36)、Y(-18~18)が与えられるので、
・スーパー楕円1:(|x|/32)^3 + (|y|/ 8)^3 <= 1 の内部であれば1を
・スーパー楕円2:(|x|/16)^3 + (|y|/16)^3 <= 1 の内部であれば2を
・1と2の両方の重なる部分であれば3を

という計算をしなければなりません。

・絶対値の計算

という困難に加えて、

・xの3乗、yの3乗の部分を乗算を使わないでどう計算するのか、

という2つ目の困難が加わった形になりました。


この辺のレベル調整というか、とてつもない問題が立ちふさがって、かつ、工夫次第でなんとかなる、しかも、安易な抜け道は塞がれているような、そういう針の穴を通すような問題づくりには毎回感服いたします。すごいです。

どういう経緯で、この4文字を選んだんだろうか?
記号^(算術XOR)が使用可能で残されているのは意図的なんだろうけど、解答についてどこまで予測していたのだろうか?
と思うわけです。
問題をつくることもさることながら、問題のデバッグにかかる労力にも頭が下がります。


さて、乗算について解決法を探らなければなりません。

課題:

(p=x<0,q=y<0,p+=x*x*x^-p,q+=y*y*y^-q,p+q<4097)*2|p/64+q<=512
ここから乗算記号*をなくす。
さらに、/(除算)も、&(算術AND)も、|(算術OR)も使用不可

しかし、あまり選択肢は無いと思います。
乗算といえば左ビットシフトで2の乗数倍を代用できますが、今回は「x * x * x」を計算する必要がありますから、掛ける数は2の乗数に限りません。よってビットシフトは使えません。

すると、加算で代用するしかありません。
x * xであれば、xをx回加算することで代用です。
ここで、制御構文forを使用する必要が出てきます。

誰もがぶつかる問題だと思いますが、解答は

Array.push(【ここ】);

に入る必要があります。
関数の引数内で制御構文forは使えません。どうやってこれを引数の外に出しましょうか?

私は最初、以下の様に対応しました。

【コード1】

((A=m(m(m(x>0,2)-1,x),m(x,x)))+m(B=m(m(m(y>0,2)-1,y),m(y,y)),64)<=32768)+m(A+B<=4096,2));}}return arr;}function m(a,b){arr=0;if(b<0){b=-b;a=-a;}{{for(i=0;i<b;i++,arr+=a

今見ると笑っちゃうのですが、超強引で、読みにくい。カッコの数があわなくて苦労しました。

これは、解答内で強引にyourCode関数の定義を終わらせてしまい、
その後ろでa*bを計算するための乗算関数m(a,b)を定義しています。
問題文の最後はreturn arrで終わっていますから、これに整合するように、
乗算関数mの返し値も変数arrに入れておく必要がありました。

絶対値の計算は、m(m(x>0,2)-1,x)で行っています。解釈すると、x * ( (x>0)*2-1 )です。xの符号に合わせて、1か-1を乗算して、|x|を計算しています。

xの絶対値の3乗を計算するだけなのに、m(m(m(x>0,2)-1,x),m(x,x))という冗長なコードが必要になってしまいました。

これを整理して、次のコードになりました。これが最初に提出したコードです。

【コード2】

((A=m(m(m(x>0,2)-1,x),m(x,x)))+m(B=m(m(m(y>0,2)-1,y),m(y,y)),64)<=32768)+m(A+B<=4096,2));}}return arr;}function m(a,b){arr=0;if(b<0)b=-b,a=-a;{{for(;b--;arr+=a

(159文字)

コードを短縮する

問題の性格上短縮の必要性はまったく無いのですが、私の性格上短縮します。

まず、

・絶対値の計算をレベル2の解答に使用したものを使用したほうが短縮できる?
・2倍、64倍には乗算関数mではなくビットシフトをつかう
・乗算関数mの引数には正の数しか与えられないように工夫し、mの内部はシンプルにする。
・不等号を工夫する。4096以下ではなくて4097未満に。

こんな事を考えて、以下のコードを書きました。

【コード3】

((p=x-(u=x<0)^-u,q=y-(v=y<0)^-v,A=m(p,m(p,p)),B=m(q,m(q,q)),A+B<4097)<<1)+(A+(B<<6)<32769));}}return arr;}function m(a,b){arr=0;{{for(;b--;arr+=a

これで145文字になりました。この後まだ縮むはずです。
が、たぶんすぐに限界がきます。新しいことを考える必要がありそうです。

乗算関数を関数オブジェクトにする

そもそも、乗算関数mって、こんな強引な場所でしか実装できないのしょうか?
いや、関数オブジェクトで良いのでは?(実はこのときまで関数オブジェクトの存在を知らなかったのでした)

・乗算関数mを関数オブジェクトとして実装してみる
・(A+B<4097)<<1とA+(B<<6)<32769の2つの項を加算しているが、
 それぞれ(0と2)・(0と1)の値しかとらないので、加算ではなくXORで代用できる。
・しかも、XORにすれば演算子の優先順位の観点から、カッコでくくる必要がなくなる。

これでコーディングしてみます。
【コード4】

(m=function(a,b){for(c=0;b--;c+=a);return c},A=m(p=x-(u=x<0)^-u,m(p,p)),B=m(q=y-(v=y<0)^-v,m(q,q)),A+B<4097)<<1^A+(B<<6)<32769

関数オブジェクトの威力で、126文字まで一気に縮みました。


乗算関数でなく3乗する関数を

これa*bの乗算関数がないとだめでしょうか?
3乗するのにm(p,m(p,p))って冗長なんですけど?

・「引数を3乗する関数t」を考えてみる。
・絶対値の計算も3乗関数t内で行うことにする
・yの3乗を64倍するところは、yの4倍を3乗したほうが短縮できそうかな?

この方針で、下のコードができました。
【コード5】

(t=function(a){for(s=0,b=a=a-(u=a<0)^-u;b--;)for(c=a;c--;s+=a);return s},t(x)+t(y)<4097)<<1^t(x)+t(y<<2)<32769

これで110文字になりました。

最初の段階で提出された解答では、最短112文字と聞いていたので、これで最短行けちゃうんじゃないでしょうか。これ以上短縮が思いつかないし。
そう思い、これで再提出しました。

挫折1

出題者様「最短は更新されて107文字です。」

ガーン。なんだと。いやコードゴルフじゃないんですがコレ。落ち着け落ち着け。
でも短縮できるとなると短縮したくなります。ちくしょう。

さらなる短縮へ returnなんていらない

えー、どこが冗長なんだろうかと考えますと、絶対値を取るところでしょうか。
b=a=a-(u=a<0)^-u の部分ですが、短縮できる匂いがプンプンします。
あれ?制御構文使えるように、3乗部分を独立関数にしたんだから、if使えるじゃないですか。

てことは、関数tはこれで良い?
【コード6】

(t=function(a){if(a<0)a=-a;for(s=0,b=a;b--;)for(c=a;c--;s+=a);return s},t(x)+t(y)<4097)<<1^t(x)+t(y<<2)<32769

これで(109文字)



あと冗長なのは、functionとかreturnとかの長い単語ですが、これは無くせないよなあ、、、。
、、、まてよ、別に3乗関数tは値を戻さなくても、外側のスコープの変数に答えを入れたら良いのでは?

理念としてはこういう感じ。returnなんていらんのです。
【コード7】

(t=function(a){if(a<0)a=-a;for(b=a;b--;)for(c=a;c--;s+=a);},s=0,t(x),t(y),s<4097)<<1^(s=0,t(x),t(y<<2),s<32769)

でもこれでは111文字に長くなってしまいました。

t(x)+t(y)と、t(x)+t(y<<2)の2つの値を知る必要があるのがネックです。
xの3乗=t(x)の計算が終わった時点で、sのバックアップをとっておいて、あとで再利用できないでしょうか?

【コード8】

(t=function(a){if(a<0)a=-a;for(b=a;b--;)for(c=a;c--;s+=a);},s=0,t(x),p=s,t(y),s<4097)<<1^(s-p<<6)+p<32769

できました!
p=s(このときのsはt(x)が入っています)でバックアップをとっておいて、
t(y<<2)の計算は、s-p(このときのsはt(x)+t(y)が入っているのです)を64倍することで計算できました。

これで105文字です。今度こそ最短いけてるでしょうか?

これ以上、短縮できるのか?

でも出題者様が以下のようなツイートをなさっていました。

・・・107文字マイナス数文字・・・だと・・・!?

どうやら105文字ではまだ最短とはいえないようです。
いやこれはコードゴルフじゃないゲフンゲフン。
と、ともかく、目標を設定することにしました。
100文字を切ることを目指してみましょう。



上の105文字の例ですが、冷静に考えると、最後の(s-p<<6)+p<32769ではpを移項できます。
移項するとs-p<<6<32769-pとなって、カッコが不要になりました。
また、3乗関数tですが、最後のセミコロンは省略できました。

【コード9】

(t=function(a){if(a<0)a=-a;for(b=a;b--;)for(c=a;c--;)s+=a},s=0,t(x),p=s,t(y),s<4097)<<1^s-p<<6<32769-p

(102文字)

100文字まであと少しです。
でも、107文字マイナス数文字、までは確認されているみたいですが、100文字を切れる保証は何処にもありません。
答えがないかもしれない問題を考えるのは、本当に難しいです。

それに、上記の102文字のコード、とても美しいです。
もうこれで十分美しいではないか、これ以上このコードをいじらなくてもいいじゃないか。
・・・と思うのですが、翌朝からはまた短縮可能かどうかの検討が始まるのでした。仕事しろ。

100文字を切るために

さて、とは言え、同じアルゴリズムではこれ以上短縮できないように思います。
逐一は書きませんが、この頃は様々な検討を試みては敗北しています。

・3乗関数tの中身をもっと短縮できないか?forを一つにできないか?
 → 無理かな?できるとしても冗長だろう
・3乗関数tを乗算関数の再帰で作れないか?
 → めちゃくちゃ冗長になる
・変数tは不要にできないか
 → どうしても2回呼ぶ必要があって無理そう
・そもそも「function」は書かなくてはいけないのか
 → 言語仕様です

こんな検討をして、いずれも無理だなあ、ということになりました。
そこで、ちょっと計算の順序を変えてみようという発想になりました。
なんとなく、s<4097の部分を、ゼロとの比較にできたら、短縮の可能性があるのではないか、というヤマカンがあったのです。

・sの初期値を0ではなく4097とする
・三乗関数tのなかではsに加算するのではなく減算する
・sは4097からxの3乗とyの3乗が減算され、マイナス方向に向かう。
・すると、s<4097の条件は、0<sに変換されます


t(x)+t(y)*64<32769 というのが、スーパー楕円2の式なのですが、
これまでは、p + (p-s<<6) < 32769 と表現していいました。これが右側部分です。

sを4097から減算方向にすることで、ここの表現が変化して、
 (4097-p) + (p-s)*64 < 32769 になります。
移項して整理すると、
 (p-s<<6) < 28672 + p となりました。

具体的にはこんなコードになります。
【コード10】

(t=function(a){if(a<0)a=-a;for(b=a;b--;)for(c=a;c--;)s-=a},s=4097,t(x),p=s,t(y),0<s)<<1^p-s<<6<28672+p

スムーズに変形してるように書いてますが、これが通るようになるまで苦労しました。
苦労しても、文字数は同じ102文字ですが、0<sの部分に可能性を感じませんか?
で、この0を消せないか、また色々チャレンジしました。

undefinedを駆使して0を表現する

最初は、3乗関数tの返り値がundefinedなので、これを0と解釈してくれないかな、という期待がありました。
こんなかんじです。t(y),0<sの部分を、t(y)<sで代用できないかと考えたコードです。
【コード11】

(t=function(a){if(a<0)a=-a;for(b=a;b--;)for(c=a;c--;)s-=a},s=4097,t(x),p=s,t(y)<s)<<1^p-s<<6<28672+p

しかしこれは封印解除に失敗しました。
javascriptよくしらないのですが、undefinedと0は違うものなんですね。

演算を通してみたらどうかと、こんなコードも書きました。
こんどは、t(x)+t(y,p=s)がゼロと解釈されてくれないかなという期待をもったコードです。
【コード12】

(t=function(a){if(a<0)a=-a;for(b=a;b--;)for(c=a;c--;)s-=a},s=4097,t(x)+t(y,p=s)<s)<<1^p-s<<6<28672+p

p=sを実行する位置に涙ぐましい工夫があります。こんな糞コード解釈してくれて有難うjavascript
しかしこのコードも封印解除に失敗しました。
undefinedとundefinedを加算してもundefinedになるんですね。うーむ。



悔し紛れに以下のコードを試します。
ビットシフトなら、ビットシフトならゼロになってくれる・・・t(x)<<(t(y,p=s)<s
【コード13】

(t=function(a){if(a<0)a=-a;for(b=a;b--;)for(c=a;c--;)s-=a},s=4097,t(x)<<t(y,p=s)<s)<<1^p-s<<6<28672+p

おめでとう!今度は封印解除に成功しました。これで101文字です。

と喜びましたが、さらにもう一個不等号削っちゃだめ?ということで・・・t(x)<t(y,p=s)

(t=function(a){if(a<0)a=-a;for(b=a;b--;)for(c=a;c--;)s-=a},s=4097,t(x)<t(y,p=s)<s)<<1^p-s<<6<28672+p

undefined < undefined はfalseで、ゼロになってくれました。これでも成功です。100文字

100文字を切った!

あと1文字でも削れれば、100文字を切るという目標が達成できるのですが、もう何処にも(削る所が)ないじゃん。
・・・いえ、ありました。右側、p-s<<6<28672+pの部分です。

ここは以前、28672でなくて32769だったので、短縮できなかったのです。

 ○*64 < 32769 + ■

という形だったので、短縮できなかったのですが、現在は、

 ○*64 < 28672 + ■

の形になったので、短縮できるようになったのです。

上記の式は、以下のように変形が可能です。そして64倍はビットシフトで表現できます。

 (○ - 448)*64 < ■

これは、sの値を4097から減算することにしたことによる副次的な効果でした。
さすがに、ここまで意図してsの減算を試したわけではなく、偶然に成り立ったものです。

これをコードに戻すと、

 p-s<<6<28672+p

これが

 p-s-448<<6<p

こう変形できます

よって全体のコードは
【コード15】

(t=function(a){if(a<0)a=-a;for(b=a;b--;)for(c=a;c--;)s-=a},s=4097,t(x)<t(y,p=s)<s)<<1^p-s-448<<6<p

このようになりました。
98文字となり、100文字を切るという目標を達成することができました。
これで3回目の提出をすることにしました。

休息

出題者様のツイートによりますと、この記事を書いている時点(2014年2月4日17時)では98文字は最短ということなので、
これで一旦頭を冷やすことに致します。仕事しろ。

が、もしかしたら、明日の朝にはまた考え始めているかもしれません。
コードゴルフじゃないのにね)

2014/2/7 19:30追記

レベル1の最短が57文字に更新されたということで、うんうん唸っていたのですが、
ああ、何のことはありませんでした。
コード14→15に適用したような変化を、レベル1のコードにも適用すればよかったのです。
プラスマイナスに工夫をしなければならないと思いますが、

(A=(x<0?-x:x)*x*x-4097,B=(y<0?y:-y)*y*y,A<B)*2|A<B+448<<6

(57文字)になりました。

これ以上の短縮はむずかしいでしょうか?

挫折2

と言いつつも、延々とその後もレベル3を考え続けておりました。
しかし全く不可能です。しょうがないよね、これできっと最短だよ。そんな風に思っていた時期が僕にもありました。

2014年2月13日 出題者様「正統派な最短96文字(更新!)です。」

ナンダッテー!
この記事書いたけど、一位にならなかったら恥ずかしいからアップしないつもりなんです。
しかも締め切りまで1週間切ってる。間に合うんだろうか?考えろ俺。

そして泥沼へ

再掲しますが、最後に提出した98文字のコードはコレでした。
【コード15】

(t=function(a){if(a<0)a=-a;for(b=a;b--;)for(c=a;c--;)s-=a},s=4097,t(x)<t(y,p=s)<s)<<1^p-s-448<<6<p

コレを如何に短縮するのか。あと2文字削ってようやく一位タイです。正直厳しい。
ひとまず、これまで検討して不可能と判断していたことを、もう一度考えなおしてみます。

・functionを削る
functionを削るために、三乗関数tをオブジェクトのsetterとして実装してみました。
【コード16】

(o={set t(a){if(a<0)a=-a;for(b=a;b--;)for(c=a;c--;)s-=a}},s=4097,o.t=x,p=s,o.t=y,0<s)<<1^p-s-448<<6<p

これでも封印解除はできます。しかし101文字と、伸びてしまいました。
functionの文字を除去出来ましたが、三乗関数が常にオブジェクトoを介したアクセスとなり、全体としては文字数短縮に至りませんでした。

・三乗関数の引数aを省略して、外側のスコープの変数にする。
【コード17】

(t=function(){for(b=a=-a;b-->0;)for(c=a;c--;)s-=a},s=4097,t(t(a=x))<t(p=s,t(a=y))<s)<<1^p-s-448<<6<p

関数tをa=x, a=-x, a=y, a=-yの4回呼んでしまおうという作戦で、
これでも封印解除できますが、100文字となり、短縮できませんでした。

簡単に書いてますが、数日うなり続けました。

forループ2つあるのは無駄だった

そんな日々でしたが、やっと手応えがありました。

・やっぱ三乗関数tの中のforループが2つあるの無駄じゃない?
 といっても再帰ループにするとものすごく長くなってしまうし・・・。

うーむ。
逆に考えて、何か使えるのに使ってない文字ない?
「%(剰余)」なんて使ってないけど?どう?

【コード18】

(t=function(a){if(a<0)a=-a;for(c=0,b=a;b;b-=((++c%a)<1))s-=a},s=4097,t(x)<t(y,p=s)<s)<<1^p-s-448<<6<p

101文字。アレ?

【コード19】

(t=function(a){if(a<0)a=-a;for(c=0,b=a;b;b-=++c%a<1)s-=a},s=4097,t(x)<t(y,p=s)<s)<<1^p-s-448<<6<p

97文字。アレアレ?

98文字から1文字短縮できました。

c=0, b=aと初期値を与えておいて、
1 cを1ずつカウントアップ
2 cがaの倍数になるたびに、bをカウントダウン
3 bがゼロになったら終了

これで、ループはひとつで良くなりました。%剰余を使えばよかったのでした。これって基本テクニック?

さらに!

4 cは初期値0からカウントアップする必要がない。
 cの初期値はaの倍数であれば正しく動作します。もちろんc=aでも良いはずです。

【コード20】

(t=function(a){if(a<0)a=-a;for(c=b=a;b;b-=++c%a<1)s-=a},s=4097,t(x)<t(y,p=s)<s)<<1^p-s-448<<6<p

これで、95文字になりました!暫定1位の記録を更新していいるはずです。
これで提出しました。さすがにこれで最短でしょう!


ーーーーー
ーーーー
ーーー
ーー

まだちょっとだけ続く

最短でしょう!とか言っときながらも、その後もうんうん考えました。
96文字までは、他の挑戦者の解答によって、短縮できるという保証がありました。
しかしいま、95文字まで短縮してしまっています。さらに短縮できるという保証はありません。
また、答えがないかもしれない問題を考えることになりました。
コードゴルフにおいて、切り込み隊長はそのフォロワーよりずっとハードだと思います。

絶対値の計算を再考する

今度は、aの絶対値をとる部分を再考してみました。
この部分は、レベル2の解答では、

p=x<0,p+=x*x*x^-p

というふうに計算していたところ、

三乗関数tを独立させるにあたり、

if(a<0)a=-a

と書き直した部分です。
aの絶対値を取るだけであれば、これ以上短くは書けないはずです。

三乗関数tの部分だけを示しますが、

t=function(a){if(a<0)a=-a;for(c=b=a;b;b-=++c%a<1)s-=a}

こうなっています。

絶対値の出し方を、レベル2で使用した方法で書いてみます。
一度は諦めた道なのですが、、、。

t=function(a){for(c=b=a=a-(u=a<0)^-u;b;b-=++c%a<1)s-=a}

残念ながら、この書き方だと1文字伸びてしまいます。

しかし、本当にaの絶対値はこんなに必要なんでしょうか?

tの内部ではa,b,cの3つの変数が、引数aの絶対値を保持し、その後にforループが始まります。
この変数を2つに抑えるのは無理でしょうか?と、考えましたが、これは無理でした。

次に、b,cがaの絶対値を持つのは許すから、引数aの絶対値を再度aに格納する必要はあるのか?と考えました。
aの符号がわからなくても問題ない形にできないだろうか?ということです。

新しい三乗関数t

t=function(a){for(c=b=a-(u=a<0)^-u;b;b-=++a%c==0)s-=c}

うむ。これでも動作します。古い三乗関数と同じ長さですが。
ともかく、a自身にaの絶対値を再格納しなくても動作するコードを書くことが出来ました。

【コード21】

(t=function(a){for(c=b=a-(u=a<0)^-u;b;b-=++a%c==0)s-=c},s=4097,t(x)<t(y,p=s)<s)<<1^p-s-448<<6<p

(95文字)

さらに、レベル2と全く同じ書き方で、余計な変数uの排除を試みます。
【コード22】

(t=function(a){for(b=a<0,c=(b+=a^-b);b;b-=++a%c==0)s-=c},s=4097,t(x)<t(y,p=s)<s)<<1^p-s-448<<6<p

(96文字)
変数uは排除出来ましたが、一文字伸びてしまいました。

この時点で、2月16日18時でした。締め切りまで24時間を切っていますが、これでギブアップか・・・。


・・・アレ?このカッコ、とれるの?

コレを c=(b+=a^-b) → こうしても → c=b+=a^-b
正しく解釈してくれることを、知りませんでした。

【コード22】

(t=function(a){for(b=a<0,c=b+=a^-b;b;b-=++a%c==0)s-=c},s=4097,t(x)<t(y,p=s)<s)<<1^p-s-448<<6<p

これで通ってしまいました。94文字です。

改めて、コード1から振り返ってみますと、もはや原型をとどめておりません。
思えば遠くへ来たものです*1

以上

スーパー楕円のダンジョンLV3 94文字への記録をまとめました。

これから、Javascriptコードゴルフをされる数寄者の方々のために、なにかのヒントをお伝えできれば幸いでございます。

拙い文章ですが、お読みいただきありがとうござました。

*1:古典部シリーズ4作目「遠まわりする雛」およびアニメ「氷菓」19話のエピソード「心当たりのあるものは」が好きです。そんだけです。すみません。

遠まわりする雛

遠まわりする雛