2018-02-27: Cookpadの1dayインターンで超絶技巧プログラミングをした
やったこと
Cookpadの1dayインターンで超絶技巧プログラミングをした
https://internship.cookpad.com/2018/spring/
Cookpad 1day Spring Internship 2018の超絶技巧プログラミングコースに参加してきた。遠藤さんに会いたいなぁ、という気持ちで応募したら通ってしまったので行かざるを得なくなった。よく考えれば直近のRuby25でも遠藤さんには会えたしそれで十分だった説はある。今アホみたいに忙しいのでこんなことをしている場合じゃねえ、的な。
当日、家を出る時間が悪かったらしく恵比須の到着が9:52くらいになる。10:00開始とあったのでとてもピンチだった。走ったら間に合うかと思ったけど鈍足エレベーターに足止めされたりして、結局会場に着いたのは10:01くらいだった。無念。
午前中は遠藤さんによる超絶技巧プログラミングの講義だった。コードのAA化のテクニックはRuby固有なわりに、それ以降はそうでもないのはどうしてだろう、という気持ちになる。どうせRubyを使うならeval使うよなぁ、と。まあボクは話を半分くらいしか聞かずQuineを書いていた。あと午前の終わりくらいにはBPEのエンコーダーを書いていた気がする。h.js用に書いたエンコーダーの方が完成度は高いんだけど、あれは書いた本人が読めない次元の謎コードな上に、読み解いてもなぜこんなに圧縮できるのかよく分からないのであまり使いたくなかった。
昼食はCookpadが用意してくれた。ありがたい。ビーフストロガノフという魔人拳を繰り出しそうな料理を食べた。おしゃれだ。
なんかPietを書く人がいたり、京都から自転車でCookpad本社まで辿り着いた人がいるということが分かった。
午後は自由に超絶技巧なプログラムを書いて発表する、という感じだった。13時から16時まで制作で、16時からは発表、となっていた。
ボクの場合は最初の30分くらいは、3時間で書けるプログラムなんて限られているので何を作ろうかなと考えていた。アイディアは二つ。
- 那珂ちゃんのAAで、実行すると2-4-11のAAになって、以降何度実行しても2-4-11になるプログラム。
- 雛鶴あいのAAで、実行すると将棋盤の形になって以降引数で動作を与えると将棋をプレイできるプログラム。
1はネタが古いのと、実装がそこまでの難易度ではないので完成しないと見せづらいので不採用として、2をやることにした。最近、将棋話題だしね。
それと、本将棋を実装するとさすがに3時間じゃ厳しいので、一度Cで実装したことのある京都将棋にした。
ここまでで30分くらい。Don Yangのコードを眺めながら色々考えていた。やっぱDon Yangすげーわ。
で、ここから2時間くらいかけて、コマンドライン引数を受け取って京都将棋をプレイできるプログラムが出来上がった。相手側が可能な手から乱数で一手選ぶだけだけど、まあ出来た。なんかバグもあるような気がしたけど、この辺でコードを整形しはじめないと間に合わない気がするので、そっちに取りかかりはじめる。
この時点で3.5Kくらいあったので、これ用のAAを今から作るのは不可能だと判断して、とりあえず迷ったときの基本として長方形に整形しはじめる。空白文字無しでも動くように調整していたので、これはすぐに出来た。しかしやはり長い気がしたので申し訳程度にBPEをかけたりもした。ちょっと縮んだので満足。
できたコードはこんな感じ。全然短くないとか、__END__は反則じゃね、とか色々と思うところはある。もっとがんばりたかった。が、時間は有限である。
eval((k="`^\\ZXWUSQPOMLKJHFC@987/").chars.reduce((s=%q~4)`0]^..\veZ};X.eWQ9U==S,xQHMP{|O7yMfaLlCK8nJ$pH&
&FseCac@,y9x+8s[7][/puts"eval((k=#{k.inspect}).chars.reduce((s=%q#{_=126.chr}#{s}#{_}).split*'')Os,c|(t=
s.split(c)).join(t.shift)})";ls=DATA.read.lines(chomp:true);B={?歩=>?飛,?金=>?桂,?銀=>?角,?と=>?香XR=B.i
nZrt;I=(R.to_a+B.to_a+[['王','王']]).to_h;Hs=l71\1^W@h_slice(2).mapO(_,l)|lFl.split('|')[1,5].mapOs|[71]
.stripWmpty??nil:71]S'▲',72]]}Xt=->l{l.split('|')[6]&.slice(1\-1)&.chars&.group_by(&:itClf)&.transform_v
alues(&:size)||{}X$hs={true=>t[l71^],LK=>t[l72]]Xdefine_method(?r)do|cs,c|caC(cs.shift);when'moZ';fx,fy,
tx,ty=cs.map(&:to_i);ft,fk=H7fy/fx];tt,tk=H7ty/tx];tk=R[tk]||tk;H7ty/tx]=[ft,I[fk]];H7fy/fx]=[nil,""];if
(!tkWmpty?);$h7c/tk]||=0;$h7c/tk]+=1;end;when'put';k=cs.shift;$h7c/R[k]||k]-=1;x9=cs.map(&:to_i);P/x]=[c
,k]eK;abort"unknown_command";end;$h7true/'王']Fabort("You_win");$h7LK/'王']Fabort("You_loC\");end;r(ARGV
,true);@s=[];4.timesOx|4.timesOy|caC(P/x/^);when(nil);$h7LK]W@h_keyOk|@s<<['put',kU]<<['put',B[k]U]Xwhen
(LK);caC(P/x/1]);when'歩';if(yS4||P+1/x/^SLK);next;end;@s<<['moZ'UU+1];when'飛';(1\`W@hOn|(y+n>4||P+n/x/
^SLK)Fbreak;@s<<['moZ'UU+n]X(1\`W@hOn|(y-n<0||P-n/x/^SLK)Fbreak;@s<<['moZ'UU-n]X(1\`W@hOn|(J>4||P/x-n/^S
LK)Fbreak;@s<<['moZ'U,J9]X(1\`W@hOn|(x-n<0||P/x-n/^SLK)Fbreak;@s<<['moZ'UQ-n9]Xwhen'金','と';y<4F(-1\1)W
@hOn|(0\`S=JFP+1/J/^!=LKF@s<<['moZ'U,J9+1]X[-1,1]W@hOn|(0\`S=JFP/J/^!=LKF@s<<['moZ'U,J9]Xy>0FP-1/x/^!=LK
F@s<<['moZ'UU-1];when'桂';y<3F[-1,1]W@hOn|(0\`S=JFP+2/J/^!=LKF@s<<['moZ'U,J9+2]Xwhen'銀';y<4F(-1\1)W@hOn
|(0\`S=JFP+1/J/^!=LKF@s<<['moZ'U,J9+1]Xy>0F[-1,1]W@hOn|(0\`S=JFP-1/J]!=LKF@s<<['moZ'U,J9-1]Xwhen'角';(1\
`W@hOn|(y+n>4||J>4||P+n/J/^SLK)Fbreak;@s<<['moZ'U,J9+n]X(1\`W@hOn|(y-n<0||J>4||P-n/J/^SLK)Fbreak;@s<<['m
oZ'U,J9-n]X(1\`W@hOn|(y+n>4||x-n<0||P+n/x-n/^SLK)Fbreak;@s<<['moZ'UQ-n9+n]X(1\`W@hOn|(y-n<0||x-n<0||P-n/
x-n/^SLK)Fbreak;@s<<['moZ'UQ-n9-n]Xwhen'香';(1\`W@hOn|(y+n>4||P+n/x/^SLK)Fbreak;@s<<['moZ'UU+n]Xwhen'王'
;y<4F(-1\1)W@hOn|(0\`S=JFP+1/J/^!=LKF@s<<['moZ'U,J9+1]X[-1,1]W@hOn|(0\`S=JFP/J/^!=LKF@s<<['moZ'U,J9]Xy!=
0F(-1\1)W@hOn|(0\`S=JFP-1/J/^!=LKF@s<<['moZ'U,J9-1]Xend;end}Xr(@s.sample,LK);rs=(0\`.mapOy|"#{_=32.chr}#
{y}#{_}|#{_}#{(0\`.mapOx|t,k=P/x];t.nil??32.chr*3:((t)?'▲':'▽')+k}*"#{_}|#{_}"}#{_}|#{_}"Xr7^+=$h7LK]Wmp
ty?F32.chr*3||$h7LK].mapO(k,v)|k*v}.join;r74]+=$h7true]Wmpty?F32.chr*3||$h7true].mapO(k,v)|k*v}.join;put
s"__END__",l70,2],rs.join(10.chr+l71]+10.chr),l71];~).split*''){|s,c|(t=s.split(c)).join(t.shift)})
__END__
| 0 | 1 | 2 | 3 | 4 |
---+-----+-----+-----+-----+-----+
0 | ▽歩 | ▽金 | ▽王 | ▽銀 | ▽と |
---+-----+-----+-----+-----+-----+
1 | | | | | |
---+-----+-----+-----+-----+-----+
2 | | | | | |
---+-----+-----+-----+-----+-----+
3 | | | | | |
---+-----+-----+-----+-----+-----+
4 | ▲と | ▲銀 | ▲王 | ▲金 | ▲歩 |
---+-----+-----+-----+-----+-----+
一応使い方としてはこんな感じ。
$ ruby ks.rb move 0 4 0 3 > ks2.rb # 0×4の位置にある歩兵を0×3に動かす
$ ruby ks2.rb move 0 3 0 0 > ks3.rb # 0x3の位置にある飛車を0×0に動かす
$ ruby ks3.rb put 歩 2 1 > ks4.rb # 2×1の位置に持駒の歩兵を置く
$ ruby ks4.rb move 2 1 2 0 # 2×1の位置にある歩兵を2×0に動かす
運が良ければこれで勝てる。が、実際はかなりの確率で王が動くか2×1の位置に金か銀が来るので大体無理。
このあとは発表。スネークゲームとかテトリスみたいなの作った人すごいな、と思った。ボクにあそこまでの実装力はない。というかrequireしたら負けだと思ってるので作れない。
ボクの発表はなんというかいい加減で申し訳なかった。京都将棋とかもうちょいちゃんと説明すべきだったと反省している。
加えて、昨日寝ている間に小人が書いてくれたらしい下のプログラムも紹介した。この辺の事情言い忘れた気がする。完全にテンパっていた。
これはJavaScriptのロゴの形をしたJavaScriptのプログラムで、実行するとRubyのロゴの形をしたRubyのプログラムで、それを実行すると元のJavaScriptのコードに、というダブルQuine。
((f=s=>x=>eval(s=(h=s=>s.replace(/\s/g,e=''))(s[0])))=>f)()`
c=String.fromCharCode;q=c(96);s=h(x+'(%w[((f='+f+')=>f)()'+q
+s+q+'])').split(e);g=([n,m,...r])=>n?c(32).repeat(n)+s.spli
ce(0,m).join(e)+g(r):c(10);console.log("P)(%~E$%((/~A+%$.+~<
/%/)'~90&).'%$~61//%&~41&%%4&'~10')%1&)~/0',%.&+~-0&0%,%+~+-
*2%)&,~-%)&%(',%'%.~)%))%.)&%%%/~('%+%8'1~(%%$%*%4(%%0~($%&%
(%1(*&.~'$%(%'%-(0%-~&$%+$%%+(4%+~&$$,')':%)~'.%%'@&'~&0$$%E
%&~)3/8&$~7@''$$".split('~').map(([...l])=>g(l.map(c=>c.char
CodeAt()-35))).join(e))``s=%w[define_method(:x){|x|s="#{x=x*
e}#{c=96.chr}s=%w[#{s*e}];;eval(s*e='');x#{c}/*#{x[0..422]}*
/".chars;("_~"*11+">(-+-~>(*2)~>()(((*~>()(4~>(**1~>(+--~>(/
-)~>(4)(~6&'))'*)(~33'6(~50 +1*~_~_"). split(?~).
map{|l|$><<l.bytes.map{|n|n -35}.ea ch_sli
ce(2).map{|(n,m)|s.shift(n) *e+32. chr*( m||0)}*
e+10.chr};}];;eval(s*e=''); x`/*(( f=s=>x=>eval(s=(h
=s=>s.replace(/\s/g,e=''))( s[0]))) =>f)()`c=Strin
g.fromCharCode;q=c(96);s=h( x+'(%w[( (f='+f+')=
>f)()'+q+s+q+'])').split(e) ;g=([n,m,... r])=>n
?c(32).repeat(n)+s.splice(0 ,m).join(e)+g(r): c(10)
;console.log("P)(%~ E$%( (/~A+% $.+~</% /)'~9
0&).'%$~61//%&~4 1&%% 4&'~1
0')%1&)~/0',%.&+~- 0&0%,%+~ +-*2%)&
,~-%)&%(',%'%.~)%))%.)&%%%/~('%+%8'1~(%%$%*%4(%%0~($%&%(%1(*
&.~'$%(%'%-(0%-~&$%+$%%+(4%+~&$$,')':%)~'.%%'@&'~&0$$%E%&~*/
s=%w[d ef
i ne_me thod(:x){|x|
s="#{x=x * e}#{c=96
.chr}s=%w[#{ s*e}];;eval( s*e=
'');x#{c}/*#{ x[0..4 22]} *
/".chars;("_~" *11+">(-+-~> (*2
)~>()(((*~>()( 4~ >(**1~>(+--~>(/-) ~>(4
)(~6&'))'*)(~ 33'6(~ 50+1*~_~_").sp lit(?~
).map{|l|$><< l.bytes.m ap{|n|n-35} .each_sl
ice(2).map{|( n,m)|s.shift( n)*e+32.c hr*(m||0
)}*e+10.ch r};}];;eval(s*e ='');x (%w[((f=s
=> x=> eval( s=(h=s=>s .rep lace(/\s/g,
e= ''))(s [0])))=>f)( )`c =S tring.fromCh
arCo de;q=c(9 6);s=h(x+'(%w[((f='+f +')=>f)()'+q+s
+q + '])').s plit(e);g=([n,m,. .. r])=>n?c(32).
r epe at(n) +s.splice(0,m) .join(e )+g(r):c(10
) ;cons ole. log("P)(%~ E$%((/~A+%$.+ ~</%/)'~90
& ).'%$~61 // %&~41&%% 4&'~10')%1&)~/0', %.&+~-0&
0 %,%+~+-*2 %)&,~- %)&%(',%'%.~)%))%.)&%%% /~('%+
%8'1~(%%$%* %4 (%%0~($%&%(%1(*&.~'$%(%'%-(0% -~&$
%+$%%+(4%+~&$ $ ,')':%)~'.%%'@&'~&0$$%E%&~)3/8&$~7 @''
$$".split('~').m ap(([...l])=>g(l.map( c
=>c.charCodeAt()-35))).join(e ))`] )
これはRubyのロゴを作るのが大変だった。正確にはロゴっぽく見えるAAを作るのが面倒だった。まあそのおかげでAAを作る知見を得たとも言える。
それとロゴを小さく作りすぎてRubyの方がめいっぱいになってる。一方でJavaScriptはスカスカで、3割くらいはコメントだったりする。しんどい。
実はPython、Haskell、RustのロゴのAAも用意してあって、それらも絡めたかったのだけど、ここまで実装した時点でこの大きさじゃボクの技術では他の言語を組み込むのは不可能だと諦めた。
終わったあとなぜかリュックに入っていた京都将棋を見せびらかしたりして、帰宅した。帰宅途中に電車が止まったりしたので、もっと早く帰るべきだったと反省している。
帰宅してからは異常に疲れていたので寝たりした。帰宅即就寝という感じだった。