I was able to get the random movement programming done in my Animatron basic program. Peter has been doing his own thing beside me while I type this description for you all. Here is my test script that has been running…
Startscript,Main,0
Label,DoItAgain,0
RandomMove,Head Fakes,12
Playmove,Look ahead,500
Playmove,Look right,500
Playmove,Look left,500
Playmove,Look right slowly,1500
Playmove,Look ahead slowly,1500
Playmove,Look left slowly,1500
Playmove,Face Right,500
Playmove,Face Forward,500
Playmove,Face Left,500
Playmove,Face Right Slow,2000
Playmove,Face Forward Slow,2000
Playmove,Face Left Slow,2000
JumpTo,DoItAgain,0
Endscript,Main,0
It was not too difficult to fit the logic into the structure of the existing program. Defining the command 'RandomMove" was similar to the SyncPoint, EndSync and Scriptpause commands; that is I only needed to define the action and a single value.
RandomMove only needs to know how many different commands it can select from when making a choice. It then uses a random number generator to pick one of them to execute next, and sets the script’s pointer to that command. The trick was to resume execution AFTER the block of commands (repeating the random selection process was easy, as the program already had a ‘Label’ and ‘JumpTo’ command). In order to do this, RandomMove also calculates the next step after the defined block of possible moves.
I defined a state variable that was then used when selecting the next command to execute. There are three defined states: No need to adjust the next step, within the random selection, so execute the current step, but set the state so that NEXT time, the command after the defined block of moves is executed.
Right now, the moves are purely random. The sample script I have included just moves his head and eyes back and forth – but it is enjoyable to see Peter move on his own - i.e. unscripted.
A current code listing follows…
[code]#include once “windows.bi”
#include once “win/mmsystem.bi”
’ ===========================================================================
’ ===========================================================================
’ ===========================================================================
’ ===========================================================================
Declare Function FindIt (array() As String, What As String) As Integer
Declare Function DisplayStep (i As Integer, j As Integer) As String
ReDim MoveDescription(0) As String ’ Move lookup table: description of move, e.g. “Move Eyes Left”
ReDim MoveCommand(0) As String ’ actual servo move command, e.g. “#0P2000 #1P2000 T100”
Dim MoveIndex As Integer =0 ’ array index of Move arrays
ReDim LabelDescription(0) As String ’ Label lookup table; name of label or script
ReDim LabelScriptIndex(0) As Integer ’ index to step after label in script arrays
Dim LabelIndex As Integer =0 ’ array index for Label arrays
ReDim PlayerStep(0) As Integer ’ Player properties: index to step in script to execute next
ReDim As Double PlayerEndWait(0) ’ array of time before next command is issued, by player
PlayerEndWait(0)=0
Dim As Double SoundEndWait=0 ’ timer value before next check of ADC for voice/sound input, ~12ms from last test.
Dim Player As Integer =0
ReDim ScriptAction(0) As Integer ’ Script properties: action to take
ReDim ScriptDescription(0) As Integer ’ Index to servo move table
ReDim ScriptOption(0) As Integer ’ value for action, i.e. pause length after command is issued
Dim ScriptStep As Integer =0, MainScript As Integer =0
Dim ErrorFound As Integer =0
’ Properties for executing a random move from a selection of ‘n’ commands
’ also, can be used for other non-linear execution plans for a script.
Const NoAdjustStepAction = 1 ’ constant values to indicate the state of processing a random move
Const InRandomSelection = 2
Const AdjustStepAction = 3
ReDim As Integer AdjustStep(0) ’ state variable for adjusting the step
’ while processing a random move
Dim As Integer Selections ’ variable to store the possible number of random moves from which to select
ReDim As Integer NextStep(0) ’ variable which will point to the step AFTER a random move block
Randomize
ReDim SyncPoints(0) As ULongInt ’ flags for sync points; bit encoded by associated players
Dim SyncPointStatus As ULongInt =0
Dim SyncPointIndex As Integer =0
Dim IndexPlayers As Integer = 0, MaxPlayers As Integer =0, NotDone As Integer =-1 ’ script player definitions
'Speech variables
Dim As String dat, ssc32cmd, PW, qs, cr
Dim As Integer level
Dim soundFile As String
'Control variables
Dim As String ControlKey
Dim SingleStep As Integer = 0
Const PauseKey=""
Const Esc = Chr(27)
'SSC-32 constants
Const scbase="#3P"
qs = “VA” + Chr(13)
cr = Chr(13)
’ Prepare Show
Dim CommPort As String ’ serial port connected to servo controller (SSC-32)
Dim Animatron As String ’ name of file containing mapping between move descriptions and SSC-32 commands
Dim ShowName As String ’ name of file containing a script for a given “show”
Const dfltCommPort = “com4:”
Const dfltMoves = “peter.csv”
Const dfltScript = “Peter-Live.csv”
’ connection to servo controller (SSC-32)
Print "Comm port “;dfltCommPort;”]: ";
Input CommPort
If CommPort = “” Then CommPort = dfltCommPort
’ short description to servo controller (SSC-32) commands mappings
Print “Character/Move file name “;dfltMoves;”]:”;
Input Animatron
If Animatron = “” Then Animatron = dfltMoves
Open Animatron For Input As #1
’ script file
Print "Show file name “;dfltScript;”]: ";
Input ShowName
If ShowName = “” Then ShowName = dfltScript
’ ===========================================================================
’ ===========================================================================
’ Define Moves (lookup table)
’ ===========================================================================
’ ===========================================================================
’ read in short description and corresponding SSC-32 command
Dim Move As String , CtrlrCmd As String
Do While Not Eof(1)And ErrorFound =0
Input #1, Move, CtrlrCmd
If UCase(Move) = “END” Then Exit Do
MoveIndex += 1
ReDim Preserve MoveDescription(MoveIndex)As String, MoveCommand(MoveIndex)As String
MoveDescription(MoveIndex) = Move
MoveCommand(MoveIndex) = CtrlrCmd+cr
Loop
Close #1
’ ===========================================================================
’ ===========================================================================
’ Define Script(s)
’ ===========================================================================
’ ===========================================================================
Open ShowName For Input As #1
Dim Action As String, Description As String, OptionValue As Integer, LocalIndex As Integer
Const PlayMove As Integer =0 ’ send commands to SSC-32
Const PlayScript As Integer =1 ’ execute script in parallel; script must be defined in same file
Const StartScript As Integer=2 ’ define new script; main script MUST be last in file
Const EndScript As Integer =3 ’ end of script routine definition
Const JumpTo As Integer =4 ’ “goto” command; label MUST exist (is not checked for)
Const Label As Integer =5 ’ definition of label used in “JumpTo” command
Const SyncPoint As Integer =6 ’ definition of scripts which will synchronize with other script(s)
Const EndSync As Integer =7 ’ definition of step in script at which to wait for synchronization
Const ScriptPause As Integer=8 ’ “Pause” or “Delay” command; pauses execution for n milliseconds
Const Say As Integer=9 ’ play external sound file; will cause servo defined in “scbase” (e.g. mouth servo) to synchronize to sound
Const RandomMove As Integer=10 ’ Randomly perform on of the following ‘n’ actions; n is the option value
Do While Not Eof(1) And ErrorFound =0
Input #1, Action, Description, OptionValue
Print Action, Description, OptionValue
Select Case UCase(Action)
Case "PLAYMOVE"
ScriptStep +=1
ReDim Preserve ScriptAction(ScriptStep), ScriptDescription(ScriptStep), ScriptOption(ScriptStep)
ScriptAction(ScriptStep) = PlayMove
LocalIndex = FindIt(MoveDescription(), Description)
If LocalIndex < 0 Then
Print "Cannot find move ";Description
ErrorFound=1
Exit Do
Else
ScriptDescription(ScriptStep) = LocalIndex
EndIf
ScriptOption(ScriptStep) = OptionValue
Case "STARTSCRIPT", "LABEL"
LabelIndex +=1
ReDim Preserve LabelDescription(LabelIndex) As String, LabelScriptIndex(LabelIndex)As Integer
LabelDescription(LabelIndex) = Description
LabelScriptIndex(LabelIndex)=ScriptStep+1
If UCase(Action) = "STARTSCRIPT" Then MainScript=ScriptStep+1
Case "PLAYSCRIPT", "JUMPTO"
ScriptStep+=1
ReDim Preserve ScriptAction(ScriptStep) As Integer, ScriptDescription(ScriptStep) As Integer, ScriptOption(ScriptStep) As Integer
If UCase(Action) = "PLAYSCRIPT" Then
ScriptAction(ScriptStep)=PlayScript
Else
ScriptAction(ScriptStep)=JumpTo
EndIf
LocalIndex = FindIt(LabelDescription(), Description)
If LocalIndex < 0 Then
Print "Cannot find script or label ";Description
ErrorFound=2
Exit Do
Else
ScriptDescription(ScriptStep) = LabelScriptIndex(LocalIndex)
EndIf
Case "SAY"
LabelIndex +=1
ReDim Preserve LabelDescription(LabelIndex) As String, LabelScriptIndex(LabelIndex)As Integer
LabelDescription(LabelIndex) = Description
LabelScriptIndex(LabelIndex)=ScriptStep+1
ScriptStep +=1
ReDim Preserve ScriptAction(ScriptStep) As Integer, ScriptDescription(ScriptStep) As Integer, ScriptOption(ScriptStep) As Integer
ScriptAction(ScriptStep) = Say
ScriptDescription(ScriptStep) = LabelIndex
Case "ENDSCRIPT"
ScriptStep +=1
ReDim Preserve ScriptAction(ScriptStep) As Integer, ScriptDescription(ScriptStep) As Integer, ScriptOption(ScriptStep) As Integer
ScriptAction(ScriptStep) = EndScript
Case "SYNCPOINT", "ENDSYNC", "SCRIPTPAUSE", "RANDOMMOVE"
ScriptStep+=1
ReDim Preserve ScriptAction(ScriptStep) As Integer, ScriptDescription(ScriptStep) As Integer, ScriptOption(ScriptStep) As Integer
If UCase(Action)="SYNCPOINT" Then
If OptionValue>SyncPointIndex Then SyncPointIndex=OptionValue
ScriptAction(ScriptStep)=SyncPoint
ElseIf UCase(Action)="ENDSYNC" Then
ScriptAction(ScriptStep)=EndSync
ElseIf UCase(Action)="RANDOMMOVE" Then
ScriptAction(ScriptStep)=RandomMove
Else
ScriptAction(ScriptStep)=ScriptPause
EndIf
ScriptDescription(ScriptStep)=OptionValue
ScriptOption(ScriptStep)=OptionValue
Case Else
Print "Unrecognized script command"
ErrorFound=4
Sleep
Exit Do
End Select
Loop
Close #1
If ErrorFound Then End ErrorFound
’ ===========================================================================
’ ===========================================================================
’ Execute processed Script(s)
’ ===========================================================================
’ ===========================================================================
If timeBeginPeriod( 1 ) = TIMERR_NOCANDO Then
Print “Specified period is out of range”
ErrorFound=3
End If
If ErrorFound Then End ErrorFound
Open Com CommPort+" 115200,N,8,1,BIN,CD,CS,DS,RS" For Binary As #1
If Err Then
Print "Could not open ";CommPort
EndIf
’ initialize sound subsystem
Put #1,qs
Sleep 12
dat=Input$(Loc(1),#1)
’ define first step
MaxPlayers=0
ControlKey=""
PlayerStep(MaxPlayers) = MainScript
AdjustStep(MaxPlayers)=NoAdjustStepAction
ReDim SyncPoints(SyncPointIndex) As ULongInt
Print “Starting to play script at “;Mainscript
Print UBound(ScriptAction);” steps in script”
Print
Print “Press any key to start”;
Sleep
ControlKey=InKey
Print “”
Print “Press <> key to pause, key to resume,”
Print " to enter/leave single step mode,"
Print " to step through script,"
Print " key to stop execution and leave the program."
Print “3…”
Sleep 1000
Print “2…”
Sleep 1000
Print “1…”
Sleep 1000
Print “!”
ControlKey=“x”
Do While ControlKey <> ""
ControlKey=InKey
Sleep 1
Loop
Do While NotDone ’ “NotDone” is true until the main script executes an “EndScript” command
For Player=0 To MaxPlayers ’ “Player = 0” is the main script
’ Speech processing …
If (SoundEndWait-Timer <0) Then
dat = Input$(Loc(1), #1)
If dat = "" Then
' Print "No data"
Else
level = Asc(dat)
' Print "Level ";level
Select Case level
Case 0 To 12
PW="600"
Case 13 To 25
PW="700"
Case 25 To 255
PW="800"
End Select
ssc32cmd=scbase+PW+cr
'Print "Command = ";ssc32cmd
Put #1,, ssc32cmd
End If
Put #1,,qs
SoundEndWait=Timer+.012
EndIf
If ((PlayerEndWait(Player)-Timer)< 0) And (PlayerStep(Player)<>0) Then
PlayerEndWait(Player)=0
Select Case AdjustStep(Player)
Case NoAdjustStepAction
ScriptStep=PlayerStep(Player)
Case InRandomSelection
ScriptStep=PlayerStep(Player)
AdjustStep(Player)=AdjustStepAction
Case AdjustStepAction
PlayerStep(Player)=NextStep(Player)
ScriptStep=NextStep(Player)
AdjustStep(Player)=NoAdjustStepAction
End Select
' Pause/Single Step script on request
ControlKey=InKey
If ControlKey = PauseKey Then
Print "Script paused per request"
Sleep
Print "Continuing..."
ElseIf ControlKey = cr Then
SingleStep = Not SingleStep
ElseIf ControlKey = Esc Then
NotDone=0
Exit For
EndIf
If SingleStep Then
Print "Single ";
Sleep
EndIf
Select Case ScriptAction(ScriptStep)
Case PlayMove
MoveIndex=ScriptDescription(ScriptStep)
Print "Step ";DisplayStep(Player,ScriptStep);": ";MoveDescription(MoveIndex),MoveCommand(MoveIndex)
Put #1,,MoveCommand(MoveIndex)
PlayerEndWait(Player)=Timer+ScriptOption(ScriptStep)/1000
PlayerStep(Player)=ScriptStep+1
Case ScriptPause
Print "Step ";DisplayStep(Player,ScriptStep);": ";
Print Using "Pause #.###";(ScriptOption(ScriptStep)/1000)
PlayerEndWait(Player)=Timer+ScriptOption(ScriptStep)/1000
PlayerStep(Player)=ScriptStep+1
Case PlayScript
IndexPlayers=1
Do While IndexPlayers <= MaxPlayers
If PlayerStep(IndexPlayers)=0 Then Exit Do
IndexPlayers+=1
Loop
If IndexPlayers>MaxPlayers Then
MaxPlayers+=1
ReDim Preserve PlayerStep(MaxPlayers) As Integer, PlayerEndWait(MaxPlayers) As Double
ReDim Preserve As Integer AdjustStep(MaxPlayers), NextStep(MaxPlayers)
EndIf
PlayerStep(IndexPlayers)=ScriptDescription(ScriptStep)
PlayerEndWait(IndexPlayers)=0
AdjustStep(IndexPlayers)=NoAdjustStepAction
PlayerStep(Player)=ScriptStep+1
Print "Step ";DisplayStep(Player,ScriptStep);": Starting script on Player ";IndexPlayers
Case EndScript
PlayerStep(Player)=0
NotDone = (Player<>0) ' main script has ended
Print "Step ";DisplayStep(Player,ScriptStep);": Scripts have completed on ";Player
Case JumpTo
Print "Step ";DisplayStep(Player,ScriptStep);": Jump to";ScriptDescription(ScriptStep)
PlayerStep(Player)=ScriptDescription(ScriptStep)
Case SyncPoint
SyncPointIndex=ScriptDescription(ScriptStep)
Print "Step ";DisplayStep(Player,ScriptStep);": Defining synchronization point ";SyncPointIndex
If Player > 63 Then
Print "Too many simultaneous Players...";Player
Exit Do
EndIf
SyncPoints(SyncPointIndex)=SyncPoints(SyncPointIndex) Or (2^Player)
PlayerStep(Player)=ScriptStep+1
Case EndSync
SyncPointIndex=ScriptDescription(ScriptStep)
If Player > 63 Then
Print "Too many simultaneous Players...";Player
Exit Do
EndIf
SyncPointStatus= SyncPoints(SyncPointIndex) And (Not 2^Player)
If SyncPointStatus <> SyncPoints(SyncPointIndex) Then
Print "Step ";DisplayStep(Player,ScriptStep);": Sync'ing to";SyncPointIndex;" ";SyncPoints(SyncPointIndex);"]"
EndIf
SyncPoints(SyncPointIndex)= SyncPointStatus
If SyncPoints(SyncPointIndex) = 0 Then
PlayerStep(Player)=ScriptStep+1
EndIf
Case RandomMove
Selections=ScriptDescription(ScriptStep)
NextStep(Player)=ScriptStep+Selections+1
MoveIndex=Int(Rnd()*Selections)+1
PlayerStep(Player)=ScriptStep+MoveIndex
AdjustStep(Player)=InRandomSelection
Print "Step ";DisplayStep(Player,ScriptStep);": RandomMove of";Selections
Case Say
soundFile=LabelDescription(ScriptDescription(ScriptStep))
Print "Step ";DisplayStep(Player,ScriptStep);": Saying ";soundFile
PlaySound(soundFile, NULL, SND_ASYNC Or SND_FILENAME)
PlayerStep(Player)=ScriptStep+1
End Select
EndIf
Next
Sleep 1
Loop
timeEndPeriod( 1 )
Print “Press any key to finish”;
Print
Sleep
End
Function FindIt (AnArray() As String, What As String) As Integer
Dim i As Integer
For i=0 To UBound(AnArray)
If AnArray(I) = What Then Exit For
Next
If i > ubound(AnArray) Then i=-16384
Return i
End Function
Function DisplayStep(i As Integer, j As Integer) As String
Dim Display_Number As String =""
Display_Number = Right(" " & Str(i),2) & “-”+Left(Str(j) & " ",3)
Return Display_Number
End Function
[/code]
Edit 1-5-11: updated code after some bug fixes; the random selection can now successfully call other scripts and has been tested with up to six scripts/players running in parallel. Had to limit the number of players/simultaneous scripts to 64; more than the amount of servos available, so is not a limit practically! Also changed the display to show the player AND step being executed…
Animatron2.zip (4.21 KB)