views:

324

answers:

4

All of the questions that I've asked recently about Python have been for this project. I have realised that the reason I'm asking so many questions may not be because I'm so new to Python (but I know a good bit of PHP) and is probably not because Python has some inherent flaw.

Thus I will now say what the project is and what my current idea is and you can either tell me I'm doing it all wrong, that there's a few things I need to learn or that Python is simply not suited to dealing with this type of project and language XYZ would be better in this instance or even that there's some open source project I might want to get involved in.

The project
I run a free turn based strategy game (think the campaign mode from the total war series but with even more complexity and depth) and am creating a combat simulator for it (again, think total war as an idea of how it'd work). I'm in no way deluded enough to think that I'll ever make anything as good as the Total war games alone but I do think that I can automate a process that I currently do by hand.

What will it do
It will have to take into account a large range of variables for the units, equipment, training, weather, terrain and so on and so forth. I'm aware it's a big task and I plan to do it a piece at a time in my free time. I've zero budget but it's a hobby that I'm prepared to put time into (and have already).

My current stumbling block
In PHP everything can access everything else, "wrong" though some might consider this it's really really handy for this. If I have an array of equipment for use by the units, I can get hold of that array from anywhere. With Python I have to remake that array each time I import the relevant data file and this seems quite a silly solution for a language that from my experience is well thought out. I've put in a system of logging function calls and class creation (because I know from a very basic version of this that I did in PHP once that it'll help a lot down the line) and the way that I've kept the data in one place is to pass each of my classes an instance to my logging list, smells like a hack to me but it's the only way I've gotten it to work.

Thus I conclude I'm missing something and would very much appreciate the insight of anybody willing to give it. Thank you.

Code samples

This creates a list of formations, so far there's only one value (besides the name) but I anticipate adding more on which is why they're a list of classes rather than just a standard list. This is found within data.py

formations = []
formationsHash = []
def createFormations(logger):
 """This creates all the formations that will be used"""

 # Standard close quarter formation, maximum number of people per square metre
 formationsHash.append('Tight')
 formations.append(Formation(logger, 'Tight', tightness = 1))

 # Standard ranged combat formation, good people per square metre but not too cramped
 formationsHash.append('Loose')
 formations.append(Formation(logger, 'Loose', tightness = 0.5))

 # Standard skirmishing formation, very good for moving around terrain and avoiding missile fire
 formationsHash.append('Skirmish')
 formations.append(Formation(logger, 'Skirmish', tightness = 0.1))

 # Very unflexible but good for charges
 formationsHash.append('Arrowhead')
 formations.append(Formation(logger, 'Arrowhead', tightness = 1))


def getFormation(searchFor):
 """Returns the fomation object with this name"""
 indexValue = formationsHash.index(searchFor)
 return formations[indexValue]

I don't have a code sample of when I'd need to access it because I've not gotten as far as making it but I anticipate the code looking something like the following:

Python
tempFormation = data.getFormation(unit.formationType)
tempTerrain = data.getTerrain(unit.currentTerrain)
unit.attackDamage = unit.attackDamage * tempTerrain.tighnessBonus(tempFormation.tightness)

The unit contains an integer that links to the index/key of the relevant terrain, formation and whatnot in the master list. Temporary variables are used to make the 3rd line shorter but in the long run will possibly cause issues if I forget to get one and it's use a value from earlier which is then incorrect (that's where the logging comes in handy).

PHP
$unit->attackDamage *= $terrain[$unit->currentTerrain]->tighnessBonus($unit->currentTerrain)

The unit class contains the index (probably a string) of the relevant terrain it's on and the formation it's in.

Maybe this will show some massive flaw in my understanding of Python (6 months vs the 3 years of PHP).

+3  A: 

I have narrowed your issue down to:

With Python I have to remake that array each time I import the relevant data file

Well you have two choices really, the first and easiest is to keep the structure in memory. That way (just like PHP) you can in theory access it from "anywhere", you are slightly limited by namespacing, but that is for your own good. It would translate as "anywhere you would like to".

The second choice is to have some data abstraction (like a database, or data file as you have) which stores and you retrieve data from this. This may be better than the first choice, as you might have far too much data to fit in memory at once. Again the way of getting this data will be available "anywhere" just like PHP.

You can either pass these things directly to instances in an explicit way, or you can use module-level globals and import them into places where you need them, as you go on to say:

and the way that I've kept the data in one place is to pass each of my classes an instance to my logging list

I can assure you that this is not a hack. It's quite reasonable, depending on the use, eg a config object can be used in the same way, as you may want to test your application with simultaneous different configs. Logging might be better suited as a module-level global that is just imported and called, as you probably only ever want one way of logging, but again, it depends on your requirements.

I guess to sum up, you really are on the right track. Try not to give in to that "hackish" smell especially when using languages you are not altogether familiar with. A hack in one language might be the gold-standard in another. And of course, best of luck with your project - it sounds fun.

Ali A
Thanks for the positive comments :)
Teifion
@Ali A: "keep the structure in memory" - The way I think to do this is "data.py should implement a singleton. All other classes can and should access the only instance of this class" Am I wrong in saying that? Did u mean something else like in memory sqllite db or something?
JV
+5  A: 

