def findBestChokepoint(self, iPlayer=-1, bCheckForVisibility=false):
"""Returns a tuple of the best chokepoint plot and its plot value."""
pBestPlot = -1
iBestValue = -1
pPlayer = -1
iTeam = -1
if (iPlayer >= 0):
pPlayer = gc.getPlayer(iPlayer)
iTeam = pPlayer.getTeam()
iFeatNebula = gc.getInfoTypeForString('FEATURE_ICE')
iFeatAsteroid = gc.getInfoTypeForString('FEATURE_FOREST')
iMaxRange = max(CyMap().getGridWidth() / 2, 60)
printd("findBestChokepoint iMaxRange = %d" % (iMaxRange))
for iPlotLoop in range(CyMap().numPlots()):
pLoopPlot = CyMap().plotByIndex(iPlotLoop)
# If we're supposed to be checking for a player's visibility then only check this plot if it's revealed
if (bCheckForVisibility):
if (not pLoopPlot.isRevealed(iTeam, false)):
continue
# CP - Check the plot being rated to see if it already belongs to someone else.
iPlotOwner = pLoopPlot.getOwner()
if ((iPlotOwner != -1) and (iPlotOwner != iPlayer)):
continue
# Don't build anywhere except in empty space & asteroids
if (pLoopPlot.getFeatureType() != -1 and pLoopPlot.getFeatureType() != iFeatAsteroid):
continue
iDistanceFromCapital = CyMap().getGridWidth()
if (pPlayer.getCapitalCity()):
iDistanceFromCapital = CyMap().calculatePathDistance(pPlayer.getCapitalCity().plot(), pLoopPlot)
# Don't look too far away (performance, more than anything)
if (iDistanceFromCapital > 0 and iDistanceFromCapital < iMaxRange):
if iDistanceFromCapital < 4 : # Discourage it from building sensor stations right next to the capital
iDistanceValueMod = -9 # it will also get a penalty down below for being close to a star system if it is within 2
else : # Highest distance scores in the zone from 1/6 iMaxRange to 2/3 iMaxRange, in this zone iDistanceValueMod will be iMaxRange/6
iDistanceValueMod = ((2 * min( iDistanceFromCapital, iMaxRange/6)) - max( iDistanceFromCapital - (2 * iMaxRange / 3), 0)) / 2
iPlotValue = 0
iNumNebula = 0
iNumAdjacentNebula = 0
iNumAsteroid = 0
iNumDamaging = 0
for iXSearchLoop in range(pLoopPlot.getX()-2, pLoopPlot.getX()+3):
for iYSearchLoop in range(pLoopPlot.getY()-2, pLoopPlot.getY()+3):
# If the map does not wrap and the plot is not on the map give a small penalty and skip to the next.
# Note that if the plot is off the map then all plots in that row and/or column are off too
# so it will actually be at least 5 plots that give this penalty.
if not CyMap().isPlot(iXSearchLoop, iYSearchLoop):
iPlotValue -= 3
continue
pSearchPlot = CyMap().plot(iXSearchLoop, iYSearchLoop)
# Don't search unseen plots in range of the one we're looking at either
if (bCheckForVisibility):
if (not pSearchPlot.isRevealed(iTeam, false)):
continue
#Build sensor stations near chokepoints -- TC01
iFeature = pSearchPlot.getFeatureType()
if iFeature == iFeatNebula:
iNumNebula += 1
if (abs(iXSearchLoop - pLoopPlot.getX()) <= 1) and (abs(iYSearchLoop - pLoopPlot.getY()) <=1):
iNumAdjacentNebula += 1
elif iFeature == iFeatAsteroid:
iNumAsteroid +=1
elif (iFeature != -1) and (gc.getFeatureInfo(iFeature).getTurnDamage() > 0): # bug fix - make sure there is a feature before trying to get the info for it, taking advantage of the short-circuit conditional evaluation
iNumDamaging += 1
elif iFeature == gc.getInfoTypeForString('FEATURE_SOLAR_SYSTEM'):
iPlotValue -= 22 # reduce value a lot if near a star system
#If other stations are present, no build -- TC01
for iUnit in range(pSearchPlot.getNumUnits()):
pOtherStarbase = pSearchPlot.getUnit(iUnit)
if pOtherStarbase.isStarbase():
# iPlotValue = 0
iPlotValue -= 99
break
# Some nebula is a good indication of a choke point.
# Too much is an indication that we are in a box canyon.
# If there are 7 or more adjacent nebula plots, then this is a bad location. Otherwise:
# As a guess, make it increase the value for more up to a max value at 13, then decrease fairly rapidly.
# Give a score of 0 for 0, increaseing by 3 per nebula up to a score of 39 at 13 through 15,
# then decreasing by 5 per nebula over 15.
# This is -1 at 23, -6 at 24 and -11 at 25 (which is not a valid location anyway; neither is one
# with 23 or 24 since it is unreachable from the capital so the iDistanceFromCapital condition
# rules it out before we get here).
# Additionally, if there are more than 4 (i.e. 5 or 6) immediately adjacent nebula plots, give a
# small penalty of -2.
if iNumAdjacentNebula > 6 :
iPlotValue -= 99
else:
iPlotValue += ( 3 * min( iNumNebula, 13)) - ( 5 * max( iNumNebula - 15, 0))
if iNumAdjacentNebula > 4 :
iPlotValue -= 2
# A few asteroids are OK, but they block the visibility (and visibility is the whole point of a sensor station)
# With 0 no change, then +5 for 1-3 (which is the max bonus), then -1 for each over 3.
# Note that there is still a bonus for being on top of asteroids given later.
iPlotValue += ( 5 * min( iNumAsteroid, 1)) - max( iNumAsteroid - 3, 0)
# Damaging features are good, but too many is not as good since the area will tend to be avoided and
# it is probably between two black holes/supernovas (which is a good chokepoint, but bad for the visibility
# aspect since looking at a lot of such plots is rather pointless).
# Give +2 per, up to a max of +30 at 15, then -1 per damaging feature over 15
iPlotValue += ( 2 * min( iNumDamaging, 15)) - max( iNumDamaging - 15, 0)
iPlotValue += iDistanceValueMod
# Little extra bonus for being in Asteroids (defense)
if (pLoopPlot.getFeatureType() == iFeatAsteroid):
iPlotValue += 4 #How small should it be?
# If this plot has the most resources in range from what we've found
if (iPlotValue > iBestValue):
iBestValue = iPlotValue
pBestPlot = pLoopPlot
printd("plot %d (%d,%d) value = %d (distance=%d (for %d), NumNebula=%d (adjacent=%d), NumAsteroid=%d, NumDamaging=%d)" %
(CyMap().plotNum(pLoopPlot.getX(), pLoopPlot.getY()), pLoopPlot.getX(), pLoopPlot.getY(),
iPlotValue, iDistanceFromCapital, iDistanceValueMod, iNumNebula, iNumAdjacentNebula, iNumAsteroid, iNumDamaging))
printd("* best plot = %d (%d,%d), value = %d" % (CyMap().plotNum(pBestPlot.getX(), pBestPlot.getY()), pBestPlot.getX(), pBestPlot.getY(), iBestValue))
return [pBestPlot, iBestValue]