AI assistance requested for a Prophet mod

Thunderbrd

C2C War Dog
Joined
Jan 2, 2010
Messages
29,967
Location
Las Vegas
I have always wanted to mod the game so that Great Prophets initiate the first circumstance of a religion's spread. There are multiple reasons why, not the least of which that I feel this would put more control in the hands of the players (and even the AI) rather than luck and some small bit of strategy defining where Holy Cities are defined.

I haven't wanted to proxy out a building for this purpose because it doesn't feel like the 'right' way to go about this.

So, on this quest, I'd of course like to make this a player selectable 'option' and how THAT element will be added is another matter down the road.

So far, I've managed to find that I can add a spread function through the XML to the prophet for each religion. In combination with a successful SDK merge with a neat little programming routine I found in the FFH2 source files, I have been successful in modding the prophet to spread AND found religions by so doing (any religion may now be spread by any prophet and the first instance of such defines the Holy City.)

Now I'm working on the AI as there are no appropriate parallels in any other unit's programming. Using it like a missionary is possible... but a waste.

So I have studied the UnitAI file and have figured out how it works to some degree and would like a bit of assistance from those who really know what they're doing with programming to help guide me to a minor 'finish' here. This step will make the mod playable, if not hardwired and dedicated into the way the game plays by the .dll.

So, for all you SDK guys out there, perhaps you could help me to develop the code for this:
Spoiler :
my basic structure for my subroutine:

* are there any religions left to be founded?

* if so, is our favorite religion left to be founded?

* if so, set that religion to be spread

* (Otherwise, we must deterimine which of the religions that can be founded are best to found... perhaps this could be established with a new XML identifier on each religion to give them a tag to the flavors preferred by the various leaderheads. (this would be using a schema but I would have to figure out how to fix up a schema for religions that includes iFlavor))

Thus would begin by making flavorcheck (for a given available religion)= sum of all results from(Current Leader's flavor (default 1) x Available Religion's flavor (default 0) where Current Leader's Flavor is the same type as Religion's flavor)

Then would establish a hierarchy of religion choices, where equivalent values we don't care how it aligns up. Thus we set best religion = flavorcheck (highest valued religion)

