Join us at the Oolite Anniversary Party -- London, 7th July 2024, 1pm
More details in this thread.

Old AI bugs

For test results, bug reports, announcements of new builds etc.

Moderators: winston, another_commander, Getafix

another_commander
Quite Grand Sub-Admiral
Quite Grand Sub-Admiral
Posts: 6580
Joined: Wed Feb 28, 2007 7:54 am

Post by another_commander »

The empty scripted cargopods bug has been (re)fixed now in SVN 1920/1921.
Edit: Even better (and actually correct) fix in SVN 1922/1923.
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

In the current pirateAI.plist there is another bug with bad timing. Since we have the target inspector I often see pirates and other ships attacking an "empty" target. It hangs in this loop until an other player attacks the pirate. Today i found a way to consistently put a pirate in this mode. Wait till the pirate is in the "LURK" state, then cloak. The AI than already has issued a "checkGroupOddsVersusTarget" command and this will eventually lead to switching to the ATTACK state without having a target.:

Code: Select all

  Oolite [ai.message.receive] -[AI reactToMessage:] (AI.m:353): AI pirateAI.plist for Asp Mark II Special 195 in state 'LURK' receives message 'ENTER'
  Oolite [ai.takeAction] -[AI takeAction:] (AI.m:411): Asp Mark II Special 195 to take action setSpeedTo: 0.0
  Oolite [ai.takeAction] -[AI takeAction:] (AI.m:411): Asp Mark II Special 195 to take action performIdle
Oolite [ai.takeAction] -[AI takeAction:] (AI.m:411): Asp Mark II Special 195 to take action scanForRandomMerchantmen
Oolite [ai.takeAction] -[AI takeAction:] (AI.m:411): Asp Mark II Special 195 to take action pauseAI: 5.0
Oolite [ai.message.receive] -[AI reactToMessage:] (AI.m:353): AI pirateAI.plist for Asp Mark II Special 195 in state 'LURK' receives message 'TARGET_FOUND'
Oolite [ai.takeAction] -[AI takeAction:] (AI.m:411): Asp Mark II Special 195 to take action setTargetToFoundTarget
Oolite [ai.takeAction] -[AI takeAction:] (AI.m:411): Asp Mark II Special 195 to take action checkGroupOddsVersusTarget
Oolite [ai.message.receive] -[AI reactToMessage:] (AI.m:353): AI pirateAI.plist for Asp Mark II Special 195 in state 'LURK' receives message 'TARGET_CLOAKED'  < --- player cloaked in lurk state
Oolite [ai.message.receive] -[AI reactToMessage:] (AI.m:353): AI pirateAI.plist for Asp Mark II Special 195 in state 'LURK' receives message 'TARGET_LOST'  <-- not programmed to do anything with this message.
Oolite [ai.takeAction] -[AI takeAction:] (AI.m:411): Asp Mark II Special 195 to take action scanForRandomMerchantmen
Oolite [ai.takeAction] -[AI takeAction:] (AI.m:411): Asp Mark II Special 195 to take action pauseAI: 5.0
Oolite [ai.message.receive] -[AI reactToMessage:] (AI.m:353): AI pirateAI.plist for Asp Mark II Special 195 in state 'LURK' receives message 'ODDS_LEVEL'
Oolite [ai.takeAction] -[AI takeAction:] (AI.m:411): Asp Mark II Special 195 to take action groupAttackTarget
  Oolite [ai.message.receive] -[AI reactToMessage:] (AI.m:353): AI pirateAI.plist for Asp Mark II Special 195 in state 'LURK' receives message 'GROUP_ATTACK_TARGET'  <--- player is ordered to attack a null target.
  Oolite [ai.takeAction] -[AI takeAction:] (AI.m:411): Asp Mark II Special 195 to take action setTargetToFoundTarget
  Oolite [ai.takeAction] -[AI takeAction:] (AI.m:411): Asp Mark II Special 195 to take action setStateTo: ATTACK_SHIP
    Oolite [ai.message.receive] -[AI reactToMessage:] (AI.m:353): AI pirateAI.plist for Asp Mark II Special 195 in state 'LURK' receives message 'EXIT'
    Oolite [ai.message.receive] -[AI reactToMessage:] (AI.m:353): AI pirateAI.plist for Asp Mark II Special 195 in state 'ATTACK_SHIP' receives message 'ENTER'
    Oolite [ai.takeAction] -[AI takeAction:] (AI.m:411): Asp Mark II Special 195 to take action performAttack
