Python如何为游戏中的每个类制定一套规则

时间:2019-03-18 17:28:52

标签: python pygame

在C#中,我们必须获取/设置规则,但是我不知道如何在Python中做到这一点。

示例: 兽人只能装备斧头,其他武器不符合资格 人类只能装备剑,其他武器才有资格。

如何告诉Python Orc无法执行上述示例中的操作?

提前感谢您的答案,希望对您来说有意义。

3 个答案:

答案 0 :(得分:3)

  

在C#中,我们必须获取/设置规则,但是我不知道如何在Python中做到这一点。

不。 Getter和Setters不会在这里为您提供帮助。请注意,Python 也具有getters / setters和dunders(类似self.__foo之类的东西),但我们不要遵循该路径。


相反,让我们看看您拥有什么:

  • 一堆东西(例如兽人人类和其他东西)
  • 一堆动作(好吧,目前只有一个动作,挥舞,但也许明天您决定吸血鬼可以喝血,但不能喝人血)
  • 和一堆规则(斧头是武器,剑是武器,兽人只能使用斧头,人类可以使用其他武器,...)。

因此,让我们尝试通过ThingsActionsRules这样的方式为游戏建模。

由于我们是很酷的孩子,所以我们开始以文本形式写下规则:

rules =[
    "Human is Person",
    "Orc is Person",
    "Person may wield Weapon",
    "Human may not wield Axe",
    "Orc may only wield Axe",
    "Sword is Weapon",
    "Bow is Weapon",
    "Axe is Weapon",
    "Cow is Animal",
    "Animal may eat Grass"
]

如您所见,我也谈到了牛,动物和草,因此我们可以看到我们将采用一种非常通用的方法。

我们知道我们的“事物”具有不同的类型,名称和调用“动作”的方式,因此,这里是我们的Thing类:

class Thing:
    def __init__(self, name, *type):
        self.name = name
        self.type = ['Thing', *type]

    def action(self, action_class, *args):
        action_class(self, *args)()

Thing的类型为'Thing',其他任何我们传递给__init__的内容,我们都可以使用action类调用Action函数(尽快创建它),并将一些参数传递给该函数。

到目前为止,如此简单。


现在,这是通用Action的样子:

class Action:
    def __init__(self, name, a, b):
        self.name = name
        self.a = a
        self.b = b

    def invoke(self):
        print('You feel a strange sensation...')

    def forbidden(self):
        print(f"{self.a.name} tries to {self.name} {self.b.name}, but that is not possible")

    def __call__(self):
        if Rules.allowed(self):
            self.invoke()
        else:
            self.forbidden()
        print('----------')

一个名字和两个东西(ab)。可以调用它(例如,用Thing.action),或者允许调用它(然后调用invoke),也可以不调用它(然后调用fobidden)。

让我们暂时忽略Rules.allowed并创建一些可做某事的动作:

class Wield(Action):
    def __init__(self, thing, weapon):
        super().__init__('wield', thing, weapon)

    def invoke(self):
        if hasattr(self.a, 'weapon'):
            print(f'{self.a.name} drops {self.a.weapon.name}')
        self.a.weapon = self.b
        print(f'{self.a.name} now wields {self.a.weapon.name}')

class Eat(Action):
    def __init__(self, thing, food):
        super().__init__('eat', thing, food)

    def forbidden(self):
        print(f'{self.a.name} tried to eat {self.b.name}, but did not like it very much...')

    def invoke(self):
        print(f'{self.a.name} eats {self.b.name}')

Wield操作将设置呼叫者的weapon,但前提是允许这样做。 Eat操作现在只显示一条消息...

因此,现在剩下要做的就是实际实现Rules.allowed了,这意味着解析我们最初创建的规则并对其执行操作。


这是Rules类:

class Rules:
    alias_list = []
    prohibition_list = []
    permission_list = []
    exclusive_list = []

    def parse_rules(rules):
        for rule in rules:
            if ' is ' in rule:
                type, alias = rule.split(' is ')
                Rules.alias_list.append((type, alias))
            elif ' may only ' in rule:
                obj, rest = rule.split(' may only ')
                action, second = rest.split(' ')
                Rules.exclusive_list.append((obj, action, second))
            elif ' may not ' in rule:
                obj, rest = rule.split(' may not ')
                action, second = rest.split(' ')
                Rules.prohibition_list.append((obj, action, second))
            elif ' may ' in rule:
                obj, rest = rule.split(' may ')
                action, second = rest.split(' ')
                Rules.permission_list.append((obj, action, second))

    def resolve_types_inner(types, aliases):
        for (source_type, alias_type) in aliases[:]:
            if source_type in types:
                types.add(alias_type)
                aliases.remove((source_type, alias_type))
                return Rules.resolve_types_inner(types, aliases)
        return types

    def resolve_types(thing):
        types = set(thing.type)
        return Rules.resolve_types_inner(types, Rules.alias_list[:])

    def allowed(action_to_test):
        a_types = Rules.resolve_types(action_to_test.a)
        b_types = Rules.resolve_types(action_to_test.b)

        for (a, action, b) in Rules.exclusive_list:
            if action == action_to_test.name:
                if a in a_types and b in b_types:
                    print ('-- allowed by exclusive_list')
                    return True

        for (a, action, b) in Rules.prohibition_list:
            if action == action_to_test.name:
                if a in a_types and b in b_types:
                    print ('-- forbidden')
                    return False

        for (a, action, b) in Rules.permission_list:
            if action == action_to_test.name:
                if a in a_types and b in b_types:
                    if not action in (x for (a2,x,b2) in Rules.exclusive_list if x == action and a2 in a_types):
                        print ('-- allowed')
                        return True
                    else:
                        print ('-- forbidden by exclusive_list')
                        return False
        print ('-- no rules match')

当然,这只是非常基本的知识,而不是成熟的规则引擎或逻辑编程语言,但是现在就可以了。

我们已经支持4个功能:

  • 别名。我们可以说A是B,B的所有规则都适用于A
  • 允许一些东西
  • 禁止物品
  • 只允许特定B使用A

parse_rules函数仅拆分字符串并将部分添加到不同的列表中,在allowed函数中,我们迭代这些列表以确定是否允许某些内容。

随时进行改进或添加新功能。


所以,现在我们可以出发了。

让我们运行以下内容:

# prepare our simple rule engine
Rules.parse_rules(rules)

# Let some things exist in the world
Carl_the_Human = Thing('Carl', 'Human')
Grump_the_Orc = Thing('Grump', 'Orc')
Sandy_the_Cow = Thing('Sandy', 'Cow')
Carls_sword = Thing("Carl's Sword of Justice", 'Sword')
Grumps_axe = Thing("Grump's rusty Axe", 'Axe')
Old_bow = Thing("An old bow", 'Bow')

# Sandy is hungry
Sandy_the_Cow.action(Wield, Grumps_axe)
Sandy_the_Cow.action(Eat, Grumps_axe)
Sandy_the_Cow.action(Eat, Thing("a bunch of grass", "Grass"))

# Carl wants to try some weapons
Carl_the_Human.action(Wield, Carls_sword)
Carl_the_Human.action(Wield, Grumps_axe)
Carl_the_Human.action(Wield, Old_bow)

# Grump wants to try some weapons    
Grump_the_Orc.action(Wield, Grumps_axe)
Grump_the_Orc.action(Wield, Carls_sword)

我们得到以下结果:

-- no rules match  
Sandy tries to wield Grump's rusty Axe, but that is not possible  
----------  
-- no rules match  
Sandy tried to eat Grump's rusty Axe, but did not like it very much...  
----------  
-- allowed  
Sandy eats a bunch of grass  
----------  
-- allowed  
Carl now wields Carl's Sword of Justice  
----------  
-- forbidden  
Carl tries to wield Grump's rusty Axe, but that is not possible  
----------  
-- allowed  
Carl drops Carl's Sword of Justice  
Carl now wields An old bow  
----------  
-- allowed by exclusive_list  
Grump now wields Grump's rusty Axe  
----------  
-- forbidden by exclusive_list  
Grump tries to wield Carl's Sword of Justice, but that is not possible  
----------

每当我们在游戏世界中需要一个新的“规则”时,我们都可以将其作为简单文本添加到规则列表中,并让我们的简单规则引擎来决定是否允许某些操作(如果扩展了,甚至应该如何进行)我们的引擎)。