Then establish that religion to be spread unless the result is 0 (the leader doesn't care about that religion at all) in which case we return false to the module as a whole.

* Then we simply push the spread mission for the selected religion, wherever the prophet has been placed and return true to return back to the subroutine for the prophet (where it simply returns and stops moving the unit that has been devoured by the 'mission' subroutine.)


This is the basic structure I'm looking to program. I could probably piece much of it together from other SDK examples, but any assistance could accelerate that process along nicely (and hopefully avoid problematic bugs along the way).

Make any sense?
 
It seems that you have most of it figured out already - even the part about where to place you code.
Do you have a priority for this for the prophet (i.e. where in AI_prophetMove() you intend to place this)?

I'm not sure how exactly to help you develop this, so I suggest you start by creating your function (AI_foundReligion() or something), add a call to it in the appropriate place in AI_prophetMove(), and start translating your logic to C++.

During the process just ask specific how-to questions, and I'd be happy to try and help you with them.
 
Yeah, all that you need now, Thunderbrd, is some C++ programming skill. You might wanna study the subject first and move forward with this project only afterwards. As with any programming, its mostly just a matter of logic. The CivIV specific help you need should be easy enough for any SDK modder to supply - like what function and what values to use - but unless you know how to code the actual logic it would be a matter of asking someone else to do the work for you. (Not that it would be a bad thing, but there are no guarantees with unpaid labor.)
 
Do you have a priority for this for the prophet (i.e. where in AI_prophetMove() you intend to place this)?
Yes. Here. This should make it so it will check to see if it can build a shrine first, THEN move on to founding a religion as the next highest priority, right?
Spoiler :
void CvUnitAI::AI_prophetMove()
{
PROFILE_FUNC();

if (AI_construct(1))
{
return;
}

if (AI_foundReligion())
{
return;
}


if (AI_discover(true, true))
{
return;
}

if (AI_construct(3))
{
return;
}

int iGoldenAgeValue = (GET_PLAYER(getOwnerINLINE()).AI_calculateGoldenAgeValue() / (GET_PLAYER(getOwnerINLINE()).unitsRequiredForGoldenAge()));
int iDiscoverValue = std::max(1, getDiscoverResearch(NO_TECH));

if (((iGoldenAgeValue * 100) / iDiscoverValue) > 60)
{
if (AI_goldenAge())
{
return;
}

if (iDiscoverValue > iGoldenAgeValue)
{
if (AI_discover())
{
return;
}
if (GET_PLAYER(getOwnerINLINE()).getUnitClassCount(getUnitClassType()) > 1)
{
if (AI_join())
{
return;
}
}
}
}
else
{
if (AI_discover())
{
return;
}

if (AI_join())
{
return;
}
}

/************************************************************************************************/
/* BETTER_BTS_AI_MOD 08/20/09 jdog5000 */
/* */
/* Unit AI, Efficiency */
/************************************************************************************************/
//if ((GET_PLAYER(getOwnerINLINE()).AI_getPlotDanger(plot(), 2) > 0) ||
if ((GET_PLAYER(getOwnerINLINE()).AI_getAnyPlotDanger(plot(), 2)) ||
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/
(getGameTurnCreated() < (GC.getGameINLINE().getGameTurn() - 25)))
{
if (AI_discover())
{
return;
}
}

if (AI_retreatToCity())
{
return;
}

/************************************************************************************************/
/* BETTER_BTS_AI_MOD 09/18/09 jdog5000 */
/* */
/* Unit AI */
/************************************************************************************************/
if( getGroup()->isStranded() )
{
if (AI_load(UNITAI_ASSAULT_SEA, MISSIONAI_LOAD_ASSAULT, NO_UNITAI, -1, -1, -1, -1, MOVE_NO_ENEMY_TERRITORY, 1))
{
return;
}
}
/************************************************************************************************/
/* BETTER_BTS_AI_MOD END */
/************************************************************************************************/

if (AI_safety())
{
return;
}

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


I suggest you start by creating your function (AI_foundReligion() or something), add a call to it in the appropriate place in AI_prophetMove(), and start translating your logic to C++.
So I could then go down to around the point where we have these going on,
Spoiler :
bool CvUnitAI::AI_spreadReligion()
{
PROFILE_FUNC();

CvCity* pLoopCity;
CvPlot* pBestPlot;
CvPlot* pBestSpreadPlot;
ReligionTypes eReligion;
int iPathTurns;
int iValue;
int iBestValue;
int iPlayerMultiplierPercent;
int iLoop;
int iI;
and begin another function in much the same way...
Spoiler :
AKA.
bool CvUnitAI::AI_foundReligion()
-declare variables-
-program above logic into function-


Does it matter where it lies among the various functions as such or can you just plop it in anywhere provided its doen with due respect to the the last function's last }? I'm thinking you can and I'm wondering if that's a mistake.

Literally, it is the process of translating the logic above into C++ coding that I was looking for some assistance with as there are many aspects of C++ that I'm finding a little confusing... still, I suppose working my way through some original coding might be the best way to earn some confidence in what I do know and continue to find out what I don't

During the process just ask specific how-to questions, and I'd be happy to try and help you with them.
Ok... I'll try to come up with a prototype but before I try to compile the thing, I'll ask if anyone sees any trouble on the horizon. Additionally, if I'm confused by something in particular, I'll ask.

One of the things I'm finding hard to wrap my head around is that I can't understand this program by reading it from the beginning... that part of the code is hidden. So if I'm to understand this correctly, files like these are called to by the main (hidden) processing code when it becomes necessary to make checks like these unit ai's, right?

@Baldyr... yeah, I'm trying where that's concerned. I'm not understanding everything I'm reading because I get lost in some of the terminology and some of their examples don't seem to register HOW that works that way. I suppose I'm just being a bit impatient to understand it all is my biggest struggle.
 
@Baldyr... yeah, I'm trying where that's concerned. I'm not understanding everything I'm reading because I get lost in some of the terminology and some of their examples don't seem to register HOW that works that way. I suppose I'm just being a bit impatient to understand it all is my biggest struggle.
I can only speak for my own methodology, but once I get over the compilation threshold I'm looking to read up on C++ in order to understand how it works. I basically wanna know what every single file does - and what every single character in there is for. I basically wanna be able to read any function definition and be crystal clear on every part of it. If there is some element to it that I don't understand I won't be happy.

Sure, it will take a while to practically decipher the entire SDK and I will most certainly be asking a lot of stupid questions along the way. But just imagine what I will be able to do once I start the actual modding. :eek: The upside is also that I might not have to invest much more time on all this theoretical learning than you or anyone else is spending on "learning by doing" and copy-pasting bits and pieces of other people's code. :D Also, while you will still have numerous gaps in your knowledge - accepting certain things as they are simply because you haven't had any reason to figure them out yet - I will be on my way to being able to break the form and design my very own code from scratch.

At this point I think that we would both be helped by getting hold of some good entry-level reference material on the language. The bits we don't need is the compilation nonsense and probably file I/O operations (the game is already equipped in this regard). But practically anything else should hold some practical potential. Any suggestions?
 
I've been working to try to understand this. I understand perfectly until things start getting fuzzy on Functions II and really goes south when it starts talking about Pointers and I've developed a massive headache somewhere around Classes and it just gets worse from there (though there are brief moments where I understand things beyond that...). I guess part of my problem is not being patient enough to really memorize all the farious functions enough to quickly understand what they're saying in some of they're examples. Recursivity blows my mind too... the concept makes sense but the way its applied doesn't seem to for me.

As a side note, I AM trying to make sure I understand how the codes I'm working with are working... but being able to read something and write something are two different things. Are there some gaps in understanding how something works when I can yet read it and understand WHAT it's saying to do? Yeah... but I figure I'll grow to understand the more I work on them. I am in the middle of trying to decipher portions of code line by line and it is helping but sometimes I run across things I can't answer for.

For example, :: . Classes really bug my head. They don't seem to be operating in the same way that its being explained in that tutorial, but then, that's possibly because this is such a multi-filed beast, not one linear program. Nevertheless, I CAN understand that they are being called to in previous portions of code. What disturbs me is not knowing if I can just insert a new one or if I'm going to have to define it elsewhere.

And, too, I'm not quite certain how the game is referencing the XML files... some of that is a mystery to me as well. I suppose if I had a better understanding of this that'd help too.
 
As Asaf said - if you try to pinpoint what you're missing he'll be able to explain that and put it into a CivIV context - probably with sample code to illustrate the implementation.

Since I already have a grasp of classes, pointer and recursion and whatelse from learning Python during the last year, I'm probably somewhat equipped to tackle this myself. But since Python is the only language I know, there will of course be disadvantage also, as I'm used to things working in a certain way - when they really don't in C++. :p

I suppose I could try to do this in tandem with you and we'd be able to figure this out together. But since Asaf has already expressed his intentions I might just hang on for the free ride! :king:

edit: I read the first chapter of the tutorial and it felt comfortable enough. All I need is some free time and I'll be able to wrap this C++ language up. ;) Unfortunately the immediate/foreseeable future might prove to be far too hectic for me. But then again, I'm hell-bent on structuring my life in a way that gives me periods of actual free time in the end. (I'm talking spending winters somewhere warm, hacking away at the laptop in the shade with a cool drink within reach.) And since I now have something to really look forward to, I'll be working that much harder to get there. :D But I might also commit to studying C++ as a pass time while I work towards my goal of SDK modding - instead of being far too busy doing the Python thing, which I really don't have time for anyway.
 
You know what we aughta do if we're serious about this Baldyr... set up a thread here to put a given Source file under analysis. As we work our way through each line, achieving an understanding of the code down to the slightest detail and expressing that understanding, it might be very enlightening to anyone who follows.

But then, we'd need to be working on the exact same source files, so it'd be best to focus on the core I suppose, though some of the mods introduce some great programming examples.
 
(See my edit in the last post, above. While the idea has its merits, it might be a while before I get to any dissecting the SDK. But I will make an effort to figure the compilation thing out, eventually. Because until I do, I don't even wanna know any C++. :rolleyes:)
 
once I get over the compilation threshold

Sorry. No more excuses for you on this subject :mischief:

Yes. Here. This should make it so it will check to see if it can build a shrine first, THEN move on to founding a religion as the next highest priority, right?

Right.
I noted that you base your code on BBAI. Is it BBAI only or is it a mod which includes it? which version? (It'll be helpful if I know this)

So I could then go down to around the point where we have these going on,
Spoiler :
AKA.
bool CvUnitAI::AI_foundReligion()
-declare variables-
-program above logic into function-


Does it matter where it lies among the various functions as such or can you just plop it in anywhere provided its doen with due respect to the the last function's last }? I'm thinking you can and I'm wondering if that's a mistake.

It doesn't matter. But note that you also need to declare this in the header file (CvUnitAI.h) as part of the class declaration. Just add it next to the declaration of AI_spreadReligion, for example.

You can read about header files here.

Ok... I'll try to come up with a prototype but before I try to compile the thing, I'll ask if anyone sees any trouble on the horizon. Additionally, if I'm confused by something in particular, I'll ask.
I suggest that you first compile. Do it before you make any changes.
Then start making your own changes.
I recommend using version control to be able to track your changes (see in my signature), but it takes a little (really little) effort to setup. I still think it could save you a LOT of time in the long run.

One of the things I'm finding hard to wrap my head around is that I can't understand this program by reading it from the beginning... that part of the code is hidden. So if I'm to understand this correctly, files like these are called to by the main (hidden) processing code when it becomes necessary to make checks like these unit ai's, right?
Yes, but most of the AI calls are internal in the DLL.
For example, your code is called in this chain:
Code:
CvGame::update()
CvGame::updateMoves()
CvPlayerAI::AI_unitUpdate()
CvSelectionGroupAI::AI_update()
CvUnitAI::AI_update()
CvUnitAI::AI_prophetMove() (since the current AI type of the unit is UNITAI_PROPHET)
 
K... didn't quite get that last bit but I THINK I understand the part about the header file... and I THINK I have already done this.

Additionally, I don't want to even try to run any programming here, or compile any, without already knowing its going to work (or at least having a really good idea that it would). Where you mentioned compiling the project, I must admit, all I seem to know how to do where that's concerned is come up with a final .dll from all source codes at once. I've read some things about compiling a particular source file but I don't understand this concept... other than to guess that it just means you're test running the code. I must admit, I'm only modifying these source code files in Notepad++ and have not attempted to try to get to know any programs made for this purpose.

I'm going to embarrassingly expose just how lost I really am where it comes to coding here. I KNOW this won't work for so many reasons, but perhaps its enough of a structure that some direct feedback will allow me to put the right variables and calls in place in the right manner. Here's the prototype I worked up... ugh...

Spoiler :

Code:
bool CvUnitAI::AI_foundReligion()

	int relfounded;
	int iI;
	int iR;
	int religval;
	ReligionTypes eReligion;
	
	relfounded = 0;
	eReligion = NO_RELIGION;
	
	for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
	{
		if (isHasReligion((ReligionTypes)iI))
		{
			relfounded((ReligionTypes)iI))=1;
		}
	}
	
		if (relfounded((ReligionTypes)iI))=0);
		{
			if (((ReligionTypes)iI)=GC.getLeaderHeadInfo(getLeaderType()).getFavoriteReligion())
				{
				eReligion=((ReligionTypes)iI)
				}
			for (iR = 0; iR < GC.getReligionInfos(FlavorCount); iR++)
				if (GC.getReligionInfos.getFlavorType())=(getLeaderType()).getFlavorType()))
					{
					religval(((ReligionTypes)iI)iR) = 		(GC.getReligionInfos.getFlavorType(FlavorValue))*(getLeaderType()).getFlavorType(FlavorValue))
					}
			if ((ReligionTypes)iI)iR) > ((ReligionTypes)iI)--iR)
				{
				eReligion = ((ReligionTypes)iI)iR)
				}
		}
	if (((((eReligion)iI)iR))>0)
		{
			getGroup()->pushMission(MISSION_SPREAD, eReligion);
			return true;
		}
		else
			return false;

