这大概是苟活之美的开始

人体工效学的段子

对工程细节的敬畏

我觉得那种灿烂的理想主义活法在文学上被歌颂得过多了。但是作为工程师…能看到其实大部分现存的系统,软件也好,建筑也好,政治也好,都是勉勉强强维持着能实现它的功能的样子。而我在无数这样的系统中间活了幸福的二十七年——不赞美一下这种漏洞百出还坚持着不会破罐子破摔的苟活精神,实在说不过去。

也正因为如此,批评软件设计、批评政治体制是如此容易吧。

“大不了一死”可以让你勇敢面对苦兮兮的生活,但想成就(哪怕只是)一件小事,仅仅靠冒险和燃烧都不太够。

被规划的生活。紫花、广播和电视节目。周更的电视剧和一次性放出的电视剧。自由的代价。

WIP.

日本小旅

(本文写于2015年4月11日)

他考究地扶起宽大的袖口,斟了两小杯七分满的红茶。
“品茶的时候不能想着工作哟”,他这样对我说,“何况是这样的雨天。”放下茶壶,盘腿坐在拜垫的另一端,与我一几相隔,眯着眼看着窗外。向外努力伸展的房檐一缕缕地淌着雨,潮湿的木质有腐烂的预感。
我们经常在无所事事的午后闲谈,谈一些我们谁都不懂的事。这个习惯自高中开始,直至今日。准确地说,是直到他回到日本(他的家乡)工作。之后这样的机会就变得很少。不过一有机会,我还是十分愿意来到他的住所,和他聊上个把钟头,仍然不失为忙碌的生活中难得的快乐时光。每一次他欢迎我的方式也都有所不同,我已经领教过他的甜点手艺,上一次我造访时他准备了糯米里面包红薯馅的食品,说叫和菓子,应该是那里的传统甜点。有时则全不顾会客之礼,信手抄一道数学题给我解(或给我证)。对于我——一个二流杂志的校对而言,最近一次接触数学,除去杂志上那篇关于女性三围与身体机能换算的投稿以外,就只能追溯到大学时光。十年前的我还是一副骄傲的样子,每每使出征得同学喝彩的新奇的技巧,也常把已知的抽象结论具象成有趣的比喻和段子。那时的我们往往因为一个数学证明争得面红耳赤。而阔别十年之后,再想起所谓数学,更像是一种禅啊道啊之类的东西。所记得的什么什么向量张成空间,哥德尔不完全定理,也都像变成了“一生二,二生三,三生万物”和“道可道,非常道”一样的模糊的东西。只有他还和当年一样,像是《孔乙己》里的孔乙己,不厌其烦地纠正我的每一个细节错误。直到发现我对学问的记忆已经连不成片才悻悻,缄口不谈,倒一杯茶望天。而今天迎接我的则是纯粹的茶道知识了。他是好为人师的那种人,而我也不讨厌让他做我的老师:一来他的娓娓道来还是颇为动听,二来我还可以时常享受一下挑战权威的乐趣。
他抿了一口茶,看着窗外的雨没有说话。 相对于他多彩的生活,我不禁为自己的单调和局促感到伤悲。按说我们是不会相互嫉妒的一种朋友,因为他远高于我;而高处不胜寒又往往需要某个地热来取暖,偏偏我又是一个机灵、敏感、时常给他提供一些奇怪灵感的地热。现在我却着实有些恐慌,感觉属于自己的那些奇怪灵感也在随着单调的日子偷偷溜走。恍然回神,雨又不知下了多久。路上已经积了薄薄的一层水,一个穿着校服的女孩匆匆走过,书包顶在头上,衣角被举过头顶的手臂带着上扬,又被雨滴打落。短裙下白皙的双腿踩着土气的校服皮鞋,打扰着水银一样的街道。两个校服男孩打着伞从街对面走过,互相推搡着笑骂,却最终因为害羞没有把伞递给她。我笑笑,他看了看我,脸上倒是出人意料的平静。
上高中的时候,有一场青春恋爱喜剧是很难的。他很幸运地有过一段,在我所知的故事里,也就和芸芸众生一样无疾而终了,在这一点上他并没有比谁更出众。 他并没有向除我以外的人显示他的难过,我很可怜他,同时也被他对我的信任感动着。那段时间他变本加厉地演算题目,只是鉴于他一贯的用功,他的变本加厉在旁人看来毫不稀奇。于是他愈加地觉得不被人理解,愈加卖力地演算。后来在一个我们共进午餐的中午,我们在雨后初晴的运动场上散步时他对我说,”我发现如果用曲线系的方式去做解析几何的压轴题会特别奏效“。我笑着给他取了外号“沃尔夫斯凯尔”。之后的日子一如往常。然后我们在那一年的六月参加高考,去了同一个城市的不同学校。
他嘟囔着,说了一些连不成句子的话,打断了我的回忆。仿佛他在尝试着用最通俗易懂的语言,向一个文字校对解释他在实验室里经历的种种趣事和烦恼。使我想起他曾经说,爱因斯坦是不屑康德的学说的,然而哥德尔证明了广义相对论和康德哲学的一致性。一个优秀的科学家应该抛却对某个想法的执念,相信证明和推理,不为了自己的失败伤悲,反而应该为共同发现了真理而雀跃,说得好像一个理性的人就失去了他难过的理由。我笑着喊他,还记得吗,沃尔夫斯凯尔,你不应该为自己脱离了一段失败的感情,发现了新的世界而雀跃吗。他说滚你妈蛋。
如果接受新的真理是痛苦的,那或者一辈子执迷不悟的人是幸福的,离真理一步之遥的人也是幸福的。我问他,做了研究员之后,最喜欢的一句话是什么。他沉默许久,说, “Wir müssen wissen. Wir werden wissen.”(我们必须知道,我们将会知道。)
我知道,他想说难得糊涂。

