Adventure AI

I’ve fixed this issue (not needed path removing leading to crash) :slight_smile:
(r1054)

I’ve given you full subversion access.
You should probably add your code to the General AI subproject of the GeniusAI (unless you want to create separate AI project).

There are two special functions that are always called in the new thread: yourTurn and activeStack (called during battle when your stack receives turn). All other functions are called from event handling thread of client.

There is one possible difficulty: callback calls (like cb->moveHero) return immediately after sending request to the server. The request is then realized (if possible) and AI receives calls about its result (like heroMoved). When server ends handling request it sends appropriate info and AI receives call to requestRealized.

Game data should not be checked/read until the package is fully applied. So eg. reading hero->movement just after calling cb->moveHero(hero, path) is not safe. Path may be or (more probably) may be not updated. There is also a risk of conflicts between threads (eg. heroMoved and yourTurn modifying same data).

If you want (it will be probably needed) I can write some code that will make possible calling cb methods and waiting until request is fully realised before returning.

Yeah, that would be great.

In addition, I am curious about something. The class ICallback is pure virtual, and it seems that the only inheriting class is CCallback. CCallback has more goodies, for example getTownInfo() which returns a vector, and is missing from ICallback.

I assume that the AI is only given access to ICallback because there is information that could potentially be used to cheat, (i.e. a CGameState *, ) in CCallback. But something makes me think that I might need access to some of these.

Also, while it is an important goal to make the AI play honestly, it might be nice if the AI could see the entire map, and maybe other things too (just in the early stages of coding).

Also, is there a way to get a vector of visitable, visible objects on the map? The thought of checking every visible tile every turn is chilling.

Wonderful.

One issue with adventure AI involves the following code.

void CGeniusAI::battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side)
{
	assert(!m_battleLogic); //************** assert fails when AI starts a battle. ***************
	m_battleLogic = new BattleAI::CBattleLogic(m_cb, army1, army2, tile, hero1, hero2, side);

	MsgBox("** CGeniusAI::battleStart **");
}

The assert fails, and the game crashes. I can’t figure it out because the only way the assertion can fail is if the same CGeniusAI has it’s battleStart() called twice before a battleEnd().

Since at the moment the AI motions are deterministic, this can be reproduced with the following save.

I have commited the AI code rev 1056.
assert.zip (79.8 KB)

If any public method of CCallback is missing from ICallback, feel free to add it.

It shouldn’t be necessary (and possible). AI and GUI should take all their information by Callback and call-ins.

AI should also be able to cheat by sending the cheat code via cb->sendMessage :wink:

There is no such info stored in the game. Adding it to the Callback would have end with checking all visible tiles (or all objects) anyway.
I’d suggest to store vector of visitable objects in the AI. You can check all visible tiles only on first AI initialization, then you can update it on tileRevealed calls. (And objectREmoved call that I’ll soon add).

Complex issue.
It’s generally caused by AI trying to move hero after stepping on monster that ends with stepping on it again and starting duplicate battle.

AI actions should be blocked when battle is triggered and AI must be informed about this. I’ll implement it within next few days. By that time AI can only avoid monsters (you can check if object with ID == 54 is present on the destination tile).

Is there a simple way to tell what race a town/hero is given CGTownInstance and CGHeroInstance?

CGTownInstance *t;
t->subID is the town type h3maparchives.celestialheavens.c … rmat_t.htm
t->town contains information about that type (always t->town->typeID == t->subID).

CGHeroInstance *h;
h->subID is the hero type: h3maparchives.celestialheavens.c … rmat_h.htm
h->type->heroType is enum with hero classes, its values follow h3maparchives.celestialheavens.c … mat_hc.htm

Is it possible to make this a priority, I’ve sorta hit a roadblock, and my current solution is kludge.

This too, I now must recreate the vector every time the AI moves a hero. (sloooow)

Done. However I haven’t tested my changes thoroughly, so if you encounter any problems, let me know.

  1. ICallback has waitTillRealize flag. By default set to false. When true, callback calls before returning wait till request is realized. So after moveHero hero (if move succeeded) will be guaranteed to be on the destination tile, his movement points will be updated, all object interaction calls over. The only exception is battle -> described below.
    waitTillRealize doesn’t work with calls answering to dialogs (is ignored, no realization info available).

  2. I’ve added to the AI m_state which wraps BattleState enum saying if this AI player is engaged into battle. Possible values:

  • NO_BATTLE
  • UPCOMING_BATTLE - triggered during hero movement handling (before returning heroMoved if waiting for realization). When set, AI cannot move hero (and generally should refrain from taking any actions). battleStart call should come soon.
  • ONGOING_BATTLE - set when battle actually starts. AI use cb to get info about battle and take only battleactions.
  • ENDING_BATTLE - battle is over but it’s result has not been saved yet (so we can still read data about army that is to be removed). When results are stored, m_state returns to NO_BATTLE.