Oolite [ai.message.receive] -[AI reactToMessage:] (AI.m:353): AI pirateAI.plist for Asp Mark II Special 195 in state 'ATTACK_SHIP' receives message 'TARGET_FOUND'
It not only happens with cloaking but with any target that suddenly disappears during the LURK state. One thing that helps a bit is sending the ship in the "TRAVEL_TO_LURK_AREA" state when it receives a "TARGET_LOST" in the "LURK" state. It is now more difficult to let the pirate attacking an empty target. (But still possible I think). But there are more AI's out there that can put a ship in this mode. I think it should be more general solved from within the code. Checking for having a target when starting a "performAttack" would solve the majority of the cases, but still not all. e.g. not with certain cases with escorts. (interceptAI.plist still needs a RESTARTED entry).

Best thing is to do the check in the update moment. Currently there is only send a TARGET_LOST when the target is out of range. This update should also check for having no target. This suggestion I filed a few weeks back as bug report at Berlios)

I also noticed that my new pirate AI had not all the cloaking stuff in that was intended. I reloaded the new AI on the same link as before. (New pirateAI) This one handles cloaked attack better. The previous did not despite my added readme). In the cargo handling I already saw differences with pirates: Hostile ships heading for the station or even in the docking process. On analysis this were pirates with full hold. I think this makes the pirate behaviour more realistic. Their main goal is piracy not assassination of traders. Only the ships with no hold become assassination ships now.
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

Looking at the policeinterceptAi.plist I noticed a bug. In the state:

Code: Select all

	"DOCK_WITH_STATION" =
	{
		ENTER = (switchLightsOff, setTargetToStation, "setDesiredRangeTo: 5000.0", performIntercept);
		"ACCEPT_DISTRESS_CALL" = (setTargetToFoundTarget, "setAITo: interceptAI.plist");
		ATTACKED = (switchLightsOn, setTargetToPrimaryAggressor, "setStateTo: ATTACK_SHIP");
		"DESIRED_RANGE_ACHIEVED" = ("setAITo: dockingAI.plist");
		"INCOMING_MISSILE" = (fightOrFleeMissile, "setStateTo: FLEE");
	};
There is no UPDATE event. But when the ship receives a distress call on its way back to the station it will do an "setAITo: interceptAI.plist". When returning from this intercept it will hang. That state needs Eighter an UPDATE or an RESTARTED entry. Probably it should be changed to:

Code: Select all

	"DOCK_WITH_STATION" =
	{
		ENTER = (switchLightsOff, setTargetToStation, "setDesiredRangeTo: 5000.0", performIntercept);
		"ACCEPT_DISTRESS_CALL" = (setTargetToFoundTarget, "setAITo: interceptAI.plist");
		ATTACKED = (switchLightsOn, setTargetToPrimaryAggressor, "setStateTo: ATTACK_SHIP");
		"DESIRED_RANGE_ACHIEVED" = ("setAITo: dockingAI.plist");
		"INCOMING_MISSILE" = (fightOrFleeMissile, "setStateTo: FLEE");
		RESTARTED = ("setStateTo: DOCK_WITH_STATION");
	};
Change of this to happen is of cause small as it needs that the launched police did kill two ships on a row to trigger this bug.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Eric, wouldn’t it be easier if you just checked the fixes in yourself? ;-)

At any rate, you now have the necessary BerliOS permissions to do so.
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

Ahruman wrote:
Eric, wouldn’t it be easier if you just checked the fixes in yourself? ;-)

At any rate, you now have the necessary BerliOS permissions to do so.
Thanks. I will do that in future. For some bugs there is clearly one solution. But for other there are different ways: By changing AI or by changing the code. e.g.

