2016-波纹疾走

标题借用了JOJO的招式名。当然这一年我没有去打吸血鬼或者柱之男,不过新世界不断在眼前展开的感受是相似的。

这一年的大部分精力都放在工作上。我们经历了技术队伍的迅速扩张、系统的成熟稳定、团队人员的缩减、销售的迅猛展开,到年底又启动了品牌客户项目。一路上踩过千奇百怪的坑,试过各种各样的辙,系统开发再改进,人员招聘又开除,到现在队伍稳定在五六十人,也初步找到了下一步的方向。这一路不算顺,有的步骤动手晚了,有的措施没达到需要的效果,不过公司还活着,还能活一阵子,成功的希望也仍然在我们手中。现在是黎明前的黑暗,我们可能坚持到日出,也可能倒下。但无论接下来遇到什么,这一年的经历都是丰富而宝贵的。唯独有一点感到惭愧的是,我已经快一年没写过工作的代码了,现在最多是用用SQL,其他多是面对人的沟通协调工作了。

这一年生活中遇到了很多事情。各种被介绍相亲无果就不必说了。意料之外的是被攻略了,同样意料之外的是被攻略只是觉得感慨,并不觉得开心。我感觉可以看到眼前浮现出自己抛出的选择肢,看到对方的行动拿到了我怎样的flag和对应的好感值。从某种意义上讲或许有趣,但首先是悲哀的。那时我其实已经被攻陷了,最后却还是因为信任和价值观的问题全部清零。

生活中的另一件大事,是至亲的离去。其实,我对这件事已经有十五年的心理准备,甚至多少成为一种心理障碍了。真到面对那一天的时候,发现自己能够冷静下来,也发现心中还是充满了无法消解的情绪。无奈,愤懑,悲伤,迷茫。对剩下的人来说,生活,已经完全变了。

这一年的业余爱好变得单薄。Galgame只打了三四部,汉化开了个没翅膀的坑却长期毫无进展,新安洲只做了个躯干,此外花了点时间在英雄无敌6上。书更是只读了薄薄的一本——王国维的《人间词话》——这些年我真是越来越远离文化了。唯一有点进度的是催苍蓝的剧本坑,他终于写完了初稿开始修改,画图的人手也基本到位了。

回头看看,去年制定的目标,大约只做了一半。新的一年,不定那么多了,尝试新的生活,做出新的成果。

搞定Lucifen,再次体会到rUGP有多难

7月底,暂离汉化一周年,忽然动了翻俺翼的念头,于是开始搞俺翼R的程序。出乎意料的是,只花了二十多天的业余时间,就把从解包到剧本封包的主要问题都解决了。跟当年搞君望LE的程序一比,天上地下——解决君望LE的解包和初步封包前后花了大半年。单纯以花费的时间衡量的话,君望LE大约比俺翼R难十倍到二十倍。即使考虑自身水平提高、改用Python开发、资料更丰富等因素,也无法忽视两种引擎复杂度的巨大差别。

首先是封包格式和对象种类。俺翼R等Lucifen引擎的游戏有多个LPK包,分别存放脚本、背景、立绘、语音等内容,每个包开头的前缀树和索引数组里记录着包内所有对象的文件名和位置大小。Crass早已提供了前缀树和索引数组的解码算法,只需要跟程序抓一下密钥就能用了……即使不会解析其中某些对象,至少能拆开LPK包。相比之下,君望LE的rio文件没有全局索引,而是把所有对象存成树形。一个对象如何连接到子对象与它的类型、内容有关,只有正确解析了一个对象,才能确认它的子对象存在哪儿、有多大。把rio文件中的所有对象取出来,几乎等于解析所有对象。这是一个可怕的工作:君望LE中已经确认的对象类型不少于88种,而且都是需要进一步解析的二进制数据。相比之下,俺翼R的LPK包解开后,需要进一步解析的基本只有elg、sob、tob三种对象。

图片解析方面差别不大。君望LE这边,westside的四个函数可以合并成一个;俺翼R这边,Crass的四个函数可以合并成两个。当然,原来的算法都是有效的;不过既然做静态替换,需要写逆算法的函数自然是越少越好。两边的难度都在算法清理和合并,写逆算法并不难。

脚本的情况和外层封包类似,虽然都是二进制脚本,但是俺翼R有全局索引信息和强特征,君望LE没有。俺翼R的tob脚本开头有记录命令组分割点的数组,而且命令组中除可自由调整的剧本正文外,每条命令开头都有特定标记。于是,很容易分割命令和提取文本,封包也不用顾忌正文长度的改变。君望LE的脚本复杂得多,11种脚本对象的解析依赖于更深一层约300种命令对象的解析,而且缺少共通的结构。我在RioX中采用的方法是,对假设树剪枝以定位每个对象开头的标记双字。虽然这个双字没有强特征,但是相邻对象标记双字的差和对象长度之间存在弱对应关系。一般情况下扫完整个脚本,假设树中就只剩一个假设了。封包时,对象长度有限制,标记双字也需要调整。

解决了解包封包,处理中文显示就有套路了:先改取字体的API,再改双字节字符判断。然后,就可以开始翻译了。

默默开坑——《俺たちに翼はないR》翻译开始

从12年8月21日君望开坑,到15年8月3日君望完整汉化发布,断断续续做了三年汉化。之后忙创业的事,度过了脱离汉化的一年。现在生活状态有所改变,准备抽一部分时间回归翻译。

随着公司的发展,虽然一直忙,但是有规律有计划的工作在减少。我需要把能招人分配出去的工作尽量分出去,腾出精力处理工作中发生的问题;反过来越来越多的突发问题也让我无法给自己分配计划性的工作,因为处理突发问题很可能造成这些工作的耽搁。结果就是,我一天到晚要时刻准备着,也会处理不少事,但是总有很多手头无事的碎片时间。既然如此,我想业余还是做点galgame翻译吧:有趣,能换换脑子,不怕被随时打断,没有进度压力。

至于翻《俺たちに翼はないR》,主要是两方面的考虑。一是有共鸣。其实当年我也曾想写自己的剧本的,也有过一个很模糊的构思。但是读到俺翼之后,发现我不用写了:我想写的主题甚至形式已经有人写了,而且达到了我无法企及的高度。那么现在我把它翻译了就好。二是文本难度很高,不担心被抢坑。当初君望从容填坑,有程序和文本双重困难护航;俺翼的程序虽然易破,文本难度却更高,长度也不亚于君望。从各方面查到的信息显示,俺翼有过两个组开坑,第一个组历经两年后弃坑,第二个组自12年接坑以来没多少动静,至少已经沉默一年多了。最近有人发出来的第一个组翻的文本我看了一眼,只占全剧本的不到10%。时至今日,游戏发售已经过了七年,会去翻而且能坚持下来的,除我之外未必再有吧。

考虑到文本难度太高、作品知名度偏低、我自己无法保证进度这三点,这次就不尝试拉队伍了,纯粹自己做。投入程度会低于君望,不保证进度,根据情况随时可能选择放弃。至于质量,只能说锻炼自己,尽力而为——毕竟王雀孙写俺翼时就是一副要把日语玩坏的架势,日本人阅读原文都可能觉得费劲。

无论如何,今天还是决定默默开坑,在此记录一下。作为前期准备,已经初步完成了《俺たちに翼はないR》的解包封包程序。最近二十几天利用工作之余跟踪程序编写代码,已经做到可以把翻译后的GBK编码的剧本封回去正常显示了,汉化所需的基本功能已完成。跟当初搞君望的程序相比,只花了大约二十分之一的时间——Lucifen就是这么简单,或者说rUGP就是这么难……