二刷《信条》

本文或多或少包含一些过度解读。

我很久以前就打算为《信条》认认真真地整理一下语言。这部电影的结尾带给我非同寻常的震撼,同时解决了我长久以来对宿命感的困惑。具体表现为曾经的我不能理解《你一生的故事》(特德·姜)结尾母亲能够看穿一切痛苦和不幸,却仍毅然决然选择走在命运指引的道路上。更恰好的是我在观赏《信条》前对Judea Pearl其人非常感兴趣,刚刚读过他的《为什么》(The Book of Why),并看过对他的一些采访。其中某个采访中有一个问题有关自由意志,Judea Pearl从很“计算”的角度回答了这个问题(参见视频)。这个回答与人是否真的有自由意志似乎不太相关,但它让我感觉到“自由意志”和“宿命”两个概念并不是不可调和的。至于如何调和,我们慢慢聊。

第一次看完信条后,我在B站看了时间逆转装置的解析,大概理清了故事的流程时已然感慨万千。不过当时觉得仍然需要二刷来确认一下自己的理解。时间一久,就把这个项目放下了——因为每次想起二刷之后就要坐在屏幕前苦思冥想把很难表述的这些想法打理清楚就觉得十分麻烦。更何况还是在无雨的加州(笑)。这次二刷信条的契机,在我随手本上是这样记录的:

当再和小孩子谈起感情建议的时候,面对那些“他对我到底有没有好感”的翘首以盼的时候,我可以清晰地感受到初恋的悸动和雀跃;也能看到(大概率会)随之而来的分手的苦涩。我深知这喜悦和苦涩的味道,但此时此刻我能说的仍然只是“他应该是喜欢你的”。然后任凭一切开始——看到一切幸福与不幸,却仍然放任其开始——这大概还是一种乐观。对了,我还是应该再看一遍《信条》。

如果说“信条”在电影中仅仅是一个时间部队的名字,加上诺兰想玩一个TENET回文游戏的话,似乎有些过于简单了。我倾向于认为“信条”在电影中恰恰是其字面意思,信条,指导某人行动的信念。虽然在荧幕上展示的时间流在动作上都严丝合缝,但诺兰并未在剧本中解释太多的问题,一切稍微越界的问题全部都抛给“不可知”和“混沌”。而在此设定下,诺兰的世界实际上每时每刻都在崩溃的边缘,且崩溃的缘由并非只有武器大亨Sator的“算法”这一种。实际上更危险的崩溃来自于人的不可控——如果你真的逆行三十年并杀死了自己的祖父,会发生什么?如果你知道自己将死的未来,从而穷极手段去逃避,会发生什么?不断尝试改变、失败、逆行、继续尝试,整个世界会在这个扭结处不断纠结无法向前,最后扭曲成一团丑陋的样子。并且概率上说,如果时间线如此扩张发散,总有至少一条会触碰到Sator的算法。电影中对时间之流有很多浪漫的处理(是的,我知道一件随机小事带来的蝴蝶效应就可以完全毁掉他们的计划,更不用说是几十年规模的漫长行动),毕竟是电影,总不能展示一盘子意面混沌给观众看。但很明确的是,没有几位主角视死如归的坚持,他们的世界线一定会扩散、崩溃、陷入混沌或者湮灭(因为“算法”的完成,陷入混沌可以等同于湮灭,如上所述)。

