实现一个可接受DND类掷骰表达式的掷骰器

271 阅读3分钟

DND掷骰器

掷骰表达式

在DND游戏中,常用“掷骰表示法”(Dice Notation)来表示随机数,如:

  • “1d6”:表示掷一次6面骰子
  • “2d8”:表示掷两次8面骰子
  • “1d6+10”:表示掷一次6面骰子,并在结果上加常数10
  • “2d6+1d20”:表示掷两次6面骰子和一次20面骰子

关于掷骰器的一些思考:

  • 掷骰是为了体现现实世界中的随机性
  • 假设我们用数值来表示战士的长剑伤害,当他是个新人的时候,可能是从1d4开始,逐步到1d6/1d8,然后随着他的熟练程度增加,开始增加固定保底伤害数值,如5+1d6,9+1d6
  • 为何要一直保留随机性呢?因为对敌人造成的伤害不仅取决于你的武器和熟练程度,还取决于敌人的应对
  • 而一把菜刀,其伤害可能是1d4,对应不小心切到手指还是砍在敌人的头上。而一把制造精良的长剑,其伤害可能是1d20,对应擦过敌人到把敌人捅个透心凉。
  • 显然的,我们利用掷骰表达式可以相对客观的来表示各类技能和装备的特性

代码

下列源代码内含2个函数,一个是掷骰子,另一个是拆解表达式并组合骰子结果:

from random import randint
​
​
def dice(CMD='1d6'):
    result = 0
    if CMD.find('d')==-1:
        result = int(CMD)
    else:
        list_cmd = CMD.split('d')
        for _ in range(int(list_cmd[0])):
            result += randint(1,int(list_cmd[1]))
​
    return result
​
def dice_expression(CMD='1d6'):
    result = 0
    list_cmd = CMD.split('+')
    for i in list_cmd:
        a = dice(i)
        print(a)
        result += a
​
    return result
​
if __name__ == '__main__':
    CMD = '1d6+1d20+10'
    print('掷骰结果:',dice_expression(CMD))

加上GUI后的成品软件

image.png

from random import randint
import tkinter as tk

# 函数
def dice(CMD='1d6',results=[]):
    '''掷骰,多次掷同一种骰子,或保持常量'''
    result = 0
    if CMD.find('d')==-1:
        # 处理常数
        result = int(CMD)
        results.append(result)
    else:
        # 处理掷骰式
        list_cmd = CMD.split('d')
        for _ in range(int(list_cmd[0])):
            num = randint(1,int(list_cmd[1]))
            result += num
            results.append(num)

    return result,results

def dice_expression(CMD='1d6'):
    '''掷骰表达式,参考DND规则,用+号隔开'''
    result = 0
    results = []
    list_cmd = CMD.split('+')
    for i in list_cmd:
        num,nums = dice(i,results)
        result += num
        # 因为results既是自变量又是因变量,所以无需额外加总

    return result,results


# GUI配置
def GUI_ini():
    # 初始化窗口
    win = tk.Tk()
    win.title('DND Roller')
    win.geometry('400x300') # 注意此处为小写字母x

    # 容器变量
    pad_x = 2
    pad_y = 2

    # 容器设置
    frame_0 = tk.Frame(win)

    frame_1 = tk.Frame(frame_0)
    frame_2 = tk.Frame(frame_0)
    frame_3 = tk.Frame(frame_0)
    frame_4 = tk.Frame(frame_0)

    # 容器摆放
    frame_0.pack(padx=pad_x,pady=pad_y,expand=True,fill='both')

    frame_1.pack(padx=pad_x,pady=pad_y,fill='x')
    frame_2.pack(padx=pad_x,pady=pad_y,fill='x')
    frame_3.pack(padx=pad_x,pady=pad_y,fill='x')
    frame_4.pack(padx=pad_x,pady=pad_y,fill='x')

    # 创建变量
    varbox = []
    varbox.append(tk.Variable())
    varbox.append(tk.Variable())
    varbox.append(tk.Variable())

    # 变量初始化
    varbox[0].set('2d6+1d20+10')
    varbox[1].set('')
    varbox[2].set('')

    # 动作
    def on_click(event=0):
        result,results = dice_expression(varbox[0].get())
        varbox[1].set(result)
        varbox[2].set(f'{results}')

    # 控件设置
    label_1 = tk.Label(frame_1,background='lightgrey',text='DND掷骰器',height=2)
    label_2 = tk.Label(frame_2,background='lightgrey',text='表达式',width=10,height=2)
    label_3 = tk.Label(frame_3,background='lightgrey',text='结果',width=10,height=2)
    label_4 = tk.Label(frame_4,background='lightgrey',text='列表',width=10,height=2)

    entry_1 = tk.Entry(frame_2,textvariable=varbox[0])
    entry_2 = tk.Entry(frame_3,textvariable=varbox[1])
    entry_3 = tk.Entry(frame_4,textvariable=varbox[2])

    button_1 = tk.Button(frame_2,text='掷骰 Enter',command=on_click,width=10)

    # 控件摆放
    label_1.pack(fill='x')

    label_2.pack(side='left',fill='y')
    button_1.pack(side='right',fill='y')
    entry_1.pack(side='left',fill='both',expand=True)

    label_3.pack(side='left',fill='y')
    entry_2.pack(side='left',fill='both',expand=True)

    label_4.pack(side='left',fill='y')
    entry_3.pack(side='left',fill='both',expand=True)

    # 绑定键盘事件
    win.bind('<KeyRelease-Return>',on_click)

    # 返还win对象
    return win

# 启动循环
GUI_ini().mainloop()