Python changeFreeBonus problem

Joined
Jul 5, 2004
Messages
23,562
Location
Canberra, Australia
I am trying to get a bonus from a building. I can't use the <FreeBonus> tag because it breaks corporations (and guilds).

In particular I have captured a wild horse (cow, pig, elephant or deer) and now want to settle it as a horse_herd (cow_herd,...) building in a city providing a bonus.

I was told to use
Code:
pCity.changeFreeBonus (gc.getInfoTypeForString("BONUS_GOLD"), 1)
but it does not work! As you can see in the image below I have built all the buildings but don't have any resources.

Bits of code and traces in my mod.

Spoiler :
In the init function I have:-

Code:
    ga_iBuildingBonusRules_InfoTypeNum = []
    if (gb_OptionBuildingResourcePossible == True):
        as_BuildingBonusRules = [
        ['BUILDING_ELEPHANT_HERD', 'BONUS_IVORY'],
        ['BUILDING_HORSE_HERD', 'BONUS_HORSE'],
        ['BUILDING_DEER_HERD', 'BONUS_DEER'],
        ['BUILDING_COW_HERD', 'BONUS_COW'],
        ['BUILDING_PIG_HERD', 'BONUS_PIG']
        ]
        
        # Convert these into integers for speed when used in game
        for i in range(as_BuildingBonusRules.__len__()):
            iBuilding = gc.getInfoTypeForString(as_BuildingBonusRules[i][0])
            iBonus = gc.getInfoTypeForString(as_BuildingBonusRules[i][1])
            BugUtil.debug("Building %s (%d) gives bonus %s (%d).", as_BuildingBonusRules[i][0],  iBuilding, as_BuildingBonusRules[i][1], iBonus)
            if (iBuilding >= 0 ) and (iBonus >= 0):
                ga_iBuildingBonusRules_InfoTypeNum.append([iBuilding, iBonus])
            else:
                BugUtil.warn("Building %s (%d) gives bonus %s (%d).  Contains bad building or bomus.", as_BuildingBonusRules[i][0],  iBuilding, as_BuildingBonusRules[i][1], iBonus)

which produces trace prints
Code:
12:41:29 DEBUG: Building BUILDING_ELEPHANT_HERD (570) gives bonus BONUS_IVORY (26).
12:41:29 DEBUG: Building BUILDING_HORSE_HERD (564) gives bonus BONUS_HORSE (4).
12:41:29 DEBUG: Building BUILDING_DEER_HERD (571) gives bonus BONUS_DEER (15).
12:41:29 DEBUG: Building BUILDING_COW_HERD (578) gives bonus BONUS_COW (13).
12:41:29 DEBUG: Building BUILDING_PIG_HERD (579) gives bonus BONUS_PIG (17).

which means it is working as expected.

In the onBuildindBuilt function I have
Code:
def onBuildingBuilt(argsList):
    'Building Completed'
    pCity, iBuildingType = argsList
    game = gc.getGame()

    global gb_OptionBuildingResourcePossible, ga_iBuildingBonusRules_InfoTypeNum
    BugUtil.debug("SubdueAnimals - onBuildingBuilt - iBuildingType %d.", iBuildingType)
    
    if (gb_OptionBuildingResourcePossible == False):
        return
    
    for i in range(ga_iBuildingBonusRules_InfoTypeNum.__len__()):
        if (iBuildingType == ga_iBuildingBonusRules_InfoTypeNum[i][0]):
            pCity.changeFreeBonus (ga_iSubdueRules_InfoTypeNum[i][1], 1)  
            BugUtil.debug("SubdueAnimals - onBuildingBuilt - Building %d gives bonus %d.", ga_iBuildingBonusRules_InfoTypeNum[i][0],  ga_iBuildingBonusRules_InfoTypeNum[i][1])
            break

Which is producing the trace messages
Code:
12:41:49 DEBUG: SubdueAnimals - onBuildingBuilt - iBuildingType 564.
12:41:49 DEBUG: SubdueAnimals - onBuildingBuilt - Building 564 gives bonus 4.

Which means I have built a horse_herd building and should get an horse resource but I am not getting the resource.
 
I don't see the problem on first scan, but here's a free lesson in using dictionaries. A dictionary is like a list (array) where each element (key) maps to a value. It's like a dictionary where each word maps to its definition. They are great because looking up a value is very fast without requiring you to loop because each key is unique.

