使用Python中的类的骰子生成器

时间:2013-01-19 00:07:06

标签: python class python-2.7

更新 有人可能会因为这种方式而放弃我,但是评论中没有足够的空间来涵盖这一点,他们明确告诉你不要通过跟进回答你自己的问题,所以这里......

我已经创建了骰子类,就像你们正在谈论的那样。

class dice():
    def __init__(self, sides, number):
        self.sides = sides
        self.number = number

    def roll(self):
        return random.randint(1, self.sides)

虽然number参数没有做任何事情。

def att():
    d = dice(20, 2)
    base = d.roll()
    if base == 1:
        print 'Miss!'
    elif base == 20:
        crit = d.roll()
        if crit < 10:
            print 'Hit!'
        else:
            print 'Critical hit!\n'
            effect = super_crit()               
    else:
        print base

如果我做这样的事情,我获得多个骰子的唯一方法就是:

def initiative():
    d = dice(10, 1)
    ini = d.roll(), d.roll()
    print ini

我也试过了:

def initiative():
    d = dice(10, 2)
    d.sides = 10
    d.number = 2
    ini = d.roll()
    print ini

对我来说这看起来多余,但是如果没有Type Error:的参数太少,我会得到dice(10, 2)。无论我使用哪种方法,我都得到相同的结果 - 一个死。我错过了什么吗?

原帖

我正在学习如何在Python 2.7中使用类,作为练习我正在为基于文本的RPG编写一个战斗模块。它使用旧学校骰子滚动方法来确定结果和效果。滚动确定命中或未命中。如果滚动自然20,则另一个滚动确定它是否是重击。如果临界命中= TRUE,则滚动另一个模具以确定实现哪个身体部位。每个身体部位与字典中的数字1-12配对。根据受影响的部分,有三种可能的输出消息。我的问题是返回整个值列表而不是特定部分。我在这里做错了什么?

是的,我知道这是超级书呆子。是的,我知道输出是蹩脚的,但它是所有占位符。

import sys, random

#dice generator
class dice():
    def d4(self):
        number = random.randint(1, 4)
        return number

    def d6(self):
        number = random.randint(1, 6)
        return number

    def d10(self):
        number = random.randint(0, 9)
        return number

    def d12(self):
        number = random.randint(1, 12)
        return number

    def d20(self):
        number = random.randint(1, 20)
        return number

    def d100(self):
        number = random.randint(0, 99)
        return number 

#critical hit effect generator        
class super_crit(dice):
    def __init__(self):
        roll = dice()
        loc = roll.d12()
        hit_loc = {1 : 'Head',
                   2 : 'Left Arm',
                   3 : 'Right Arm',
                   4 : 'Left Leg',
                   5 : 'Right Leg',
                   6 : 'Left Hand',
                   7 : 'Right Hand',
                   8 : 'Left Foot',
                   9 : 'Right Foot',
                   10 : 'Chest',
                   11 : 'Stomach',
                   12 : 'Body'}
        part = hit_loc.values()
        for w in part:
            if loc <= 9:
                print w, "has been severed!"
            elif loc == 10:
                print "You sink your blade into his", w, "and pierce the heart!"
            elif loc == 11:
                print "You slash him across the", w, "and eviscerate him!"
            elif loc == 12:
                print "You shred the enemy's", w, "to ribbons!"

class attackRoll(dice):
        pass

#Attack function        
def att():
    roll = attackRoll()
    base = roll.d20()
    if base == 1:
        print 'Miss!'
    elif base == 20:
        crit = roll.d20()
        if crit < 10:
            print 'Hit!'
        else:
            effect = super_crit()               
    else:
        print base

def main():
    att()


if __name__ == '__main__':

main()

7 个答案:

答案 0 :(得分:5)

循环遍历dict的所有值:

part = hit_loc.values()
for w in part:
    # loop over each part, so print a damage message for all 12 bodyparts

也许您打算选择受影响的那个?

part = hit_loc[loc]  # assign *one* body part to `part`
if loc <= 9:
    print part, "has been severed!"
elif loc == 10:
    print "You sink your blade into his", part, "and pierce the heart!"
elif loc == 11:
    print "You slash him across the", part, "and eviscerate him!"