最后,我只是想默默做点有趣的事。看到本文的朋友们就不要替我公布了。

一道奥数题

昨天姜总在微信里发了几张图,说是今年奥数国家队选拔的题。心血来潮就去做了一道。做它其实是因为它不需要太多的知识,初中代数几何加上极限的epsilon-delta思想就够了。最喜欢这样的题了~ 更不要说这道题简洁优美。这才是真正的数学和数学探索,才不是被人民大众妖魔化的那些奇葩东西。

犯了很多错误后,具体证明整理了一两个小时。要在当年,这道题我应该还是能在规定时间内拿满分吧,不过一天也就一题的样子吧,距离国家队还是很遥远啊。

题目:求所有正整数映射到正整数的函数f,满足以下条件:对任意三个互不相同的正实数a、b、c,长为a、b、c的三条线可以构成三角形,当且仅当长为f(a)、f(b)、f(c)的三条线段可构成三角形。

证明:

命题1. f是单射
用反证法,若不成立则存在不同的正实数a和b,使f(a)=f(b),不妨设a<b
则a、a+b、3(a+b)/2不构成三角形(因a + (a+b) < 3(a+b)/2),故f(a)、f(a+b)、f(3(a+b)/2)不构成三角形
但是b、a+b、3(a+b)/2构成三角形(因b < a+b < 3(a+b)/2且b + (a+b) > 3(a+b)/2),因而f(b)=f(a)、f(a+b)、f(3(a+b)/2)构成三角形
矛盾
于是假设不成立,f为单射,可设f的逆映射为g

命题2. 对任何正实数a!=b,对所有非负实数x,f(a+b+x)或者不小于f(a)+f(b),或者不大于|f(a)-f(b)|
首先a + b <= a+b+x,a、b、a+b+x不构成三角形,故对所有非负实数x f(a+b+x) >= f(a) + f(b) 或 f(a+b+x) + f(a) <= f(b) 或 f(a+b+x) + f(b) <= f(a)
其中 f(a+b+x) + f(a) <= f(b) 或 f(a+b+x) + f(b) <= f(a) 即 f(a+b+x) <= f(b) - f(a) 或 f(a+b+x) <= f(a) - f(b)
亦即 f(a+b+x) <= max(f(b)-f(a), f(a)-f(b)) = |f(a)-f(b)| 得证f(a+b+x)或者不小于f(a)+f(b)或者不大于|f(a)-f(b)|

命题3. 对任何正实数a!=b,对所有非负实数x,或者f(a+b+x)总不小于f(a)+f(b),或者f(a+b+x)总不大于|f(a)-f(b)|
要证明的是不会出现两个非负实数x1<x2,使f(a+b+x1)和f(a+b+x2)一个不小于f(a)+f(b)另一个不大于|f(a)-f(b)|
设y=x2-x1,存在足够大的正整数n,使得正实数z = y/n < min(a,b)
对k=0,1,…,n-1,必然存在k使得f(a+b+x1+kz)和f(a+b+x1+(k+1)z)一个不小于f(a)+f(b)另一个不大于|f(a)-f(b)|
(因为如果这一点不成立,对应k=0和k=n的f(a+b+x1)和f(a+b+x2)将会同时不小于f(a)+f(b)或者同时不大于|f(a)-f(b)|,与假设不符)
于是|f(a+b+x1+kz) – f(a+b+x1+(k+1)z)| <= f(a)+f(b) – |f(a)-f(b)| = 2min(f(a), f(b))
另一方面,a、a+b+x1+kz、a+b+x1+(k+1)z构成三角形,所以|f(a+b+x1+kz) – f(a+b+x1+(k+1)z)| < f(a)
同理|f(a+b+x1+kz) – f(a+b+x1+(k+1)z)| < f(b)
所以|f(a+b+x1+kz) – f(a+b+x1+(k+1)z)| < min(f(a), f(b)) < 2min(f(a), f(b))
矛盾
于是命题3得证

