God Engine - AI API for B&W2

Godplex

New member
Joined
Feb 12, 2011
Messages
25
Time for the GRAND unveiling of the project I've been working on over the weekend...

GOD ENGINE - A new API for programming robust, adaptive artificial intelligence into Black and White 2

The idea, and some of the implementation, was inspired by Daxter's God AI script.

So how does it differ from Daxter's original God AI?

It's been entirely rewritten from the ground up with a sexy new object-oriented design. This will allow the creation of MUCH more robust systems. God Engine is a full featured engine, rather than just a single AI, and it comes with an easy to use, high level API (Application Programming Interface) that allows ANYONE to take advantage of the engine's full power.

Sexy Engine Features:
- Capable of running many AI instances at once (without their data colliding)
- Capable of running many AI personalities at once
- Easily make new AI personalities with the simple API

Sexy API Features (MUCH more coming soon!)
- SUPER easy to use!
- Move the avatar (god hand) to an object with 1 line of code!
- Gather x number of resource from y object with 1 line of code! (then drop it off with 1 more)
- Cast x miracle on y object with 1 line of code!
- AI spends mana when casting miracles
- AI regenerates mana based on their worshipers (customizable)

As of this writing, all of the above have already been implemented; however, I don't feel it's quite complete enough to release. Also, I don't even know if anyone will use it being that the modding community is dead... but either way, I'm having a damn good time working on it! If one person ends up using it and liking it then it will have been worth my time (even if that one person is just me.)

I think I'll use this thread as a mini-blog about the development of God Engine, more updates coming soon.
 
As a Java Developer by profession, I can appreciate the Object Oriented design alright.  Sounds spiffy :D .
 
1:00 am Update:

Just finished the first AI ever coded with God Engine. I call it Bimbo.

I haven't tested it yet, and I'm looking forward to hammering out at least a couple bugs. Shouldn't be too many. I've already tested all the GE methods in bite-sized unit tests.

Bimbo Features
- Gathers resources based on desire
^- Waters field if necessary
- Puts out fires in a given radius

Simple and sweet. Enough functionality to see how the GE methods I've implemented so far work in practice, rather than just in unit tests.

Here goes...

EDIT: Java, cool  :cool: that reminds me I need more coffee...
EDIT2: Hammered out all bugs except for firefighting ability, will fix later.
 
Return Values.

In order for encapsulation to work, scripts need to be able to output various data. That's easy enough to do using global variables, granted that only one instance of your script will be executed at a time. God Engine, however, is designed to run 2, 3, 10 instances of AI at the same time, so I've implemented some clever tricks to make it thread-safe.

For starters, I made system for a language with no native return support, and ambiguous reference param support (can't tell if something is an object pointer or a value just by looking at it) to return values to their calling method safely. The returning method simply calls the script "geReturnVal(MyOutput)" when it wants to return something. The geReturnVal method will then check if the global variable I'm using for return values is locked. If it is locked, then it will wait until it's unlocked by the method wanting the returned value. Once unlocked, it will store the value and lock it once more (so that another geReturnVal call will not overwrite it before it can be read by the method that needs it.)

An example "get" using the API looks like this:
Code:
// This gets a unique identifier for a new god process, which is used
// behind the scenes to index into arrays in the God Engine.
run script geGetNewGodHandle(rTown)
godHandle = geReturnVal
run script geUnlockReturn

It seems a bit clunky to use 3 lines of code to access god data (mana, quantity of resource in hand, etc.) but it's (very nearly) guaranteed to be safe.

Another method I've tried is to use reference params... but like I mentioned above, they are ambiguous in challenge language. If godHandle is a number, the language will decide (on it's own) to pass it byval, which means it won't actually be returned to the function that needs it. When I tested this, all the handles were invalid and no god was instantiated. One way that this could work is if godHandle was explicitly set to some object, and then used one of the object's properties as a return value. It's a bit of a hack, but it could be made legible and neat with enumerated constants and clever naming. I may or may not experiment with this design option.
 
Hmm.  Thread management is always a tricky thing.

As you've kinda pointed out, that locking solution isn't iron-clad in that two threads unfortunate enough to call a "get" at the same time would potentially get a lock on the same parameter and cause unwanted behaviour.

Is there any way to create instances of the same object and pass them back up?

Alternatively, might I suggest that your global parameter could be a map of some sort.  Any "get" passes in it's own unique id.  The final value it wants is then retrieved from the global map using that id.