With Python I have to remake that array each time I import the relevant data file

You're missing a subtle point of Python semantics here. When you import a module for a second time, you aren't re-executing the code in that module. The name is found in a list of all modules imported, and the same module is returned to you. So the second time you import your module, you'll get a reference to the same list (in Python, don't say array, say list).

You'll probably need to post specific code samples to get more help, it seems like there are a few Python misconceptions mixed into this, and once those are cleared up you'll have a simpler time.

Ned Batchelder
I wouldn't call it a "subtle" point. I'd call it "central".
S.Lott
+3  A: 

Please don't reinvent the wheel. Your formationsHash as a list of key values isn't helpful and it duplicates the features of a dictionary.

def createFormations(logger):
    """This creates all the formations that will be used"""
    formations = {}
    formations['Tight']= Formation(logger, 'Tight', tightness = 1)
    formations['Loose']= Formation(logger, 'Loose', tightness = 0.5)
    formations['Skirmish']= Formation(logger, 'Skirmish', tightness = 0.1)
    formations['Arrowhead']= Formation(logger, 'Arrowhead', tightness = 1)
    return formations

Note, you don't actually need getFormation, since it does you no good. You can simply use something like this.

formations = createFormations( whatever )
f= formations[name]
S.Lott
I thought a list would be a better idea, now I think about it again I'm not sure why I thought that.
Teifion
This reduces code and speeds up lookups. It's better in every way.
recursive
+1  A: 

"data.py creates an array (well, list), to use this list from another file I need to import data.py and remake said list."

I can't figure out what you're talking about. Seriously.

Here's a main program, which imports the data, and another module.

SomeMainProgram.py

import data
import someOtherModule

print data.formations['Arrowhead']
someOtherModule.function()

someOtherModule.py

import data
def function():
    print data.formations['Tight']

data.py

import theLoggerThing
class Formation( object ):
    pass # details omitted.
def createFormations( logger ):
    pass # details omitted
formations= createFormations( theLoggerThing.logger )

So the main program works like this.

  1. import data. The data module is imported.

    a. import theLoggerThing. Whatever this is.

    b. class Formation( object ):. Create a class Formations.

    c. def createFormations( logger ):. Create a function createFormations.

    d. formations =. Create an object, formations.

  2. import someOtherModule. The someOtherModule is imported.

    a. import data. Nothing happens. data is already available globally. This is a reference to what is -- effectively -- a Singleton. All Python modules are Singletons.

    b. def function. Create a function function.

  3. print data.formations['Arrowhead']. Evaluate data.formations, which is a dictionary object. Do a get('Arrowhead') on the dictionary which does some magical lookup and returns the object found there (an instance of Formation).

  4. someOtherModule.function().

    What happens during this function evaluation.

    a. print data.formations['Tight']. Evaluate data.formations, which is a dictionary object. Do a get('Tight') on the dictionary which does some magical lookup and returns the object found there (an instance of Formation).

S.Lott
It was the singleton bit I wasn't understanding. I thought that importing from a 2nd place would create a separate entity, not a link to the original.
Teifion
http://www.python.org/doc/2.5.2/ref/import.html - "find a module, and initialize it if necessary". The definition of how import works provides numerous helpful details.
S.Lott