于是,电影的开头变得容易理解:CIA老板挑选男主的时候是看重他视死如归的决心(吞药),并且告诫他要阻止世界毁灭你需要记住“信条”,皆因为此。最终他们能完成任务,解除“算法”,无疑也是因几位主人公都是坚守信条的人:尼尔有宁愿牺牲自己也要完成任务的觉悟;男主有知道结局的悲伤也不恻隐、最终招募了尼尔的坚定;女主有牺牲一切也要守护孩子的决心。这些“信条”让他们无论何种情况都会做出相同的选择。正因为尼尔会为了男主而牺牲自己,所以男主才会得救——至于时间,只是给了事情发生的契机,但时间并不参与在这因果关系当中。正是通过玩弄时间的把戏,通过让时间的流向可以随意操控,才能展示出那些不为时间所动的东西。

此刻你也能隐隐地感觉到“宿命”与“自由意志”是两个维度的概念了吗?尼尔的宿命是和男主老哥度过一段快乐的出生入死的岁月,然后孤独地逆向走来,再葬身于此。但尼尔从未施行过他的自由意志吗?他的每一个选择都是由他的自由意志做出的。你追问,“可是如果说他的决定都受他内心的柔软和善良所驱使,那这真的算‘自由’意志吗?”。没错,Pearl回答你,自由意志是一种幻觉。甚至,“自由”本身就不是幻觉(simulacrum)吗?而“宿命”是在此幻觉(simulacrum)之外的概念,像是从四维空间俯视三维空间,或说看四维空间的一个投影——诚然,我们的过去与未来昭然若揭,我们的选择在赤裸裸展开的第四个维度上只是一个坐标的微小扰动——但我们不能、也不应该去这样看问题,因为这不是我们切身所感的世界。Jordan Peterson说,人的意义一大部分在于解决当下的痛苦;“一万年后你我不过是历史的尘埃”不能成为此时此刻遭受病痛折磨之人的安慰之词。所以宿命存在,自由意志也在,我们仍然需要努力生活。“Free will is also a nice exercise。”

最后,希望《黑客帝国4》不要搞砸。

粗俗地速记一点对“事件驱动”的新理解

最近在给公司写一个高性能代理,成品在不久的将来会以GPL协议开源。关于这个项目我会写一组博客,目前已经有了一个草稿,详细内容有待填充。

这两天一直在思考如何重构代理的工作线程部分,以及需要的话应该如何实现多路复用(multiplexing)。昨晚看了一个Boost.asio的思路,早上又读了读HAProxy是如何抽象epoll的。asio应用起来思路非常清晰,但抽象层次略高于我需要的Proxy;HAProxy的抽象层次合适、思路干净,但对于我的Proxy的应用范围来说又过于复杂。晚上散步继续琢磨如何将两者的优点整理进手头的项目时,突然体会到一点程序模块间分工的“第一原理”。思路从“事件驱动”(Event-Driven)而来,有点类似“单一责任原则”,但又稍微更泛泛一些。粗俗地说:

事儿来了,会干的部分干明白;不会干的部分交给会干的干。

似乎有点显然,我稍微展开一点。

艰苦朴素

所谓代理,要做的事情不过就是读取A的消息,转发给B;读取B的消息,转发给A。一个最简单的代理逻辑如下:

所谓(edge-triggered-)epoll,不过就是当文件提示符(file descriptor)的状态发生变化的时候产生一个事件,并在事件里包含该文件提示符现在的状态,如是否有新消息可以读,是否有空间可以写等等。但这里存在一个小问题——如果epoll产生事件告知程序”这个fd有数据可以读,而现有的read buffer空间不足以读完全部内容时,epoll将不会产生另外一个事件(因为“读”状态一直都是准备就绪的,也就没有状态变化来引发另外一个事件)。所以每次有一端将内容传递给下一段发送后,程序一定要手动调用handle_*_read来检查一个fd是否还有残留的数据,所以这里我们加一个逻辑:

在早期版本中,这段代码(其中一个函数)长这个样子:proxy-a-bit-messy-imo (github.com)

可以看到tunnel的成员函数既需要关心buffer的状态,又需要明白epoll的关键字(EAGAIN/EWOULDBLOCK),并且还要记得在适当的地方重试(最后一行)。主观上这个复杂程度(对于目标应用来讲)算不算高暂且不论,它几乎没有任何抽象,需要理解每一个层面的问题才能正确工作。另外它直接依赖epoll的关键字,使得代码较难重构。

