def test_calculateDistance_differentOrMissingAttributes(self): # emporer penguin: emporer penguin # wings 2 2 # color blackwhite blackwhite # size big - # population - notsomany rt = RelationType("is_a") at = RelationAttributeType("wings") at2 = RelationAttributeType("color") at3 = RelationAttributeType("size") at4 = RelationAttributeType("population") source = Node("EmporerPenguin") target = Node("Bird") r = Relation(rt, source, target) a = r.createAttribute(at, 2) a2 = r.createAttribute(at2, "blackwhite") a3 = r.createAttribute(at3, "big") r2 = Relation(rt, source, target) a4 = r2.createAttribute(at, 2) a5 = r2.createAttribute(at2, "blackwhite") a6 = r2.createAttribute(at4, "notsomany") dist = gd.calculateRelationDistance(r, r2) self.assertEqual(dist, 0.125)
def test_relationAttributeType(self): at = RelationAttributeType("amount") at2 = RelationAttributeType("amount") self.assertEqual(at.name, "amount") self.assertEqual(str(at), "amount") self.assertEqual(repr(at), "RelationAttributeType(name = 'amount')") self.assertIs(at, at2)
def test_RelationMatching(self): rt = RelationType("is_a") at = RelationAttributeType("wings") at2 = RelationAttributeType("color") at3 = RelationAttributeType("size") at4 = RelationAttributeType("population") source = Node("EmporerPenguin") target = Node("Bird") target2 = Node("Thing") r = Relation(rt, source, target) r.createAttribute(at, 2) r.createAttribute(at2, "blackwhite") r.createAttribute(at3, "big") r2 = Relation(rt, source, target) r2.createAttribute(at, 2) r2.createAttribute(at2, "blackwhite") r2.createAttribute(at4, "notsomany") r3 = Relation(rt, source, target2) r3.createAttribute(at, 4) r3.createAttribute(at2, "black") r3.createAttribute(at3, "enormous") t = Topology() t.insertNode(source) t.insertNode(target) t.insertNode(target2) t.insertRelation(r3) t.insertRelation(r) t.insertRelation(r2) matchedRelations = gd.matchRelations(r, source, t) self.assertEqual(matchedRelations[0], r) self.assertEqual(matchedRelations[1], r2) self.assertEqual(matchedRelations[2], r3)
def test_calculateAttributeDistance_sameRelation(self): rt = RelationType("is_a") at = RelationAttributeType("wings") at2 = RelationAttributeType("color") at3 = RelationAttributeType("size") source = Node("EmporerPenguin") target = Node("Bird") r = Relation(rt, source, target) a = r.createAttribute(at, 2) a2 = r.createAttribute(at2, "blackwhite") a3 = r.createAttribute(at3, "big") dist = gd.calculateRelationDistance(r, r) self.assertEqual(dist, 0)
def test_load(self): with open("TestData.json") as file: net = json.load(file) t = Topology() t.load(net) self.assertTrue(t.existsNode(Node("bird"))) self.assertTrue(t.existsNode(Node("pelican"))) self.assertTrue(len(t.nodes), 4) self.assertEqual(len(t.relations),4) self.assertEqual(t.nodes[1], Node("pelican")) self.assertEqual(t.nodes[1].attributes[0].type, NodeAttributeType("size")) self.assertEqual(t.nodes[1].attributes[0].value, "big") self.assertEqual(t.nodes[1].attributes[1].type, NodeAttributeType("color")) self.assertEqual(t.nodes[1].attributes[1].value, "white") r = Relation(RelationType("is_a"), Node("pelican"), Node("bird")) self.assertEqual(t.relations[0], r) r2 = Relation(RelationType("has"), Node("bird"), Node("wings")) r2.createAttribute(RelationAttributeType("amount"), 2) self.assertEqual(t.relations[1], r2)
def load(self, data): ''' This function loads a topology from a dictionary. All previous nodes and relations, if existing, are wiped out. ''' self.nodes.clear() self.relations.clear() attributetypes = data.get("attributetypes") relationtypes = data.get("relationtypes") for node in data.get("nodes"): # load nodes self.nodes.append(Node(node)) if "node_attributes" in data: for node_attribute in data.get( "node_attributes"): # load node attributes at = NodeAttributeType(attributetypes[node_attribute[0]]) index = node_attribute[1] value = node_attribute[2] self.nodes[index].createAttribute(at, value) for relation in data.get("relations"): # load relations rt = RelationType(relationtypes[relation[0]]) source = self.nodes[relation[1]] target = self.nodes[relation[2]] self.relations.append(Relation(rt, source, target)) for relation_attribute in data.get( "relation_attributes"): # load relation attributes at = RelationAttributeType(attributetypes[relation_attribute[0]]) index = relation_attribute[1] value = relation_attribute[2] self.relations[index].createAttribute(at, value)
def test_calculateDistance_Symmetry(self): rt = RelationType("is_a") at = RelationAttributeType("wings") at2 = RelationAttributeType("size") source = Node("EmporerPenguin") source2 = Node("LittlePenguin") target = Node("Bird") r = Relation(rt, source, target) r2 = Relation(rt, source2, target) r.createAttribute(at, 2) r2.createAttribute(at, 2) r.createAttribute(at2, "big") r2.createAttribute(at2, "small") dist1 = gd.calculateRelationDistance(r, r2) dist2 = gd.calculateRelationDistance(r2, r) self.assertEqual(dist1, dist2)
def test_hasAttributeOfType(self): rt = RelationType("has") at = RelationAttributeType("amount") source = Node("bird") target = Node("wing") r = Relation(rt, source, target) a = r.createAttribute(at, 2) x = r.hasAttributeOfType(at) self.assertTrue(x)
def test_SameAttributeTypeTwice(self): rt = RelationType("has") at = RelationAttributeType("amount") source = Node("bird") target = Node("wing") r = Relation(rt, source, target) a = r.createAttribute(at, 2) with self.assertRaises(AssertionError): a2 = r.createAttribute(at, 10)
def relationGetAttributeValue(env, topology, type, source, target, attributeType): tmpRelation = Relation(RelationType(type), Node(source), Node(target)) t = env.vars[topology] assert t is not None r = t.tryGetRelation(tmpRelation) assert r is not None value = r.getAttributeValue(RelationAttributeType(attributeType)) return {"result": value}
def relationCreateAttribute(env, topology, relationType, source, target, attributeType, value, attributes=None): tmpRelation = Relation(RelationType(relationType), Node(source), Node(target)) for type, value in attributes: tmpRelation.createAttribute(RelationAttributeType(type), value) t = env.vars[topology] assert t is not None r = t.tryGetRelation(tmpRelation) assert r is not None r.createAttribute(RelationAttributeType(attributeType), value) return success
def hasAnyAttribute(topology, relation): for attr in data: type = RelationAttributeType( attr["type"]) if attr["type"] != None else None value = attr["value"] for attribute in relation.attributes: if (type == None or attribute.type == type) and ( value == None or attribute.value == value): return True return False
def relationHasAttribute(env, topology, type, source, target, attributeType, value): tmpRelation = Relation(RelationType(type), Node(source), Node(target)) attr = Attribute(RelationAttributeType(attributeType), value) t = env.vars[topology] assert t is not None r = t.tryGetRelation(tmpRelation) assert r is not None result = r.hasAttribute(attr) return {"result": result}
def test_calculateDistance_customWeights(self): rt = RelationType("is_a") at = RelationAttributeType("wings") at2 = RelationAttributeType("size") source = Node("EmporerPenguin") source2 = Node("LittlePenguin") target = Node("Bird") r = Relation(rt, source, target) r2 = Relation(rt, source2, target) r.createAttribute(at, 2) r.createAttribute(at2, "big") r2.createAttribute(at2, "small") dist = gd.calculateRelationDistance(r, r2, wSource=0.5, wDifferentStringValue=0.3, wMissingAttribute=0.7) self.assertEqual(dist, 0.75)
def test_relationWithAttributes(self): rt = RelationType("has") at = RelationAttributeType("amount") source = Node("bird") target = Node("wing") r = Relation(rt, source, target) a = r.createAttribute(at, 2) self.assertIs(r.attributes[0], a) self.assertIs(a.type, at) self.assertEqual(a.value, 2) self.assertEqual(str(a), "amount: 2") self.assertEqual(repr(a), "Attribute(type = 'amount', value = '2')")
def createRelation(topology, source, relationType, target, attributeType=None, attributeValue=None): relationtype = RelationType(relationType) relation = Relation(relationtype, source, target) if attributeType != None: relationAttributeType = RelationAttributeType(attributeType) relation.createAttribute(relationAttributeType, attributeValue) topology.insertRelation(relation) return relation
def relationHasAttributeOfType(env, topology, type, source, target, attributeType): tmpRelation = Relation(RelationType(type), Node(source), Node(target)) t = env.vars[topology] assert t is not None r = None for rel in t.relations: if rel.source == tmpRelation.source and rel.target == tmpRelation.target and rel.type == tmpRelation.type: r = rel assert r is not None result = r.hasAttributeOfType(RelationAttributeType(attributeType)) return {"result": result}
def hasAllAttributes(topology, relation): for attr in data: type = RelationAttributeType( attr["type"]) if attr["type"] != None else None value = attr["value"] attributeInAttributeList = False for attribute in relation.attributes: if (type == None or attribute.type == type) and ( value == None or attribute.value == value): attributeInAttributeList = True # if the current attribute does not exist within the relation: return False if not attributeInAttributeList: return False # if none of the given attributes is missing in the relation: return True return True
def hasAnyRelation(topology, node): for rel in data: type = RelationType(rel["type"]) if rel["type"] != None else None target = Node(rel["target"]) if rel["target"] != None else None attrs = [ Attribute(RelationAttributeType(a["type"]), a[value]) for a in rel["attributes"] ] if rel["attributes"] != None else None relations = [r for r in topology.relations if r.source == node] for r in relations: if (type == None or r.type == type) and ( target == None or r.target == target) and (attrs == None or all( r.hasAttribute(a) for a in attrs)): return True return False
def missingAnyAttribute(topology, relation): for attr in data: type = RelationAttributeType( attr["type"]) if attr["type"] != None else None value = attr["value"] attributeInAttributeList = False # Flag for attribute in relation.attributes: if (type != None and attribute.type == type) or ( value != None and attribute.value == value): attributeInAttributeList = True break # If an attribute is found, that does not exist within the relation: return True if not attributeInAttributeList: return True # It the relation is not missing any of the given attributes: return False return False
def missingAllRelations(topology, node): for rel in data: type = RelationType(rel["type"]) if rel["type"] != None else None target = Node(rel["target"]) if rel["target"] != None else None attrs = [ Attribute(RelationAttributeType(a["type"]), a["value"]) for a in rel["attributes"] ] if rel["attributes"] != None else None relations = [r for r in topology.relations if r.source == node] for r in relations: # if a relation is found, that does exist within the topology: return False if (type == None or r.type == type) and ( target == None or r.target == target) and (attrs == None or all( r.hasAttribute(a) for a in attrs)): return False # if none of the given relations is found within the topology: return True return True
def hasAllRelations(topology, node): for rel in data: type = RelationType(rel["type"]) if rel["type"] != None else None target = Node(rel["target"]) if rel["target"] != None else None attrs = [ Attribute(RelationAttributeType(a["type"]), a["value"]) for a in rel["attributes"] ] if rel["attributes"] != None else None relations = [r for r in topology.relations if r.source == node] relationInRelationList = False for r in relations: if (type == None or r.type == type) and ( target == None or r.target == target) and (attrs == None or all( r.hasAttribute(a) for a in attrs)): relationInRelationList = True # if the current relation does not exist within the topology: return False if not relationInRelationList: return False # if none of the given relations is missing in the topology: return True return True
def missingAnyRelation(topology, node): for rel in data: type = RelationType(rel["type"]) if rel["type"] != None else None target = Node(rel["target"]) if rel["target"] != None else None attrs = [ Attribute(RelationAttributeType(a["type"]), a["value"]) for a in rel["attributes"] ] if rel["attributes"] != None else None relationInRelationList = False # Flag relation = [r for r in topology.relations if r.source == node] for r in relations: if (type != None and r.type == type) or ( target != None and r.target == target) or (attrs != None and any( r.hasAttribute(a) for a in attrs)): relationInRelationList = True break # If a relation is found, that does not exist within the topology: return True if not relationInRelationList: return True # It the topology is not missing any of the given relations: return False return False
def parseLog(file, parent): counter = 0 current_linenumber = 0 test = "" top = Topology() top.setParent(parent) #### utilities #### utilities = { # counters "counters": { "friendlyPlayerCounter": 0, "hostilePlayerCounter": 0, "minionCount": 0, "hearthstone_internal_id_count": 0, "Location_Friendly_Deck_count": 0, "Location_Friendly_Hand_count": 0, "Location_Friendly_Board_count": 0, "Location_Friendly_Graveyard_count": 0, "Location_Hostile_Deck_count": 0, "Location_Hostile_Hand_count": 0, "Location_Hostile_Board_count": 0, "Location_Hostile_Graveyard_count": 0, "Location_Friendly_Play_Weapon_count": 0, "Location_Hostile_Play_Weapon_count": 0, "Location_Hostile_Secrets_count": 0, "Location_Friendly_Secrets_count": 0 }, # heroes "heroes": { "Jaina Proudmoore": "Mage", "Rexxar": "Hunter", "Uther Lightbringer": "Paladin", "Garrosh Hellscream": "Warrior", "Malfurion Stormrage": "Druid", "Gul'dan": "Warlock", "Thrall": "Shaman", "Anduin Wrynn": "Priest", "Valeera Sanguinar": "Rogue" }, # zones "zones": { "FRIENDLY DECK": "Location_Friendly_Deck", "FRIENDLY HAND": "Location_Friendly_Hand", "FRIENDLY PLAY": "Location_Friendly_Board", "FRIENDLY GRAVEYARD": "Location_Friendly_Graveyard", "OPPOSING DECK": "Location_Hostile_Deck", "OPPOSING HAND": "Location_Hostile_Hand", "OPPOSING PLAY": "Location_Hostile_Board", "OPPOSING GRAVEYARD": "Location_Hostile_Graveyard", "FRIENDLY PLAY (Hero)": "Location_Friendly_Play_Hero", "FRIENDLY PLAY (Hero Power)": "Location_Friendly_Play_Hero_Power", "FRIENDLY PLAY (Weapon)": "Location_Friendly_Play_Weapon", "FRIENDLY SECRET": "Location_Friendly_Secrets", "OPPOSING PLAY (Hero)": "Location_Hostile_Play_Hero", "OPPOSING PLAY (Hero Power)": "Location_Hostile_Play_Hero_Power", "OPPOSING PLAY (Weapon)": "Location_Hostile_Play_Weapon", "OPPOSING SECRET": "Location_Hostile_Secrets" }, "current_locations": { "Location_Friendly_Deck": None, "Location_Friendly_Hand": None, "Location_Friendly_Board": None, "Location_Friendly_Graveyard": None, "Location_Hostile_Deck": None, "Location_Hostile_Hand": None, "Location_Hostile_Board": None, "Location_Hostile_Graveyard": None }, "current_locations_filled": { "Location_Friendly_Deck": [], "Location_Friendly_Hand": [], "Location_Friendly_Board": [], "Location_Friendly_Graveyard": [], "Location_Hostile_Deck": [], "Location_Hostile_Hand": [], "Location_Hostile_Board": [], "Location_Hostile_Graveyard": [] }, # this dictionary maps a minion_id to an actual minion node, which may change every turn # a was_a relation is created if the minion changes "minion_history": {}, # this dictionary maps a minion_id to its type "minion_type": {}, # this dictionary maps a minion to their current health in order to calculate it more easily "minion_health": {}, "minion_damage": {}, ### utilities ### "current_player": -1, "zones_used_in_this_turn": {}, "current_turn": 1, "current_turn_node": None, "current_subturn": 0, "current_subturn_node": None } # initialization game0 = createNode(top, "Game0") createRelation(top, game0, "is_a", getNode(top, "Game")) #this is the initial turn gameSetup = createNode(top, "GameSetup") createRelation(top, gameSetup, "is_a", getNode(top, "Turn")) utilities["current_turn_node"] = gameSetup subturn0 = createNode(top, "SubTurn_0") createRelation(top, subturn0, "is_a", getNode(top, "Subturn")) createRelation(top, gameSetup, "has", subturn0) utilities["current_subturn_node"] = subturn0 utilities["current_subturn"] = 1 friendlyPlayer = createNode( top, "FriendlyPlayer_{0}".format( utilities["counters"]["friendlyPlayerCounter"])) hostilePlayer = createNode( top, "HostilePlayer_{0}".format( utilities["counters"]["hostilePlayerCounter"])) friendly_player_node = getNode(top, "Friendly_Player") hostile_player_node = getNode(top, "Hostile_Player") createRelation(top, friendlyPlayer, "is_a", friendly_player_node) createRelation(top, hostilePlayer, "is_a", hostile_player_node) createRelation(top, game0, "has", friendlyPlayer) createRelation(top, game0, "has", hostilePlayer) nextLine, phrase, current_linenumber = goToNextPhrases( file, ["TRANSITIONING"], current_linenumber) minion_type = "" # set up of initial game state # detect initialization end: when "The Coin" is transitioned the initialization is over while nextLine != -1 and minion_type != "The Coin": minionCount = utilities["counters"]["minionCount"] nextLine = nextLine.split("TRANSITIONING card [", 1)[1] lineEnd = nextLine.split("] to ", 1)[1][:-1] valuePairs = parseValues_Transitioning(nextLine) minion_id = valuePairs["id"] minion_node_name = "Minion_{0}".format(minionCount) minion_node = getOrCreateNode(top, minion_node_name) hearthstone_internal_card_id_node = createNode( top, "hearthstone_internal_card_id_{0}".format(minion_id)) createRelation(top, minion_node, "has", hearthstone_internal_card_id_node) #TODO link to static network -> hearthstone_internal_card_id_XX - is_a - hearthstone_internal_card_id #add minion to the minion_history dict utilities["minion_history"][minion_id] = minion_node if (top.existsNodeByName("{0}_0".format(utilities["zones"][lineEnd]))): targetZone = top.getNodeByName("{0}_0".format( utilities["zones"][lineEnd])) else: targetZone = createNode( top, "{0}_0".format(utilities["zones"][lineEnd])) targetZone_node = getNode(parent, utilities["zones"][lineEnd]) createRelation(top, targetZone, "is_a", targetZone_node) #update counter utilities["counters"][utilities["zones"][lineEnd] + "_count"] = 1 createRelation(top, targetZone, "has", minion_node) if "name" in valuePairs.keys(): minion_type = valuePairs["name"] minion_type_node = getNode(parent, minion_type) if minion_type_node is None: minion_type_node = getNode(parent, utilities["heroes"][minion_type]) minion_health = 30 utilities["minion_health"][minion_id] = minion_health utilities["minion_damage"][minion_id] = 0 minion_curhealth_rel = createRelation( top, minion_node, "has", getNode(parent, "CurHealth")) minion_curhealth_rel.createAttribute( RelationAttributeType("amount"), minion_health) else: minion_health_rel = getRelation(parent, minion_type, "has", "MaxHealth") if minion_health_rel is None: #TODO: make it a spell print( "Minion '{}' has no maxhealth. -> is no minion".format( minion_type)) else: assert (minion_health_rel is not None, "{0} has no attribute 'MaxHealth'!") minion_health = minion_health_rel.getAttributeValue( RelationAttributeType("amount")) utilities["minion_health"][minion_id] = minion_health utilities["minion_damage"][minion_id] = 0 createRelation(top, minion_node, "has", getNode(parent, "CurHealth"), RelationAttributeType("amount"), minion_health) utilities["minion_type"][minion_id] = minion_type_node createRelation(top, minion_node, "is_a", minion_type_node) else: utilities["minion_type"][minion_id] = None minionCount = minionCount + 1 utilities["counters"]["minionCount"] = minionCount if minion_type == "The Coin": print("The Coin passed") nextLine, phrase, current_linenumber = goToNextPhrases( file, ["TRANSITIONING"], current_linenumber) nextLine, phrase, current_linenumber = goToNextPhrases( file, ["TRANSITIONING", "BlockType=ATTACK", "tag=DAMAGE"], current_linenumber) #normal game play starts here while nextLine != -1 and "CREATE_GAME" not in nextLine: utilities, top = functions[phrase](nextLine, utilities, top) nextLine, phrase, current_linenumber = goToNextPhrases( file, ["TRANSITIONING", "BlockType=ATTACK", "tag=DAMAGE", "CREATE_GAME"], current_linenumber) return top
def ProcessPhrase_Transition(org_line, utilities, topology): line = org_line.split("TRANSITIONING card [", 1)[1] lineEnd = line.split("] to ", 1)[1][:-1] while lineEnd == "": return utilities, topology zone = utilities["zones"][lineEnd] valuePairs = parseValues_Transitioning(line) new_player = valuePairs["player"] #check if new Turn is needed if new_player != utilities["current_player"]: utilities, topology = startNewTurn(utilities, topology) #since transitions are also subturns, create new subturn newSubturn = createNode(topology, "SubTurn_{}".format(utilities["current_subturn"])) createRelation(topology, newSubturn, "is_a", getNode(topology, "Subturn")) if not utilities["current_subturn_node"] is None: createRelation(topology, newSubturn, "was", utilities["current_subtturn_node"]) utilities["current_subturn"] += 1 utilities["current_subturn_node"] = newSubturn minionCount = utilities["counters"]["minionCount"] utilities["counters"]["minionCount"] = minionCount + 1 minion_id = valuePairs["id"] minion_node_name = "Minion_{0}".format(minionCount) minion_node = getNode(topology, minion_node_name) if minion_node is None: minion_node = createNode(topology, minion_node_name) hearthstone_internal_card_id_node = getOrCreateNode( topology, "hearthstone_internal_card_id_{0}".format(minion_id)) createRelation(topology, minion_node, "has", hearthstone_internal_card_id_node) # create relation to previous state of the minion if it existed before if minion_id in utilities["minion_history"].keys(): previous = utilities["minion_history"][minion_id] createRelation(topology, minion_node, "was", previous) utilities["minion_history"][minion_id] = minion_node else: utilities["minion_history"][minion_id] = minion_node # create new Zone instance if neccessary current_zone_name = "{0}_{1}".format( zone, utilities["counters"][zone + "_count"]) if current_zone_name in utilities["zones_used_in_this_turn"].keys(): targetZone = utilities["zones_used_in_this_turn"][current_zone_name] else: targetZone = createNode(topology, current_zone_name) pastZone = getNode( topology, "{0}_{1}".format(zone, utilities["counters"][zone + "_count"] - 1)) if pastZone is None: parentZone = getNode(topology.parent, utilities["zones"][lineEnd]) createRelation(topology, targetZone, "is_a", parentZone) else: createRelation(topology, targetZone, "was", pastZone) # update counter utilities["counters"][zone + "_count"] = utilities["counters"][zone + "_count"] + 1 utilities["zones_used_in_this_turn"][current_zone_name] = targetZone createRelation(topology, targetZone, "has", minion_node) #associate targetZone with current subturn createRelation(topology, newSubturn, "has", targetZone) #update the current_locations dicts if not zone in [ "Location_Friendly_Play_Hero", "Location_Hostile_Play_Hero", "Location_Hostile_Play_Hero_Power", "Location_Friendly_Play_Hero_Power", "Location_Friendly_Play_Weapon", "Location_Hostile_Play_Weapon", "Location_Friendly_Secrets", "Location_Hostile_Secrets" ]: utilities["current_locations_filled"][zone].append(minion_node) utilities["current_locations"][zone] = targetZone if "name" in valuePairs.keys(): minion_type = valuePairs["name"] minion_type_node = getNode(topology.parent, minion_type) if minion_type_node is None: minion_type_node = getNode(topology.parent, utilities["heroes"][minion_type]) createRelation(topology, minion_node, "is_a", minion_type_node) # when the minion is not in the dict "minion_type", then it must be a new one, that hasen't been in the game yet. if minion_id not in utilities["minion_type"].keys( ) or utilities["minion_type"][minion_id] is None: utilities["minion_type"][minion_id] = minion_type_node #get the relation that indicates how much maxhealth a minion has (from static network) minion_maxhealth_rel = getRelation(topology.parent, minion_type, "has", "MaxHealth") # this relation should exist otherwise -> error if minion_maxhealth_rel is None: print("minion '{}' has no maxhealth -> is no minion".format( minion_type)) else: assert (minion_maxhealth_rel is not None, "{0} has no attribute 'MaxHealth'!") # now get the actual amount of health from the relation and set it in the minion_health dict minion_health = minion_maxhealth_rel.getAttributeValue( RelationAttributeType("amount")) utilities["minion_health"][minion_id] = minion_health utilities["minion_damage"][minion_id] = 0 #create relation from current minion node to CurHealth (static node) minion_curhealth_rel = createRelation( topology, minion_node, "has", getNode(topology.parent, "CurHealth")) #this relation should have the current health, i.e. the maxhealth at this point, as an amount minion_curhealth_rel.createAttribute( RelationAttributeType("amount"), minion_health) return utilities, topology