那么也许我们拥有远程武器和近战武器,剑士也可以使用长矛而不是弓箭,弓箭手可以使用弓箭和长矛而不是近战武器?

没问题,只需将其写在规则中即可:

"Ranged is Weapon",
"Melee is Weapon",
"Bow is Ranged",
"Spear is Ranged",
"Sword is Melee",
"Human is Person",
"Archer is Human",
"Swordman is Human",
"Person may wield Weapon",
"Archer may not wield Melee",
"Swordman may not wield Bow"

示例:

Swordman = Thing('the old Guy', 'Swordman')
Archer = Thing('the Archer', 'Archer')
Carls_sword = Thing("Carl's Sword of Justice", 'Sword')
Old_bow = Thing("An old bow", 'Bow')
Spear = Thing("A golden Spear", 'Spear')

Archer.action(Wield, Carls_sword)
Archer.action(Wield, Old_bow)
Archer.action(Wield, Spear)

Swordman.action(Wield, Carls_sword)
Swordman.action(Wield, Old_bow)
Swordman.action(Wield, Spear)

结果:

-- forbidden
the Archer tries to wield Carl's Sword of Justice, but that is not possible
----------
-- allowed
the Archer now wields An old bow
----------
-- allowed
the Archer drops An old bow
the Archer now wields A golden Spear
----------
-- allowed
the old Guy now wields Carl's Sword of Justice
----------
-- forbidden
the old Guy tries to wield An old bow, but that is not possible
----------
-- allowed
the old Guy drops Carl's Sword of Justice
the old Guy now wields A golden Spear
----------

以下是完整的可运行代码,供您尝试:

rules =[
    "Human is Person",
    "Orc is Person",
    "Person may wield Weapon",
    "Human may not wield Axe",
    "Orc may only wield Axe",
    "Sword is Weapon",
    "Bow is Weapon",
    "Axe is Weapon",
    "Cow is Animal",
    "Animal may eat Grass"
]

class Rules:
    alias_list = []
    prohibition_list = []
    permission_list = []
    exclusive_list = []

    def parse_rules(rules):
        for rule in rules:
            if ' is ' in rule:
                type, alias = rule.split(' is ')
                Rules.alias_list.append((type, alias))
            elif ' may only ' in rule:
                obj, rest = rule.split(' may only ')
                action, second = rest.split(' ')
                Rules.exclusive_list.append((obj, action, second))
            elif ' may not ' in rule:
                obj, rest = rule.split(' may not ')
                action, second = rest.split(' ')
                Rules.prohibition_list.append((obj, action, second))
            elif ' may ' in rule:
                obj, rest = rule.split(' may ')
                action, second = rest.split(' ')
                Rules.permission_list.append((obj, action, second))

    def resolve_types_inner(types, aliases):
        for (source_type, alias_type) in aliases[:]:
            if source_type in types:
                types.add(alias_type)
                aliases.remove((source_type, alias_type))
                return Rules.resolve_types_inner(types, aliases)
        return types

    def resolve_types(thing):
        types = set(thing.type)
        return Rules.resolve_types_inner(types, Rules.alias_list[:])

    def allowed(action_to_test):
        a_types = Rules.resolve_types(action_to_test.a)
        b_types = Rules.resolve_types(action_to_test.b)

        for (a, action, b) in Rules.exclusive_list:
            if action == action_to_test.name:
                if a in a_types and b in b_types:
                    print ('-- allowed by exclusive_list')
                    return True

        for (a, action, b) in Rules.prohibition_list:
            if action == action_to_test.name:
                if a in a_types and b in b_types:
                    print ('-- forbidden')
                    return False

        for (a, action, b) in Rules.permission_list:
            if action == action_to_test.name:
                if a in a_types and b in b_types:
                    if not action in (x for (a2,x,b2) in Rules.exclusive_list if x == action and a2 in a_types):
                        print ('-- allowed')
                        return True
                    else:
                        print ('-- forbidden by exclusive_list')
                        return False

        print ('-- no rules match')

class Action:
    def __init__(self, name, a, b):
        self.name = name
        self.a = a
        self.b = b

    def invoke(self):
        print('You feel a strange sensation...')

    def forbidden(self):
        print(f"{self.a.name} tries to {self.name} {self.b.name}, but that is not possible")

    def __call__(self):
        if Rules.allowed(self):
            self.invoke()
        else:
            self.forbidden()
        print('----------')