As I'm unfamiliar with the do's and don'ts of the scripting language, I'm purely speculating here, but hopefully, that may give you some ideas :) .
 
Those are awesome ideas, thanks!  :)
I've only ever worked on amateur coding projects, so I really appreciate the professional insight.

fenton_pat said:
Is there any way to create instances of the same object and pass them back up?
I'd love to do this, if I can figure out how challenge language passes it's data. It doesn't indicate whether the thing you're passing is a reference or a value.  There is an "address of" function mentioned in the documentation that supposedly gets the pointer of an object, but it generates a syntax error... I may be using it wrong, or the documentation may be off (there are little errors throughout the documentation, and some of the functions are noted as "hasn't been used in a long time, may not work". Maybe the address of function was removed and they didn't update it?)

I'm sure this will be clear when I have more experience with the language; I just started using it a few days ago after all.

fenton_pat said:
Alternatively, might I suggest that your global parameter could be a map of some sort.  Any "get" passes in it's own unique id.  The final value it wants is then retrieved from the global map using that id.
I'm going to give this a try, thanks for the idea.

fenton_pat said:
As I'm unfamiliar with the do's and don'ts of the scripting language, I'm purely speculating here, but hopefully, that may give you some ideas :) .
Your ideas are always appreciated  :)
 
API Update: God Build
- Have the god construct x building with 1 line of code
- Have the god repair x building with the same line of code

Bimbo Updates:
- Will construct any new buildings as they're placed
- Fire fighting now works properly

Building functionality took some effort to implement... both in GE and in Bimbo... If anyone knows an easy way to iterate through game constants, it would save me a lot of hassle and a few CPU cycles... Also if there is an easy way to look up build costs of buildings within code. (I entered all the building costs manually.) This is great news for the rest of you, though, because I created a method to get wood/ore cost of x object.

One note about repairing; it causes the buildings to come out weird... (they are technically complete but still look broken...) so I stopped having Bimbo repair stuff... but the functionality is there in case someone wants to make use of it.

It will probably not be much longer before I have a good prototype to demo, so stay tuned for updates.
 
Another irritating err in challenge language documentation:
10.2.7 get object disciple type

Gets the object disciple type of a villager

get MyVillager disciple type

My syntax:
Code:
villagerDiscipleType = get villagerToMod disciple type

Results? Grrrrr....  :angry naughty syntax error

I really need a way to check this, so my god hands don't keep re-casting their people over and over...

EDIT: Fixed
 
API Update
- Create a disciple of x type from random villager with 1 line of code
- Remove a disciple of x type with 1 line of code

Bimbo Update:
- Will now maintain an exact count of each disciple type

I think the project is about ready for an alpha release, and I'd like to get some feedback.

Wondering if any of you would like to try it? A few testers to offer suggestions and report bugs would be nice.

Alpha release will just be a .chl file for you to use with some map. Source code release will come later. If you would like to participate, let me know which maps you would like to use with the prototype.
 
I would be willing to help testing,

However - You would need to tell me how to install it, and how to 'use' / 'test' it :p

I myself have never used any custom map or other mods appart from skin / texture mods in bw2 :p
 
Godplex said:
If anyone knows an easy way to iterate through game constants, it would save me a lot of hassle and a few CPU cycles...

Again, I don't know anything about the script language, but in theory, if you could make up an array of those constants on script startup, then you could potentially loop through that array much easier any time you wanted as opposed to duplicating code on each of them.
 
fenton_pat said:
Again, I don't know anything about the script language, but in theory, if you could make up an array of those constants on script startup, then you could potentially loop through that array much easier any time you wanted as opposed to duplicating code on each of them.
The problem is that the game treats the game constants in a special way. They aren't treated as numbers, at least as far as you're allowed to script with them. For example, you can't set a normal variable (like an array index or value) to a game constant, and you can't pass them as params.

I actually did do something very similar to what you're suggesting, though. I made my own enums out of global variables and I use those when I want to pass them as params, or do something number/variable oriented. For example, my method geThrowMiracle takes a member of my own miracle type enum (ex. GE_MIRACLE_TYPE_HEAL coresponds to MIRACLE_TYPE_HEAL) but when it comes time to actually instantiate the miracle, only the game constant MIRACLE_TYPE_HEAL can be used, so I still have to have a seprate line for each one.

I wish I could just go "MyNewMiracle = create new MiracleTypeVariable at..." it would make things much simpler.

