best way to iterate adjacent plots?

davidlallen

Deity
Joined
Apr 28, 2008
Messages
4,743
Location
California
I have a common code segment I use in python to look at all the adjacent plots:
Code:
maxX = CyMap().getGridWidth() ; maxY = CyMap().getGridHeight()
curX = pPlot.getX() ; curY = pPlot.getY()
for iX in range (curX - 1, curX + 2):
	if (iX < 0) or (iX >= maxX): continue
	for iY in range (curY - 1, curY + 2):
		if (iY < 0) or (iY >= maxY): continue
		pPlot2 = CyMap().plot(iX, iY)
		... do additional stuff
Now I have a reason to also check if going from pPlot to pPlot2 crosses a river. There is a handy function, CyPlot.isRiverCrossing(DirectionTypes eDirection) which does what I want. However, I can't figure out how to modify my loop to use DirectionTypes. The sdk functions which go from one plot to another by DirectionType aren't exposed in any way I can understand.

Looking in the sdk code, there are functions like cyPlotDirection defined in CyGameCoreUtils.cpp, and there is this declaration in CyGameCoreUtilsInterface.cpp:
Code:
void CyGameCoreUtilsPythonInterface()
{
	OutputDebugString("Python Extension Module - CyGameCoreUtilsPythonInterface\n");
	...
	python::def("plotDirection", cyPlotDirection, python::return_value_policy<python::manage_new_object>(), "CyPlot* (int iX, int iY, DirectionTypes eDirection)");
But, if this counts as exposing the function to python, I do not understand how to use it.

Can anybody show example python code which uses DirectionTypes to go from one plot to another?
 
I have tried the following, and it doesn't work either. I declared local arrays lDx, lDy with values of 0,-1,+1 to correspond to the directions.
Code:
curX = pPlot.getX() ; curY = pPlot.getY()
for iDir in range (8):
	iX = curX + self.lDx[iDir] ; iY = curY + self.lDy[iDir]
	pPlot2 = CyMap().plot(iX, iY)
	if pPlot.isRiverCrossing(iDir) ...
Unfortunately,
Code:
ArgumentError: Python argument types in
    CyPlot.isRiverCrossing(CyPlot, int)
did not match C++ signature:
    isRiverCrossing(class CyPlot {lvalue}, enum DirectionTypes)
ERR: Python function onEvent failed, module CvEventInterface
I have to use an int to access my array, but I suppose the function is looking for DirectionTypes.DIRECTION_NORTH.

These local arrays can't be the best approach. But, to use the local arrays and also use this function, I need to cast between an int and an enum defined in C. I can't find the way to do this in python; any suggestions?
 
Did you try if pPlot.isRiverCrossing(DirectionTypes(iDir))?
 
I use this
Code:
				for i in range (DirectionTypes.NUM_DIRECTION_TYPES):
					pLoopPlot=pPlot
					if i==0:
						pLoopPlot=plotDirection(pPlot.getX(),pPlot.getY(),DirectionTypes.DIRECTION_NORTH)
					if i==1:
						pLoopPlot=plotDirection(pPlot.getX(),pPlot.getY(),DirectionTypes.DIRECTION_NORTHEAST)
					if i==2:
						pLoopPlot=plotDirection(pPlot.getX(),pPlot.getY(),DirectionTypes.DIRECTION_EAST)
					if i==3:
						pLoopPlot=plotDirection(pPlot.getX(),pPlot.getY(),DirectionTypes.DIRECTION_SOUTHEAST)
					if i==4:
						pLoopPlot=plotDirection(pPlot.getX(),pPlot.getY(),DirectionTypes.DIRECTION_SOUTH)
					if i==5:
						pLoopPlot=plotDirection(pPlot.getX(),pPlot.getY(),DirectionTypes.DIRECTION_SOUTHWEST)
					if i==6:
						pLoopPlot=plotDirection(pPlot.getX(),pPlot.getY(),DirectionTypes.DIRECTION_WEST)
					if i==7:
						pLoopPlot=plotDirection(pPlot.getX(),pPlot.getY(),DirectionTypes.DIRECTION_NORTHWEST)
 
@ sephi, I did not realize that plotDirection() was globally defined, but I can use this function at the python console prompt. So I guess no package import, or object reference is needed to use functions in CyGameCoreUtilsPythonInterface.

@ general tso, your casting suggestion works. Not sure why I wasn't able to find that; it's the opposite of C syntax and my python references didn't cover that.

So it seems that a loop like:
Code:
curX = pPlot.getX() ; curY = pPlot.getY()
for i in range (DirectionTypes.NUM_DIRECTION_TYPES):
	pLoopPlot=plotDirection(curX, curY, DirectionTypes(i))
Should work, and it will be very clean. I will try that tonight. Thanks!
 
In related news, I assume that plotDirection handles x/y wrapping correctly. But, what happens if you call plotDirection(something, 0, DirectionTypes.DIRECTION_NORTH), that is, you request a plot which is off the map? There does not appear to be any routine pPlot.isValid, and if I have to check the coordinates before I call, then I have to do all the work myself anyway.

How to check if plotDirection returns an invalid plot?
 
There is a CyPlot.isNone() that appears to be what you need. This is used to check the plot in various map scripts and in CvMapGeneratorUtil.py after calling plotDirection().
 
Yes, a CyPlot will be returned with a NULL CvPlot*, so use isNone() to check that it's valid.

BTW, DirectionTypes(i) is not casting; it's instantiating the DirectionTypes class with i as the value. All of the enums are simple integer-wrapping classes. If you're concerned about creating a bunch of those objects--say your looping code is in canMoveInto() or whatever that callback is called--create a global list to hold the values:

Code:
DIRECTIONS = (
    DirectionTypes.DIRECTION_NORTH,
    DirectionTypes.DIRECTION_NORTHEAST,
    DirectionTypes.DIRECTION_EAST,
    ...
    DirectionTypes.DIRECTION_NORTHWEST,
)

for d in DIRECTIONS:
    plot = plotDirection(x, y, d)
    if not plot.isNone():
        ...
 
Thanks for the details. If I call DirectionTypes(i) inside a loop as shown in my previous post, doesn't the allocated int go out of scope at the end of each loop iteration? Then the next loop iteration will allocate the same space over again, assuming garbage collection is efficient. So there is never more than one int allocated anyway.
 
Exactly. I only showed the example to illustrate more what's going on. Python uses reference counting so the created object will be deallocated at the end of each loop iteration. I say object because DirectionTypes and all the other enumerations are classes, and each value is an instance of that class. Yes, the object holds only an integer beyond what it normally holds, but that includes dictionaries for attributes, functions, etc. It's not much memory, but if this is called thousands of times per turn it could pay off to create those objects once up front.
 
Back
Top Bottom