Here’s a TBT blog post from our provisional patent filing for a method for simulating intelligence by programming a virtual mind. This foundational technology was used to create our proof of concept Quipples™ The Internet Game Show of Satirical Riddles in Adobe Director using the built-in object oriented Lingo programming language. Quipples is no longer running online but I may repost samples from it in a future TBT post. Stay tuned!
Detailed Drawings of the Invention
Description of the Invention
The method of this invention uses a number of programmed components to construct a virtual mind that may be used to simulate intelligence in computer software, interactive characters, systems, machines, robots, etc. in which they appear to think for themselves and react spontaneously to user, system, environmental and other external input and events.
One simplified example implementation of the method of this invention is outlined in FIG. 1, where a basic VirtualMind 1 is constructed and used to implement a simple InteractiveCharacter 2 and control 22 a character animation component 3. This simple example of the method of the invention is also capable of interacting with various external components 4, through their APIs 5. This VirtualMind can process and respond to external data and events 6 generated by the external components. It can also manipulate, influence and control 7 the external components by sending data and messages out to the external components, through their APIs. External components could include the following; computer software, applications, externally programmed interactive characters, computer operating systems, communications systems, network systems, databases, machines, robots, and any other external component or device for which an API exists or could be created with which to interface the VirtualMind.
The method of this invention for simulating intelligence begins with small object building blocks called Thoughts 8. A Thought object can perform a simple primary function and may also be linked to any number of other Thought objects through links of varying strengths called synapses. These simple Thought objects may be linked together in any number of ways to form more complex Thought objects. These more complex Thought objects may also be combined into yet more complex Thought objects and this construction can continue until the desired level of Thought complexity is achieved resulting in a rich ThoughtWeb 9. As Thoughts are executed and the synapses are followed, executed Thoughts trigger other Thoughts 20 throughout the ThoughtWeb.
The method of this invention includes a component called a ThoughtStack 10, which is responsible for processing the Thoughts and traversing the ThoughtWeb. Thoughts are pushed onto the ThoughtStack where they are executed. If a new Thought is pushed onto the ThoughtStack while an existing Thought is being executed, the original Thought may be suspended while the new Thought is executed. When the new Thought is finished, it is popped off the ThoughtStack and the previous Thought may resume execution. A single ThoughtStack may be used or multiple ThoughtStack may be run concurrently, enabling multiple trains of thought to be processed simultaneously. The simple example illustrated in FIG. 1. uses a single ThoughtStack.
The method of this invention includes a component called the Mood 11, which regulates the relative personality of the VirtualMind, and in turn, the software or hardware being controlled. The Mood may be implemented in a variety of ways, from a simple sliding scale that ranges from angry to excited, to multiple sliding scales, each representing a different mood facet, such as happiness, attention, pain, well-being, etc. If using multiple scales, they may be combined into two or higher dimensional tables or data structures. Changes in the VirtualMind’s Mood may influence and/or trigger Thoughts. Thoughts may in turn influence or change the Mood resulting in a bi-directional feedback loop 12. In this simple example implementation, the Mood influences the character animation component, which may play different or modified animation, depending upon the VirtualMind’s current Mood.
The method of this invention includes a component called the SenseStack 13, which receives input 6 from the user, the environment and other external components 4. There may be one or more SenseStacks. There could be a specific SenseStack for each different type or group of input events. The simplified example in FIG. 1. illustrates a single SenseStack which handles all input events. Events from the user or environment are passed from the system to the VirtualMind’s SenseStack(s) as event objects 14. Event objects may include objects that represent typed data input, voice input, audio input, visual input, mouse movements, time based events, Internet based events or any other sources of input available from external components 4. Event objects can be used to trigger Thoughts and Thoughts can also process event objects 15.
The method of this invention includes short and long term memory components. As events from the SenseStack(s) are processed by various Thoughts they may be discarded or stored 16 in short term 17 or long term memory 18, along with Thoughts themselves, for further use by the VirtualMind. Stored Thoughts, data and events may also be used as a source of data collection for the users and/or deployers of the system through exporting data 19 as text files, XML data, databases, generating e-mail, etc. back through the external components 4 through their APIs 5.
Thoughts can store/retrieve 16 Thoughts, data or events to/from short term memory 17, which can in turn can store/retrieve 21 Thoughts data or events to/from long term memory which in turn can also store/retrieve 19 thoughts data or events to/from external components where they may be stored/retrieved by the user and/or other systems. Short term memory can also trigger Thoughts 16 that may have been stored in short or long term memory or even stored in external components. This enables the VirtualMind to have new Thoughts injected into it from external sources.
The most basic building block of this method for simulating intelligence is the simple Thought object. Thought objects are modular, flexible and run-time extensible. They may be custom programmed or even expressed as XML data structures for storage and retrieval to/from various external components. In this simplified example of a VirtualMind, used to control a simple InteractiveCharacter, a small set of relatively simple Thoughts will be used to illustrate this one simple implementation of the method of this invention. This particular embodiment of the invention was programmed in the object-oriented Lingo language of Macromedia Director, however the method of this invention is equally applicable to any other software or hardware language in which it could be programmed.
FIG. 2 illustrates a basic structure that may be used to design the Thoughts and the ThoughtStack which executes them. In order to understand how the Thoughts themselves work, it is necessary to first understand the unique construction of the ThoughtStack and its ancestor classes to create an instance of the VirtualMind 1. In this simple example the VirtualMind is derived from a single ThoughtStack, though it could also be constructed in such a way to handle any number of ThoughtStacks to process multithreaded Thoughts.
The method of this invention used to construct the VirtualMind begins with a simple generic Stack object 24, which is used to manage a linear dataList.
Stack Class ----- Attributes order -- stack order, one from the orders list, default is #FILO orders -- list of possible orders [ #FILO, #FIFO, #RAND ] dataList -- the list of data in the stack listLength -- the length of the dataList nextIndex -- the stack index for the next item to be returned ----- Services -- push(x) Push item x onto the stack -- pop() Pop the NEXT item off the stack, returns VOID on underflow -- peek() Peek at the NEXT item on the stack without popping it off -- Returns VOID on underflow -- onStack(x) Returns TRUE if item x is already on stack or FALSE if not -- stackCount() Returns the count of the items currently on the stack -- clear() clears the contents of the stack
In typical object-oriented programming, a class is built with a predetermined number of preprogrammed services which it can execute when it receives messages. The method of this invention uses a unique specialization of the Stack class, called an ActivityStack 25, to instead give any subclass the ability to add/delete and execute a dynamic set of dynamic services. The services themselves are built as objects called Activities which can be added/removed from the ActivityStack’s activityList 29 where they may be stored. Activity objects may also be stored elsewhere and passed to the ActivityStack for processing at run time. In this manner, a specialized ActivityStack class can perform dynamic run-time extensible services.
ActivityStack Class ----- Attributes property Ancestor -- A specialization of the Stack class property activityList -- property list of named Activity objects ----- Services -- addActivity(name,object) Adds a named activity object to the activityList -- deleteActivity(name) Deletes a named activity from the activityList -- doActivity(name/activity,argList) Push activity on Stack(self) and init it -- currentActivity(name) See if the named activity is on top of the Stack -- pop() Pop Activity off Stack & resume prev. Activity -- stepFrame() Pass stepFrame to Activity on top of Stack for incremental processing
When a message is received by a specialization of an ActivityStack to perform an Activity (doActivity), it checks to see if it was passed an activity name or an actual activity object. If it’s a name, it retrieves the activity object from its activityList. It pushes a link to the Activity object onto itself (since the ActivityStack is itself a specialization of a generic Stack) where it executes the Activity by calling the Activities init() service. It also allows the Activity to perform incremental processing (In Lingo this is done by passing stepFrame() messages to it) until it finishes its task. When an Activity finishes its task, it pops itself off the ActivityStack and any previously running Activity may be resumed.
In this simple example of the method of this invention, the Activity class 26 is really only a template used to help indicate the basic services that a derived Activity should include. Its function in this Lingo example is similar to using pure virtual functions in C++.
Activity Class ----- Attributes property Ancestor -- Derived from a specialized ActivityStack class ----- Services -- init(argOrArgList) Initialize and/or invoke activity with argument list -- resume() resume incremental processing -- stepFrame() perform a single incremental processing increment
These basic interface services of an Activity are declared for all derived Activities, but their actual definition and implementation is left to the derived Activities themselves.
Also notice another break with conventional object-oriented programming here. The Activity is itself derived from a specialized ActivityStack. It is not really a specialization of an ActivityStack, in the normal sense, but still derived from it so that it may be used much like a simple method of a specialized ActivityStack. In this way a specialized Activity may use Me.doActivity( #activityName, activityArgList ) (Me in Lingo would be this-> in C++) to invoke other specialized Activities belonging to the same specialized ActivityStack. This unique method allows the use of specialized Activities, of a specialized ActivityStack, to become a much more powerful and extensible structure than a normal class with its normally static predefined services.
In this simple example of the method of this invention for simulating intelligence, the ActivityStack 25 is further specialized into a ThoughtStack 10.
ThoughtStack Class ----- Attributes property Ancestor -- A specialization of an ActivityStack class ----- Services -- addActivity( [ packageName, thoughtName ], ThoughtObject ) Adds a Thought object to the activityList -- deleteActivity( [ packageName, thoughtName ] ) Deletes a named Thought from the activityList -- doActivity( package/ThoughtNameOrObject, argList ) Invoke a Thought from the activityList -- currentActivity( [ packageName, thoughtName ] ) See if the named thought is on top of the Stack -- push( package/ThoughtNameOrObject ) Push thought on stack by Thought or package/Concept
The main difference between an ActivityStack and a ThoughtStack is that the ThoughtStack has its activity list extended from a one dimensional property list to a two dimensional property list (additional dimensions are also possible depending upon the complexity desired.) This allows the ThoughtStack to better organize its specialized Activities into functional groups or packages 30 of specialized Thoughts 31. This makes changing/adding/managing entire new packages or sets of Thoughts more flexible and efficient at run time.
The method of this invention includes a specialization of the Activity class called a Thought 27.
Thought Class ----- Attributes property Ancestor -- A specialized Activity property moodModifier -- Optional amount to change the mood ----- Services -- init(argOrArgList) Send moodModifier to Mood component
The Thought class includes any specialized code that all Thoughts may share. In this simple example of one possible implementation, the Thought class includes a moodModifier. When a specialized Thought is invoked by calling its init() method, it passes the init() message through to this Thought ancestor class, which sends its moodModifier attribute to the Mood component 28 to modify the Mood of the VirtualMind. In this way, if the VirtualMind executes a “happy” Thought, for instance a laugh Thought, it may in turn make the VirtualMind even more “happy” which may in turn affect which additional Thoughts are invoked, creating a simple feedback loop.
For simplicity, in this example implementation of the method of this invention for simulating intelligence, the Mood component 11 is relatively simple and functions by primarily managing and tracking a couple of sliding scales and counters. Of course in a more complex implementation, additional sliding scales and/or multidimensional scales could be used to represent an even more complex Mood instead.
Mood Class ----- Attributes property Mind -- Pointer back to this Mood’s Mind property moodNum -- Current mood scale: float +1.0 <-> -1.0 property moodName -- Current mood name from the moodList property moodList -- [#Excited, #Happy, #Neutral, #Sad, #Angry] property mouseAttention -- Mouse attention scale: float 0.0 <-> 1.0 property mouseMovedTime -- Time mouse last moved property lastEventTime -- Last time an event appeared on the SenseStack property abuseLimit -- how tolerant mind is to abuse property abuse -- current level of abuse counter property excitedLimit -- how tolerant mind is to being over excited property excited -- current level of overexcitement ----- Services -- activate() reset mood when Autiton is activated -- getMoodNum() retrieve current mood number -- getMoodName() retrieve current mood name -- getEnergy() retrieve current energy or agitation level -- getSerenity() retrieve calmness level (inverse of energy) -- getPositivity() retrieve level of positive outlook -- getOffsetMoodName(x) retrieve mood name offset x from current mood name -- bored() check if Autiton is bored, really bored or not -- resetBoredom() reset bored timer -- mouseFactor() create mouseFactor from mouseAttention and moodNum -- moodSwing(x) switch moodNum by x and update name etc. if necessary -- setMood(x) directly set moodNum, usually used internally
In this simple example of a mood component the first item being tracked is a scale called the moodNum, which is a sliding scale from +1.0 to -1.0. This number may be used directly in calculations within Thoughts that make different decisions based upon the VirtualMind’s current Mood state. In this particular implementation, there are also a number of access services which translate the mood number into different interpretations for direct use by Thoughts and/or other components of the InteractiveCharacter such as the animation component. This effectively translates the single mood scale into numerous other scales that are all tied together.
getEnergy returns abs( Me.moodNum ). Which is the current level of energy or agitation that the VirtualMind is experiencing. A neutral mood represents a low energy or a state of calmness. As the VirtualMind becomes more excited or more angry, the level of energy or agitation rises. This is used for many purposes which may directly affect the processing of Thoughts, such as affecting the rate at which new Thoughts are being initiated, How long pauses may be in the VirtualMind’s Thought processes, which Thoughts are invoked, etc. It is also used to influence the speed of motion and animation by the animation component, based on the current getEnergy() of the VirtualMind, different animation may be used and/or the animation may be played at different speeds or to different extents, etc.
getSerenity returns 1.0 – Me.getEnergy() and is the inverse of getEnergy. As such it is used by Thoughts that make decisions based upon how calm the VirtualMind is. It may also be used by the animation component to adjust the animation being played.
getPositivity returns (Me.moodNum + 1.0) / 2.0. This translates the mood from the range of +1.0 to -1.0 into the new range from 0 to +1.0. This represents how positive the VirtualMind currently is, from not positive, to fully positive. It is also used by various Thoughts that may make decisions based upon how positive the VirtualMind currently is, as well as by the animation component to influence any animation that may be playing.
In a more complex implementation of the method of this invention, separate independent scales or multidimensional scales could be used to track these mood components just described and other mood components as well, using modified and/or additional algorithms.
In this simple example of a mood component this mood scale is also divided up into five distinct mood names from the following list [#Excited, #Happy, #Neutral, #Sad, #Angry]. The algorithm used to select the current mood name from this particular moodList using the mood number is as follows:
Me.moodName = Me.moodList[ integer( Me.moodNum * 2 ) + 3 ]
In this particular implementation where the method of this invention is used to create a VirtualMind that simulates intelligence in a simple InteractiveCharacter with a simple animation component, whenever the moodName changes, the new name is passed out to the animation component so that new animation may be selected. This way if the character is currently happy, all the facial expressions and lip sync animation, etc., may be from a set of happy faces. If the character is currently angry, the animation from an angry set may be used. Any number of distinct moodNames may be used to drive any number of different animation sets (with appropriate algorithm adjustments of course). The moodName may also be used directly by any Thoughts which may also use the current moodName to select other Thoughts to invoke or to influence decisions being made, as will be shown shortly.
In this particular implementation where the method of this invention is used to create a VirtualMind that simulates intelligence in a simple InteractiveCharacter with a simple animation component, another sliding scale being monitored is the mouseAttention which ranges from 0.0 to 1.0. The mouseAttention represents how interested the InteractiveCharacter is in watching the cursor on the screen. If the user moves the cursor, the character may look at the moving cursor or he may not. It depends on a combination of the current mouseAttention and the current moodNum using the following algorithm.
mouseFactor = ( (Me.moodNum + 1)/4.0 ) + Me.mouseAttention / 2.0
In this implementation, for example, the mouseFactor may be used by EventThoughts that process mouse movements to determine if the character will look at the mouse or not if it is clicked or moved. If the mouseFactor is high, the character may even look at the mouse just to see if it has moved or not.
In this particular implementation of the method of this invention, another item being tracked by the Mood is the level of boredom. Thoughts can make different decisions based upon whether the VirtualMind is currently bored or not. This particular example of the bored scale uses the characters current serenity (as previously discussed) to adjust how long it takes the virtual mind to become bored since the last time the VirtualMind has received any user input events on the SenseStack, represented by the value in Me.lastEventTime. The following algorithm returns 1 if currently bored ( inactivity between 5-8 minutes ) or 2 if really bored ( inactivity between 8-12 minutes), or 0 if not bored.
if the ticks - Me.lastEventTime >= \ (8 * 60 * 60) + ( 4 * 60 * 60 * Me.getSerenity() ) then return 2 else if the ticks - Me.lastEventTime >= \ (5 * 60 * 60) + ( 3 * 60 * 60 * Me.getSerenity() ) then return 1 else return 0 end if
The mood is also responsible for regulating and modifying the mood scales themselves. This is typically done by a Thought sending a moodModifier value to the Mood’s moodSwing() service which adds the value to its moodNum. moodSwing() also checks for out of bounds conditions which may be used to trigger other changes to the moods or trigger other thoughts. One simple example of modifying mood/behavior when a scale goes out of bounds is that whenever the mood goes below -1 (which is beyond angry) an abuse attribute is incremented and the moodNum is reset to -1. If the abuseLimit is finally reached, then the interactive character has had enough of the user, a HangupThought is triggered and the character may hang up on the user, closing the Internet connection or quitting the program. A similar attribute, excited, is used to track how many times the mood goes above +1.0, meaning the virtual mind is over excited. If the excitedLimit is finally reached, that triggers a mood change, such as a sudden drop from excited to angry, and other thoughts are triggered where the interactive character may tell the user to stop doing whatever they are doing that is overexciting the character.
The method of this invention for simulating intelligence uses some basic Thought objects as building blocks to construct a rich ThoughtWeb. It’s the execution of these Thoughts and the spontaneous transversal of the ThoughtWeb using the previously described components that give the VirtualMind the basic ability to simulate intelligence FIG.3.
A simple example of a basic Thought in this simple implementation example of simulating intelligence in an InteractiveCharacter is a CommandThought 36. A CommandThought contains a text string and executes the string in whatever underlying computer language that the mind instance happens to be programmed in (in this simple implementation example, the underlying computer language is Macromedia Director’s Lingo language).
CommandThought Class ----- Attributes property Ancestor -- A specialization of a Thought property commandString -- text string to perform as a do statement ----- Services -- init() Perform the commandString using a Lingo do statement
This allows a Thought object to be able to perform any action that the underlying computer language is capable of performing. Since it’s a text string, interpreted at the time of execution (using a do statement in Lingo, an eval statement in Unix, or an equivalent statement in another language), it also would enable Thoughts to be added at run-time, from the user, the system, the Internet or the VirtualMind itself, which, in a more complex implementation of the method of this invention, could potentially be one method to allow the generation of new thoughts on the fly.
A specialized form of a CommandThought is a ControlThought 49. A ControlThought is used to control another component of the system. In this simple implementation example it can be used to send messages to the Character Animation Component and/or any other External Component API.
ControlThought Class ----- Attributes property Ancestor -- A specialization of a CommandThought property controlMessage -- text message to send to an external API ----- Services -- init() Send message using a Lingo do statement, via ancestor
A ControlThought may be further specialized to define which API to interface with and exactly how to do so, depending upon each particular API being communicated with.
Using the method of this invention in this particular simplified example of simulating intelligence in an InteractiveCharacter, one of the primary Thought objects is the PhraseThought 32. A PhraseThought is a basic Thought which, in this simple example, can instruct the character animation component to have the character speak 33 a single segment of dialog which could be a single word, a phrase, a sentence or a longer segment of dialog.
PhraseThought Class ----- Attributes property Ancestor -- A specialization of a Thought property phrasePath -- the SUB path to this audio file property phraseID -- The phrase ID number within my phrase folder property fileExt -- The file extension, ".SWA" property lipSyncString -- the phonetic text string for lip sync property resumable -- flag indicating if this thought object is resumable\ -- 1 = resumable, 0 = not resumable, -1 = only resumable property target -- who Autiton is speaking to #user, #none, default is #user ----- Services -- init() Tell the animation component to speak the line -- resume() resume (reinitialize) if necessary -- finished() pop itself off the ThoughtStack
When a PhraseThought is pushed on the ThoughtStack which invokes it by sending it an init() message, the PhraseThought sends a message to the character animation component with the complete path or URL of the audio file to be played, along with a line of text that describes the phonetic composition of the audio file. The character animation component then uses this information to play the dialog segment while performing lip sync animation and looking at the user, if the user is the target being addressed.
When the character animation component finishes the dialog, it sends a finished message back to the PhraseThought, which then pops itself off the ThoughtStack, allowing any previous Thought to be resumed. If a PhraseThought is currently running and a new Thought is executed which interrupts the current PhraseThought, when the interrupting Thought is done and popped off the ThoughtStack, the ThoughtStack sends a resume message to the PhraseThought which can then reinitialize itself thereby instructing the character animation component to speak the line of dialog again, if the PhraseThought is flagged as resumable. If the particular PhraseThought is not resumable, it would simply pop itself off the ThoughtStack and allow any previous Thought to resume.
In this simple implementation, an example of a PhraseThought could contain the following
A URL to an audio file that spoke the word “Hello”
The text string “Halow” which is the phonetic composition of the audio file for lip sync.
A target of #user, which indicated that it is to be spoken to the user.
A moodSwing of 0.05 which makes the VirtualMind a little happier when the PhraseThought is invoked.
Such a phonetic lip sync string and target are determined by the particular character animation component that the VirtualMind is being interfaced with. They are only used here to give a concrete example of using a PhraseThought to send a command to a character animation component.
-- phonetic lip sync lookup Mouth: Used for... -: Closed A: AI E: E O: O C: CDGHJKNRSTXYZ F: FV M: MBP L: L(TH Replace with L) Q: QUW
Of course in other implementations of the method of this invention, such a PhraseThought could be used for many other purposes; such as instructing an external voice synthesis component to speak a line of dialog, with or without accompanying lip sync or other animation; an interactive text engine to display a line of text dialog; an animation engine to play a particular sequence of animation; or any system robot or machine to perform a simple action, etc.
Thoughts may also be further specialized by introducing new class types. In this simple implementation example for instance, a PhraseThought could be further specialized into two new sub classes that are interruptible
FIG. 3: 34
TypeInterrupt PhraseThought Class ----- Attributes property Ancestor -- A specialization of a PhraseThought ----- Services -- stepFrame() Stops the PhraseThought if the user starts typing
FIG. 3: 35
InputInterrupt PhraseThought Class ----- Attributes property Ancestor -- A specialization of a PhraseThought ----- Services -- stepFrame() Stops the PhraseThought if anything’s on the SenseStack
In the method of this invention, Thoughts may be linked together into larger structures using a special type of Thought known as a SynapseThought. SynapseThoughts are primarily composed of one or more links to other Thoughts along with a mechanism for determining which of its Thought links to invoke, if any. SynapseThoughts may be used in any combination to create a rich and complex ThoughtWeb, Figure 4.
The most basic SynapseThought is the ConditionalThought 37, which evaluates a text string to determine which of up to two possible thought links to follow, if any.
ConditionalThought Class ----- Attributes property Ancestor -- A specialization of a Thought property conditionString -- Test string to evaluate as a do statement property TrueThought -- Link to Thought to trigger if testString is TRUE property FalseThought -- Link to Thought to trigger if testString is FALSE ----- Services -- init(argList) Evaluate testString and if TRUE, invoke TrueThought -- If FALSE, invoke FalseThought, if there is one.
In Lingo this is accomplished as follows:
testResult = 0 -- must create it before using it in a do statement do "testResult =" && Me.conditionString if testResult AND objectP( Me.TrueThought ) then Me.doActivity( Me.TrueThought, argList ) else if objectP( Me.FalseThought ) then Me.doActivity( Me.FalseThought, argList ) end if
Another example of a SynapseThought is a ProbableThought 38 which is derived from the ConditionalThought. A ProbableThought is a ConditionalThought with a probability modifier added to the conditional test, to allow the creation of synapse links of various strengths. Such synapses of various strengths are one key element that gives the method of this invention a higher degree of simulating intelligence, by creating a ThoughtWeb with loosely defined thought probabilities, rather than absolute, prescripted thought patterns, as may be typically used in other methods.
ProbableThought Class ----- Attributes property Ancestor -- A specialization of a ConditionalThought property probability -- The percentage probability 0-100 ----- Services -- init(argList) Evaluates (probability [AND testString]) -- If TRUE, invoke TrueThought -- If FALSE, invoke FalseThought, if there is one.
In this case a random function is used to evaluate the probability test. If a string conditional test is also supplied, it is also evaluated. The two results are combined with a boolean AND test and if TRUE, the TrueThought is invoked. If FALSE, the FalseThought is invoked, if any.
This is accomplished in Lingo as follows:
probabilityResult = random(100) <= probability conditionResult = 1 -- if conditionString supplied, evaluate it if stringP(Me.conditionString) then do "conditionResult =" && Me.conditionString end if finalResult = probabilityResult AND conditionResult if finalResult then if objectP( Me.TrueThought ) then Me.doActivity( Me.TrueThought, argList ) end if else if objectP( Me.FalseThought ) then Me.doActivity( Me.FalseThought, argList ) end if
A SynapseThought that can be used to tie a larger number of Thoughts together, in the method of this invention, is the StackThought 39 .
StackThought Class ----- Attributes property Ancestor -- A specialization of a Thought property StackOfThoughts -- A Refillable or Infinite Stack of Thought links property doAll -- T/F Flag if all at once or one at a time property infinite -- T/F Flag if refills when done or just stops ----- Services -- init() Push on ThoughtStack if doAll, then doNextThought() -- stepFrame() doNextThought() -- addThought() add a Thought to the StackOfThoughts -- doNextThought() pop the next Thought off StackOfThoughts and invoke it.
A StackThought contains a Stack 24 of links to Thought objects which may be triggered in linear (FIFO or FILO) or random order. One right after the other, until all are finished (if doAll is TRUE), or one at a time, each time the StackThought itself is triggered. A StackThought’s StackOfThoughts is either an InfiniteStack 41 or a RefillableStack 40, depending on whether the StackThought’s infinite flag is TRUE or FALSE. A RefillableStack maintains a masterList of all its items so that it may be refilled if desired. An InfiniteStack is a RefillableStack that automatically refills itself when its last item is popped off it.
A StackThought may be specialized in any number of ways. In this simple implementation example of the method of this invention, an example of a StackThought specialization is the ThinkThought 42.
ThinkThought Class ----- Attributes property Ancestor -- A specialization of a StackThought ----- Services -- init() Push on ThoughtStack, tell Character to #think -- resume() Tell Character Animation Component to #think -- stepFrame() Delay & Tell Character to think before doNextThought()
A ThinkThought is a StackThought that has a built in random delay (modified by the current Mood) between invoking its Thoughts. During this delay it also sends messages to the Character Animation Component instructing the animated character to appear to be thinking and may even generate “I’m thinking” thoughts. When the delay is over and after doNextThought() is called, it may also push an “I had an idea” Thought on top of the ThoughtStack to be invoked before the Thought pushed from doNextThought() is invoked.
Another SynapseThought that contains a list of links, is a CaseThought 43. A CaseThought contains a list of Thought links, along with a two dimensional list of corresponding target strings. When a CaseThought is invoked, it’s passed a target string or an integer index. When passed a string, the CaseThought searches each list of target strings and if it finds a match, the corresponding Thought object is invoked. If it’s passed an integer, it invokes the Thought object at that index in the list. This enables Thoughts to be associated with lists of keywords or indexes, any one of which can trigger a corresponding Thought.
CaseThought Class ----- Attributes property Ancestor -- A specialization of a Thought property ThoughtList -- List of Thought links property KeysList -- 2D List of key strings, one sublist for each Thought property caseCount -- Number of Thoughts in list ----- Services -- init(keyORindex) Invoke Thought by key string or integer index
In this simple implementation example of the method of this invention for simulating intelligence, a key SynapseThought is the PhraseListThought 44. A PhraseListThought may be derived from either a StackThought or a CaseThought, depending on how its list of Thoughts are to be invoked.
PhraseListThought Class ----- Attributes property Ancestor -- A specialization of a StackThought OR a CaseThought property built -- Flag if this thoughts subthoughts are built yet. property dialogKeyList -- package/concept/group list to locate dialogList property dialogList -- dialog list used to build the sub Thoughts property PhraseListPath -- Path object that each phrase can add its ID to ----- Services -- init() If not built, then buildThoughts() prior to invoking -- buildThoughts() Build all the individual PhraseThoughts in the PhraseList
One of the primary reasons for creating this derivative PhraseListThought in this simple implementation example is to allow delayed creation and building of the linked PhraseThoughts. Even in a simple implementation which could easily contain thousands of possible PhraseThoughts, simply constructing all those Thoughts takes time. All the individual PhraseThoughts, linked to a PhraseListThought, only need to be constructed if and when the particular PhraseListThought is invoked. If a particular PhraseListThought is never invoked, then none of its linked PhraseThoughts ever need to be constructed, unless of course if they are also linked in some other PhraseListThought that is invoked. Until the PhraseThoughts actually need to be invoked (and therefore built) they can instead remain in a simpler form of a text list of attributes, file/URL paths and lip sync lines, in a multidimensional array of packages and Concepts organized to mirror the ThoughtStack’s 10, multidimensional ActivityList.
In a more optimized implementation of the method of this invention, this delayed construction of Thoughts could be pushed further downline into the PhraseThoughts themselves. In such a further optimized implementation, when a PhraseListThought is invoked, and therefore built, it would only actually build the particular PhraseThought to be invoked, rather than all it’s linked PhraseThoughts, which may or may not be invoked.
In this simple example implementation of the method of this invention for simulating intelligence, the multidimensional text array used to construct all the PhraseListThoughts, along with other variables, attributes and values being tracked, is stored in the VirtualMind’s Short Term Memory 17 which upon the VirtualMind’s initialization is read and constructed from disk files stored on the Internet which together with preference files on the user’s machine, comprise the VirtualMind’s Long Term Memory 18. When the VirtualMind is shut down, any changes to the VirtualMind’s Short Term Memory are then written out to the Mind’s Long Term Memory comprised of the disk files at Internet URLs as well as on the user’s machine.
The following is an example of how the text entries for a PhraseListThought representing a “Neutral” group in a “Hello” Concept of an “Intro” Package could be written in Lingo in a Long Term Memory file. Notice that the first entry in the list is used to store the settings for the entire list. This way when the PhraseListThought is invoked, it builds each of its linked PhraseThoughts using the same set of settings attributes. Any of these attributes may also be overridden for any individual PhraseThought by including the overriding attribute and value in the individual PhraseThought’s list, as seen in PhraseThought P005 below. This implementation example is written in Lingo, but the method of this invention including Long Term Memory files could also be implemented in other computer languages such as C++ or even as XML data structures.
package = #Intro concept = #Hello group = #N settings = \ [ #Resumable:TRUE, #Order:#RAND, #DoAll:FALSE, #Infinite:TRUE, #moodSwing:0.05 ] ps = [:] addProp Ps, #Settings, settings addProp Ps, #P001, [ #lipSync:"Halloou-----" ] addProp Ps, #P002, [ #lipSync:"LaarrHeearr---" ] addProp Ps, #P003, [ #lipSync:"HalouSirr-" ] addProp Ps, #P004, [ #lipSync:"HallowLeerr-----" ] addProp Ps, #P005, [ #lipSync:"NiiinouHeerr-----", #moodSwing:0.25 ] addProp Ps, #P006, [ #lipSync:"Niinous-Residanc------" ] ShorTermMem.setDialogList( package, concept, group, Ps )
The paths/URLs to the audio files are not listed above because in this simple implementation example they are built dynamically by adding the package, concept, group, PhraseID and extension to a baseURL in the following manner.
PhraseListThoughts are used to organize individual PhraseThoughts (and other Thoughts as well) into related groups of Thoughts called Concepts. A Concept is a single idea that may be expressed in a number of different ways. These are most easily explained by example.
An example of a linear PhraseListThought with doAll = FALSE could be a Concept for “stop” which could be expressed as a single phrase taken from a list in order, one each time the Concept is invoked, from a set such as:
OK Got it That’s enough Cut it out Enough already Stop it I said stop it You had better stop that right now! STOP THAT! THAT’S IT! I’VE HAD ENOUGH! NOW YOU’VE DONE IT!!
In this example, each time the Concept is invoked the expression of that concept is stronger and stronger. Also, since any Thought 27 may also have a mood modifier, each successive invocation of such a “stop” Concept could also affect the Virtual Mind’s Mood, in this simple example implementation, making the interactive character more and more angry.
An example of a random PhraseListThought could be a Concept for “Hello” which could be expressed with a random phrase from a set such as:
Hello Hi Howdy Hi there Hello there Hola
An example of a linear PhraseListThought (with doAll = TRUE) could be a concept for “Introduction” which could be expressed as a linear list of phrases invoked one right after another, such as:
Hi My name is Mike I’m an Autiton I’m very glad to meet you.
Remember that each phrase is actually a PhraseThought and since they are all Thoughts, other Thoughts may be used in PhraseListThoughts as well, including any other SynapseThoughts, even additional nested PhraseListThoughts which may be built in a recursive manner. They may be combined in an infinite number of ways to achieve the desired level of complexity.
So the example “Introduction” PhraseListThought could also be constructed as follows:
“Hello” Concept “I’m” Concept “Name” Concept “I’m a” Concept “MeetYou” Concept
Where each Concept in the list is actually a random PhraseListThought of its own which chooses a random PhraseThought each time it’s invoked. Such compound Thoughts allows the VirtualMind to simulate intelligence and act much more spontaneously than would be possible using many other prescripted methods.
In this simple example implementation of using the method of this invention for simulating intelligence in an interactive character, another SynapseThought is a MoodyThought 45.
MoodyThought Class ----- Attributes property Ancestor -- A specialization of a Thought property ThoughtList -- List of 1-5 Thought links ----- Services -- init() Invoke a Thought based upon current Mood state
In this simple implementation example using the simple Mood 11 outlined previously, a MoodyThought contains a list of links to from one to five other Thoughts to be invoked depending on the current Mood. When the MoodyThought is invoked, it queries the Mood and then uses the current mood state to determine which of its linked Thoughts to invoke. One possible example implementation (in Lingo) of which follows:
on init Me, argList if Me.Self.active AND NOT Me.onstack(Me) then case count( ThoughtList ) of 1: -- only one thought, so return it whichThought = 1 2: -- there is an Excited/Happy or a Sad/Angry thought if Me.Self.ptrs.Mood.getMoodNum() < 0 then whichThought = 1 else whichThought = 2 3: -- there is an Excited/Happy, Neutral and Sad/Angry thought case Me.Self.ptrs.Mood.getMoodName() of #Angry, #Sad: whichThought = 1 #Neutral: whichThought = 2 #Happy, #Excited: whichThought = 3 end case 4: -- there is an Excited, Happy, Sad, and Angry thought case Me.Self.ptrs.Mood.getMoodName() of #Angry: whichThought = 1 #Sad: whichThought = 2 #Neutral: -- there is no neutral thought, see if above or below zero if Me.Self.ptrs.Mood.getMoodNum() < 0 then whichThought = 2 else whichThought = 3 #Happy: whichThought = 3 #Excited: whichThought = 4 end case 5: -- there is a thought for each mood case Me.Self.ptrs.Mood.getMoodName() of #Angry: whichThought = 1 #Sad: whichThought = 2 #Neutral: whichThought = 3 #Happy: whichThought = 4 #Excited: whichThought = 5 end case end case Me.push(Me) -- as a placeholder so not pushed on more than once Me.doActivity( Me.ThoughtList[ whichThought ], argList ) -- trigger one of the thoughts end if end init
In this simple example implementation of the the method of this invention, another related SynapseThought is a MoodyPhraseListThought 46. This type of thought is a MoodyThought built of from 1 – 5 PhraseListThoughts. This way, based on the character’s Mood at the time that the thought is triggered, a different PhraseList thought would be invoked to reflect the characters current Mood. The selected PhraseListThought would then invoke one of its individual PhraseThoughts. As in the PhraseListThoughts, the building of the MoodyPhraseListThought is delayed until it is invoked. An example of a moodyPhraseListThought might be as follows, where each mood has an associated random PhraseList:
Correct Excited: Absolutely!, Great!, Correct!, You got it! Happy: Right, Good, OK, Swell Neutral: OK, Yup, okey dokey Sad: I guess so, sure, OK Angry: Whatever!, Fine!, Whatever you say...
This way, if the VirtualMind thinks “Correct,” it would express the Concept with a random PhraseThought from the PhraseListThought associated with the current Mood; Excited, Happy, Neutral, Sad or Angry.
Another SynapseThought in this simple implementation example is the WaitAnswerThought 47. This is a Thought that instructs the InteractiveCharacter to wait for the user to provide some input.
WaitAnswer Class ----- Attributes property Ancestor -- A specialization of a Thought property waitStart -- Ticks when we started to wait property waitLength -- Number of ticks to wait property QuestionThought -- The thought to repeat question if necessary property AnswerThought -- The thought to pass the answer to when received property NoAnswerThought -- The thought to execute if time expires with no answer property TickThought -- The thought to execute periodically to remind user ----- Services -- init(QuestionThought) Start timer, push itself on the ThoughtStack, wait -- resume() Instruct Character Animation Component to wait -- stepFrame() If input on SenseStack, process it, otherwise wait
The WaitAnswerThought has variable attributes that determine the amount of time to wait and what Thoughts to have, if any, while waiting (TickThought), including repeating the QuestionThought. When an answer is available on the SenseStack, it is then passed to an AnswerThought which is typically a CaseThought that will scan the answer to see if it matches what the character might be looking for, and in turn trigger a CorrectThought or IncorrectThought. If no answer is received before time runs out, then the NoAnswerThought is invoked, if any.
In this implementation example, a WaitAnswerThought can be used linked from another SynapseThought called a QAThought 48.
QAThought Class ----- Attributes property Ancestor -- A specialization of a Thought property QuestionThought -- The thought that asks a question property WaitAnsThought -- The thought to wait for and handle the answer ----- Services -- init() Do the QuestionThought, if any and push on ThoughtStack -- resume() Do the WaitAnswerThought, if any, and pass QuestionThought
When the QAThought is invoked, it invokes its QuestionThought. When its QuestionThought finishes and the QAThought resumes, the QAThought then invokes its WaitAnswerThought with a link to itsQuestionThought. The WaitAnswerThought then waits for and responds to the user’s answer as described previously.
Using the method of this invention, including all the previous Thought objects and others derived from these, together with the described components of the VirtualMind, it is possible to design and build a deep and complex ThoughtWeb which enables a VirtualMind to simulate intelligence and control other systems such as interactive characters, software applications, Internet applications, operating systems, mechanical robots and any other type of information, electronic or mechanical system that may be programmable.
Michael Scaramozzino, President / Creative Director
DreamLight Incorporated, DreamLight.com