Advertisement

AI and Self Modifying Code

Started by March 28, 2006 07:20 PM
15 comments, last by GameDev.net 18 years, 7 months ago
If you are exploring this area, be aware that OS writers (including those writing console operating systems) are slowly backing away from self modifying code.

Basically, it is seen as a security risk. If code is marked as 'read only', the system can trap virus/buffer-exploit attempts at taking control of the system. This is particularly risky on platforms where security is critical (ie the 360 and PS3).

If you go this route, be aware that your code might not work on some platforms in the future.
I guess one way for me to do it is to write code into a DLL file and then load and execute that portion of code which would make security less of a problem. When I need to update the code I could just unload the file write my code into it and reload it again.
Advertisement
In Prolog (like in Lisp, but maybe even more so) self-modifying code is nothing special, mainly because there's no difference between (in c/c++ terms) is date and program, the program is data and data forms a program. Data/Programs consist of facts and rules, generating rules/data in memory and saving them to file (or directly to file) is easy.

There is a good Prolog (Free, as in beer) called SWI-Prolog, it has an extensive c/c++ interface and you can integrate the prolog engine in your c/c++ program, the opposite is equally possible (for speeding up (imperative) procedures, since it's not always the fastest language.

But having said so, you can write a rule-based decision system in no time, and will be easy to maintain, and as I pointed out in the above, dynamically changing that rule-base is fully possible, so your program/agent can learn as it goes along.

Thinking again about where the trouble of implementing 'Self-Modifying Code' would pay off (specificly an AI usage) :

One would be filters/classifiers for sensory data customized to a specific objects behavioral patterns. My long term project has objects that inherit skills and abilities (potentially hundreds of them) and each skill has one or more behavioral 'solution' for a situational factor. Every new object that comes into an objects sensor awareness is to be classified (and its significance can change depending on distance, requiring re-evaluation). Priorities also shift depending on the objects internal state (ie- when hungry, identifying food increases in importance).

The constant reevaluation of dynamic sensory data (and on a rich detail world) is one of my concerns as to whether there will be enough CPU to run one such object let alone many of them. Optomizations like caching classifications as tracking data and only reevaluating periodicly (different periods depending on the initial classification...). Even with such measures the AI CPU load can still be prohibitive. Consider the overhead of calling over a hundred callbacks against each target object and each callback having to be generalized
in its logic.


The 'code' self-modify rebuilding process is equivalent to the interpretation scan that whould have to be done against the object's skill/ability set (which would all have to be done every classifier execution if self-modifying code isnt implemented). Each inheritable 'skill/ability' would have its own code building method (which may have options depending on some interrelation complexity and insertion of constants based on the objects current attributes/states). Macros no doubt will clean up implementing this part. Optomizing schemes like ordering the test logic so that all the highest priority evaluations are done first and a quick-exit (bypassing evaluations of a lower quantum of priority) could be done. Of course it would complicate the 'code building' process -- require sorting and appending to quantum blocks which are assembled/stitched together at the end...


Most of the code is a sequence of IF-THEN tests against attributes of the target object, including dynamic relations like range and possibly the objects 'stance' (ie- Objx Isa Troll AND Objx.Range<
#$%^$%^& imbedded html $%^&%^ glad i saved it on a file or I would be really pissed....


continued :




SafeRange AND Objx.stance==ANGRY ). Such logic is fairly regular and generating native code straightforward.
The matching THEN directive usually calls a function that adds a potential action/reaction or generalization to a result list and would include a Priority (which itself may be an equation/calculation to be semi-fuzzy). At the end of the classification, the result list (probably sorted by priority on the insert) allows the highest priority to be selected or further processing to be done to pick a proper reaction (ie- whats the best path to run from a group of lions when the result list has 3 of them identified as significant threats).
There may even be multiple result lists for specific classification types.
The classifiers output data can become input data for one of more risk/resource analysis.


If the code clauses are very regular, a bytecode mini-interpretor may be possible that wouldn't lose too much performance over native code. Having the
'code' be data would eliminate the 'write' permission access complication as well as be platform independant.


How often would the modifiable code need to be modified/rebuilt?..... it would be possible to break into two code blocks and put the logic blocks that change frequently in a second set to decrease the task of rebuilding (but that might interfere with sorting and starts to look real ugly to implement...). The periodic rebuilding may be a shorter interval for more intelligent objects (or ones that are schitzo in their behavior). Its workable as long as the cost of rebuilding is offset adaquately by the execution efficiency.


I will have to look more into this. Ive written several mini-interpretors before and a simple opcode/stack based/static parameter function/minimal imbedded math equation/single block depth type is fairly easy. The patterns for native code generation are actually mostly the same (just more bytes emitted).

Actually, if we are looking at AI scripts, then there's no reason why it can't be modified in real-time. Well, its just how you define "modify." You can fairly easily "swap" behavior sets.

So, if you have a creature and 10 sets of behavior scripts, you can cycle through them and see how well each one performs, then modify them as they get swapped out. Kind of like what you do in Genetic Programming. However, there's a reason why GP isn't very popular or used alot. There are infinite looping issues with solutions generated in GP, especially if you want to support looping as a feature. It gets really nasty because its not trivial to tell the difference between an infinite loop and one that's just executing a large number of times. Apart from that, if you maintain strong type checks, GP should work pretty well.
Advertisement
Quote: Original post by WeirdoFu
Actually, if we are looking at AI scripts, then there's no reason why it can't be modified in real-time. Well, its just how you define "modify." You can fairly easily "swap" behavior sets.

So, if you have a creature and 10 sets of behavior scripts, you can cycle through them and see how well each one performs, then modify them as they get swapped out. Kind of like what you do in Genetic Programming. However, there's a reason why GP isn't very popular or used alot. There are infinite looping issues with solutions generated in GP, especially if you want to support looping as a feature. It gets really nasty because its not trivial to tell the difference between an infinite loop and one that's just executing a large number of times. Apart from that, if you maintain strong type checks, GP should work pretty well.




The usage is a bit more complex than a simple modal 'switch'. Even if you have a object with a set of simultaneous behaviors (genes) that can be substituted (effectivly my objects with inheritted skills/abilities are something of like this), the mechanism that causes the code chunks to execute and how fine the customization can be is the issue.
Doing it either via 'switch' statements (very static/clumsy) or a more versatile link list of callbacks, there is still the overhead AND the code chunks themselves remain unoptimized (generalized). If you try to make the cistomization finer, then you multiply the number of callbacks or add option parameters to them.

If you rebuild (self-modify) code, you can within each chunk do simplifications (like value calculation to imbed as constant) and situationally select modal options (subchunks-- fine level customization) and possibly have reordering. You certainly cut out the selection logic and callback overhead.
The code chunk is pared down to only the code needed.


For my project such a system has merits because my AI objects are highly polymorphic (true polymorphism...) and the AI is a heavyweight planner/simultaneouus task system tied to a realtime simulation.
I need the processing savings that self-modifying code can probably deliver.
The sensor pre-filtering/classification function would be my first use.


This topic is closed to new replies.

Advertisement