大家好,我是 Calcitem 方解石,一名程序员,开源业余爱好者。
Calcitem 后面多加了一个 m?是的,不是笔误哈,Calcite 这个单词很容易被先占,所以加了一个 m。
去年对自己而言是特殊的一年,《Sanmill 直棋游戏》经过两年的开发后,首个版本正式发布,并被收录到 F-Droid 应用市场。
项目初期相关调研、UI设计、代码编写是由我独立制作,所以借着 Sanmill 这款游戏,我想和大家分享一些开源软件作品制作的流程和五味杂陈的小故事,包括创作和制作的过程涉及到的方方面面。
这是一个开源项目制作过程分享,不是教学篇,如果大家也想建立自己的开源社区,希望写下的这些能给你一些帮助,大伙看着开心就好哈!
做一款简约的棋类“微游戏”,公交上,地铁上,年轻人随手利用碎片时间三分钟轻松来一局。可深可浅,可易可难,遇到有意思的棋局可以导出分享,是自己很多年前就一直想做的事情,因为一直担心自己水平和沉淀不够,掌控力不足,或是做出来体验不好,或是重复造轮子做无用功,所以在长期等待属于自己的时机。直到2019年的6月,感觉树,泥土、水、气候都合适了,条件和时机到了,便开始行动。
首先需要选择一个合适的题材。做复杂的游戏很难,做出简单而不失趣味的游戏也很不容易。
我的视线落在了直棋上。直棋规则可简述为:“先摆后移,成三吃子,余三飞子,无路者负。” 简单的规则给我留下了深刻的印象。随着对直棋的更进一步的了解,自己开始萌发依据这个题材创作微游戏的想法。
直棋在中国曾经是一种流行的民间游戏,别名很多。目前只能找到两本介绍民间棋种的书有提及直棋。一本是金盾出版社1997年出版的《民间棋类100种》,另一本为化学工业出版社2019年出版的《民间棋类游戏大百科》,下图为此书对直棋的介绍:
在网上能找到的视频比较少, 《柳州三棋传奇》 是其中拍得还不错的一个。
小时候大家或许有过玩直棋的经历,随着电脑和智能手机的普及,年轻人接触这些棋类游戏的机会越来越少,直棋逐渐被人们遗忘。如果能够通过制作一个游戏,以唤起对大家回忆那些和长辈,和小伙伴们一起玩直棋,喊“三”的瞬间,并将这种棋类传承下去,未尝不是一件美事。
下面来说说直棋的特点。
大道至简。直棋规则简单,容易被记住,每个人都可以轻松上手,老少皆宜,更容易近一步普及。
但直棋要玩精通不简单,新手下棋往往二三十步不到结束战斗,但如在双方水平都很高的情况下,有些棋局甚至走到八九十步甚至两百步以上才能分出胜负。
直棋与中国古代文化和思维的方式有不少契合之处。
古语有云:“夫美者,上下、内外、大小、远近皆无害焉,故曰美。”里里外外皆均衡妥帖,方为“美”。直棋棋盘体现出和谐、庄重和对称美。棋盘上有三个圈。我们的世界,万事万物都在循环,日出日落、月盈月亏、四季更替、生老病死、细胞的分裂和消亡,等等等等,都是循环发展的。整个世界就是活动着数不清的圆圈。
黑白双方,一阴一阳。
“3” 在直棋中是一个重要的数字,棋盘上有内圈、中圈、外圈共三圈,成三即可吃子,少于三则负。
在中国文化里,“三”表示多,凡事不过三,很多时候,三具有典型性,如:三甲,也是一条界线。三连在一起就成了“丰”、“王”。
“三”的前世经历 一文中提到:
“三”在中国文化中是个很重要的数字,中国人的好多文化观念都是来源于三。《说文》中说,“三,天地人之道也。从三数。”这是说,“三”首先体现的是宇宙天地和人生社会的意义,上为天,下为地,中间挺立的是人,这充分体现了中国哲学中的天人合一、顺应自然的和谐文化理念。
带三字的成语:事不过三、三生万物、三人成虎,三顾茅庐、三迁之教、三省吾身、三思而行、三阳开泰、狡兔三窟、三分鼎立、三寸之舌、举一反三、入木三分、退避三舍、三人行必有我师、此地无银三百两,等等。
“九”是最大的数字,在中国古代,它象征着多元和广阔,“九州”泛指中国。
此文中提到:
中国古代的天干地支纪年法中国,有十二地支。还有十二生肖、十二时辰、十二个月,古代皇帝王冠上的摆穗也是十二个。
其实12在与中华文化里是一个非常有特殊意义的数字,十二这个数字在中国的历史上一直有着独特的含义,从十二时辰,到十二月份、十二生肖,在中国,十二就是一个轮回。
中国传统文化的十二的含义,是指向天道与人道的合一,代表了人间所有的变化,更是指向了所有中国传统社会的朝代的更替与兴衰。
棋盘上有24个点。二十四在中国文化中也是一个重要的数字。
《中国哲学体系的展现—二十四节气》一文中提到:
二十四节气是古人留下的一串密码,在劳动人民解读自然规律的同时,贵生意识、阴阳五行思想、天人和谐理念等中国特有的哲学理念成为万能的密码本,为中国人理解世界提供广泛的灵感。
在标准九子直棋中,当一方剩下3枚棋子,另一方剩余6枚棋子时,6比3的局面不足以奠定胜局,大概率会成为和棋。甚至只剩下3的一方都有反败为胜的机会。而7比3的局面才是必胜局。
七本身是阳数,有微阴中出,则阴阳相济,万事备矣。
在 《数字7隐藏着宇宙密码?为何说万物逢7必变?答案刷新认知》一文中指出:
数字7在世界各国文化中都占有一定的地位,会产生不同的情感寓意以及文化观念,所以数字7对整个世界的文化有着广泛而又深刻的影响,虽然中西方文化存在着明显的差异,但是数字7在中西文化当中都是具有神秘色彩而又含有深刻意义的一个数字。西方人不仅认为数字7还有幸运、美满、多数等定义,并且天有七重、地有九重的说法,认为七重天堂为极乐世界。
《闽南民间「直棋」记趣 》一文指出:“直棋蓄含九宫八卦玄机。”
文中未具体说明具体的联系,我尝试做了一些解读,如有不准确之处还望大家指出哈。
乾,意为天,能量最大,最稳定。直棋中,成三后的一方也有能量,可以吃掉对方的棋子,而已成三的三枚棋子也很稳定,因为在标准规则中,当对手成三时,无法取出已经在“三连”中的棋子。
坤,意为地。大地很软,万物被大地滋养。在直棋中,三个空点存在很多可能想,棋手可以放在任何想放的地方,很有弹性。
坎,意为水。人离不开水,水对人很重要,所以将棋子放在中间占据活动力强的点很重要,这会给后续发展带来很大的潜力。
离,意为火。火灾意味着紧急情况。将棋子放在三点的中间,这样可以防止对手成三,同时占据中心点也是优势点,因此应优先考虑,紧急。
艮,意为山。山的意思是告诉我们要适可而止,不要过分奢求。爬山的时候,累了就休息一下,不要勉强自己,不然还没到山顶就上不了山了。有的事做得快,有的事得做得慢。例如,开车的人没有太多的时间来欣赏周围的风景,骑单车的人可以细细欣赏风景。所以,该走快的时候就走快,该走慢的时候就慢走。在直棋中,三个空位占据一点,就适可为止了,不要想着在在剩余空位再放一颗子冲三,在有经验的棋手面前往往威胁不大。
震,是雷声或地震的意思,所以当遇到地震时,应该非常小心。如果对方放了一颗子在空位,需要小心识别其真实意图。
和其他棋类不同,直棋是非常违反直觉的,尤其是九子直棋。以下列举几点:
执后手比先手容易得多,因为后手方可以在摆棋阶段战略性地放置最后一枚棋子,进入走棋阶段后更容易控制局面。
不要试图成为首先成三的一方。通常在摆棋阶段成三的第一个人很容易被封锁。
多子未必就占优,三对四的局面说不定对于三枚子的一方是必胜局。哪怕开局先让对方吃一子,也未必会影响局势。此所谓“愚者求子,智者求势”。
关键的一点:你试图赢,你就会输。不能想着赢棋。先瞄准和棋的目标,让自己避免输棋,观察对手,还可以轻轻将对手引诱到容易犯错的局面中,等对方露出破绽,就像一只兔子奔跑中撞到树桩上,便抓住机会轻轻推动,这样你就赢了。
这和孙子兵法的思想是相通的。
《军形篇:学会等待—不作死就不会死》一文中指出:
先胜后战
记住四个字最关键:先胜后战。我们把它翻译一下,就是赢了再打。 我们经常说打仗要打得赢,不对,赢了再打,没有赢就不要打,这就叫做先胜后战。我们来看一段原文:
孙子曰:“昔之善战者,先为不可胜,以待敌之可胜。不可胜在己,可胜在敌。”
孙子说,古代真正善于作战的人,先规划自己,让自己成为不可战胜的,这叫“先为不可胜,以待敌之可胜。”然后再等待敌人可以被战胜的时机。 “不可胜在己”,完全在于自己,而什么时候敌人可胜呢?那完全在于敌人,不归我管。
孙子曰:“故善战者,能为不可胜,不能使敌之可胜。故曰:胜可知,而不可为。”
这个人是很善战的一个良将、大将、名将,他最大的本事到什么程度?就是能让自己成为不可战胜的,但是他没有本事让敌人一定可以被战胜。所以说, “胜可知而不可为”,胜利是可以提前知道的。 但是如果说不可胜呢?你是不可强求,不能把不可胜变成可胜的。
一句话,人管得了自己,管不了别人,先管好自己再观察别人,别人如果无懈可击,我们是没办法取胜的。“胜可知而不可为”,可以判断我们能胜,但是没有胜的形势,不可强求。
不可胜又不可强求,你应该怎么办呢?第一个策略就是不办。 为什么一定要办呢?明明办不了,你还非要办。
后面还有一句话,叫“善战者,胜已败之敌也”,他自己已经败了,这时候你赶紧去推一把,叫“胜已败之敌”。
等待是重要的军事行动
所以如果不可胜,那么就应该不办。很多人败就败在不知道事情可以不办,就是所谓的不作死就不会死,办不到的事不要强求,留得青山在,不怕没柴烧。如果非要办,反而会输光老本。
不可胜怎么办?不办。很多人就接受不了这一点,怎么能不办呢?不办不就是不作为吗?必须要有所作为,因为他不作为心不安。这是病,得治。什么病?这是一种战略焦虑症,忘了作为的代价、损失和风险。 事实上不出手,并非不作为,而是积累自己,等待时机。
《三国演义》里面,诸葛亮就是不停地作为,几乎是没有任何胜算的作为,最后是把自己累死了。他应该等,等待时机,锻炼身体。一是争取自己活得长;二是把国内治理富足强大,教育好下一代传承下去。
所以等待是一个非常重要的策略,等待在很多情况下,都是最好的选择。 可惜呀,认识到这一点的人太少了。
等待什么呢?就是等待形势的变化,形变成为形胜,势变化为势胜,这时候我就可以先胜后战,赢了再打。 形胜是在等待中积累,让自己不可胜,越来越强。 势胜是胜机出现,然后你就抓住机会,一战而定,这些都只有通过等待才能得来。
你可能要问了,那等不来怎么办呢?等不来就算了呗,还非要归你呀?你一定要认识到,这个世界不是围着你转的,不是所有事你都能搞定的,该放弃的时候你要放弃。如果志在必得,那你可能就是灭亡。这是一个人生态度问题了,是个价值观问题了。
我们从这一篇里,就是要学一个等待的待,等待不是不作为,往往是最重要的军事行动的组成部分。
但是我们人生当中,很多人最大的毛病,就是不能等待,不能等待就是自己作死。
敌人是不可战胜的
我们可以用一句你听起来可能不那么带劲的话,来概括一下孙子的思想:**敌人是不可战胜的。**后面再加一句,只能等他自己败。
那你说不能战胜那怎么办?相安无事呗。他也不能战胜,你也不能战胜,大家不打,各过各的日子不是挺好的,为什么一定要打呢?你不能怀有战胜他人的目的。
还有一个关键,是自己不要作死,我们往往就是自己作死。
如果对方也一直不犯错,怎么办呢?
《始计篇:诡计,就是引诱对方犯错误》一文指出:
第一, 诡道是引诱对方犯错误。李世民说,兵法就四个字 “多方以误”,就是想方设法地去引诱对方犯错误。
第二,别人对我“多方以误”,我怎么能不上对方的当呢?现在我们有一句话叫做“不忘初心”,在兵法上面有一句话叫“不忘本谋”,你本来是怎么谋划的,你别忘了。
当然,虽然直棋反直觉,但也有一些是符合直觉的地方,如尽量占据交叉点,这样棋子的活动能力更强,和其他棋类是相通的。
一花独放不是春,百花齐放春满园。
国内乃至世界均有很多直棋变体规则,九子直棋风靡湖南部分地区。其他地方以十二子直棋及其变种居多。《十二子直棋与三种设计》 此文详细介绍了各地的棋规差异,值得一读。
轩辕剑柒中的逐鹿棋这种变种就更为复杂了,可谓脑洞大开。
有意思的是,东西方都声称自己为直棋游戏的起源地。这里我不探究起源,而是更注重寻找东西方文化共通之处,直棋是全人类的,全世界能在直棋中找到共同的语言。
直棋有在国内外均一定的群众基础。
下面分享几张从搜索引擎和社交媒体上扒到的图:
从《农村老汉集市上对阵“成三棋”》 一文中可以看到,在国内,画风是这样的:
棋盘也很难买到。题外话,之前曾经在淘宝上购买了一个“侗族三三棋”棋盘和棋子,是卖家因为为了弘扬三三棋文化而制作的,因为投入成本高,这批卖完就不会再制作了,只剩下最后一副棋盘了,算是绝版。卖家免费寄送给我当作纪念了,感动。
在西方国家,尤其是德国、瑞士和匈牙利,以及南非,直棋是一项形如国际象棋这样很严肃、正式的运动项目。他们有直棋协会,举办正式比赛。
以下为一些棋手在社交网站上分享的2021年匈牙利全国直棋锦标赛的照片:
这是德国某地绘制了直棋棋盘的公园,这样在地面上画有棋盘的地方在德国还有不少。
国际象棋、中国象棋、带“贴目”规则的围棋、带“禁手”规则的五子棋只能算是相对较公平的游戏,而非绝对公平游戏。直棋则是绝对公平游戏。即双方均走出完美着法的情况下最终结果是和棋,这是已经得到机器证明的结论。
瑞士计算机科学家 Ralph Gasser 教授在 1996 年首先使用机器证明了九子直棋 (Nine Men’s Morris) 标准规则为公平游戏。
台湾师范大学资讯工程研究所的李明臻于2012年在论文《台湾直棋的胜负问题之研究》证明了台湾直棋也是公平的规则。
柏林工业大学的匈牙利计算机专家 Gábor E. Gévay 于2014年通过机器证明了其变种规则南非直棋和 Lasker Morris 也是公平游戏。
注:直棋是绝对公平的游戏,和上面曾经提到的“执后手比先手容易得多”并不矛盾。
Sanmill 的开发上,前期优先实现常见的棋规,并加入完美数据库生成和读取的相关功能,后面大家都可以在这个平台上自由地添加和改良各种棋规,然后使用已有的逆行算法代码推理证明棋规的公平性。
开始行动之前,确定项目的理念为:文化传承、中国元素、简约、纯粹、安全、可控、可扩展、可持续、可定制。
不少人可能玩过刺客信条中的莫里斯九子棋,但是以通关目的为主,从各 app 的网上评论可以看到不乏 “玩不过刺客信条” 的评论,可见很多人是冲着通关的目的下载。这个作品将不是满足用户一时之快和好奇心而作,而是立足于弘扬和传承直棋文化。
软件作品和其他艺术作品不同的地方是,绝非一锤子买卖,作品的发布只是开始。未来想尝试的功能扩展还有不少,会在社区的加持下持续创作,参与者一起打造,继续演绎着新篇章。即便光阴流逝,岁月荏苒,项目也能一直延续和发展下去。
首版软件必须包含中国的元素,在规则的设置上,包含成三棋、打三棋等常见变种。项目的英文名称 Sanmill,中的 “San” 就是“三”的发音,“Mill” 在英语中指“直棋”。所以这是一个中西兼容的合成词。
要做简约的游戏。这是一个大众化的棋类,一个功能专业齐全但易于使用的用户界面是上上之选。我希望的界面是简单、质朴、柔和的。棋盘使用木色,背景使用草色。希望单纯的色彩让玩家能联想到生机勃勃的绿草,看到清新带有质感的自然之美。背景宣传图则是真实拍摄的,以草地为背景,木色的纸板为棋盘,深邃,静谧,希望玩家看到宣传图后能更容易唤起这种联想。
要做纯粹的游戏。这个软件不使用闭源的组件,以保证安全可控。App 不申请特殊权限,也不会做网络版,网络游戏作弊判断是非常复杂的。
为了适应玩家的需求,让整个作品都能自主可控,不能简单地用一些开源组件搭建出来,而是对核心模块,包括算法引擎进行深度优化,在 AI 算法效率上要力争做到最优,不做第二。
发展统一接口标准。提升通用化水平。做好拆分和分层,将业务逻辑和算法引擎分离。后台引擎提供类似国际象棋引擎 Stockfish 所使用的 UCI 接口。
App 主要由 C++ 实现的游戏引擎和 Dart 实现的业务逻辑层组成,使用两种语言是为了相互取长补短。C++没有 GC卡顿的问题,适合处理算法。这一层只负责变化不太大的 AI 逻辑。与 C++互补,Dart 层主要负责变更相对频繁的业务上层逻辑。有些游戏相关的逻辑,需要 C++ 和 Dart 语言分别实现和维护,未来还将切换为 FFI 的形式。
最终希望能核心引擎能够被合并到 Fairly-Stockfish 项目中,因此引擎部分的代码风格尽量模仿和复用 Stockfish 的风格。
对于 UI,把选择权留给了用户,添加了很多颜色配置选项,当然对于普通用户来说,选择一个合适的配色方案并不容易。未来还将保存为预设功能,形如下面 SmallFish 这样:
做好基础设施,提升持续集成度,提升代码资源复用,将代码静态分析尽量做到位。广泛使用各种开源代码托管平台,充分利用这项平台提供的丰富的代码检测功能,提升代码质量。单元测试即便不能做到广泛覆盖,也会先把测试基础框架搭建好。
“好软件是用出来的”,开发完基础功能后就发布,倾听玩家的反馈,后续迭代升级不断完善。
在创作之前需要进行充分的准备,争取全面搜集资料。自己此前在直棋题材这个领域做了各种功课,包括试用应用商店中40多款 app,使用 PC 上的完美数据库,阅读国内外的论文,了解大家曾经是怎样创作和研究的,再力求精益求精,力争把作品做深,做出自己的特点。在首版发布后,也得到了热心用户的很多帮助,持续积累完善。
研究每一款 app 的界面和设计思路,每一次和专业人士交流,都是学习的好机会,每一次新的发现都能使自己激发出想象不到的潜在能量。
前面已提到,直棋是绝对公平的游戏,这是经过机器证明得出的结论。在机器证明过程中,通过逆行算法,可以生成每个局面的数据,包括对双方而言是赢棋、输棋、还是和棋的局面,如双方完美行棋,多少回合后棋局分出胜负等这几项信息。
有了完美数据库这个“直棋上帝”,我们就可以让 AI 和完美数据库对战成千上万盘,统计和棋率(不是胜率,因为完美数据库是不可战胜的),作为 AI 优化的效果衡量参考标准。
值得一提的是,人类顶级高手有些着法并非完美,因为高手知道人类可以处理哪些线路,不能处理哪些线路。有的赢棋线路非常长,完美数据库能看到,但人类算不到。所以高手往往会选择数据库中所谓的错误着法来引诱对方犯错,但没有人可以利用。对人类着法是否绝妙的评判不仅是看完美数据库,更重要的是这种引诱的效果,这和对 AI 的评判角度是不一样的。
目前能找到的完美数据库有:
费伦茨·沃尔曼和乔吉·班迪开发的 Brilliant Mill 不开源,为收费程序,通常用于严肃玩家分析棋局。
优点是支持常见的开局:
德国程序员 Madweasel 在自己主页上发布了一个完美数据库,解压缩后为 60G 大小,代码开源。可惜其没有严格遵守标准规则。例如,在下棋阶段结束时,当黑方放置最后一个棋子形成三连,黑方全部被封锁时,Madweasel 的做法是判断黑方负,而标准规则是让黑方移除白方的一枚棋子并继续进入到走棋阶段。其代码目前已经被集成到 Sanmill 中,不过只在 Qt 的部分可以使用,用于验证 AI 的优化效果。Flutter 的部分则未加入此模块,因为完美数据库所需内存大,故不适合放到移动设备中。
大多数玩家不想和完美数据库对战,因为没有任何赢棋的机会,是索然无味的。当然也有一些严肃的玩家,希望能包含完美数据库的功能。
前面提到柏林工业大学的匈牙利计算机专家 Gábor E. Gévay 也参与了直棋变种规则公平性的机器证明。Malom 就是 Gábor E. Gévay 开源的算法代码。目前 Sanmill 暂未借鉴其代码,而是使用了上面提到的 Muehle。
奥地利格拉茨工业大学有一个在线完美数据库。用起来比上面的单机版方便很多。
Dirk Farin 与2009年编写的 Morris 程序,在 Ubuntu 下通过 sudo apt-get install morris
即可安装。
其特点是支持非常多的棋规变种,可惜没有一个中国棋规变种。
其 AI 实力也不错,支持多种参数调整。不过 AI 的算法实现的精细度有所不足,影响了走棋的效率。
任天堂《世界游戏大全51》中的直棋,很多人玩,可以作为验证 AI 效果的对比对象。如果 Sanmill 的棋力连世界游戏大全51都不如则是当然是不可接受的。
King of Mill 为专业玩家设计。最高的12级是使用在线完美数据库。奇怪的是,当网络断开时,11 级无法运行,因此可以确定 Kill of Mill 是通过将坏招混入完美招式来实现难度降低的。其很可能根本没有开发本地的搜索算法。
Master 是不带完美数据库的程序中 AI 实力最强的,不开源,因为其收费,因此没有下载到 PC 版的程序进行棋力对比。不过友人提供了一个 Android 手机版,据说没有 PC 版强。其具有等级分(ELO)计算功能。
不支持悔棋是最大的不足。
Doublemill 3 的UI 尤其是其动画效果非常值得学习。
Doublemill 3 的用户界面上有一个指向 Play 商店的按钮,但 Doublemill 已经从 Play 商店中消失了。原因未知。
Doublemill 在中国各应用商店随处可见,广为流传,但不支持中国规则。它的风格很清新,菜单是抽屉式菜单从左侧滑出,可作为 UI 的重点参考对象。
在直棋的中文百度百科网站上,Mill一词的内容原本是 Doublemill 3 的介绍。(在首个版本发布后,编辑了中文百度百科网站,将其替换为支持中国棋规的 Sanmill。)
初期,Sanmill 的 Flutter 用户界面的很多源代码都是从《中国象棋-棋路》这个开源的象棋程序中引用的,棋路的作者也给我了很多指点,甚为感谢。
棋路的作者发表的《实战 Flutter 象棋从零到上架》 非常值得一读。我也是从中受益颇多。
Android 和 Apple 的应用商店中有多个国际象棋应用使用 Stockfish 作为引擎。Stockfish 已经成为国际象棋引擎事实上的标准,这些作者可以专注于制作好的 GUI。Stockfish 作者自己做的 app 在体验上并非最佳,GUI 功能更强大的是 Smallfish,看起来像是在 Stockfish 官方 app 基础上进行了扩展。
Smallfish 是苹果 App Store 上最好的国际象棋应用,得分高达 4.8,分数之高极为罕见。它实现的功能值得借鉴,是 Sanmill 后续的发展方向。
国际象棋程序 Lichess 的分析功能比较好用,也值得学习。(目前 Sanmill 的分析功能暂未实现)
直棋大多数顶级玩家可以在 playok 或 flyordie 找到。顶级棋手现在大多来自那里,而且非常出色。在 playok,我们可以观看并下载他们的比赛以供以后查看。
最知名的国际象棋引擎为 Stockfish,现在有了 NNUE 加持后棋力超强,为事实上的标准。
Stockfish 的源代码非常清晰。人们可能只知道它是最强的国际象棋引擎。事实上,它的代码也是最好的。它设计精良,是一件艺术品。Sanmill 的算法引擎部分,从代码结构和变量命名都可以模仿,一方面是方便熟悉 Stockfish 代码的开发者能更容易地熟悉 Sanmill 代码,另一方面,也为 Sanmill 算法部分最终整合到 Fairy-Stockfish 项目中提供基础。
So gewinnt man Mühle 是德国棋手汉斯·舒尔曼和曼弗雷德·努舍勒于1986年写的指导如何下直棋的书。这本书非常古老,现在直棋理论已经发生了很大变化。直棋前世界冠军马斯库曾表示,这本书可以阅读它阅读它,但不要学习它,因为很多理论现在已经过时了,但对我们来说还是有用的。相比之下,Rainer Rosenberger 的资料更有参考价值。
接下来就是五年计划路线图了。分为下面几个阶段:
首先基于使用 Qt 编写的 NineChess 项目,在得到作者的指点后,在此项目基础上,主要进行了如下优化:
哈希表采用了 kshk123 的实现,并去除了无用的锁以及哈希桶。和 STL 自带的通用型哈希表相比,显著提升了性能。
参考了象棋巫师的 《Zobrist 键值》这篇文章 ,定义 Zobrist 数组。并参考 《置换表》这篇文章,以及 Stockfish 的最优着法搜索模块的基本逻辑,修改 alpha-beta 剪枝算法。
对 stack 进行优化,使用自编写的 Stack 类取代 std::stack,内部实现基于静态数组,提升效率。
实现着法排序
利用了人类知识,将棋盘点划分重要性,优先抢占高优先级的点位。而对于优先级相同的点位,使用随机排序,以避免人类按同样的赢棋线路反复获胜,提高可玩性。
在局面评估值相同的情况下,优先成三,其次堵对方的三。这和前面章节提到的不应过于激进地成三不矛盾,因为往往由于可移动性因子的评估值更高,使得局面评估值已经存在差异,没轮到同值情况下的比较。
实现不定长的搜索深度
优化排序算法
使用 Stockfish 的插入排序算法取代 std::sort
, 提升效率。
三次重复局面算和棋
Qt 部分能用标准 C++ 替换掉的尽量换掉
最重要的是将 Qt 的多线程管理相关代码替换为 Stockfish 中使用 C++ 标准库的代码,提升可移植性。
Qt 部分增加了网络对战功能
对战时偶然会停止,概率约10盘出现1盘,较高。因此后面改为使用基于共享内存的引擎对战功能。
Qt 部分增加了引擎对战功能
为基于共享内存的方式实现,用途是用于 AI 优化前后的不同引擎的对弈,评估 AI 优化效果。
适配并集成完美数据库功能
前文提到,如有逆推生成的完美数据库(直棋上帝),则可以作为参考检验 AI 算法的演进效果。
通过 Visual Studio 社区版的性能分析器分析每个代码片段的可优化之处,利用缓存预取等技术,不断迭代优化。
目前 Sanmill 的 Qt 版可加载德国程序员 Madweasel 的 Muehle 完美数据库了。Muehle 完美数据库是基于非标准的 直棋规则,所以我只用它来评估 AI 与完美数据库的区别。每次评估之前,需要临时修改 Sanmill 程序的宏开关以允许 Sanmill 使用非标准的直棋规则。
引擎优化完成后,开始实现 Flutter 部分。包括人机对战、人人对战、机器对战(更多是用于评估 AI 效果和稳定性测试)三大对战模式,以及悔棋、导出棋谱、导入棋谱、难度设置、棋风设置、规则设置、外观设置等基础功能。
这一阶段已于2021年年中完成。
2022年的主要工作是将 Flutter 平台通信机制迁移到 FFI 之上,基于 FFI 开发 iOS/macOS/Linux/Web 等其他版本。Windows 版本也将进行完善。
Qt 版本也需要近一步完善。
此为2023年的主要目标。
国内的棋规目前已经支持压子等常见规则,但还远远不够,后续还需增加担子等规则。
对于国外的棋规,重点是增加 Lasker Morris 以及南非直棋的棋规。
为2024年的主要目标。
当下人工智能的一个分支——机器学习应用带来了巨大的冲击力,展现出超强的实力。
如果能利用 NNUE,那么理论上 app 的体积只增加20-30M,便将棋力显著提升一个档次,或许有机会极大概率和动辄几十个G的完美数据库战平。这将是令人兴奋的一个提升。
此项工作的挑战性较大,因此规划较晚进行。
为2025年的主要目标。
根据国家出版总署相关政策的要求,手游,无论是网络版还是单机版,都必须获取版号,接入防沉迷系统后才能在国内应用商店上架,而获取版号的前提是需要先注册公司,获得经营许可证,加上版号的申请,总体费用不菲。
对于社区维护的开源项目而言当然是不现实的。因此,要想在国内应用商店上架,提升受众群体,需要先对 app 改造为教育类应用,主要内容是教人们如何下直棋,介绍直棋技巧,提供练习等等,而把人机对战作为一个小小的附属功能,并将其隐藏到深处。这就需要搜集大量的棋谱,是一个长期的过程,因此规划到很晚再开展。
下面就是开发过程啦。
得益于 Google 的 Flutter 技术,Sanmill 可以相对更轻松移植到各种不同的 OS 平台。Flutter 是2018年后逐渐开始火起来的(好时代!)。因为 Sanmill 起步较晚,没有历史包袱,所以选用了较新的 Flutter 框架。
软件作品和艺术作品不同的地方是需要尽量避免重复造轮子,是在多人的肩膀上的再创作。开始设计之前,需要进行制作环节中重要的一步,就是处理合规性,搞定许可证授权后,才能开始紧锣密鼓地制作安排。
每个组件都会有不同的授权方式,我们需要根据自己的情况选择合适的组件。
首先研究了各种许可证,关于许可证的 FAQ 以及各应用商店的开发者协议。 然后和许可不明的原作者取得联系,获取其邮件形式提供的书面的明示授权。最后确认直接使用的组件以及组件的组件的依赖都没有许可证兼容性风险,再开开始编码。之后,还用许可证合规性扫描工具扫描确认无异常。对于各个组件许可证明示不全的风险,引入了一个专门分析和显示许可证的包,将各种依赖组件的许可证通通扒出来显示在 App 界面上。
音频文件则选择了 Adobe Sound Effects,可以在软件作品中自由使用。
接下来开始制作的第一步,也许大家没想到,首先最重要的是重构——也就是修改原有的代码。改代码的过程比写代码还要艰辛,因为需要读懂原来的代码,如不把代码修改风格,写代码也不会顺畅。并且在重构的过程中也是熟悉代码的过程。作为程序员,我们不仅需要把对用户可见的部分做好,也需要关注代码的每个细节,对代码的处理手段会决定作品的品质。
重构过程中代码风格尽量往久负盛名的成熟的开源代码靠拢,比如 Stockfish。
重构完成后开始编码。编码过程中还可能伴随着不断的的重构。
Monkey 测试、自对弈稳定性测试是少不了的。有些 Android 兼容性的 Bug 真机才能复现,从咸鱼上套到一些古董手机,Android 版本很低的,分辨率特殊的手机进行测试。
编码和自测接下来就是发布到 Play Store 和 F-Droid。
之所以要发布到 F-Droid,一方面是因为国内应用市场无法上架,原因前文已说明。另一方面,F-Droid 只收录开源 app,他们会对源代码进行审核确认没有使用任何专有代码,安全可信后才会同意收录申请,因此被 F-Droid 收录就相当于完成了安全性认证。
下面来说说 Play Store 上遇到种种。
截至到2022年2月初,用户数统计如下表所示。安装人数排名靠前的国家和地区按每百万人用户数统计如下,可以看到伊朗以及中东欧的几个相邻的国家最为流行。美国的下载量估计大部分来自排名靠前的这些国家的移民后裔。
国家/地区 | 用户数 | 人口(万) | 每百万人所含用户数 |
---|---|---|---|
伊朗 | 2871 | 8482 | 33.85 |
匈牙利 | 248 | 977 | 25.38 |
瑞士 | 103 | 859 | 11.99 |
德国 | 893 | 8313 | 10.74 |
克罗地亚 | 34 | 408 | 8.33 |
奥地利 | 62 | 890 | 6.97 |
罗马尼亚 | 102 | 2218 | 4.60 |
丹麦 | 22 | 582 | 3.78 |
斯洛伐克 | 19 | 546 | 3.48 |
捷克 | 35 | 1069 | 3.27 |
香港 | 23 | 750 | 3.07 |
阿富汗 | 30 | 3289 | 0.91 |
俄罗斯 | 126 | 14548 | 0.87 |
乌克兰 | 31 | 4119 | 0.75 |
英国 | 49 | 6708 | 0.73 |
台湾 | 17 | 2357 | 0.72 |
波兰 | 25 | 3816 | 0.66 |
西班牙 | 26 | 4739 | 0.55 |
法国 | 34 | 6749 | 0.50 |
南非 | 24 | 6014 | 0.40 |
意大利 | 20 | 5911 | 0.34 |
美国 | 102 | 33247 | 0.31 |
土耳其 | 24 | 8361 | 0.29 |
巴基斯坦 | 46 | 22520 | 0.20 |
墨西哥 | 23 | 12601 | 0.18 |
印度 | 64 | 139580 | 0.05 |
中国大陆 | 17 | 141260 | 0.01 |
当然,一些国家和地区的数据太小,不足以判断差异。中国大陆的统计数据不代表直棋在中国的流行程度。
每周平均评分变化从刚上架时的4.9一直往下掉。除了2021年10月初和2022年1月初发布两个重要新版本时稍有上升外,其他时候总体趋势是下跌的态势,目前已经低于类似应用的中间值。
从崩溃数据统计看,没有发生明显的变化,基本可以排除是 app 稳定性问题影响。从评论的情况看,初期的差评多集中在棋规支持不完善上,可以理解,越到后期不明原因差评越来越多了,有的只打1星不留言,有的留言像是随手从其他地方帖一些不相关的文字贴上来。随着下载量的增加类情况肯定越来越多,这时还得保持一颗平常心,没有谁的作品能让所有人都满意,我们只能尽量做到最好。
目前 Play Store 的评论主要来自伊朗、奥地利、德国、俄罗斯、英国、印度、巴西、中国大陆、波兰、捷克等国家和地区。德国、奥地利、英国、印度、中国大陆、捷克这几个国家和地区的用户留言非常认真,会明确地指明优点和不足,其他国家的用户留言就比较随意了。
在中文中,“棋子”这个词没有同义词。但在外国语中不一定,比如在波斯语中,“棋子”和 “坚果” 是同义词,在匈牙利语中,棋子和“光盘”是同义词。直棋专有名词之多给翻译带来了困难。所幸在首版 app 发布后,各国的热心用户参与到翻译过程中,通过翻译工作,和世界各地的用户交流各国的语言和文化,是意外的收获。其中匈牙利语翻译是由一位 71 岁的化学工程师完成的,她克服了使用电脑的种种不便,最终完成了翻译,非常不容易。
此前我从来未想到过盲人朋友还可以使用 app 下棋,所以没有实现无障碍功能。后来奥地利一位盲人朋友给我发了一个支持无障碍的国际象棋 app 给我参考,才大开眼界。将添加无障碍支持的 Sanmill 发给这位盲人朋友后,他竟然很快就完成了一局,兴奋地将棋谱发给我,超乎意料。
首版软件发布后,陆续有世界各地开发者加入到 Sanmill 社区,提交补丁,从他们的补丁中自己也能发现自己设计上的一些不足之处,涨了见识。开发者也会就 app 的发展方向上提出意见。比如有些开发者会建议做减法。不同的意见都是一道道新的大门,大家把分歧拿到沟通的层面上,才能扩展得更宽更长,因此对于各种不同意见我都持开放性态度。当然,路线图也阶段性目标公布到项目网站中,供贡献者们了解。
感谢阅读,希望这篇文章能对大家了解开源软件的制作有所帮助!
我们正在进入一个开源蓬勃发展的大时代。“软件定义未来的世界,开源决定软件的未来”。近年来,跨平台技术快速发展,开发模式发生巨变,Flutter 等新技术涌现,以各种开发工具都在不断迭代升级,带来更好的编程体验。当今的应用开发更简单、业务维护更便捷、生产力提升、给我们利用新技术快速开发提供了机会,当今有那么多优秀的工具和创新的工程思想可参考,为我们提供了创作上的更多可能性。
没有社区的项目是难以长久的。我希望 Sanmill 项目无论是社区还是代码都能保持目前的高活跃度,经过全世界开发者在多种场景下的打磨,不断完善、升级迭代,满足世界各地不同用户的需求。
我会尽我的最大努力来确保文中内容的准确性,但难免会存在疏漏。欢迎读者反馈问题。谢谢!
谱发给我,超乎意料。
首版软件发布后,陆续有世界各地开发者加入到 Sanmill 社区,提交补丁,从他们的补丁中自己也能发现自己设计上的一些不足之处,涨了见识。开发者也会就 app 的发展方向上提出意见。比如有些开发者会建议做减法。不同的意见都是一道道新的大门,大家把分歧拿到沟通的层面上,才能扩展得更宽更长,因此对于各种不同意见我都持开放性态度。当然,路线图也阶段性目标公布到项目网站中,供贡献者们了解。
感谢阅读,希望这篇文章能对大家了解开源软件的制作有所帮助!
我们正在进入一个开源蓬勃发展的大时代。“软件定义未来的世界,开源决定软件的未来”。近年来,跨平台技术快速发展,开发模式发生巨变,Flutter 等新技术涌现,以各种开发工具都在不断迭代升级,带来更好的编程体验。当今的应用开发更简单、业务维护更便捷、生产力提升、给我们利用新技术快速开发提供了机会,当今有那么多优秀的工具和创新的工程思想可参考,为我们提供了创作上的更多可能性。
没有社区的项目是难以长久的。我希望 Sanmill 项目无论是社区还是代码都能保持目前的高活跃度,经过全世界开发者在多种场景下的打磨,不断完善、升级迭代,满足世界各地不同用户的需求。
我会尽我的最大努力来确保文中内容的准确性,但难免会存在疏漏。欢迎读者反馈问题。谢谢!