elif loc == 12:
    print "You shred the enemy's", part, "to ribbons!"

换句话说,你根本不需要循环。

请注意,每当您发现自己使用一系列连续数字作为字典的键时,您也可以将其改为列表:

hit_loc = [
    'Head', 'Left Arm', 'Right Arm', 'Left Leg', 
    'Right Leg', 'Left Hand', 'Right Hand', 'Left Foot', 'Right Foot',
    'Chest', 'Stomach', 'Body'
]

现在除了索引从0到11之外,所以使用loc - 1找到正确的身体部位:

part = hit_loc[loc - 1]

答案 1 :(得分:3)

正切 ...

骰子类的简单版本可能类似于:

class Die(object):
    def __init__(self, sides = 6):
        self.sides = sides

    def roll(self):
        return randint(1, self.sides)

现在你有了一个通用的骰子对象,每次你想出一个新的滚动模式时都不需要你添加新的方法。 E.g。

d = Die(10);
d.roll() # ---> some value between 1 and 10

添加可选的startincrement属性,以便您可以将生成5, 10, ..., 95, 100的D20作为练习留给学生。

答案 2 :(得分:2)

这看起来像是一个学习Python的有趣程序。其他答案大部分都涵盖了你需要的东西,但我只想指出一件事:

如果你试图用骰子在现实生活中选择一些东西,那么这样做的方法是为东西分配数字,然后滚动骰子,并使用骰子中的数字来确定选择了哪个东西。但是,在Python中,你可以做更简单的事情:你可以告诉Python“这里有很多东西,我想随机选择一个项目。”其功能是:random.choice()

所以对于重要的打击东西,你可以替换它:

hit_loc = {1 : 'Head',
           2 : 'Left Arm',
           3 : 'Right Arm',
           4 : 'Left Leg',
           5 : 'Right Leg',
           6 : 'Left Hand',
           7 : 'Right Hand',
           8 : 'Left Foot',
           9 : 'Right Foot',
          10 : 'Chest',
          11 : 'Stomach',
          12 : 'Body'}

# two steps to randomly choose a hit location
loc = roll.d12()
part = hit_loc[loc]

用这个:

locations = ('Head', 'Left Arm', 'Right Arm', 'Left Leg', 'Right Leg',
         'Left Hand', 'Right Hand', 'Left Foot', 'Right Foot',
         'Chest', 'Stomach', 'Body')

# one step to randomly choose a hit location
part = random.choice(locations)

通常,当您想弄清楚如何使用像random这样的模块时,阅读文档确实很有用。在Python中,代码总是提供一些简洁的文档;从Python提示符,你可以这样做:

>> import random
>> help(random)

help()命令会打印出简洁的文档,如果你仔细阅读,你会发现random.choice()random.shuffle()和其他漂亮的东西。

享受Python的乐趣!

P.S。这是一个有趣的示例程序。不过,这不是课程的最佳范例。你在这里使用一个类只是为了得到一个命名空间,它将相关的函数组合在一起,这没有错,但是当一个类封装了一些数据和一些函数时,它更有用。这是一个建议:创建一个Person()类,它有一个名为hit_points的成员变量,并编写一个攻击另一个.attack()实例的方法函数Person。然后编写一个程序,生成两个Person()个实例并让它们互相争斗!

我建议它应该是这样的:

black_knight = Person()
king_arthur = Person()

while True:
    king_arthur.attack(black_knight)
    if black_knight.hit_points <= 0:
        print "Victory is mine!"
        break
    black_knight.attack(king_arthur)
    if king_arthur.hit_points <= 0:
        print "None shall pass!"
        break

在此示例中,Person实例将人员(点击点)和与人合作的功能(一个人可以attack另一个人)的信息捆绑在一起。

您可以扩展模型以包含重要命中; Person实例可以跟踪它收到的关键命中数;一个Person可能有库存,包括总是获得重击的武器(如“Vorpal Bunny Teeth”,“Antioch的圣手榴弹”等)

事实上,您可以继续为Person添加更多内容以及更多方法功能,如果您坚持使用它,您可能会将整个事情变成真正的游戏。

答案 3 :(得分:1)

而不是on-loop over hit_loc.values(),只需设置loc=hit_loc

