Afforess
The White Wizard
It's likely that the civic yields are not properly being valued (overvalued, at first guess), causing this behavior. Thanks for the report Iceciro; I'll try to quash this in beta4.
Did you find the cause for the fort-craze, Afforess? In r181 you removed all traces of Forts Connect Improvements from CityAI so I guess it's not happening anymore but it really shouldn't have in the first place because of the obvious differences in yield.the Ottomans at some point really went fort-crazy
My only problem right now (and its probably a rather small problem) is that the AI still builds a settler right off the bat which can be quite problematic sometimes for them, especially with raging barbarians enabled but I digress.
And how exactly?I have already tackled this for the next beta.
Did you find the cause for the fort-craze, Afforess? In r181 you removed all traces of Forts Connect Improvements from CityAI so I guess it's not happening anymore but it really shouldn't have in the first place because of the obvious differences in yield.
And how exactly?
bool CvCityAI::AI_chooseUnit(UnitAITypes eUnitAI, int iOdds)
{
UnitTypes eBestUnit;
/************************************************************************************************/
/* Afforess Start 06/26/10 */
/* */
/* Avoid Making Settlers Very Early Game */
/************************************************************************************************/
if (eUnitAI == UNITAI_SETTLE)
{
if (isCapital() && getPopulation() == 1)
{
int iMinTurnsForSettler = getFoodTurnsLeft();
if (GC.getGameINLINE().isOption(GAMEOPTION_RAGING_BARBARIANS))
{
iMinTurnsForSettler *= 3;
iMinTurnsForSettler /= 2;
}
if (GC.getGameINLINE().isOption(GAMEOPTION_BARBARIAN_WORLD))
{
iMinTurnsForSettler *= 3;
iMinTurnsForSettler /= 2;
}
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_BARBARIANS))
{
iMinTurnsForSettler = -1;
}
if (GC.getGameINLINE().getElapsedGameTurns() < iMinTurnsForSettler)
{
return false;
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if (eUnitAI != NO_UNITAI)
{
eBestUnit = AI_bestUnitAI(eUnitAI);
}
else
{
eBestUnit = AI_bestUnit(false, NO_ADVISOR, &eUnitAI);
}
if (eBestUnit != NO_UNIT)
{
if( iOdds < 0 ||
getUnitProduction(eBestUnit) > 0 ||
GC.getGameINLINE().getSorenRandNum(100, "City AI choose unit") < iOdds )
{
pushOrder(ORDER_TRAIN, eBestUnit, eUnitAI, false, false, false);
return true;
}
}
return false;
}
Even with +1There is a civic in RoM that gives +1with Forts, which could have further caused issues as well...
You might be writing only for your own modmod but that doesn't mean nothing could be useful for Better AI.I don't think it would be incredibly beneficial in base BTS, as I write the AI strictly for RoM gameplay, not standard gameplay.
Even with +1the other improvement should have been far superior, so yield comparison should have resulted in a negative value. Combined with zero bonus from connecting an already connected resource, I am still confused about how this could happen. Unless +1
from civic is valued a dozen times higher than simply +1
.
Settlers being built on the first turn should have been fixed for Better BTS AI 1.0 though, I was sure jdog made it so the AI would build at least an escort first, even if the warrior unit is the only choice.. unless it already has escort units from difficulty level.
As for your code, I honestly don't understand it. getElapsedGameTurns() >= getFoodTurnsLeft() as settler building condition makes very little sense to me but I guess you did warn me
I would have used something like (getFoodTurnsLeft()*2 > getProductionTurnsLeft(eBestUnit, 0)) - Only build the settler if building it takes less than twice the time of waiting for city growth to 2 pop.
.. or getProductionTurnsLeft(eBestUnit, 0)/3 > getElapsedGameTurns()
bool CvCityAI::AI_chooseUnit(UnitAITypes eUnitAI, int iOdds)
{
UnitTypes eBestUnit;
if (eUnitAI != NO_UNITAI)
{
eBestUnit = AI_bestUnitAI(eUnitAI);
}
else
{
eBestUnit = AI_bestUnit(false, NO_ADVISOR, &eUnitAI);
}
if (eBestUnit != NO_UNIT)
{
/************************************************************************************************/
/* Afforess Start 06/27/10 */
/* */
/* Avoid Making Settlers Very Early Game */
/************************************************************************************************/
if (eUnitAI == UNITAI_SETTLE)
{
if (isCapital() && getPopulation() == 1)
{
int iMinTurnsForSettler = getFoodTurnsLeft();
iMinTurnsForSettler *= 3;
iMinTurnsForSettler /= 2;
if (GC.getGameINLINE().isOption(GAMEOPTION_RAGING_BARBARIANS))
{
iMinTurnsForSettler *= 3;
iMinTurnsForSettler /= 2;
}
if (GC.getGameINLINE().isOption(GAMEOPTION_BARBARIAN_WORLD))
{
iMinTurnsForSettler *= 3;
iMinTurnsForSettler /= 2;
}
if (GC.getGameINLINE().isOption(GAMEOPTION_NO_BARBARIANS))
{
iMinTurnsForSettler = -1;
}
if (getProductionTurnsLeft(eBestUnit, 0) > iMinTurnsForSettler)
{
return false;
}
}
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
if( iOdds < 0 ||
getUnitProduction(eBestUnit) > 0 ||
GC.getGameINLINE().getSorenRandNum(100, "City AI choose unit") < iOdds )
{
pushOrder(ORDER_TRAIN, eBestUnit, eUnitAI, false, false, false);
return true;
}
}
return false;
}
Code looks good now, should hinder the AI from building settlers in pop1 capitals almost all the time. You might need an exception for cases like capital in foodless environment (ice/dessert or plains/tundra hills), even if that probably never happens:
(if all city radius tiles are owned OR border popping takes longer than settler building) AND foodDifference() <= 0 then let the city build the settler, at least eventually (like when productionturns/3 > gameturns)
It might be easier to play a team game with an AI on your team, then you can watch an AI as the entire game progresses.Yes the worker AI isn't trivial but what improvement is built for a city is all in CvCityAI::AI_bestPlotBuild(), not even 900 lines of code.
Oh well, I guess I'll have to watch out if my AI opponents are building stupid forts too.. I hope not.
Specifying isDevelopingCity() question: what buildings get chosen there, that wouldn't get chosen without it? I was playing with the thought of stealing it but I wasn't exactly sure what you were trying to do with it.CityAI:
* isDevelopingCity(): does it do much good? ie is that a candidate for Better AI?
* check if building is prereq for units: do units add value even when player doesn't have the required techs? do they, if we can already build them because of civics or so? is that the same code FFH is using? Also value should probably scale to (unitCombatValue / our current best unitCombatValue), not just unitCombat. And a bool BuildingInfo::isPrereqForAnyUnit() would probably be a good idea too.
PlayerAI:
* foundValue: shouldn't bonus from getRiverBuildings() only go to CITY_HOME_PLOT, instead of every single LoopPlot?
if (pPlot->isRiver())
{
[COLOR="Green"]/************************************************************************************************/
/* BETTER_BTS_AI_MOD 02/03/09 jdog5000 */
/* */
/* Settler AI */
/************************************************************************************************/[/COLOR]
iValue += [B]300[/B];
[COLOR="Green"]/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/[/COLOR]
}
[COLOR="Green"]/************************************************************************************************/
/* Afforess Start 04/01/10 */
/* */
/* */
/************************************************************************************************/[/COLOR]
if (pPlot->isFreshWater() [B]&& pPlot->getFeatureType() != NO_FEATURE && GC.getFeatureInfo(pPlot->getFeatureType()).getHealthPercent() >= 0[/B])
{
iValue += 40;
iValue += (GC.getDefineINT("FRESH_WATER_HEALTH_CHANGE") * 30);
}
[COLOR="Green"]/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/[/COLOR]
if (pLoopPlot->isRiver())
{
[COLOR="Green"]/************************************************************************************************/
/* Afforess Start 06/08/10 */
/* */
/* */
/************************************************************************************************/[/COLOR]
iTempValue += [B]100 * GC.getGameINLINE().getRiverBuildings()[/B];
[COLOR="Green"]/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/[/COLOR]
}
More questions about your AI code:
(note that I simply took some notes while looking through your code, so it's not very verbose but I hope you still know what I'm talking about)
Specifying isDevelopingCity() question: what buildings get chosen there, that wouldn't get chosen without it? I was playing with the thought of stealing it but I wasn't exactly sure what you were trying to do with it.
AI_foundValue: I believe you are adding a lot of value to secondary river plots when you actually just wanted to give that bonus if the city itself would be next to a river.
Spoiler :The part I understand: city next to river is nice (changed value from Better AI's 60 to 300):
Right afterwards a part I do not comprehend: Why would you encourage settling on forests?Code:if (pPlot->isRiver()) { [COLOR="Green"]/************************************************************************************************/ /* BETTER_BTS_AI_MOD 02/03/09 jdog5000 */ /* */ /* Settler AI */ /************************************************************************************************/[/COLOR] iValue += [B]300[/B]; [COLOR="Green"]/************************************************************************************************/ /* BETTER_BTS_AI_MOD END */ /************************************************************************************************/[/COLOR] }
Code:[COLOR="Green"]/************************************************************************************************/ /* Afforess Start 04/01/10 */ /* */ /* */ /************************************************************************************************/[/COLOR] if (pPlot->isFreshWater() [B]&& pPlot->getFeatureType() != NO_FEATURE && GC.getFeatureInfo(pPlot->getFeatureType()).getHealthPercent() >= 0[/B]) { iValue += 40; iValue += (GC.getDefineINT("FRESH_WATER_HEALTH_CHANGE") * 30); } [COLOR="Green"]/************************************************************************************************/ /* Afforess END */ /************************************************************************************************/[/COLOR]
And the part where I you probably went too far (BTS "+10" became AND "+100*GC.getGameINLINE().getRiverBuildings()"):
Code:if (pLoopPlot->isRiver()) { [COLOR="Green"]/************************************************************************************************/ /* Afforess Start 06/08/10 */ /* */ /* */ /************************************************************************************************/[/COLOR] iTempValue += [B]100 * GC.getGameINLINE().getRiverBuildings()[/B]; [COLOR="Green"]/************************************************************************************************/ /* Afforess END */ /************************************************************************************************/[/COLOR] }
I still don't get what that code is supposed to do though. Is it supposed to make settling on top of jungles less attractive? Maybe what you meant wasIt's not so much an encouragement for forests as a way to ensure the AI don't overvalue jungles. In RoM, jungles provide fresh water too, so the BTS code made them jungle-happy with city founding.
[COLOR="Green"]//no extra value if we are settling on top of a feature with negative health%[/COLOR]
if (pPlot->isFreshWater() && [B](pPlot->getFeatureType() == NO_FEATURE || ([/B]pPlot->getFeatureType() != NO_FEATURE && GC.getFeatureInfo(pPlot->getFeatureType()).getHealthPercent() >= 0[B]))[/B])
In BTS, you can only build the river buildings if your city itself is next to a river. If that is true for RoM too, then the code should be:As for the river code, there are about 10 buildings in RoM that require rivers, and they all very powerful and make settling on rivers very significant, much more so than in BTS. I don't think it's overvalued, if anything, it may still be too small.
if (pLoopPlot->isRiver())
{
[COLOR="Green"]/************************************************************************************************/
/* Afforess Start 06/08/10 */
/* */
/* */
/************************************************************************************************/[/COLOR]
[B]if (pPlot->isRiver())[/B]
[B]{[/B]
iTempValue += 100 * GC.getGameINLINE().getRiverBuildings();
[B]}
else
{
iTempValue += 10
}[/B]
[COLOR="Green"]/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/[/COLOR]
}
I still don't get what that code is supposed to do though. Is it supposed to make settling on top of jungles less attractive? Maybe what you meant was
If that's not it I will just have to accept that commenting on a mod I never played is not such a good ideaCode:[COLOR="Green"]//no extra value if we are settling on top of a feature with negative health%[/COLOR] if (pPlot->isFreshWater() && [B](pPlot->getFeatureType() == NO_FEATURE || ([/B]pPlot->getFeatureType() != NO_FEATURE && GC.getFeatureInfo(pPlot->getFeatureType()).getHealthPercent() >= 0[B]))[/B])
![]()
In BTS, you can only build the river buildings if your city itself is next to a river. If that is true for RoM too, then the code should be:
Code:if (pLoopPlot->isRiver()) { [COLOR="Green"]/************************************************************************************************/ /* Afforess Start 06/08/10 */ /* */ /* */ /************************************************************************************************/[/COLOR] [B]if (pPlot->isRiver())[/B] [B]{[/B] iTempValue += 100 * GC.getGameINLINE().getRiverBuildings(); [B]} else { iTempValue += 10 }[/B] [COLOR="Green"]/************************************************************************************************/ /* Afforess END */ /************************************************************************************************/[/COLOR] }
if (pLoopPlot->isRiver())
{
/************************************************************************************************/
/* Afforess Start 07/21/10 */
/* */
/* Favor extra river tiles for eventual building yields, if we are on a river */
/************************************************************************************************/
if (pPlot->isRiver())
{
iTempValue += 10 * GC.getGameINLINE().getRiverBuildings();
}
else
{
iTempValue += 10;
}
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
if (pPlot->isHills())
{
iValue += 200;
}
if (pPlot->isRiver())
{
/************************************************************************************************/
/* Afforess Start 07/21/10 */
/* */
/* */
/************************************************************************************************/
iValue += 100 * GC.getGameINLINE().getRiverBuildings();
/************************************************************************************************/
/* Afforess END */
/************************************************************************************************/
}
Ok, now I am completely confused. River is no feature, and that should be the main source of fresh water. Also you aren't checking the source of the fresh water, you are merely checking what kind of feature there is on the city plot.Wow, it seems that I neglected the fact that inland lakes give fresh water too, I was thinking fresh water was only from features. Nice find.
if (pLoopPlot != NULL)
{
if (pLoopPlot->isLake())
{
return true;
}
if (pLoopPlot->getFeatureType() != NO_FEATURE)
{
if (GC.getFeatureInfo(pLoopPlot->getFeatureType()).isAddsFreshWater() [B]&& (!bIgnoreJungle || GC.getFeatureInfo(pPlot->getFeatureType()).getHealthPercent() >= 0)[/B])
{
return true;
}
}
if (pPlot->isFreshWater([B]true[/B])
It would only ignore Oasis if Oasis had health% < 0. Which wouldn't really make sense..That would ignore Oasis's too; but I see your point.
Actually it was just too long ago so I didn't remember it all.self said:I didn't play-test but I think I still figured it out. You were right, it must be the +1, I have no other explanation for it. (bla)
Wow, it seems that I neglected the fact that inland lakes give fresh water too, I was thinking fresh water was only from features. Nice find.