ls612's C2C AI thread

ls612

Deity
Moderator
Joined
Mar 10, 2008
Messages
8,289
Location
America
This thread is for me to take on small AI tasks for Koshling in order to learn how to do SDK stuff better. Similar to my much-loved units thread:p, Koshling will be giving me things to do, and I will post any problems or suggestions I encounter here. Hopefully this can eventually add another skill to my set, as well as helping Koshling with some of his simpler tasks.
 
Ok, here's anice easy and self-contained one for starters:

Currently the AI evaluator for 'best unit' of an AI type doesn't take account of certain factors (for certain AI types) that have become important in C2C.

The specific one I am aware of (but feel free to scan the routine generally and consider other cases too), is that for UNITAI_CITY_DEFENDER it does not take into consideration any per-unit monetary cost (town watchmen!), or that a unit is DefendOnly (town watchmen again). Both of these factors should lower the units perceived value in this role (in the case of DefendOnly, by a fixed ratio - maybe 80% say for this AI type; for the cost by some calculation based on current income and expenditure.

These should be very localized changes to make - in CvPlayerAI::AI_unitValue(). The first large switch statement checks whetehr the unit beign considered is a valid choice at all (should be fine as it is). The second switch than calculates the value (this is where the UNITAI_CITY_DEFENDER clause (at least) needs some adjustment).
 
Ok, here's anice easy and self-contained one for starters:

Currently the AI evaluator for 'best unit' of an AI type doesn't take account of certain factors (for certain AI types) that have become important in C2C.

The specific one I am aware of (but feel free to scan the routine generally and consider other cases too), is that for UNITAI_CITY_DEFENDER it does not take into consideration any per-unit monetary cost (town watchmen!), or that a unit is DefendOnly (town watchmen again). Both of these factors should lower the units perceived value in this role (in the case of DefendOnly, by a fixed ratio - maybe 80% say for this AI type; for the cost by some calculation based on current income and expenditure.

These should be very localized changes to make - in CvPlayerAI::AI_unitValue(). The first large switch statement checks whetehr the unit beign considered is a valid choice at all (should be fine as it is). The second switch than calculates the value (this is where the UNITAI_CITY_DEFENDER clause (at least) needs some adjustment).

Sure, I'll get on to that. I'll make sure, at least at first, to post the code I modify for you to check and critique, so that I can get better at this.

Also, would it make sense to add the worker AI subroutine to the trunk DLL? It would do nothing until we added Combat Workers, but it would make the management of the old DLLs that much easier (as we found yesterday).
 
Sure, I'll get on to that. I'll make sure, at least at first, to post the code I modify for you to check and critique, so that I can get better at this.

Also, would it make sense to add the worker AI subroutine to the trunk DLL? It would do nothing until we added Combat Workers, but it would make the management of the old DLLs that much easier (as we found yesterday).

Sure.
 
I'd think that ideally you'd want the gold cost amount to play a factor, thus something like -5% PER gold expense?

Also, although town watch can't attack, they do make for very important units to have in every city. And this gets to the root of one of my biggest irritations with the ai structure the way it's currently setup. Shouldn't the ai wish to 'cover all bases'? The system it utilizes seems just fine for vanilla but for c2c with so many specialized unit roles, it seems that we need more ai types to get it to work correctly.

Example:
Town watch are only minimally useful as city defenders. In fact, I no longer think of them as city defenders at all beyond an incidental application based on their presence in the city when it comes under attack. I build them to battle crime. And in war they become merely fodder. I know we have some kind of new ai established for them in this regard that interacts with the property system as a whole, but this is a truly specialized role that no other unit line fills.

Thus, shouldn't AI_ANTI_CRIME be established as an ai type and then evaluated independently of the rest, letting the ai rulers know without question that this line must be built to combat their crime wherever it may exist? Do it right and the ai would manage crime far better than a human player ever could as it can keep full visibility of all factors in all cities in every round and utilize a perfectly measured response without overkill at all times, ordering policing units to resolve positive crime counts wherever they may exist.

Anti-Crime units never need to be built to go to war unless you want to make sure you have them in place in your newly conquered cities from the moment you arrive (optional war prep that can take place after war readiness is achieved but war motive hasn't been established yet.) They COULD be used for fodder emergency city defense if their production cost to power ratio was cheap enough but you'd have to want to lose them in the expected fight considering the gold maintenance costs of the units. You'd want at least one in every city to combat criminal style units, even if there's no crime there, assuming we'll be infusing them with some anti-stealth unit benefits (I don't think they have any yet but It'll be easier to give that to them once units can achieve multiple combat classes.)

This differs from considering them for the role of City Defender, a military force designed to repel attackers from the walls. Rarely, I think, would they ever be built for THIS purpose. Here, the not being able to attack and gold costs would make them poor selections. Often, its valuable to have your city defenders leap out and take care of minimal nearby threats. I was going to add the melee units get no city defense promos to the list but I've still got the graphics for those promos to do so I won't add that as a factor right now ;)

This is just one example. I feel the ai makes a lot of tactical errors by not having a good understanding of the specifics of various unit roles. AI_COUNTER, in particular, seems way over-stuffed with various roles for units to fill. Now I admit I have never researched the structure to a full understanding, only interacted with it in minor ways, so if I'm missing some of the ways these things have been considered, by all means, I'm bringing this up so I can be corrected.
 
@Thunderbrd:

I have already got the modifier for being OnlyDefensive working, and I am currently trying to code it so that 1 percentage point of value is subtracted for 2:gold: of extra cost (I don't expect that we will ever have a unit with anything approaching 200 gold of extra cost).
 
Seems a bit light though doesn't it? I get what you're saying, and I guess as a player I haven't looked too closely at that costs, but wouldn't even just -10gp be astronomically expensive? I'm just thinking 1%/2gp sounds much weaker than I'd consider it from a player's perspective. I would personally think of it more along the lines of -5% per gold which is of course waaaay different from your evaluation. So maybe a compromise, -2%/gold cost? That gives us out to what, 50 gold cost max before units are patently dismissed as useless by the ai?
 
@Koshling:

OK, I have some code here that passes the compiler with no errors or warnings, could you please tell me if this is good, and that it does what I think it does?

PHP:
		//  ls612: consider that a unit with OnlyDefensive is less useful
		
		if (GC.getUnitInfo(eUnit).isOnlyDefensive())
		{
			iValue *= (4/5);
		}
		//  ls612: consider that a unit with ExtraCost is less useful
		iValue *= ((200 - (GC.getUnitInfo(eUnit).getExtraCost())) / 200);

This is added on after all of the routines with iValue += something, so that the order of operations is correct. (add all modifiers, then multiply by a constant.)
 
Seems a bit light though doesn't it? I get what you're saying, and I guess as a player I haven't looked too closely at that costs, but wouldn't even just -10gp be astronomically expensive? I'm just thinking 1%/2gp sounds much weaker than I'd consider it from a player's perspective. I would personally think of it more along the lines of -5% per gold which is of course waaaay different from your evaluation. So maybe a compromise, -2%/gold cost? That gives us out to what, 50 gold cost max before units are patently dismissed as useless by the ai?

Not really. I assume that most of the time there will be a decent gold surplus for both human and AI. If I wanted to get really creative, I could subtract value based on what percentage of the AI's gold surplus the unit takes, but I don't know which function tells what the AI's gold surplus is.
 
PHP:
		//  ls612: consider that a unit with OnlyDefensive is less useful
		
		if (GC.getUnitInfo(eUnit).isOnlyDefensive())
		{
			iValue *= (4/5);
                        //To avoid a potential problem with the code rounding our 4/5 before multiplying it to iValue (not sure if there is here or not but I try to be careful where I don't know for sure...) I would express it as:
                        iValue *= 4;
                        iValue /=5;
		}
		//  ls612: consider that a unit with ExtraCost is less useful
		iValue *= ((200 - (GC.getUnitInfo(eUnit).getExtraCost())) / 200);
//This portion should probably be iValue += iValue * ((200 - GC.getUnitInfo(eUnit).getExtraCost())/200) as you're finding the adjusting value and adding it, be it positive or negative, to the ongoing total iValue.


These are just my impressions. I throw them in to see if I'll be corrected as well ;)
 
PHP:
		//  ls612: consider that a unit with OnlyDefensive is less useful
		
		if (GC.getUnitInfo(eUnit).isOnlyDefensive())
		{
			iValue *= (4/5);
                        //To avoid a potential problem with the code rounding our 4/5 before multiplying it to iValue (not sure if there is here or not but I try to be careful where I don't know for sure...) I would express it as:
                        iValue *= 4;
                        iValue /=5;
		}
		//  ls612: consider that a unit with ExtraCost is less useful
		iValue *= ((200 - (GC.getUnitInfo(eUnit).getExtraCost())) / 200);
//This portion should probably be iValue += iValue * ((200 - GC.getUnitInfo(eUnit).getExtraCost())/200) as you're finding the adjusting value and adding it, be it positive or negative, to the ongoing total iValue.


These are just my impressions. I throw them in to see if I'll be corrected as well ;)

1. Rounding 4/5 to what? I would hope it wouldn't round to 1. :rolleyes:

2. No, that's not the way it works. There is no such thing as negative extra costs. I take the extra cost, divide it by 2, and subtract that many percentage points from the value, so a unit with 6 extra cost would have 100 - (6/2) percent value, or 97% value to the AI, so the iValue would be multiplied by 0.97. If Koshling tells me what function gets the AI's surplus, I'll change it to be based off of that instead.
 
OK, I think I figured out how to measure the value based on the AI's gold situation.

PHP:
		//  ls612: consider that a unit with OnlyDefensive is less useful
		
		if (GC.getUnitInfo(eUnit).isOnlyDefensive())
		{
			iValue *= 4;
			iValue /= 5;
		}
		//  ls612: consider that a unit with ExtraCost is less useful

		if (getGoldPerTurn() >= GC.getUnitInfo(eUnit).getExtraCost())	
		{			
			iValue *= ((getGoldPerTurn()) - (GC.getUnitInfo(eUnit).getExtraCost()) / (getGoldPerTurn()));
		}

		if (getGoldPerTurn() < GC.getUnitInfo(eUnit).getExtraCost())	
		{			
			iValue = 0;
		}

@Koshling: Does that do what I think it does? It is supposed to make it so that the AI subtracts a portion of value from a unit based off of what portion of it's surplus the unit's extracost uses. If you think that this code is OK, I'll go and commit it to the SVN.
 
What other simple AI tasks do you have that I could handle? I am actually enjoying learning all about this, it is far more fun than repetitive unit coding.
 
@thunderbird - I've worked this over a bit with ls612 to avoid the rounding issues, ao don't worry about that aspect.

In regard to the need for more aitypes, yes and no. What you point out is indeed an issue with the entire ai structure. However, more ai types isn't really the solution, because we are into a situation where the ai needs mo roles fulfilled than the optimal number of units to fulfill them, which in effect means it is ot sufficient for the ai to order a unit for the purpose of fulfilling a role (ai type). What would be needed would be a more general parameterisation whereby the city builds (or orders in the new system) units with a much more general specification (think array of weighted ai types).

If we did add a new ai type (as an interim fix) then it should be a generic property-needs ai type, not a crime specific one, otherwise the ai won't work as the XML changes to add new properties.
 
Fair 'nuff on the rounding matter. I guess I was saying it as a way of confirming what I was somewhat unsure of myself. From what I can tell, integers cannot be decimalized at any point without causing it to round out and can create unexpected results when that takes place. However, I was a bit unsure about the effect of *= on the process.

I'm not really following why we'd need to generalize unit ai types more rather than getting more specific. I'm curious what your vision extends to there... I didn't really fully grasp everything you were saying about that... Wouldn't we find more specific ai types more easy to get the ai to command properly?

And as for the generic property needs, how would that work if every property must be handled in different ways? I mean, I foresee that some properties will be desirable to extend rather than always wanting to combat them. Already, for crime, we have unit abilities intended to add crime to enemy lands. So I presume you're not saying we'd need to have all properties handled in the same manner... therefore I'm hoping you could share some insight on what you'd do in the code to get the ai to understand that each property need is a unique case.
 
Fair 'nuff on the rounding matter. I guess I was saying it as a way of confirming what I was somewhat unsure of myself. From what I can tell, integers cannot be decimalized at any point without causing it to round out and can create unexpected results when that takes place. However, I was a bit unsure about the effect of *= on the process.
In general:
Code:
A *= B/C;
is most often better written as either:
Code:
A = (A*B)/C;
or
Code:
A *= (100*B)/C;
A /= 100;
Both avoid rounding issues when B and C are of comparable size (or B < C generally). This is because in
Code:
A *= B/C;
the right hand side (B/C) is evaluated first, as the same type as variables B and C - so if they are ints and B < C it will ALWAYS evaluate to 0, and the overall statement will ALWAYS set A to 0.
However, you do have to watch out for cases where A and B are large, in case A*B causes an integer overflow. In cases where that may potentially happen you may have to resort to more complex code such as the following:
Code:
int iDivisor = C;
while(MAX_INT/std::max(1,A) < B && iDivisor >= 2)
{
    B /= 2;
    iDivisor /= 2;
}

// Might want to test for iDivisor >= 2 here - if it isn't the result IS inherently overflowing
A = (A*B)/iDivisor;
This technique is used in a few places where very large numbers occur, and lack of using it in a few others is responsible for things like the end techs displaying a cost of 0 (arithmetic on them has overflowed). Obviously the /= 2 bit causes some rounding effects, but the result is generally sufficiently accurate for its intended purpose.

I'm not really following why we'd need to generalize unit ai types more rather than getting more specific. I'm curious what your vision extends to there... I didn't really fully grasp everything you were saying about that... Wouldn't we find more specific ai types more easy to get the ai to command properly?
Well. Suppose a city has 8 roles it wants to fulfill (if we add lots more like crime fighting, fire fighting, ...). If each unit can only be instantiated (and searched for indeed) by one UnitAI that implies each city needs AT LEAST 8 units. Because each unit (well stack strictly) can only be operating one AI type, it can (in the current system) only fulfill one role. Suppose instead that each unit had an array of AI types (possibly with 'effectiveness' weights). On unit move evaluation it could evaluate the best move for EACH of those AI types (aka roles), with a 'need weight' for each that would be calculated in the AI routines for that AI type and is obviously situational (the defender role has a high need weight while the city is under attack for example). It multiplies the need weight by the effectiveness weight for each action and performs the one with the highest result. Thus one unit sometimes behaves as a defender, sometimes as a crime fighter, ... etc. At build/order time the city would not ask for the best unit of a certain AI type - it would instead present an array of its perceived 'need weights' to a unit selection routine that would evaluate the best unit given the (multiple) current needs. Such a system would decouple the number of roles that need performing from the appropriate number of units for a city (which have to perform them)

And as for the generic property needs, how would that work if every property must be handled in different ways? I mean, I foresee that some properties will be desirable to extend rather than always wanting to combat them. Already, for crime, we have unit abilities intended to add crime to enemy lands. So I presume you're not saying we'd need to have all properties handled in the same manner... therefore I'm hoping you could share some insight on what you'd do in the code to get the ai to understand that each property need is a unique case.
It works fine now - how do you think the AI evaluates properties on buildings and so on? Each property has an AIWeight and an effective range (in the property definition XML). The AI assesses where it is currently in the effective range, and weights property manipulator effects according to how the unit/building would move the equilibrium point within the effective range. Whether higher is better or lower is better depends on the sign of the AIWeight, so units that ADD a property with negative AI weight tend to be selected against for AI types that would keep it in the city (e.g. - defenders), those that counteract such a property are selected for. The current position in the effective range (and how far the unit/bulding would move the equilibrium) determine how much weight is given to this factor when evaluating thw unit/building (so in a high crime city, where we are further through the effective range of the crime property, crime fighting is given a higher value)
 
I'm going to have to read through both of those a number of times to sort that out to a full understanding, and reference the code to see what you're saying in progress but all in all they both sound pretty genius in theory. In part this means 'mind boggling to me though it sounds like it would work well'. This is much of why I try to leave most of the ai work to you and only try to do what is minimally necessary to make my stuff somewhat handleable by the ai...
 
I'm going to have to read through both of those a number of times to sort that out to a full understanding, and reference the code to see what you're saying in progress but all in all they both sound pretty genius in theory. In part this means 'mind boggling to me though it sounds like it would work well'. This is much of why I try to leave most of the ai work to you and only try to do what is minimally necessary to make my stuff somewhat handleable by the ai...

I'm not proposing to actually DO that in the short-medium term, because it would be a fairly radical change and take a while to stabilize I expect. I'm just saying that the current system doesn't really allow you to keep adding unitAI types for very specific things, and until somethign more generic is put in place we have to live within the constraints of that. Long term I might try something like the above, but no promises.
 
Y'know, reading back through all that, I'm not sure it would really be so bad for ai cities (and especially attack stacks) to stock themselves up that much. It would certainly make taking them a bit more challenging when it came around to it, and if they are doing so to make sure their nation functions more profitably, then it can't really be too much of a drag on them. As a player, I'm finding I, myself, am stocking my cities with more units than the usual vanilla game in C2C.
 
Is there a way to make Automated workers collaborate more?

After a certain point, when my empire's large enough, micromanaging improvements becomes too cumbersome to my tastes. So I tend to build a large amount of workers, spread them around and automate them. The problem is that the majority of the workers tend to pile up in a city while just a few tend to the improvements in single unit stacks, so building improvements costs quite a bit more time and money, unless you relent and micromanage. This behavior is especially apparent when a city gains access to a new tile(s).

It's not a huge deal, but better worker AI would be something "nice to have" :)
 
Back
Top Bottom