New experimental BetterAI version : more human-like and warlike behaviour

Yup, the problem is well known, the AI still doesn't use well its collateral units to defend its cities. Maybe I can fix that, it's probably not easy though.

Did you notice anything else ? Were there many wars and if so, when ? (if you played with my 'unofficial' version)
 
Here is the function for collateral unit AI behaviour:
Code:
void CvUnitAI::AI_collateralMove()
{
	PROFILE_FUNC();

	if (AI_leaveAttack(1, 20, 100))
	{
		return;
	}
	
	if (AI_guardCity(false, true, 1))
	{
		return;
	}

	if (AI_heal(30, 1))
	{
		return;
	}

	if (AI_cityAttack(1, 35))
	{
		return;
	}

	if (AI_anyAttack(1, 45, 3))
	{
		return;
	}

	if (AI_anyAttack(1, 55, 2))
	{
		return;
	}

	if (AI_anyAttack(1, 35, 3))
	{
		return;
	}

	if (AI_anyAttack(1, 30, 4))
	{
		return;
	}

	if (AI_anyAttack(1, 20, 5))
	{
		return;
	}

	if (AI_heal())
	{
		return;
	}

	if (!noDefensiveBonus())
	{
		if (AI_guardCity(false, false))
		{
			return;
		}
	}

	if (AI_anyAttack(2, 55, 3))
	{
		return;
	}

	if (AI_cityAttack(2, 50))
	{
		return;
	}

	if (AI_anyAttack(2, 60))
	{
		return;
	}

	if (AI_protect(50))
	{
		return;
	}

//    if (AI_group(UNITAI_ATTACK_CITY, -1, -1, -1, /*bIgnoreFaster*/ true))
//	{
//		return;
//	}

	if (AI_guardCity(false, true, 3))
	{
		return;
	}

	if (AI_retreatToCity())
	{
		return;
	}

	if (AI_safety())
	{
		return;
	}

	getGroup()->pushMission(MISSION_SKIP);
	return;
}

The first bit:

if (AI_leaveAttack(1, 20, 100))
{
return;
}

is "try to find something to attack within 1 square".

Code:
bool CvUnitAI::AI_leaveAttack(int iRange, int iOddsThreshold, int iStrengthThreshold)

One problem is the last parameter: it only tries to attack if the collateral unit's plot strength is greater than the attacking plot strength.

Pratically, what "could" be done there is an attack and defense simulation: see if attacking leads to better results than defending, esp. if you are in a city.

Tricky.

As an aside, I found something interesting: CvUnit::currEffectiveStr should be changed, or the AI should use something different to approximate odds: one's ability to win a skirmish is closer to combat strength squared, not linear with combat strength. I'd have to be careful making sure that units don't use this value for actual combat, just for AI purposes... This seems to be the case: the only use of currEffectiveStr is in "*AI.cpp" files.

To be clear:

The strength of a unit, in a last-unit-standing fight, is roughly purportional to currHP * currCombat^2 -- a bit less than that, if they don't have time to heal up between fights.

Collateral units work differently. When attacking, their power is dependant on the defender having many units.

If the defender has "enough" units, a collateral unit does damage purportional to:
(TargetAvgCollateralCombatPower / CollateralDamage)^1.5 * number_of_targets
in the collateral phase: the exact value is tricky to work out.

1: Take # of targets with less than "MAX_COLL_DAMAGE" damage.
2: Then, roughly (CollateralStr/DefenderCollateralDefense)^1.5 * 28 damage is done to each one, on average. (that takes into account both an approximation of hit chance (roughly power ratio), and damage per collateral attack (roughly sqrt of power ratio, times 28).

...

If we factor in the effects of repeated combat from smaller stacks fighting larger ones, we can simplify this.

Have the power-estimate be equal to:
HP * Combat^1.5

where the units are "estimated power damage done to an enemy stack in a suicide-charge-until-dead".

Then collateral damage adds:
85 * CollateralStr^1.5
to this value.

So, we get:
HP * CombatStr^1.5 + 85 * (CollateralPercent * CollateralStr)^1.5
as our "better than current estimate" of collateral unit power contribution to an attacking stack. :)

So a full-HP catapult (str 5) with +50% collateral strength contributes:
1118 from direct-attack power.
1746 from collateral-damage power.
for a total of:
2864 attack-power.

A full-HP praetorian (str 9) with combat 1 (+10%) contributes:
3115 from direct-attack power.

A full-HP axeman (str 5) with combat 2 (+20%) contributes:
1470 from direct-attack power.

You will notice that catapults read out as rather powerful under this system -- this reflects the power of catapults in the real game against target stacks.

Ideally one would factor in the enemy stack size: a single enemy unit will result in catapults overestimating their importance, while a large stack will result in catapults underestimating their importance.
 