Moving hero may look like that:

m_cb->moveHero(h,dst);
//check if movement triggered battle
if(m_state.get() != NO_BATTLE)
{
	//wait for battle end
	m_state.waitUntil(NO_BATTLE);

	//wait over, battle over too. hero might be killed. check.
	if(m_cb->getHeroSerial(h) < 0) 
	{ 
		//stop movement
	}
}
  1. I’ve added objectRemoved call-in to CGameInterface. In junction with tileRevealed and heroMoved it should be enough to update visitable objects vector without recreating it.

Hey, how hard would it be to create a mode in which all players are AI?

It would make the process of making the AI good much easier.

It is easy but without providing in-game GUI. So you would have to track the progress of the game via console / logfile output or debugger.
Otherwise it won’t be easy. I can still write this but it would need some time.

At least for now I wouldn’t need a GUI.

Not sure but maybe GUI would be nice in the future. Mostly just being able to see the map.

If it’s not too much trouble, it would be great if you would implement this for the console. Thanks.

I’ve added simple and dirty solution but it seems work fine.
Type “onlyai” command before starting game and all players will be set to AI.
Alternatively, to save typing, you may just init gOnlyAI (CMT.cpp l. 72) with false (but don’t commit that change).

It will be done eventually. I’m now working on partial restructuring GUI system to make it more independent from player interface. But it’s for improvements in pregame.
Hot-seat and in game GUI without plaer will be done in further future.

By the way: I must say you’re doing really great job! Many thanks for contribution and keep on! :slight_smile:

It’s nice to see someone working on the AI.

There’s a couple of compile issues left on linux. “gcc” is not as lenient as vc++. I have attached the compile log against rev 1091.
comperr.txt.zip (1.79 KB)

I looked a bit more at this issue. It turns out that “gcc” chokes on that code because a set iterator returns a
const. It’s simply not a good idea to modify an element in a set because the ordering could change, and thus the properties of the set become broken. The set should be replaced with something more appropriate, such as a vector if ordering is not necessary.

It’s not so much that the ordering is necessary, but the ability to search for duplicates in sub linear time. So a vector wouldn’t be appropriate. Perhaps a better data structure would be a hash set, but the STL still doesn’t have one of those.

Since the things being modified do not change the ordering, I do think it is ok for it to be std::set. Not sure, however how to fix the problem. Sorry.

Elements can be modified but it’s not allowed to make changes affecting the order of objects. AFAIK standard guarantees that set iterator is dereferencible to T& (not const), so gcc behaviour seems to be a bug. It’s may be justified by immutable order requirement but it limits usability of set.

The problem can be workarounded by const_cast to T& or by adding “mutable” specifier to fields we want to modify in set members.
My proposal is to:

  1. Make HeroObjective::whoCanAchieve mutable. Make HeroObjective::operator< independent from whoCanAchieve. (so our changes won’t affect set order - it can lead to problems also on MSVC. MSVC allows element modification but it’s still our responsibility to not modify order).
  2. Make AIObjectivePtrCont::AIObjective const.

But if you have other, better ideas, feel free to realize them :slight_smile:

Is there a way to get a player’s income for every resource? It would be useful for the AI to be able to conserve on resources it’s not getting much of.

Not really. Player get resource income from various sources (town structures, mines, hero skills, hero specialities) which are not unified for now.
My suggestion is to store resource stockpile before ending the turn and compare it with stockpile after starting next turn. Difference will be income.

Ok, I can do that easily enough for now, but:

  1. It would be nice if the AI was stateless in the sense that the moves it makes are not dependent on anything other than the current state of the game, and
  2. It would be nice if eventually the AI would know that it makes more wood if it just flagged a wood mine or, more obscurely, more mercury if it just recruited Rissa.

I’m going on vacation for a couple weeks, and I won’t have a computer with a compiler, so it will be a while before I will resume work on the AI. But don’t worry, it’ll happen.

In the mean time, the AI has approximate values for objects that give resources/gold, everything else gets a different random value every turn. I think this is the best state I can really leave it in. Hopefully it will make testing the AI possible.

Good luck, I can’t wait to see the progress made in my absence.

[size=75] Comment added by : Zamolxis: 22-08-2009, 12:49 ][/size]
[size=75]Enjoy your vacation! :wink:[/size]