This document summarizes Yusuke Endoh's presentation on esoteric and obfuscated Ruby programming at the 2018 Bath Ruby Conference. It discusses programming in Ruby using only limited letters or symbols, and creating self-descriptive and funny Ruby programs. Examples shown include Ruby code using only numbers, underscores, or other limited characters to represent strings, as well as programs that reconstruct themselves when evaluated.
2. And Now for Something
Completely Different — in Ruby
Yusuke Endoh (@mametter)
Cookpad Inc.
Bath Ruby Conference 2018
(2018/03/23)
3. Yusuke Endoh (@mametter)
• MRI committer
– Was the release manager for 2.0
– Implemented coverage.so,
keyword arguments, etc.
• Branch coverage (Ruby 2.5)
• Working at Cookpad Inc.
– Cookpad employs
two full-time Ruby committers
(Koichi Sasada and me)
4. • A recipe sharing platform service
– Monthly Average Users: 90 million
– https://cookpad.com/uk
PR: Cookpad Inc.
5. • A recipe sharing platform service
– Monthly Average Users: 90 million
– https://cookpad.com/uk
PR: Cookpad Inc.
We're hiring!
HQ is in Bristol
Aim to be No.1 in 100 countries
(Now in 22 Languages and 68 Countries)
6. Today's Topic
• "Completely different" Ruby programs
– Ruby can do anything!
• Agenda
– 1. Ruby with limited letters
– 2. Funny, self-descriptive Ruby programs
8. A Ruby program
require "1234567890"
164494982952174876928097944808592
345203368399394864831728762954025
223544083864187912864887414172728
297936126807895282322033562499828
91940559064073941914244494205218
9. Demo
• "Hello, world!" program by using only
numbers
– You can encode not only Hello world,
but also any Ruby programs
$ ruby H3110.rb
Hello, Bath & Bristol Ruby!
$
Hello world only by numbers
11. Internal: Gödel numbering
• A technique to encode
any given string to a natural number
– In: a byte array 𝑛1, 𝑛2, 𝑛3, 𝑛4, 𝑛5, …
– Out:2 𝑛1 × 3 𝑛2 × 5 𝑛3 × 7 𝑛4 × 11 𝑛5 × ⋯
– To decode it, use prime factorization
• The gem uses much easier way
– Encoding: str.unpack("H*")[0].hex
– Decoding: [num.to_s(16)].pack("H*")
Hello world only by numbers
12. Internal: Ruby's black magic
• How to get the number
– It is just placed at the toplevel of code
• My answer:
– ObjectSpace.each_object(Integer)
• Unsure if this is portable, but it works anyway
– Try to decode all enumerated integers,
identify the desired Ruby code, and eval
– See the source code (32 lines) in detail
• https://github.com/mame/1234567890
Hello world only by numbers
13. How to install
Hello world only by numbers
$ gem install 1234567890_
Don't forget underscore!
(rubygems.org required at least one letter)
15. Demo
• "Hello, world!" program written using
only underscores
$ ruby __hello__.rb
Hello, Bath & Bristol Ruby!
$
Hello world only by "_"
16. Internal: Base-6
• Encode the Gödel number in Base-6
• Represent each digit with underscores
– Use length (n+1) for digit n
• Why base-6 is chosen?
– To minimize the expected code length:
log 𝑏 126 × ൗ𝑏
2 + 1 + 1
Hello world only by "_"
304313…
____ _ _____ ____ __ ____ …
18. Summary
• Ruby beats letter limitation
• More?
– See my RubyConf 2017 talk
begin begin begin public begin begin def each
clear rescue begin begin begin end end end
concat begin dup ensure concat begin clear
concat concat concat concat concat concat size
concat begin begin begin size end end end
begin size end ensure begin clear end end end
concat begin dup ensure concat begin clear
begin concat concat size end until hex
concat concat concat concat begin size end
concat concat begin size end rescue upcase
begin concat begin concat size end end
begin size end ensure begin clear end end end
concat begin dup ensure concat begin clear
concat begin concat size end unless begin end
begin concat concat concat begin size end end
@_="_"=~/$/;_=@_+@_;$><<(""<<(_*_*_+@_)*_*_*_<<
((_+@_)*_*_*_+@_)*_*_+@_<<(((_+@_)*_*_+@_)*_+@_
)*_ *_ << ( ( (_+@_ )*_*_+
@_) * _+ + @_) * _*_ <<((((+
_+@_)*_ * _+ + +@_ )*_+@_)*
_++ @_ )* _ ++@_ <<((_*_+@_
)*_ + + + @_)* _*_ <<_*_*_*_*
_<< ( (_ * _ +@_) *_*_*_++@_
)*_<<(((_++@_)*_+@_)*_*_+@_)*_*_+@_<<((_+@_)*_*
_*_*_ +@_)* _<<( ( (_+ +@_
)*_ ++ @_ ) *_+
@_) *_*_*_+ @_<< ( _* _ *_*_*_++
@_) *_+@_<< (((( _ ++ + +@_)*
_*_ ++ @_ )* _ +@_)*_++
@_)*_ +@_<< (((( _+ + @_)*_*_+
@_)*_+@_)*_+@_)*_<<(((_+@_)*_*_*_+@_)*_+@_)*_<<
_*_*_*_*_+@_<<(_*_+@_)*_)#_$`/^|:()[_-|?|_||:`/
Only symbols Only alphabets
22. Internal
• Reconstruct self
• Reshape itself
as the next number
– Embedded font data
– Fizzbuzz
– Shaping
• Output the result
• Make it "executable
ASCII-art"
Self-descriptive FizzBuzz
eval(s=s=
%w@proc{|
n|z=32.ch
r;k="[#{n
+=1}]";u=
":>==;<==?"[m=n**4
%-15,m+13]||"#{$f=
k}";d="Y.E.#{c=64.
chr}*'')";$f||d<<z
+k;t="eval(s=s=%w#
{c+s=s[0,
334]}#$f#
";25.time
s{|y|m=u.
bytes.map
{|v|t<<s;
(0..[62-v
,2].min).
map{|x|"i
f0zgl11p0
zghuhku744d8hzeg41qtfx7xs7t
wflr".to_i(36)[x+32+v*3-y/5
*44]<1?z*9:t.slice!(0,9)}<<
z}.join.rstrip;y>23&&m[-9,9
]=d;puts(m)}}[1]#pY.E.@*'')
23. Quine
• A program that outputs itself
• Trick
– Step 1. Reconstruct self as a string
– Step 2. Print it
• Additional step makes Quine "funny"
eval s="puts('eval s='+s.inspect)"
Self-descriptive FizzBuzz
25. Executable ASCII-art
• Write a code with no space and backslash
• Wrap it with "eval(%w(" and ").join)"
• You can shape your code as you like
puts"Hello,world!"
eval(%w(puts"Hello,world!").join)
eval(%w(pu
ls "H
el lo
,w or
ld!")*"")#
#=> Hello,world!
27. Quine-relay
• A Ruby code
• that generates Rust code
• that generates Scala code
• …
• that generates REXX code
• that generates the original Ruby code
128 languages
involved
in total
Quine-relay
28. Quine-relay
• A Ruby code
• that generates Rust code
• that generates Scala code
• …
• that generates REXX code
• that generates the original Ruby code
128 languages
involved
in total
Quine-relay
30. Internal
– Step 1. Reconstruct self as a string
– Step 2. Wrap Python's Hello world
– Step 3. Print it
Quine-relay
eval s="puts( 'eval s='+s.dump )"
Ruby's Quine
print( "Hello!" )
+Python's Hello world
eval s="puts('print('+('eval s='+s.dump).dump+')')"
↓RubyPython Quine
31. Internal
• In theory: Repeat this process for 127
languages
• In practical: The naïve approach
explodes the size of intermediate code
– To avoid this problem, it uses many tricks
• compression algorithm
• on-time code generation
• etc.
– See the generator in detail
https://github.com/mame/quine-relay/blob/master/src/code-gen.rb
Quine-relay
32. Monumental Quine
A column object 3D model data
Ruby code
is inscribed
You can buy it at Shapeways!
https://www.shapeways.com/shops/mametter
Execute
the code
3D
printer
33. Internal
• The shorter is code, the cheaper ☺
– 3D printing fee: $15 per one line
– The code does not contain complex-glyph letters
("3", "8", "g") to omit them from font data
33
eval(_=%[b='DEILMQTVY';eval((%[a=(-1)EE0.5;f=->EfVf.each_slice(2)Y;c=->wVz=->dVd.mapVd=d.rotate(1)YY
;Q=->k,l,mV((m-k)E(l-k).conT).arQ<0Y;y=[];x=0.99;o,T=w.partitionV|n|d=0;z[n].mapV|k,l|y<<f[k,x,k,1,l
,1,l,x];d+=k.conTElY;d.arQ<0Y;f[o,T,[[0,d=2IEa,d+15,15]],o.map(Dd=:reverse),T.map(Dd),[]].mapV|o,T|T
.mapV|h|z[h].max_byV|u,|u.realY;Y.sort_byV|i,|-i.realY.mapV|h|i,=h;v=0;o.mapV|n|z[n].mapV|x|m,l,E,k=
x;e=(i-m).arQEE2;v<eDD(d=Q[k,m,l])^(Q[k,m,i]^d|(Q[m,l,i]^d))DDo.allMV|n|z[n].allMV|k,l|[i,m,k,l].uni
q.size<4||Q[i,k,l]==Q[m,k,l]||Q[i,m,k]==Q[i,m,l]YYDD(v=e;w=n;T=Ex,m,Eh,i)YY;w[0..-1]=TY;o.mapV|v|t,=
s=z[v];n=->rVk,m,l=r;k=k[2],l,m[2];r[I]=Q[Ek]DDv.allMV|q|z[k].anyMV|k,l|q==k||Q[k,q,l]YYY;z[s].mapV|
k,l,m|l[0,2]=k,m;n[l]Y;(s[I..-1].mapVt=t[1]until(t[I]);k,m=l=t;m[0],k[1]=t;n[m];n[t=k];lY<<t).mapV|k
,m,l|y<<f[k[2],x,l,x,m[2],x]YY;x=1Y;yY;e=0;%[`^Tx52t<^cd,7/w(kabvbEz5arIwIa17.=c'slxr=-'4|e)EwkMI,_^
pvMVhsnME.rLw_k)^tp>+TIEduE45u>mv%^Y=Vny-`zce)k`heIt%`Vzf;c2nk4d|Vp^D_,,|kDDL2r_sDy%%fiMV6cYE)5`,m/k
YQ/;IEezMVv,QchILY|p%%i<vstDt)=M7vLcT>=4Q2(vwael61//<ck>-l67uQ;2Tq,c_'qEIcm1cL;i++2-tYdbenq%pxr;2'Vn
(uDa)n)zf4w)%5vh;ssV5kI;)z;2=+Toe<VL_D0`VLu;ook_y+eT`>IeT9-(i<MiVdd)ib_y+x`s^_u>M1s/mYQvEY`vMxu%Y0u5
7Qaoh2<Mkd'vtkp^zTc`E->Ep+icop_u0%=-zv,omq`Qz/41DV'`f9L5`M'bVxx%/;qLtq12%q:V'9,fD,ovu%qr|+e+rudI`Ir0
5chVd+Q5`II76rY0laT%b(I>Y%EQ1xf^|r,1)%4--zQa'<qxL-7Y0+|'vvbIxso0usv;%.I:pLQbe5a),Vu|91(0EIv^T>c5Dmd:
9-I15%Lp/>>z57^,Th2>%la0;5`dE1<xvrd09^9zz<.t,LpofrTTsQi'u5;Lwp7+zmm`'>qy;f6)||Ikw_0wdMM5<hmn64wbQ_rD
m'>so7b..4qy`nQrz%Vf7Ii^epY(x=|49Lh(=>sI_sbofb7|qM,unaD%^i:|;_tEEnb-DDt`t%I2h;0x5f^yhs,pbLf+m^e>yqzQ
'%::|^=,5-b=^_1x1se`kp,%wq4T%;'E.:Dsp_V-0||,)=;.a|<%0QY:;t:fEmk:4|_%o-.:aooq/6mThdvz4`uQqY1r'em.5'z1
2p7e%%pp6ebMM,m`5QpYx'd`,`6a4T)6Q.k.E.YsdiE^ox9pyrsr%|(kfn=y9q`6;=V<z%9(0cf^yp=:Irw-c/y>%iie%)y-1i(y
'V-n^uTva%l0Q>,yz;E0:LbV'eTb6MIb``Da.__ihbacxY|fc6>pTtl;ivVt,q>/%w,=hnI+i90>10u59te,Ildw4p94x`iwvs`f
+^)w1M>%wf^].bytesV|i|e=eE59+(i-5)%L9Y;Q=->iVk=e%i;e/=i;kY;d=VI2=>c[[]]Y;54.upto(1I0)V|h|d[h%L9+I7]=
c[(0..Q[5]).mapVl=o=T=[];n=0;(-2..Q[17]).mapV[l=Q[2],o=Q[1I]+Q[21]Ea+1+a]Y.flat_mapV|m,n|E,(h,)=[[o,
l],[(o+o=n)/2,0]][0..lDl=m]Y.mapV|o,l|n=l<1M((n==0ML:1).upto(L)V|k|T<<h+kE(n-h)/4+kEkE(o-2En+h)/64Y;
h=o;0):oY;TY]Y;n=[];m=0;v=aEE0.04;z=15/v.arQ;w=-0.2I;h='eval(_=%['+_+'])';h.tr(b,']+b+%[').bytesV|o|
q=-w+s=wEm+=1;r=vEp=vEEmEz;d[o].mapV|v|n<<v.mapV|v,l|T,k=v.rect;[(p+(r-p)ET/=15)El,q+wET-k]YY;m<101D
Dn+=[f[p,q,r,s,E[r,0]E(m/100),r,2,p,2],f[p,2,r,2,k=rET=0.976,2,TE=p,2],f[T,2,k,2,k,x=-715,T,x],f[T,x
,k,x,r,x,p,x],f[p,x,r,x,r,s-l=690,p,q-l,E[p,x+2]E(1/m)]];Y;T=VY;k=VY;l=''<<I2;m=n.mapV|i|(p,q),(r,s)
,(t,u)=Ei;p-=r;r-=t;Mf+l+i.mapV|m|[[T,:v,Em],[k,:vn,(rE(q-s)-pE(s-u))Ea,(p.conTEr).imaQ]].mapV|T,o,p
,w|T[[o,E(p.rect<<w).mapV|p|(pE500).round/z/10Y]El]||=T.size+1YE'//'YElY;o=''<<I5;puts(%(Q%squine')%
l,o+%V'+(eval(%[Y+h+%V]);exit);'Y,T.keys,k.keys,m,o+M');]).tr(b,'%)27>fiz|'.tr('x%-|','%-'<<125)));'
[[[ Monumental Quine (c) 2015 Yusuke Endoh -- tested with ruby 2.2.1 -- built on 2015/04/01 ]]]'])
Embedded TrueType Font Data
+ Renderer
Triangulation Algorithm
for Font Polygons
3D Model Generator
Monumental Quine
38. Demo
• Works as an ordinary Quine
$ ruby lquine.rb > lquine2.rb
$ diff –s lquine.rb lquine2.rb
lquine.rb and lquine2.rb are identical
Lipogram Quine
41. Demo
• You can delete any one letter
• It automatically restore itself!
$ sed s/a//g lquine.rb > broken.rb
$ ruby broken.rb > broken2.rb
$ diff –s broken.rb broken2.rb
broken.rb and broken2.rb are identical
Lipogram Quine
42. Internal
• Main trick:
– This code executes code2
• The string literal is just ignored
• If "a" is removed:
– This code executes code1 and terminates
Lipogram Quine
"#a{ code1;exit }"; code2
"#{ code1;exit }"; code2
String interpolation!
44. More? Read my book!
• "The World of
Obfuscated,
Esoteric, Artistic
Programming"
– Contains about 40
codes like this talk
– Written in Japanese
45. Related contests
• International Obfuscated C Code Contest
(IOCCC)
– A programming contest for hard-to-read
programs written in C language
• Transcendental Ruby Imbroglio Contest
for rubyKaigi (TRICK)
– The judges (including I) held TRICK twice
– TRICK FINAL is now open (~ 2018/03/31)
• https://github.com/tric/trick2018
• Winners will be awarded at RubyKaigi 2018
46. Conclusion
• Ruby is incredibly flexible
– You can use Ruby with broken keyboards
(only numbers, or only underscores)
– You can write artistic and super-robust
Quine in Ruby
• Ruby Programming is fun
– Even without practicality
– If you like practicality, join
• One more thing…