Here's the first part that sets up the dictionary. I have not changed the name of the main global variable you are creating.

Code:
	ga_iBuildingBonusRules_InfoTypeNum = {}
	if gb_OptionBuildingResourcePossible:
		as_BuildingBonusRules = {
			'BUILDING_ELEPHANT_HERD': 'BONUS_IVORY',
			'BUILDING_HORSE_HERD': 'BONUS_HORSE',
			'BUILDING_DEER_HERD': 'BONUS_DEER',
			'BUILDING_COW_HERD': 'BONUS_COW',
			'BUILDING_PIG_HERD': 'BONUS_PIG',
		}
		
		# Convert these into integers for speed when used in game
		for sBuilding, sBonus in as_BuildingBonusRules.iteritems():
			iBuilding = gc.getInfoTypeForString(sBuilding)
			iBonus = gc.getInfoTypeForString(sBonus)
			if (iBuilding >= 0 ) and (iBonus >= 0):
				BugUtil.debug("Building %s (%d) gives bonus %s (%d).", sBuilding,  iBuilding, sBonus, iBonus)
				ga_iBuildingBonusRules_InfoTypeNum{iBuilding} = iBonus
			else:
				BugUtil.error("Building %s (%d) or bonus %s (%d) is invalid.", sBuilding,  iBuilding, sBonus, iBonus)

While the code above doesn't look that different, the code that accesses it is simpler:

Code:
def onBuildingBuilt(argsList):
    'Building Completed'
    pCity, iBuildingType = argsList
    game = gc.getGame()

    BugUtil.debug("SubdueAnimals - onBuildingBuilt - iBuildingType %d.", iBuildingType)
    if gb_OptionBuildingResourcePossible and iBuildingType in ga_iBuildingBonusRules_InfoTypeNum:
        iBonus = ga_iBuildingBonusRules_InfoTypeNum[iBuildingType]
        iBefore = pCity.getFreeBonus(iBonus)
        pCity.changeFreeBonus(iBonus, 1)
        iAfter = pCity.getFreeBonus(iBonus)
        BugUtil.debug("SubdueAnimals - onBuildingBuilt - Building %d changed bonus %d from %d to %d.", iBuildingType,  iBonus, iBefore, iAfter)

I've also made a few other simplicications:

  • "if x == True" is the same as "if x".
  • You don't need to declare global variables if you aren't going to assign a new value to them.
Lastly, the new debug message prints the value before and after the change so you can see the effect. Hopefully this will give a clue.
 
I can't see where the ga_iSubdueRules_InfoTypeNum variable is defined. Shouldn't you be using ga_iBuildingBonusRules_InfoTypeNum with CyCity.changeFreeBonus() instead?
 
I don't see the problem on first scan, but here's a free lesson in using dictionaries. A dictionary is like a list (array) where each element (key) maps to a value. It's like a dictionary where each word maps to its definition. They are great because looking up a value is very fast without requiring you to loop because each key is unique.

Here's the first part that sets up the dictionary. I have not changed the name of the main global variable you are creating.

Code:
	ga_iBuildingBonusRules_InfoTypeNum = {}
	if gb_OptionBuildingResourcePossible:
		as_BuildingBonusRules = {
			'BUILDING_ELEPHANT_HERD': 'BONUS_IVORY',
			'BUILDING_HORSE_HERD': 'BONUS_HORSE',
			'BUILDING_DEER_HERD': 'BONUS_DEER',
			'BUILDING_COW_HERD': 'BONUS_COW',
			'BUILDING_PIG_HERD': 'BONUS_PIG',
		}
		
		# Convert these into integers for speed when used in game
		for sBuilding, sBonus in as_BuildingBonusRules.iteritems():
			iBuilding = gc.getInfoTypeForString(sBuilding)
			iBonus = gc.getInfoTypeForString(sBonus)
			if (iBuilding >= 0 ) and (iBonus >= 0):
				BugUtil.debug("Building %s (%d) gives bonus %s (%d).", sBuilding,  iBuilding, sBonus, iBonus)
				ga_iBuildingBonusRules_InfoTypeNum{iBuilding} = iBonus
			else:
				BugUtil.error("Building %s (%d) or bonus %s (%d) is invalid.", sBuilding,  iBuilding, sBonus, iBonus)