In the current implementation of BetterAI, I often see multiple defending artillery type units build by the AI. However, they typically attack single attackers that are just pillaging and they stay away from larger stacks. Just the opposite from what you'd want.

If a unit strength valuation system is going to be used like suggested above or something similar, then it is very important that the strength from artillery type units like catapults is estimated correctly and that means taking into account the stack size of the enemy. You'd want the AI to attacks single units and stacks of size 2 with the right counter units, while it values the collateral damage attack higher once the stack is larger.
 
The problem is that 100 strength thing.

It will only attack if it thinks this stack is stronger than the target stack.

This makes catapults attack lone units...

The next test is AI_guardCity. This ends up saying "we should guard this city".

Not so good. :)

So, in english:

bool CvUnitAI::AI_leaveAttack(int iRange, int iOddsThreshold, int iStrengthThreshold)

1> We suicide charge if our stack is at least as strong as the target stack, and we have at least a 25% chance of victory. This is a bad idea -- this is something you do with cavalry and "get rid of pillagers" units.

2> If not, and we are in a city, we stop.

Also a bad idea. This prevents collateral units in cities from doing . .. .. .. ..

Next, we see if we need healing.

bool CvUnitAI::AI_cityAttack(int iRange, int iOddsThreshold, bool bFollow)

Range 1, Odds 35%

bool CvUnitAI::AI_anyAttack(int iRange, int iOddsThreshold, int iMinStack, bool bFollow)

Range 1, Odds 45%, MinStack 3
Range 1, Odds 55%, MinStack 2
Range 1, Odds 35%, MinStack 3
Range 1, Odds 30%, MinStack 4
Range 1, Odds 20%, MinStack 5

Is there a stack within 1 range that we have a 45% chance of winning with at least 3 units in it?

Then we heal up.

Then if we have a defensive bonus, we think about guarding a city.

Anyattack:
Range 1, Odds 55%, MinStack 3
CityAttack:
Range 2, Odds 50%
AnyAttack:
Range 2, Odds 60%

Then see about protecting other units (50% odds).
Then think about guarding a city.
Then think about retreating to a city.
Then think about finding a safe spot.
Then skip one's turn.

...

With the better approximation of "my strength vs yours" above, we can improve the AI_anyAttack. What if it did a check to see if I can defeat the target, rather than the odds of each unit?

Use leave-attack style code, but have it pay attention to the defender's stack size: collateral power can only sum to a certain percentage of the target's total power (based off of the max collateral damage %).

Ie, an AI_attackCrush test.

The AI_attackLeave can also take a "min target stack size" parameter, or take that into account, much like AI_anyAttack.

Hmm...
 
Good finding ! It's clear that something is wrong in the tactical evaluation before the fight... The good solution would clearly be to 'simulate' the fight (which we probably can't do except if you want to wait one hour between each turn :) ).

I agree with you, the collateral units need to be fixed ! Tell me if you work on that part so that I don't work on it simultaneously. ;)
 
I figure a two-pass algorithm.

The first uses a better power approximation to see if it makes sense to try.

If that passes, and it is close to a toss-up, run a simulation of the fight. If we come out ahead, we chaaarge! with our entire stack.

Ie: we only run this simulation of the fight after we have decided who we want to crush, and just before we execute the unit-charge. The simulation acts as a check on our first-estimate attack guess.

Given that the first-estimate says "we should crush that stack":
If we have a P chance of winning the battle, we will attack with probability P.

So if we have a 90% chance of winning, we will attack 90% of the time:
10% we don't engage.
9% we engage and lose.
81% we engage and win.

If we have a 20% chance of winning, we will only attack 20% of the time.
80% we don't engage.
4% we engage and win
16% we engage and lose.

Admittedly, we probably make this decision every round -- so if the enemy sticks a stack outside of our city and the first-pass thinks we should try to crush it, eventually we will give it a go. :)

But the delay might be enough for some kind of "bring additional defenders to protect against a hostile stack" code to kick in.

The important part of doing the simulation before attacking is that the AI will almost never "crush attack" when it has no hope of winning against the defenders.

"Crush attack" AI code should only activate when the target is a stack and we are a stack of sufficient size.

...

But that's improving the AI. To make catapults stop acting like nimrods, I think you can just delete the first two checks:
Code:
	if (AI_leaveAttack(1, 20, 100))
	{
		return;
	}
	
	if (AI_guardCity(false, true, 1))
	{
		return;
	}

Then the siege unit will NOT try to attack weak attacking stacks with a 20% chance of winning, and it will NOT compusively guard cities.
 
The thing that is putting me off playing the game is the non-use of collateral units. When you're outside an enemy city and they have a bunch of cats just sit there and do nothing, it makes the game feel too easy.
 