重构

这个重构吸收了一点HAProxy的设计,大概长这个样子:proxy-a-bit-less-messy-imo · GitHub

在写这段代码时我的思路是这样的(如上文的“第一原则”所言):

  • 我真的想不清楚当我收到EPOLLIN和EPOLLOUT(事件中的参数)的时候应该做什么,但是至少我可以对它们进行解释。
    • EPOLLIN是说哪里有一些数据可以读了
    • EPOLLOUT是说哪里有一些空间可以写了
    • 所以我先抽象这个“哪里”出来,按成规起名叫Channel;然后仅仅用事件中的参数更新自己的状态
    • 更新完状态之后做什么?我不知道,但有人应该知道,所以啥都不做
  • 有一位兄台,它不懂、甚至不想知道什么是EPOLLIN和EPOLLOUT。但它知道如何做双向转发,随便起个名叫Pipe。
    • 不管我需要做什么行动,在此之前肯定至少有一个事件抵达,所以不需要额外的事件触发器(需要一点分析,请随意跳过)
      • 如果我需要读,之前一定至少有一个EPOLLIN(没准备好读什么?)
      • 如果我需要写,之前要么有一个EPOLLIN(不读数据写什么?),要么有一个EPOLLOUT(腾出空间写了)
      • 如果我的fd一直没读完(因为此端的读buffer满了),那么此间彼端一定EWOULDBLOCK在写(否则彼端的写buffer应该是空的,可以转移此端读buffer的内容并重置状态),所以此后一定有一个EPOLLOUT
    • 如果A能读,让A读
    • 如果A有数据,B能写,把A的数据给B写
    • 对B也一样
  • 那么让它们俩都实现一个事件处理器(EventHandler)接口
    • handle方法负责“干明白会干的事儿”
    • next方法负责“对于不会干的事儿,找一个会干的人”
    • propagate方法负责把消息传递出去

几个优点:

  1. 主观体验好了一些(less cognitive load)
  2. 可以将系统IO换成poll/select/kqueue甚至io_uring,不会影响上层的代码逻辑(lower change amplification)
  3. 更模块化一些,例如可以在不替换Pipe逻辑的情况下,将Channel更换为支持多路复用的MuxChannel(lower change amplification)

综上,根据《软件设计哲学》(A Philosophy of Software Design (John Ousterhout))所述(详情),这个重构降低了系统的复杂程度,是一个改进。

另外:

  1. 两个gist都不是最终代码,仅供演示参考
  2. 为啥要用epoll这么底层的东西写?主要是想学造轮子(我闲的);其次是我们确实需要一个支持AF_VSOCK的高性能代理——扩展现有的成熟框架(wangle, asio, etc)的话要么难度也不小,要么是框架本身太重。
  3. 似乎已经不是“速记”了啊…写了蛮久,还学了学怎么用Inkscape画图。

AWS Enclave & KMS

AWS Enclave is a type of VM whose host machine cannot access its memory. The host machine can only use certain APIs that’s provided by an enclave via vsock. This secure feature is provided by AWS Nitro System, therefore an enclave has to run on a Nitro-based EC2 instance. Also, an enclave image is built upon a docker image with aws nitro-cli.

Other than secure from host machine, a running enclave instance can generate attestation document that can somehow (I’m planning to dig deep in later posts) be verified by AWS services like its key management service (AWS KMS), so an enclave can use kms:decrypt without exposing any encryption/decryption details to host machine.

Think about the following scenario: you have a web service and wants to distribute a client library, and you need to make sure your web service only communicate to the client you distribute to. Or, only your client can understand the response.

If you haven’t seen the difficulties in this problem, hint: how can you prevent the host intercepting your http (even https) to get the messages and poke the memory of your client to get the secret, then make a fake (probably malicious) client to cheat?

AWS Enclave provides a solution like this:

  • use a secret to encrypt all messages
  • store the secret inside your client, so your client can decrypt the messages
  • distribute your feature inside a enclave image, so your user cannot access the secret from memory
  • decrypt with a service that can check the validity of enclave attestation document
    1. you may decrypt with kms:decrypt and setup key policy to automate attestation
    2. you may implement your own attestation and let the client load the key on start

Next part is my following the code to find corresponding APIs in the sample enclave-kms project. May be too detailed and not so interesting. If you are ready click ‘Page 2’ (WordPress never failed to surprise me).

再碎碎念一篇,然后发个技术博好了

原来的标题叫《我似乎解决了那个值得解决的问题,但于是更加困惑了》

说说彩六,1943.9小时。围绕着它度过了过山车一般的一个月,快乐也忙乱的一个月。认识了一些人,失去了一些人;经历了从惊喜到失望,从“啊原来这就是”到“哦其实不可能”——然后我还是在自己原来的地方。看起来风浪很大,不过终究还是无聊的生命中无聊的一个月。有一段发觉自己原来从未真正体验过彩虹六号,而今只觉得我可能永远也不会真正体验彩虹六号。所以就这样吧,最后一次谈这个话题。

关于ClubHouse:无聊至极。

算了不写了,先睡觉了。真是到了却道天凉好个秋的时候了。

如果这个主题反复出现,说明它是一个值得解决的问题

我听了一部广播剧,然后被其制作之精良给感动到了。

这篇博客是完全的碎碎念,原标题:我的人生有1800小时的彩虹六号。

截至此时,steam记载我的彩虹六号围攻游戏时间是1799.4小时,其中包含一些躺下睡着了忘记关游戏的挂机时间。不过毕竟数字在那里,我们就这样算。

问题在于,坦白说,我好像没有在哪一件事情上投入过这么长的时间。所以常常会想,如果这些时间被用来做A,做B,做C,此刻我的生活会不会有什么不一样。应该会的。好像太久没有专注在什么当中,也太久没有感受到所谓“成就感”了——以至于写一篇博客都很难。愚以为,除非是技术主题的,想写出一篇不自怨自艾的博客多多少少需要点mansplaining的自信。

最近在读…此时我的眼睛在书桌周围疯狂地扫视每本书的书脊,但无一想得起内容来。大概最近什么都没有在读。二月初有一个难得的面向全公司做去年项目的汇报机会,但是苦思冥想(大概也没有很认真地)过后还是只能记得零散的片段,暂且没法连成一个什么整体来讲述,酷似这篇博客。这种无法深入思考的感受自从居家隔离开始便困扰着我—没有责怪居家隔离的意思—因为我是擅长在人前表演出积极的样子的,然后依着fake it till you make it的定律,往往在实际上也能小有成就,至少做得还不赖。但离开了众人后失去了对比,就很难明白自己都在做些什么。

广播剧的名字叫《顾小姐和曲小姐》,作者晚之。没有什么应该特意在此推荐的理由,也没有任何传统意义上的“文学价值”。All Credits Here. 我很好奇这些从五湖四海聚在一起的配音演员和美工们是出于什么想法在做,或许会盈利吗?我不知道。但是确实让我回想起小时候和论坛的坛友们一起考虑做NJ(Net DJ,现在不知道还在不在用这个词了,可能统一叫主播了)的时候,几个女生录节目,我负责在论坛上加MP3播放器。那时候Flash还是主流的媒体工具,之后Adobe用十年的时间来缓慢的下葬它,最后被大连高铁胜利攻关。说这个也不单是怀旧,大抵是我最近看了韩寒新开的直播空间,满满的少年感,心里不住地羡慕,也想找一下少年的感觉的原因。

和付桐一样地,老板最近也常和我提起First Principle(我还是分不太清Principle/Principal)的事。互联网行业总是被形容成瞬息万变的风口,但就First Principle而言似乎还真的没有什么大变化。黑盒白盒,输入输出。另外,郭德纲老师说过,计算机也是两件事——一个是程序,一个是存储。他说出来的时候我还小诧异了一下,不知道是拾谁人的牙慧,但对相声演员来说这个总结足够惊艳。之后话题就回到腌咸菜了。

我可以理解国内义务教育教师口中流行的那句“不要对了就沾沾自喜,要记住自己哪里错了”,但我真心认为很多时候“知道自己为什么是对的”更难。照着标准答案分辨错误是显然的,但是在没有标准答案的世界里不知道自己为什么是正确的,好像会很迷茫很辛苦。

从对我有意义的方面来看,我的生活变化很小。一样的三八生活(八小时睡觉,八小时工作,八小时做自己。并且居家隔离的背景似乎还强化了这一点),一样的东奔西走结果什么都做不成。十年如一日地希望减重。责备自己之后又原谅自己;再到原谅得有些过分。那天在散步的时候我问自己:

如果每时每刻的我都是我,那么我应该满足哪一个我的要求?是那个要求“及时行乐”的我吗?从来、永远都不是吗?

回答第一个分句是容易的,道德和自我要求让我很容易否决其建议——总是满足最近处的需要长远来看绝不是好的策略。但妥善回答第二个问题有点难度。如果我从不满足当下的我的(属于七宗罪之一的)愿望,是不是也不算过有俗世兴味的生活?我内心里是不想脱俗的。