Terrible... I know. Shows how much I don't know. It's like a mutated, bulbous stick figure trying to pass as a portrait. But maybe it can be carved down to something real, no?
 
If your code had indentation (tabs, basically) you should use
Code:
 tags to display it. If your code really is all that bad (I wouldn't know) it sure looks miserable now. :p
 
ah... thanks... was wondering how to get the tabs to come through. I'll be editing this thing as I go, trying to perfect it one piece at a time as I learn.
 
First a general comment:
You cannot (and I really cannot stress that enough) write C++ code without frequently compiling it, especially if you're not an expert C++ programmer (and even then when you write more than just minor functions).
Half of the point in compilation is that there is something that checks your code for you, at least in some sense.
And you definitely cannot learn C++ without compiling your code all the time, read the compiler errors (and you will have plenty of them) and learn how to solve them.

So I suggest you setup the compilation process, use the BBAI as code base (or whatever code you're using), compile it, and only then start making changes to the code.
And compile frequently.
When you compile after you only changed one .cpp file, only this file will be recompiled. If you change a header file (.h), only those files which include this file will be recompiled (or those which include them etc.).

I can already see some errors in your code, but the compiler can point most of them to you, and that will make you understand better the code you're trying to write, and therefore write better code.
 
This constant compiling is bound to make C++ programming tedious at best. Learning will also take time. :p But on the other hand, I guess it teaches you to be more careful and really make sure your code is in order before trying it. Right now I can easily write several thousand lines of Python without ever running the code. :lol:
 
This constant compiling is bound to make C++ programming tedious at best.

You should be aware of the fact, that the minority of all programming languages are script languages ;).

Right now I can easily write several thousand lines of Python without ever running the code. :lol:

I can, too. Gives me only around 2 errors per line of code.
 
I can, too. Gives me only around 2 errors per line of code.
I probably get one error for every 50 lines of code, or something. But I do go over it again and again before testing. I do however not really consider debugging a part of the process of writing code - that comes afterwards and if I know my code inside and out it is most of the time easy to fix those little mistakes.

There's nothing quite like firing up some 500 lines of code for the first time - and have it work right away! Well, mostly anyway. :lol:
 
Ok, well, I'll work on installing (or finding what I've installed already) a compiler and working to understand how to do that. It seems a bit silly to me to insert any of this coding for a compile until I know I've sorted out the parts I'm just putting in there for self reference into what should be a workable code. If I thought the prototype might have a chance of working, I'd try to compile it. It would be like relying on a spellchecker otherwise... a useful tool, but still prone to unexpected results as it may read your 'There' as being just fine but you really should've used 'their'.

