几个月前,我写过一个专门用来玩卡坦岛的命令行骰子工具,里面的骰子函数都是写死的,只能选2d6/2d10/3d4这几个,够用,但是不方便,适用性太差。最近入了《Pathfinder基础包》,准备重新开始跑团,看几个跑团QQ群里都有骰子机器人(方便大家开网团的时候投骰子),他们输入『.r 3d6』『.r 4d10』甚至『.r 1d97』这种实际中并不存在的骰子都可以得到值,自由度非常高。
于是我计划用Python来实现这种高自由度的骰子。首先遇到的问题就是如何让程序识别『3d6』『1d4』『1d8』这种跑团黑话。先普及一下,d4/d6/d8/d10/d20等都指骰子的面数,例如d4指的是四面骰,d20则指二十面骰,1d4指扔1个四面骰,2d6则指扔两个六面骰。普通游戏一般用不到这么多种类的骰子,而在以大量数值检定为核心的TRPG(桌面角色扮演游戏)中,这些骰子就不可或缺了。
首先想到的方法是用正则表达式来解析命令。以最常用的『1d6』为例,『1』指骰子个数,『6』是骰子类型(面数),『d』则是分割二者的分隔符。用正则表达式来写的话,应该是这样:
roll = input('> ') match = re.search(r'(\d+)([Dd])(\d+)', roll)
先让用户输入命令,然后开始解析命令。命令的结构是『数字』+『D或d』+『数字』,正则表达式如上图。最早的版本里,是 r'(\d)([Dd])(\d)’ ,两个数字位都没『+』,后来发现第二个数字位必须带『+』(因为骰子类型有可能是两位数甚至三位数,例如1d20,1d100),于是我干脆把两个数字位都变成可以无限位取值的。
到此,解析命令完成。下一个问题发生在定义函数时的全局变量上。早期版本如下:
result = 0 def d(n): result = randint(1, n)
函数外部出现了变量 result,函数内部又给 result 赋值,电脑就懵圈了。在这里,我一直没搞懂的问题是,定义函数时的返回值,并不是返回给某个变量,而是对应了这个函数本身。result = 0 这个变量的初始化也可以删掉。在朱老师的指导下,终于搞明白这个问题,于是代码顺利改成这样:
def d(n): return randint(1, n)
最后,做好一个 for 循环来实现反复扔骰子的动作即可:
for i in range(m): result = d(n) dice.append(result) print(result) print('和为: ', sum(dice))
至此,其实还没有写完,脚本还有很多地方需要完善,但是已经不再是当初那个被朱老师批评的『到处给全局变量赋值的超级烂代码』了。日拱一卒,余欣慰也。
Github地址
欢迎各位去围观我写的其他小脚本,帮我改改这些超级烂代码!