簡體   English   中英

Python如何為游戲中的每個類制定一套規則

[英]Python how to to make set of rules for each class in a game

在C#中,我們必須獲取/設置規則,但是我不知道如何在Python中做到這一點。

示例:獸人只能裝備斧頭,其他武器不合格人類只能裝備劍,其他武器不合格。

我怎樣才能告訴Python Orc無法完成上述示例中的操作?

預先感謝您的答復,希望這對你們有意義。

在C#中,我們必須獲取/設置規則,但是我不知道如何在Python中做到這一點。

不會。Getters和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'類型'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忽略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 ,但Wield是允許它。 好吧, 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的內容

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)的《 巫師與戰士》系列,該系列完全討論了這個問題(我的答案也受到該系列的啟發),甚至使用了類似的示例(幻想類和武器),但是恕我直言在OO編程語言中,用對象對錯誤的事物建模並試圖將業務邏輯強加到語言類型系統中是一個常見的陷阱。

Python語言沒有有效的機制來限制對實例或方法的訪問。 但是,有一個約定,在字段/方法的名稱前加上下划線以模擬“受保護”或“私有”行為。

但是,默認情況下,Python類中的所有成員都是公共的。

如果您希望Orc類具有某些成員,而Human類則沒有,而您仍然希望這些類相互關聯(因為它們都是字符),那正是繼承的目的。 這是一個示例繼承層次結構:

繼承層次

這是一個示例實現:

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都有武器,但是那是不同種類的武器。 為了擴展它,您可以為每種類型創建一個“可用武器”集,並且僅在該武器集中創建一個實例。 對於您實施游戲而言,這可能是可行的設計選擇,也可能不是。 選擇無止境

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM