【问题标题】:Battleship: How to automate instances creation?战舰:如何自动创建实例?
【发布时间】:2018-08-07 15:07:37
【问题描述】:

我正在制作战舰游戏。我创建了一个 Ship 类来给船一个位置。

创建类后,我必须创建所有实例,我想知道是否有办法自动化。

大部分程序是无关紧要的,但我将其保留以防万一它可能会影响它是否可以自动化。

import random

class Ship(object):

    def __init__(self, length):
        self.length = length

    def direction(self):
        vh = random.choice(['v','h'])
        return vh

    def location(self):
        space = []
        row = random.randint(0, 10-self.length)
        column = random.randint(0, 10-self.length)
        if self.direction == 'v':
            for x in range(self.length):
                space.append(f'{column}{row+x}')
        else:
            for x in range(self.length):
                space.append(f'{column}{row+x}')
        return space

ships_amount = {
    'carrier' : 1,
    'battleship' : 2,
    'cruiser' : 3,
    'destroyer' : 4
}

ships_length = {
    'carrier' : 5,
    'battleship' : 4,
    'cruiser' : 3,
    'destroyer' : 2
}

我想这样做:

carrier1 = Ship(ships_length['carrier'])
battleship1 = Ship(ships_length['battleship'])
battleship2 = Ship(ships_length['battleship'])
cruiser1 = Ship(ships_length['cruiser'])
cruiser2 = Ship(ships_length['cruiser'])
cruiser3 = Ship(ships_length['cruiser'])
destroyer1 = Ship(ships_length['destroyer'])
destroyer2 = Ship(ships_length['destroyer'])
destroyer3 = Ship(ships_length['destroyer'])
destroyer4 = Ship(ships_length['destroyer'])

但自动化

【问题讨论】:

  • 你想得到什么结果?清单可以吗?您目前获得每艘船的命名变量。
  • 你想遍历ships_length吗?
  • 我已经尝试过迭代 ship_amount 并迭代每个键的数量值,然后为每艘船创建一个实例,但它不起作用
  • @doctorlove 我不明白你的意思是什么我想要结束。我只想创建实例。我可以稍后获取其他内容的位置。
  • 是否有任何理由为每个对象创建单独的名称?有几种方法可以做到这一点,但如果您想稍后以编程方式访问它们,它并不是很有用。最好创建一个字典并使用名称作为键。

标签: python python-3.x class oop


【解决方案1】:

您可以遍历您想要的船并查找它们的长度来制作它们:

 ships = []
 for type_of_ship in ships_amount:
   ships.append(Ship(ships_length[type_of_ship]))

甚至

ships = [Ship(ships_length[k]) for k in ships_amount]

