Python changeFreeBonus problem

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.
I probably wouldn't be able to resist the urge to include something... that elegant into my code. :mischief: But I'm yet to find a use for this myself.

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. :)
I concur, although I only have a fraction of the programming experience of any of you. :p Coding on my own main project can feel like a lot of work and frequenting these forums result in all manner of fun programming challenges! :D
 
II concur, although I only have a fraction of the programming experience of any of you. :p Coding on my own main project can feel like a lot of work and frequenting these forums result in all manner of fun programming challenges! :D

I have always found I program best when I can bounce ideas and knowledge off others.
 
I probably wouldn't be able to resist the urge to include something... that elegant into my code. :mischief: But I'm yet to find a use for this myself.
And as expected it didn't take long for me to find good uses for generator expressions. :D Sweet.
 
I use generator functions in PlayerUtil.py and TradeUtil.py for providing a function that works well in a for loop.

Code:
for player in PlayerUtil.players(alive=True, barbarian=False):
    ...

for deal in TradeUtil.tradePartners(ePlayer):
    ...

It's not that much different from having those functions return a list containing all of the objects to iterate over, but it was more fun to write the code. ;)
 
It's not that much different from having those functions return a list containing all of the objects to iterate over, but it was more fun to write the code. ;)
But what are the actual functions? :confused:

Please explain all of this in detail. Now, please. :p

I'm thinking: "Fun, fun, fun!" :D
 
Check out PlayerUtil.py, specifically the players() function starting on line 203.

Heh, I was such a Python noob back then. Looking at players() and teams() they could have been made more generic with some trickery: create a separate generator function that took the count function (getMAX_CIV_PLAYERS) and the getter function (getPlayer), and then loop over it yielding whatever it yielded. At least I put the code that checks that the player/team is desired into a separate function. :)
 
Looking at players() and teams() they could have been made more generic with some trickery: create a separate generator function that took the count function (getMAX_CIV_PLAYERS) and the getter function (getPlayer), and then loop over it yielding whatever it yielded.
How would you have done it now? If its not too much to ask?
 
Code:
for ePlayer in range(gc.getMAX_PLAYERS()):
    player = gc.getPlayer(ePlayer)
    if not player.isNone() and player.isEverAlive():
        if matchPlayerOrTeam(player, alive, human, barbarian, minor, active):
            yield player
What does the yield command do? :confused: I can follow the code all the way to this last line. What does the function return? How is it a "generator function"?

edit: Found it. I'm clearly not spending enough time on studying this stuff. Because the documentation is right there. :rolleyes:
 
No worries, it's very different from normal functions yet clearly works in a similar way. We are used to thinking of functions as executing some logic and returning a result (one or more values at the same time). Generator functions actually create a temporary generator object that yields one or more values in a loop.

When the function is entered, a new generator object is created to contain all of the parameters and any local variables created. Each time yield is used, a value is return to the caller, but the object remains. The looping constructs in Python are written to use generator objects. You could use them yourself without a loop, but there'd be no real point normally. Of course there's always some case where every feature would be useful; let's just say it would be rare. :)

Rewriting the players() and teams() functions to use a new third function that did the real work would look like this:

Code:
def players(alive=None, human=None, barbarian=None, minor=None, active=None):
    for player in playersOrTeams(gc.getMAX_PLAYERS(), gc.getPlayer, alive, human, barbarian, minor, active):
        yield player

def teams(alive=None, human=None, barbarian=None, minor=None, active=None):
    for team in playersOrTeams(gc.getMAX_TEAMS(), gc.getTeam, alive, human, barbarian, minor, active):
        yield team

def playersOrTeams(count, accessor, alive, human, barbarian, minor, active):
    for id in range(count):
        playerOrTeam = accessor(id)
        if not playerOrTeam.isNone() and playerOrTeam.isEverAlive():
            if matchPlayerOrTeam(playerOrTeam, alive, human, barbarian, minor, active):
                yield playerOrTeam

I'm not sure the "savings" in typing and genericity is worth the increased complexity, but I would probably lean toward this approach now. Code duplication is an invitation to error so it's almost always a good idea to eliminate it. It's not worth it if the increased complexity invites more error. ;)

Looking at the code again, there is probably a way to have the top two functions return a new generator function themselves instead of looping over the actual playersOrTeams() generator function in the same way you can write a function to return a new function. I haven't done it, so I don't recall the syntax, but I'm pretty sure it's doable.
 
Oh, sweet baby Jesus! :eek2:

My next programming project will be so cool.

One thing that has been disturbing me with my current project is that I have like 30 instances of basically this code:
Code:
for tCoords in pContext.plotList:
    pPlot = gc.getMap().plot(tCoords[0], tCoords[1])
I've now come to suspect that while I wasn't able to get rid of repeating this code with a generator expression, a generator function might be just what I need?
Code:
def coords(lPlotList):
    for tCoords in lPlotList:
        yield gc.getMap().plot(tCoords[0], tCoords[1])
And then I can just use:
Code:
for pPlot in coords(pContext.plotList):
Oh, man. This is so great!
 
Even better, you can save one call to the SDK for every coordinate by saving the CyMap in the generator:

Code:
def coords(lPlotList):
    map = CyMap()    # or gc.getMap() if you prefer
    for tCoords in lPlotList:
        yield map.plot(tCoords[0], tCoords[1])

I would probably call that functions plots() since it takes a list of coordinates but returns plots. You might combine those into plotsForCoords(), but I think it's pretty clear just as plots().

Here's another trick, though it's hardly exciting in this context. You can use * to expand any tuple or list into arguments to a function call:

Code:
yield map.plot(*tCoords)

This works only because the order is the same in this context. In fact, I would probably rewrite it like so:

Code:
def plots(coords):
    map = CyMap()
    for x, y in coords:
        yield map.plot(x, y)

To clarify what's happening in case it's not obvious, the above is a shortening of

Code:
def plots(coords):
    map = CyMap()
    for tCoord in coords:
        x, y = tCoord
        yield map.plot(x, y)

which you've no doubt seen when dealing with an argsList in an event handler.
 
Yeah, all good stuff. I actually dumbed my own code down as I have numerous constants and helper functions in my application at this point. What it would look like in reality:
Code:
def plotsFromCoords(lPlotList):
    for tCoords in lPlotList:
        yield getPlot(tCoords)
But I guess I could update getPlot():
Code:
def getPlot(tCoords):
        if isValid(tCoords):
                return Map.plot(*tCoords)
And I do think this is exiting!
 
Wow, you just answered another of my questions before I even got to ask it. I too think I have a place for the generator function and it is to do with the map also. I just have not been confident enough yo use it yet. After spending a day trying to figure out why my index wasn't an integer. (On one of my for iplot,y in desertPlotList... I had dropped the y and was getting the (iplot, y) as the iPlot - duh').
 
Top Bottom