[BtS] Navy AI

jkp1187

Unindicted Co-Conspirator
Joined
Aug 29, 2004
Messages
2,496
Location
Pittsburgh, Pennsylvania
I wanted to start this thread so that issues directly related to how the AI uses (or mis-uses) its naval units could be discussed.

I want to play a few games with basic 3.17 and the BAI 3.17 before I come up with any detailed commentary. But one thing that I've noticed in the past (and I recall seeing others agreeing here) was that the AI doesn't seem to use the blockade function enough. I noticed myself when scripting random events involving barbarian sea units, that they pretty much have to be given the UNITAI_PIRATE_SEA in order to blockade. When assigned this, the AI-controlled units will blockade...at least enough to force the player to build some sea units and hunt them down. Given that many Civ players tend to pay scant attention to their own fleets, perhaps getting the AI to use UNITAI_PIRATE_SEA a little more often would encourage more sea unit construction?

Interested in hearing your thoughts.
 
Definitely the naval AI often takes a backseat, good to bring it up!

I did a quick search through the code, it seems that for a unit to be set as blockading CvUnit:: plunder() must be called. This is only called if the unit's mission is set to MISSION_PLUNDER. There appear to be several ways that MISSION_PLUNDER is used but in many circumstances it seems to be a "if I'm not doing anything else" ... ie, a carrier group will set up a blockade once they're in position for launching aircraft.

Most of the intentional blockades seem to be set from CvUnitAI::AI_blockade and CvUnitAI::AI_pirateBlockade ... AI_blockade is only called from two places: CvUnitAI::AI_attackSeaMove and AI_carrierSeaMove. I think AI_attackSeaMove is the more interesting for what we're looking for (it's for attack units after all), the unit will only call AI_blockade if there's nothing nearby to attack or bombard, no friendly units to group with, and it happens to be already on a plot owned by the enemy ... that's a very rare circumstance. Seems like mostly these AIs will bounce between bombarding different cities, though I think they can set up blockades when they're done bombarding.

AI_pirateBlockade is called only from CvUnitAI::AI_pirateSeaMove ... pirate blockading seems much more likely to happen, particularly for barbarians. If the unit is not in friendly territory it only checks for units within 2 turns to attack, has a couple of rare checks for exploration and pillaging, and then chooses to blockade. In their own territory it adds a check for hunting down enemy units also in their territory plus a check for joining with other pirates.