While the code above doesn't look that different, the code that accesses it is simpler:

Code:
def onBuildingBuilt(argsList):
    'Building Completed'
    pCity, iBuildingType = argsList
    game = gc.getGame()

    BugUtil.debug("SubdueAnimals - onBuildingBuilt - iBuildingType %d.", iBuildingType)
    if gb_OptionBuildingResourcePossible and iBuildingType in ga_iBuildingBonusRules_InfoTypeNum:
        iBonus = ga_iBuildingBonusRules_InfoTypeNum[iBuildingType]
        iBefore = pCity.getFreeBonus(iBonus)
        pCity.changeFreeBonus(iBonus, 1)
        iAfter = pCity.getFreeBonus(iBonus)
        BugUtil.debug("SubdueAnimals - onBuildingBuilt - Building %d changed bonus %d from %d to %d.", iBuildingType,  iBonus, iBefore, iAfter)

I've also made a few other simplicications:

  • "if x == True" is the same as "if x".
  • You don't need to declare global variables if you aren't going to assign a new value to them.
Lastly, the new debug message prints the value before and after the change so you can see the effect. Hopefully this will give a clue.

Thanks for that. I had been wondering if it was possible to do things that way but have never used dictionaries and do not yet understand them. I have 4 such arrays in the mod.

I can't see where the ga_iSubdueRules_InfoTypeNum variable is defined. Shouldn't you be using ga_iBuildingBonusRules_InfoTypeNum with CyCity.changeFreeBonus() instead?

yep, that is the answer, I cut and pasted from elsewhere. My usual seeing what I expect to see problem :(. Or in this case not quite enough pasting since I type a variable name once and then copy and paste it into the code because of my dyslexia and typing problem ;).
 
Have you enabled Python exceptions?

The code made me feel dyslexic too - is it your code or are you using someone else's?
 
Have you enabled Python exceptions?

The code made me feel dyslexic too - is it your code or are you using someone else's?

No it's all my code, I just like using variable whit meaningful names. I had duplicated a bit of code from before which was working and then went through replacing the names for the new bit of code and missed one. This was the last bit to do before I put the mod up for others to use. Now I need to change most of the arrays into dictionaries since much of the reason for the code is speed as well as function.
 
The variable names were a bit tough on me, but they would of course make that much more sense to you.

Since you're working with dictionaries for the first time (?) I thought this could be helpful.
 
Good catch. This is a good example of the usefulness of using temporary variables, most especially when using the same value in two places (to change the value and to debug what was changed). Any time you duplicate code--no matter how small--you invite error. And just like vampires, errors will gladly enter and wreak havoc once invited. ;)
 
Good catch.
I only noticed it when I had already given up and didn't try to find the problem anymore. :D I think its the variable names that were simply too intricate to register in my brain. So I had to shut it down first. :lol:
 
The variable names were a bit tough on me, but they would of course make that much more sense to you.

Since you're working with dictionaries for the first time (?) I thought this could be helpful.

I had been considering a "sparse array" as a better solution for this code for about a week. How was I to know that "they" had changed the name to "dictionary" ;). Although I do remember one of my colleagues going on about dictionaries back in the 80's. He was not good at explaining what they were. Another name that was used for such a structure was "recode table". Interestingly the book I bought on Python does not mention them at all.

Now to see if the second element can be an array that would be much more useful :).
 
OK, I have converted two arrays to dictionaries successfully but when I try and do the third and last I get a syntax error and after spending 3/4 of an hour trying to figure it out I am no closer to a solution.