代码还有许多其他问题。以下是一些:

  • 使用函数定义而不是 dice
  • 的类
  • 缩进对 main()
  • 的调用
  • 带有数字键的词典可以替换为列表元组

以下是代码第一部分的一种可能的重构:

from functools import partial
from random import randint

d4 = partial(randint, 1, 4)
d6 = partial(randint, 1, 6)
d10 = partial(randint, 0, 10)
d12 = partial(randint, 1, 12)
d20 = partial(randint, 1, 20)
d100 = partial(randint, 0, 99)

def critical_hit_effect():
    locations = ('Head', 'Left Arm', 'Right Arm', 'Left Leg', 'Right Leg',
                 'Left Hand', 'Right Hand', 'Left Foot', 'Right Foot',
                 'Chest', 'Stomach', 'Body')
    roll = d12()
    if roll == 10:
        msg = "You sink your blade into his %s and pierce the heart!"
    elif roll == 11:
        msg = "You slash him across the %s and eviscerate him!"
    elif roll == 12:
        msg = "You shred the enemy's %s to ribbons!"
    else:
        msg = "%s has been severed!"
    print msg % locations[roll-1]

答案 4 :(得分:1)

我认为当你有另一个问题时,可以发布另一个问题。既然你更新了你的问题以询问如何做多个骰子,我正在提出另一个答案。 (我怀疑我会得到任何积分,但我并不关心这些要点。)

有些游戏需要知道掷骰子是如何形成的。例如,在超级英雄游戏 Champions 中,你有时需要计算有多少六面掷骰子。 (我想......自从我上场以来已经很久了。)

无论如何,你的骰子类应该返回一个骰子滚动结果的列表或元组,而不是总结骰子并返回总和。我会告诉你们两个。如果元组令人困惑,只要忽略它,直到你对Python有更多了解;你现在不需要学习这个。

Python有一个非常方便的方法来制作一个列表,称为“列表理解”。如果你谷歌的“列表理解”,你会发现很多页面的解释。 (这就是为什么我没有得到任何分数... StackOverflow并不需要另一个列表理解解释!)基本上,它的工作方式如下:

[ 表达 for 可变 in 序列 if 条件 ]

从中间开始查看“for”部分。这意味着“为序列的每个成员设置一个变量。检查条件,如果为真,则计算表达式并将结果附加到新列表。”

一个简单的例子:列出1到10之间奇数的平方列表。这样做:

lst_odd_squares = [x**2 for x in range(10) if (x % 2) == 1]
print(lst_odd_squares) # prints: [1, 9, 25, 49, 81]

以下是使用for循环执行此操作的方法:

lst_odd_squares = []  # start with an empty list
for x in range(10):  # just like the for part
    if (x % 2) == 1:  # just like the condition part
        lst_odd_squares.append(x**2)  # append the expression part

“条件”部分是可选的,这个特定的例子可以这样做:

lst_odd_squares = [x**2 for x in range(1, 10, 2)]

这里我们从1开始,然后逐步开始,所以我们只是直接生成我们想要的数字。

表达式中通常包含for变量,但不必:

counters = [0 for x in range(10)]

这将生成一个包含10个整数的列表,最初都设置为0.然后您可以将它们用作计数器或其他东西。

无论如何,这是制作一个返回列表的骰子滚动类的方法:

import random

class Dice():
    def __init__(self, sides):
        self.sides = sides

    def roll(self, number):
        """return a list of length number, with random values from 1 to self.sides"""
        return [random.randint(1, self.sides) for _ in range(number)]

    def roll_one(self):
        """return a single value from 1 to self.sides"""
        return random.randint(1, self.sides)

d6 = Dice(6)
d10 = Dice(10)

print d6.roll(3)  # roll 3d6
print d10.roll(2)  # roll 2d10

print d10.roll(1)  # roll 1d10 -- returns a list of length 1
print d10.roll_one() # roll a single d10 -- returns a single integer

我改变了你的代码。我只是让.roll()方法函数采用number参数,而不是每次需要滚动不同数量的骰子时创建不同的类实例。你的方式不一定是错的,但我认为这种方式更容易一些。

通常最好使用大写字母作为类名。大多数Python人喜欢使用的规则列表,称为“PEP 8”,类名称的大写是规则之一:

http://www.python.org/dev/peps/pep-0008/#class-names

(顺便说一句,学习Python 书将以优秀的PEP 8风格为您提供所有示例。如果您从该书中学习,您将学习PEP 8方式。这是其中之一这本书教你所有关于Python的方法。)

我使用单个下划线(_)作为循环变量。这是合法的,有时这样做是为了表明你不打算使用这个价值。我们需要一个循环变量,但我们实际上并没有使用它,就像上面的counters示例一样。

Python有一个“列表理解”,但没有“元组理解”。所以这是制作元组的一种方法:首先制作列表,然后从列表中构建一个元组。

lst = [random.randint(1, self.sides) for _ in range(number)]
return tuple(lst)

您可以一次完成所有操作,列表中没有明确的变量名称:

return tuple([random.randint(1, self.sides) for _ in range(number)])

这有点难看。如果我们要构建列表,为什么不返回它呢?为什么要将它输入tuple()然后在使用它之后将其销毁?

Python提供了一种称为“生成器表达式”的东西,有点像列表理解。重要的两个区别:0)你使用括号,而不是方括号; 1)它不构建任何东西,它返回一个可以产生一系列值的“迭代器”对象。 (当Python提取出一系列值时,我们说Python正在“迭代”这个对象。)如果你把这个“迭代器”传递给tuple(),你可以直接建立一个元组而不先建立一个列表然后摧毁清单。这里很酷的技巧:您需要使用括号来调用tuple(),您可以使用这些括号作为生成器表达式所需的括号。

return tuple(random.randint(1, self.sides) for _ in range(number))

同样,如果生成器表达式的东西现在看起来很奇怪或很难,那就忽略它吧。列表理解是你需要的很多东西。

答案 5 :(得分:0)

这就是我在为Pathfinder游戏I DM建立的工具包中处理多个骰子的方式:

import random

def rolldice(number,sides):
    Result = []
    for dice in range(0, number):
        Result.append(random.randint(1,sides))
    return Result

class Dice():
    def __init__(self, number, sides):
        self.sides = sides
        self.number = number

    def roll(self):
        return rolldice(self.number, self.sides)

# 10th level fireball
fireball = Dice(10,6)
damage = fireball.roll()
# all of the rolls (for 4d6 drop lowest, 2d20 take highest, etc)
print (damage)
# total damage
print (sum(damage))
# a d20 roll
d20 = Dice(1,20)
print (sum(d20.roll()))

输出:

[2, 3, 1, 4, 2, 1, 5, 4, 5, 4]
31
13

答案 6 :(得分:0)

我将在这里留下一些我编写的漂亮的 Python 元类魔法。这是给你,未来的代码考古学家,你有足够的决心从埋藏在这个线程的深处挖掘它。

from random import randint
class MetaRoll(type):
    def __getattr__(cls, dice):
        n,n_faces = [int(part) if part else 1 for part in
                     dice.lower().strip('a').strip('_').split('d')]
        return sum( randint(1,n_faces) for i in range(n) )
class Roll(metaclass=MetaRoll):
    """ A handy RPG styled dice roller.

    The implementation has been enchanted using Python __getattr__ and metaclass
    magic. Beyond that, only the random module is used.
    enchanted using Python __getattr__ and metaclass magic. 

    As a result, any roll is exposed as a public attribute and the dice
    roller that can be used like this:

    ``Roll.a_2D6 + 2``

    Note that the ``a_`` is needed to allow names starting with numbers, and, 
    e.g. ``2D10`` is written as ``a_2D10`` or ``a2D10``.

    Attributes:
        a_3D6 (int): Rolls three six sided dice and returns the sum. This is
                      most boring example with a nice probability distribution.
        D6 (int): If there is just one die, the attribute name does not start
                   with a number. Hence, the ``a_`` prefix can be omitted.
    """
    pass


print(Roll.D6) # Outputs an integer between [1,6]
print(Roll.D20+2) # Note how easy it is to add modifiers
print(Roll.a_3d6-2) # Outputs a sum of random integers from a range [1,16].

ps。当描述您所做的事情比编写代码花费的时间更长时,您就知道自己是个向导。