(在第二个示例中,ky 是 key 的简写,或者在 for 循环中现在称为 type_of_ship

这将为您提供每种类型的船之一。

这不会为您提供名为“carrier1”等的变量,但您将能够对ships 中的每个项目进行处理。

例如

for ship in ships:
    print(ship.length)

要获得每种船只的规定数量或数量,您需要在循环中制造额外的船只。 通过迭代items(),您将得到一个键和值,我称之为kv,尽管它们应该得到更好的名称。 字典中的值告诉你有多少:

ships = []
for k, v in ships_amount.items():
    ships.extend([Ship(ships_length[k]) for _ in range(v)])

这会给你你要求的 10 艘船。

【讨论】:

  • 在第一个示例中使用代表性名称可能会更有帮助,例如“ship_type”而不是“k”。这样,在迭代字典时可能更容易理解返回的内容。但无论如何,一个不错的小总结!
  • 匆忙进行黑客攻击 - 但没有任何借口。
【解决方案2】:

如果您为所需的每个 Ship 模型创建 Ship 的子类,您可以将它们分组到 Fleet 中,然后直接在一行代码中创建舰队...

可能是这样的:

import random

class Ship:             # this becomes an abstract class, not to be instanciated
                        # you could have it inherit from ABC (Abstract Base Class)
    def __init__(self):
        self.length = self.__class__.length
        self.heading = None
        self.set_heading()

        self.location = None
        self.set_location()

    def set_heading(self):
        self.heading = random.choice(['v','h'])

    def set_location(self):   # this method needs more work to prevent
                              # ships to occupy the same spot and overlap 
        space = []
        row = random.randint(0, 10 - self.length)
        column = random.randint(0, 10 - self.length)
        if self.heading == 'v':
            for c in range(self.length):
                space.append((row, column + c))
        elif self.heading == 'h':
            for r in range(self.length):
                space.append((row + r, column))
        self.location = space

    def __str__(self):
        return f'{self.__class__.__name__} at {self.location}'


class AircraftCarrier(Ship):    # every type of ship inherits from the base class Ship
    length = 5                  # Each class of ship can have its own specifications
                                # here, length, but it could be firepower, number of sailors, cannons, etc...

class BattleShip(Ship):
    length = 4

class Cruiser(Ship):
    length = 3

class Destroyer(Ship):
    length = 2


class Fleet:
    ships_number = {AircraftCarrier : 1,
                    BattleShip: 2, 
                    Cruiser: 3, 
                    Destroyer: 4}
    def __init__(self):
        self.ships = [ship() for ship, number in Fleet.ships_number.items() 
                      for _ in range(number)]

    def __str__(self):
        return '\n'.join([str(ship) for ship in self.ships])


if __name__ == '__main__':

    fleet = Fleet()         # <-- the creation of the entire Fleet of Ships 
    print(fleet)            #     takes now one line of code now

示例输出:

(位置是随机分配的,每次运行都会有所不同。)

AircraftCarrier at [(1, 2), (2, 2), (3, 2), (4, 2), (5, 2)]
BattleShip at [(5, 3), (6, 3), (7, 3), (8, 3)]
BattleShip at [(5, 1), (6, 1), (7, 1), (8, 1)]
Cruiser at [(4, 7), (5, 7), (6, 7)]
Cruiser at [(0, 5), (0, 6), (0, 7)]
Cruiser at [(6, 6), (7, 6), (8, 6)]
Destroyer at [(4, 8), (5, 8)]
Destroyer at [(3, 5), (4, 5)]
Destroyer at [(1, 5), (1, 6)]
Destroyer at [(2, 1), (2, 2)]

添加一种新型船:

添加新船型非常简单:只需创建一个继承抽象基类Ship的新类,并将新船的数量添加到舰队组成中即可:

class Submarine(Ship):
    length = 1

Fleet.ships_number[Submarine] = 5   # or add this entry directly in the class Fleet data

舰队现在增加了 5 艘潜艇:

AircraftCarrier at [(4, 1), (5, 1), (6, 1), (7, 1), (8, 1)]
BattleShip at [(5, 5), (6, 5), (7, 5), (8, 5)]
BattleShip at [(0, 0), (1, 0), (2, 0), (3, 0)]
Cruiser at [(5, 2), (5, 3), (5, 4)]
Cruiser at [(2, 0), (3, 0), (4, 0)]
Cruiser at [(7, 7), (8, 7), (9, 7)]
Destroyer at [(4, 3), (5, 3)]
Destroyer at [(2, 1), (2, 2)]
Destroyer at [(0, 8), (1, 8)]
Destroyer at [(3, 6), (3, 7)]
Submarine at [(8, 8)]
Submarine at [(0, 7)]
Submarine at [(3, 4)]
Submarine at [(5, 9)]
Submarine at [(9, 3)]

【讨论】:

  • 好建议,但有一个缺陷(除了可能的重叠):在底部 5 行或右侧 5 列中根本不能放置载体,因为两个标题的长度都被盲目地减去。 “row = random.randint(0, 10 - self.length)”可以替换为“row = random.randint(0, 10 - (self.length * (self.heading == 'v') or 1)) " 和 "column = random.randint(0, 10 - self.length)" 通过 "column = random.randint(0, 10 - (self.length * (self.heading == 'h') or 1))"如果标题为“v”,则仅减去行的长度,如果标题为“h”,则减去列的长度。否则船宽减 1。
  • "self.length = self.__class__.length" 类属性 'length' 就足够了,因为船长是船型的固有属性。除非我们预计相同船型的长度会有所不同。
  • Reblochon,我完全理解,您已经表示需要对其进行修改。我知道这只是慷慨附加价值的一部分。我的问题是:除了最终类中定义的类属性“length”之外,为什么还要在每个 Ship 的 init 中创建实例属性“self.length”?将长度定义为类属性还不够吗?您可以使用“self.length”在抽象类中访问它,就像它是一个实例属性一样。
  • 也就是说,将长度作为每个飞船实例的属性在当时感觉是正确的做法。例如,我喜欢每个对象都有自己的长度:类属性在类级别被修改时会影响所有对象;一旦创建,一种类型的船几乎不会改变长度,但是您可以拥有更大的潜艇,并通过修改长度来做到这一点?此外,这个长度实例属性稍后可能用于表示一艘船的剩余活动长度,在它的一些段被击中之后......
  • 为了解决船只的放置问题,我想在船只所在的位置有一个Ocean 对象或MaritimeMap 对象会很有用。该对象可以验证船舶放置是否足够。它还可以将投篮限定为命中或未命中......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-19
  • 1970-01-01
  • 2021-11-30
  • 2013-04-17
  • 1970-01-01
相关资源
最近更新 更多