拓冰建站拓冰建站
首页 / 资讯中心 / 正文

TRICK 2022 获奖作品深度解析:Ruby 极客艺术中的 Quine、流式 GIF 与“反重力“代码

TRICK 2022 获奖作品深度解析Ruby 极客艺术中的 Quine、流式 GIF 与反重力代码【免费下载链接】rubyThe Ruby Programming Language项目地址: https://gitcode.com/GitHub_Trending/ru/ruby导读本文聚焦当前仓库 sample/trick2022/ 目录——第 4 届 TRICKTranscendental Ruby Imbroglio Contest for rubyKaigi2022三个获奖作品的官方收录地逐一拆解水族箱 Quine无 JavaScript 交互式分形与可自我变换的反重力代码三件作品背后的实现机制。读完本文你将理解如何在 Ruby 中利用模素数线性方程做自修复、用纯 Ruby 实现 HTTP 服务器与流式 GIF、以及如何让一段代码在不同对齐方向下自我重写。背景什么是 TRICK以及这份 README 在仓库中的定位README.md 明确说明该目录收录的是TRICK 2022第 4 届 Transcendental Ruby Imbroglio Contest for rubyKaigi的获奖作品共三件全部以 MIT 协议许可作品奖项作者01-tompng/entry.rbBest fishbowl最佳鱼缸Tomoya Ishida (tompng)02-tompng/entry.rbMost interactive code最具交互性代码Tomoya Ishida (tompng)03-mame/entry.rbMost anti-gravity最反重力代码Yusuke Endoh远藤侑介需要特别强调的是 README 中的一句警告THESE ARE BAD EXAMPLES! You must NOT use them as a sample code.这些是反面教材切勿把它们当作示例代码使用。这意味着这些作品的观赏价值与研究价值远大于工程参考价值——它们是为了在语法合法的前提下制造视觉与行为的惊奇而刻意写成的。文章后续会说明它们为何危险。三个条目各自带有 authors.markdown作者信息与 remarks.markdown运行说明 设计思路配套文档以下分析均以这些官方说明为主体并以 entry.rb 源码为佐证。一、01-tompngBest fishbowl —— 会游泳的水族箱 Quine1.1 运行方式作者在 remarks.markdown 中给出两种运行方式# 无参数直接运行 ruby entry.rb # 带一个非 ASCII 半角字符参数字符会进入画面 ruby entry.rb ⬮ ruby entry.rb 作者确认在ruby 3.0.0p0 (x86_64-darwin19)与ruby 3.1.0p0 (x86_64-darwin20)上均可运行。1.2 核心创意每一帧都是可执行的 Quine所谓aquatic quine水族箱 Quine指的不是普通输出自身源码的 Quine而是一个动态系统代码中有一部分字符会被空格 覆盖抹掉但程序有能力恢复缺失的部分动画的每一帧本身都是一个可执行的 Ruby 程序运行后会从当前位置让鱼继续游动——即每帧都是 quine动画得以无限延续。要欣赏这个效果建议在支持 ANSI 转义序列的终端中运行程序通过\e[H光标复位类控制序列在原地重绘画面。1.3 内部机制一模 89 线性方程纠错这是整个作品最硬核的部分。源码被切分成**长度为 135 的块block**进行纠错。每个块由89 种字符组成[*(!..W), [, *(]..})]即 ASCII 可见字符中去除\、^、_、、X等保留字符后的集合共 89 种。设计约束是一个线性方程组matrix(size: 45x135) * block_vector(size: 135) % 89 zero_vector(size: 45)也就是说每个 135 长度的字符块必须落在某个 45×135 矩阵的核空间中模 89 意义下。当块中部分字符被空格抹掉后恢复问题就转化为在模 89 上解一个线性方程。作者给出的最小示例remarks.markdown 原文require matrix matrix Matrix[[3, 1, 4], [1, 5, 9], [2, 6, 5]] class Integer def quo(x) self * x.pow(87, 89) % 89 # Fermats little theorem. 89 is a prime number. def abs() [self % 89, 89 - self % 89].min # To avoid division by multiple of 89. end answer matrix.lup.solve([1, 2, 3]) # Vector[24, 42, 83] (matrix * answer).map { _1 % 89 } # Vector[1, 2, 3]这段代码本身就是一个微型教科书费马小定理89 是素数因此对任意非零元素x有x^88 ≡ 1 (mod 89)于是x.pow(87, 89)正是x在模 89 下的乘法逆元x^(89-2) x^87。作者用Integer#quo重定义除法为乘逆元再取模让Matrix#lup.solve能在模素数环上求解abs的巧用为避免除以 89 的倍数模意义下不可逆abs返回到 0 与到 89 的较短距离从而把结果归约到安全范围lup分解Matrix#lup.solve是标准线性求解器配合上面两个 monkey-patch就在纯 Ruby 中完成了有限域上的求解。回到 entry.rb可以看到实际实现正是如此组织的源码以$与X等字符作为拼接锚点主体形如eval((s%~c...第 1 行通过s[2*_11]第 1-2 行每隔一个字符取一次来重建真正的代码串并在第 4 行出现a.pow(87Xh32.chr——即a.pow(87, 89)的费马小定理求逆第 34 行附近出现.lup.与solve第 38 行以.split(?X);(0..36).map{s[2*_1].split}.join.tr($,$/)完成最终拼装。源码中分散的X正是被用来挖空再恢复的占位结构。1.4 内部机制二用气泡位置恢复动画帧号整个鱼缸动画是一个960 帧的循环。程序如何知道当前处于第几帧、从而从当前位置继续答案在 remarks 的说明中通过已执行源码中浮起气泡floating bubbles的位置来反推当前帧号。对应地entry.rb 第 12 行出现t(0..959).f...即帧迭代范围为 0..959第 8-9 行的q-t,i{...}是给定帧号t与索引i计算坐标的函数含(ti*99)%960的取模与ivmod(80)的整数除法取余第 16-20 行可见气泡运动方程含sin(2*l-t*g*80-...)等相位项。鱼与气泡的运动都是帧号t的确定性函数所以只要从源码残留文本中识别气泡坐标就能定位帧号并续播动画。二、02-tompngMost interactive code —— 没有一行 JavaScript 的交互网页2.1 运行方式与效果remarks.markdown 给出了三步玩法# 1. 以端口号启动 ruby entry.rb 8080 # 2. 浏览器打开 open http://localhost:8080 # 3. 点击画面进行交互效果是一个HTTP 服务器提供分形生物游乐场fractal creature playground你可以在网页上看到神秘分形生物的心跳点击画面即可改变它的形状。最令人惊讶的是——这个交互式网页完全没有使用 JavaScript。作者确认在 ChromemacOS、Android、FirefoxmacOS、EdgemacOS上工作正常已知限制Safari 与 iOS 上无法工作。2.2 三个关键技术支柱作者在 remarks 中自述了内部结构结合 entry.rb 源码可以一一对应① 分形迭代函数系统Iterated Function System, IFS第 1-2 行的p(c0..2).map{[_1/9r,0,5**_1.i/3,1,0]}定义了 3 个仿射变换参数复数5**_1.i/3表示以 1/3 比例旋转 5 的幂次角度第 6 行hp.map{...2**(_1*t.i)/_4**0.5/(1Math.sin(2*t-9*_1%2)**32/16)...}对每个变换按时间t计算权重并做双线性插值第 8-11 行的g-{...}即插值采样函数最终在每个网格点上叠加各变换的贡献[h.sum{...}*0.9,1].min生成不断形变的分形图案。② 渲染服务端流式 GIF浏览器拿到的不是图片文件而是一路持续推送的animated GIF 流。源码第 17-23 行响应头被改写为Content-Type: image/gif随后发送GIF87a头第 18 行用[84,84,246,0,*(0..383).map{...}].pack(v3c*)打包 GIF 逻辑屏幕描述符宽 84、高 84、色深 246再用[127,128,*_1].packc*构造每一帧的图像数据块第 21-22 行最后以1,129].pack(V3x v2na*c2x)追加帧尾q[5]第 23 行实现每 5 毫秒 sleep 控制帧率loop{...}无限推帧。这种长连接持续吐帧的手法正是无 JS 动画的底层来源。③ 交互input typeimage 隐形 iframe 表单这是整个作品最精妙的 trick。HTML 部分第 13-17 行styleiframe{opacity:0;height:0;}input{width:252px;}/style form targetiinput srcg...随机串 typeimageiframe namei/iframe/forminput typeimage本身就是可点击的图片按钮点击时浏览器会把点击坐标作为表单字段提交——这不需要任何 JS。表单的target指向一个隐形 iframeopacity:0;height:0于是提交动作不会导致页面跳转只是静默地把坐标发给服务器。服务端在收到包含坐标的 POST 后从源码第 25-27 行的r.scan(/\d/).map{_1.to_f/126-1}解析出归一化坐标并把新生成的生物压入状态数组pp[rand-0.5, (zxy.i)*1.5, z/(z.abs0.9), 0, -p[-3][4]-1]下一帧分形就随之改变。整个闭环点击 → 原生表单提交 → iframe 静默接收 → 服务端更新状态 → GIF 流下一帧呈现新形态全程零 JavaScript。HTTP 层由第 4 行TCPServer.new($*[0]||0)建立端口取自命令行参数缺省为 0 即随机端口随后从.addr[1]读出并打印open http://localhost:port每个连接用Thread.new处理第 12 行请求按路径分派/返回 HTML 页面/g...进入 GIF 推流分支其余路径含坐标的 POST更新分形状态。2.3 限制的根源Safari/iOS 无法工作的原因作者未在 remarks 中详述从实现看可推断与流式 GIF 的长连接渲染以及input typeimage在 WebKit 系浏览器中的处理差异相关属于平台兼容性问题使用时需留意。三、03-mameMost anti-gravity —— 会自我搬运的反重力代码3.1 一段不断升级的魔术remarks.markdown 用一段游戏化的说明带领读者逐层揭开作品过程如下# 第一步普通运行看到字符串被抖动 $ ruby entry.rb # 第二步对 entry.rb 施加向左重力删除所有空格即 sed 去空格 # 得到 up.rb —— 一个对输入文本施加向上重力的程序 $ sed s/ //g entry.rb | tee up.rb # 用它处理测试文件 test.txt可以看到字符向上掉落 $ cat test.txt $ ruby up.rb test.txt # 第三步对 entry.rb 施加向上重力得到 left.rb # —— 一个对输入文本施加向左重力的程序 $ ruby up.rb entry.rb | tee left.rb $ ruby left.rb test.txt # 第四步不再需要 sed用 left.rb 自己就能生成 up.rb $ ruby left.rb entry.rb up.rb # 第五步终极形态——对 left.rb 施加向左重力得到 horizontal.rb # 运行它效果与 entry.rb 相同水平方向上的原始程序 $ ruby left.rb left.rb | tee horizontal.rb $ ruby horizontal.rb # 对称地对 up.rb 施加向上重力得到 vertical.rb $ ruby up.rb up.rb | tee vertical.rb $ ruby vertical.rb换句话说作者写了一个四合一程序同一份源码在不同重力方向下会表现为完全不同的四个程序并且能互相生成对方。test.txt 就是用来演示字符被重力搬运效果的样本输入。3.2 实现原理共享代码片段与对齐检测作者在 remarks 末尾给出了防剧透提示spoiler核心机制有三点① 高度复用的代码片段四个程序之间共享大量代码。例如读取输入的片段b$.read在 entry.rb 第 8 行可见b$.read由up.rb与left.rb共同使用而horizontal.rb与vertical.rb共享putsTRICK2022这个输出片段源码第 3 行。远距离的字符会被跨程序反复复用——这正是反重力的字面来源字符被重力推到不同位置构成了不同程序。② 对齐状态检测程序需要判断自己当前是否已被对齐左对齐/顶对齐来决定执行哪段行为。作者给出了简化的 gimmick# 检测是否左对齐left-aligned \ #{puts(not left-aligned yet)} # {puts(left-aligned)} # 检测是否顶对齐top-aligned # xx{puts(top-aligned)} x#{puts(not top-aligned yet)} 原理在于去掉空格前后注释符#之后的换行与缩进关系发生变化导致puts调用是否被执行或是否进入注释/字符串字面量发生翻转。源码第 13-14 行的b.sub!(/^#$\K(\S)/x,a)?$1:a正是向左重力的核心——正则/^#$\K(\S)/x匹配行首注释内容之后紧邻的非空白字符\K重置匹配起点后仅替换该字符实现了把字母向左搬一格。③ 大量试错调参作者坦言我做了大量细微调整和反复试验已经不完全清楚它具体是怎么工作的了——这句话本身就是对该作品复杂度的最好注脚。需要同时区分top-left-aligned与left-top-aligned两种双向对齐状态四向程序共享一份文本任何一行字符的位置都是全局最优问题的解。四、为什么 README 反复警告These are BAD examples回到 README 的警告结合源码可以具体说明这三点坏在哪里依赖未文档化的 Monkey-patch01 号作品重写了Integer#quo与Integer#absremarks.markdown这会改变整个进程内所有数学运算的语义在任何真实业务中都是灾难依赖非标准字符与终端行为三个作品的正确输出都依赖 ANSI 转义、宽字符渲染与特定终端/浏览器跨环境行为不稳定可读性与可维护性为零源码刻意把字符串切碎、用$/X等字符做拼接锚点、把语义藏在注释与空白排版里正是为了让人类读不懂。因此这些代码是竞赛艺术是研究 Ruby 语法边界、eval/pack/正则/线程/网络编程极致的标本但绝不该出现在生产代码中。五、从这些作品能学到什么安全的研究方式以研究而非照抄为前提三件作品分别指向 Ruby 的几项硬核能力模运算与有限域线性代数Integer#pow的模幂、费马小定理求逆、matrix库的lup.solve在模素数环上的应用01 号纯 Ruby 网络编程TCPServer、多线程连接处理、HTTP 协议手写、pack二进制编码构造 GIF 流02 号文本变换与自修改代码sed式字符搬运、sub/正则\K的精准替换、通过排版状态切换代码语义03 号。如需复现仓库已按 MIT 协议收录全部源码与说明01-tompng/含 Gemfile 依赖清单、02-tompng/、03-mame/含演示用 test.txt。在各自对应的 Ruby 3.x 环境01、02 号与任意现代 Ruby03 号下均可按上文命令原样运行、观赏与调试。建议在隔离环境中运行切勿将这些技巧引入实际工程。结语TRICK 2022 的三件获奖作品分别把 Ruby 推向了自修复的活代码零 JS 的实时交互与可自我重写的多维程序三个极端。它们的存在证明一门语言对表达力的宽容可以催生出远超实用范畴的艺术而 README 那句 BAD EXAMPLES 的警告恰好划出了竞赛代码与工程代码之间不可逾越的界线——欣赏它们理解它们然后继续写好的 Ruby。【免费下载链接】rubyThe Ruby Programming Language项目地址: https://gitcode.com/GitHub_Trending/ru/ruby创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门