A human player will often use its defending collateral damage units to attack the threatening stack even if it doesn't intend to fully destroy the attacking stack (that turn). I've done this sometimes when the AI approaches my city with a stack that is too large to defend against. I hurt this attack stack with some artillery type units (for instance catapults), then attack with some good offensive units (say macemen, knights). But if the stack is far from being defeated, then I won't throw in my city defenders (say longbowmen). Often this will make it impossible for the attacking stack to take my city that turn and give me some time to get reinforcements to the threatened city. I couldn't destroy the threatening stack, but I've made it less threatening by hurting it with collateral damage attacks (and some strong attackers) and then waiting for reinforcements while defending my city.

The situation is very rare that the best move for a collateral damage type of unit is to stay in the city.
If the attacking stack is weak, then you can hurt them with collateral damage attacks and destroy the stack with attacking units.
If the attacking stack is too strong to defeat, then the collateral damage attack will often weaken them enough to get some reinforcements there before the stack can take the city.
If the attacking stack is far too strong and will capture the city anyway then the best move would be to evacuate the city and let the collateral damage units fight another day. But attacking the stack doesn't have to be a bad move. It will hurt the attackers and they will probably suffer higher losses when attacking the city because their units are weakened.
The only time when collateral damage units shouldn't attack is if the stack consists of very few units.

This is not perfect and a human player would not always use its collateral damage units to attack a threatening stack, but if the AI would attack every stack with collateral damage units, then that is hard to abuse by a human player.
 
Right, Roland. That's what we want the AI to do, but it currently doesn't. Right now, Yakk etc are absolutely correct that the AI currently does some nonsense such as designate cats as garrison units. Whatever it's doing, it's wacked and needs to be fixed.

Wodan
 
So I've been hacking my my copy of the source and making a few changes -- I've made the Power calculation more accurate, the effectiveStrength calculation more accurate, tweaked catapult changes, and patched the "on continent" war chance.

Klock, are your changes checked into sourceforge? I want to see if your changes and mine work together well.
 
Nice work, Yakk ! Did you test your changes to see if it felt better in the game ?

My own changes are not in Sourceforge right now, my subscription there didn't work (I don't know why right now)... I guess I could send you the source files I modified, I commented my changes.

As I said in the other thread though, right now I don't feel it's a good idea to work on the AI since Blake made a whole new AI for BtS and it's about to be released.

This feeling is being reinforced by the lack of interest for BetterAI nowadays (everybody waits for BtS, it's normal...). I had very few feedback on my own changes, so I really think the best is now to wait and to work together (not only us two, but any fan who knows dev a bit and wants in) on Blake's new AI after the release, maybe even after the patch.
 
Well, your main question was, are there more wars now and are these wars 'reasonable'?
I need many games to give a proper answer;)
Now I have been through five monarch games with normal aggression level and there were many wars. On greater continents they were ok, but when the map fractal map generator leads to islands which can be reached with triremes, sometimes the AI declared on me (I'm absolutely sure without any bribing). Then once in a time a single galley made a crazy attempt to land... But all in all this version plays much better.
 
Nice work, Yakk ! Did you test your changes to see if it felt better in the game ?

My own changes are not in Sourceforge right now, my subscription there didn't work (I don't know why right now)... I guess I could send you the source files I modified, I commented my changes.

Or just host them on CivFanatics. :)

As I said in the other thread though, right now I don't feel it's a good idea to work on the AI since Blake made a whole new AI for BtS and it's about to be released.

Bah -- Blake did the same thing with Warlords. :) While it was a leap in quality, it wasn't a complete reworking of how things went.

I figure that getting familiar with the code and making improvements now is a great time -- so they will be fresh in our minds when BTS-AI comes out, and we can fold them into BTS-AI quicker.