The compilation works much like Access's compiler I presume?

Ah, I forgot to mention, this sourcecode comes from the most recent AND source available and is intended for a C2C modmod.

Also, I can understand not pinpointing anything specific in there and offering any advice on how to fix it. It's very convoluted at the moment. I'm working through some of the tutorials and taking the time to get to know what is being said and where things are actually calling out to and what every symbol means etc... so I'll be there eventually, and hopefully soon.
 
You might wanna start small with your function. Define it and give it a return value. Compile. Test. Then add one conditional statement, have it change the return value. Compile. Test. Now you're doing it, so add one more if clause to see if you can get it right. Compile. Test.

Repeat this process until you finish your first functioning function. After that it should be smooth sailing going forward. :goodjob:
 
You might wanna start small with your function. Define it and give it a return value. Compile. Test. Then add one conditional statement, have it change the return value. Compile. Test. Now you're doing it, so add one more if clause to see if you can get it right. Compile. Test.

Repeat this process until you finish your first functioning function. After that it should be smooth sailing going forward. :goodjob:

Sounds about right :D

Ok, well, I'll work on installing (or finding what I've installed already) a compiler and working to understand how to do that.

Use my tutorial (in my signature). Setting up the environment shouldn't be difficult.

It seems a bit silly to me to insert any of this coding for a compile until I know I've sorted out the parts I'm just putting in there for self reference into what should be a workable code. If I thought the prototype might have a chance of working, I'd try to compile it. It would be like relying on a spellchecker otherwise... a useful tool, but still prone to unexpected results as it may read your 'There' as being just fine but you really should've used 'their'.