Code:-
Code:
    as_SubdueRules = {
        'UNIT_LION': 'UNIT_SUBDUED_LION',
        'UNIT_LION_PACK':  'UNIT_SUBDUED_LION_PACK',
        'UNIT_CAVE_LION':  'UNIT_SUBDUED_CAVE_LION',
        'UNIT_BEAR': 'UNIT_SUBDUED_BEAR',
        'UNIT_POLARBEAR': 'UNIT_SUBDUED_POLARBEAR',
        'UNIT_CAVEBEAR': 'UNIT_SUBDUED_CAVEBEAR',
        'UNIT_WOLF':  'UNIT_SUBDUED_WOLF',
        'UNIT_DIREWOLF': 'UNIT_SUBDUED_DIREWOLF',
        'UNIT_BENGALTIGER': 'UNIT_SUBDUED_BENGALTIGER',
        'UNIT_SIBERIANTIGER':'UNIT_SUBDUED_SIBERIANTIGER',
        'UNIT_ELEPHANT': 'UNIT_SUBDUED_ELEPHANT',
        'UNIT_CHEETAH': 'UNIT_SUBDUED_CHEETAH',
        'UNIT_HORSE': 'UNIT_SUBDUED_HORSE',
        'UNIT_STAG_DEER': 'UNIT_SUBDUED_STAG_DEER',
        'UNIT_PANTHER': 'UNIT_SUBDUED_PANTHER',
        'UNIT_ANIMAL_JAGUAR': 'UNIT_SUBDUED_JAGUAR',
        'UNIT_GORILLA': 'UNIT_SUBDUED_GORILLA',
        'UNIT_BISON': 'UNIT_SUBDUED_BISON',
        'UNIT_BOAR': 'UNIT_SUBDUED_BOAR',
        'UNIT_PANDA_BEAR': 'UNIT_SUBDUED_PANDA_BEAR',
        'UNIT_HYENA': 'UNIT_SUBDUED_HYENA',
        'UNIT_KOMODO_DRAGON': 'UNIT_SUBDUED_KOMODO_DRAGON',
        'UNIT_MAMMOTH': 'UNIT_SUBDUED_MAMMOTH',
        'UNIT_ORANGUTAN': 'UNIT_SUBDUED_ORANGUTAN',
        'UNIT_RHINO': 'UNIT_SUBDUED_RHINO',
        'UNIT_SUBDUED_LION': 'UNIT_SUBDUED_LION',
        'UNIT_SUBDUED_LION_PACK': 'UNIT_SUBDUED_LION_PACK',
        'UNIT_SUBDUED_CAVE_LION': 'UNIT_SUBDUED_CAVE_LION',
        'UNIT_SUBDUED_BEAR': 'UNIT_SUBDUED_BEAR',
        'UNIT_SUBDUED_POLARBEAR': 'UNIT_SUBDUED_POLARBEAR',
        'UNIT_SUBDUED_CAVEBEAR': 'UNIT_SUBDUED_CAVEBEAR',
        'UNIT_SUBDUED_WOLF':  'UNIT_SUBDUED_WOLF',
        'UNIT_SUBDUED_DIREWOLF':  'UNIT_SUBDUED_DIREWOLF',
        'UNIT_SUBDUED_BENGALTIGER':  'UNIT_SUBDUED_BENGALTIGER',
        'UNIT_SUBDUED_SIBERIANTIGER': 'UNIT_SUBDUED_SIBERIANTIGER',
        'UNIT_SUBDUED_ELEPHANT': 'UNIT_SUBDUED_ELEPHANT',
        'UNIT_SUBDUED_CHEETAH': 'UNIT_SUBDUED_CHEETAH',
        'UNIT_SUBDUED_HORSE': 'UNIT_SUBDUED_HORSE',
        'UNIT_SUBDUED_STAG_DEER': 'UNIT_SUBDUED_STAG_DEER',
        'UNIT_SUBDUED_PANTHER': 'UNIT_SUBDUED_PANTHER',
        'UNIT_SUBDUED_JAGUAR': 'UNIT_SUBDUED_JAGUAR',
        'UNIT_SUBDUED_GORILLA': 'UNIT_SUBDUED_GORILLA',
        'UNIT_SUBDUED_BISON': 'UNIT_SUBDUED_BISON',
        'UNIT_SUBDUED_BOAR': 'UNIT_SUBDUED_BOAR',
        'UNIT_SUBDUED_PANDA_BEAR': 'UNIT_SUBDUED_PANDA_BEAR',
        'UNIT_SUBDUED_HYENA': 'UNIT_SUBDUED_HYENA',
        'UNIT_SUBDUED_KOMODO_DRAGON': 'UNIT_SUBDUED_KOMODO_DRAGON',
        'UNIT_SUBDUED_MAMMOTH': 'UNIT_SUBDUED_MAMMOTH',
        'UNIT_SUBDUED_ORANGUTAN': 'UNIT_SUBDUED_ORANGUTAN',
        'UNIT_SUBDUED_RHINO': 'UNIT_SUBDUED_RHINO'
        }
    
    # Convert these into integers for speed when used in game
    BugUtil.debug("Subdue Animals capture definitions:-")
    gai_SubdueRules = {}
    for sDefeatedUnit, sCapturedUnit in as_SubdueRules.iteritems():
        iDefeatedUnit = gc.getInfoTypeForString(sDefeatedUnit)
        iCapturedUnit = gc.getInfoTypeForString(sCapturedUnit)
        if (iDefeatedUnit >= 0 ) and (iCapturedUnit >= 0):
            BugUtil.debug("Capture %s (%d) as %s (%d).", sDefeatedUnit,  iDefeatedUnit, sCapturedUnit, iCapturedUnit)
            gai_SubdueRules{iDefeatedUnit} = iCapturedUnit
        else:
            BugUtil.warn("Capture %s (%d) as %s (%d).  Contains an invalid unit!", sDefeatedUnit,  iDefeatedUnit, sCapturedUnit, iCapturedUnit)

