1

I have a piece of code that defines possible item types and enchantment types in a game. Some enchantment types are incompatible with each other or with certain item types. I would like each item type to have information on which enchantments are incompatible with it, as well as have every enchantment contain information about the items it is compatible with, without having to constantly look one of them up.

items.py:

from enum import Enum, auto

class ItemType(Enum):
  PLATE = auto()
  HELMET = auto()
  BOOTS = auto()
  SWORD = auto()
  BOW = auto()
# Get compatible enchantment types
from enchantments import EnchantmentType
for it in ItemType:
  it.compatible_enchantments = set(filter(lambda ench: it in ench.compatible_items, EnchantmentType))


class Item:
  def __init__(self, item_type, enchantments=None):
    self.type = item_type
    self.enchantments = []
    if enchantments:
      for ench in enchantments:
        self.add_enchantment(ench)

  def add_enchantment(self, enchantment, i=None):
    if enchantment.type not in self.compatible_enchantments:
      raise ValueError(f"Enchantment {enchantment.type} is incompatible with {self.type}.")
    if i is None:
      self.enchantments.append(enchantment)
    else:
      self.enchantments.insert(enchantment, i)    from enum import Enum
from items import ItemType as IT

enchantments.py:

from enum import Enum, auto

class EnchantmentType(Enum):
  ENHANCED_ARMOUR = IT.HELMET, IT.PLATE, IT.BOOTS, IT.RING
  ENHANCED_DAMAGE = IT.SWORD, IT.BOW, IT.RING
  YELLOW_COLOUR = ()

  def __init__(self, *compatible_item_types):
    self.compatible_items = set(IT) if not compatible_item_types else compatible_item_types
    self.incompatible_enchantments = set()
# Fill in the incompatible enchantments
for mutualy_exclusive_group in (
    {EnchantmentType.ENHANCED_ARMOUR, EnchantmentType.ENHANCED_DAMAGE}
):
  for ench in mutualy_exclusive_group:
    ench.incompatible_enchantments += mutualy_exclusive_group - {ench}


class Enchantment:
  def __init__(self, enchantment_type, level=None):
    self.type = enchantment_type
    self.level = level

main.py:

from enchantments import EnchantmentType as ET

print(ET.ENHANCED_ARMOUR.compatible_items)
print(ET.ENHANCED_DAMAGE.compatible_items)
print(ET.YELLOW_COLOUR.compatible_items)

Running this code gives me an ImportError citing circular imports.

Following this answer I've tried changing the from x import y statements to import x but am still getting the same error.

I've temporarily solved the issue by removing from enchantments import EnchantmentType from items.py and moving the lines

from enchantments import EnchantmentType
for it in ItemType:
  it.compatible_enchantments = set(filter(lambda ench: it in ench.compatible_items, EnchantmentType))

into enchantments.py. However, this requires that any module that wishes to have correct information about the enchantments manually import enchantments.py -- importing items.py only will result in all the compatible_enchantments being empty.

Is there a way to make my circular imports work without merging the two files?

Mate de Vita
  • 1,102
  • 12
  • 32
  • You should decide what comes logically first and what then builds on that. From your code it's not completely clear to me, but here's another example: say there are various components, such as tires or engines, which are used to manufacture cars. Not every component is compatible with each type of car, but that's not the components' concern. A diesel engine does not need to know whether it can be used in a Tesla car, it's the car's job to identify what components can be used to manufacture it. Hence you would have `cars.py` importing `components.py` but not the other way round. – a_guest Feb 05 '21 at 21:04

1 Answers1

0

Instead of setting compatible_enchantments on each of the ItemTypes, why not have a dictionary somewhere (probably in enchantments.py) that maps an EnchantmentType to the valid set of ItemTypes it can be applied to?

To me, it seems like the ItemType doesn't need to know about enchantments, but rather enchantments need to know if they can be applied to a particular ItemType.

Andrew McClement
  • 1,171
  • 5
  • 14