最后我是这样和自己约定的:不是每个长远的目标都值得追求;但是如果一件事总是在我的新年愿望中出现,那么就不加犹豫地做它。长久以来东奔西走的灵魂可能也确实觉得合理,想到这个准则的那一瞬间,身体里好像有什么东西落了地。

所以我说,如果这个主题反复出现,说明它是一个值得解决的问题。

我们应该真诚地生活——看《绿皮书》

本文有剧透成分。

在欣赏电影和剧情向游戏的时候我很少两次踏入同一条河流,破例的情况屈指可数:小时候想和表哥为了熬夜而熬夜看过二十几遍《终结者2》;实在太喜欢《金刚狼3》夹在现实主义和浪漫主义之间的奇妙路线,看过首映之后,等黑白版发售之后又在宾馆电视里看了一次黑白版;应该还有,但是确实屈指可数。

从影片上映起至今,我看过三次《绿皮书》。第一次在影院,第二次在回美国的飞机上,第三次忘了。万事开头难——这部电影女朋友比我早看到一些,之后就全力推荐我去看。不过她的话术有些糟糕,讲故事梗概的时候还是把它归为了“讲种族问题的那点事儿”,搞得我下意识有点抵触。不久之后,我刚好在mall里躲雨的时候发现里面有家影院,就贡献了《绿皮书》一张票。名义上,我想,这样回去和老婆好交代;实际上是同期的片子要么看过了,要么真提不起兴趣。

看完之后直接的记忆只有感动和震撼,因为它绝不是“讲种族问题的那点事儿”。如果您也和我一样因为这类概括而错过了它,我想说,早看晚看都来得及。写到这里,虽然还没入题,还是有一点题外话想说。惯于和网友对线,疏于整理日志来记录自己的思想,似乎让我不设置好一个靶子便很难有的放矢。鉴于此,请原谅我复制豆瓣的简介如下:

托尼(维果·莫腾森 Viggo Mortensen 饰)是一个吊儿郎当游手好闲的混混,在一家夜总会做侍者。这间夜总会因故要停业几个月,可托尼所要支付的房租和生活费不会因此取消,所以他的当务之急是去寻找另一份工作来填补这几个月的空缺。在这个节骨眼上,一位名叫唐雪莉(马赫沙拉·阿里 Mahershala Ali 饰)的黑人钢琴家提出雇佣托尼。

  唐雪莉即将开始为期八个星期的南下巡回演出,可是,那个时候南方对黑人的歧视非常的严重,于是托尼便成为了唐雪莉的司机兼保镖。一路上,两人迥异的性格使得他们之间产生了很多的矛盾,与此同时,唐雪莉在南方所遭受的种种不公平的对待也让托尼对种族歧视感到深恶痛绝。

https://movie.douban.com/subject/27060077/

在我眼里,托尼不是一个简单的、可以用“吊儿郎当”和“游手好闲”来形容的混混。他人情练达,关系网广泛:无论经济状况如何都能经营和睦的家庭;和兄弟们有健康的关系;在社会上总有人愿意为他提供工作。他有小聪明:用“帽子戏法”骗取大佬的欢心,懂得怎么用灰色手段快速解决问题。他做事果决:该出手时就出手,能打也抗打,因此大家也都觉得他可靠。所以即使是一开始的托尼,也不是一个随处可见的混混。

但不止于此。我总觉得他有一种莫名的魅力——能感觉到,但很难表达。和不同的朋友聊过很多次,才渐渐和每个阶段的自己统一了话术。当然这个角色有忠诚、勇敢之类很明显的优点,在此就不再提。

这个角色打动我最深的地方,是他对于高雅的生活的毫不掩饰的向往。他纵使不习惯和不同肤色、不同阶级的人生活从而显得有些尴尬,但当他第一次体验到唐的演奏的时候所流露出的欣赏是无比真诚的,他甚至立刻开始和旁边的小哥自夸:“看,那是我朋友,是不是牛B?”。看似平常,但是我很少见到谁在自己不欣赏的人做出有趣的成就时给出相似的赞美。我甚至说它是一切使托尼的生活发生好转的主要原因,因为它成全了托尼的那种真诚的拿来主义。唐教他给老婆写情信。他不排斥唐那套文绉绉的行文方式,也不在乎别人是不是能看出他在偷学拼写。他很单纯,“有人教我如何给我的老婆写好信——这是一件好事”。想想现实中人面对如此简单的命题,还要拧巴许久才能承认(甚至不承认)其正确性,很难不被他的纯粹所打动。