I was busy tracing an AI of a plain escorted trader. I noticed something weird that must very often happen with escorted traders. While flying it scans always for hostiles. When it first finds something it issues a fightOrFleeHostiles. Because it has escorts it always sends a FLEEING and deploys the escorts. On the second scan is still finds hostiles and receives a target found and issues again a fightOrFleeHostiles but now it has no escorts and might return a FIGHTING message. However, this FIGHTING message arrives when he is already in the FLEEING state. FIGHTING is also defined here so it starts to fight:

Code: Select all

Python 579 to take action scanForHostiles (1st update scan)
Python 579 in state 'HEAD_FOR_PLANET' receives message 'TARGET_FOUND' (1st find)
Python 579 to take action fightOrFleeHostiles (1st reaction)

Python 579 to take action scanForHostiles (2nd update scan)
Python 579 in state 'HEAD_FOR_PLANET' receives message 'TARGET_FOUND' (2nd find)
Python 579 to take action fightOrFleeHostiles (2nd reaction)
Python 579 in state 'HEAD_FOR_PLANET' receives message 'DEPLOYING_ESCORTS' (messages on 1st reaction)
Python 579 in state 'HEAD_FOR_PLANET' receives message 'FLEEING' (messages on 1st reaction)
Python 579 to take action setTargetToPrimaryAggressor
Python 579 to take action setStateTo: FLEE

Python 579 in state 'FLEE' receives message 'ENTER'
Python 579 to take action performFlee
Python 579 in state 'FLEE' receives message 'FIGHTING' (messages on 2nd reaction)
Python 579 to take action setTargetToPrimaryAggressor
Python 579 to take action setStateTo: ATTACK_SHIP
Python 579 in state 'ATTACK_SHIP' receives message 'ENTER'
Python 579 to take action broadcastDistressMessage
Python 579 to take action performAttack

This can probably be prevented by adding a state in between. I have currently rewritten the code to immediately switch to a new state upon issuing a fightOrFleeHostiles and will test that. But seeing all those late messages arriving in states were they are already forgotten asks for a new AI command: clearMessages. Probably easy to implement and would be very useful when you don't want to take old messages with you when switching to a new state and are sure you want to forget any other messages. I am just unsure if this must be the remainder of the message stack that is evaluated or the new one that is build for evaluating in the next update. Or even both.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

It seems to me that a clearMessages command could easily cause trouble, because it’s not obvious what messages will be in the queue in any given time. (Technical note: it isn’t actually a queue, or a stack as Eric described it, but a set: only one message of a given type can be pending, and delivery order is arbitrary.) Perhaps it would be safer to have a command to remove specific messages (e.g. dropMessages: FLEEING FIGHTING), and a command to list the current pending messages for testing purposes?
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

Ahruman wrote:
Perhaps it would be safer to have a command to remove specific messages (e.g. dropMessages: FLEEING FIGHTING), and a command to list the current pending messages for testing purposes?
That might be just as good as it is in most cases a specific pending message that gives troubles. In this case it is FLEEING, but I also remember an other occasion where I always got a TARGET_LOST while the AI already had a new found target. These problems are solvable by adding an extra STATE but deleting a specific message keeps the AI compact.

In above case I will solve it by adding second FLEE state where the ship ends on receiving a "DEPLOYING_ESCORTS" because this is also send in these problem cases. This second flee is because I noticed more problems with fleeing ships with escorts because attacks on an escort are "auto relayed" to the mother. While fleeing the mother should ignore those attacks, because this was the whole purpose of always fleeing when there are escorts.

--------
I left a trader fly for an hour while its escorts attacked the aggressor. On every attack on the escorts, the mother also received an attack. Mother switched to an attack state and received a lost_target because the real aggressor attacking its escort was miles away. The escort was never able to kill the aggressor but the mother AI was kept being interrupted by the auto attack messages and fell back in the HEAD_FOR_PLANET state. It was never able to dock.
I think that these ATTACK messages to group members as generated by takeEnergyDamage: from: becauseOf: in shipEntity should also do a range check before sending. Now distant group leaders or distant pirates get attack messages with targets what are beyond scanner range.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

I’ve added dropMessages: and debugDumpPendingMessages.

While testing this, I added an INCOMING_MISSILE handler to planetPatrolAI.plist. Because superhumanly instant responses have always bugged me, I did it by calling an intermediate AI like this:

Code: Select all

