Hi all,
I've recently returned to this genius mod ROM:AND and started a new game.
As usually i am playing at deity difficulty with one city on giant map with turned off tech diffusion and without a realistic timescale , and this means that I am trying to get as much as possible from each possibility. And still far away from AI - for example now I just've researched ship building and built my first galley and some AI's are already close to space ( this is due to another bug , anyway, I'll create for this separate post ).
And apparently, as I see I can get a huge naval army and a lot of technologies with just one galley.
For example, I've started trading with De Gaulle ( DG in this post).
First I traded my Galley with DG on his Galleon - it's already kind of joke as Galley is much cheaper and worse than Galleon.
Then I traded this Galleon with his Galleon and Flute - so, now I have Galleon and a Flute.
Then I continued to trade like this until I got from from him all his navy - 3 galleons, galleas and flute.
Then I started getting Technoligies. I traded several ships on a Scripture ( with a very profitable exchange rate, like 1 hammer = 6 bulbs or something) , and then returned Galleons back with the same technic
Then I got Sculpture and Ethics with the same way - and returned the fleet again.
Continuing this I could probably get all navy, all siege units and all technologies from all civilizations just in several moves.
So, this is definitely broken, and probably requires some investigation.
What do you think? I started to look at this function for calculating the trade value of the unit - why it is so inconsistent.
First I check A new dawn.log, just beginning of a trading process:
So, we already see that AI Unit Value for Galley and Galleon is the same - 462.
But later it changed for Galley even to 607 and all this values continue changed in an almost randomly looking at first sight way. I have taken logs from this just one trade window , got all distinct values, ordered records and got this:
So, during the same trade window AI estimate for Galleass, for example, was floating from 66 to 303.
For Galleon - from 462 to 1052.
For Galley - from 462 to 607 ( ridiculously high, because apparently you can get a tech with 452 bulbs just on galley).
And how it got in trades Great General ( this was never on the table ) I even have no idea, and no idea why it's cost is negative.
Now we are looking into a code.
For this kind of units it calculates like this ( CvPlayerAI::AI_militaryUnitTradeVal ) :
So, we take getProductionCost of the best unit and adjust it on the coefficient depending of ratio between unitAIValue of this unit and unitAIValue of the best buildable unit of that domain. And later normalize this based on our speed and multiply by 2 * 3.3 = 6.6 as train percent on normal speed is 330.
This sounds reasonable on a first sight. So, if some unit is really outdated, we don't value it too much - e.q. Galleys and the era of Galleons should not cost probably even their hammers
But it is wrong and I'll show - why.
For example, in our case. Fluyt, Galleon, Galley, Galleas all have domain DOMAIN_SEA.
Their possible AI's are: UNITAI_ASSAULT_SEA. ESCORT_SEA, SETTLER_SEA, MISSIONARY_SEA, SPY_SEA.
ATTACK_SEA, RESERVE_SEA, EXPLORE_SEA, ASSAULT_SEA.
Galley's cost is 25, Galleas - 65, Fluyt - 85, Galleon - 130.
Combat values are: 3, 10, 10, 16
Moves are: 2 , 2, 3, 4.
Cargo for them are: 2, 0, 3, 4.
Now I am checking function AI_unitValue - it is really big and complicated. But if we will read only the lines that are applicable to our case we see. First we add some value for AI preference.
Later we add into this values based on units parameters:
So, first we need to know combat value for the unit. This is the function:
So, basically it is just combat value divided on getBestLandUnitCombat(). What is interested about this function, that it takes into account all units from all players in the game ( and not only land units, actually...). So, one person that will get some strong unit dramatically decrease the combat strength estimate of all players even if they have no idea of that unit or that player. It would be hard for me to know it, but fortunately there is interface for it in cygame, so by console command I've got it and it is 50 in the current game.
So, for Galley we will get: 1 + 200/50/2 + 400 + 600 = 1003 ( so, combat value as you see - doesn't matter at all under this conditions! )
Galleass: 1 + 1000/50/2 + 400 + 0 = 411 ( and now Galley is twice as valuable as Galleas because of ignoring combat value... )
Fluyt: 1 + 1000/50/2 + 600 + 600 = 1211
Galleon: 1 + 1600/50/2 + 800 + 600 = 1417.
I have checked this with console:
v_area = gc.getPlayer(12).getCapitalCity().area()
gc.getPlayer(12).AI_unitValue(194, 28, v_area)
1004
gc.getPlayer(12).AI_unitValue(201, 28, v_area)
1417
Where unit 194 is Galley and 201 is Galleon.
Now, I trade propose DG to trade Galley to Galleon and I get this:
How game engine converts now ( 1004, 1417) into ( 904, 462 ) I have no idea.
It has no sense at all. The final value should be something close to the construction values - 25 and 120.
And after the trade if I try to trade Galleon with Galleass I get this:
For now I have to do some pause as I need some sleep. I can not debug this as I don't have Visual Studio 2008, and I am out of fantasy what can I check in Python.
But I think that using complicated function AI_unitValue was a very bad idea , with simple getProductionCost you would get much more stable and fair estimate. And this AI_unitValue with all it's random numbers like:
add 200 for movement, add 300 for cargo space, add 1 for an improvement etc. looking like giving mostly random number without any sense.
I've recently returned to this genius mod ROM:AND and started a new game.
As usually i am playing at deity difficulty with one city on giant map with turned off tech diffusion and without a realistic timescale , and this means that I am trying to get as much as possible from each possibility. And still far away from AI - for example now I just've researched ship building and built my first galley and some AI's are already close to space ( this is due to another bug , anyway, I'll create for this separate post ).
And apparently, as I see I can get a huge naval army and a lot of technologies with just one galley.
For example, I've started trading with De Gaulle ( DG in this post).
First I traded my Galley with DG on his Galleon - it's already kind of joke as Galley is much cheaper and worse than Galleon.
Then I traded this Galleon with his Galleon and Flute - so, now I have Galleon and a Flute.
Then I continued to trade like this until I got from from him all his navy - 3 galleons, galleas and flute.
Then I started getting Technoligies. I traded several ships on a Scripture ( with a very profitable exchange rate, like 1 hammer = 6 bulbs or something) , and then returned Galleons back with the same technic
Then I got Sculpture and Ethics with the same way - and returned the fleet again.
Continuing this I could probably get all navy, all siege units and all technologies from all civilizations just in several moves.
So, this is definitely broken, and probably requires some investigation.
What do you think? I started to look at this function for calculating the trade value of the unit - why it is so inconsistent.
First I check A new dawn.log, just beginning of a trading process:
[194507.734] AI Unit Value For Galley is 462
[194544.406] AI Unit Value For Galleon is 462
[194546.781] AI Unit Value For Galley is 607
[194546.781] AI Unit Value For Galleon is 462
[194549.438] AI Unit Value For Galley is 607
[194549.438] AI Unit Value For Galleon is 462
[194607.094] AI Unit Value For Galley is 607
[194707.109] AI Unit Value For Galleon is 858
[194707.109] AI Unit Value For Fluyt is 732
[194707.109] AI Unit Value For Galleon is 858
[194707.109] AI Unit Value For Galleon is 858
[194707.109] AI Unit Value For Galleass is 244
[194715.875] AI Unit Value For Galleon is 858
[194715.875] AI Unit Value For Galleon is 462
[194715.875] AI Unit Value For Fluyt is 732
[194715.875] AI Unit Value For Galleon is 858
[194715.875] AI Unit Value For Galleass is 244
So, we already see that AI Unit Value for Galley and Galleon is the same - 462.
But later it changed for Galley even to 607 and all this values continue changed in an almost randomly looking at first sight way. I have taken logs from this just one trade window , got all distinct values, ordered records and got this:
Ballista is 501
Ballista is 732
Bombard is 1135
Bombard is 1161
Bombard is 1848
Bombard is 2310
Bombard is 897
Brigantine is 1650
Brigantine is 825
Cannon is 1656
Cannon is 3709
Caravel is 495
Fluyt is 396
Fluyt is 561
Fluyt is 732
Galleass is 184
Galleass is 244
Galleass is 303
Galleass is 66
Galleon is 1042
Galleon is 462
Galleon is 653
Galleon is 858
Galley is 462
Galley is 607
Great General is -13
So, during the same trade window AI estimate for Galleass, for example, was floating from 66 to 303.
For Galleon - from 462 to 1052.
For Galley - from 462 to 607 ( ridiculously high, because apparently you can get a tech with 452 bulbs just on galley).
And how it got in trades Great General ( this was never on the table ) I even have no idea, and no idea why it's cost is negative.
Now we are looking into a code.
For this kind of units it calculates like this ( CvPlayerAI::AI_militaryUnitTradeVal ) :
UnitTypes eBestUnit = bestBuildableUnitForAIType(pUnit->getDomainType(), eAIType);
int iBestUnitAIValue = AI_unitValue(eBestUnit, (UnitAITypes)GC.getUnitInfo(eUnit).getDefaultUnitAIType(), getCapitalCity()->area());
int iThisUnitAIValue = AI_unitValue(eUnit, (UnitAITypes)GC.getUnitInfo(eUnit).getDefaultUnitAIType(), getCapitalCity()->area());
// Value as cost of production of the unit we can build scaled by their relative AI value
iValue = (iThisUnitAIValue * GC.getUnitInfo(eBestUnit).getProductionCost())/std::max(1,iBestUnitAIValue);
// Normalise for game speed, and double as approximate hammer->gold conversion
iValue = (2 * iValue * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent()) / 100;
So, we take getProductionCost of the best unit and adjust it on the coefficient depending of ratio between unitAIValue of this unit and unitAIValue of the best buildable unit of that domain. And later normalize this based on our speed and multiply by 2 * 3.3 = 6.6 as train percent on normal speed is 330.
This sounds reasonable on a first sight. So, if some unit is really outdated, we don't value it too much - e.q. Galleys and the era of Galleons should not cost probably even their hammers
But it is wrong and I'll show - why.
For example, in our case. Fluyt, Galleon, Galley, Galleas all have domain DOMAIN_SEA.
Their possible AI's are: UNITAI_ASSAULT_SEA. ESCORT_SEA, SETTLER_SEA, MISSIONARY_SEA, SPY_SEA.
ATTACK_SEA, RESERVE_SEA, EXPLORE_SEA, ASSAULT_SEA.
Galley's cost is 25, Galleas - 65, Fluyt - 85, Galleon - 130.
Combat values are: 3, 10, 10, 16
Moves are: 2 , 2, 3, 4.
Cargo for them are: 2, 0, 3, 4.
Now I am checking function AI_unitValue - it is really big and complicated. But if we will read only the lines that are applicable to our case we see. First we add some value for AI preference.
This doesn't matter in our case as both leaders doesn't have preference for sea units.iValue = 1;
iValue += GC.getUnitInfo(eUnit).getAIWeight();
Later we add into this values based on units parameters:
case UNITAI_ASSAULT_SEA:
case UNITAI_SETTLER_SEA:
case UNITAI_MISSIONARY_SEA:
case UNITAI_SPY_SEA:
iValue += (iCombatValue / 2);
iValue += (GC.getUnitInfo(eUnit).getMoves() * 200);
// Thomas SG - AC: Advanced Cargo START
{
iValue += (std::min(2,GC.getUnitInfo(eUnit).getTotalCargoSpace()) * 300);
}
// Thomas SG - AC: Advanced Cargo END
/************************************************************************************************/
/* BETTER_BTS_AI_MOD 05/18/09 jdog5000 */
/* */
/* City AI */
/************************************************************************************************/
// Never build galley transports when ocean faring ones exist (issue mainly for Carracks)
iValue /= (1 + AI_unitImpassableCount(eUnit));
So, first we need to know combat value for the unit. This is the function:
int CvGameAI::AI_combatValue(UnitTypes eUnit)
{
int iValue;
iValue = 100;
if (GC.getUnitInfo(eUnit).getDomainType() == DOMAIN_AIR)
{
iValue *= GC.getUnitInfo(eUnit).getAirCombat();
}
else
{
iValue *= GC.getUnitInfo(eUnit).getCombat();
// UncutDragon
// original
//iValue *= ((((GC.getUnitInfo(eUnit).getFirstStrikes() * 2) + GC.getUnitInfo(eUnit).getChanceFirstStrikes()) * (GC.getDefineINT("COMBAT_DAMAGE") / 5)) + 100);
// modified
iValue *= ((((GC.getUnitInfo(eUnit).getFirstStrikes() * 2) + GC.getUnitInfo(eUnit).getChanceFirstStrikes()) * (GC.getCOMBAT_DAMAGE() / 5)) + 100);
// /UncutDragon
iValue /= 100;
}
iValue /= getBestLandUnitCombat();
return iValue;
}
So, basically it is just combat value divided on getBestLandUnitCombat(). What is interested about this function, that it takes into account all units from all players in the game ( and not only land units, actually...). So, one person that will get some strong unit dramatically decrease the combat strength estimate of all players even if they have no idea of that unit or that player. It would be hard for me to know it, but fortunately there is interface for it in cygame, so by console command I've got it and it is 50 in the current game.
So, for Galley we will get: 1 + 200/50/2 + 400 + 600 = 1003 ( so, combat value as you see - doesn't matter at all under this conditions! )
Galleass: 1 + 1000/50/2 + 400 + 0 = 411 ( and now Galley is twice as valuable as Galleas because of ignoring combat value... )
Fluyt: 1 + 1000/50/2 + 600 + 600 = 1211
Galleon: 1 + 1600/50/2 + 800 + 600 = 1417.
I have checked this with console:
v_area = gc.getPlayer(12).getCapitalCity().area()
gc.getPlayer(12).AI_unitValue(194, 28, v_area)
1004
gc.getPlayer(12).AI_unitValue(201, 28, v_area)
1417
Where unit 194 is Galley and 201 is Galleon.
Now, I trade propose DG to trade Galley to Galleon and I get this:
[208604.578] AI Unit Value For Galley is 904
[208604.578] AI Unit Value For Galleon is 462
How game engine converts now ( 1004, 1417) into ( 904, 462 ) I have no idea.
It has no sense at all. The final value should be something close to the construction values - 25 and 120.
And after the trade if I try to trade Galleon with Galleass I get this:
[210670.234] AI Unit Value For Galleass is 66
[210670.234] AI Unit Value For Galleon is 1280
For now I have to do some pause as I need some sleep. I can not debug this as I don't have Visual Studio 2008, and I am out of fantasy what can I check in Python.
But I think that using complicated function AI_unitValue was a very bad idea , with simple getProductionCost you would get much more stable and fair estimate. And this AI_unitValue with all it's random numbers like:
add 200 for movement, add 300 for cargo space, add 1 for an improvement etc. looking like giving mostly random number without any sense.