相比之下,唐这个人是非常拧巴的——这两个人的特点似乎都可以归咎于环境。托尼是小平民,大概也没受过好的教育。有聪明(我甚至说他有智慧),但是受限于眼界,只能在力所能及的地方略施小计。唐,他的拧巴也有原因:南方白人的怒拳相向倒还好,北方白人暗戳戳的歧视确实很让人难以捉摸;而同时他作为一个黑皮肤贵族又难以和底层的黑人相处。在肤色和阶级两个维度上都和主流群体完美错过,确实别扭。

之所以说这绝不是一部“种族问题”电影,一个原因是众所周知的讲“种族问题”的电影都不争气。总结起来就是它们一贯的表现手法都会陷入身份政治的圈套里。但我认为《绿皮书》是反身份政治的,它的白人混混、黑人精英的设定如果被理解成“白人也有烂货,黑人也有高尚的品格”其实是一种逆向歧视(你细品),所以绝非如此。我认为它和身份政治无关,甚至是反身份政治的理由是:我没有看出在身份意义上这两个人解决了任何事情。我不相信托尼在经历此事后就会对所有黑人视如兄弟,同样也不相信唐会在未来的“黑白两道”走得更加容易。他们之间温柔的化学反应,恰恰是因为两人发现了彼此作为个体的闪光点,有让自己向往的部分——唐过得自尊、富足而高雅,托尼活得简单、通透又真诚。

然后有一天二号靶问我,“你不觉得托尼从一开始的深度歧视,到后面接受唐到家里过圣诞,这个转变有点过于突兀吗?”我不觉得突兀,但是统一话术还是用了几分钟时间。

结论是这样的:我不认为电影一开始的托尼打心眼里就那么讨厌黑人。他把黑人水管工用过的水杯扔掉,在我看来更像是一种表演。我不知道这算不算Toxic Masculinity的一部分(这个话题可以之后再聊),但是在东北男人看来,这种“表演”是很好理解的。要在雄性之中脱颖而出,一个屡试不爽的方式就是在大家向往的方向做得过火。靠规则管理可以解决的事情,一定要做个物理屏障,Alpha;明明有棍子,非要用拳头打老虎,Alpha;口罩戴着尴尬又不舒服,所以干脆不戴,Alpha;你们都暗戳戳的搞种族歧视,我一定要明着搞,显得不共戴天,Alpha。(我真不是在说川普。)托尼下意识地进行如此“表演”是很好共情的,毕竟他一直以来赖以生存的手段就是不断让人认为自己是Alpha。

但唐的生活状态就缺乏了表演吗?唐有更多的表演,虽然并不Alpha。印象最深的、被我谈论最多次的就是炸鸡环节:托尼请唐吃炸鸡,唐婉拒;托尼认为黑人都喜欢吃炸鸡,所以表示了惊讶;唐认为托尼用肤色概括自己,所以表达了愤怒;托尼并没有理解他的愤怒,依然盛情邀请他吃炸鸡;唐抱着怀疑和不满的态度试了一下,发现真好吃。然后俩人吃了一路。

他们因着各自的理由表演下去,这理由主要来自一种群体幻觉。因为有着种族歧视的历史,又有半个国家还未开化,托尼隐约觉得他的兄弟们讨厌黑人。也因此,他的兄弟们觉得他也不喜欢黑人。每个人的表演在Alpha争夺战中愈演愈烈,变成了习惯性的、无来由的歧视。也是相似的原因,唐觉得每一个把他和“黑人”连接起来的符号都是对他的侮辱,以至于他一度与两个群体都脱离了联系。这些幻觉在反复的强化中被赋予了实体,直到两个灵魂真正的交会才能澄清。故事结尾,唐终于愿意坦诚“自己的孤独是自己的选择”,决定去托尼家敲门。托尼也愿意为这个珍贵的朋友放弃自己营造的Alpha幻觉。而见到唐的一家人,在尴尬了一秒钟后马上邀请唐在桌边就坐。在那一秒钟里,我能听见全家人的幻觉融化的声音。

我容易且愿意被真诚的生活打动,是因为我自己也是一个别扭人。感觉似乎应该再加一段合适的结尾,但是太久没有写字,写到这里已经头皮发麻了。姚若龙的一句歌词我一直记忆犹新,已经从早恋时代哼唱到快到晚婚。“这是我们的纪念日”,范玮琪唱,但不是纪念什么相爱、牵手、拥抱、接吻。她唱“纪念我们开始对自己诚实”。所以大抵也确乎是一个重要的日子。

Understanding Position-Based Dynamics