The best way I've found to deal with this is from a design perspective; try and hide away all the big, nasty, repeated-game-constant chunks inside the low-level implementation file (GodEngine.txt.) That way the actual AI decision-making-code file (Bimbo.txt) is quite tidy and malleable, without sacrificing any of its power.
 
Well.. I think you stumped into the point which makes BW2 a heck of a CPU hog..

Bad base coding.. I always assumed..
the way they handle most other things.. such as building types.. and other such.. Quite horrid and atrocious.
 
Speaking from a professional programmer's perspective, I think I can understand why they did what they did.

The code they are writing was probably to never see the light of day and was only ever implemented for the sole purpose of B&W2.  Therefore, it wasn't as important for it to follow best coding practices.

Normally, when time is of the limited variety, this is one of the first corners to be cut without suffering many consequences.  The developers of the script language only had to make sure the scripters understood the syntax and where it had it's limitations due to incomplete features.

If something didn't wind up being used in the original game, then it is far more likely it was incomplete in some fashion.
 
For sure.

B&W2 is a game that I followed zealously through its development, and Lionhead mentioned over and over that its philosophy was "it'll be done when it's done". Four years into development, that philosophy turned into "oh $hit, now we're out of money!" and the game had to be released with whatever features they could deliver in a polished state at the time. Now, poor Lionhead doesn't even have the rights to B&W3 anymore  :( and I believe they are a part of Microsoft now.

I would really love to have seen what they could have done with the game with another full year of development.

P.S.
Why is swearing broken on this site??? It always turns my awesome language into something lame... The server must be haunted by a scary ghost.
 
Godplex said:
I would really love to have seen what they could have done with the game with another full year of development.

You and me both buddy.

Godplex said:
P.S.
Why is swearing broken on this site??? It always turns my awesome language into something lame... The server must be haunted by a scary ghost.

Nope.  Twas just our sense of humour here.  We didn't want boring stars as some people like using the stars to still get swearing across.  So instead we went with substituting in silly words - starting with substituting in B&W-related terms, but then branching out from that for the more creature swearing.

I particularly like how we sensored the shit out of some popular fucking phrases :grin2! (see what I did there  :laugh2 )

I even recall one thread where people started getting creative with the substituted words.  Whole conversations started having two meanings - the dirty version and the plain daft one!
 
I like to fish with some nosehair! - No really!.. I do.
(I do love the filter, it keeps the site clean of profanity.)


Anyway, when can I go test something? :D
 
Sylcai said:
Anyway, when can I go test something? :D
Soon.

I'm just polishing up a few things for the (rough) prototype. For the past couple hours, I was trying to get the thing to throw properly, but plotting a trajectory on a planet whose gravity and air friction are a mystery is tricky. I believe that an object's density property plays a part in how much air friction it receives, but I don't know the range of values for that property (it could be between 0.0 and 1.0, or it could be 1 to OVER 9000!!!)

I believe that range ambiguity is also the key to the strange repair bug I mentioned before as well. I assumed that a building's health was from 0.0 to 1.0, like its built % property, but it may not be so. Looks like I'm going to have to experiment, because I need to know the max health of a building in order to calculate build cost & know when to stop repairing. 100 seems like a good number assume is the max, since that's the max health of platoons, but it still comes down to guess work. (It could be the number of materials needed divided by some x, in which case it would be tricky to figure out. Or it could be structure's volume for all I know, which would be... icky.)
 
i really hope this AI script is going to be releast. It sounds just like what i need for my map.
 
Exit2008 said:
i really hope this AI script is going to be releast. It sounds just like what i need for my map.
Released beta 0.9b for ye.

http://www.bwfiles.com/files/file.php?id=1333

It's been sitting on my computer for over a month, I just haven't uploaded it lol. I worked on it for about a week or two then started working on a tool to make things easier and more robust. After I made some headway on that, I made the mistake of trying the game Mount & Blade for the first time and I was out of commission for the next month  :D

If you want to use Bimbo, the bad (made for test, not fun) AI then all you have to do is call it in your main script sort of like this example:
Code:
run script InitAI
begin script InitAI
	oTown = get town with id 0
	2Town = get town with id 2
start
	// (Note that background script not required, it spawns its own thread anyway)
	run script bimNewBimbo(oTown)
	run script bimNewBimbo(2Town)
end script InitAI

but I would recommend making a custom one, using Bimbo as a template. See the readme, and see the Bimbo.

There's also a compiled script for those who just want to see what it does without writing code. See readme.

Good luck, have fun  :cool:
 
Back
Top