I think that you're under the impression that the compiler is just like a spell checker.
In some ways it might be, but keep in mind that you don't know the language ;)

Note that it does much more than checking that you have spelled your variables correctly, or that you've defined them at all.
It checks for types, it checks for validity of expressions, it checks whether functions you call exist at all and whether they exist in the specific context in which you try to call them (e.g. whether a function belongs to a class).

And in addition to checking it also translates your code to machine code so you can test it. As Baldyr said, it is probably better to write your function in parts and see if each part works before going forward, unless you know how you want everything to look, and then just compile during the writing.

Of course the compiler will not check your logic. That's the programmer's job, and luckily you can also test to see if it actually works.

The compilation works much like Access's compiler I presume?

I'm not sure what you mean. It does the same job in the sense that it turns something into a runnable program. In any other way it's probably different.

Ah, I forgot to mention, this sourcecode comes from the most recent AND source available and is intended for a C2C modmod.

In this case, you should use it as the base code like this:
After setting up the environment, copy the original BTS (3.19) files to your working folder (the same folder with the makefile and the Visual Studio project files).
Then copy the AND source files - *.cpp, *.h, *.inl (if they exist), CvGameCoreDLL.rc (if exists) - over these files.
The reason is that not always you have all the required source files in a mod, since sometimes only the source files that were changed are included.
Be careful not to override the makefile or the VS files.

Also, I can understand not pinpointing anything specific in there and offering any advice on how to fix it. It's very convoluted at the moment. I'm working through some of the tutorials and taking the time to get to know what is being said and where things are actually calling out to and what every symbol means etc... so I'll be there eventually, and hopefully soon.

I'm sorry. If you think it'll be more helpful I can try, but It's not always that simple looking at the code and figuring out what's going on with it. That's where the compiler is very handy.

Spoiler :

Just as an example - the following statement would not have compiled:
Code:
relfounded((ReligionTypes)iI))=1;

This is because relfounded is of type int, and you cannot apply parentheses to this type - it is undefined.

It seems that you want an array there, but accessing it and defining it is different than what you've written.


BTW, I re-read your original post and I understand that you already merged some SDK code, compiled it and tested that it works?
Then why not using that as base and go on from there?

In case I misunderstood you and you didn't - I suggest you first implement the non-AI functionality, and only then (after you've seen that it's working) add the AI, since the AI will need to use this functionality anyway.
 
Back
Top Bottom