This post will be my notes on PDB study and will only include points I feel note-worthy. It is by no means a well-rounded tutorial. Btw I learned the principals from this online course: https://www.youtube.com/watch?v=fH3VW9SaQ_c (kudos)

Along with the note I’ll implement the distance constraint to illustrate how it works.

Reminders

  • PBD does not even try to simulate actual physics. It only looks good. All physics ‘references’ (for example, so-called momentum conservation) are attempts to make the scene look real.
  • PDB algorithm is a constraint solver. The algorithm introduced in the course is one-constraint-a-time, Gauss-Seidel method.

Math Review

  • Gauss-Seidel updates proposed values during iteration
  • Jacobi updates values only after each iteration (symmetrical?)

PBD Loop

before mainloop, all vertices should be assigned an initial position x_i, velocity v_i and invert-mass w_i (1/m_i).

  1. for each vert i, update proposed velocity v_i = v_i + t * w_i * external_force_i
  2. damp velocities. a simple way is times 0.99
  3. for each vert i, update proposed position p_i = x_i + t * v_i
  4. for each vert i, detect collection and generate collision constraints x_i -> p_i
  5. solve constraints – it’s numerical solver, so here will be an inner loop
  6. for each vert i, finalize velocity v_i to be actual velocity (p_i – x_i) / t
  7. for each vert i, finalize position x_i to be p_i

Cheatsheet

Distance constraint
C(p_i, p_j) = |p_i, p_j| - d
(look carefully you will notice it is not Hooke's law)

For constraint C(p1, p2), with invert mass of w1, w2 respectively,
Gradient of p1: (p1-p2)/|p1-p2|
Gradient of p2: (p2-p1)/|p2-p1|
Update of p1: -w1/(w1+w2)*(|p1-p2|-d)*(p1-p2)/|p1-p2|
Update of p2: +w2/(w1+w2)*(|p1-p2|-d)*(p1-p2)/|p1-p2|

Code

GitHub Gist: https://gist.github.com/stormouse/90a8d22582ad4e9bc6c91c4a5f80e842

https://editor.p5js.org/stormouse/present/lbMzg3OsY

Research: Indoor CQC Map Generation (1)

Objective: write an algorithm that can create a floor plan (single story) that is reasonable enough to be used as a top-down close quarter combat (CQC) game map.

I don’t know how to do it yet, so I’d study some basic floorplan design principles. The remaining of the article would be my notes watching this video.


Inputs

  • Size of the floorplan
  • Number of stories
  • Single/Multi-family
  • Number of each type of rooms, also garage
  • Thickness of external/internal walls

Features of Space/Room

Y/S/N – Yes/Semi/No

  • Sqft needs (Y/S/N)
  • Adjacencies (list of room references, immediate or not)
  • Public access (Y/S/N)
  • Daylight/Views (Y/S/N)
  • Acoustical privacy (Y/S/N)
  • Plumbing (Y/S/N)
  • Special equipment (DW, MW, Refri…)

Macro Constraint Solving

  1. Fill in the features of each unary feature (anything other than adjacency) with average value + variation
  2. Create a few reasonble and strict requirements on adjacency
  3. Constraint solving on adjacency
  4. Generate Relationship-Diagram (9:08 in the video)

Grouping by Constraints

  1. Group rooms by their features. (e.g. kitchen+living room + dining room since none of them requires privacy)
  2. Place rooms in different directions and makes it somewhat reasonable.

Abstract Floor Plan

  1. Follow the grouping result, draw circles of correct scale with respect to the actual space requirement, at the approximate place we planned earlier.
  2. Ensure each “connected component” is pure, rather than mixed with private and non-private.
  3. Set daylight direction and try to satisfy lighting/view requirements.
  4. Resolve adjacency problems by not allowing adjacency edges to cross. (How)

Bubble Diagram

Draw bubbles (fat circles just like in the abstract floor plan) and make them fit.

Block Diagram

Growing bubbles and make them squarer. Okay to leave some space in between squares as hallways.

(How to make things align though?)

Figure out room sizes

Tip: sum of furniture area + circulation room ~= final room size

Ideas: pre-make furniture assets so save some difficulty generating furnitures.

Open Correct Doors

  • Open correct doors according to the adjacency map.
  • Make sure every room can be accessed
  • Special restrictions for bathroom
  • Keep doors away from beds, toilets, desks

Walls

  • Pluming walls
  • Exterior walls
  • Seperating walls
  • No walls (Imaginary walls that’s removed with common sense, e.g. living & dining & kitchen)
  • Half walls (Shelves or anything that works as a half-separation)