Error:-
Code:
Traceback (most recent call last):
  File "BugConfig", line 110, in unknown_endtag
  File "BugConfig", line 334, in endChild
  File "BugConfig", line 337, in end
  File "BugConfig", line 318, in process
  File "BugConfig", line 525, in handle
  File "BugUtil", line 643, in getFunction
  File "BugUtil", line 630, in lookupFunction
  File "BugUtil", line 622, in lookupModule
  File "<string>", line 35, in load_module
  File "<string>", line 13, in _get_code
  File "SubdueAnimals", line 158
 
    gai_SubdueRules{iDefeatedUnit} = iCapturedUnit
 
                   ^
 
SyntaxError: invalid syntax
 
You use [] to access elements in a dictionary, list, and tuple. You use {} to define a new dictionary. Yes, it's confusing, but just memorize that and you'll be okay. Single values go between the []s.

Code:
gai_SubdueRules[B][COLOR="Red"][[/COLOR][/B]iDefeatedUnit[COLOR="Red"][B]][/B][/COLOR] = iCapturedUnit

BTW, while a dictionary works similar to a sparse array, they are fundamentally different. A sparse array is just an array (list in Python) where many of the elements don't have values.

Code:
myList = [1, 2, 3, 4, 5]
mySparseArray = [None, None, 1, None, 2, None, None, None, 3, 4, None, 5]

The above isn't a "true" sparse array because elements 0, 1, 3, 5, 6, 7, and 10 have None as their value which is a specific value in Python. The key is that lists and arrays use integers as their indexes (the key you use to look up the value); dictionaries can have any object as their keys, and you can even mix them in the same dictionary:

Code:
myCrazyDict = {
        42: "answer",
        Point(3, 8): "top-left",
        gc.getPlayer(2): gc.getTeam(8),
        ["x", "y", 12, {}, "hi!"]: {1: 17, 9: 27, "hi": "bye"},
}

Each key in a dictionary must be unique. Said another way, each key can be mapped to a single value, though that "value" may be an object that itself contains many values such as another dict, a list, an object, whatever. This is exactly the same as a list.

Code:
myCrazyList = [
        42,
        Point(3, 8),
        gc.getPlayer(2),
        ["x", "y", 12, {}, "hi!"],
}

See here there are no colons :)) to denote mappings. Indexes in a list always start at zero and increase by one. In a "true" sparse array the indexes can be any integers with gaps between them. A sparse array is typically implemented using a dictionary, hash table or map (all essentially the same thing for this discussion).

I am extremely surprised a Python book could skip dictionaries since they are at the heart of Python--even before objects since objects are built using them!

Side note: I typically name my dictionaries "x by y" where "y" represents the keys and "x" the values: citiesByPlayer, animalsCapturedIdByKilledId, etc. It's short for saying, "this structure groups each captured animal by its killed animal ID". Kinda. ;) When naming variables and functions you typically want to name things by their intention (what they do or how they are used).
 
The key is that lists and arrays use integers as their indexes (the key you use to look up the value); dictionaries can have any object as their keys, and you can even mix them in the same dictionary:
Hmm... I've been under the impression that dictionary keys can't be mutable data types, like a list or a dictionary. But a string or a tuple is fine - them being immutable.