I'll try adding an ATTACK barb ship and a PIRATE barb ship using the world builder and see how they behave ... CvGame::createBarbarianUnits only creates units with ATTACK AI (both land and sea). I just peaked at the unit XML and the only unit that uses the PIRATE AI is the Privateer. This wouldn't stop us from pushing PIRATE on say 1/3 of Barbarian galleys (galleys don't naturally do ATTACK anyway since the introduction of the Trireme.

(EDIT: Barbarian sea units use a separate move function, AI_barbAttackSeaMove ... this function never checks for blockading, seems like the best solution would be to add something there.)

On a related note, I think the AI should really build Privateers more often ... plus I think there should be actual Barbarian Privateers, but that's not really for this mod.
 
Okay, I went to check out the bombard AI in CvUnitAI::AI_seaBombardRange and found something I don't understand. In the meat of the function it's assigning values to different targets, here's the first part of the value code:

Code:
		int iValue = (AI_getUnitAIType() == UNITAI_ASSAULT_SEA) ? 0 : 1; 
		
		iValue += (kPlayer.AI_plotTargetMissionAIs(pBombardCity->plot(), MISSIONAI_ASSAULT, NULL, 2) * 3);
		iValue += (kPlayer.AI_adjacentPotentialAttackers(pBombardCity->plot(), true));
		
		if (iValue > 0)
		{
		     may potentially bombard this target ...

I get the second two lines, they make a lot of sense ... assign more value to a target that might actually be attacked. Very reasonable. The first line doesn't make sense to me ... if this unit is not running UNITAI_ASSAULT_SEA always give a value of 1 (ASSAULT_SEA is the AI for transports that run invasions).

First of all, are there any transport units in the game that can do bombardments? I guess it's good to have the check, you wouldn't want transports running around bombarding unnecessary targets when they could be picking up more troops. But why would you always want attack units like Frigates to bombard, even if you have no troops that are intending to attack the target?

Bombard damage to cities does effect which city the AI decides to target for land attacks, but there's no logic here for bombarding cities that might make good targets ... only logic for if the city is already a target.

If we simply remove that top line then the AI won't bombard cities that it has no intention of assaulting ... right now the AI will often send units around bombarding seemingly random targets, I think this is why.

Thoughts?
 
If we simply remove that top line then the AI won't bombard cities that it has no intention of assaulting ... right now the AI will often send units around bombarding seemingly random targets, I think this is why.

Thoughts?

But this would then mean that the human player would know the AI is going to attack a city that it bombards. I think there should still be a (smaller) random chance of the AI bombarding a city that it doesn't intend to assault to keep us on our toes.
 
But this would then mean that the human player would know the AI is going to attack a city that it bombards. I think there should still be a (smaller) random chance of the AI bombarding a city that it doesn't intend to assault to keep us on our toes.

Unless it's bombarding the turn OF the attack, of course. In which case, the Human player will have no warning whatsoever (ala the Sirian Doctrine.) I think it would be good to get the AI to make more use of this, if possible.
 
Might I recommend disabling the ability to move military units through another civ's territory except when at war (no open borders for military units) while developing the naval AI (I'd like to see significant changes permanently, but that's another topic for another thread)? Civ's AI has always used this ability as a crutch to avoid proper naval usage, forced to go without it, I think it'll get you better results in the long run.
 
But this would then mean that the human player would know the AI is going to attack a city that it bombards. I think there should still be a (smaller) random chance of the AI bombarding a city that it doesn't intend to assault to keep us on our toes.

That's a good point ... we wouldn't want the AI to be too predictable. I looked a little closer at the two subsequent value adds dealing with mission ais and potential attackers, the mission ai check has a range of 2 tiles while the attacker check has a range of 1, so the AI does not telegraph its intentions ahead of time with sea bombardment.

This bombard code is also the one place where an attack ship will on its own move into enemy territory to camp outside a city. It will move in to attack units or pillage but this bombard code is really the key to having ships harass the enemy. I don't think we should remove it at all, just tweak the behavior in a couple ways: support other player's attacks on enemy cities and make better use of blockades.

First, currently the unit assigns no extra value for whether anyone else is about to attack the city, only its owner's units. This would be one easy change that would definitely make sense, add something like:

Code:
		if( iValue > 0 && GET_PLAYER(pBombardCity->getOwnerINLINE()).AI_getPlotDanger(pBombardCity->plot(), 2, false) > 0 )
		{
			iValue += 2;
		}

(This danger check is the same code the AI uses to determine if a city is in danger of being attacked) Adding a flat two means the unit will definitely still prioritize its own player's attacks, even if they're small compared to others. When the player has no impending attacks of its own then the units will support someone else's attack if it's relatively close. Adding just 1 would make it support only very close attacks, 3 would cause it to move multiple turns to support another attack.

My other idea is that when there is no attack pending against any city the unit could opt to bombard a city that isn't currently being bombarded. Once it's in position to bombard attack units will do the bombard and then run a blockade mission. Causing the AI ships to spread out and bombard & blockade more cities seems like a more sensible behavior than just bombarding whatever is close ... it will make better use of blockades, putting a bigger hurt on the enemy economy. The one issue of course is that by increasing spread a little bit the AI units are a bit more vulnerable to attack, but again we can control how far the AI looks for another bombard target by setting how much to add. Adding just 1 or 2 would make it only pick fairly nearby targets so the units wouldn't be spread to thin. The code would check for units running MISSIONAI_BLOCKADE (which is what bombarding sea units run) on the city plot, just like the existing check for MISSIONAI_ASSAULT.
 
My other idea is that when there is no attack pending against any city the unit could opt to bombard a city that isn't currently being bombarded. Once it's in position to bombard attack units will do the bombard and then run a blockade mission.


This is one thing I need clarification on -- will a blockade work if you've blockaded in the same turn?

I ask because the update notes for 3.13 say:

Blockading consumes all remaining movement
Blockade is canceled when unit does something other than blockade

I always sort of assumed (perhaps incorrectly) that if a unit did something other than blockade for that turn, the blockade wouldn't kick in until the next turn.
 
The blockade takes effect immediately, at least in terms of blocking trade. I don't know whether you have to spend the whole turn to get plunder, it doesn't look like it.

Just a bit more info, in AI_pirateBlockade() there's already a check just like I was thinking for whether the city is already blockaded by another unit to cause Privateers to fan out to do more damage.
 
Thanks for your very clear description of the bombard and blockade behaviour. I'm learning more very quickly because of this clarity.

So you mean adding something like this "meaty" behaviour out of CvUnitAI::AI_pirateBlockade:
Code:
if (!bPlotBlockaded)
{
	if (isEnemy(pPlotCity->getTeam(), pLoopPlot))				{
		int iCityValue = 3 + pPlotCity->getPopulation();
		CityValue *= (atWar(getTeam(), pPlotCity->getTeam()) ? 1 : 3);
		if (GET_PLAYER(pPlotCity->getOwnerINLINE()).isNoForeignTrade())
		{
			iCityValue /= 2;
		}
			iPopulationValue += iCityValue;
		}
	}
}
to the bombard target code in AI in CvUnitAI::AI_seaBombardRange, modified so that only meaty war target cities are chosen, with the aim that once bombarding is finished or if bombarding is not viable, switch to blockading? At the same time not to increase the seperation of navy stacks too much and thus destroy their ability to defend each other? So in this way frigates will not have such a tendancy to sit by any city simply bombarding it for no good reason.....

Cheers.
 
Yeah, basically. The group of Frigates sitting around bombarding any old city endlessly is exactly what I'm talking about. It seems like when they're bombarding they're also blockading, so I don't want to remove the bombard cities we won't actually attack behavior in favor of just blockading ... you get both with bombard, plus it's more annoying to the victim. I just want two groups to pick different targets more often without separating too much so that more cities fall under the blockade.

By the way, unless I'm reading something wrong only Privateers can collect plunder from blockades, Frigates and other ships cannot ... the blockaded city has to be an enemy of the blockader, but the teams cannot be at war. In makes sense I guess, if you're at war trade is just halted while a Privateer would only pick some off. It kind of surprised me though ...
 
By the way, unless I'm reading something wrong only Privateers can collect plunder from blockades, Frigates and other ships cannot ... the blockaded city has to be an enemy of the blockader, but the teams cannot be at war. In makes sense I guess, if you're at war trade is just halted while a Privateer would only pick some off. It kind of surprised me though ...

Yes, that's exactly right. Probably made that way to give extra encouragement to building privateers. (Although it's actually a little ahistorical, as sailors in the Royal Navy were expected to supplement their own income in wartime by plundering enemy ships.)
 
Yeah, basically. The group of Frigates sitting around bombarding any old city endlessly is exactly what I'm talking about. It seems like when they're bombarding they're also blockading, so I don't want to remove the bombard cities we won't actually attack behavior in favor of just blockading ... you get both with bombard, plus it's more annoying to the victim. I just want two groups to pick different targets more often without separating too much so that more cities fall under the blockade.


Oh, and after looking through the comments, this would be interesting to try for an upcoming version. Doesn't have to be perfect on the first roll of the dice, either...if it turns out that the AI acts MORE stupid than before, it can always be removed or altered. But if it makes the AI act stupid, but still less stupid than before, it's still an improvement. :)
 
I know you have a lot of pirate events which spawn barbarian vessels, but it would be nice to see barbarian privateers that spawned on their own.

Yes, I would like that, too.
 
I know you have a lot of pirate events which spawn barbarian vessels, but it would be nice to see barbarian privateers that spawned on their own.

You should have a quick read through this thread. The reason why privateers aren't spawned is due to the high tech requirements. I think 3.17 has fixed the CreateBarbarian function so that spawns happen more frequently, though. I haven't updated yet, so I haven't tried.
 
Top Bottom