This feeling is being reinforced by the lack of interest for BetterAI nowadays (everybody waits for BtS, it's normal...). I had very few feedback on my own changes, so I really think the best is now to wait and to work together (not only us two, but any fan who knows dev a bit and wants in) on Blake's new AI after the release, maybe even after the patch.

*grin* -- I'm gonna be a curmudgeon, and release a custom-compiled and improved BetterAI for my IsoMod if I get it done pre-BTS.

I didn't notice -- happen to see what license BetterAI is released under?

... checking...

RPL:
c. Create Extensions to the Licensed Software consistent with the rights
granted by this License, provided that You make the Source Code to
any Extensions You Deploy available to all third parties under the
terms of this License, document Your Modifications clearly, and title
all Extensions distinctly from the Licensed Software.

So I gotta bundle up source code if I distribute the binary.

...

Actually, I gotta share my source code to anyone even if I don't distribute the binary. Neat. :)

I suspect we need rights to be able to check in changes to BetterAI on source forge.
 
So I've been hacking my my copy of the source and making a few changes -- I've made the Power calculation more accurate, the effectiveStrength calculation more accurate, tweaked catapult changes, and patched the "on continent" war chance.

I totally want a compiled version of this! Please!

If we have a 20% chance of winning, we will only attack 20% of the time.
80% we don't engage.
4% we engage and win
16% we engage and lose.

Im not sure how this will play out. I would cap it at around 40-50% or so. Because as a human I will brutally exploit this: Get a bigger stack near an enemy stack. Wait turn. Wait. Wait. Wait. Wait. Enemy stack attacks and commits suicide.
 
Yakk, I just discovered your mod from this thread, can't wait to try it out
sounds awesome
what a useless post by me this was
that's just how excited I am
 
I've played your mod and its great! :D

Man can the AI conquer now, although it still does foolish suicidal attacks but its much better. There are a lot more wars between AIs now, but I still see a bias against the human player, AIs will declare war on me even though I'm much more powerful, it has other weaker neighbors, and/or are far away from me.

Also the AIs makes a fatal mistake when they're warring, they defortify their cities leaving only 1 unit to defend to send the other units in a stack to attack, while this can be a smart tactic it leaves them open to attack from a neighbor or naval invasion.

So when an AI declares war on you all you have to do is have a stack to counter theirs and flank them by building 1-2 galleys and a trireme or a gallon or 2 and invade them on the other side of their empire and capture and burn their cities to the ground.

This needs to be fixed.
 
Units tied up with defending don't go to war. Note that in the mod linked, I reduce the AI's advantage against the player significantly: so try boosting your difficulty level, and see if you can deal with the enemy forces.

As an aside, the changes I made to defensive alloitment should be limited to:
A> Border cities.
B> Cities under threat. The number of extra units needed was changed to (threatening power/average defender power), instead of just the raw number of threatening units.

You could, under the standard BetterAI build, threaten a city with a stack of warriors, and cause most defenders to freeze up and not sally! With a few warrior stacks (or other trash troops) backed up with a strong defense unit or two, you could lock down the entire enemy army, even if it overpowered you significantly. :)

...

I should see about increasing the bias of the AI towards dog-piles and crushing weak neighbours, like klok did -- I think mine still has the standard "attack people who we hate much stronger than people we like" code.

Ideally, I'd like various AI nations to conquor and grow much like a player would: finding a nearby weak nation, expanding, and repeating -- and form de-facto defensive/offensive alliances by having a tendency to dogpile with friendly civilizations.

It should be standard for multiple civilizations to go to war with you at once, and going to war should be contingent on having allies sufficient to crush your target, and your target being worth conquoring.

Ie, when a civilization wants to go to war, it should look at your relative power points. It should then seek to gather an alliance at least 1.5 to 2.0 times as powerful as the target, and then go to war with the target.

To invite humans to such a dogpile, it would conditionally ask other AI "if I get the human to join the war, will you join in too?", produce a dogpile large enough to defeat the target with the human, ask the human to go to war -- if the human says yes, activate the conditional agreements and go to war. Otherwise, keep searching for another set of AIs who would agree to go to war with the target.

I'd call this "effective conquest". ;)

When attacked, it should try the same kind of dogpile tricks against the attacking alliance.

Another bug I found is that the AI will keep on MASSING even when it has an economic collapse going on. A massing strategy should keep track of how many offensive units it can afford, as well as how many units it has.

Should the AI prefer teaming up with the most powerful civilization, or prefer taking it down?
 
I took your (Yakk's) game core out for a spin, with aggressive AI on the Earth map. Played as the Aztecs on Monarch, and found myself surprised to be the first to Optics, and the tech leader. Then I got a copy of someone's world map and found out why I was up on tech. Cyrus had wiped out Egypt, Arabia, and India. Frederick had wiped out the Russians, the Romans, and the French. Qin had taken out the Mongol hordes. With everyone willing to take more units out of their cities for combat, things had gone nuts over there.

I've been playing with the standard Better AI for a while, and while there have been plenty of wars, this has been the most successful warring I've seen. I'm pretty sure that the AI is now much more effective at leveraging a tech advantage.

Played a few other games, and I found out quickly that I needed to drop down a level when I had more than one neighbor. Big stacks have been decimated as I brought them to cities, and I've wished more than once that I had Chichen Itza:eek: . Having the AI commit more of their army to active rather than passive war is dangerous.

It's tons of fun.
 
Hmm, what about other human behaviors? It seems alot of players specialise a GPP farm a Science city,commerce city production cities or play to more specific strategies (from the start - I'm going for a cultural win).

Disclaimer: Havn't read up on much about better AI just thought I'd throw in 2c for people to do what they will with it.
 
Back
Top Bottom