I can't seem to find anything to support my claim though, so it could just be confusion on my part. (Even if it kinda makes sense, doesn't it?)

I am extremely surprised a Python book could skip dictionaries since they are at the heart of Python--even before objects since objects are built using them!
I feel the same way, but since I've only ever looked in one Python textbook I didn't dare to make such a claim. But my textbook never did mention iteritems() so I've been doing without it. :rolleyes: Its actually the exact thing I've been missing all along! :p (The textbook I linked to earlier in this thread does also cover the data structures stacks, queues and threes. Dictionaries on the other hand were covered in the basics.)
 
Hmm... I've been under the impression that dictionary keys can't be mutable data types, like a list or a dictionary. But a string or a tuple is fine - them being immutable.

Yes, you're correct. I posted too carelessly. The keys cannot be mutable (able to change) because of how dictionaries are implemented in Python. There's nothing in the definition of the generic map data structure that requires this, but it's usually the case. If a key changes it probably needs to tell the map so it can be reindexed.

But my textbook never did mention iteritems() so I've been doing without it.

It works like items() without making a copy of the dictionary. As long as you aren't going to modify the dictionary during the loop, iteritems() is better because it's faster, especially for large dictionaries. Similarly you have itervalues() and iterkeys().

The textbook I linked to earlier in this thread does also cover the data structures stacks, queues and threes. Dictionaries on the other hand were covered in the basics.

Dictionaries and lists are built-in types in Python. Sets too as of 2.4. Stacks, queues, and trees have to be coded by you, though I'm sure there are billions of free data structure modules online. In fact, the list class includes methods that allow you to use lists as stacks and queues.
 
It works like items() without making a copy of the dictionary. As long as you aren't going to modify the dictionary during the loop, iteritems() is better because it's faster, especially for large dictionaries. Similarly you have itervalues() and iterkeys().
I've just been using a for-in loop to access the keys. Needless to say its tricky to fetch both keys and values this way without iteritems()!

Dictionaries and lists are built-in types in Python. Sets too as of 2.4. Stacks, queues, and trees have to be coded by you, though I'm sure there are billions of free data structure modules online.
This is why I would also be surprised not to have a built-in type covered in an book on Python. :crazyeye:

In fact, the list class includes methods that allow you to use lists as stacks and queues.
:lol: I find this hilarious! So they are more or less "built-in types" after all, since the list type already has all the tools to be handles as queues and stacks!

Sets I'm yet to look into, as they weren't mentioned in my textbook - at all... :p
 
Thank you both. I was getting {} [] and () mixed up but I think I have it straight now.

I have decided to go with arrays where I have to go through the whole list every time and for the first use where I am defining the rules using strings. Then when I convert the strings to integers, in the init function, I also convert the array to a dictionary. That way the bits in the event code uses dictionaries which is where speed is important.
 
This is why I would also be surprised not to have a built-in type covered in an book on Python. :crazyeye:

Exactly! If there are two built-in data structures, leaving one out is ridiculous. You've covered only 50%. :eek:

So they are more or less "built-in types" after all, since the list type already has all the tools to be handles as queues and stacks!

Yes and no. Yes, the functionality is there. No, they do not behave like strict stacks and queues. Part of a data structure is what features it supports, but just as important is what features it does not support. If you are using a list as a stack but accidentally insert a new item at the bottom of the stack, you've broken the stack contract (first in, last out). Tracking down a bug like this would be tedious at best.

So ideally a language that claims to implement both stacks and queues should enforce the proper API. If you really need one of them, wrap a list with the API you need. For example in BUG I use a priority queue to track the reminders and when they should trigger. You can place items into the queue (it puts them in the right spot) and take the "next" item out. That's it. No sneaking an item in at the end.

Sets I'm yet to look into, as they weren't mentioned in my textbook - at all... :p

Sets are very simple, like a dictionary without values--only unique keys. A set can hold any number of unique immutable items and supports but a few options: add(item), remove(item), and contains(item). They work just like mathematical sets, and Python provides helpful methods like union(), intersection(), difference(), etc. I use them a lot in Civ4lerts.

Many toolkits cheat and use a dictionary under the hood and store a common cheap value for each "key" in the set. Easy peasy.

Then when I convert the strings to integers, in the init function, I also convert the array to a dictionary.