{
    GLOBAL =
    {
        ENTER = ("rollD: 4");
        ROLL_1 = ("pauseAI: 0.7");
        ROLL_2 = ("pauseAI: 1.2");
        ROLL_3 = ("pauseAI: 1.8");
        ROLL_3 = ("pauseAI: 2.5");
        
        UPDATE = ("setStateTo: REACT");
    };
    REACT =
    {
        ENTER = (fightOrFleeMissile, setTargetToPrimaryAggressor, "setAITo: interceptAI.plist");
        UPDATE = (exitAI);
    };
}
Strangely, this works fine for INCOMING_MISSILE, but if I try using it for ATTACKED the ship immediately switches to interceptAI.plist. I can’t see an obvious reason for this.

Anyway, I quite like the delayed reaction behaviour, but if it’s used for one AI it should be used for all of them. It may be an idea to use a longer set of delays for traders than for police and pirates, too. I’m also considering adding a pauseAIRandom: method, such that pauseAIRandom: 0.5 2.5 will select a value between 0.5 and 2.5 (with a pseudo-normal distribution rather than a linear distribution) and pause for that long.
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

Ahruman wrote:
While testing this, I added an INCOMING_MISSILE handler to planetPatrolAI.plist. Because superhumanly instant responses have always bugged me, I did it by calling an intermediate AI like this:
Nice idea that delay, I'll try to play with that.
An other way to get rid of the super human reaction speed it to change the reactToMessage to a normal message in fireMissile for non-player targets. I don't see the necessity to send it as a reactToMessage for a NPC. That way the time until the next update will take care of the delay in reaction on missiles. But in combat UPDATE delay is normally very short.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

The problem with that is that fireMissile doesn’t know what the delay time is; it could be anything from 0.01 seconds to 20 seconds or more, neither of which make much sense. Also, you can’t vary it for different types of NPCs.
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

OK, I bundled a few modified AI's in the next file new 1.72 AI's
It contains a modified pirateAI, the modified traderAI and some others. For me the modifications work well. They are bundled in an oxp for easier installation. The internal requires.plist will let it only work with 1.72
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

Eric Walch wrote:
OK, I bundled a few modified AI's in the next file new 1.72 AI's
The current route1traderAI had still another issue. When you attacked it when it was already heading for the station, it would return to the head_for_planet state after the attack. It now will not get any AEGIS_CLOSE_TO_PLANET message anymore. It now flies to the surface until it gets a DESIRED_RANGE_ACHIEVED message. However, depending on the flight angle the ship often crashed into the surface before it got that message. In my last upload I therefor added a APPROACHING_SURFACE message. That fires at a height of 500 meters. I hoped it would save the ship, but yesterday I had an AI trace of a ship that got that message, entered the GOTO_STATION state but than still crashed because the reaction time was to short.

There is one way to solve this easy and that is to introduce a new AI file. I called it the traderInterceptAI.plist that now contain all the flee and attack messages. route1Trader now is left only with two states: heading for planet and heading for the station. It is working well and the AI looks less complex. I re-uploaded the changes to the previous link: new 1.72 AI's

I also experimented a bit with the delay code suggested by Ahruman a few messages back. It can be incorporated in above code (but is not yet). Currently I am testing it also. However I noticed some strange things and switched back. For some reason it is often not deploying its escorts with my second missile.

-----
On an other point I noticed a problem with exitAI. When a ship is in waypointAI.plist and receives a INCOMING_MISSILE message it performs an exitAI. However, the new AI has no clue what reaction brought him there. There is only a RESTART message. Somehow we need a new command: exitAIWithMessage:. This way we can let the original AI do a reaction. This idea let me look at the code of messageMother:. That sends a message to the mother. But for non escorts the mother is itself. So I tested the message:

Code: Select all

"NOTHING_FOUND" = (exitAI, "messageMother: NO_TARGETS_FOUND");
This exits the current AI and than sends a message to itself. I tested it an yes, the message arrives in the right AI:

Code: Select all

Oolite [ai.takeAction] AI.m:411: Boa 266 to take action exitAI
  Oolite [ai.stack.pop] AI.m:201: Popping previous state machine for <AI 0x5ed3b90>{"traderInterceptAI.plist" in state: "FLEE_FOR_MISSILE" for Boa 266}
  Oolite [ai.message.receive] AI.m:353: AI route1traderAI.plist for Boa 266 in state 'HEAD_FOR_PLANET' receives message 'RESTARTED'
