Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based AIs

General discussion for players of Oolite.

Moderators: another_commander, winston

User avatar
cim
Quite Grand Sub-Admiral
Quite Grand Sub-Admiral
Posts: 4072
Joined: Fri Nov 11, 2011 6:19 pm

Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based AIs

Post by cim »

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?
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 are performAttack 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 example route1traderAI.plist could be represented (roughly) as:
  • 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
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.

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):

Code: 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);
}
This is considerably shorter than the several hundred lines needed for 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:

Code: Select all

{
	search: searchFunction,
	behaviour: ...
}
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)

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 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.
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
Switeck
---- E L I T E ----
---- E L I T E ----
Posts: 2412
Joined: Mon May 31, 2010 11:11 pm

Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based

Post by Switeck »

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. :P

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.
User avatar
cim
Quite Grand Sub-Admiral
Quite Grand Sub-Admiral
Posts: 4072
Joined: Fri Nov 11, 2011 6:19 pm

Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based

Post by cim »

Switeck 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. :P
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 like
  1. If mothership in combat; Aggressive escort
  2. If mothership exists; Escort
  3. If suitable mothership in range; Join escort group
  4. Unconditionally; Become cargo
When a Thargon is launched, it would immediately drop down to state 3, lock on to the mothership right next to it, and then recalculate to attack the mothership's target / escort the mothership. If the mothership gets destroyed, it would switch to another one if possible, or go idle if not. (This adds "following" automatically)

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.
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...
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 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.
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 [EliteWiki] 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 place
Switeck
---- E L I T E ----
---- E L I T E ----
Posts: 2412
Joined: Mon May 31, 2010 11:11 pm

Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based

Post by Switeck »

Or towing could be an extreme form of flying in formation. :lol:
So long as the lead ship isn't turning quickly or often, it might look ok.
User avatar
Thargoid
Thargoid
Thargoid
Posts: 5525
Joined: Thu Jun 12, 2008 6:55 pm

Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based

Post by Thargoid »

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.
User avatar
aegidian
Master and Commander
Master and Commander
Posts: 1160
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

Post by aegidian »

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.
"The planet Rear is scourged by well-intentioned OXZs."

Oolite models and gear? click here!
Switeck
---- E L I T E ----
---- E L I T E ----
Posts: 2412
Joined: Mon May 31, 2010 11:11 pm

Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based

Post by Switeck »

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.
Especially since:
"INCOMING_MISSILE" = (fightOrFleeMissile);
...has to be used almost every section that the AI stays more than a split-second in.
User avatar
Disembodied
Jedi Spam Assassin
Jedi Spam Assassin
Posts: 6874
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

Post by Disembodied »

Should a Thargoid mothership, if not in combat, scoop up any available Thargons, active or inactive?
User avatar
cim
Quite Grand Sub-Admiral
Quite Grand Sub-Admiral
Posts: 4072
Joined: Fri Nov 11, 2011 6:19 pm

Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based

Post by cim »

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.
That could work.

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
User avatar
Thargoid
Thargoid
Thargoid
Posts: 5525
Joined: Thu Jun 12, 2008 6:55 pm

Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based

Post by Thargoid »

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.
User avatar
aegidian
Master and Commander
Master and Commander
Posts: 1160
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

Post by aegidian »

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.)
"The planet Rear is scourged by well-intentioned OXZs."

Oolite models and gear? click here!
User avatar
cim
Quite Grand Sub-Admiral
Quite Grand Sub-Admiral
Posts: 4072
Joined: Fri Nov 11, 2011 6:19 pm

Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based

Post by cim »

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.)
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.

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)
User avatar
JensAyton
Grand Admiral Emeritus
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

Post by JensAyton »

Syntax thing: all those 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)));
    }
}
(This is based on the canonical fallback implementation of bind. I was going to call it “with”, but that’s a deprecated keyword.)
User avatar
cim
Quite Grand Sub-Admiral
Quite Grand Sub-Admiral
Posts: 4072
Joined: Fri Nov 11, 2011 6:19 pm

Re: Proposals for Oolite 1.79/1.80, 3 of 5: Javascript-based

Post by cim »

JensAyton wrote:
Syntax thing: all those 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).
Much better. None of them require this binding at all. Thanks.
User avatar
Disembodied
Jedi Spam Assassin
Jedi Spam Assassin
Posts: 6874
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

Post by Disembodied »

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
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.
Post Reply