Python allows some pretty quirky but terse shortcuts for this kind of stuff. It would be tough to explain here, but I'll just show ya so you can marvel at Python's brilliance. :)

Code:
as_SubdueRules = {
        'UNIT_LION': 'UNIT_SUBDUED_LION',
        'UNIT_LION_PACK':  'UNIT_SUBDUED_LION_PACK',
        'UNIT_CAVE_LION':  'UNIT_SUBDUED_CAVE_LION',
        'UNIT_BEAR': 'UNIT_SUBDUED_BEAR',
        'UNIT_POLARBEAR': 'UNIT_SUBDUED_POLARBEAR',
        'UNIT_CAVEBEAR': 'UNIT_SUBDUED_CAVEBEAR',
        ...
}

gai_SubdueRules = dict(
        (gc.getInfoTypeForString(sDefeated), gc.getInfoTypeForString(sCaptured))
        for sDefeated, sCaptured in as_SubdueRules.iteritems()
)

(Of course it does no error checking or logging)

The above is known as a generator expression in that it generates values on-the-fly for building the dictionary. A simple example is

Code:
firstTenEvenNumbers = [x * 2 for x in range(10)]

Python is pretty fun. :D
 
Python allows some pretty quirky but terse shortcuts for this kind of stuff. It would be tough to explain here, but I'll just show ya so you can marvel at Python's brilliance. :)

Code:
as_SubdueRules = {
        'UNIT_LION': 'UNIT_SUBDUED_LION',
        'UNIT_LION_PACK':  'UNIT_SUBDUED_LION_PACK',
        'UNIT_CAVE_LION':  'UNIT_SUBDUED_CAVE_LION',
        'UNIT_BEAR': 'UNIT_SUBDUED_BEAR',
        'UNIT_POLARBEAR': 'UNIT_SUBDUED_POLARBEAR',
        'UNIT_CAVEBEAR': 'UNIT_SUBDUED_CAVEBEAR',
        ...
}

gai_SubdueRules = dict(
        (gc.getInfoTypeForString(sDefeated), gc.getInfoTypeForString(sCaptured))
        for sDefeated, sCaptured in as_SubdueRules.iteritems()
)

(Of course it does no error checking or logging)

The above is known as a generator expression in that it generates values on-the-fly for building the dictionary. A simple example is

Code:
firstTenEvenNumbers = [x * 2 for x in range(10)]
:eek2: OMG! :eek:

Python is pretty fun. :D
It is! :goodjob: I'm yet to wrap my head around your examples, but this is exactly the sort of thing I like about programming.
 
Python allows some pretty quirky but terse shortcuts for this kind of stuff. It would be tough to explain here, but I'll just show ya so you can marvel at Python's brilliance. :)

Code:
as_SubdueRules = {
        'UNIT_LION': 'UNIT_SUBDUED_LION',
        'UNIT_LION_PACK':  'UNIT_SUBDUED_LION_PACK',
        'UNIT_CAVE_LION':  'UNIT_SUBDUED_CAVE_LION',
        'UNIT_BEAR': 'UNIT_SUBDUED_BEAR',
        'UNIT_POLARBEAR': 'UNIT_SUBDUED_POLARBEAR',
        'UNIT_CAVEBEAR': 'UNIT_SUBDUED_CAVEBEAR',
        ...
}

gai_SubdueRules = dict(
        (gc.getInfoTypeForString(sDefeated), gc.getInfoTypeForString(sCaptured))
        for sDefeated, sCaptured in as_SubdueRules.iteritems()
)

(Of course it does no error checking or logging)

If I could trust the data source I would use such a susinct bit of code, but I am dyslexic so don't trust my typing :) and I would like to release this so others can mod it fairly easily too.

Since starting this thread I have halved the size of the two biggest arrays and got rid of one altogether because I realised I could deduce the test of contents/rules. That is what these arrays represent, rules, which I am thinking I could expand into a "prisoner of war mod" but that's another story. ;)
 
And I wouldn't use the above code either; I just wanted to show off Python's awesome expressiveness. Logging and error-checking are too important in this context to be skipped. Let us know if there are other arrays you'd like to convert, or other parts you feel might be too complex. It's fun to have little problems like this to solve while slogging through the next BUG/BULL releases. :)
 
Top Bottom