Javascript-based AIs
An alternative language for writing ship AIs in, similar to the use of Javascript ship/world scripts instead of legacy scripting.
Problem:
The current state machine AI, while capable of creating extremely complex behaviour, takes a large amount of boilerplate lines to set up even simple behaviours such as "fly to destination" or "fight aggressors".
Code re-use between AIs, beyond a few things which can be entirely self-contained (e.g.interceptAI.plist
) is mostly limited to copy/paste. Within an AI script, each state is likely to need a number of standard copy/pasted event responses (e.g. "ATTACKED
", "INCOMING_MISSILE
", etc)
Several temporary variables such as "found target" and "primary aggressor" are needed as the state machine language has no native variable storage. Querying ship state such as energy level requires issuing an AI command, and then waiting to receive an AI event triggered by it.
Proposal
Allow Javascript to be used as an alternative scripting language for AIs. The current state machine language would continue to be supported for compatibility with older OXPs, but eventually discouraged.
Part 1
Implement Javascript properties, methods, and ship script event handlers for all AI commands and events not currently having a JS equivalent. The major ones areperformAttack
and the other commands which set the internal flight behaviour. Some of the additional properties and event handlers will also be useful to other OXP writing.
This will also require entry wormholes to be given a JS representation (only read-only: fuller scripting of wormholes is a separate task, not included here, and maybe not for 1.80 at all)
This will allow JS-based AIs to be implemented in the ship script, but this is probably only of use to a few OXPs, as this then ties the AI to the ship script, making it difficult to switch AIs.
Part 2
Allow JS files to be placed in the AIs directory, and initialised with a setAI command (setAI("aiFile")
would look for the JS version first, then the plist version). The AI script and ship script will then both receive all events, and can respond accordingly.
This allows useful JS-based AIs, but writing them can still involve a lot of boilerplate code, probably copy/pasted from another AI. The ability to have local variables makes certain things simpler than the state machine AI, but the construction of AI is still at quite a low level.
Part 3
Priority over-ride. New behavior dictated. Must break target into component materials.
Create a priority-based AI library. The idea behind this is that rather than (re)defining common behaviours every time, a ship could be given a priority list of behaviours, which call library functions, which insert the appropriate event handlers into the AI script. For exampleroute1traderAI.plist
could be represented (roughly) as:Each of these priorities would have a test to see if it was relevant behaviour, and a library function which set up the appropriate event handlers. Certain event handlers (especially combat-relevant ones) would then cause the priority list to be evaluated again. Most AIs would also have a timer to allow the list to be reevaluated periodically.
- Flee strong attackers
- Fight weak attackers until escape possible
- Dock with nearby station
- Approach station around current planet
- Approach planet with station
- Jump to nearby system
OXP writers could then use the library functions much of the time for common behaviour such as "destroy this ship" or "fly to waypoint/destination", etc. For more specialist behaviour, the OXP would provide a function with the same effect as the library functions.
Use of the library functions would not be required at all, of course - low-level AI writing (or sticking with the state machine AI) would remain possible.
All native AIs would at this point be rewritten to be JS-based using the AI library. Most of them are likely to end up being very short as a result. For example, the trader AI might be (syntax, function names, parameters, etc. for illustration only - actual ones not yet decided, and comments welcome):
This is considerably shorter than the several hundred lines needed forCode: Select all
this.startAI = function() { var ailib = worldScripts.OoliteAI; // world script is easiest but we could make it a property of something else (e.g. global.aiLib) this.$destination = ailib.getFriendlyStation(this.ship); if (!this.$destination) { this.ship.setAI("exitingTraderAI"); return; } ailib.setPriorities(this,[ { condition: ailib.conditionLosingCombat, behaviour: ailib.behaviourFleeCombat, reconsider: 5 }, { condition: ailib.conditionInCombat, behaviour: ailib.behaviourRepelAggressors, reconsider: 5 }, { condition: ailib.conditionNearStation.bind(ailib,this.$destination,25E3), behaviour: ailib.behaviourDockStation.bind(ailib,this.$destination), // no need for time-based reconsideration here; can do it all with events }, { condition: ailib.objectsWithinRange.bind(ailib,system.mainPlanet,this.$destination,system.mainPlanet.radius*3), truebranch: [ { condition: ailib.conditionNearPlanet.bind(ailib,system.mainPlanet), behaviour: ailib.behaviourApproachDestination.bind(ailib,this.$destination.position,15E3) }, { condition: ailib.conditionObjectValid.bind(ailib,this.$destination), behaviour: ailib.behaviourApproachDestination.bind(ailib,system.mainPlanet,system.mainPlanet.radius*4), } ], // priority subtree for station near planet falsebranch: [ { condition: ailib.conditionObjectValid.bind(ailib,this.$destination), behaviour: ailib.behaviourApproachDestination.bind(ailib,this.$destination.position,15E3) } ] // priority subtree for station away from planet }, { // no condition = always true setAI: "route1traderAI.js" // restart AI to get a new station since the current one has vanished } ]); } this.stopAI = function() { worldScripts.OoliteAI.cleanup(this); }
route1TraderAI
,dockingAI
,traderInterceptAI
, and so on. More importantly, a more complex trading routine would not take many more lines in the AI script (though the AI library might be quite complex internally)
Something which will be needed but isn't in the example above:
The search function would select a target based on criteria, and pass that target to the behaviour. If no suitable target was found, it would return false and the behaviour would not be executed (as if a condition had failed)Code: Select all
{ search: searchFunction, behaviour: ... }
The lists below are intended to cover behaviour in the core game. Writing extra behaviours should be fairly straightforward and is expected. Still, if there are obvious cases missing they should be in the core game.
Preliminary list of combat behavioursPreliminary list of non-combat behaviours
- Flee combat: Fly away from all hostile vessels at top speed, dropping cargo if requested and possible.
- Repel attackers: Fire on any attackers (self, then group) until they flee, then ignore them.
- Destroy attackers: Fire on any attackers (self, then group) until they are dead.
- Aggressive Escort: Fire on mothership's target until they are dead.
- Defensive Escort: Fire on ships attacking mothership until they flee.
- Destroy target: Destroy the selected target.
- Rob target: Fire on selected target, requesting cargo dumping, destroying if not complied with (or too little cargo dropped)
- Intercept target: Ram target
- Missile: Behave as missile. Parameters for detonation function and ECM susceptibility.
- Mine: Wait, then detonate. Parameter for detonation function.
- Go to destination: go to specified range from specified coordinates, avoiding obstacles
- Dock: dock with specified station
- Await docking: stop and wait for ships to dock
- Escort: form up in escort formation with mothership / group leader, including following to witchspace
- Guard: loop at a specified distance from an object (e.g. the station patrols)
- Salvage: collect scoopable objects, prioritising escape pods
- Mine: locate asteroids/boulders and break them apart
- Sunskim: head to solar atmosphere, refuel until full
- Wait: stop, maintain position
- Tumble: rotate
- Land: land on selected planet/moon
- Exit system: go to new system
Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based AIs
Moderators: winston, another_commander
Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based AIs
Thanks for your comments on the first two. This one's a little more technical in detail than the first two, but hopefully should make OXP writing easier. Let me know what you think, especially if you've set up complex behaviours with the current AI system. What do you think to the draft syntax? Are there any core behaviours missing?
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
Maybe it could offer an easier way to handle Thargons, especially for OXPs that wish to have them "wake back up" when Thargoids get near them. (Yes, I know there's already an OXP that does that...but this was the best complex example I could think of.)
In Switeck's Shipping OXP, the thing that gave me the most trouble to do was getting Thargons to follow Thargoid warships around if they lacked a target to attack. If there are multiple complex/contradictory actions to be taken, I usually had to break each action into its own section called by another section that could ask 1 specific "question" of sorts. The "Target Found" result for searching for motherships OR stuff to kill naturally had different actions connected to them.
Another example was I had police and bounty hunter ships patrol the space-lanes, look for hostiles/pirates/fugitives, AND scoop cargo. (Vipers never scooped cargo on account of no cargo hold and/or fuel scoops.) I never could completely get rid of the glitches my AI.plist script had when it neared then end of a patrol route, because it kept wanting to find a new endpoint after exiting each script section. I never did try converting it to use defined waypoints though...
Another thing that javascript-based AIs might do eaiser is ship towing? Like in Anarchies and Buoy Repair, which mostly just use complex joined models to simulate it.
In Switeck's Shipping OXP, the thing that gave me the most trouble to do was getting Thargons to follow Thargoid warships around if they lacked a target to attack. If there are multiple complex/contradictory actions to be taken, I usually had to break each action into its own section called by another section that could ask 1 specific "question" of sorts. The "Target Found" result for searching for motherships OR stuff to kill naturally had different actions connected to them.
Another example was I had police and bounty hunter ships patrol the space-lanes, look for hostiles/pirates/fugitives, AND scoop cargo. (Vipers never scooped cargo on account of no cargo hold and/or fuel scoops.) I never could completely get rid of the glitches my AI.plist script had when it neared then end of a patrol route, because it kept wanting to find a new endpoint after exiting each script section. I never did try converting it to use defined waypoints though...
Another thing that javascript-based AIs might do eaiser is ship towing? Like in Anarchies and Buoy Repair, which mostly just use complex joined models to simulate it.
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
So this I think would be pretty straightforward in priority-based AI, if I get it right. The standard AI for Thargons is likely to be something likeSwiteck wrote:In Switeck's Shipping OXP, the thing that gave me the most trouble to do was getting Thargons to follow Thargoid warships around if they lacked a target to attack. If there are multiple complex/contradictory actions to be taken, I usually had to break each action into its own section called by another section that could ask 1 specific "question" of sorts. The "Target Found" result for searching for motherships OR stuff to kill naturally had different actions connected to them.
- If mothership in combat; Aggressive escort
- If mothership exists; Escort
- If suitable mothership in range; Join escort group
- Unconditionally; Become cargo
If you wanted them to be able to reacquire motherships from the idle state, you'd just put a recalculation time on state 4, and they'd periodically check for a new warship. You'd also need a "Become active" on state 3.
Again, should be very straightforward. You'd just set up the various priorities - combat, salvage, patrol - and then at the bottom of the list you'd have nothing to fight, nothing to salvage, and have reached your destination, so you'd just set a new destination there, and ask it to reassess the priorities.Switeck wrote:Another example was I had police and bounty hunter ships patrol the space-lanes, look for hostiles/pirates/fugitives, AND scoop cargo. (Vipers never scooped cargo on account of no cargo hold and/or fuel scoops.) I never could completely get rid of the glitches my AI.plist script had when it neared then end of a patrol route, because it kept wanting to find a new endpoint after exiting each script section. I never did try converting it to use defined waypoints though...
Probably a bit, but the key bit of that is going to be a low-level frame-by-frame action, so that needs new flight modes. You could write them with the scriptable flight AIs to get the tug to line itself up next to the object, and extend the tow cable, and you'd probably need a fair bit of scripting too to keep the object in the right placeSwiteck wrote:Another thing that javascript-based AIs might do eaiser is ship towing? Like in Anarchies and Buoy Repair, which mostly just use complex joined models to simulate it.
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
Or towing could be an extreme form of flying in formation.
So long as the lead ship isn't turning quickly or often, it might look ok.
So long as the lead ship isn't turning quickly or often, it might look ok.
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
It certainly gets my vote (having written a few AI/js combo pairs that would certainly benefit from streamlining and simplifying that this offers). The syntax above looks a bit abstract, but I'm certainly behind the concept. Especially if we can use it rather more flexibly than we can at the moment, to give a wider range of behaviours especially during combat.
My OXPs via Boxspace or from my Wiki pages .
Thargoid TV
Dropbox Referral Link
Thargoid TV
Dropbox Referral Link
- aegidian
- Master and Commander
- Posts: 1161
- Joined: Thu May 20, 2004 10:46 pm
- Location: London UK
- Contact:
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
Not sure JS is the best way to deal with the problems with AIs suggested. There may be other solutions, for example using a preprocessor for AI files to simplify setting up responses 'as_per_state: STATE_X', where multiple identical responses are required.
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
Especially since:aegidian wrote:There may be other solutions, for example using a preprocessor for AI files to simplify setting up responses 'as_per_state: STATE_X', where multiple identical responses are required.
"INCOMING_MISSILE" = (fightOrFleeMissile);
...has to be used almost every section that the AI stays more than a split-second in.
- Disembodied
- Jedi Spam Assassin
- Posts: 6885
- Joined: Thu Jul 12, 2007 10:54 pm
- Location: Carter's Snort
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
Should a Thargoid mothership, if not in combat, scoop up any available Thargons, active or inactive?
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
That could work.aegidian wrote:Not sure JS is the best way to deal with the problems with AIs suggested. There may be other solutions, for example using a preprocessor for AI files to simplify setting up responses 'as_per_state: STATE_X', where multiple identical responses are required.
It's more the state checks which I think JS could do a lot better. With the state machine AI, if you want an
if x then y else z
construct, the test x
has to be explicitly added to the Oolite core, and could take a year to get into a stable release. Or you could call out to the ship script to do the test, and have that send arbitrary AI messages back, but then you've got your AI half in Javascript and half in state machine, and you can't reuse the AI on another ship without also copying the ship script.Or scanning, for instance - we've implemented 11 separate scanForX AI commands, but if someone wants "scan for ships attacking mothership", we either have to add another entry to the command list, or advise them to use a ship script and the generic
system.filteredEntities
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
That's the kind of thing I was thinking of (the scanning example), but hopefully with the proposals it can be widened to extend flexibility to things like combat. At the moment what we can do via AI is very limited, but if js is included too then things can be potentially much more flexible.
The combined AI/js system does work, but it always feels rather more awkward than it needs to be. Plus if it's not done carefully, the different timings involved between the AI and the script polling can easily lead to loops, unintended calls, things going out of sequence or the js response ending up going to the wrong AI state. It's always felt somewhat disjointed, although it is much more powerful than the original AI-alone usage.
The combined AI/js system does work, but it always feels rather more awkward than it needs to be. Plus if it's not done carefully, the different timings involved between the AI and the script polling can easily lead to loops, unintended calls, things going out of sequence or the js response ending up going to the wrong AI state. It's always felt somewhat disjointed, although it is much more powerful than the original AI-alone usage.
My OXPs via Boxspace or from my Wiki pages .
Thargoid TV
Dropbox Referral Link
Thargoid TV
Dropbox Referral Link
- aegidian
- Master and Commander
- Posts: 1161
- Joined: Thu May 20, 2004 10:46 pm
- Location: London UK
- Contact:
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
I can see good arguments for increasing AI flexibility with JS, especially if AI's could call JS scripts for more advanced scans, and other actions beyond Oolite's core scope. But I strongly feel that replacing state-machine AI with script JS might not be good.
I guess the concept here is probably what's rubbing me the wrong way. And it may be more to do with the way I think.
I can think about AI in terms of state machines, for me they are a good analog to a simple, thinking entity ("I'm concerned with doing THIS thing and I'm in THIS state, and I'm going to react to THAT going on") For whatever reason, I can't make the same mental equivalence with JS code.
I'd certainly like to see a test done first, to see if coding JS AI's that behave identically to the state-machine AI's can be done easily (and in such a way that the code makes conceptual sense as intelligence.)
I guess the concept here is probably what's rubbing me the wrong way. And it may be more to do with the way I think.
I can think about AI in terms of state machines, for me they are a good analog to a simple, thinking entity ("I'm concerned with doing THIS thing and I'm in THIS state, and I'm going to react to THAT going on") For whatever reason, I can't make the same mental equivalence with JS code.
I'd certainly like to see a test done first, to see if coding JS AI's that behave identically to the state-machine AI's can be done easily (and in such a way that the code makes conceptual sense as intelligence.)
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
Yes, certainly. The first two steps just allow JS AIs to be written (well, they can already be written. It allows them to be written without needing an API in the state machine script to handle a few missing functions) and switched sensibly.aegidian wrote:I can think about AI in terms of state machines, for me they are a good analog to a simple, thinking entity ("I'm concerned with doing THIS thing and I'm in THIS state, and I'm going to react to THAT going on") For whatever reason, I can't make the same mental equivalence with JS code.
I'd certainly like to see a test done first, to see if coding JS AI's that behave identically to the state-machine AI's can be done easily (and in such a way that the code makes conceptual sense as intelligence.)
The third step ... if it turns out not to work, or not be generally suitable, then I'll release it as an OXP instead. I agree that state machines are a sensible way to run the AI internally: ultimately all the AI library is going to do is act as a preprocessor/interpreter to build a much more complex state machine than would be practical to write by hand. (And the AI library is always optional for OXPers: they could just construct the AI directly as a state machine in JS, or even write a different library that took an AI syntax very similar to the current plist syntax and use that instead ... or continue to use the plist state machine, for that matter)
- JensAyton
- Grand Admiral Emeritus
- Posts: 6657
- Joined: Sat Apr 02, 2005 2:43 pm
- Location: Sweden
- Contact:
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
Syntax thing: all those
The implementation would be something like this (in the prefix script):
(This is based on the canonical fallback implementation of
foo.bind(ailib, bar)
bits are going to get annoying. I suggest making all the ailib actions pre-bound or not requiring binding this
(necessary for the nullary ones anyway), and adding support for something like foo.using(bar)
.The implementation would be something like this (in the prefix script):
Code: Select all
Function.prototype.using = function()
{
let func = this;
let args = arguments;
return function()
{
func.apply(null, args.concat(Array.prototype.slice.call(arguments)));
}
}
bind
. I was going to call it “with”, but that’s a deprecated keyword.)E-mail: [email protected]
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
Much better. None of them requireJensAyton wrote:Syntax thing: all thosefoo.bind(ailib, bar)
bits are going to get annoying. I suggest making all the ailib actions pre-bound or not requiring bindingthis
(necessary for the nullary ones anyway), and adding support for something likefoo.using(bar)
.
this
binding at all. Thanks.- Disembodied
- Jedi Spam Assassin
- Posts: 6885
- Joined: Thu Jul 12, 2007 10:54 pm
- Location: Carter's Snort
Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based
At the risk of introducing player-centricity, would it be worth adding something along the lines of "head towards the player"? Or adding in the player as a specific (and obviously mobile) "destination"? It could be useful for OXP authors.cim wrote:Preliminary list of non-combat behaviours
- Go to destination: go to specified range from specified coordinates, avoiding obstacles
- Dock: dock with specified station
- Await docking: stop and wait for ships to dock
- Escort: form up in escort formation with mothership / group leader, including following to witchspace
- Guard: loop at a specified distance from an object (e.g. the station patrols)
- Salvage: collect scoopable objects, prioritising escape pods
- Mine: locate asteroids/boulders and break them apart
- Sunskim: head to solar atmosphere, refuel until full
- Wait: stop, maintain position
- Tumble: rotate
- Land: land on selected planet/moon
- Exit system: go to new system