命题4. 对任何正实数a!=b,对所有非负实数x,f(a+b+x) >= f(a)+f(b)
也就是排除命题3中的另一种可能性,对所有非负实数x,f(a+b+x)总不大于|f(a)-f(b)|
用反证法,若命题4不成立,根据命题3,对某组正实数a!=b,对所有非负实数x,f(a+b+x) <= |f(a)-f(b)|
设 c = g(max(f(a), f(b))),d = g(min(f(a), f(b))),则f(c+d+x) <= |f(c)-f(d)| = f(c) – f(d) < f(c)
考虑不构成三角形的c、c+d+x、c+d+x+c,故 f(c+d+x) + f(c+d+x+c) <= f(c)
令x=0和c/2,两式相加,有 f(c+d) + f(2c+d) + f(3c/2+d) + f(5c/2+d) <= 2f(c) 但是c、c+d+x、c+d+x+c/2构成三角形,f(c+d+x) + f(c+d+x+c/2) > f(c)
令x=0和c,两式相加,有 f(c+d) + f(2c+d) + f(3c/2+d) + f(5c/2+d) > 2f(c)
矛盾
于是假设不成立,命题4得证

命题5. f严格单调增
根据命题4,对任何正实数a<b,f(b) >= f(a) + f(b-a) > f(a)

命题6. 对任何e > 0,存在正实数x,使f(x) < e
nf(1/(n+1)!) < sigma_{i=2}^{n+1}{f(1/i!)}
根据命题4,对正实数a!=b,f(a)+f(b) <= f(a+b),故 nf(1/(n+1)!) < f( sigma_{i=2}^{n+1}{1/i!} )
由命题5,nf(1/(n+1)!) < f(1),就是说 f(1/(n+1)!) < f(1)/n 对任何e > 0,存在足够大的n使 f(1)/n < e,取 x = 1/(n+1)! 根据单调性即有 f(x) < e

命题7. 对任何正实数a!=b,f(a) + f(b) = f(a+b)
反证法,假设不成立,则存在正实数a!=b使f(a) + f(b) < f(a+b)
设e = f(a+b)-f(a)-f(b),根据命题6,存在正实数x,使f(x) < e/2,则f(a) + f(b) + 2f(x) < f(a+b),不妨设x<min(a,b) a、a+x/2、a+x构成三角形故f(a)+f(x)>f(a+x/2),同理f(b)+f(x)>f(b+x/2)
进一步地a+x/2、b+x/2、a+b构成三角形(a+x/2<a+b,b+x/2<a+b,a+x/2+b+x/2>a+b),故f(a+x/2) + f(b+x/2) > f(a+b)
于是f(a) + f(b) + 2f(x) > f(a+b),矛盾
于是假设不成立,命题得证

命题8. 对任何正实数a和b,f(a) + f(b) = f(a+b)
a!=b已证,a=b时 f(a) + f(b) = f(a) + f(a) = f(a) + f(a/3) + f(2a/3) = f(4a/3) + f(2a/3) = f(2a) = f(a+b)

然后就是教科书上最经典的函数方程了,先证有理数然后用单调性证无理数

在BitBucket拆分git代码库操作备忘

把一个大的代码库的其中一部分拆分出来产生新的代码库,保留提交历史。参考BitBucket官方说明和部分网文。

1. 创建新的空代码库newrepo。

2. 在本地把旧代码库clone下来并放进文件夹newrepo。

git clone git@bitbucket.org:user/oldrepo.git newrepo

3. 进入文件夹解除到远程库的链接。

git remote rm origin

4. 删除不需要的文件及其记录,可以是曾经存在但现在已删除的历史文件。其中git rm部分如果报错说匹配不到文件,就加入–ignore-unmatch选项。

git filter-branch -f –index-filter ‘git rm –cached -r dir1 dir2’ — –all

