Once more: FFH and the AI

Yes; I guess I mis-understood the intended context.


Sounds pretty good. Personally, I'd be tempted to make the initial random value dependent on difficulty level (so it would be large at low levels, and small at high levels). I don't think that's essential, though; at worst, we penalize the AI further at the lower levels.

Hah! Great minds etc. I quote from my conversation with a friend on IRC last night:

(I just realized that would be in German, so I'll translate ;P)

[03:09:43] <Daniel> ich hab mal mit dem gedanken gespielt, das random value vom schwierigkeitsgrad abhaengig zu machen
[03:09:45] <Daniel> was haelst du davon?
[03:09:51] <Daniel> sprich umso leichter desto zufaelliger
[03:09:53] <Offenbarungsjoe> ich w&#252;rds mal rausnehmen

in English:

<Daniel> I thought about making the random value dependent on difficulty level
<Daniel> What do you think?
<Daniel> That is to say, easier equals more random
<Offenbarungsjoe> I'd take it out completely for now

But I'm absolutely with you. Good thinking :)

RE: ATTACK_SEA. A bit sloppy debugging on my side here. What it actually checks is if there currently is no unit with standard unitai_attack_sea under the player's control; so if he hasn't built one yet, he gets the 200 bonus for each unit that comes with this ai. I agree though that this is an unwarranted bonus. I'd probably remove that logic completely.
 
EDIT: Added a link to the first post.
 
Btw, Bringa, do you know something about python? I've been wondering on making the targetting spells useful for AIs: simply let them affect a random enemy unit within range. I'm wondering if an approach as follows might work. Eg a banish spell (untested):

Code:
def reqAIBanish(caster):
	if not canCast(caster):
		return False
	pPlayer = gc.getPlayer(caster.getOwner())
	if pPlayer.isHuman():
		return False
	iRange = 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION1')):
		iRange = iRange + 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION2')):
		iRange = iRange + 1
	for iiX in range(iX - iRange, iX + (iRange + 1), 1):
		for iiY in range(iY - iRange, iY + (iRange + 1), 1):
			pPlot = CyMap().plot(iiX,iiY)
			for i in range(pPlot.getNumUnits()):
				pUnit = pPlot.getUnit(i)
				if (caster.getOwner() != pUnit.getOwner() and pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_MAGIC_IMMUNE')) == False):
					return True
	return False

def spellAIBanish(caster):
	doCast(caster)
	iX = caster.getX()
	iY = caster.getY()
	iRange = 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION1')):
		iRange = iRange + 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION2')):
		iRange = iRange + 1
	iPlayer = caster.getOwner()
	for iiX in range(iX - iRange, iX + (iRange + 1), 1):
		for iiY in range(iY - iRange, iY + (iRange + 1), 1):
			pPlot = CyMap().plot(iiX,iiY)
			for i in range(pPlot.getNumUnits()):
				pUnit = pPlot.getUnit(i)
				list = []
				list.append(pUnit)
				iRnd = CyGame().getSorenRandNum(len(list), "Maniac")
				target = list[iRnd]
				if isResisted(target,caster,20) == False:
					CyInterface().addMessage(target.getOwner(),True,25,'Unit Banished.','AS2D_LOSS_LATE',1,'Art/Interface/Buttons/Spells/Banish.dds',ColorTypes(8),target.getX(),target.getY(),True,True)
					startWar(caster.getOwner(), target.getOwner(), caster, target)
					target.kill(True,0)

Hmm, should add something checking if caster is at war with unit target, and unit isn't magic immune.
Edit: and lots of other stuff...
 
I thought I'd replied to this! Musta been eaten by the internet monster.

What files I changed? I didn't keep track, really, but these for sure: leaderheads, techs, units, buildings, globaldefines, bunch of related schemas, maybe civic infos.

I haven't actually looked at unit AI at ALL yet so before I can comment on your code, I'd have to go have a look how they do things right now.

I have a new priority, though. The power graph is horribly inaccurate. It doesn't reflect unit level at all (crucial in FFH) and it reflects unit base strength poorly (one str 13 unit is surely more powerful than 10 str 3 units). This messes with the AI. They think they're safe when actually the human player already vastly out-built them. In a game we played just now I had about 8 conjurors and 12 more adepts about to level up while my ally had lasha valas at level 19 (!!) and a bunch of really really high level vampires. Our AI enemies had typical garrisons of one macemen, two warriors and two archers. A joke, obviously. Still, each one of them had more than twice our power in the power graph. I don't know if we're going to finish that game. We've won so obviously. It will be a fun rape though ;)

I think this will be the next thing I'll tinker with: power rating and making the AI build sufficient troops. I mean, seriously, if their capital has not even a quarter of the troops that we have in one of our bordertowns, something's wrong. Also, I'll have a look at the code where the AI decides what to build. Building warriors and archers late-game is simply wasting hammers, but the AI doesn't understand that yet.
 
I have a new priority, though. The power graph is horribly inaccurate. It doesn't reflect unit level at all (crucial in FFH) and it reflects unit base strength poorly (one str 13 unit is surely more powerful than 10 str 3 units). This messes with the AI. They think they're safe when actually the human player already vastly out-built them. In a game we played just now I had about 8 conjurors and 12 more adepts about to level up while my ally had lasha valas at level 19 (!!) and a bunch of really really high level vampires. Our AI enemies had typical garrisons of one macemen, two warriors and two archers. A joke, obviously. Still, each one of them had more than twice our power in the power graph. I don't know if we're going to finish that game. We've won so obviously. It will be a fun rape though ;)

I think this will be the next thing I'll tinker with: power rating and making the AI build sufficient troops. I mean, seriously, if their capital has not even a quarter of the troops that we have in one of our bordertowns, something's wrong. Also, I'll have a look at the code where the AI decides what to build. Building warriors and archers late-game is simply wasting hammers, but the AI doesn't understand that yet.

It would be really great if this were fixed...
 
I haven't actually looked at unit AI at ALL yet so before I can comment on your code, I'd have to go have a look how they do things right now.

It has nothing to do with unit AI. It's just a new spell.

I think this will be the next thing I'll tinker with: power rating and making the AI build sufficient troops. I mean, seriously, if their capital has not even a quarter of the troops that we have in one of our bordertowns, something's wrong. Also, I'll have a look at the code where the AI decides what to build. Building warriors and archers late-game is simply wasting hammers, but the AI doesn't understand that yet.

I think you may be wasting your time here. Blake/Iustus' Better AI already addresses this, putting extra troops in border towns etc. Though a FfH specific issue is probably that the AI doesn't build the necessary building in certain cities to upgrade their units, meaning they always stay stuck with warriors & archers. At least I often see cities with longbowman garrisons, while in the city right next to that there are only a couple warriors.
 
At least I often see cities with longbowman garrisons, while in the city right next to that there are only a couple warriors.
I think that their city defenders are always assigned to the city in which they are built? If so it is a big problem in a game that aims for specialization and one someone should fix.
 
@ Maniac: I would love to see BetterAI in FFh, it is makes such a huge difference IMO.. Unfortunately it is a Warlords component, and Kael has pretty much said FFh will never make it to Warlords :(

Meaning how is she wasting her time? Can't use BetterAI with FFh afaik
 
afair there is a betterai for vanilla as well. The problem is merging the two sdks. It can be done, I guess, but it'll be a LOT of work.
 
dont think so. the Requirements listen on their forum says Warlords 2.08.. unless i havent seen it. That would be awesome, if so.

Its nice having the city governor actually be useful :)
 
wow, sweet!
 
@ Maniac: I would love to see BetterAI in FFh, it is makes such a huge difference IMO.. Unfortunately it is a Warlords component, and Kael has pretty much said FFh will never make it to Warlords :(

Meaning how is she wasting her time? Can't use BetterAI with FFh afaik

Kael has said in some post a while ago he was on a 'wait and see' to implement Better AI, first letting it develop and polish somewhat further.


Btw, something which bothers me, the AI often seems to build only one mana improvement, meaning you see Varn Gosam with only Fire mana, the Khazad with multiple Earth manas... Would it be possible to add something to the worker code so that the AI doesn't build mana improvements of mana types it already has?
 
Does anyone know what could be the problem with the line
pUnit = p2Player.getUnit(list[iRnd])
near the end?

Changing the line to pUnit = pPlot.getUnit(list[iRnd]) doesn't help.

I'm trying to create an alternative version of Banish for the AI in which a random enemy unit within range is killed.

Code:
def reqAIBanish(caster):
	if not canCast(caster):
		return False
	pPlayer = gc.getPlayer(caster.getOwner())
	iX = caster.getX()
	iY = caster.getY()
	iRange = 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION1')):
		iRange = iRange + 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION2')):
		iRange = iRange + 1
	for iiX in range(iX - iRange, iX + (iRange + 1), 1):
		for iiY in range(iY - iRange, iY + (iRange + 1), 1):
			pPlot = CyMap().plot(iiX,iiY)
			for i in range(pPlot.getNumUnits()):
				pUnit = pPlot.getUnit(i)
				eTeam = gc.getTeam(pPlayer.getTeam())
				p2Player = gc.getPlayer(pUnit.getOwner())
				i2Team = p2Player.getTeam()
				if (caster.getOwner() != pUnit.getOwner() and eTeam.isAtWar(i2Team) and pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_MAGIC_IMMUNE')) == False):
					return True
	return False

def spellAIBanish(caster):
	doCast(caster)
	iX = caster.getX()
	iY = caster.getY()
	iRange = 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION1')):
		iRange = iRange + 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION2')):
		iRange = iRange + 1
	pPlayer = gc.getPlayer(caster.getOwner())
	for iiX in range(iX - iRange, iX + (iRange + 1), 1):
		for iiY in range(iY - iRange, iY + (iRange + 1), 1):
			pPlot = CyMap().plot(iiX,iiY)
			for i in range(pPlot.getNumUnits()):
				pUnit = pPlot.getUnit(i)
				eTeam = gc.getTeam(pPlayer.getTeam())
				p2Player = gc.getPlayer(pUnit.getOwner())
				i2Team = p2Player.getTeam()
				list = []
				if (caster.getOwner() != pUnit.getOwner() and eTeam.isAtWar(i2Team) and pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_MAGIC_IMMUNE')) == False):
					list.append(i)
				iRnd = CyGame().getSorenRandNum(len(list), "Maniac")
				[b]pUnit = p2Player.getUnit(list[iRnd])[/b]
				if isResisted(pUnit,caster,20) == False:
					CyInterface().addMessage(pUnit.getOwner(),True,25,'Unit Banished.','AS2D_LOSS_LATE',1,'Art/Interface/Buttons/Spells/Banish.dds',ColorTypes(8),pUnit.getX(),pUnit.getY(),True,True)
					pUnit.kill(True,0)

A python exception when I press the spell button:
 
Is something planned to be done about the fact that the AI switches to Pacificsm in the first fifty turns? It can be a powerful civic in some circumstances, true, but for the more warlike leaders, it can really cripple them. Those Civs that tend to adopt Pacificsm early ought to start with it as their main civic, I think.
 
Does anyone know what could be the problem with the line
pUnit = p2Player.getUnit(list[iRnd])
near the end?

Changing the line to pUnit = pPlot.getUnit(list[iRnd]) doesn't help.

I'm trying to create an alternative version of Banish for the AI in which a random enemy unit within range is killed.

Code:
def reqAIBanish(caster):
	if not canCast(caster):
		return False
	pPlayer = gc.getPlayer(caster.getOwner())
	iX = caster.getX()
	iY = caster.getY()
	iRange = 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION1')):
		iRange = iRange + 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION2')):
		iRange = iRange + 1
	for iiX in range(iX - iRange, iX + (iRange + 1), 1):
		for iiY in range(iY - iRange, iY + (iRange + 1), 1):
			pPlot = CyMap().plot(iiX,iiY)
			for i in range(pPlot.getNumUnits()):
				pUnit = pPlot.getUnit(i)
				eTeam = gc.getTeam(pPlayer.getTeam())
				p2Player = gc.getPlayer(pUnit.getOwner())
				i2Team = p2Player.getTeam()
				if (caster.getOwner() != pUnit.getOwner() and eTeam.isAtWar(i2Team) and pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_MAGIC_IMMUNE')) == False):
					return True
	return False

def spellAIBanish(caster):
	doCast(caster)
	iX = caster.getX()
	iY = caster.getY()
	iRange = 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION1')):
		iRange = iRange + 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION2')):
		iRange = iRange + 1
	pPlayer = gc.getPlayer(caster.getOwner())
	for iiX in range(iX - iRange, iX + (iRange + 1), 1):
		for iiY in range(iY - iRange, iY + (iRange + 1), 1):
			pPlot = CyMap().plot(iiX,iiY)
			for i in range(pPlot.getNumUnits()):
				pUnit = pPlot.getUnit(i)
				eTeam = gc.getTeam(pPlayer.getTeam())
				p2Player = gc.getPlayer(pUnit.getOwner())
				i2Team = p2Player.getTeam()
				list = []
				if (caster.getOwner() != pUnit.getOwner() and eTeam.isAtWar(i2Team) and pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_MAGIC_IMMUNE')) == False):
					list.append(i)
				iRnd = CyGame().getSorenRandNum(len(list), "Maniac")
				[b]pUnit = p2Player.getUnit(list[iRnd])[/b]
				if isResisted(pUnit,caster,20) == False:
					CyInterface().addMessage(pUnit.getOwner(),True,25,'Unit Banished.','AS2D_LOSS_LATE',1,'Art/Interface/Buttons/Spells/Banish.dds',ColorTypes(8),pUnit.getX(),pUnit.getY(),True,True)
					pUnit.kill(True,0)

A python exception when I press the spell button:

The list is always empty or has 1 element in it. You should cycle through all the units, building the list, then after that is all done pull a random unit from the list. So declare list before the for loops start. Build the list in the for loops. Then after the for loops are all done grab a random element.

Once you have that you will also want to check to make sure the size of list is greater than 0 before you pull a random element from it.
 
The list is always empty or has 1 element in it. You should cycle through all the units, building the list, then after that is all done pull a random unit from the list. So declare list before the for loops start. Build the list in the for loops. Then after the for loops are all done grab a random element.

Thanks. After reading your post some ten times I finally understood what the hell you were talking about. :mischief:

I present to you the first reliable Cylon detector err I mean AI 'targetable' spell:

Code:
def reqAIBanish(caster):
	if not canCast(caster):
		return False
	pPlayer = gc.getPlayer(caster.getOwner())
	if pPlayer.isHuman():
		return False
	iX = caster.getX()
	iY = caster.getY()
	iRange = 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION1')):
		iRange = iRange + 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION2')):
		iRange = iRange + 1
	for iiX in range(iX - iRange, iX + (iRange + 1), 1):
		for iiY in range(iY - iRange, iY + (iRange + 1), 1):
			pPlot = CyMap().plot(iiX,iiY)
			for i in range(pPlot.getNumUnits()):
				pUnit = pPlot.getUnit(i)
				eTeam = gc.getTeam(pPlayer.getTeam())
				p2Player = gc.getPlayer(pUnit.getOwner())
				i2Team = p2Player.getTeam()
				if (caster.getOwner() != pUnit.getOwner() and eTeam.isAtWar(i2Team) and pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_MAGIC_IMMUNE')) == False):
					return True
	return False

def spellAIBanish(caster):
	doCast(caster)
	pPlayer = gc.getPlayer(caster.getOwner())
	iX = caster.getX()
	iY = caster.getY()
	iRange = 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION1')):
		iRange = iRange + 1
	if caster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_EXTENSION2')):
		iRange = iRange + 1
	list = []
	for iiX in range(iX - iRange, iX + (iRange + 1), 1):
		for iiY in range(iY - iRange, iY + (iRange + 1), 1):
			pPlot = CyMap().plot(iiX,iiY)
			for i in range(pPlot.getNumUnits()):
				pUnit = pPlot.getUnit(i)
				eTeam = gc.getTeam(pPlayer.getTeam())
				p2Player = gc.getPlayer(pUnit.getOwner())
				i2Team = p2Player.getTeam()
				if (caster.getOwner() != pUnit.getOwner() and eTeam.isAtWar(i2Team) and pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_MAGIC_IMMUNE')) == False):
					list.append(pUnit)
	iRnd = CyGame().getSorenRandNum(len(list), "Maniac")
	pUnit = list[iRnd]
	if isResisted(pUnit,caster,20) == False:
		CyInterface().addMessage(pUnit.getOwner(),True,25,'Unit Banished.','AS2D_LOSS_LATE',1,'Art/Interface/Buttons/Spells/Banish.dds',ColorTypes(8),pUnit.getX(),pUnit.getY(),True,True)
		pUnit.kill(True,0)

Once you have that you will also want to check to make sure the size of list is greater than 0

Doesn't the spell requirement already assure that?
 
As a geek, I resent reinventing the wheel, so what I'm doing right now is trying to merge BetterAI with FFH. It is, as was to be expected, a shitload of work. I'm wondering if anyone before me has ever tried to do this?

Improving the AI in FFH goes beyond just adjusting weights in xml files and teaching the AI the new stuff FFH introduced; there's a lot to be done in terms of city governor and tech selection and so on. Starting from scratch just seems pointless to me if we can also use someone else's work. Right now I'm going through a number of compile errors after resolving all the conflicts SVN found (not all of them were very clear, so I might have made some mistakes there as well). If someone wants to help me, or better yet, if someone's tried to do this before me, let's hear from you!

[edit] Nope, this is definitely too difficult for me; I got halfway through CvCityAI.cpp before I decided I'd have to give up. A complete merge would be wonderful but will probably not be possible without the involvement of someone familiar both with the FFH and the BetterAI source. I might try to bring over single functions in the future, but for now I won't do anything else with the BetterAI source.
 
well i suppose you could help a little bit by increasing the minimum number of unit allowing buildings that the ai wil build in the ffh editor so the ai might produce more advanced units more often
 
Back
Top Bottom