2023/02/18
メンテできないコードを
メンテする技術
福岡 Rubyist会議 03 Keynote
ぺん!
@tompng
• ぺん! @tompng ( tomoya ishida )
• CTO at
• 授業支援クラウド
• TRICK(超絶技巧Ruby意味不明コンテスト)
• TRICK2018 5作品入賞 (3位 5位 他)
• TRICK2022 1位 2位 4位
I am a pen
自己紹介
Transcendental Ruby Imbroglio Contest for rubyKaigi
TRICKとは?
• 超絶技巧Ruby意味不明コンテスト https://github.com/tric/
• RubyKaigiの名物コンテンツ(毎回あるわけではない)
• △ 読めないコードを書く
• ◯ 審査員を驚かせる・感動させる・笑わせるコードを書く
• ルール: あなたの作品は審査員に驚き、感動、笑いの全部また
は一部を与えなければならない
Transcendental Ruby Imbroglio Contest for rubyKaigi
TRICKとは?
• 現世利益のない、意味不明な Ruby プログラムを書く。
• Ruby のある種の神秘性(と仕様バグ)を明らかにする。
• Ruby 処理系の意外な堅牢性と移植性を示す。
• メンテナンス不能だが価値あるコードを世に放つことで Ruby の
仕様を安定化する。
TRICKの理念
https://github.com/tric/trick2018/blob/master/README.ja.md
1位、2位、4位
eval((s=%~c=(0..35
).map{s[2*_1+1]}*'';class$Inte
ger;def$quXinclude(Math ;spXo(a)=self*
a.pow(87X=h=32.chr;g=PI/480;ls=(sp*31X,89)%89;
def$abX+'eval((s=%'+(n=? .next)+s*88.chr+[nXs()=[a
=self%X+'.split(',sp*25+'?'+88.chr+');(0..36).mapX89,89-
a].miX{s[2*_1].split}',sp*31+".join.tr('$',$/)))"]*$/)Xn;end
;reqX.split$/;trap(:INT){puts;exit};q=->t,i{a,y=((t+i*99)Xuire
'matrX%960). ivmod(80);[(a*(7+i)+i*23)%79+(y+a)/(5+i%4)%2,39Xix';1
5.tiX-y/2]};p=->t,u{a=->b,c{(0..5).sum{(u%2-1)*E**(t*(b+c*_1)*gXmes{
|i,*X.i+ i*u+=5+sin(u*u))}};x,z=a[5,3]. 5,3].rect;x+=y.Xv|z=
*?!Xi a[19,4];z+=w;r=(4+(x.abs+z.i).ab };t=(0..959).fX..?
W,?Xind{|t|(0..29).all?{x,y=q[t,_1];(x 2||h=ls[y][x]X[,*
?]..X[/[^!-}]/]}};h=($**h+h).chr;eval( []} ->(x,yX?};a
=(0X,a,b){x=x*36+39.5;y=19.5-y*18;b*=1 |i|((yX..1
34)X-b).ceil..y+b).map{|j|((x-i)/a+(y j)/ .times{X.ma
p{zXx,z=p[t,_1];l=u```=0;while``````(l<1)``; u+```=0 ;d=x-y;X.in
dex(Xl+=(d.abs+(z-w``)``.i).ab``s*1.``1 ;x``,z=y``,w;o[v``=``x.r d.imag/Xc[i+
15*Xd.abs*l*sin(2*``l-t``*g*80``-_1) l*(``1-l)/``6,a``=l*( -l)**2*0.X_1]
)};X7,a*2]&&o[v,z,``0.0``3,l**`` ``times``{|i``|(8+i).times{|Xw=*
MatXj|o[sin(i)/2+````` ```sin( `/2.0`````````)*j/200,j*0.0Xrix
[*(X5-1,0.02,0.1]``}} ``. q[t,``_1];m``[y][x]= };i=-X0..
44).X1;$><<(['%%','[H .map{|j|(0..79).map{|k|x=(Xmap{
|i,X -39.5)/35.8;y=( i+=1;m[j][k]?h:c[i]):ls[j]X*b|
v<<X[k];}*''}*$/<<0) 1)%9 te"`")#qv.jSaL{=;q(Q}4fXa.z
ip(Xjs(:#tK`Jm))FKO /A9(2'%iorvf7 eEa0uV xv+Q@qUU](L@&Py .1v'X0..
).suXydSEH{-GI|-5(,z G5evpq,[b50 D[ t {on,I?VStS`?G@LoqFCXm{|j
,k|Xj1.QnxKz!mH%o# )b2Seut,]! 48 lBieJGi 5jeNPD#b}H3X-(p
=(iXaVz#8*+US,hgF 5#6]y-` 4hy HN hF75WjD!0IxJ$sX+k)
.powX+UP"cNUE9- G< tHvV;Ib <-s U T ? vlE xylg=x#X(i+k
,88)XV9u$9lKb9 @C do7+-w >l { v9 { P l ga%]AK<e&'X+1)*
(j||(X4ifK/6S+ k} @@*a} 6rS xn"Q[M 8 `|g>$#BrjXb<<p;
0))}XtbDp'Kc t2 Dat9C s C rL+ g,j]Tf B< eMI+zzkWX;b}]
.lup.XtVP<ak IM E/+)B jwv uB (Twqed D* dyf_dT7Xsolve
(v);13Xn:8 #_ RiSTO, [Fk m O]O#"+ a_ cT_.X5.time
s{c[i+X e5 T`FBEC q*f 2 o@{a<eUG aW PX15*_1]
=z[a[_1]X z_@`nll 7F1 2 [=^uS0z^ 6X||w.shif
t]}};eval(Xfg K#R N bp-E_Xc)~.split
(

?X);(0..36).map{s[2*_1].split
}

.join.tr('$',$/))
)

q=->{!sleep _1/1e2};p=
(

c=0..2).map{[_1/9r ,0,5**_1.i/3,1,0]
}

require'socket';puts'op' "en http://localhost:#{
(

w=TCPServer.new$*[0]||0).addr[1]}";Thread.new{q[2];f=[-1
]*s=3;t=Time.now.to_f;p.select!{0<_1[3]=[_1[3]+_1[4]/8.0,1
].min};9.times{h=p.map{[2**(_1*t.i)/_4**0.5/(1+Math.sin(2*t
-

9*_1%2)**32/16),_2+_4*( _3-_2)]};r=[s*3/2,84].min;g=->{x,y
=

(s*(1+_1+1i)/2).rect;x<0 ||x>=s-1||y<0||y>=s-1?0:((l=f[y+1])
[

x+1]*(a=x%1)+(1-a)*l[x] )*(b=y%1)+(1-b)*((l=f[y])[x+1]*a+(1
-

a)*l[x])};f=(1..r).map {|y|(1..r).map{|x|z=1.5+1.5i-3.0*(
y

.i+x)/r;[h.sum{g[_1.*z +_2]}*0.9,1].min}};s=r};c=f.flatte
n

redo};loop{s=w.accept ; Thread.new{r=s.gets;h='HTTP/1.1 '
+

"200 OKrnContent-" 'T' "ype:text/htmlrnrn";r['/ ']?s
.

<<(h+'<style>ifram' 'e{' 'opacity:0;height:0;}input{wid'
+

'th:252px;}</styl' 'e>' '<form target="i"><input src="'
+

"g#{rand}" type" '="im' 'age"><iframe name="i"></ifra'
+

'me></form>'):r ['/g'] ?(h[/:.+l/]=?:'image/gif';s<
<

h+'GIF8' '7a'+[84
,

84,246,0,*(0..383).map {15*_1. /(383r)**(3-_1
%

3)*17}].pack('v3c*'); loop{ s<<[67434785,5
,

44,84,84,7,c.map{_1* 127} .each_slice(126
).map{[127,128,*_1 ] .pack'c*'}*''
,

1,129].pack('V3x' 'v2na*c2x');q
[

5];q.[]1while(r ==r=c)}):(x,y
,

z=r.scan(/d+/).map{_1.to_f
/

126-1};z&&p<<[rand-0.5,
(

z=x+y.i)*1.5,z/(z
.

abs+0.9),0,-p
[

-3][4]=-1
]

s.<<h);
s

.clos
e

}
}

rule = $*[00]&.to_i || 3
0

h ||= $*[01]&.to_i || 3
2

[ % if % TRICK-2022 %% %
]

# ] if a /%{(?<a>)/ =~ %} if + %} - %
[

# } if b /%<(?<b>)/ =~ %> if - %> + %
{

# > if c /%+(?<c>)/ =~ %+ if + %+ - %
<

# + if d /%-(?<d>)/ =~ %- if - %- + %
+

# - if e f, g, h, / =~ %/ if def e(f, g, h, *)
=

# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.
!

# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)

# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &
&

# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -=
1

eval(File.read f ||= __FILE__
)

水槽(aquarium)
鼓動(heartbeat)
変化(localvar)
https://github.com/tric/trick2022/
git log
タイムライン
水槽
aquarium
鼓動
heartbeat
変化
localvar
提出
提出
提出
• 現世利益のない、意味不明な Ruby プログラムを書く。
• Ruby のある種の神秘性(と仕様バグ)を明らかにする。
• Ruby 処理系の意外な堅牢性と移植性を示す。
• メンテナンス不能だが価値あるコードを世に放つことで Ruby の
仕様を安定化する。
TRICKの理念
本当に?
• Nヵ月前の自分は別人
• 完成させて提出するまではメンテナンスが必要
• 本当にメンテナンス不能なら提出すらできない…かも
メンテナンス可能?不能?
• TRICK入賞したコードの解説・メンテ可能にするための工夫
• 1位 水槽(aquarium) 〜 Best
fi
shbowl
• 2位 鼓動(heartbeat) 〜 Most interactive code
• 4位 変化(localvar) 〜 Most orderly code
• あなたの生活を支えていたTRICK
• おまけ
今日話すこと
水槽(aquarium)
Best
fi
shbowl
eval((s=%~c=(0..35
).map{s[2*_1+1]}*'';class$Inte
ger;def$quXinclude(Math ;spXo(a)=self*
a.pow(87X=h=32.chr;g=PI/480;ls=(sp*31X,89)%89;
def$abX+'eval((s=%'+(n=? .next)+s*88.chr+[nXs()=[a
=self%X+'.split(',sp*25+'?'+88.chr+');(0..36).mapX89,89-
a].miX{s[2*_1].split}',sp*31+".join.tr('$',$/)))"]*$/)Xn;end
;reqX.split$/;trap(:INT){puts;exit};q=->t,i{a,y=((t+i*99)Xuire
'matrX%960). ivmod(80);[(a*(7+i)+i*23)%79+(y+a)/(5+i%4)%2,39Xix';1
5.tiX-y/2]};p=->t,u{a=->b,c{(0..5).sum{(u%2-1)*E**(t*(b+c*_1)*gXmes{
|i,*X.i+ i*u+=5+sin(u*u))}};x,z=a[5,3]. 5,3].rect;x+=y.Xv|z=
*?!Xi a[19,4];z+=w;r=(4+(x.abs+z.i).ab };t=(0..959).fX..?
W,?Xind{|t|(0..29).all?{x,y=q[t,_1];(x 2||h=ls[y][x]X[,*
?]..X[/[^!-}]/]}};h=($**h+h).chr;eval( []} ->(x,yX?};a
=(0X,a,b){x=x*36+39.5;y=19.5-y*18;b*=1 |i|((yX..1
34)X-b).ceil..y+b).map{|j|((x-i)/a+(y j)/ .times{X.ma
p{zXx,z=p[t,_1];l=u```=0;while``````(l<1)``; u+```=0 ;d=x-y;X.in
dex(Xl+=(d.abs+(z-w``)``.i).ab``s*1.``1 ;x``,z=y``,w;o[v``=``x.r d.imag/Xc[i+
15*Xd.abs*l*sin(2*``l-t``*g*80``-_1) l*(``1-l)/``6,a``=l*( -l)**2*0.X_1]
)};X7,a*2]&&o[v,z,``0.0``3,l**`` ``times``{|i``|(8+i).times{|Xw=*
MatXj|o[sin(i)/2+````` ```sin( `/2.0`````````)*j/200,j*0.0Xrix
[*(X5-1,0.02,0.1]``}} ``. q[t,``_1];m``[y][x]= };i=-X0..
44).X1;$><<(['%%','[H .map{|j|(0..79).map{|k|x=(Xmap{
|i,X -39.5)/35.8;y=( i+=1;m[j][k]?h:c[i]):ls[j]X*b|
v<<X[k];}*''}*$/<<0) 1)%9 te"`")#qv.jSaL{=;q(Q}4fXa.z
ip(Xjs(:#tK`Jm))FKO /A9(2'%iorvf7 eEa0uV xv+Q@qUU](L@&Py .1v'X0..
).suXydSEH{-GI|-5(,z G5evpq,[b50 D[ t {on,I?VStS`?G@LoqFCXm{|j
,k|Xj1.QnxKz!mH%o# )b2Seut,]! 48 lBieJGi 5jeNPD#b}H3X-(p
=(iXaVz#8*+US,hgF 5#6]y-` 4hy HN hF75WjD!0IxJ$sX+k)
.powX+UP"cNUE9- G< tHvV;Ib <-s U T ? vlE xylg=x#X(i+k
,88)XV9u$9lKb9 @C do7+-w >l { v9 { P l ga%]AK<e&'X+1)*
(j||(X4ifK/6S+ k} @@*a} 6rS xn"Q[M 8 `|g>$#BrjXb<<p;
0))}XtbDp'Kc t2 Dat9C s C rL+ g,j]Tf B< eMI+zzkWX;b}]
.lup.XtVP<ak IM E/+)B jwv uB (Twqed D* dyf_dT7Xsolve
(v);13Xn:8 #_ RiSTO, [Fk m O]O#"+ a_ cT_.X5.time
s{c[i+X e5 T`FBEC q*f 2 o@{a<eUG aW PX15*_1]
=z[a[_1]X z_@`nll 7F1 2 [=^uS0z^ 6X||w.shif
t]}};eval(Xfg K#R N bp-E_Xc)~.split
(

?X);(0..36).map{s[2*_1].split
}

.join.tr('$',$/)))
• 960フレームのループアニメーション
• どのフレームも実行可能
• アニメーションQuine
Best
fi
shbowl 最高の金魚鉢で賞
水槽(aquarium)
https://gist.github.com/tompng/baf4738e78ee43773b24dabd43fca57c
https://github.com/tric/trick2022/tree/master/01-tompng
• 自分自身のソースコードと全く同じ文字列を出力するプログラム
• 自己言及 自己複製
• プログラマーの嗜みの一つ
Quineとは?
https://ja.wikipedia.org/wiki/クワイン̲(プログラミング)
Quineの例
% cat quine.rb
 

eval s="puts 'eval s='+s.inspect"%
% ruby quine.rb
 

eval s="puts 'eval s='+s.inspect
"

% |
eval s="puts 'eval s='+s.inspect"
ソースコード
出力
Quineの例
s="puts 'eval s='+s.inspect"
eval s
eval s="puts 'eval s='+s.inspect"
puts 'eval s='+s.inspect
「eval s=」+「文字列sを""で囲ったもの」
変数sに代入して、sの中身をrubyのプログラムとして評価
以下のものをputsする
eval s="puts 'eval s='+s.inspect"
• Quineを書きやすい言語
• eval や %() %w() などの記法が充実
• Quine + α で何をするかが大事
• Ruby committer 遠藤さんの本があるよ
Quine friendlyなRuby
RubyとQuine
eval s=%(code)
eval s=%w(c o d e w i t h s p a c e).join
• 動くQuineをそう呼んでる(僕が)
• アニメーションQuineの例
• ターミナル上で映像を見せて、最後にQuineを表示する
• ターミナルに出力するエスケープシーケンスを含めて
Quineになっている
• 全てのフレームが実行可能なコードになっている
Animation Quine
↑僕はこれが好き
Animation Quine
ikacode.rb partyparrot.rb
https://gist.github.com/tompng/f7376a042c211969b7e57002b6017281
https://gist.github.com/tompng/842616aa61b2b2cb6af52b0389ef3799
従来のAnimation Quineの課題
目に優しくない
従来のAnimation Quine
2/7.0)**2+(x-1/6. 0)**2-0.
0

h=0.1*Math.ata n(Math.t an(u
=

2*t+=1)*(1-Ma th.s in(u)* *8
)

t;c=([C,?#,C ]*' ').s ub(
/

=#{t%768}"). ch ars; m=((
1

j|(1..2 *n) .map{ |i|p=(
1

+(1. 5*(2*j-n)/n).i)*Math ::E**(M
a

).i *0. 96;S[p.real *(1.0+h)
+

.0 +h) ]<0? '!':32.chr}
2/7.0)**2+(x-1/6.0)**
2

h=0.1*Math.atan(Math.
t

2*t+=1)*(1-Math.sin(u
)

t;c=([C,?#,C]*'').sub
(

=#{t%768}").chars;m=(
(

j|(1..2*n).map{|i|p=(
1

+(1.5*(2*j-n)/n).i)*M
a

).i *0.96;S[p.real*(1
.

.0 +h)]<0?'!':32.chr}
空白を除去
空白を挿入
文字がずれる
コード
アスキーアート
水槽のQuine
eval((s=%~c=(0..35
).map{s[2*_1+1]}*'';class$Inte
ger;def$quXinclude(Math ;spXo(a)=self*
a.pow(87X=h=32.chr;g=PI/480;ls=(sp*31X,89)%89;
def$abX+'eval((s=%'+(n=? .next)+s*88.chr+[nXs()=[a
=self%X+'.split(',sp*25+'?'+88.chr+');(0..36).mapX89,89-
a].miX{s[2*_1].split}',sp*31+".join.tr('$',$/)))"]*$/)Xn;end
;reqX.split$/;trap(:INT){puts;exit};q=->t,i{a,y=((t+i*99)Xuire
'matrX%960). ivmod(80);[(a*(7+i)+i*23)%79+(y+a)/(5+i%4)%2,39Xix';1
5.tiX-y/2]};p=->t,u{a=->b,c{(0..5).sum{(u%2-1)*E**(t*(b+c*_1)*gXmes{
|i,*X.i+ i*u+=5+sin(u*u))}};x,z=a[5,3]. 5,3].rect;x+=y.Xv|z=
*?!Xi a[19,4];z+=w;r=(4+(x.abs+z.i).ab };t=(0..959).fX..?
W,?Xind{|t|(0..29).all?{x,y=q[t,_1];(x 2||h=ls[y][x]X[,*
?]..X[/[^!-}]/]}};h=($**h+h).chr;eval( []} ->(x,yX?};a
=(0X,a,b){x=x*36+39.5;y=19.5-y*18;b*=1 |i|((yX..1
34)X-b).ceil..y+b).map{|j|((x-i)/a+(y j)/ .times{X.ma
p{zXx,z=p[t,_1];l=u```=0;while``````(l<1)``; u+```=0 ;d=x-y;X.in
dex(Xl+=(d.abs+(z-w``)``.i).ab``s*1.``1 ;x``,z=y``,w;o[v``=``x.r d.imag/Xc[i+
15*Xd.abs*l*sin(2*``l-t``*g*80``-_1) l*(``1-l)/``6,a``=l*( -l)**2*0.X_1]
)};X7,a*2]&&o[v,z,``0.0``3,l**`` ``times``{|i``|(8+i).times{|Xw=*
MatXj|o[sin(i)/2+````` ```sin( `/2.0`````````)*j/200,j*0.0Xrix
[*(X5-1,0.02,0.1]``}} ``. q[t,``_1];m``[y][x]= };i=-X0..
44).X1;$><<(['%%','[H .map{|j|(0..79).map{|k|x=(Xmap{
|i,X -39.5)/35.8;y=( i+=1;m[j][k]?h:c[i]):ls[j]X*b|
v<<X[k];}*''}*$/<<0) 1)%9 te"`")#qv.jSaL{=;q(Q}4fXa.z
ip(Xjs(:#tK`Jm))FKO /A9(2'%iorvf7 eEa0uV xv+Q@qUU](L@&Py .1v'X0..
).suXydSEH{-GI|-5(,z G5evpq,[b50 D[ t {on,I?VStS`?G@LoqFCXm{|j
,k|Xj1.QnxKz!mH%o# )b2Seut,]! 48 lBieJGi 5jeNPD#b}H3X-(p
=(iXaVz#8*+US,hgF 5#6]y-` 4hy HN hF75WjD!0IxJ$sX+k)
.powX+UP"cNUE9- G< tHvV;Ib <-s U T ? vlE xylg=x#X(i+k
,88)XV9u$9lKb9 @C do7+-w >l { v9 { P l ga%]AK<e&'X+1)*
(j||(X4ifK/6S+ k} @@*a} 6rS xn"Q[M 8 `|g>$#BrjXb<<p;
0))}XtbDp'Kc t2 Dat9C s C rL+ g,j]Tf B< eMI+zzkWX;b}]
.lup.XtVP<ak IM E/+)B jwv uB (Twqed D* dyf_dT7Xsolve
(v);13Xn:8 #_ RiSTO, [Fk m O]O#"+ a_ cT_.X5.time
s{c[i+X e5 T`FBEC q*f 2 o@{a<eUG aW PX15*_1]
=z[a[_1]X z_@`nll 7F1 2 [=^uS0z^ 6X||w.shif
t]}};eval(Xfg K#R N bp-E_Xc)~.split
(

?X);(0..36).map{s[2*_1].split
}

.join.tr('$',$/)))
コードを空白に
置き換えると
目に優しいが
データが消失してしまう
水槽のQuine
eval((s=%~c=(0..35
).map{s[2*_1+1]}*'';class$Inte
ger;def$quXinclude(Math ;spXo(a)=self*
a.pow(87X=h=32.chr;g=PI/480;ls=(sp*31X,89)%89;
def$abX+'eval((s=%'+(n=? .next)+s*88.chr+[nXs()=[a
=self%X+'.split(',sp*25+'?'+88.chr+');(0..36).mapX89,89-
a].miX{s[2*_1].split}',sp*31+".join.tr('$',$/)))"]*$/)Xn;end
;reqX.split$/;trap(:INT){puts;exit};q=->t,i{a,y=((t+i*99)Xuire
'matrX%960). ivmod(80);[(a*(7+i)+i*23)%79+(y+a)/(5+i%4)%2,39Xix';1
5.tiX-y/2]};p=->t,u{a=->b,c{(0..5).sum{(u%2-1)*E**(t*(b+c*_1)*gXmes{
|i,*X.i+ i*u+=5+sin(u*u))}};x,z=a[5,3]. 5,3].rect;x+=y.Xv|z=
*?!Xi a[19,4];z+=w;r=(4+(x.abs+z.i).ab };t=(0..959).fX..?
W,?Xind{|t|(0..29).all?{x,y=q[t,_1];(x 2||h=ls[y][x]X[,*
?]..X[/[^!-}]/]}};h=($**h+h).chr;eval( []} ->(x,yX?};a
=(0X,a,b){x=x*36+39.5;y=19.5-y*18;b*=1 |i|((yX..1
34)X-b).ceil..y+b).map{|j|((x-i)/a+(y j)/ .times{X.ma
p{zXx,z=p[t,_1];l=u```=0;while``````(l<1)``; u+```=0 ;d=x-y;X.in
dex(Xl+=(d.abs+(z-w``)``.i).ab``s*1.``1 ;x``,z=y``,w;o[v``=``x.r d.imag/Xc[i+
15*Xd.abs*l*sin(2*``l-t``*g*80``-_1) l*(``1-l)/``6,a``=l*( -l)**2*0.X_1]
)};X7,a*2]&&o[v,z,``0.0``3,l**`` ``times``{|i``|(8+i).times{|Xw=*
MatXj|o[sin(i)/2+````` ```sin( `/2.0`````````)*j/200,j*0.0Xrix
[*(X5-1,0.02,0.1]``}} ``. q[t,``_1];m``[y][x]= };i=-X0..
44).X1;$><<(['%%','[H .map{|j|(0..79).map{|k|x=(Xmap{
|i,X -39.5)/35.8;y=( i+=1;m[j][k]?h:c[i]):ls[j]X*b|
v<<X[k];}*''}*$/<<0) 1)%9 te"`")#qv.jSaL{=;q(Q}4fXa.z
ip(Xjs(:#tK`Jm))FKO /A9(2'%iorvf7 eEa0uV xv+Q@qUU](L@&Py .1v'X0..
).suXydSEH{-GI|-5(,z G5evpq,[b50 D[ t {on,I?VStS`?G@LoqFCXm{|j
,k|Xj1.QnxKz!mH%o# )b2Seut,]! 48 lBieJGi 5jeNPD#b}H3X-(p
=(iXaVz#8*+US,hgF 5#6]y-` 4hy HN hF75WjD!0IxJ$sX+k)
.powX+UP"cNUE9- G< tHvV;Ib <-s U T ? vlE xylg=x#X(i+k
,88)XV9u$9lKb9 @C do7+-w >l { v9 { P l ga%]AK<e&'X+1)*
(j||(X4ifK/6S+ k} @@*a} 6rS xn"Q[M 8 `|g>$#BrjXb<<p;
0))}XtbDp'Kc t2 Dat9C s C rL+ g,j]Tf B< eMI+zzkWX;b}]
.lup.XtVP<ak IM E/+)B jwv uB (Twqed D* dyf_dT7Xsolve
(v);13Xn:8 #_ RiSTO, [Fk m O]O#"+ a_ cT_.X5.time
s{c[i+X e5 T`FBEC q*f 2 o@{a<eUG aW PX15*_1]
=z[a[_1]X z_@`nll 7F1 2 [=^uS0z^ 6X||w.shif
t]}};eval(Xfg K#R N bp-E_Xc)~.split
(

?X);(0..36).map{s[2*_1].split
}

.join.tr('$',$/)))
消えたデータは復元できるようにすれば良い
誤り訂正符号
水槽のQuine with 誤り訂正符号
The Making of
水槽(aquarium)
線香花火Quine
TRICKのための練習作1
(誤り訂正符号 リードソロモン符号)
https://github.com/tompng/hanabi̲quine/
schoolTakt(会社のサービス)のロゴQuine
TRICKのための練習作2
(誤り訂正符号 オリジナル)
https://gist.github.com/tompng/1dc31390281df4746780
ff
570060d45b
• リードソロモン符号
• QRコードなどに使われてる
• 非常に優秀
• Quineに使うにはコードが長い
• オリジナル
• 欠点だらけ(速度が遅い、など)
• だがコードが短い
誤り訂正符号
誤り訂正符号
rs=->s,b=[1],*bi{63.times{|i|j=b[i]<<1;j>63&&j^=67;bi[b[i+1]=j]=62-i};ml=->i,j,k=0{6.times{k^=i[_1]
*

j<<_1};(k.bit_length-1).downto(6){k[_1]==1&&k^=67<<(_1-6)};k};ix=[];cd=s.chars.map.with_index{_1.or
d

<127?_1.upcase.ord-32:(ix<<_2;0)};v=[];m=(0...n=ix.size).map{|i|j=0;cd.each_with_index{j^=ml[_1,b[i
*

_2%63]]};v<<j;ix.map{b[i*_1%63]}};(n-1).times{|i|k=b[bi[m[i][i]]];(l=i+1...n).each{|j|a=ml[m[j][i],
k

];l.each{m[j][_1]^=ml[a,m[i][_1]]};v[j]^=ml[a,v[i]]}};(n-1).downto(0){|i|j=b[bi[m[i][i]]];i.times{k
=

ml[m[_1][i],j];v[_1]^=ml[v[i],k]};v[i]=ml[v[i],j]};v.zip(ix){s[_2]=(_1+32).chr.downcase}};80.times{
|

i|c=->{(i+_1*29)%80};m='"#!'+92.chr;rs[s=(r=0..38).map{$z[38-_1][c[_1]+10].tr('{|}~',m)}*''];s.tr!m
,

'{|}~';r.each{$z[38-_1][c[_1]+10]=s[_1]}};eval$z[0,27].map{_1[10,80].split}*''
c=(0..35).map{s[2*_1+1]}*'';class Integer;def quo(a)=self*a.pow(87,89)%89;def abs()=[a=self%89,89-a
]

.min;end;require'matrix';15.times{|i,*v|z=*?!..?W,?[,*?]..?};a=(0..134).map{z.index(c[i+15*_1])};w=
*

Matrix[*(0..44).map{|i,*b|v<<a.zip(0..).sum{|j,k|-(p=(i+k).pow(i+k,88)+1)*(j||(b<<p;0))};b}].lup.so
l

ve(v);135.times{c[i+15*_1]=z[a[_1]||w.shift]}}
線香花火(リードソロモン符号)の誤り訂正コード
(誤り検出機能は無し 非ascii箇所が誤り) 778文字
水槽の誤り訂正コード 346文字
適当な行列を用意して
誤り訂正符号
行列
要素は乱数っぽければ何でもいい
ゼ
ロ
ベ
ク
ト
ル
=
(mod 89)
R
U
B
Y
の
コ
|
ド
?
?
?
?
?
?
?
?
?
?
?
?
等式を満たすように冗長データを作る
こうして作った誤り訂正できるデータを
水槽内に15セットばらばらに配置
誤り訂正符号
行列
要素は乱数っぽければ何でもいい
R
U
B
Y
の
コ
|
ド
と
誤
り
訂
正
用
の
冗
長
デ
|
タ
ゼ
ロ
ベ
ク
ト
ル
=
(mod 89)
誤り箇所に対応する行列の成分を抜き出し R
U
B
Y
の
?
?
ド
と
誤
り
?
正
用
?
冗
長
デ
|
タ
誤り訂正符号
行列
要素は乱数っぽければ何でもいい
=
(mod 89)
ゼ
ロ
ベ
ク
ト
ル
?
?
?
?
• mod 89 の演算で連立方程式を解くと誤り訂正できる
• matrixライブラリを使う
• Integerにモンキーパッチ
• Integer#quo
• mod89での除算
• Integer#abs
• ピボット選択を誤魔化して
ゼロ除算を回避する
• 89は使える文字種(文字コード32〜126)の個数以下の最大の素数
誤り訂正符号
ベ
ク
ト
ル
=
行列
(mod 89)
• 誤り訂正をもっとも生かせる演出はなんだろう
• 海好きだし球体の水槽で泳ぐ魚にしよう(実は金魚ではなかった)
題材決め
• 水槽の枠の半径と太さ
• 誤り訂正コードが
綺麗に収まるサイズ
• TRICKの最大文字数
のレギュレーション
サイズ決め
eval((s=%~c=(0..35
).map{s[2*_1+1]}*'';class$Inte
ger;def$quX Xo(a)=self*
a.pow(87X X,89)%89;
def$abX Xs()=[a
=self%X X89,89-
a].miX Xn;end
;reqX Xuire
'matrX Xix';1
5.tiX Xmes{
|i,*X Xv|z=
*?!X X..?
W,?X X[,*
?]..X X?};a
=(0X X..1
34)X X.ma
p{zX X.in
dex(X Xc[i+
15*X X_1]
)};X Xw=*
MatX Xrix
[*(X X0..
44).X Xmap{
|i,X X*b|
v<<X Xa.z
ip(X X0..
).suX Xm{|j
,k|X X-(p
=(iX X+k)
.powX X(i+k
,88)X X+1)*
(j||(X Xb<<p;
0))}X X;b}]
.lup.X Xsolve
(v);13X X5.time
s{c[i+X X15*_1]
=z[a[_1]X X||w.shif
t]}};eval(X Xc)~.split
(

?X);(0..36).map{s[2*_1].split
}

.join.tr('$',$/))
)
• canvas代わりの二次元配列に描画
• 楕円を描く描画命令を作る
• 楕円は万能
• 魚も描ける
• 海藻も描ける
• 直線や多角形の描画命令は作らない(コード量が増える)
魚の描画処理を書く
draw_oval(x, y, w, h); canva
s

=
>

[[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
,

[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]
,

[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
,

[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
,

[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
,

[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
,

[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]
,

[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]]
• 魚の泳ぐ軌道は sin cos を複数組み合わせた計算式で決める
魚・海藻の描画
軌道
魚の頭
魚の尾
魚の体長分、軌道を遡った位置
• 魚の泳ぐ軌道は sin cos を複数組み合わせた計算式で決める
• 楕円で魚の体を描画する 体の厚みを表現できる
魚・海藻の描画
実際は魚が体をくねらせる動きも加えています
• 海藻も楕円で描画
• 楕円は万能
魚・海藻の描画
• 誤り訂正するコードを生成
• 埋め込むコードに誤り訂正符号を付加
• 配置して完成…?
埋め込んで完成?
code = File.read('embed_code.rb') + '#'
correction_code = generate_correction_code(parameters
)

code = add_redundant_code(code, parameters
)

File.write 'output.rb', format_aquarium(correction_code, code)
• 復元のための冗長データが少なくなる
• 演出に制限(魚や海藻などを描画できる面積)
• コードゴルフ(コードを短くする)
• 空白を消す・インデントも消す・変数名短くする・その他
• コードゴルフをすると可読性が悪くなる メンテできなくなる
コードが長すぎる
コードゴルフとメンテの両立
width = 80
cx = width / 2
width = 80
# CODE_START
cx = EMBED[width / 2]
cx = 40
このコードは長い
マクロのようなものを導入
最終成果物は短い
コードゴルフとメンテの両立
t=(0..EMBED[FRAMES-1]).find{|t
|

(0..EMBED[BUBBLES-1]).all?
{

x,y=q[t,_1]
;

(x+y*2i-40-40i).abs>32||h=ls[y][x][/[^!-}]/
]

}

}
;

h=($**h+h).chr
;

EMBED_RAW['eval(%!'
]

loop
{

m = (0..39).map{[]}
;

o = ->(x, y, a, b)
{

x=x*36+39.5
;

y=19.5-y*18
;

b*=18
;

((x-a*=36).ceil..x+a).map{|i
|

((y-b).ceil..y+b).map{|j
|

((x-i)/a+(y-j)/b.i).abs<1&&m[j][i]=1
}

埋め込む水槽描画処理
ギリギリ読める?範囲で
コードを短く短く短くする
EMBED[FRAMES-1]
コードゴルフとメンテの両立
code = code.gsub(/# .+/, '').delete(" n").gsub(/EMBED[[^[]]+]/)
{

embed_encode eval _1[/[[^[]]+]/][1...-1
]

}.gsub(/EMBED_RAW[[^[]]+]/)
{

eval _1[/[[^[]]+]/][1...-1
]

}

aa = <<AA
*** ****** ** ** ***
** ** ** ** ** ** ** **
** ** ** ** ** ** ** **
** ** ** * ** ** ** ** **
********* ** *** ** ** *********
** ** ***** ** ****** ** **
AA
aa_inserts = [0, 70, 141, 213, 285, 357].map{_1+740
}

aa.split($/).zip(aa_inserts).each do |line, idx
|

line.chars.each.with_index(idx) do |c, i
|

next if c == ' '
正規表現でマクロ展開
AQUAの文字を入れる
ジェネレーター
# rubocop:disable all
# FRAMES = 960
# BUBBLES = 30
include(Math)
;

sp = h = 32.chr
;

g = PI/EMBED[FRAMES/2]
;

ls = (EMBED_RAW[head_code2]+s*88.chr+EMBED_RAW[tail_code2]).split$/
;

trap(:INT){puts;exit}
;

q = ->t,i{a,y=((t+i*99)%EMBED[FRAMES]).divmod(80);[(a*(7+i)+i*23)%79+(y+a)/(5+i%4)%2,39-y/2]}
;

p = ->t,u
{

a=->b,c{(0..5).sum{(u%2-1)*E**(t*(b+c*_1)*g.i+6i*u+=5+sin(u*u))}}
;

x,z = a[5,3].rect
;

y,w = a[5,3].rect
;

x+=y.i+a[19,4]
;

z+=w
;

r=(4+(x.abs+z.i).abs)/1.5
;

[x/r, z/r
]

}
;

t=(0..EMBED[FRAMES-1]).find{|t
|

(0..EMBED[BUBBLES-1]).all?
{

x,y=q[t,_1]
;

(x+y*2i-40-40i).abs>32||h=ls[y][x][/[^!-}]/
]

}

}
;

h=($**h+h).chr
;

EMBED_RAW['eval(%!'
]

loop
{

m = (0..39).map{[]}
;

o = ->(x, y, a, b)
{

x=x*36+39.5
;

y=19.5-y*18
;

b*=18
;

((x-a*=36).ceil..x+a).map{|i
|

((y-b).ceil..y+b).map{|j
|

((x-i)/a+(y-j)/b.i).abs<1&&m[j][i]=1
}

}

}
;

3.times
{

x,z = p[t, _1]
;

l=u=0
;

while(l<1)
;

y,w=p[t-u+=0.2, _1]
;

d=x-y
;

l+=(d.abs+(z-w).i).abs*1.15
;

x,z=y,w
;

o[v=x.real+d.imag/d.abs*l*sin(2*l-t*g*80-_1)/8, z+l*(1-l)/6, a=l*(1-l)**2*0.7, a*2]&&o[v, z, 0.03, l**4/6
]

end
}
;

8.times{|i
|

(8+i).times{|j
|

o[sin(i)/2+sin(-t*g*20+i+j/2.0)*j/200,j*0.05-1,0.02,0.1
]

}

}
;

EMBED[BUBBLES].times{x,y=q[t,_1];m[y][x]=1}
;

i=-1
;

$><<(['%%','[H','[J%;'<<13]*27.chr+(0..39).map{|j
|

(0..79).map{|k
|

x=(k-39.5)/EMBED[40*0.895]
;

y=(j-19.5)/EMBED[20*0.895]
;

(1>r=x*x+y*y)? (i+=1;m[j][k] ? h : c[i]) : ls[j][k]
;

}*''
}*$/<<0)
;

t=(t+1)%EMBED[FRAMES]
;

sleep(0.05
)

}

EMBED_RAW['!.delete"`")']
EMBED_RAW['eval(%!']
EMBED_RAW['!.delete"`")']
任意の場所に
"̀" を挿入できる
(AQUAの装飾)
完成...?
eval((s=%~c=(0..35
).map{s[2*_1+1]}*'';class$Inte
ger;def$quXinclude(Math ;spXo(a)=self*
a.pow(87X=h=32.chr;g=PI/480;ls=(sp*31X,89)%89;
def$abX+'eval((s=%'+(n=? .next)+s*88.chr+[nXs()=[a
=self%X+'.split(',sp*25+'?'+88.chr+');(0..36).mapX89,89-
a].miX{s[2*_1].split}',sp*31+".join.tr('$',$/)))"]*$/)Xn;end
;reqX.split$/;trap(:INT){puts;exit};q=->t,i{a,y=((t+i*99)Xuire
'matrX%960). ivmod(80);[(a*(7+i)+i*23)%79+(y+a)/(5+i%4)%2,39Xix';1
5.tiX-y/2]};p=->t,u{a=->b,c{(0..5).sum{(u%2-1)*E**(t*(b+c*_1)*gXmes{
|i,*X.i+ i*u+=5+sin(u*u))}};x,z=a[5,3]. 5,3].rect;x+=y.Xv|z=
*?!Xi a[19,4];z+=w;r=(4+(x.abs+z.i).ab };t=(0..959).fX..?
W,?Xind{|t|(0..29).all?{x,y=q[t,_1];(x 2||h=ls[y][x]X[,*
?]..X[/[^!-}]/]}};h=($**h+h).chr;eval( []} ->(x,yX?};a
=(0X,a,b){x=x*36+39.5;y=19.5-y*18;b*=1 |i|((yX..1
34)X-b).ceil..y+b).map{|j|((x-i)/a+(y j)/ .times{X.ma
p{zXx,z=p[t,_1];l=u```=0;while``````(l<1)``; u+```=0 ;d=x-y;X.in
dex(Xl+=(d.abs+(z-w``)``.i).ab``s*1.``1 ;x``,z=y``,w;o[v``=``x.r d.imag/Xc[i+
15*Xd.abs*l*sin(2*``l-t``*g*80``-_1) l*(``1-l)/``6,a``=l*( -l)**2*0.X_1]
)};X7,a*2]&&o[v,z,``0.0``3,l**`` ``times``{|i``|(8+i).times{|Xw=*
MatXj|o[sin(i)/2+````` ```sin( `/2.0`````````)*j/200,j*0.0Xrix
[*(X5-1,0.02,0.1]``}} ``. q[t,``_1];m``[y][x]= };i=-X0..
44).X1;$><<(['%%','[H .map{|j|(0..79).map{|k|x=(Xmap{
|i,X -39.5)/35.8;y=( i+=1;m[j][k]?h:c[i]):ls[j]X*b|
v<<X[k];}*''}*$/<<0) 1)%9 te"`")#qv.jSaL{=;q(Q}4fXa.z
ip(Xjs(:#tK`Jm))FKO /A9(2'%iorvf7 eEa0uV xv+Q@qUU](L@&Py .1v'X0..
).suXydSEH{-GI|-5(,z G5evpq,[b50 D[ t {on,I?VStS`?G@LoqFCXm{|j
,k|Xj1.QnxKz!mH%o# )b2Seut,]! 48 lBieJGi 5jeNPD#b}H3X-(p
=(iXaVz#8*+US,hgF 5#6]y-` 4hy HN hF75WjD!0IxJ$sX+k)
.powX+UP"cNUE9- G< tHvV;Ib <-s U T ? vlE xylg=x#X(i+k
,88)XV9u$9lKb9 @C do7+-w >l { v9 { P l ga%]AK<e&'X+1)*
(j||(X4ifK/6S+ k} @@*a} 6rS xn"Q[M 8 `|g>$#BrjXb<<p;
0))}XtbDp'Kc t2 Dat9C s C rL+ g,j]Tf B< eMI+zzkWX;b}]
.lup.XtVP<ak IM E/+)B jwv uB (Twqed D* dyf_dT7Xsolve
(v);13Xn:8 #_ RiSTO, [Fk m O]O#"+ a_ cT_.X5.time
s{c[i+X e5 T`FBEC q*f 2 o@{a<eUG aW PX15*_1]
=z[a[_1]X z_@`nll 7F1 2 [=^uS0z^ 6X||w.shif
t]}};eval(Xfg K#R N bp-E_Xc)~.split
(

?X);(0..36).map{s[2*_1].split
}

.join.tr('$',$/)))
• 全てのフレームが復元可能なのか
• 復元できない(空白が多すぎる)なら魚の大きさを小さくする
• 冗長化データの中に #{ #$ #@ が含まれてはいけない
• 全てのフレームでフレーム番号を特定できるか
• 泡の位置を使ってフレーム番号を特定している
• 泡があるはずの場所が空白か(泡・魚・海藻の区別はつかない)
• 誤判定してしまうフレームがないよう十分な数の泡を用意
本当に動くのか?
• 速いテスト(10 frame / sec)
• 各フレームを誤り訂正関数に渡して復元できるか
• フレーム番号が正しく計算されているか
• 遅い厳密なテスト(0.5 frame / sec)
• IO.popen('ruby', '-e', frames[i]
)

• あまりに遅いのでランダムサンプリング
• 提出前には全960フレームで最終テスト
• あとで見返せるように全フレームをtxtファイルに保存・動画化
速いテストと厳密な遅いテスト
テストコード
• 見つかる不具合
• 魚の体が途切れてるフレーム発見
• 泡の動きが規則的すぎる
• 海藻の波打つ方向が逆
• 直して、テスト走らせて、数日寝かす
• 変なところがしばらく見つからなくなったところで提出
水槽のメンテナンス
完成
後日談
https://twitter.com/Yubeshi/status/1567880244459339778
• コンテスト終了後に機種依存が発覚
• 浮動小数点誤差
• seed固定の乱数の代わりとして適当な小数の計算を使っていた
突然のメンテナンス
後日談
# utility function
- def param(a,b)=((a+b)**9.1).to_i % (MOD-1)+1
+ def param(a,b)=(a+b).pow(a+b,MOD-1)+1
# actual embedded code
- v<<a.zip(0..).sum{-(p=((i+_2)**9.1).to_i%88+1)*(_1||(b<<p;0))};b
+ v<<a.zip(0..).sum{|j,k|-(p=(i+k).pow(i+k,88)+1)*(j||(b<<p;0))};b
メンテ可能にしていてよかった
鼓動(heartbeat)
Most interactive code
q=->{!sleep _1/1e2};p=
(

c=0..2).map{[_1/9r ,0,5**_1.i/3,1,0]
}

require'socket';puts'op' "en http://localhost:#{
(

w=TCPServer.new$*[0]||0).addr[1]}";Thread.new{q[2];f=[-1
]*s=3;t=Time.now.to_f;p.select!{0<_1[3]=[_1[3]+_1[4]/8.0,1
].min};9.times{h=p.map{[2**(_1*t.i)/_4**0.5/(1+Math.sin(2*t
-

9*_1%2)**32/16),_2+_4*( _3-_2)]};r=[s*3/2,84].min;g=->{x,y
=

(s*(1+_1+1i)/2).rect;x<0 ||x>=s-1||y<0||y>=s-1?0:((l=f[y+1])
[

x+1]*(a=x%1)+(1-a)*l[x] )*(b=y%1)+(1-b)*((l=f[y])[x+1]*a+(1
-

a)*l[x])};f=(1..r).map {|y|(1..r).map{|x|z=1.5+1.5i-3.0*(
y

.i+x)/r;[h.sum{g[_1.*z +_2]}*0.9,1].min}};s=r};c=f.flatte
n

redo};loop{s=w.accept ; Thread.new{r=s.gets;h='HTTP/1.1 '
+

"200 OKrnContent-" 'T' "ype:text/htmlrnrn";r['/ ']?s
.

<<(h+'<style>ifram' 'e{' 'opacity:0;height:0;}input{wid'
+

'th:252px;}</styl' 'e>' '<form target="i"><input src="'
+

"g#{rand}" type" '="im' 'age"><iframe name="i"></ifra'
+

'me></form>'):r ['/g'] ?(h[/:.+l/]=?:'image/gif';s<
<

h+'GIF8' '7a'+[84
,

84,246,0,*(0..383).map {15*_1. /(383r)**(3-_1
%

3)*17}].pack('v3c*'); loop{ s<<[67434785,5
,

44,84,84,7,c.map{_1* 127} .each_slice(126
).map{[127,128,*_1 ] .pack'c*'}*''
,

1,129].pack('V3x' 'v2na*c2x');q
[

5];q.[]1while(r ==r=c)}):(x,y
,

z=r.scan(/d+/).map{_1.to_f
/

126-1};z&&p<<[rand-0.5,
(

z=x+y.i)*1.5,z/(z
.

abs+0.9),0,-p
[

-3][4]=-1
]

s.<<h);
s

.clos
e

}}
https://github.com/tric/trick2022/tree/master/02-tompng
https://gist.github.com/tompng/edfa53e6a504791b1cd941b667b58179
鼓動(heartbeat)
• Webサーバー 複数ウィンドウ同期
•WebSocket?
•Canvas?
•JavaScript?
TRICK
• <input type="image">
通常はクリックすると /action?x=12&y=34 へページ遷移
• <form target="iframe-name">
iframe内で開くことでページ遷移させない Ajaxもどき
• <input type="image" src="streaming-gif-url">
gifアニメーションのstreaming
TRICK
<style>
iframe{opacity:0;height:0;}input{width:252px;}
</style>
<form target="i">
<input src="g0.06344007482554526" type="image">
<iframe name="i"></iframe>
</form>
• アスキーアート化
• フラクタルの生成
• 反復関数系
• 徐々に解像度を上げることで高速化
• 小ネタ
• -1 を [1,1,1,1,1] の代わりに使う
• 読みにくいSyntax
• GIFを生成するコード量が少なくて済む画像サイズ・色数の決定
• 圧縮フォーマットを無圧縮画像かのように楽に生成したい
鼓動 こだわりポイント
←今日はこれだけ
1while ?a'bc'
• %w() 記法が便利
• %w().joi
n

• %w()*'
'

• どこにでも空白や改行を挿入可能
• アスキーアート化が簡単
%w(hello world) #=> ["hello", "world"]
Rubyのコードとアスキーアート
eval(%w(
puts 'Hap
py' +32
.ch r+'
Hack ing!
').join
)

Happy Hacking!
色合い
%w@t=96;S=->x,y{(20*
*(x+y-2)+20**(x-y-2)+2.7**(-x-0.5-1
.2*(2.5+(1.2-Math.cos(15*y))**0.25)/(5+(5
*y/3+5*x*y/9)**16)/(1+Math.exp(4*x)))-0.67)
*(((y*y+4e-3)**0.5-0.33)**2+((11*x-1)/12)**2
-1/7.0)*((y.abs-4/13.0)**2+(x-1/8.0)**2-0.11
)*((y.abs-2/7.0)**2+(x-1/6. 0)**2-0.0
3)};r=->n{h=0.1*Math.ata n(Math.t an(u=
Math::PI/12*t+=1)*(1-Ma th.s in(u)* *8)
);q=??.next;c=([C,?#,C ]*' ').s ub(/
=[0-9]+/,"=#{t%768}"). ch ars; m=((1
..n).map{|j|(1..2 *n) .map{ |i|p=(1
.5*(i-n)/n+(1. 5*(2*j-n)/n).i)*Math ::E**(Math
::PI*t/128).i *0. 96;S[p.real *(1.0+h)+0.3,
p.imag/(1.0 +h) ]<0? '!':32.chr}*''}*$
/).sub(/.![ ^!]| !!!/ ,'%w'+q).match(/^(.+!.+
)!{11}([^!] *)!{9 }(.+) !{15}([^!]*)!{13}(.+!.
+)!([^!]+)$ /m);m[1..6] .zip([q+'.tap{|c|C
=','c};C<<%w '+q,q+' ;at_exit{eval(',"C*
'')};C<<%w"+q, q]).join.gsub(?!){c.
shift}};loop{$><<"#{27.chr}[1;1H"+r[3
2];sleep (0@.tap{|c|C=c};C<<%w@
.05)}@;at_exit{eval
(

C*'')};C<<%w@#t=
0;S=->x,y{
(20**(x
+y-@
q=->{!sleep _1/1e2};p=
(

c=0..2).map{[_1/9r ,0,5**_1.i/3,1,0]
}

require'socket';puts'op' "en http://localhost:#{
(

w=TCPServer.new$*[0]||0).addr[1]}";Thread.new{q[2];f=[-1
]*s=3;t=Time.now.to_f;p.select!{0<_1[3]=[_1[3]+_1[4]/8.0,1
].min};9.times{h=p.map{[2**(_1*t.i)/_4**0.5/(1+Math.sin(2*t
-

9*_1%2)**32/16),_2+_4*( _3-_2)]};r=[s*3/2,84].min;g=->{x,y
=

(s*(1+_1+1i)/2).rect;x<0 ||x>=s-1||y<0||y>=s-1?0:((l=f[y+1])
[

x+1]*(a=x%1)+(1-a)*l[x] )*(b=y%1)+(1-b)*((l=f[y])[x+1]*a+(1
-

a)*l[x])};f=(1..r).map {|y|(1..r).map{|x|z=1.5+1.5i-3.0*(
y

.i+x)/r;[h.sum{g[_1.*z +_2]}*0.9,1].min}};s=r};c=f.flatte
n

redo};loop{s=w.accept ; Thread.new{r=s.gets;h='HTTP/1.1 '
+

"200 OKrnContent-" 'T' "ype:text/htmlrnrn";r['/ ']?s
.

<<(h+'<style>ifram' 'e{' 'opacity:0;height:0;}input{wid'
+

'th:252px;}</styl' 'e>' '<form target="i"><input src="'
+

"g#{rand}" type" '="im' 'age"><iframe name="i"></ifra'
+

'me></form>'):r ['/g'] ?(h[/:.+l/]=?:'image/gif';s<
<

h+'GIF8' '7a'+[84
,

84,246,0,*(0..383).map {15*_1. /(383r)**(3-_1
%

3)*17}].pack('v3c*'); loop{ s<<[67434785,5
,

44,84,84,7,c.map{_1* 127} .each_slice(126
).map{[127,128,*_1 ] .pack'c*'}*''
,

1,129].pack('V3x' 'v2na*c2x');q
[

5];q.[]1while(r ==r=c)}):(x,y
,

z=r.scan(/d+/).map{_1.to_f
/

126-1};z&&p<<[rand-0.5,
(

z=x+y.i)*1.5,z/(z
.

abs+0.9),0,-p
[

-3][4]=-1
]

s.<<h);
s

.clos
e

}}
%w使用 ほぼ単色 %w不使用 鮮やか
• Quineなら是非使いたい
• 使わないと難しい(できる表現が限られてしまう)
• Quineでないなら使わなくていい
• Syntax Highlightでつけた色が綺麗になる
• せっかくなので色鮮やかにしたい
%w()記法
• Syntax Error
• Unde
fi
ned method ch
• Unde
fi
ned method r
syntax error, unexpected syntax error, expected non syntax error (Syntax Error)
Syntax Error
puts"e[
1m"+ 'Hap
py' +32
.ch r+'
Hack ing!
'+"e[m"
Syntax Error!
def make
コードゴルフ(手動
)

loop do
コードゴルフ(改行除去
)

アスキーアートに整形
文法チェック
手動で修正
end
end
作り方
フォーマッターにより自動化
• 1文字変数の方が空白や改行を入れやすくなる
• インデント・空白・改行は消す?
• メンテしにくくなる…
• 改行を単に消すべき箇所、セミコロンに置き換えるべき箇所
まずはコードゴルフ
10.times{|i| # ;不要
puts(1) # ;必要
puts(2) # ;不要
}
• インデント・改行はフォーマットの前処理で消す
• 消し方: セミコロンに置き換えるか単に消すかはRipperで判定
標準ライブラリRipperに任せる
def ast(code) = 位置情報を除去(Ripper.sexp(code)
)

original_ast = ast(original_code
)

minimized_code = original_cod
e

while minimized_code.match? /n/
a = minimized_code.sub(/n */, ''
)

b = minimized_code.sub(/n */, ';'
)

if ast(a) == original_as
t

minimized_code =
a

elsif ast(b) == original_as
t

minimized_code =
b

else
rais
e

end
end
元のコードのS式
インデント・改行を消したS式
セミコロンに置き換えたS式
変化していなければ消してOK
フォーマット
template = <<TEMPLATE
##### #####
######### #########
######################
######################
####################
################
##########
######
##
TEMPLATE
chars = File.read('code.rb').char
s

puts template.gsub('#') { chrars.shift }
アスキーアートのテンプレートを用意
コードゴルフ後のコードサイズから
アスキーアートの大きさを決める
テンプレートの#をコードに置換していく
• 壊れていないかRipperで判定
• Syntax OKか
• 構文木がフォーマット前後で
変化していないか
フォーマット
# original
(1.0+2
)

#=> 3.0
# ok
(1.0
+

2
)

# => 3.0
# syntax error
(1
.0+2
)

# syntax ok but broken
(1.0
+2
)

# => 2
• フォーマットで壊れないコードに手動で直す
• 順序を入れ替える・他色々頑張る
フォーマットでコードが壊れる場合
フォーマット
puts("R" "uby"); → puts( "R" "uby")
puts("Ruby");;;; → puts( "Ru by");;
壊れる(Ruとbyの間に空白が入る)
壊れない
s="hello";puts(s) → s= "hello";puts(s)
puts("hello");;;; → pu ts("hello");;;;
壊れる
壊れない
• フォーマッターにかける・書き換える の繰り返し
• 文字列分割
• 順序入れ替え
• コードゴルフで短くしたり長くしたり調整
• 最初で短くしすぎず、コードゴルフの余地を残すと楽
• それでも厳しい時はアスキーアートの形を微調整
フォーマット
特に大変だった箇所
フォーマット
'th:252px;}</styl' 'e>' '<form target="i"><input src="'
+

"g#{rand}" type" '="im' 'age"><iframe name="i"></ifra'
+

'me></form>'):r ['/g'] ?(h[/:.+l/]=?:'image/gif';s<
<

h+'GIF8' '7a'+[84
,

84,246,0,*(0..383).map {15*_1. /(383r)**(3-_1
%

3)*17}].pack('v3c*'); loop{ s<<[67434785,5
,

44,84,84,7,c.map{_1* 127} .each_slice(126
).map{[127,128,*_1 ] .pack'c*'}*''
,

1,129].pack('V3x' 'v2na*c2x');q
[

5];q.[]1while(r ==r=c)}):(x,y
,

z=r.scan(/d+/).map{_1.to_f
/

126-1};z&&p<<[rand-0.5,
(

z=x+y.i)*1.5,z/(z
.

abs+0.9),0,-p
[

-3][4]=-1
]

s.<<h);
s

.clos
e

}}
# rubocop:disable all
q=->{!sleep _1/1e2
}

p=(c=0..2).map{[_1/9r,0,5**_1.i/3,1,0]
}

require'socket'
puts'op'"en http://localhost:#{(w=TCPServer.new$*[0]||0).addr[1]}"
Thread.new
{

q[2
]

f=[-1]*s=3
t=Time.now.to_
f

p.select!{0<_1[3]=[_1[3]+_1[4]/8.0,1].min
}

9.times
{

h=p.map{[2**(_1*t.i)/_4**0.5/(1+Math.sin(2*t-9*_1%2)**32/16),_2+_4*(_3-_2)]
}

r=[s*3/2,84].mi
n

g=->
{

x,y=(s*(1+_1+1i)/2).rec
t

x<0||x>=s-1||y<0||y>=s-1?0:((l=f[y+1])[x+1]*(a=x%1)+(1-a)*l[x])*(b=y%1)+(1-b)*((l=f[y])[x+1]*a+(1-a)*l[x]
)

}

f=(1..r).map{|y
|

(1..r).map{|x
|

z=1.5+1.5i-3.0*(y.i+x)/
r

[h.sum{g[_1.*z+_2]}*0.9,1].mi
n

}

}

s=
r

}

c=f.flatte
n

redo
}

loop
{

s=w.accep
t

Thread.new
{

r=s.get
s

h='HTTP/1.1 '+"200 OKrnContent-"'T'"ype:text/htmlrnrn"
r['/ ']
?

s.<<(h+'<style>ifram''e{''opacity:0;height:0;}input{wid'+'th:252px;}</styl''e>''<form target="i"><input src="'+"g#{rand}"
type"'="im''age"><iframe name="i"></ifra'+'me></form>'
)

:

r['/g']
?

(

h[/:.+l/]=?:'image/gif'
s<<h+'GIF8''7a'+[84,84,246,0,*(0..383).map{15*_1./(383r)**(3-_1%3)*17}].pack('v3c*'
)

loop
{

s<<[67434785,5,44,84,84,7,c.map{_1*127}.each_slice(126).map{[127,128,*_1].pack'c*'}*'',1,129].pack('V3x''v2na*c2x'
)

q[5
]

q.[]1while(r==r=c
)

}

)

:

(

x,y,z=r.scan(/d+/).map{_1.to_f/126-1
}

z&&p<<[rand-0.5,(z=x+y.i)*1.5,z/(z.abs+0.9),0,-p[-3][4]=-1
]

s.<<
h

)

s.clos
e

}

}

heartbeat.rb 本体
メンテできなくもない本体(heartbeat.rb)
メンテ可能なフォーマッター(heartbeat̲formatter.rb)
メンテできなさそうに見える生成コード(output.rb)
• メンテ可能か?
• Yes (またあの面倒な書き換え・フォーマッター・書き換えのルーチンをするのか…)
Most interactive code
鼓動(heartbeat)
+
変化(localvar)
Most Orderly Code
変化(localvar)
rule = $*[00]&.to_i || 3
0

h ||= $*[01]&.to_i || 3
2

[ % if % TRICK-2022 %% %
]

# ] if a /%{(?<a>)/ =~ %} if + %} - %
[

# } if b /%<(?<b>)/ =~ %> if - %> + %
{

# > if c /%+(?<c>)/ =~ %+ if + %+ - %
<

# + if d /%-(?<d>)/ =~ %- if - %- + %
+

# - if e f, g, h, / =~ %/ if def e(f, g, h, *)
=

# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.
!

# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)

# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &
&

# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -=
1

eval(File.read f ||= __FILE__)
https://github.com/tric/trick2022/tree/master/04-tompng
https://gist.github.com/tompng/00772bba6a49a467778b072cb78f7aa9
変化(localvar)
[tomoya:04-tompng]% ruby entry.rb
 

#
 

###
 

## #
 

## ####
 

## # #
 

## #### ###
 

## # # #
 

## #### ######
 

## # ### #
 

## #### ## # ###
 

## # # #### ## #  

## #### ## # # ####
 

## # ### ## ## # #
 

## #### ## ### ### ## ###
 

## # # ### # ### # #
 

## #### ## # # ##### #######
 

## # ### #### # ### #
 

## #### ## ### ## ## # ###
 

## # # ### # ## ### #### ## #
 

## #### ## # ###### # # ### ####
 

## # ### #### #### ### ## # #
 

## #### ## ### # ## # # # ### ###
 

## # # ### # ### ## # ### ## # # # #
 

## #### ## # ### # # #### # # ## ######
 

## # ### #### ## ##### # ##### # # #
 

## #### ## ### # ## # # ## # ##### ###
 

## # # ### # ## # #### ## # ## ## # ## #
 

## #### ## # ### # # # ### #### # ## # ## # ####
 

## # ### #### #### ## ## ### # # #### # # #
 

## #### ## ### # ## # # ### # ## #### ### ## ###
 

## # # ### # ## # # ##### # ###### # # ## # # #
 

## #### ## # ### # # #### #### #### ## # # ########
#

[tomoya:04-tompng]% _
姿1 初期状態
5つの姿に変化する
rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
姿2
5つの姿に変化する
rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &
&

# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
姿3
5つの姿に変化する
rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)

# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
姿4
5つの姿に変化する
rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.
!

# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
姿5
5つの姿に変化する
rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *)
=

# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
初期状態に戻る
5つの姿に変化する
rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
下から逆順に実行される
Most Orderly
rule = $*[00]&.to_i || 3
0

h ||= $*[01]&.to_i || 3
2

[ % if % TRICK-2022 %% %
]

# ] if a /%{(?<a>)/ =~ %} if + %} - %
[

# } if b /%<(?<b>)/ =~ %> if - %> + %
{

# > if c /%+(?<c>)/ =~ %+ if + %+ - %
<

# + if d /%-(?<d>)/ =~ %- if - %- + %
+

# - if e f, g, h, / =~ %/ if def e(f, g, h, *)
=

# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.
!

# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)

# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &
&

# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__)
ローカル変数のTRICK
a /2#/ ← a(regexp)
a = 4
a /2#/ ← a / 2 # comment
/(?<a>)/ =~ '' ← ローカル変数aが定義される
a /%{(?<a>)/ =~ '' if false #} if fals
e

a /%{(?<a>)/ =~ '' if false #} if false
eval File.read(__FILE__)
• 下から順に実行
• 部分的に上下対称
• 縦に揃ったコードにしたい
• 読みにくいSyntaxを使いたい
• ジェネレーターとかは作らず(作れず) いきなり手書き
方針
%[s] %{t} %}r} %<i> %>n> %|g||
01/31 ローカル変数に普通に代入, def a(*)=1
変更履歴
rule = ARGV[0]&.to_i || 3
0

n = ARGV[1]&.to_i || 3
2

f ||= __FILE_
_

def a(*) = def b(*) = def c(*) = def d(*) =
1

a /a+%!/i, a = %
(

# !.ord; b /b+%?/i, b = %
{

# ?.ord; c /c+%$/i, c = %
[

# $.ord; d /d+%@/i, d = %
|

# @.ord; e s, i, f if i < n && def e(s, i, f) = self
.

# |.ord; s = t.map { rule[_1.join.to_i 2] }
;

# ].ord; t = s.rotate(-1).zip s, s.rotate(1)
;

# }.ord; puts s.map { (32 + 3 * _1).chr }.join
;

# ).ord; s ||= [*[0] * n, 1, *[0] * n]; i = i.to_i + 1
;

eval(File.read f
)
02/01 def a(*) = 1を消す
変更履歴
rule = ARGV[0]&.to_i || 3
0

h ||= ARGV[1]&.to_i || 3
2

f ||= __FILE_
_

a /e+%{/i, a = %
[

# } if false; b /f+%</m, b = %
(

# > if false; c /g+%+/n, c = %
>

# + if false; d /h+%-/o, d = %
}

# - if false; e f, g, h if def e(f, g, h) = % &
&

# } if false; g = i.map { rule.[] _1.join.to_i 2
}

# > if false; i = g.rotate(-1).zip(g, g.rotate(1)
)

# ) if false; puts g.join.tr('01', ' #'); 0 < h &
&

# ] if false; h -= 1; g ||= [*i = [0] * h, 1, *i
]

eval(File.read f)
02/08 named captureでローカル変数定義
変更履歴
rule = ARGV[0]&.to_i || 3
0

h ||= ARGV[1]&.to_i || 3
2

f ||= __FILE_
_

a /l*%{(?<a>)/i =~ %
[

# } if false; b /k*%<(?<b>)/m =~ %
(

# > if false; c /j*%+(?<c>)/n =~ %
}

# + if false; d /i*%-(?<d>)/o =~ %
>

# - if false; e f, g, h if def e(f, g, h) = % &
&

# > if false; g.map! do rule.[] _1.join.to_i 2 en
d

# } if false; g = *([g[-1], *g, g[0]].each_cons 3
)

# ) if false; puts g.join.tr('01', ' #'); 0 < h &
&

# ] if false; h = ~-h; g ||= [*i = [0] * h, 1] +
i

eval(File.read f)
02/17 縦のラインを揃える
変更履歴
rule = $*[00]&.to_i || 3
0

h ||= $*[01]&.to_i || 3
2

% # if a /%{(?<a>)/ =~ %} if +%} - %
[

# } if b /%<(?<b>)/ =~ %> if -%> + %
(

# > if c /%+(?<c>)/ =~ %+ if +%+ - %
}

# + if d /%-(?<d>)/ =~ %- if -%- + %
>

# - if e f, g, h, / =~ %/ if def e(f, g, h, *)
=

# > if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.
!

# } if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)

# ) if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || 0 < h &
&

# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -=
1

eval(File.read f ||= __FILE__)
02/23 より上下対称に
変更履歴
rule = $*[00]&.to_i || 3
0

h ||= $*[01]&.to_i || 3
2

[ % if % TRICK-2022 %% %
]

# ] if a /%{(?<a>)/ =~ %} if + %} - %
[

# } if b /%<(?<b>)/ =~ %> if - %> + %
{

# > if c /%+(?<c>)/ =~ %+ if + %+ - %
<

# + if d /%-(?<d>)/ =~ %- if - %- + %
+

# - if e f, g, h, / =~ %/ if def e(f, g, h, *)
=

# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.
!

# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)

# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &
&

# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -=
1

eval(File.read f ||= __FILE__)
• Ripperでparseできない
• ruby -run -e colorize entry.rb
• RubyVM::AbstractSyntaxTree ならparseできる
• 最低限このコードを色つけするツールを作成
• ruby strict_colorizer.rb -n 5 entry.rb
色をつけにくいコード
サポートするツール
[tomoya:localvar]% ruby strict_colorizer.rb -n 5 localvar.r
b

rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__
)

rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3))
# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &
&

# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__
)

rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
# + if d /%-(?<d>)/ =~ %- if - %- + %+
# - if e f, g, h, / =~ %/ if def e(f, g, h, *) =
# + if d /%-(?<d>)/ =~ %- if g.map! do rule.[] _1.join.to_i 2 end.!
# > if c /%+(?<c>)/ =~ %+ if !(g = *[g[-1], *g, g[0]].each_cons(3)
)

# } if b /%<(?<b>)/ =~ %> if puts(g.join.tr '01', ' #') || h > 0 &&
# ] if a /%{(?<a>)/ =~ %} if !g ||= [*g = [0] * h, 1] + g if h -= 1
eval(File.read f ||= __FILE__
)

rule = $*[00]&.to_i || 30
h ||= $*[01]&.to_i || 32
[ % if % TRICK-2022 %% %]
# ] if a /%{(?<a>)/ =~ %} if + %} - %[
# } if b /%<(?<b>)/ =~ %> if - %> + %{
# > if c /%+(?<c>)/ =~ %+ if + %+ - %<
• コードを書くにはあまり役に立たなかった
• このスライドの作成には役に立った
5パターンの色付けツール
Colorizer
• パズルみたいで面白いが…
• 正直メンテはきびしい
• あきらめて気合いで書く
• メンテできるとは限らない
…これメンテナンスできるのか?
TRICKのコードのメンテ
TRICKとメンテナンス
• 仕掛けを生かす
• いい仕掛けを思いつく 最大限生かす
• 手作業を惜しまない
• 作るのは一点もの 量産できても最高のものを1つだけ作る
• 完成させる
• 本当に完成するのか? 気合いで完成させる
かけられる時間も労力も有限ですよね
TRICKに大事なこと
•メンテナンス可能にすること
•ジェネレーター
•テストコード
•各種ツールを整備する
TRICKに大事なこと
あなたの生活を
支えていたTRICK
• 現世利益のない、意味不明な Ruby プログラムを書く
• Ruby のある種の神秘性(と仕様バグ)を明らかにする
• Ruby 処理系の意外な堅牢性と移植性を示す
• メンテナンス不能だが価値あるコードを世に放つことで Ruby
の仕様を安定化する
TRICKの理念
代入式
IRBとTRICK
[tomoya:~]% ir
b

irb(main):001:0> 12.times.to_
a

=>
 

[0
,

1
,

2
,

3
,

4
,

5
,

6
,

7
,

8
,

9
,

10
,

11
]

irb(main):002:0> _
[tomoya:~]% ir
b

irb(main):001:0> x = 12.times.to_
a

=>
 

[0
,

..
.

irb(main):002:0> _
代入式の場合は
長いinspect結果を省略
IRBのTRICK耐性
IRBとTRICK
IRB vs local variable tric
k

a /2.0#/ if false; x = [a = 1] * 100
0

a /2.0#/ if false; x = [a = 1] * 100
0
IRBのTRICK耐性
IRBとTRICK
[tomoya:~]% ir
b

irb(main):001:0> a /2.0#/ if false; x = [a = 1] * 1000
=>
 

[1
,

..
.

irb(main):002:0> a /2.0#/ if false; x = [a = 1] * 1000
=> 0.5
irb(main):003:0> _
代入式判定 正しいColoring
line.untaint if RUBY_VERSION < '2.7'
if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty
?

IRB.set_measure_callbac
k

end
+ # Assignment expression check should be done before @context.evaluate to handle
 

code like `a /2#/ if false; a = 1`
+ is_assignment = assignment_expression?(line)
if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty
?

..
.

@context.evaluate(line, line_no, exception: exc
)

..
.

end
if @context.echo
?

- if assignment_expression?(line)
+ if is_assignment
if @context.echo_on_assignment
?

output_value(@context.echo_on_assignment? == :truncate
)

end
else
output_valu
e

end
end
assignment̲expression?(line) を移動しただけ
IRB 謎の変更
← evaluate
IRBのテストコードにTRICKは潜んでいた
IRBは実質TRICK
irb/test/irb/yamatanooroti/test_rendering.r
b

def test_assignment_expression_truncate
write_irbrc <<~'LINES'
puts 'start IRB'
LINES
start_terminal(40, 80, %W{ruby -I#{@pwd}/lib #{@pwd}/exe/irb}, startup_message: 'start IRB'
)

# Assignment expression code that turns into non-assignment expression after evaluation
code = "a /'/i if false; a=1; x=1000.times.to_a#'.size"
write(code + "n"
)

clos
e

assert_screen(<<~EOC
)

start IRB
irb(main):001:0> #{code}
=>
[0,
...
irb(main):002:0>
EOC
end
a /'/i if false; a=1; x=1000.times.to_a#'.size
• RubyKaigiで見た
• Rubyのサンプルコードで見た
IRBにそんなコード書く人いる?
IRBは実質TRICK
https://twitter.com/sue445/status/1622910543714713604
Rubyの仕様の安定化に役立つ…かもしれない
IRBにとって辛そうなコード
Did = <<you, :
know #{:that}
you
# can nest
symbol
?

# => ["know thatn", :symbol?]
for
i in
j do
puts :hello
end
# combinatio
n

def self.for(x = <<~A + :
heredoc#{:in
}

A
# comment
do
)

=begi
n

embdo
c

=en
d

=
1
• 現世利益のない、意味不明な Ruby プログラムを書く
• Ruby のある種の神秘性(と仕様バグ)を明らかにする
• Ruby 処理系の意外な堅牢性と移植性を示す
• メンテナンス不能だが価値あるコードを世に放つことで Ruby
の仕様を安定化する
TRICKの理念
事実だった
おまけ
福岡Rubyist会議Quine
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 

zmzgUftevUHrBkkAC4cu6LdjpQ2e2O7MzfVdZD8YkqCHYE3AMsb2/fY
 

BN00JvGOJgHqMj1bdH0tK6ycr/Jyd9pU1BUF+nOuOSL1uuOirRuxwMxoyJd
 

GTA7KSPzUwxLc6QrtDS/wMLEWHKcU+5hIS9mA6GFr7CxrT5kUE5TVwMi2NQERbK
 

cPHIcDdmzG8E/o+vU1UD/Nb2UOelompGtY35OrmdOrVo8BQRshY6JKe6Wc1hWUkKTqj
 

nZoPKivNqA+Hq5mF718eqvAYiec76WF0wI8566KxsW9UkpMeLW7aSXpOcz6hpYhUyIrkxoy
 

/7gofbgyS1hBYlayYRBZ81hC2vTG6ZrJ3zMFGmgY0PKSCA+8YZ6gW0hKjQo9DkC1BSVsRShzReW
 

P7vH06zTsW07NxsEF5Of+opCOdFfSPEzWwZiZTocMAHAKcFexDIJnzCrX▄JbF+ygQ02XwmezBZEp9aO
 

tN+xSU6xK1tNRaKKSy7uK4YObgHf8yIkpxMWAsNPB+wZgqOoS8BB0jkG▄▄▀Wta6pBt6fQE4mE6EASN0AqH3
 

nt+8C1yZLTa1Dtqe9/36EmBYwc2LMfny3paEoj9UEQs77kOTTLAgAey▄▄▀mG0b274fHRGJowdKMWrYXLS3Qkm84
 

1wZ9bPFcYKobR2nbSOrhx7jYfZ0XaLaNoFp8cKAihsWqvK2wbl65DK▄▄▀MfF4E9▄▄▄▀0WZwTynMIigjTKeiQK3ltzBE
 

nGE9wR8QEbgWjYTCnDPoKFODEGhaFbsqa/k5RoJvgJR4tR/gzHuGR▄▄▀82ur▄▄▄▀▀Qe5jAmcrdMvYogujWWTbfdWQ5ZLSjx
 

F0u2Z1rBcC3MhDwO5g0EBBSeAlzW+/XV2ZbOMIB+PV/LvHx/fH+f▄▄▀/kD▄▄▄▀▀uCT34JsiVGxvDSZRNPo6EvWu1A354FO0/O0D
 

UeXX8kj+mJcbkPSIuxnV80VxJjOl7REHTvcbYcWoBl0VFCMZhgMK▄█Q▄▄▄▀▀kHPnXV0T9g/F14tKr62ufUjaCI/AVyeGKP8LsNGiof2
 

iO8c62WeNI7Yt5gaH9+4G47wRzKivShl617wUrGMexwiVch1GMl▄███▀▀nyQHWZ5zvoTGk7/T8Y6YTf'.unpack1(?m))#wS6(Mbi=;et?o
 

U(&lNMNQ,lw8rhr'TD@!s=s.lines!@@u_y#kpA*lQ7>m95uis▄█▀▀█X█l'L6J2@+[|;VOT[bJmcrx@![1..].join!@@#L*b.EXdE}Y}}`.>>m
 

a]hIA@:-8tu'Ztm-Xf@!.chars-[$/!@@1pZUrx#9RQXhYHp)/vA>█:g█z█▄UH7`Ego#sNOM($m(SP}D>@!];eval'cla!@@BeZEiGQa6S?0Exy5l>T
 

+]lneulM7`oe?|Ao|K.@!ss_Integer;!@@<Xbm?h9ka=lP4puO_De▄▄█▄█▄▄█▄▄Md'A6=|xKgrL1.u?zKd@!def_quo(a)=(!@@wR!7SZYYYuYXw.fAE]<
 

@(:.k9TJ<:D.e:lKnMSR@!self*a.pow(8!@@h*r▄▄▄▄█▀▀▀▀▀▀▀▀▀▀▀▀{█u█#v█w▀▀▀▀▀▀▀▀▀▀▀▀█▄▄▄▄R{0@!7,89)).modulo!@@hd&[<kw*4yEE**6pt[nN
 

('DsbvQj`KK`:G@AW@!(89);def_abs(!@@█▀▀bI|cUMn<Q8E-HleDDo█wd█+█t6Iv_RJ[3y${bId$l.▀▀█@!)=modulo(89);e!@@F:l>p(Bm}?o8uHh$+
 

8v5QL+Yh-+.uY@!nd'.tr(?_,32.ch!@@█▄▄rjH=;a<{{O&tJXEm/t█xj█O█xyn-{s]LL/TKvL=Pa<▄▄█@!r);require'matri!@@CFQY}:cw|@G,<
 

I`>-lv9qxD@!x';70.times{|i,*!@@▀▄2▀▀▀▀█▄▄▄▄▄▄▄▄▄▄▄▄I█▄B█J█6▄▄▄▄▄▄▄▄▄▄▄▄█▀▀▀▀v▄▀@!v|z=[?!,*?#,?$,*?!@@B,O?EB|s[?
 

#*Mekp4@!&..?[,*?],*?_..?}!@@z▀▄&v0B3FiHw+k]|Tmu▀▀▀▀▀▀▀▀▀▀?*.!}(/<vPYSs,Z-▄▀f@!];a=(0..101).map{z!@@+Pkp_j$
 

50]@!.index(s[i+70*_1])}!@@&f▀▄WKN|>gc'(pM)UZA$]0c#gt|0dZOer=q,Z2vdL6:5▄▀ZV@!;w=*Matrix[*(0..61).!@@:k=
 

@!map{|i,*b|v<<a.zip(0.!@@s8▀▄CA{?)as&u@c&`QXE$kj$kX5e6;AuMZXfs0_0md▄▀[|@!.).sum{|j,k|-(p=(i+k).!@@
 

@!pow(i+k,88)+1)*(j||(!@@tX▀█N,Do7JsnU.A2tx7y-Y9/T-4:t1MI_T(Tq(v{█▀WG@!b<<p;0))};b}].lup.sol!@@
 

@!ve(v);102.times{s[i!@@!RF█▄du5Q1[kA;*sQUT6&V$8>@!N{QYHZl|0q▄█YLt@!+70*_1]=z[a[_1]||w.s!@@
 

@!hift]}};eval(s*'')!@@Yqi▀▄Ul@b0vOyiyx7Up0s.-?+7su4=M>w'{▄▀e5:O6aQ4a{iA>OxF#:5GPhAp$Ck
 

-(`j$UOytHW)mH[w,(dJ|vixCs█▄,9(3I<!nOsWXVM7?E@j#8RpZ;r▄█CpkutdJL&c[D/Qrq!@CVF@OwGKR
 

(=*;o#g@J`+f1@UUSG&=BxUD3▀█▄CAH]8/|O&z8w[G{_'901Wy▄█▀+@qMy_sMoF<[k/P2y!5*Z$pVly
 

:Cxw$KP`0JbJ0,E.qx7F*ZOe]▀▀▄▄4[!e?qtUn-O:V*(A▄▄▀▀aVhBAZSK[-!qcYD|&j1G&cpR]M
 

;!AU{I4A'f}0+Mv)KFR7(&}y1u▀▀▀▄x24qc-16-Q▄▀▀▀{<<OKs+neRC$79xHFx&eKW?FKk<
 

?l>lNj_xe67zJgEX1gq&eyk9?x&▀▄I?xwy>W{▄▀JKL](zMFgwI2_Y(rO||$!e*wiNH'
 

=PkgN,!j=O0c84#A&]j<po5njr▀▀▀▀▀▀▀▀▀▀.wwH];pm=p!w67N5G*1d7bab;6N
 

3x,6HEyUbQaqO!=$}7R&ZzO-TJ<<*dktB#o5QTHkwl[+bhRvd>25uOSg<{[
 

||SVge#SH,Kz@|dDAC1;No$s.Gj5O#YB=tE?<zoX!]2gjfI}3`TJPuX
 

ZP$]>S2aCd?LS&Djluhm-G}Xdlu=a({U+9sD?}LGx;9b/Xslq,|
 

]=2UHb>q$]H6PA;[r0D>k.IvK*8@b]]#XjnL{[Hnf}>QPa]
 

sm19W[4(9OWQmg0X{_].4y-=<z0|AVf,jGox@YwEPFv
 

{(?,{SKfV/=ZyP/jM]c|[cHXzP[*H1JH@z`d./6
 

4b>*UCA2oiX$v]|6>:UR`,7G2bXa!0J({=_
 

vt)]&VFUC;j;Ku8s'-W0/D(D/jF8<zb
 

+1j6dRz#QKIkZU(wusf$Q,UC@aH
 

>7CB2a{zq#c@N# (}VzBy8x|$=5hV&TL-oHMow
 

+c N6 s:A$-i6a>]o|zv$4}WI
 

>y IkNogqre?2h[S$ |`7>r,.Kev*$0>7
 

K3 AT MI$E b;MeGSS-(o$
 

W{ Mv i/#Sfxg:uk <f n!Yu5Pa
 

wk ,B Nq LB I[F
 

l8 %. then{;eval (#
 

(+ s= _1 )#
 

). +@ &&s.scan(( 20
 

23 && 2/ 18  

/@!([^ @]+)!@@/))*''}
 

https://gist.github.com/tompng/509296fe288f37457bebbe35ad4db01d
福岡Rubyist会議Quine
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
zmzgUftevUHrBkkAC4cu6LdjpQ2e2O7MzfVdZD8YkqCHYE3AMsb2/fY
 

BN00JvGOJgHqMj1bdH0tK6ycr/Jyd9pU1BUF+nOuOSL1uuOirRuxwMxoyJd
 

GTA7KSPzUwxLc6QrtDS/wMLEWHKcU+5hIS9mA6GFr7CxrT5kUE5TVwMi2NQERbK
 

cPHIcDdmzG8E/o+vU1UD/Nb2UOelompGtY35OrmdOrVo8BQRshY6JKe6Wc1hWUkKTqj
 

nZoPKivNqA+Hq5mF718eqvAYiec76WF0wI8566KxsW9UkpMeLW7aSXpOcz6hpYhUyIrkxoy
 

/7gofbgyS1hBYlayYRBZ81hC2vTG6ZrJ3zMFGmgY0PKSCA+8YZ6gW0hKjQo9DkC1BSVsRShzReW
 

P7vH06zTsW07NxsEF5Of+opCOdFfSPEzWwZiZTocMAHAKcFexDIJnzCrX▄JbF+ygQ02XwmezBZEp9aO
 

tN+xSU6xK1tNRaKKSy7uK4YObgHf8yIkpxMWAsNPB+wZgqOoS8BB0jkG▄▄▀Wta6pBt6fQE4mE6EASN0AqH3
 

nt+8C1yZLTa1Dtqe9/36EmBYwc2LMfny3paEoj9UEQs77kOTTLAgAey▄▄▀mG0b274fHRGJowdKMWrYXLS3Qkm84
 

1wZ9bPFcYKobR2nbSOrhx7jYfZ0XaLaNoFp8cKAihsWqvK2wbl65DK▄▄▀MfF4E9▄▄▄▀0WZwTynMIigjTKeiQK3ltzBE
 

nGE9wR8QEbgWjYTCnDPoKFODEGhaFbsqa/k5RoJvgJR4tR/gzHuGR▄▄▀82ur▄▄▄▀▀Qe5jAmcrdMvYogujWWTbfdWQ5ZLSjx
 

F0u2Z1rBcC3MhDwO5g0EBBSeAlzW+/XV2ZbOMIB+PV/LvHx/fH+f▄▄▀/kD▄▄▄▀▀uCT34JsiVGxvDSZRNPo6EvWu1A354FO0/O0D
 

UeXX8kj+mJcbkPSIuxnV80VxJjOl7REHTvcbYcWoBl0VFCMZhgMK▄█Q▄▄▄▀▀kHPnXV0T9g/F14tKr62ufUjaCI/AVyeGKP8LsNGiof2
 

iO8c62WeNI7Yt5gaH9+4G47wRzKivShl617wUrGMexwiVch1GMl▄███▀▀nyQHWZ5zvoTGk7/T8Y6YTf'.unpack1(?m))#wS6(Mbi=;et?o
 

U(&lNMNQ,lw8rhr'TD@!s=s.lines!@@u_y#kpA*lQ7>m95uis▄█▀▀█X█l'L6J2@+[|;VOT[bJmcrx@![1..].join!@@#L*b.EXdE}Y}}`.>>m
 

a]hIA@:-8tu'Ztm-Xf@!.chars-[$/!@@1pZUrx#9RQXhYHp)/vA>█:g█z█▄UH7`Ego#sNOM($m(SP}D>@!];eval'cla!@@BeZEiGQa6S?0Exy5l>T
 

+]lneulM7`oe?|Ao|K.@!ss_Integer;!@@<Xbm?h9ka=lP4puO_De▄▄█▄█▄▄█▄▄Md'A6=|xKgrL1.u?zKd@!def_quo(a)=(!@@wR!7SZYYYuYXw.fAE]<
 

@(:.k9TJ<:D.e:lKnMSR@!self*a.pow(8!@@h*r▄▄▄▄█▀▀▀▀▀▀▀▀▀▀▀▀{█u█#v█w▀▀▀▀▀▀▀▀▀▀▀▀█▄▄▄▄R{0@!7,89)).modulo!@@hd&[<kw*4yEE**6pt[nN
 

('DsbvQj`KK`:G@AW@!(89);def_abs(!@@█▀▀bI|cUMn<Q8E-HleDDo█wd█+█t6Iv_RJ[3y${bId$l.▀▀█@!)=modulo(89);e!@@F:l>p(Bm}?o8uHh$+
 

8v5QL+Yh-+.uY@!nd'.tr(?_,32.ch!@@█▄▄rjH=;a<{{O&tJXEm/t█xj█O█xyn-{s]LL/TKvL=Pa<▄▄█@!r);require'matri!@@CFQY}:cw|@G,<
 

I`>-lv9qxD@!x';70.times{|i,*!@@▀▄2▀▀▀▀█▄▄▄▄▄▄▄▄▄▄▄▄I█▄B█J█6▄▄▄▄▄▄▄▄▄▄▄▄█▀▀▀▀v▄▀@!v|z=[?!,*?#,?$,*?!@@B,O?EB|s[?
 

#*Mekp4@!&..?[,*?],*?_..?}!@@z▀▄&v0B3FiHw+k]|Tmu▀▀▀▀▀▀▀▀▀▀?*.!}(/<vPYSs,Z-▄▀f@!];a=(0..101).map{z!@@+Pkp_j$
 

50]@!.index(s[i+70*_1])}!@@&f▀▄WKN|>gc'(pM)UZA$]0c#gt|0dZOer=q,Z2vdL6:5▄▀ZV@!;w=*Matrix[*(0..61).!@@:k=
 

@!map{|i,*b|v<<a.zip(0.!@@s8▀▄CA{?)as&u@c&`QXE$kj$kX5e6;AuMZXfs0_0md▄▀[|@!.).sum{|j,k|-(p=(i+k).!@@
 

@!pow(i+k,88)+1)*(j||(!@@tX▀█N,Do7JsnU.A2tx7y-Y9/T-4:t1MI_T(Tq(v{█▀WG@!b<<p;0))};b}].lup.sol!@@
 

@!ve(v);102.times{s[i!@@!RF█▄du5Q1[kA;*sQUT6&V$8>@!N{QYHZl|0q▄█YLt@!+70*_1]=z[a[_1]||w.s!@@
 

@!hift]}};eval(s*'')!@@Yqi▀▄Ul@b0vOyiyx7Up0s.-?+7su4=M>w'{▄▀e5:O6aQ4a{iA>OxF#:5GPhAp$Ck
 

-(`j$UOytHW)mH[w,(dJ|vixCs█▄,9(3I<!nOsWXVM7?E@j#8RpZ;r▄█CpkutdJL&c[D/Qrq!@CVF@OwGKR
 

(=*;o#g@J`+f1@UUSG&=BxUD3▀█▄CAH]8/|O&z8w[G{_'901Wy▄█▀+@qMy_sMoF<[k/P2y!5*Z$pVly
 

:Cxw$KP`0JbJ0,E.qx7F*ZOe]▀▀▄▄4[!e?qtUn-O:V*(A▄▄▀▀aVhBAZSK[-!qcYD|&j1G&cpR]M
 

;!AU{I4A'f}0+Mv)KFR7(&}y1u▀▀▀▄x24qc-16-Q▄▀▀▀{<<OKs+neRC$79xHFx&eKW?FKk<
 

?l>lNj_xe67zJgEX1gq&eyk9?x&▀▄I?xwy>W{▄▀JKL](zMFgwI2_Y(rO||$!e*wiNH'
 

=PkgN,!j=O0c84#A&]j<po5njr▀▀▀▀▀▀▀▀▀▀.wwH];pm=p!w67N5G*1d7bab;6N
 

3x,6HEyUbQaqO!=$}7R&ZzO-TJ<<*dktB#o5QTHkwl[+bhRvd>25uOSg<{[
 

||SVge#SH,Kz@|dDAC1;No$s.Gj5O#YB=tE?<zoX!]2gjfI}3`TJPuX
 

ZP$]>S2aCd?LS&Djluhm-G}Xdlu=a({U+9sD?}LGx;9b/Xslq,|
 

]=2UHb>q$]H6PA;[r0D>k.IvK*8@b]]#XjnL{[Hnf}>QPa]
 

sm19W[4(9OWQmg0X{_].4y-=<z0|AVf,jGox@YwEPFv
 

{(?,{SKfV/=ZyP/jM]c|[cHXzP[*H1JH@z`d./6
 

4b>*UCA2oiX$v]|6>:UR`,7G2bXa!0J({=_
 

vt)]&VFUC;j;Ku8s'-W0/D(D/jF8<zb
 

+1j6dRz#QKIkZU(wusf$Q,UC@aH
 

>7CB2a{zq#c@N# (}VzBy8x|$=5hV&TL-oHMow
 

+c N6 s:A$-i6a>]o|zv$4}WI
 

>y IkNogqre?2h[S$ |`7>r,.Kev*$0>7
 

K3 AT MI$E b;MeGSS-(o$
 

W{ Mv i/#Sfxg:uk <f n!Yu5Pa
 

wk ,B Nq LB I[F
 

l8 %. then{;eval (#
(+ s= _1 )#
). +@ &&s.scan(( 20
 

23 && 2/ 18  

/@!([^ @]+)!@@/))*''}
 

1行目の%は
常に4n+2個
誤り訂正するコードの断片を集めてeval
eval(s.scan(/印(断片)印/).join)
福岡Rubyist会議Quine
%%%%%%%%%%
D8YkqCHYE3AMsb2/fY8E
 

9pU1BUF+nOuOSL1uuOirRuxwMxoyJd1x5
 

MLEWHKcU+5hIS9mA6GFr7CxrT5▄▄▀5TVwMi2NQERbKKCVa
 

o+vU1UD/Nb2UOelompGtY35OrmdOrVo8B▄▄▀▀Y6JKe6Wc1hWUkKTqjzUN6W
 

Hq5mF718eqvAYiec76WF0wI8566KxsW9Uk▄▄▀▀W7aSXp▄cz6hpYhUyIrkxoy+F7WLV
 

YlayYRBZ81hC2vTG6ZrJ3zMFGmgY0PKSC▄▄▀YZ6gW▄▀▀▀Qo9DkC1BSVsRShzReWzLTmZV9
 

NxsEF5Of+opCOdFfSPEzWwZiZTocMAHA▄▄▀▀xD▄▄▀▀▀rXYJbF+ygQ02XwmezBZEp9aOUY9vvjO8
 

aKKSy7uK4YObgHf8yIkpxMWAsNPB+wZ▄▀▀o▄▄▀▀0jkGX+KWta6pBt6fQE4mE6EASN0AqH3C1B2HMWlF
 

qe9/36EmBYwc2LMfny3paEoj9UEQs▄█▀▄█▀▀AgAeyqy4mG0b274fHRGJowdKMWrYXLS3Qkm84VjZ59uT6jr
 

nbSOrhx7jYfZ0XaLaNoFp8cKAihs▄████▀bl65DKBKYMfF4E91CY60WZwTynMIigjTKeiQK3ltzBE4T7xDZ8OAhY
 

CnDPoKFODEGhaFbsqa/k5RoJvgJ▀██R█g▀▄uGRyU382urK/eBDQe5jAmcrdMvYogujWWTbfdWQ5ZLSjxP4mpqZSkbmBg
 

5g0EBBSeAlzW+/XV2ZbOMIB+PV/LvH█/█H+█DZq/kD/XPoeuCT34JsiVGxvDSZRNPo6EvWu1A354FO0/O0DS/zqMzD+R+e13
 

uxnV80VxJjOl7REHTvcbYcWoBl0VFCMZ█g█▄x█QHpXyzkHPnXV0T9g/F14tKr62ufUjaCI/AVyeGKP8LsNGiof2EG90fDkkrOvFL
U

9+4G47wRzKivShl617wUrGMexwiVch1GM█z7█r█BnyQHWZ5zvoTGk7/T8Y6YTf'.unpack1(?m))#wS6(Mbi=;et?or)'j71F'Mz.
k

@!s=s.lines!@@u_y#kpA*lQ7>m95uisxo█u9█w█'L6J2@+[|;VOT[bJmcrx@![1..].join!@@#L*b.EXdE}Y}}`.>>mshB0.jU``
$

@!.chars-[$/!@@1pZUrx#9RQXhYHp)/vA>y:█*█n▀█H7`Ego#sNOM($m(SP}D>@!];eval'cla!@@BeZEiGQa6S?0Exy5l>TO@iQm&]
G

@!ss_Integer;!@@<Xbm?h9ka=lP4puO_De▄▄▄█▄█▄▄█▄Md'A6=|xKgrL1.u?zKd@!def_quo(a)=(!@@wR!7SZYYYuYXw.fAE]<|w=G
 

@!self*a.pow(8!@@h*r▄▄▄▄█▀▀▀▀▀▀▀▀▀▀▀▀{]█n█v=█▀▀▀▀▀▀▀▀▀▀▀▀█▄▄▄▄R{0@!7,89)).modulo!@@hd&[<kw*4yEE**6pt[nN_
 

W@!(89);def_abs(!@@█▀▀bI|cUMn<Q8E-HleDDoD█d█+c█6Iv_RJ[3y${bId$l.▀▀█@!)=modulo(89);e!@@F:l>p(Bm}?o8uHh$+w'
 

@!nd'.tr(?_,32.ch!@@█▄▄rjH=;a<{{O&tJXEm/tw█j█O▄█yn-{s]LL/TKvL=Pa<▄▄█@!r);require'matri!@@CFQY}:cw|@G,<Sh
 

@!x';70.times{|i,*!@@▀▄2▀▀▀▀█▄▄▄▄▄▄▄▄▄▄▄▄I0█B█J█6▄▄▄▄▄▄▄▄▄▄▄▄█▀▀▀▀v▄▀@!v|z=[?!,*?#,?$,*?!@@B,O?EB|s[?xVw
 

4@!&..?[,*?],*?_..?}!@@z▀▄&v0B3FiHw+k]|Tmu▀▀▀▀▀▀▀▀▀▀?*.!}(/<vPYSs,Z-▄▀f@!];a=(0..101).map{z!@@+Pkp_j$wAV3
 

@!.index(s[i+70*_1])}!@@&f▀▄WKN|>gc'(pM)UZA$]0c#gt|0dZOer=q,Z2vdL6:5▄▀ZV@!;w=*Matrix[*(0..61).!@@:k=hzKi
 

@!map{|i,*b|v<<a.zip(0.!@@s8▀▄CA{?)as&u@c&`QXE$kj$kX5e6;AuMZXfs0_0md▄▀[|@!.).sum{|j,k|-(p=(i+k).!@@K9Rd=
 

{Mk`@!pow(i+k,88)+1)*(j||(!@@tX▀█N,Do7JsnU.A2tx7y-Y9/T-4:t1MI_T(Tq(v{█▀WG@!b<<p;0))};b}].lup.sol!@@+*Tg0N
 

/K4uVW}@!ve(v);102.times{s[i!@@!RF█▄du5Q1[kA;*sQUT6&V$8>@!N{QYHZl|0q▄█YLt@!+70*_1]=z[a[_1]||w.s!@@F#OkKv
 

LP6?jo@!hift]}};eval(s*'')!@@Yqi▀▄Ul@b0vOyiyx7Up0s.-?+7su4=M>w'{▄▀e5:O6aQ4a{iA>OxF#:5GPhAp$Ck*YlpJh>
 

;cor=-(`j$UOytHW)mH[w,(dJ|vixCs█▄,9(3I<!nOsWXVM7?E@j#8RpZ;r▄█CpkutdJL&c[D/Qrq!@CVF@OwGKR$_kzPf4@
 

AXk-(=*;o#g@J`+f1@UUSG&=BxUD3▀█▄CAH]8/|O&z8w[G{_'901Wy▄█▀+@qMy_sMoF<[k/P2y!5*Z$pVlyolcrjuG]
 

2Il:Cxw$KP`0JbJ0,E.qx7F*ZOe]▀▀▄▄4[!e?qtUn-O:V*(A▄▄▀▀aVhBAZSK[-!qcYD|&j1G&cpR]M&E,>9>t[+
 

F';!AU{I4A'f}0+Mv)KFR7(&}y1u▀▀▀▄x24qc-16-Q▄▀▀▀{<<OKs+neRC$79xHFx&eKW?FKk<&+&oy>wW=9
 

x?l>lNj_xe67zJgEX1gq&eyk9?x&▀▄I?xwy>W{▄▀JKL](zMFgwI2_Y(rO||$!e*wiNH'<@;M`U>l<q
 

=PkgN,!j=O0c84#A&]j<po5njr▀▀▀▀▀▀▀▀▀▀.wwH];pm=p!w67N5G*1d7bab;6N$2)TkvlNXNv
 

x,6HEyUbQaqO!=$}7R&ZzO-TJ<<*dktB#o5QTHkwl[+bhRvd>25uOSg<{[]c4(B0oYi.T&
 

SVge#SH,Kz@|dDAC1;No$s.Gj5O#YB=tE?<zoX!]2gjfI}3`TJPuXFBRzi|I_VE?s
 

]>S2aCd?LS&Djluhm-G}Xdlu=a({U+9sD?}LGx;9b/Xslq,|bIQl$KQ*>xmAH
 

Hb>q$]H6PA;[r0D>k.IvK*8@b]]#XjnL{[Hnf}>QPa]?|I/7k;`pkWN0M
 

[4(9OWQmg0X{_].4y-=<z0|AVf,jGox@YwEPFv|uJP:*Lx@|W6{X
 

KfV/=ZyP/jM]c|[cHXzP[*H1JH@z`d./6&=86,h?RxZL'aTw
 

2oiX$v]|6>:UR`,7G2bXa!0J({=_dh}R|h$vpy{+??F/
 

C;j;Ku8s'-W0/D(D/jF8<zb*x(&tlqPx4Cpe]Y0
 

KIkZU(wusf$Q,UC@aHyzcjL/&QE!#iM:O-2
 

>7CB2a{zq#c@N# =5hV&TL-oHMowxIU0k`6uyI?rgeTkAA
 

+c N6 |zv$4}WIy34Mu0Z79x&,z=@}W6
 

>y IkNogqre?2h[S$ 0>7l=d$&1A|zuTpFGvW0Jx
 

K3 AT MI$E `d=z)6i,=L0-dbPl;T
 

W{ Mv i/#Sfxg:uk <f j5'p,O5Yz{uUY
 

wk ,B Nq LB J|Do)_'zJ
 

l8 %. then{;eval (# YiKGu
(+ s= _1 )#
). +@ &&s.scan(( 20
 

23 && 2/ 18  

/@!([^ @]+)!@@/))*''}
 

1行目の%は
常に4n+2個
どのフレームでも
文字が消えない場所に
誤り訂正するコードを配置
誤り訂正するコードの断片を集めてeval
eval(s.scan(/印(断片)印/).join)
福岡Rubyist会議Quine
%%%%%%%%%%
require'zlib';eval(Zlib.inflate'eF5tVltzmzgUftevUHrBkkAC4cu6LdjpQ2e2O7MzfVdZD8YkqCHYE3AMsb2/fY8EidPMemxzdO76ztERqChwjKcTdDjAU45DlG03
OZBKLT8mmOHDIcEuVozULKQiT7NiVZc6y8nhQBN00JvGOJgHqMj1bdH0tK6ycr/Jyd9pU1BUF+nOuOSL1uuOirRuxwMxoyJd1x5peee+rCTvEnGftpE8ow+LKHr3M1d/wi/8
6x2aStHo+7zGmy0+GeqEzD9mMQ5EEKIUG2KGTA7KSPzUwxLc6QrtDS/wMLEWHKcU+5hIS9mA6GFr7CxrT5kUE5TVwMi2NQERbKKCVa2rfpWl1WNq5P2u+8TAz+6oAouaweWM
UJ2lZf6iBoJAjNFm28R8sfNu4it5HFypQcPHIcDdmzG8E/o+vU1UD/Nb2UOelompGtY35OrmdOrVo8BQRshY6JKe6Wc1hWUkKTqjzUN6WJW66ouSeuvnVGKSumvqhwJKeINJ
yte2LMzGXMiLoUq9zLtJXjEy4yVBeVnnZoPKivNqA+Hq5mF718eqvAYiec76WF0wI8566KxsW9UkpMeLW7aSXpOcz6hpYhUyIrkxoy+F7WLVNIyMWdPwkHLyjTHC5QyWlEvq
zz3oiF6zz0LNPJkcUSe0q/iET7Vn//7gofbgyS1hBYlayYRBZ81hC2vTG6ZrJ3zMFGmgY0PKSCA+8YZ6gW0hKjQo9DkC1BSVsRShzReWzLTmZV9rD69dm6zxSX5890NDUTao
gzNWJm8Nsngwmb2YzN6aNJF0nHGP7vH06zTsW07NxsEF5Of+opCOdFfSPEzWwZiZTocMAHAKcFexDIJnzCrXYJbF+ygQ02XwmezBZEp9aOUY9vvjO88oSm1uG7D3KzeDZFBq
O4+nthchuawGJKvnzPQJtTF0GtN+xSU6xK1tNRaKKSy7uK4YObgHf8yIkpxMWAsNPB+wZgqOoS8BB0jkGX+KWta6pBt6fQE4mE6EASN0AqH3C1B2HMWlF3imf+C4Qg4WhekM
QkLlWhsG1ibbC/pPMQwtob0nt+8C1yZLTa1Dtqe9/36EmBYwc2LMfny3paEoj9UEQs77kOTTLAgAeyqy4mG0b274fHRGJowdKMWrYXLS3Qkm84VjZ59uT6jrQDdkukNtC5Ru
OQxkbucEnF9zeoOo6xyn61wZ9bPFcYKobR2nbSOrhx7jYfZ0XaLaNoFp8cKAihsWqvK2wbl65DKBKYMfF4E91CY60WZwTynMIigjTKeiQK3ltzBE4T7xDZ8OAhYHYj7/0vVP
tE7r3II1AXMzU6NgyeVnGE9wR8QEbgWjYTCnDPoKFODEGhaFbsqa/k5RoJvgJR4tR/gzHuGRyU382urK/eBDQe5jAmcrdMvYogujWWTbfdWQ5ZLSjxP4mpqZSkbmBgFPz3qQ
GrhjxIB6F08DwNSV1F0u2Z1rBcC3MhDwO5g0EBBSeAlzW+/XV2ZbOMIB+PV/LvHx/fH+fDZq/kD/XPoeuCT34JsiVGxvDSZRNPo6EvWu1A354FO0/O0DS/zqMzD+R+e13m/M
i94r87f6lv1RYNwUeXX8kj+mJcbkPSIuxnV80VxJjOl7REHTvcbYcWoBl0VFCMZhgMKxYQHpXyzkHPnXV0T9g/F14tKr62ufUjaCI/AVyeGKP8LsNGiof2EG90fDkkrOvFL
U

+ilP4hL1rwjYviO8c62WeNI7Yt5gaH9+4G47wRzKivShl617wUrGMexwiVch1GMlz7Zr0BnyQHWZ5zvoTGk7/T8Y6YTf'.unpack1(?m))#wS6(Mbi=;et?or)'j71F'Mz.
k

2d[C0:qfka{U(&lNMNQ,lw8rhr'TD@!s=s.lines!@@u_y#kpA*lQ7>m95uisxo!u9Xwl'L6J2@+[|;VOT[bJmcrx@![1..].join!@@#L*b.EXdE}Y}}`.>>mshB0.jU``
$

fkBtsk@OLa]hIA@:-8tu'Ztm-Xf@!.chars-[$/!@@1pZUrx#9RQXhYHp)/vA>y:g*znuUH7`Ego#sNOM($m(SP}D>@!];eval'cla!@@BeZEiGQa6S?0Exy5l>TO@iQm&]
G

`@E>1yo+]lneulM7`oe?|Ao|K.@!ss_Integer;!@@<Xbm?h9ka=lP4puO_De/TzQD+_g7hMd'A6=|xKgrL1.u?zKd@!def_quo(a)=(!@@wR!7SZYYYuYXw.fAE]<|w=GJB
QiY#I@(:.k9TJ<:D.e:lKnMSR@!self*a.pow(8!@@h*rl1P{G67c`@P2?i5d-{]un#v=wBlbS55jZwo9kY[I:QR{0@!7,89)).modulo!@@hd&[<kw*4yEE**6pt[nN_qsu
CI*&/e1('DsbvQj`KK`:G@AW@!(89);def_abs(!@@_K{bI|cUMn<Q8E-HleDDoDwdM+ct6Iv_RJ[3y${bId$l.dz&@!)=modulo(89);e!@@F:l>p(Bm}?o8uHh$+w'7|{$
'7xM:h{Pg8v5QL+Yh-+.uY@!nd'.tr(?_,32.ch!@@/0xrjH=;a<{{O&tJXEm/twxjYOMxyn-{s]LL/TKvL=Pa<,cV@!r);require'matri!@@CFQY}:cw|@G,<ShFMUV5q
4h0d'6S_H4)I`>-lv9qxD@!x';70.times{|i,*!@@)!24T2t/N/Wf;[m@tqlMI0QBlJ,68&sR#N).h}|Uwe_[}v1|@!v|z=[?!,*?#,?$,*?!@@B,O?EB|s[?xVw?Qi@7Q0
O7`adO@IjIg<n#*Mekp4@!&..?[,*?],*?_..?}!@@zj6&v0B3FiHw+k]|TmujtLwU1Y{bS?*.!}(/<vPYSs,Z->1f@!];a=(0..101).map{z!@@+Pkp_j$wAV3udpWiBCN
Tk|w?;i1}d?vN0J50]@!.index(s[i+70*_1])}!@@&fpWWKN|>gc'(pM)UZA$]0c#gt|0dZOer=q,Z2vdL6:5V,ZV@!;w=*Matrix[*(0..61).!@@:k=hzKi3-9>wvq)5`
d,pZn;|n}_fz;1[qK@!map{|i,*b|v<<a.zip(0.!@@s8*-CA{?)as&u@c&`QXE$kj$kX5e6;AuMZXfs0_0mdaP[|@!.).sum{|j,k|-(p=(i+k).!@@K9Rd=tW{p}DmAE<U
_5!|1K}LuP0=t+g{Mk`@!pow(i+k,88)+1)*(j||(!@@tX}fN,Do7JsnU.A2tx7y-Y9/T-4:t1MI_T(Tq(v{gpWG@!b<<p;0))};b}].lup.sol!@@+*Tg0Nzrkl1(JyV!Qr
?yl9,7FUN?<8FC/K4uVW}@!ve(v);102.times{s[i!@@!RFC&du5Q1[kA;*sQUT6&V$8>@!N{QYHZl|0qbNYLt@!+70*_1]=z[a[_1]||w.s!@@F#OkKv-Na#7v{kd.M_8$
o-1#>[!oh/lyUWgKkLP6?jo@!hift]}};eval(s*'')!@@YqiAlUl@b0vOyiyx7Up0s.-?+7su4=M>w'{eue5:O6aQ4a{iA>OxF#:5GPhAp$Ck*YlpJh>6G@3[fF?!KH=<qj
qEo7@T]Ry[2,2>(VV#Q!;cor=-(`j$UOytHW)mH[w,(dJ|vixCsEb,9(3I<!nOsWXVM7?E@j#8RpZ;rd>CpkutdJL&c[D/Qrq!@CVF@OwGKR$_kzPf4@lELJ*Ao]][j*FS6I
,_&tW=v]|bN1BOXn4O/|2ydAXk-(=*;o#g@J`+f1@UUSG&=BxUD3F@sCAH]8/|O&z8w[G{_'901Wy;4_+@qMy_sMoF<[k/P2y!5*Z$pVlyolcrjuG]MGcgo`Z:7-U&CN6Gxc
3{v!(_0vOp&VK]gsO{/s[cUD$U2Il:Cxw$KP`0JbJ0,E.qx7F*ZOe]<dp$4[!e?qtUn-O:V*(A7$3)aVhBAZSK[-!qcYD|&j1G&cpR]M&E,>9>t[+dH$uExJh[.rgd;cl*Ii
_F_uilAAmHS-P@CAEevGkwAlRo&`kF';!AU{I4A'f}0+Mv)KFR7(&}y1uXbc>x24qc-16-QY9fy{<<OKs+neRC$79xHFx&eKW?FKk<&+&oy>wW=9>bRLc5OH#aP5N25{I.M:
.?J2HcCl89:YwW-rlU*;&!-2=E!hiZa@x?l>lNj_xe67zJgEX1gq&eyk9?x&32I?xwy>W{|RJKL](zMFgwI2_Y(rO||$!e*wiNH'<@;M`U>l<ql4kHf|knx})5W!N8QJ@?)P
9fQwn?Aqw$!DltVX*&SN<rc(JygljJ>a&us=PkgN,!j=O0c84#A&]j<po5njr?bokDx0z(z.wwH];pm=p!w67N5G*1d7bab;6N$2)TkvlNXNv`XOkT}cl[rzH2D0fDXzc3V?
7A&$'P-H{m6ZI,K2{=Ty+&LJtX5GFxC2`t<9R3x,6HEyUbQaqO!=$}7R&ZzO-TJ<<*dktB#o5QTHkwl[+bhRvd>25uOSg<{[]c4(B0oYi.T&n36ti@lj{}1#cks,aFWUXv;p
>,z&dg4WFb9/#|{PMKD9==Scop?y{e]E?0Csl)$||SVge#SH,Kz@|dDAC1;No$s.Gj5O#YB=tE?<zoX!]2gjfI}3`TJPuXFBRzi|I_VE?s&2&[#KZjcD/OuF@96c}yvu*fl:
`5a(C/$rjVyj#.+7)u*'8x'(m)=:GEK;PUQ!['fv)ZP$]>S2aCd?LS&Djluhm-G}Xdlu=a({U+9sD?}LGx;9b/Xslq,|bIQl$KQ*>xmAHnVLY}/v$2]P[{$hxc7rNT;/b?1q
K,_jHb2cc(,Ga.A-S}rUL5O@r`qXvE6+c!)xVDl0>[W]=2UHb>q$]H6PA;[r0D>k.IvK*8@b]]#XjnL{[Hnf}>QPa]?|I/7k;`pkWN0M![U)I,{Nae+$@_=TQC9sHib{Ae>p
$9yf8?6?|5k/m@]CaE}itV5Ns#<@T[Vvve2#b$KugyhVEsm19W[4(9OWQmg0X{_].4y-=<z0|AVf,jGox@YwEPFv|uJP:*Lx@|W6{X8KQtwBN`IB-jCor/]BG3wE;TUl,7HD
r'uz[c?Bc>DXSAk)ak|Nu2dlPE{0'GGk$i/V6Y({})KI&dv{(?,{SKfV/=ZyP/jM]c|[cHXzP[*H1JH@z`d./6&=86,h?RxZL'aTwcohx$g0){XXbTX.cE}t]+/g2DhX9AAX
=X9s|5Mg3rX6B?@k?I6'+q??7iDt$4vThV0fSx)tjAeElX1c04b>*UCA2oiX$v]|6>:UR`,7G2bXa!0J({=_dh}R|h$vpy{+??F/icq?ID`M(iORTeU]/W'@*ye(>W&2B;KF
<)vE9[MjWJ@e]VLs&Sio7hLq!Ci1{axSYno_Nf,!29vpYe=9gc@vt)]&VFUC;j;Ku8s'-W0/D(D/jF8<zb*x(&tlqPx4Cpe]Y0Tk!7KrVOU5[5;9.U=)7(21!XoJ@[XE/UWL
/18UEMmVu;]==7ze9mX{S`GhzL7#`L!SN-Lv.,uHI/*_qM92*.*j}+1j6dRz#QKIkZU(wusf$Q,UC@aHyzcjL/&QE!#iM:O-2tMuv9+D@4e*$d9pC2h>yJSHr08.yWk8g;zc
-ClUMbpd;g&ioNF0>7CB2a{zq#c@N#X?7__ZxCwr+/rzNPN*uEWDRcs(}VzBy8x|$=5hV&TL-oHMowxIU0k`6uyI?rgeTkAAN;wPD,=_R@KDo[wBwFS]AUq|L834$*]t/0s$
[AkEl)FgFvN.1:U2+cMna[TDG3[|oZN6&*gVj|H#Zj<n#D}gzZeCaoRMZs:A$-i6a>]o|zv$4}WIy34Mu0Z79x&,z=@}W61Czv_0X'Mn=`,dq3q#11ZMBk**d@/+*IO1xeXw
qOcc3loO[Wy5$y*->y-cIkNogqre?2h[S$&{g_o(HBVnsI8W|m2{+s{d;ed|`7>r,.Kev*$0>7l=d$&1A|zuTpFGvW0Jx0pg7cg0?+6jZCwojwm$a12?(se::@Qn&XYB/pEH
y,gp]=/zCl|r0Eh}K3IqATfVbKnHBS5FMI$EpXE!x2kr4Y-|P-Oc8bSG5f#(Pb;MeGSS-(o$bZ`d=z)6i,=L0-dbPl;TQ8WPmDdskmWd&N3&o1tKnC]q4eRA+Q+h-[A|?=$k
y0Ed(macT:[LJyH+W{$6Mv35i/#Sfxg:ukEe<fHtA@2[PXiQ{wqyBfhqHRRym'yn!Yu5Pa9b;:6@vj5'p,O5Yz{uUYF=D@`vpD(>W@uFoz&|0AcJP3SmA:VXV|f,)=H=B(PB
{&`mSB|a'eg]&XqLwk1[,BSJNq+@qp&|LhWqLBCon][`M+=;P*,)H(u?{Y]qVTO6FI[FFpbk=8!?=>A`J|Do)_'zJ}B;RpOUcf3*u1'J6&Y59y,Hkx3Pa|?z!Py;bt4V2|a&
gKCUv3us1XxfIZ[Wl8IE%. then{;eval (# YiKGu
(+ s= _1 )#
). +@ &&s.scan(( 20
 

23 && 2/ 18  

/@!([^ @]+)!@@/))*''}
 

画面全体を復元
Ruby
%%%%%%%%%%
 

O7MzfVdZD8YkqCHYE3AMsb2/fY8
 

bdH0tK6ycr/Jyd9pU1BUF+nOuOSL1uuOirRuxwMxoyJd1x
 

zUwxLc6QrtDS/wMLEWHKcU+5hIS9mA6GFr7CxrT5kUE5TVwMi2NQERbKKC
 

mzG8E/o+vU1UD/Nb2UOelompGtY35OrmdO▄Vo8BQRshY6JKe6Wc1hWUkKTqjzUN
 

qA+Hq5mF718eqvAYiec76WF0wI8566KxsW▄▀▄▀MeLW7aSXpOcz6hpYhUyIrkxoy+F7
 

S1hBYlayYRBZ81hC2vTG6ZrJ3zMFGmgY0PK▄▀█▀8YZ6gW0hKjQo9DkC1BSVsRShzReWzLTm
 

W07NxsEF5Of+opCOdFfSPEzWwZiZTocMAHA▄▀█▀xDIJnzCrXYJbF+ygQ02XwmezBZEp9aOUY9v
 

1tNRaKKSy7uK4YObgHf8yIkpxMWAsNPB+wZg▄▀█S8BB0jkGX+KWta6pBt6fQE4mE6EASN0AqH3C1B2H
 

Ta1Dtqe9/36EmBYwc2LMfny3paEoj9UEQs77k▄██LAgAeyqy4mG0b274fHRGJowdKMWrYXLS3Qkm84VjZ59u
 

obR2nbSOrhx7jYfZ0XaLaNoFp8cKAihsWqvK2▄█▀65DKBKYMfF4E91CY60WZwTynMIigjTKeiQK3ltzBE4T7xDZ
 

gWjYTCnDPoKFODEGhaFbsqa/k5RoJvgJR4tR/gz█▀GRyU382urK/eBDQe5jAmcrdMvYogujWWTbfdWQ5ZLSjxP4mpqZS
 

MhDwO5g0EBBSeAlzW+/XV2ZbOMIB+P▄▄▄▄▄▄▄▄▄██▄▄▄▄▄D/XPoeuCT34JsiVGxvDSZRNPo6EvWu1A354FO0/O0DS/zqMzD
 

bkPSIuxnV80VxJjOl7REHTvcbY▄▄█▀▀▀█▄▄▄▄▄▄▄█▄▄▄▄▄▄█▀▀▀█▄▄V0T9g/F14tKr62ufUjaCI/AVyeGKP8LsNGiof2EG90fDkk
 

t5gaH9+4G47wRzKivShl617▄▄▀▀Mexw▄█▀h1GMlz7Zr0BnyQ▀█▄5zvo▀▀▄▄/T8Y6YTf'.unpack1(?m))#wS6(Mbi=;et?or)'j71F'M
 

rhr'TD@!s=s.lines!@@u_▄▄▀▀A*lQ7▄▀95uisxo!u9Xwl'L6J2@+▀▄;VOT[▀▀▄▄rx@![1..].join!@@#L*b.EXdE}Y}}`.>>mshB0.jU``
 

tm-Xf@!.chars-[$/!@@1p▄▀rx#9RQ▄█▀Hp)/vA>y:g*znuUH7`Ego#▀█▄M($m(S▀▄D>@!];eval'cla!@@BeZEiGQa6S?0Exy5l>TO@iQm&]
 

|Ao|K.@!ss_Integer;!@@▄█bm?h9ka=l▀▄puO_De/TzQD+_g7hMd'A6=▄▀KgrL1.u?z█▄@!def_quo(a)=(!@@wR!7SZYYYuYXw.fAE]<|w=G
 

lKnMSR@!self*a.pow(8!@@█*rl1P{G67c`@▀▄?i5d-{]un#v=wBlbS5▄▀Zwo9kY[I:QR{█@!7,89)).modulo!@@hd&[<kw*4yEE**6pt[nN
 

K`:G@AW@!(89);def_abs(!@@█K{bI|cUMn<Q8E▀▀▄eDDoDwdM+ct6Iv▄▀▀[3y${bId$l.dz█@!)=modulo(89);e!@@F:l>p(Bm}?o8uHh$+w
 

Yh-+.uY@!nd'.tr(?_,32.ch!@@█0xrjH=;a<{{O&tJX█▄/twxjYOMxy▄█{s]LL/TKvL=Pa<,c█@!r);require'matri!@@CFQY}:cw|@G,<S
 

-lv9qxD@!x';70.times{|i,*!@@█!24T2t/N/Wf;[m@tql▀▄0QBlJ,▄▀&sR#N).h}|Uwe_[}v1█@!v|z=[?!,*?#,?$,*?!@@B,O?EB|s[?xV
 

n#*Mekp4@!&..?[,*?],*?_..?}!@@▀█6&v0B3FiHw+k]|Tmujt▀▄U1▄▀bS?*.!}(/<vPYSs,Z->█▀@!];a=(0..101).map{z!@@+Pkp_j$wA
 

vN0J50]@!.index(s[i+70*_1])}!@@&f▀▄WKN|>gc'(pM)UZA$]0▀▀▀▀|0dZOer=q,Z2vdL6:5▄▀ZV@!;w=*Matrix[*(0..61).!@@:k=hzK
 

1[qK@!map{|i,*b|v<<a.zip(0.!@@s8▀▀▄▄{?)as&u@c&`QXE$kj$kX5e6;AuMZXfs0_0▄▄▀▀[|@!.).sum{|j,k|-(p=(i+k).!@@K9R
 

{Mk`@!pow(i+k,88)+1)*(j||(!@@tX}f▀█▄▄7JsnU.A2tx7y-Y9/T-4:t1MI_T(T▄▄█▀gpWG@!b<<p;0))};b}].lup.sol!@@+*T
 

VW}@!ve(v);102.times{s[i!@@!RFC&▀▄▀▀█▄▄▄▄*sQUT6&V$8>@!N▄▄▄▄█▀▀▄▀bNYLt@!+70*_1]=z[a[_1]||w.s!@@F#Ok
 

?jo@!hift]}};eval(s*'')!@@YqiAlU▀█▄0vO▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀4=M▄█▀{eue5:O6aQ4a{iA>OxF#:5GPhAp$Ck*Ylp
 

r=-(`j$UOytHW)mH[w,(dJ|vixCsEb,▀▀▄▄▄!nOsWXVM7?E@j#▄▄▄▀▀rd>CpkutdJL&c[D/Qrq!@CVF@OwGKR$_kzP
 

k-(=*;o#g@J`+f1@UUSG&=BxUD3F@sCAH▀▀▀█▄▄▄▄▄▄▄▄█▀▀▀1Wy;4_+@qMy_sMoF<[k/P2y!5*Z$pVlyolcrj
 

l:Cxw$KP`0JbJ0,E.qx7F*ZOe]<dp$4[!e?qtUn-O:V*(A7$3)aVhBAZSK[-!qcYD|&j1G&cpR]M&E,>9>
 

;!AU{I4A'f}0+Mv)KFR7(&}y1uXbc>x24qc-16-QY9fy{<<OKs+neRC$79xHFx&eKW?FKk<&+&oy>
 

?l>lNj_xe67zJgEX1gq&eyk9?x&32I?xwy>W{|RJKL](zMFgwI2_Y(rO||$!e*wiNH'<@;M`U>
 

PkgN,!j=O0c84#A&]j<po5njr?bokDx0z(z.wwH];pm=p!w67N5G*1d7bab;6N$2)Tkvl
 

x,6HEyUbQaqO!=$}7R&ZzO-TJ<<*dktB#o5QTHkwl[+bhRvd>25uOSg<{[]c4(B0oY
 

SVge#SH,Kz@|dDAC1;No$s.Gj5O#YB=tE?<zoX!]2gjfI}3`TJPuXFBRzi|I_
 

]>S2aCd?LS&Djluhm-G}Xdlu=a({U+9sD?}LGx;9b/Xslq,|bIQl$KQ*
 

UHb>q$]H6PA;[r0D>k.IvK*8@b]]#XjnL{[Hnf}>QPa]?|I/7k;`p
 

W[4(9OWQmg0X{_].4y-=<z0|AVf,jGox@YwEPFv|uJP:*Lx@
 

{SKfV/=ZyP/jM]c|[cHXzP[*H1JH@z`d./6&=86,h?RxZ
 

CA2oiX$v]|6>:UR`,7G2bXa!0J({=_dh}R|h$vpy
 

FUC;j;Ku8s'-W0/D(D/jF8<zb*x(&tlqPx4C
 

z#QKIkZU(wusf$Q,UC@aHyzcjL/&QE!#
 

>7CB2a{zq#c@N# x|$=5hV&TL-oHMowxIU0k`6uyI?r
 

+c N6 a>]o|zv$4}WIy34Mu0Z79x&,
 

>y IkNogqre?2h[S$ ev*$0>7l=d$&1A|zuTpF
 

K3 AT MI$E (o$bZ`d=z)6i,=L0
 

W{ Mv i/#Sfxg:uk <f ;:6@vj5'p,O
 

wk ,B Nq LB ?=>A`J|
 

l8 %. then{;eval (# TY+
 

(+ s= _1 )#
 

). +@ &&s.scan(( 20
 

23 && 2/ 18  

/@!([^ @]+)!@@/))*''}
 

Rubyのコードはもっと自由に書ける
Ruby
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 

zmzgUftevUHrBkkAC4cu6LdjpQ2e2O7MzfVdZD8YkqCHYE3AMsb2/fY
 

BN00JvGOJgHqMj1bdH0tK6ycr/Jyd9pU1BUF+nOuOSL1uuOirRuxwMxoyJd
 

GTA7KSPzUwxLc6QrtDS/wMLEWHKcU+5hIS9mA6GFr7CxrT5kUE5TVwMi2NQERbK
 

cPHIcDdmzG8E/o+vU1UD/Nb2UOelompGtY35OrmdOrVo8BQRshY6JKe6Wc1hWUkKTqj
 

nZoPKivNqA+Hq5mF718eqvAYiec76WF0wI8566KxsW9UkpMeLW7aSXpOcz6hpYhUyIrkxoy
 

/7gofbgyS1hBYlayYRBZ81hC2vTG6ZrJ3zMFGmgY0PKSCA+8YZ6gW0hKjQo9DkC1BSVsRShzReW
 

P7vH06zTsW07NxsEF5Of+opCOdFfSPEzWwZiZTocMAHAKcFexDIJnzCrXYJbF+ygQ02XwmezBZEp9aO
 

tN+xSU6xK1tNRaKKSy7uK4YObgHf8yI▄▄▄▀▀▀sNPB█wZg▀▀▀▄▄▄B0jkGX+KWta6pBt6fQE4mE6EASN0AqH3
 

nt+8C1yZLTa1Dtqe9/36EmBYwc2LMf▄█▀▀aEoj9UEQs█7kOTTLAg▀▀█▄y4mG0b274fHRGJowdKMWrYXLS3Qkm84
 

1wZ9bPFcYKobR2nbSOrhx7jYfZ0Xa▄█▀oFp8cKAihsWqv█2wbl65DKBKYM▀█▄E91CY60WZwTynMIigjTKeiQK3ltzBE
 

nGE9wR8QEbgWjYTCnDPoKFODEGhaF▄▀qa/k5RoJvgJR4tR/█zHuGRyU382urK/e▀▄Qe5jAmcrdMvYogujWWTbfdWQ5ZLSjx
 

F0u2Z1rBcC3MhDwO5g0EBBSeAlzW+▄█V2ZbOMIB+PV/LvHx/f█+fDZq/kD/XPoeuCT3█▄siVGxvDSZRNPo6EvWu1A354FO0/O0D
 

UeXX8kj+mJcbkPSIuxnV80VxJjOl7R▄▀TvcbYcWoBl0VFCMZhgM█xYQHpXyzkHPnXV0T9g▀▄14tKr62ufUjaCI/AVyeGKP8LsNGiof2
 

iO8c62WeNI7Yt5gaH9+4G47wRzKivSh█617wUrGMexwiVch1GMlz7█r0BnyQHWZ5zvoTGk7/T8█6YTf'.unpack1(?m))#wS6(Mbi=;et?o
 

U(&lNMNQ,lw8rhr'TD@!s=s.lines!@@█_y#kpA*lQ7>m9█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█+[|;VOT[bJmcr█@![1..].join!@@#L*b.EXdE}Y}}`.>>m
 

a]hIA@:-8tu'Ztm-Xf@!.chars-[$/!@@▄▀ZUrx#9RQXh▄█▀)/vA>y:g*znuUH7`Eg▀█▄NOM($m(SP}▀▄@!];eval'cla!@@BeZEiGQa6S?0Exy5l>T
 

+]lneulM7`oe?|Ao|K.@!ss_Integer;!@@█Xbm?h9ka=▄█▀puO_De/TzQD+_g7hMd'A6=▀█▄grL1.u?zK█@!def_quo(a)=(!@@wR!7SZYYYuYXw.fAE]<
 

@(:.k9TJ<:D.e:lKnMSR@!self*a.pow(8!@@█*rl1P{G67██▄P2?i5d-{]un#v=wBlbS55j▄██9kY[I:QR{█@!7,89)).modulo!@@hd&[<kw*4yEE**6pt[nN
 

('DsbvQj`KK`:G@AW@!(89);def_abs(!@@█K{bI|cUMn█Q▀█▄HleDDoDwdM+ct6Iv_R▄█▀y█{bId$l.dz█@!)=modulo(89);e!@@F:l>p(Bm}?o8uHh$+
 

8v5QL+Yh-+.uY@!nd'.tr(?_,32.ch!@@█0xrjH=;a<█{O&▀█▄Em/twxjYOMxyn-▄█▀LL/█KvL=Pa<,c█@!r);require'matri!@@CFQY}:cw|@G,<
 

I`>-lv9qxD@!x';70.times{|i,*!@@█!24T2t/N/█f;[m@▀█▄MI0QBlJ,68▄█▀#N).h█|Uwe_[}v1█@!v|z=[?!,*?#,?$,*?!@@B,O?EB|s[?
 

#*Mekp4@!&..?[,*?],*?_..?}!@@█j6&v0B3Fi█w+k]|Tm▀█▄LwU1Y{▄█▀*.!}(/<█PYSs,Z->1█@!];a=(0..101).map{z!@@+Pkp_j$
 

50]@!.index(s[i+70*_1])}!@@▀▄pWWKN|>g█'(pM)UZA$▀█▄#g▄█▀dZOer=q,Z█vdL6:5V,▄▀@!;w=*Matrix[*(0..61).!@@:k=
 

@!map{|i,*b|v<<a.zip(0.!@@█▄*-CA{?)█s&u@c&`QXE$▀██▀X5e6;AuMZXf█0_0mdaP▄█@!.).sum{|j,k|-(p=(i+k).!@@
 

@!pow(i+k,88)+1)*(j||(!@@█▄}fN,Do█JsnU.A2tx7y-Y█/T-4:t1MI_T(█q(v{gp▄█@!b<<p;0))};b}].lup.sol!@@
 

@!ve(v);102.times{s[i!@@█▄FC&du█Q1[kA;*sQUT6&█$8>@!N{QYHZl█0qbNY▄█@!+70*_1]=z[a[_1]||w.s!@@
 

@!hift]}};eval(s*'')!@@▀█▄AlU█@b0vOyiyx7Up0█.-?+7su4=M>w█{eu▄█▀O6aQ4a{iA>OxF#:5GPhAp$Ck
 

-(`j$UOytHW)mH[w,(dJ|vi▀▄▄E█,9(3I<!nOsWXV█7?E@j#8RpZ;r█>▄▄▀utdJL&c[D/Qrq!@CVF@OwGKR
 

(=*;o#g@J`+f1@UUSG&=BxU▀██@sCAH]8/|O&z8█[G{_'901Wy;4██▀qMy_sMoF<[k/P2y!5*Z$pVly
 

:Cxw$KP`0JbJ0,E.qx7F*ZO▀▀█▄p$4[!e?qtU█-O:V*(A7$▄█▀▀hBAZSK[-!qcYD|&j1G&cpR]M
 

;!AU{I4A'f}0+Mv)KFR7(&}y1▀▀▀▄▄▄▄▄▄▄█▄▄▄▄▄▄▀▀▀<<OKs+neRC$79xHFx&eKW?FKk<
 

?l>lNj_xe67zJgEX1gq&eyk9?x&32I?xwy>W{|RJKL](zMFgwI2_Y(rO||$!e*wiNH'
 

=PkgN,!j=O0c84#A&]j<po5njr?bokDx0z(z.wwH];pm=p!w67N5G*1d7bab;6N
 

3x,6HEyUbQaqO!=$}7R&ZzO-TJ<<*dktB#o5QTHkwl[+bhRvd>25uOSg<{[
 

||SVge#SH,Kz@|dDAC1;No$s.Gj5O#YB=tE?<zoX!]2gjfI}3`TJPuX
 

ZP$]>S2aCd?LS&Djluhm-G}Xdlu=a({U+9sD?}LGx;9b/Xslq,|
 

]=2UHb>q$]H6PA;[r0D>k.IvK*8@b]]#XjnL{[Hnf}>QPa]
 

sm19W[4(9OWQmg0X{_].4y-=<z0|AVf,jGox@YwEPFv
 

{(?,{SKfV/=ZyP/jM]c|[cHXzP[*H1JH@z`d./6
 

4b>*UCA2oiX$v]|6>:UR`,7G2bXa!0J({=_
 

vt)]&VFUC;j;Ku8s'-W0/D(D/jF8<zb
 

+1j6dRz#QKIkZU(wusf$Q,UC@aH
 

>7CB2a{zq#c@N# (}VzBy8x|$=5hV&TL-oHMow
 

+c N6 s:A$-i6a>]o|zv$4}WI
 

>y IkNogqre?2h[S$ |`7>r,.Kev*$0>7
 

K3 AT MI$E b;MeGSS-(o$
 

W{ Mv i/#Sfxg:uk <f n!Yu5Pa
 

wk ,B Nq LB I[F
 

l8 %. then{;eval (#
 

(+ s= _1 )#
 

). +@ &&s.scan(( 20
 

23 && 2/ 18  

/@!([^ @]+)!@@/))*''}
 

Rubyのコードはもっと自由に書ける
Rubyであなたはもっと自由になれる

メンテできないコードをメンテする技術