不参与Wordle热潮是不对的,还有什么比在C++20中创建一个纯编译时版本的游戏更好的方法呢?我很自豪地向你们展示…Wordlexpr!
Wordlexpr完全是在编译时玩的,因为从来没有生成过任何可执行文件——游戏是通过编译器错误体验的。因此,我们需要解决几个问题来实现一切:
为了滥用编译器,以我们自己喜欢的任意字符串输出错误,让我们首先尝试找出如何让它打印出一个简单的字符串文字。第一次尝试,静态_断言,似乎很有希望:
错误:静态断言失败:欢迎使用Wordlexpr!
然而,我们的喜悦是短暂的,因为static_assert只接受字符串文字——字符的constexpr数组或const char*将不会用作参数:
那么,将字符串的内容存储为结构类型的一部分,然后生成包含此类类型的错误,怎么样?
错误:变量';打印<'a''b''c''d'>_'
美好的我们可以在编译器输出中看到我们的字符,理论上我们可以在编译时根据自己的喜好修改或生成字符序列。然而,使用字符。。。模板参数包非常繁琐,最终输出的可读性不强。
C++20的P0732R2:“非类型模板参数中的类类型”在这里起到了解救作用!简而言之,我们可以使用任何文本类型作为非类型模板参数。因此,我们可以创建自己的小编译时字符串文本类型:
结构ct_str
然后,我们可以接受ct_str作为打印的模板参数,并使用与之前相同的想法:
错误:变量';打印<;ct#u str{";欢迎来到Wordlexpr!";,21}>_' 有
现在我们有了一种方法,可以让编译器发出任何我们想要的错误。事实上,我们可以在编译时对ct_str执行字符串操作:
constexpr ct_str test()
错误:变量';打印<;ct#u str{";欢迎来到wordlexpr.";,20}>_' 有
通过使用append、contains、replace等功能扩展ct_str,我们最终将能够在编译时创建任何类型的字符串,并将其作为错误打印出来。
如果我们允许用户通过预处理器定义在命令行上提供种子,这真的不是什么大事。伪随机数的生成总是确定性的,最终结果只取决于RNG的状态和最初提供的种子。
将诸如Mersenne Twister这样的通用RNG引擎移植到C++20 constexpr是相当容易的。就Wordlexpr而言,模运算符(%)足够:
如果我们允许用户通过预处理器定义给我们一个种子,为什么不允许用户在同一个游戏会话中通过告诉我们他们上次玩的地方来取得进展呢?可以将其视为现代游戏中的任何保存文件系统,但“保存文件”是一个短字符串,将被传递给编译器:
用户不必自己提出状态字符串——它将由Wordlexpr在每一步生成:
错误:变量';打印<;ct_str{";你猜到了‘crane’。结果是:'x-xx-'。
剩下要做的就是为状态定义编码和解码函数:
在Wordlexpr中,我使用了一个简单的Caesar密码将猜测编码到字符串中,而不使其可读。这并不是真的必要,但一般来说,另一种编译时游戏可能希望通过执行某种编码来隐藏当前状态。
我希望你喜欢这个关于Wordlexpr工作原理的简短解释。请记住,您可以自己播放,并在编译器资源管理器上查看整个源代码。请随意提出任何问题!
我的书“拥抱现代C++安全”现在在所有主要经销商上都可以使用。请考虑购买它并与你的朋友和同事分享这个消息,这很有帮助!
欲了解更多信息,请阅读以下采访:“为什么4个彭博工程师写了另一本C++书”
如果你喜欢快节奏的开源街机游戏,它允许用户创建内容,那就看看open Hexagon吧,这是我在Steam和itch上推出的第一款完全发布的游戏。伊奥。
Open Hexagon是Terry Cavanagh备受好评的Super Hexagon的精神继承者。特里完全支持我的项目!谢谢
我在业余时间提供1-1个C++辅导和咨询课程。如果你对它感兴趣,可以通过mail(在)vittorioromeo(点)com或Twitter联系。