5. 删除变空的提交。这里会遗留一些合并时产生的提交。

git filter-branch -f –commit-filter ‘git_commit_non_empty_tree “$@”‘ — –all

6. 回收垃圾

git gc –aggressive –prune=now

7. 重设远程库并推送代码。

git remote add origin git@bitbucket.org/user/newrepo.git

git push origin master

五律·百日

行游爱峻奇,百夙进天梯。

烁日冬犹夏,流风北向西。

踯躅多顿挫,寂寞少凭依。

此去云山远,漂泊未有期。

——2015.11.16

七律·探父病感怀

严君病重信忽闻,
千里心萦忐忑深。
夜雨急趋归省路,
晨星怅叹旅羁人。
相扶莫挽凋颓势,
反哺无还养育恩。
惜取余时休错过,
流年无影岁无痕。

君望汉化:终于只剩我自己了

age社的游戏,过去多少年里大家都说“这个引擎太难破,解决了就能汉化了”。老实说,我一开始也是这样认为的。所以两年前下功夫搞定了文本解包封包的问题,并期待着有翻译把我的成果用起来,把“有生之年”的君望和Muv-Luv系列翻出来。

但是我很快发现自己想错了。其实在我看到Muv-Luv Alternative的坑在某组人手里晾了几年的时候,就该意识到的。翻译同样是难关。君望和Muv-Luv三部曲文本量都是巨大的,而且不容易翻:君望文风含蓄细腻,Muv-Luv三部曲则涉及大量政治军事设定。Muv-Luv三部曲有幸在几位八月党的手中变成了中文,君望则仍然在我和KFC的手里。

去年三月,君望第一章的汉化以KFC风铃工作室的名义发布。在汉化第一章的半年里,大家分头初翻,然后我和苍蓝各过一遍。虽然30%的文本靠我初翻,30%的文本靠我重翻,总的来说还是比较顺利的。但是,第一章毕竟只是个引子。遗憾的是,第一章发布之后,当我们面对第二章遥/水月线五倍于前的文本时,KFC这边几乎就没有人手来支持君望这边了,包括苍蓝。在最近的一年半里,除我之外的所有人一共只做过二期80多万字中7%的初翻和0.5%的润色。KFC的工作重心在近月及其续作上,现在和不远的将来都没有力量进行君望的汉化。所以,君望的汉化在很长时间里停滞了。

我的目的是填坑,不是占坑。既然KFC无法提供支援,而我的翻译技能也在这两年里成长起来,那么我的选择就只有脱离组织,独自汉化。经过几个月的奋斗,今天,遥线和水月线的全部文本终于完成了初翻和一次校润,第一章的文本也重翻了。接下来的两周我将尝试一下图片的替换,然后在8月27日水月生日那天发布遥线和水月线的汉化补丁。

这次发布的汉化,原文文本包括第一章大约100万字,其中77%的文本完全是我翻/校/润的,没别人碰过;为了提高质量和统一风格,其他23%我也重翻了。以个人名义发布,本身也意味着,所有文本的正确性和通顺程度都由我负责。可以预计到,一定会有人诟病翻译质量——其实作为玩家我也会的。翻译文艺作品本身就是困难的再创作,现有文本的质量我也不算满意。但是一个进入三十代的互联网码农在上班和加班之余搞出来的东西,毕竟是有限度的。不过,我有信心宣称我的翻译质量比大多数汉化组都高,也高于之前发布的第一章。

汉化是长期艰苦的义务劳动。汉化界的主力是学生,因为他们有更多时间。但是,真正在意质量的,啃硬骨头的,往往却是已经工作的大叔们。君望也是如此:开坑的时候摇旗呐喊的不少,过来翻了一点点就消失的也有几位,然而落实到出相对完整的汉化补丁,从程序到翻译都是我一个人的事了。我是真心想要把这件事做出来。我会坚持下去,至少把茜线和三章补全,给出完整的主线。