class Wield(Action):
    def __init__(self, thing, weapon):
        super().__init__('wield', thing, weapon)

    def invoke(self):
        if hasattr(self.a, 'weapon'):
            print(f'{self.a.name} drops {self.a.weapon.name}')
        self.a.weapon = self.b
        print(f'{self.a.name} now wields {self.a.weapon.name}')

class Eat(Action):
    def __init__(self, thing, food):
        super().__init__('eat', thing, food)

    def forbidden(self):
        print(f'{self.a.name} tried to eat {self.b.name}, but did not like it very much...')

    def invoke(self):
        print(f'{self.a.name} eats {self.b.name}')

class Thing:
    def __init__(self, name, *type):
        self.name = name
        self.type = ['Thing', *type]

    def action(self, action_class, *args):
        action_class(self, *args)()

if __name__ == '__main__':

    Rules.parse_rules(rules)

    Carl_the_Human = Thing('Carl', 'Human')
    Grump_the_Orc = Thing('Grump', 'Orc')
    Sandy_the_Cow = Thing('Sandy', 'Cow')
    Carls_sword = Thing("Carl's Sword of Justice", 'Sword')
    Grumps_axe = Thing("Grump's rusty Axe", 'Axe')
    Old_bow = Thing("An old bow", 'Bow')

    Sandy_the_Cow.action(Wield, Grumps_axe)
    Sandy_the_Cow.action(Eat, Grumps_axe)
    Sandy_the_Cow.action(Eat, Thing("a bunch of grass", "Grass"))

    Carl_the_Human.action(Wield, Carls_sword)
    Carl_the_Human.action(Wield, Grumps_axe)
    Carl_the_Human.action(Wield, Old_bow)

    Grump_the_Orc.action(Wield, Grumps_axe)
    Grump_the_Orc.action(Wield, Carls_sword)

请注意,确实有一些编程语言,例如Inform7

如果您想了解更多信息,建议您阅读埃里克·利珀特(Eric Lippert)的Wizards and warriors系列,该系列完全讨论了这个问题(我的答案也受到该系列的启发),甚至还使用了类似的示例(幻想类和武器),但是恕我直言,在OO编程语言中,用对象对错误的事物进行建模并尝试将业务逻辑强制进入语言类型系统是常见的陷阱。

答案 1 :(得分:0)

Python语言没有有效的机制来限制对实例或方法的访问。但是,有一个约定,在字段/方法的名称前加上下划线以模拟“受保护”或“私有”行为。

但是,默认情况下,Python类中的所有成员都是公共的。

答案 2 :(得分:0)

如果您希望类Orc具有某些成员,而类Human没有某些成员,并且您仍然希望这些类相互关联(因为它们都是字符),那正是继承的目的。这是一个示例继承层次结构:

Inheritance hierarchy

这是一个示例实现:

class Weapon:
    def __init__(self, damage):
        self.damage = damage

class Gun(Weapon):
    def __init__(self):
        Weapon.__init__(self, 300)

class Grenade(Weapon):
    def __init__(self):
        Weapon.__init__(self, 1000)

class Knife(Weapon):
    def __init__(self):
        Weapon.__init__(self, 50)

class Hammer(Weapon):
    def __init__(self):
        Weapon.__init__(self, 100)


class Character:
    def __init__(self, name, default_weapon, additional):
        self.name = name
        self.default_weapon = default_weapon
        self.additional = additional

    def attack(self):
        pass # some implementation

class Heavily_armed(Character):
    def __init__(self, name):
        Character.__init__(self, name, Gun(), Grenade())


class Lightly_armed(Character):
    def __init__(self, name):
        Character.__init__(self, name, Knife(), Hammer())

class Human(Lightly_armed):
    def __init__(self, name, age, height):
        Lightly_armed.__init__(self, name)
        self.height = height
        self.age = age

class Orc(Heavily_armed):
    def __init__(self, name, danger):
        Heavily_armed.__init__(self, name)
        self.danger = danger

如您所见,每个Character都有武器,但是它们是不同种类的武器。为了扩展它,您可以为每种类型创建一个“可用武器”集,并且仅在该武器集中创建一个实例。对于您实施游戏而言,这可能是可行的设计选择,也可能不是。选择无止境