Oolite [ai.takeAction] AI.m:411: Boa 266 to take action messageMother: NO_TARGETS_FOUND
  Oolite [ai.message.receive] AI.m:353: AI route1traderAI.plist for Boa 266 in state 'HEAD_FOR_PLANET' receives message 'NO_TARGETS_FOUND'
That means that in gotoWaipontAi we could use the message:

Code: Select all

"INCOMING_MISSILE" = (exitAI, "messageMother: INCOMING_MISSILE");
Although it does work, it is of course no guarantee it will work in the future. Specially with the change of the entity ID. My question to the coders: can we be sure that this will keep working? Or is it better to introduce a new exitAIWithMessage: command?
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Have you tried (exitAI, "setStateTo: SOMETHING")? As far as I can see it should work, although the RESTARTED message handler will run before the setStateTo: (so any reaction will have to be handled in the SOMETHING state’s ENTER handler).
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

Ahruman wrote:
Have you tried (exitAI, "setStateTo: SOMETHING")? As far as I can see it should work, although the RESTARTED message handler will run before the setStateTo: (so any reaction will have to be handled in the SOMETHING state’s ENTER handler).
Not tested, but it will of course work. But that means you loose the reason to switch AI in the first place because you want to return to the state were you left. e.g for a patrol ship heading to or from the planet. By just sending a message you keep the state were you left from.

And it works perfect at the moment. I found a trader in gotoWaypointAi. I fired a missile, It returned to its 'motherAI', there it received the INCOMINNG_MISSILE and than reacted correctly to jump into my new traderInterceptAi in the state FLEE_FOR_MISSILE. And when it dealt with he missile it felt back in the state were it originally came from: HEAD_TO_PLANET

The messageMother is send nicely to itself, even when it is a ship without escorts, so currently it always works. (Only not for escorts of course, but those never enter a gotoWaypointAI.)
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

I see no real problems in next code for the gotoWaypointAI.plist:

Code: Select all

{
	GLOBAL =
	{
		ENTER = ("setStateTo: NEW_WAYPOINT");
	};
	"GO_TO_WAYPOINT" =
	{
		"WAYPOINT_SET" = ("setStateTo: NEW_WAYPOINT");
		"DESIRED_RANGE_ACHIEVED" = (exitAI);
		ATTACKED = (exitAI, "messageMother: ATTACKED");
		"INCOMING_MISSILE" = (exitAI, "messageMother: INCOMING_MISSILE");
		UPDATE = ("setSpeedFactorTo: 0.75", performFlyToRangeFromDestination, checkCourseToDestination);
	};
	"NEW_WAYPOINT" =
	{
		ENTER = ("setSpeedFactorTo: 0.0", "setDesiredRangeTo: 50.0", checkCourseToDestination);
		"WAYPOINT_SET" = ("setStateTo: NEW_WAYPOINT");
		"COURSE_OK" = ("setStateTo: GO_TO_WAYPOINT");
		"DESIRED_RANGE_ACHIEVED" = (exitAI);
		ATTACKED = (exitAI, "messageMother: ATTACKED");
		"INCOMING_MISSILE" = (exitAI, "messageMother: INCOMING_MISSILE");
	};
}
I see in the code that on initialising a ship, the owner is always set to itself. Only when it becomes an escort or a defence ship the owner switches to an other ship. So a messageMother always sends the message to itself when it is owner of itself. Problems could only arise with custom scripts that have station defenders or escorts that use the gotoWaypoint.plist. Currently I don't know of such custom scripts but I know the current situation is not optimal.

For example: Find a trader, start flying in front of him until he enters the gotoWaypointAI. (without a console, you see this by the trader leaving its course). Than fire a missile at him. He will not react other that leave the gotoWaypoint and entering the route1traderAI. And when you are still blocking its way it returns again to the gotoWaypoint. But he will never use its ECM, start to flee or start an attack without above modification. (You only get an reaction when the missile hits him and he is still in the route1traderAI.)
Post Reply