EggPlant AI ramblings
the vision thing...
I've been pondering over what my vision is for EggPlant...
I think one of my key goals for the AI should be to create something that's sort of a "human trainer" to get n00bs like me to try different game strategies and thus learn to play WZ in a more experienced way. Could an AI take a n00b and effectively train them to a point where they have a fighting chance in an MP game? At the same time, could an AI make single player skirmishes much more engaging for all levels of player?
I think one of my key goals for the AI should be to create something that's sort of a "human trainer" to get n00bs like me to try different game strategies and thus learn to play WZ in a more experienced way. Could an AI take a n00b and effectively train them to a point where they have a fighting chance in an MP game? At the same time, could an AI make single player skirmishes much more engaging for all levels of player?
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
-- https://warzone.atlassian.net/wiki/display/GO
-
cumandgetit
- Trained

- Posts: 103
- Joined: 06 Feb 2009, 04:02
Re: EggPlant AI ramblings
i think if you hit your target goals with eggplant it will definitely make for a far richer play experience than mp is at present.
will that convince dedicated mpers to change how they play mp ? i would have to say no.
could those dedicated mpers enjoy eggplant as you've concieved it ? - i would say yes, at least a sub-segment will.
would the majority that don't play mp and prefer campaign and skirmish go for it as you are creating it ? - for sure those folks will take to eggplant like a duck to water.
could eggplant as you are envisioning it right now serve as a trainor for MP, the way you have to play to win ? - no way. in order for it to serve as such a trainor for mp the very way it's played to win at this time you would have to revise your goals into something far less interesting and pretty much boring for many fans of the game.
so imho if you wanna make an mp trainor revise the current eggplant to that very specific end after this version is done & released.
basically i do not think that one eggplant can serve the two different audiences - dedicated mpers or wannabes and those who who do not give a chit about that mp experience, never will, and would die for the experience your current goals with eggplant would offer.
wz mp has presented pretty much the same very limited tactical experience since 1999, not to mention its online shortfalls, technical and otherwise.
if existing mp mechanics could support winning the way you are concieving eggplant's rich gameplay then wz would be a far better game, imho. there are somethings that could be done to that end but they are not in the works, so to speak. though with the new js api and the ability to more readily revise the gui, taken in tandem, well then those changes become more practical.
this is a complicated proposition as i see it. i hope i haven't made a jibber-jabber mess of it with my answer.
will that convince dedicated mpers to change how they play mp ? i would have to say no.
could those dedicated mpers enjoy eggplant as you've concieved it ? - i would say yes, at least a sub-segment will.
would the majority that don't play mp and prefer campaign and skirmish go for it as you are creating it ? - for sure those folks will take to eggplant like a duck to water.
could eggplant as you are envisioning it right now serve as a trainor for MP, the way you have to play to win ? - no way. in order for it to serve as such a trainor for mp the very way it's played to win at this time you would have to revise your goals into something far less interesting and pretty much boring for many fans of the game.
so imho if you wanna make an mp trainor revise the current eggplant to that very specific end after this version is done & released.
basically i do not think that one eggplant can serve the two different audiences - dedicated mpers or wannabes and those who who do not give a chit about that mp experience, never will, and would die for the experience your current goals with eggplant would offer.
wz mp has presented pretty much the same very limited tactical experience since 1999, not to mention its online shortfalls, technical and otherwise.
if existing mp mechanics could support winning the way you are concieving eggplant's rich gameplay then wz would be a far better game, imho. there are somethings that could be done to that end but they are not in the works, so to speak. though with the new js api and the ability to more readily revise the gui, taken in tandem, well then those changes become more practical.
this is a complicated proposition as i see it. i hope i haven't made a jibber-jabber mess of it with my answer.
Trainer AI
Hrm, that's given me another idea...
If the JS API exposes methods allowing AIs to communicate with other players (including AI players) then I plan on implementing a rich chat-command set in to EggPlant.
This would allow me to create a separate EggTrainer AI mod. A player wanting some training could set up a 3-player game (or just select a specific map with EggTrainer already in it) and EggTrainer would basically be a bunch of tutorial scripts. It would interact with human player and EggPlant player to run through various training scenarios.
EDIT: EggTimer would be a better name for the trainer AI
If the JS API exposes methods allowing AIs to communicate with other players (including AI players) then I plan on implementing a rich chat-command set in to EggPlant.
This would allow me to create a separate EggTrainer AI mod. A player wanting some training could set up a 3-player game (or just select a specific map with EggTrainer already in it) and EggTrainer would basically be a bunch of tutorial scripts. It would interact with human player and EggPlant player to run through various training scenarios.
EDIT: EggTimer would be a better name for the trainer AI
Last edited by aubergine on 19 Jan 2012, 06:22, edited 1 time in total.
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
-- https://warzone.atlassian.net/wiki/display/GO
Better sector navigation
I'm considering using partial functions (with pseudo-binding) to reference sectors - this will remove the need to do base36 encoding of row/column, avoid the grimness of multidimensional arrays, use static names for my sector objects and, finally, look really cool in the source code!
My Map AI will be getting a new method: .getSector(col,row) // returns a map sector object
The reason for allowing it to operate as a partial function is that it gives me a lot of flexibility as to how I iterate through sectors.
I mentioned that I was using pseudo-binding earlier... Unfortunately qtScript doesn't have full support for ECMA-262 Edition 5, so there is no Function.prototype.bind() method. As a result, I'm doing a little cheat in the code above by creating an enclosed reference (called 'map') because after the first calling of the .getSector() method the scope might change meaning that this != Map
Now, I can get a specific sector as follows (this is normal use of the method):
But what happens if I want to iterate through all sectors in column 5?
That works fine, but it's creating a new row function every time - we can cache that though, the revised code becomes:
Nice and readable, eh?
And of course we can iterate through a columns in a row:
We can also use this in sector objects, for example:
But what if we want to iterate though all sectors? Well, Map.sectors is an array and when sectors are first instantiated, this happens:
So as well as getting sectors by ID as shown earlier, we can iterate over the numerically-referenced array as an alternative:
Sorted!
My Map AI will be getting a new method: .getSector(col,row) // returns a map sector object
The reason for allowing it to operate as a partial function is that it gives me a lot of flexibility as to how I iterate through sectors.
Code: Select all
// col = x-axis sector units
// row = y-axis sector units
Map.prototype.getSector = function(col,row) {
var map = this; // important: we can't assume (this === Map) from this point onwards
if (!col) {
return function(col) {return map.sectors(map.getSector(col,row));}
} else if (!row) {
return function(row) {return map.sectors(map.getSector(col,row));}
} else {
var sector = col+"-"+row; // name of sector
return (sector in map.sectors) ? map.sectors[sector] : undefined;
}
}
Now, I can get a specific sector as follows (this is normal use of the method):
Code: Select all
var theSector = Map.sector(5,7); // get obj for sector at col 5, row 7
Code: Select all
// need to know how many sector rows (y-axis) there are:
var row = Math.floor(mapHeight % 20); // mapHeight (in tiles) defined by JS API, and sectors are 20x20 tiles
// and as I habitually iterate backwards through arrays, to iterate sectors in column 5 we do this:
var currentSector;
while (-1<--row) {
currentSector = Map.getSector(5)(row);
// do stuff with current sector
}
Code: Select all
var col = Map.getSector(5); // cache column 5
var row = Math.floor(mapHeight % 20);
var currentSector;
while (-1<--row) {
currentSector = col(row);
// do stuff with current sector
}
And of course we can iterate through a columns in a row:
Code: Select all
var col = Math.floor(mapWidth % 20);
var row = Map.getSector(undefined,7); // cache row 7 by marking col param as undefined
var currentSector;
while (-1<--col) {
currentSector = row(col); // note how the col,row order has swapped
// do stuff with current sector
}
Code: Select all
// in Sector class we prototype a method:
Sector.prototype.getAdjacentSector = function(direction) {
direction = direction.toLowerCase();
// this.col and this.row are defined during class instantiation
switch (direction) {
case "n" : return Map.getSector(this.col,this.row-1); // north
case "e" : return Map.getSector(this.col+1,this.row); // east
case "s" : return Map.getSector(this.col,this.row+1); // south
case "w" : return Map.getSector(this.col-1,this.row); // west
default: return this;
}
Code: Select all
Map.sectors = [];
var col = Math.floor(mapWidth % 20);
var row = Math.floor(mapHeight % 20);
var currentSector;
while (-1<--col) {
while (-1<--row) {
currentSector = new EPSector(col,row); // instantiation defines instance level .col, .row, .id, etc.
Map.sectors[currentSector.id] = currentSector; // create named reference to sector (.id in form c-r)
Map.sectors.push(currentSector); // push on to numerically-referenced array
}
}
Code: Select all
var sector = Map.sectors.length;
var currentSector;
while (-1<--sector) {
currentSector = Map.sectors[sector];
// do stuff
}
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
-- https://warzone.atlassian.net/wiki/display/GO
- Rman Virgil
- Professional

- Posts: 3812
- Joined: 25 Sep 2006, 01:06
- Location: USA
Re: Trainer AI
Bravo on that modular expansion.aubergine wrote:Hrm, that's given me another idea...
If the JS API exposes methods allowing AIs to communicate with other players (including AI players) then I plan on implementing a rich chat-command set in to EggPlant.
This would allow me to create a separate EggTrainer AI mod. A player wanting some training could set up a 3-player game (or just select a specific map with EggTrainer already in it) and EggTrainer would basically be a bunch of tutorial scripts. It would interact with human player and EggPlant player to run through various training scenarios.
EDIT: would be a better name for the trainer AI
Apt, I like.EggTimer...
Talk about me whistling Dixie in Yank country.cumandgetit wrote:.......
wz mp has presented pretty much the same very limited tactical experience since 1999, not to mention its online shortfalls, technical and otherwise.
if existing mp mechanics could support winning the way you are concieving eggplant's rich gameplay then wz would be a far better game, imho. there are somethings that could be done to that end but they are not in the works, so to speak. though with the new js api and the ability to more readily revise the gui, taken in tandem, well then those changes become more practical.
this is a complicated proposition as i see it. i hope i haven't made a jibber-jabber mess of it with my answer.
- Regards, RV
.
Progress update
Just a quick progress update...
After huge amounts of hacking around with my scripts, I've decided to create a data abstraction layer. I was just getting way too much duplicate or crufty code to be able to see the wood for the trees.
I've named the abstraction layer "VAULT" and once complete I will be releasing the code so others can use it in their AIs.
When the game starts, I start by getting a list of all structures and droids that I can see, plus player starting positions and oil resource positions. I put these in to the vault.
Whenever anything new gets detected, triggering eventObjectSeen(), that gets added to the vault as well. However, because eventObjectSeen() only notifies me of new stuff that I can see, and not what my allies can see (at least I think this is the case having looked at the C++ code), I also have a timer that occasionally triggers a scan of things my allies can see so that any missing stuff gets added to the vault.
I've taken great care to avoid putting stuff in the vault that my AI can't "see", because I want my AI to avoid cheating wherever possible. Obviously, some things (such a droids that were visible but have now moved out of sensor range) will still get in to the vault, but hopefully not too many.
When something gets destroyed, triggering eventDestroyed(), the corresponding object gets removed from the vault. However, eventDestroyed() only gets called when my stuff gets destroyed, not when enemy/ally stuff gets destroyed. This means that when enemy/ally stuff is put in to the vault I have to bind() to it in order to get notified when it's been destroyed.
The vault isn't just an array of game objects. That would be too simple, not to mention failing miserably because the data in the 'static' game objects would quickly be out-of-date.
When an object is put in to the vault, it's actually wrapped in a custom object written by me. As well as exposing all the expected properties of the game object, my wrapper also provides properties such as .isMy, .isEnemy, .isVTOL, etc., and useful methods such as .distanceFrom() and .canReach(), etc. These properties and methods ensure a much higher degree of consistency in my code, and make it a lot easier to read. Because most are getter/setter properties on the prototype chain, there's virtually no overhead when instantiating new objects. I've created wrappers for every kind of object, so that each object has meaningful properties and methods. For example, on an AREA object, I have a .center property (x,y of center point in the area) and so on.
The vault is extensible, so it's easy to define new wrappers, filters and so on. This means that if I want to extend objects in my main AI code, or add new types of custom object (such as a SECTOR = map sector), I can do that too.
Because game objects are static snapshots of an objects' state at a given game time (which can be determined from the gameTime global property exposed by the JS API), they will soon be out of date because the object moves, gets attacked, etc. So, my wrappers have a built in cache of the game object that's automatically invalidated if the gameTime has changed. When the cache gets flushed for an object, I get a fresh copy using (@Per: look away now!!) objFromId(). To avoid using objFromId() too much, I allow the cache to persist for a few milliseconds after the gameTime changes, freeing up some CPU cycles for the rest of my AI code to do it's stuff.
Anyway, the vault does more than just wrapping game objects and storing them. It also categorises them, making it easier for my code elsewhere to get the objects it's looking for. And, it maintains a primitive copy of items in a global "sessionStorage" object, allowing vault data to effectively persist through saving/loading a game.
Hopefully I'll be releasing the vault code by the end of this week so others can have a play with it.
After huge amounts of hacking around with my scripts, I've decided to create a data abstraction layer. I was just getting way too much duplicate or crufty code to be able to see the wood for the trees.
I've named the abstraction layer "VAULT" and once complete I will be releasing the code so others can use it in their AIs.
When the game starts, I start by getting a list of all structures and droids that I can see, plus player starting positions and oil resource positions. I put these in to the vault.
Whenever anything new gets detected, triggering eventObjectSeen(), that gets added to the vault as well. However, because eventObjectSeen() only notifies me of new stuff that I can see, and not what my allies can see (at least I think this is the case having looked at the C++ code), I also have a timer that occasionally triggers a scan of things my allies can see so that any missing stuff gets added to the vault.
I've taken great care to avoid putting stuff in the vault that my AI can't "see", because I want my AI to avoid cheating wherever possible. Obviously, some things (such a droids that were visible but have now moved out of sensor range) will still get in to the vault, but hopefully not too many.
When something gets destroyed, triggering eventDestroyed(), the corresponding object gets removed from the vault. However, eventDestroyed() only gets called when my stuff gets destroyed, not when enemy/ally stuff gets destroyed. This means that when enemy/ally stuff is put in to the vault I have to bind() to it in order to get notified when it's been destroyed.
The vault isn't just an array of game objects. That would be too simple, not to mention failing miserably because the data in the 'static' game objects would quickly be out-of-date.
When an object is put in to the vault, it's actually wrapped in a custom object written by me. As well as exposing all the expected properties of the game object, my wrapper also provides properties such as .isMy, .isEnemy, .isVTOL, etc., and useful methods such as .distanceFrom() and .canReach(), etc. These properties and methods ensure a much higher degree of consistency in my code, and make it a lot easier to read. Because most are getter/setter properties on the prototype chain, there's virtually no overhead when instantiating new objects. I've created wrappers for every kind of object, so that each object has meaningful properties and methods. For example, on an AREA object, I have a .center property (x,y of center point in the area) and so on.
The vault is extensible, so it's easy to define new wrappers, filters and so on. This means that if I want to extend objects in my main AI code, or add new types of custom object (such as a SECTOR = map sector), I can do that too.
Because game objects are static snapshots of an objects' state at a given game time (which can be determined from the gameTime global property exposed by the JS API), they will soon be out of date because the object moves, gets attacked, etc. So, my wrappers have a built in cache of the game object that's automatically invalidated if the gameTime has changed. When the cache gets flushed for an object, I get a fresh copy using (@Per: look away now!!) objFromId(). To avoid using objFromId() too much, I allow the cache to persist for a few milliseconds after the gameTime changes, freeing up some CPU cycles for the rest of my AI code to do it's stuff.
Anyway, the vault does more than just wrapping game objects and storing them. It also categorises them, making it easier for my code elsewhere to get the objects it's looking for. And, it maintains a primitive copy of items in a global "sessionStorage" object, allowing vault data to effectively persist through saving/loading a game.
Hopefully I'll be releasing the vault code by the end of this week so others can have a play with it.
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
-- https://warzone.atlassian.net/wiki/display/GO
Re: EggPlant AI ramblings
Just found this great blog post about the comma operator and thought I'd share: http://javascriptweblog.wordpress.com/2 ... -operator/
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
-- https://warzone.atlassian.net/wiki/display/GO
Re: EggPlant AI ramblings
Note that it can often be the case that units are destroyed outside your vision. It could be destroyed by another player (eg if game is 1v1v1), or recycled, or burned to death after leaving vision range. So tracking enemy units through fog of war is a somewhat difficult proposition, if you want to do so without cheating.
Re: EggPlant AI ramblings
Yup - that's the main scenario where my AI will effectively be cheatingPer wrote:Note that it can often be the case that units are destroyed outside your vision. It could be destroyed by another player (eg if game is 1v1v1), or recycled, or burned to death after leaving vision range. So tracking enemy units through fog of war is a somewhat difficult proposition, if you want to do so without cheating.
I'm currently performance tuning and simplifying my vault code - objects are cached with a TTL (time to live) of 1.5 seconds currently. I decided that I don't need exact x,y coords or health, etc., and that a lag of 1.5 seconds (ish) on updates for those values should suffice for my AI's needs. Also, objects in the vault cache are only invalidated if they are accessed - and for most of the time I'm only accessing a small number of objects. Aside from using up a bit more RAM, it should hopefully put less strain on the CPU overall (will test more fully once next Mac master is released).
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
-- https://warzone.atlassian.net/wiki/display/GO
Re: EggPlant AI ramblings
So, I've just started re-writing my VAULT library *again*.
I was previously categorising objects based on their type (eg. DROID, STRUCTURE, etc) and sub-type (eg. from properties like droidType, stattype, etc).
For example, if I wanted a list of all cyborgs, I'd do something like this:
var allCyborgs = VAULT[DROID][DROID_CYBORG];
Then I wanted a list of just my cyborgs, or just enemy cyborgs. And then I wanted them ordered by health or maybe distance from a specific location. I quickly realised that trying to categorise things in to separate lists was totally the wrong direction to take.
So, now I've got everything in one big list and a rudimentary way of filtering the list.
For a given "search" I wanted to ensure that the list was only iterated through once. Because some properties of objects (eg. .health and .x, .y, etc.) can trigger cache refreshes (calling objFromId()) the order in which the list is filtered (searched) is important.
The order of precedence currently goes something like this:
* Filter on static properties (id, name, type, subtype, etc) first - these will never trigger a cache refresh
* Filter on pseudo-static properties next - a pseudo-static property is something that can be kept up-to-date using events, and thus doesn't need to rely on the cached game object. For example, .player won't change without eventObjectTransfer() being triggered - so I can keep that up-to-date myself and avoid cache refreshes when accessing that property. By association, things like .isAlly and .isEnemy can also be treated as pseudo-static.
* Filter on basic dynamic properties (x, y, health, etc) - these might cause a cache refresh
* Filter on complex dynamic properties/methods (.nearestRepairFacility, etc) - these not only rely on basic dynamic properties (and can thus require cache refresh) but they also do a bunch of other stuff making them more "expensive" in terms of CPU usage
Once the list filtering is complete, I can perform any post-processing that's required:
* Apply sorting - in some cases I want to sort the list, eg. by health, position, or whatever.
* Apply culling of dead objects - if any cache updates triggered so far detected dead objects (eg. a droid that was destroyed but somehow not yet removed from the vault), those objects are trimmed from the list
* Apply trimming - in most cases I want to limit the number of results returned, eg. I just want the first 10 matching objects
However, I've not got that far yet. I'm trying to find a nice way to determine whether an object property is static, pseudo-static (which I'll probably end up treating as static), basic-dynamic or complex-dynamic. At first glance, it might seem trivial to do, until you realise that the VAULT is extensible - my wrappers for standard game objects can be extended, and completely custom object types can be added as well.
For example, I have a concept of "settlements" which is a custom object "type". There are then "sub types" such as "camp", "derrick", "outpost" and so on. The property/method classifications for a given type/sub-type combination will always be the same.
My current thoughts are to expose "getter" functions for each of the getter/setter properties (known as "accessors" in JS), and create function equivalents of data properties (which might actually be functions themselves). I could then define properties on a function to state what classification it has, and always access the functions instead of properties during "searches".
For example, .x is a basic-dynamic "accessor" property that uses getter/setter functions. If I expose my _getX() function, I can then do something like this:
I can easily write a helper function to make defining such things cleaner, but it just doesn't feel like a good way to be going about things. For example, what if I forget to add a classification property to an exposed getter? And what if I forget to expose the getter?!
I want the VAULT to not only be re-usable and extensible for other devs, I want it to be trivially easy to use as well. It should just be a case of a couple of include()'s (for util.js and vault.js) to get a functional VAULT that automagically keeps track of what the AI can see on the map. Extending the provided game object wrappers, creating custom object wrappers and doing searches should not require any voodoo.
*my brain is melting*
I'm also thinking that the VAULT would be a good place to have a play with jQuery-like selectors - enabling a "find stuff, do stuff with it" approach to scripting. Imagine something like:
Hrm, maybe instead of struggling with a search function I should just implement jQuery-like selectors?
I was previously categorising objects based on their type (eg. DROID, STRUCTURE, etc) and sub-type (eg. from properties like droidType, stattype, etc).
For example, if I wanted a list of all cyborgs, I'd do something like this:
var allCyborgs = VAULT[DROID][DROID_CYBORG];
Then I wanted a list of just my cyborgs, or just enemy cyborgs. And then I wanted them ordered by health or maybe distance from a specific location. I quickly realised that trying to categorise things in to separate lists was totally the wrong direction to take.
So, now I've got everything in one big list and a rudimentary way of filtering the list.
For a given "search" I wanted to ensure that the list was only iterated through once. Because some properties of objects (eg. .health and .x, .y, etc.) can trigger cache refreshes (calling objFromId()) the order in which the list is filtered (searched) is important.
The order of precedence currently goes something like this:
* Filter on static properties (id, name, type, subtype, etc) first - these will never trigger a cache refresh
* Filter on pseudo-static properties next - a pseudo-static property is something that can be kept up-to-date using events, and thus doesn't need to rely on the cached game object. For example, .player won't change without eventObjectTransfer() being triggered - so I can keep that up-to-date myself and avoid cache refreshes when accessing that property. By association, things like .isAlly and .isEnemy can also be treated as pseudo-static.
* Filter on basic dynamic properties (x, y, health, etc) - these might cause a cache refresh
* Filter on complex dynamic properties/methods (.nearestRepairFacility, etc) - these not only rely on basic dynamic properties (and can thus require cache refresh) but they also do a bunch of other stuff making them more "expensive" in terms of CPU usage
Once the list filtering is complete, I can perform any post-processing that's required:
* Apply sorting - in some cases I want to sort the list, eg. by health, position, or whatever.
* Apply culling of dead objects - if any cache updates triggered so far detected dead objects (eg. a droid that was destroyed but somehow not yet removed from the vault), those objects are trimmed from the list
* Apply trimming - in most cases I want to limit the number of results returned, eg. I just want the first 10 matching objects
However, I've not got that far yet. I'm trying to find a nice way to determine whether an object property is static, pseudo-static (which I'll probably end up treating as static), basic-dynamic or complex-dynamic. At first glance, it might seem trivial to do, until you realise that the VAULT is extensible - my wrappers for standard game objects can be extended, and completely custom object types can be added as well.
For example, I have a concept of "settlements" which is a custom object "type". There are then "sub types" such as "camp", "derrick", "outpost" and so on. The property/method classifications for a given type/sub-type combination will always be the same.
My current thoughts are to expose "getter" functions for each of the getter/setter properties (known as "accessors" in JS), and create function equivalents of data properties (which might actually be functions themselves). I could then define properties on a function to state what classification it has, and always access the functions instead of properties during "searches".
For example, .x is a basic-dynamic "accessor" property that uses getter/setter functions. If I expose my _getX() function, I can then do something like this:
Code: Select all
// expose getter function:
someClass.prototype._getX = function(){return this._obj.x;}
// Note: this._obj is also an accessor in which the getter will intelligently refresh the cached game object if it's too stale).
// state what classification the function has:
someClass.prototype._getX.isBasicDynamic = true;
I want the VAULT to not only be re-usable and extensible for other devs, I want it to be trivially easy to use as well. It should just be a case of a couple of include()'s (for util.js and vault.js) to get a functional VAULT that automagically keeps track of what the AI can see on the map. Extending the provided game object wrappers, creating custom object wrappers and doing searches should not require any voodoo.
*my brain is melting*
I'm also thinking that the VAULT would be a good place to have a play with jQuery-like selectors - enabling a "find stuff, do stuff with it" approach to scripting. Imagine something like:
Code: Select all
const TILES = 128; // 128 pixels per tile
const SECTOR_SIZE = 20; // 20 tiles per sector
const SECTORS = (SECTOR_SIZE * TILES);
var $ = VAULT; // think of this as root of a DOM
eventAttacked(victim,attacker) {
// does victim need repairs?
$(victim).healthBelow(50).getRepaired();
// attack with VTOLs if possible
$("[subtype=vtol]:my:not([order=attack])") // my vtols, that aren't already attacking something
.healthTo(80) // must be at least 80% health
.maxRangeTo(attacker,2*SECTORS) // within 2 sectors of the attacker
.holdFire() // don't get distracted attacking other stuff
.attack(attacker); // attack the attacker
}
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
-- https://warzone.atlassian.net/wiki/display/GO
Re: EggPlant AI ramblings
You guys are going to trip balls when you see VAULT...
Task: Find nearest oil resource to my base, order the closest free truck to go build a derrick (spelt properly!) on it:

Fully extensible as well - you can add custom selectors, filters, methods, etc.
Does anyone have a hashmap of building names referenced by friendly names? For example:
Otherwise I'm going to have to spend an age trying to make one...
Task: Find nearest oil resource to my base, order the closest free truck to go build a derrick (spelt properly!) on it:
Code: Select all
VAULT("#resource.oil")
.closestTo("#player.me",1)
.setTarget()
.findNearest("#droid.truck[player=me]:isReady",1)
.build("#building.derrick");
Fully extensible as well - you can add custom selectors, filters, methods, etc.
Does anyone have a hashmap of building names referenced by friendly names? For example:
Code: Select all
var buildings = {
derrick: "A0ResourceExtractor",
researchLab: "A0ResearchFacility",
... etc ...
}
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
-- https://warzone.atlassian.net/wiki/display/GO
Re: EggPlant AI ramblings
Oh, and more awesomeness - here's how I made .closestTo():
.uCompose is a new method in my util.js v0.5 which I'll hopefully have released next week. It creates a new function based on two existing functions. I've also added a .uCurry() and might integrate that with .uCompose(), then I could do something like this:
I should probably rename "VAULT" to "$", eh?
Code: Select all
VAULT.fn.closestTo = VAULT.fn.first.uCompose(VAULT.fn.sortByDistanceTo);
Code: Select all
VAULT.fn.closestToMyBase = VAULT.fn.first.uCompose(VAULT.fn.sortByDistanceTo,["#player.me",1]);
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
-- https://warzone.atlassian.net/wiki/display/GO
- Goth Zagog-Thou
- Regular

- Posts: 1582
- Joined: 06 Jan 2007, 08:08
- Location: Delta Base
- Contact:
Re: EggPlant AI ramblings
Nice.
Looks really promising. Keep up the good work!
-
Searge-Major
- Trained

- Posts: 182
- Joined: 10 Sep 2011, 03:36
- Location: Great Southern region, Western Australia
Re: EggPlant AI ramblings
*browsing forums...*
*stumbles across this thread*
*reads thread right through...*
result =

Looks really interesting, best of luck with it.
Not much else I can say that other haven't...
About how long until a release? I know one person who'd download it...
*stumbles across this thread*
*reads thread right through...*
result =
Looks really interesting, best of luck with it.
Not much else I can say that other haven't...
About how long until a release? I know one person who'd download it...
I fully realise my input is just another drop in the proverbial bucket. It is my goal to make each drop count.
Re: EggPlant AI ramblings
It's probably going to be a few months before EggPlant surfaces for testing.Searge-Major wrote:About how long until a release? I know one person who'd download it...
I'd got a fair way in to coding it when I realised I needed a much more descriptive (yet concise) way of interacting with objects on the map - hence me starting work on VAULT.
VAULT has since turned in to a sizeable project in its own right - but well worth it. For example, it takes something like this (note the scrollbars):
Code: Select all
function huntForOil() {
var droidlist = enumGroup(oilerGroup);
var oillist = enumFeature(-1, oilres);
for (var j=0; j<oillist.length; ++j)
oillist[j].ordered=false;
for (var i=0; i<droidlist.length; ++i) if (droidlist[i].order != DORDER_BUILD) {
var range1=999;
var k=-1;
for (var j=0; j<oillist.length; ++j)
if (safeDest(me,oillist[j].x,oillist[j].y) && !oillist[j].ordered) {
var range2=distBetweenTwoPoints(droidlist[i].x, droidlist[i].y, oillist[j].x, oillist[j].y);
if (range2<range1) {
range1=range2;
k=j;
}
}
if (k!=-1) {
orderDroidStatsLoc(droidlist[i], DORDER_BUILD, derrick, oillist[k].x, oillist[k].y);
//make sure not too many trucks build the same oil
oillist[k].ordered=true;
} else {
//return to base if no oils are available for capturing
returnToBase(droidlist[i]);
}
}
}
Code: Select all
$(".oil-resource:isSafe")
.closestTo("#me",1)
.setTarget()
.findNearest(".truck:isMy:isReady",2)
.build("derrick");
Once I've got VAULT out of the way I'll be working on things like Group and Mission AIs as the next step. Being able to use jQuery-like code (see oil example above) is going to be critical for an AI the size of EggPlant. VAULT lets me do quite complex things with very little code, and it's easy to see what the code is doing as well = I'm less likely to run away screaming
With my current RL schedule, I'm expecting VAULT to be done in about 2-3 weeks then hopefully the Group/Mission AIs will be done a month or two after that.
The thing that's motivating me currently (in addition to all the awesome support I'm getting in this forum) is that I'm certain that I can get squadrons of VTOLs doing aerial "dog fights". As in proper air-to-air combat, potentially with hundreds of VTOLs spread across several AI players fighting for air supremacy. It's the thought of uploading a video of that to youtube that's made me commit to completing this project.
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
-- https://warzone.atlassian.net/wiki/display/GO
