Author Topic: Boolean Bug  (Read 4693 times)

ThePaveHawker

  • Guest
Boolean Bug
« on: February 02, 2018, 03:35:47 PM »

In many ways, VoiceAttack is like a sports car. Since this is my story, that sports car is a Ford GT.  It looks fast, it sounds fast, and given the proper pilot it will go fast and handle like a dream.  Now, most people who would purchase a Ford GT probably carefully drive it around, never tasting the feeling of "Let's see what this baby can do!" (Citation needed?...hehehe..Nah, it’s my story!  Personally, I would do both.)  Likewise, folks who purchase VoiceAttack without “seeing what this baby can do,” can never really know its true capability.

-VoiceAttack pushes buttons for me when I say something.  Sweet. (Look at this Ford GT parked in my garage!)

-VoiceAttack pushes buttons for me when I say something only if those buttons have a certain "state".  (This Ford GT sure looks nice, sitting in my garage!)

-VoiceAttack tracks the state of everything in Elite Dangerous and makes changes to ensure the ship is ready for any given situation. (Starting to warm those tires now!)

-VoiceAttack becomes a virtual Co-Pilot Assist for anything I can dream up.  Now I begin developing course material for an online VoiceAttack class.  (Damn this Ford GT is fast!)

Ok, I'm in.

Let's see what this baby can do!

<VoiceAttack has encountered a non-recoverable error and will now close.>

Huh?

It's one thing to find an error or a bug, but another thing altogether when a program simply kicks you out of the driver's seat and goes home, marbles and all.

Commence operation "Bug Hunt".

First thing to do seemed obvious; undue the last change made.  Problem is, my code (VA scripts) are not sequential (the code is not written like typical code in a procedural language).  The code is setup as objects.  To illustrate this a bit more clearly, imagine a large corporation.  This corporation has many departments such as accounting, sales and marketing.  If a person represents the active focus of the main command (or function) in a piece of code, we can understand a bit of how I design my VA code.

Imagine this person needs to know how many items a particular salesperson sold in the month of January.  The sequential method would be for this person to go into the sales department, open up their files and start digging through data to find the answer.  For many reasons, this is a bad idea.  Imagine how the sales department folks would react if an "outsider" came into their personal workspace and started making a mess of things?  There's a better way to do things.  This person can simply walk up to the desk in the sales department (the sales department would be considered an "object") and ask the question. The sales department would go through their own data and retrieve an answer for the person at the front desk. The person at the front desk can then go on their merry little way pursuing other (hopefully more interesting) adventures.

‘Undoing the last change’ wasn't a straightforward solution because I had to code the "person" seeking an answer, and different "departments” (read, "objects") for this person to pose appropriate questions to. Until the different objects were created and talking to one another, the only means to test an object was to "hand jam" data into a list of static variables and then manually feed that list into an object to see what came out.  The real test doesn't come until an object poses a question to another object and receives an answer.

That's when the crashes started.

By themselves, the objects (VA commands) appeared to behave properly. I could call object A and get an answer. I could then manually take that answer and feed it to object B and get an equally satisfying answer. But, when I posed a question to the same object A and had it feed a question directly to the same object B, that Ford GT picked up its marbles and went home (VoiceAttack crashed with an unrecoverable error). To make things worse, this didn't always seem to be the case. I felt like I was tracking down an intermittent electrical issue that manifested itself in various ways on my (oh, I wish...) spectacular Ford GT.

Simply taking out a piece of code wasn't in itself an end-all method, nor was changing the values of variables or even limiting the amount of data being stuffed through the different objects. Sometimes a change would break the code and the crash would cease. Sometimes it was not because the removed code was the problem, but rather the removed code didn't allow other parts of the code to be called (in which the crash-bug was roosting). The question always seemed to be "Did I just take out the buggy code, or did I change a parameter that now doesn't trigger the crash?"

Spending way more time than I'm going to admit (so it isn't known that I'm an amateur...*grin*...), I began removing loops, simplifying variable formats, and making any changes that would both make the code simpler AND keep crashing when it was executed.

I found, quite quickly, that by not having the objects talk to one another (again, I'm referring to the different commands I coded in VA), I was unable to get VA to crash.  I was therefore doomed to debug this thing with all objects in place, and talking to one another, if I were to have any hopes of finding the source of why my beloved (and fictitious) Ford GT likes marbles and no longer wants to play.

Sometimes if I changed the value of dynamic variables, the crash would stop.  But, the code was hopelessly complex to have hope of finding its source.  Sometimes the number of variables I passed through a given object at one time changed, and the system would crash, then not crash, then crash again.  Holy crap.  It felt like I was whittling away at a giant tree trunk looking for a toothpick nestled somewhere inside. Whenever I tried to dismantle the robust code, the bug would vanish before I could get a bead on it.  Then a thought occurred to me.  Not so much a thought really, but a question.

Why is the colon character (":") not a legal character in a Boolean compare statement?  I mean, a colon works everywhere else, right?  I concluded there must be a persistent bug somewhere downstream, and this was a hacker’s stop-gap measure that would hopefully avoid said bug.  (I choose to believe that anything without a working colon is bound to be somewhat anal-retentive (Google “Freud”)).  Maybe this bug is, therefore, nestled deep inside a Boolean engine process somewhere?  So off I go, tearing down code while being wary to keep all Boolean-related material in tact (and thereby continuing to crash VA).  Now I was able to really gut the code yet maintain the crash event.  I had to keep the crash event happening so I could continue to look at the code that was left, which would hopefully reveal the crash-bug.  If the code could be gutted to a single statement (with at least two objects communicating, since the crash promptly vanished if there wasn't at least two objects talking to one another), the bug might be found.  One has to keep in mind that VoiceAttack does not have a variable tracker or step-execution monitor feature.  One can only wind it up and see what happens.

Upon discovering the Boolean compare function had no colon (yes, pun INDEED intended, and appropriately so I might add.), and believing anything that has no colon, or disallows copious uses of colons (yikes, that sounded better in my head), was a possible source of an internal blockage (why let a good thing die when we can just keep kicking it around?).  As a wise man once said, "This town needs an enema!" (Google it if you're a lot younger than me...oh those pesky Batman fans!  *nudge-nudge*, *wink-wink*).  But alas, I digest.

Then I got lucky.  (Had nothing to do with a colon.  Er, wait...it did actually).  That pesky colon and the question of why it was disallowed by any Boolean compare statement seemed a bit too discriminatory for my liking.  Remember when I said I got lucky?  I, for no apparent reason (that's my story and I'm sticking to it!), decided to see what happens if I take all Boolean variables and set them to "Not Set", and THEN call another object.

*Poof*  No more crash.


Huh?

Out came paper and pencil, and many matrices were scribbled on college-ruled sheets.  Finally I had something to test.  For the same reason I stated earlier about not wanting to admit how much time it took to figure this out, I plead the same justification for not wanting to state just how many ?&@%!#@ matrices it took to narrow things down.

My goal was to make a "rule" on how to crash VA, so the bug could be reported and hopefully fixed (Oh how I want my Boolean compare statements to have colons!).  It was a simple rule, but there seemed to be an exception; Two objects in my code were able to talk to one another without crashing.  If the proposed crash rule were correct the code seemed at first glance as though it shouldn't have worked, yet it did.

Did I miss it?  Did I whittle away the ‘crash-toothpick’ long ago and not realize it?  But the rule was 100% repeatable, without any deviation in result whatsoever.

And thus the days turned into years… (Re: “Studies on the Structure of Time,” edited by Buccheri, Gesu and Saniga.  ISBN 0-306-46439-X). 

If I wasn't in love with "write something to the event log" statements before, I was smitten after she started whispering those oh-so-sweet nothings into my ear (they have that on Twitch btw, if you ever want to see what it sounds like...er... I mean, there is this "thing" one can purchase.  It looks like a human head (sans head) with ears.  Inside these ears are microphones.  Inside these microphones are the sound vibrations of a seductively (sometimes) attractive woman whispering... Once again, I digest... Oh look, a squirrel!...)

Those "write something to the event log" statements (please don't ask how many, it’s embarrassing) revealed my Eureka moment: The objects which first appeared to violate the absoluteness of the crash-rule, were in fact NOT in violation of said rule!  A quick manual assignment of a Command-level Boolean variable (to ensure the crash-rule conditions were met) and *BAM*...my philosophical Ford GT packed up its marbles and went home. 

The rule stands, so far.  Now on to more permutations of the crash-rule to find out just how far and wide this thing really is.  At some point I decided to call it "done."  Instead of claiming to be done, it would be more accurate to say it was really a ‘point of abandonment’.  Hopefully I'm ‘done’ and the toothpick is now firmly in my grasp, able to remove any grit that may be present in the tire treads of my Ford GT.

Now, (with baited breath), “Let’s see what this baby can do!”

(JUMP POINT)

For those who skipped ahead (and ESPECIALLY for those who endured the above self-serving narrative), here are the results of many (hand-scribbled) matrix permutations:

Current VoiceAttack Version: 1.6.9

“The ‘Boolean-Bug’” (kinda fun to say, no?)

-OR-

“How to Crash VoiceAttack with Two Commands and Three Statements”

This bug is like a mouse-trap; it has to first be set before it can be triggered.  Just because it has been set doesn’t mean it will be triggered.  Likewise, even if one presses the “trigger”, the trap will not go off (i.e. VoiceAttack Crash) if it hasn’t first be set.



First, set the trap:


Step One:

Create a command. Call it anything you’d like. I’ll call mine “Command B”.

In Command B, reference at least one Command-Level Boolean variable.  (Note: Command-Level variables are preceded with a tilde character “~”) 

There are FIVE ways to accomplish this, but only one of the five statements need be used to setup for a VA crash:

1-Set Boolean [~MyVariable2] to (True)
2-Set Boolean [~MyVariable2] to (False)
3-Set Boolean [~MyVariable2] to (Not Set) #(yup!)
4-Say, ‘Some Words {BOOL:~MyVariable2}’
5-Write, ‘ Some Words {BOOL:~MyVariable2}’ to log

Again, ANY of the above five statements, when placed in a command as the sole statement in said command, will work.


I’ll write my statement as:

 “Write, ‘ Some Words {BOOL:~MyVariable2}’ to log”

(Note: The color choice is irrelevant. I have matrices that say so.)

Step Two:

Create a second command. Call it anything you’d like. I’ll call mine “Command A”.


In Command A, set a Boolean variable to either True or False.

There are TWO ways to accomplish this, but only one is required to setup for a VA crash:

1-Set Boolean [~MyVariable1] to (True)
2-Set Boolean [~MyVariable1] to (False)

Again, ANY of the above two statements, when placed in a command as the sole statement in said command, will work.

Now the trap is set, but it won’t be triggered unless we do one more thing.  Have “Command A” talk to “Command B”.

Here’s what I mean.

In “Command A”, which has a single statement assigning a Command-level Boolean variable to either True or False, add a second statement which calls “Command B”.

There is ONE way to accomplish this, and this is the part that actually springs the trap after it has been set (i.e. Causes VoiceAttack to crash):

Add the following statement to “Command A”:

Execute command, ‘Command B’

Now before we actually spring the trap (and cause VoiceAttack to pick up its marbles and go home with an unrecoverable error), let’s recap.

Two commands: “Command A” and “Command B”

Command B
      Write, ‘ Some Words {BOOL:~MyVariable2}’ to log

Command A
      Set Boolean [~MyVariable1] to (True)
      Execute command, ‘Command B’

That’s it.  The trap is set and ready to be sprung.

Before saying “Command A”, please be grossly aware this will cause VoiceAttack to encounter a non-recoverable error and promptly close with no fanfare.  Only say “Command A” if you understand that I, in no way, condone such reckless behavior and am merely attempting to inform others of what will happen if they DO say it.  I have no idea if a VoiceAttack crash will cause damage to software or system, so don’t say the command.  In fact, delete “Command A” and “Command B” immediately.

With that said, I’ve crashed VoiceAttack using similar methods hundreds of times and have noticed no permanent damage (other than to some game controllers, but that’s another story…hehehehe).

The Workaround:

Until the Boolean-Bug has been exterminated, here’s how to work around it:

1)   VoiceAttack will continue to disallow the colon character (“:”) in Boolean compare statements.  (Blah!)
2)   BEFORE ‘executing another command’ from within a VA command, set all Command-Level Boolean variables to “Not Set”.

In the above code examples, were one to add a third statement to “Command A” prior to “Execute command, ‘Command B’” (and after the “Set Boolean” statement of course) in the following format, the code will NOT cause VoiceAttack to crash:

Command A
      Set Boolean [~MyVariable1] to (True)
      Set Boolean [~MyVariable1] to (Not Set)
Execute command, ‘Command B’

What this means is, one can use all the command-level Boolean variables desired, so long as they are ALL set to “Not Set” prior to calling another command which references any Command-level Boolean variable (see the 5 examples above).

Another option is to use Global-level Boolean variables instead of Command-level Boolean variables.  To some (myself included), this seems like a sin to assign Global-level variables to something that will only be used once, in a specific command.  (Not to mention the obvious house-cleaning efforts this would require to track those variables lest they come flying in like some sort of sick, cosmic space debris and strike at the heart of some other command’s variables.  It can work, but one doesn’t have to like it.  That’s like saying “It’s raining, but at least I’m wet.”)

On a semi-related note: Do you, like me, desperately want to use a colon character in a Boolean compare statement?  Here’s a workaround:

First, set a (I prefer Command-level) Boolean variable to the value of some dynamic variable name (which has the colon character in it).

Second, use this new Command-level Boolean variable in a Boolean compare statement.

Here’s an example:

Set Boolean [~vehicle] to [{TXT:~car}_{INT:~vehicle_number}]
Begin Boolean Compare : [~vehicle] Equals True

The above code assumes the text value of “~car” and the integer value of “~vehicle_number” have previously been set to some values, but hopefully the point is illustrated.  In case the point isn’t clearly illustrated, check this out.  In the above coding example, if the TEXT value of ~car was previously set to “Ford_GT” and the INTEGER value of ~vehicle_number was previously set to “42”, then the Boolean compare statement is in essence:

Begin Boolean Compare : [Ford_GT_42] Equals True

Because:
Begin Boolean Compare : [{TXT:~car}_{INT:~vehicle_number}]
isn’t allowed (it has colons in it).

What this means is if {BOOL:Ford_GT_42} returns “TRUE”, this could imply that the Ford GT, car number 42, has gone home with its marbles.  (Or whatever significance one might envision if the Boolean TRUE of Ford_GT_42 were the case).

Just be sure to “Set Boolean [~vehicle] to (Not Set)” before calling another command from the above command example and all will be well. (Again, I have matrices that say so.)

Note: The variables ~car and ~vehicle_number (above), although are indeed Command-level variables, they are not BOOLEAN Command-level variables.  They therefore will NOT contribute to the setting, or the springing, of the Boolean-Bug (and therefore do not need to be set as “Not Set” prior to calling another command).

I hope this helps someone. And to that one person who found any humor in this paper, thank you for your support.


P.S. Any references to marbles are purely fictitious.  No Ford GT (to my knowledge) has any affinity for, or interest in them.

P.P.S. As a wise man once said, "Game on!"  (Again, google it if you're a lot younger than I am.  Oh those pesky street-hockey fans!)

P.P.P.S. (I think that’s a thing, right?) This would have been SO much less time consuming had I the privilege of perusing the VoiceAttack source code.  (Note to the genius behind VoiceAttack  I’ll sign a non-disclosure agreement, were that ever to become a possibility.)


In parting, a final thought:

“Never speak in absolutes; it makes you sound arrogant.”

One more final thought:

“Nothing rhymes with ‘oranges’.”



Cheers!

-ThePaveHawker

“Write, ‘ {BOOL:~ThePaveHawker}’ to log”



Addendum for those who will peruse the VoiceAttack source code in search of a fix for this bug:
   It appears to be an issue with the handling of Boolean Command-level variables, specifically when a child command is called which references, in any way, another Command-level Boolean variable.
   
   A suggestion could be to start with a variable tracker and monitor the value of a Command-level Boolean variable (which has been set to True or False) in a Parent command.  Start the monitoring when the Parent command sets the value.  As the Parent command calls, and returns from, a Child Command (again, the Child must reference at least a single Command-level Boolean variable), monitor what happens to the handling of the original Parent Command-level Boolean variable.

   My guess is when the Parent command calls a Child command (again, which references a Command-level Boolean variable in any way), the handling of the Parent command Command-level Boolean variable is not tracking all the way through the return from the Child command and is “noticed/felt” upon exiting from the Parent command (execution complete moment from the Parent command).

   I suspect that upon return to the Parent command, the Parent command Command-level Boolean variables will not be restored properly, leaving a NULL() reference hanging when the source code is specifically expecting the variable’s value to be there, and has no way to address the issue.
   
   The mere existence of a Child command Command-level Boolean variable (its value is irrelevant) for some reason seems to be the catalyst for the apparent mishandling of the Parent command Command-level Boolean value not being properly restored to the value it had prior to the Parent command calling the Child command.

   If no Parent Command-level Boolean is set (to either True or False) prior to calling a Child command which references, in any way, at least a single Command-level Boolean, the crash event does not occur.

   If the Child command does not reference at least a single Command-level Boolean variable, the crash event does not occur.
   
   The Child command Boolean variable can be set to T, F or Not Set (including using the “set to Not Set” option), and will contribute to the crash event.

   The Child command does not need to specifically assign a value to a Command-level Boolean variable.  The crash event also occurs when the Child command references a Command-level Boolean variable in a “Say something with TTS” or a “Write something to log” statement (so long as that statement has a Command-level Boolean variable named within.  The Child Boolean variable, if in a “Say” or “Write” statement, need not be referenced anywhere else prior to the “Say” or “Write” statement.  The Child command needs but one statement to contribute to the crash-event.

   To restate; In reference to the role the Child command Command-level Boolean variables play in the crash-event.  It does not matter if the value of said variable is set, forced to not set, not set, or ever mentioned prior to a “say” or “write” event.  The mere existence of a tilde’d Boolean variable anywhere in the command contributes to the crash-event.

   Is the handling of the Child command’s Command-level Boolean variable interfering with the handling of the Parent command Command-level Boolean variable?  Is the Parent Boolean properly restored/handled upon return (specifically, upon execution-complete exiting of the Parent command?)

   If nothing shows up in the process, what happens at the moment the program control exits (completes) the Parent command?  Perhaps the Parent command Command-level Boolean variable is not behaving properly and this is ‘noticed’ upon a normal (execution complete) from the Parent command.

   I sincerely hope this makes your job easier.

   Best of luck!

-ThePaveHawker

“Write, ‘ {BOOL:~ThePaveHawker}’ to log”


TheThingIs

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 278
    • HCS Voicepacks
Re: Boolean Bug
« Reply #1 on: February 02, 2018, 03:59:13 PM »
oh wow that was a read :p

The ~ command variable bug when "not set" has been fixed in the latest beta...(I think that's what you were referring to) :)
The Singularity profile - One profile to rule them all and at HCS we bound them ;)

You see, TheThingIs, eventually you'll be allright.

ThePaveHawker

  • Guest
Re: Boolean Bug
« Reply #2 on: February 02, 2018, 04:23:58 PM »
Hi, and thanks for the reply.

I am not referring to the ~command variable bug when "not set".

If a command sets a single command level (~ tilde'd variable name) BOOLEAN variable to either TRUE or FALSE, and then calls a second command that references (in any way) any BOOLEAN tilde'd variable (command level) (note, even a say something, or write to log will do it), VA crashes. 

In my paper, I referenced two hypothetical commands, "Command A" and "Command B".   Command A has two statements; setting the boolean variable, and calling command B.  Command B has one command; it attempts to write to log the value of a previously unset boolean variable.



Hope this helps,
-ThePaveHawker

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4759
  • RTFM
Re: Boolean Bug
« Reply #3 on: February 02, 2018, 04:38:27 PM »
TheThingIs is quite correct, it's because the boolean value is not set in the second command:
Quote from: VoiceAttackHelp.pdf page 161
WITHOUT command-shared variables:
Command A sets text variable ~myText to 'hi'. Command A then executes Command B as a subcommand. Command B reads ~myText and it is, 'Not Set' (null). That is because ~myText is command-scoped (single, ‘~’). It is only available to Command A.

If you use "~~" as the prefix instead(which means it's shared with other commands in the same execution chain), it should work fine in your context.


As a side note, I'd suggest you wrap your VoiceAttack actions with code tags(using the "#" button above the textbox, both for readability and to prevent
Code: [Select]
[Blue]From getting misinterpreted and coloring most of your text(which also removes the tag itself).

Gary

  • Administrator
  • Hero Member
  • *****
  • Posts: 2827
Re: Boolean Bug
« Reply #4 on: February 02, 2018, 05:00:08 PM »
I tried out your abridged version of the issue in your second post and was not able to reproduce the problem (attached is what I tried out).  Have you tried the later betas?  If so, in order to resolve what you are experiencing in a speedy manner, what I would need from you (if you can) is effectively an exported NEW profile with *only* the commands necessary to reproduce the problem.  Also, as much detail as possible (hopefully step-by-step) of what you are expecting versus what you are experiencing from within that profile.  What this will do is cut out any clutter as well as eliminate any other actions that may be affecting the problematic actions.  I would encourage you to try out the commands in the emptied profile just as a sanity check.

Gary

  • Administrator
  • Hero Member
  • *****
  • Posts: 2827
Re: Boolean Bug
« Reply #5 on: February 02, 2018, 05:04:52 PM »
Thanks for helping, guys... I posted when Pfeil was posting ;)

As we work closer to VoiceAttack OS (Skynet edition), I'm sure we'll find more of the good stuff lurking beneath ready to make us stumble.  However, when VA becomes our overlord, it will know who's been faithful to it... I'll be the first tossed into the chipper (just FYI).

ThePaveHawker

  • Guest
Re: Boolean Bug
« Reply #6 on: February 02, 2018, 05:30:54 PM »
Hello, and again thanks for the replies.

Here's some sample code.

Two commands:
STOP
and
CRASH SUBCOMMAND

In either command, there are a few disabled statements. If any one (or more, or all, but not NONE) of the statements is ENABLED (this goes for both of the two commands), the crash happens.

Notice:
1) They are all Command-Level (scoped) Boolean variables

2) Doesn't matter if they are set or not set (except in the parent command, in which case it can not be "not set" or the crash does not happen).

3) Doesn't matter if the subcommand tries to reference a variable from the parent command (which it can't, and shouldn't, and doesn't, since we're only dealing with Command-Level variables here.)  (Note: I do understand the ~~ scoping, but my goal here is not to make things work, but to shine light on the nature of the bug.)

4) Notice my code doesn't actually DO anything (other than call one command from another.  I'm not trying to figure out how to make some piece of code work.   I simply found a bug, isolated the circumstances and checked for alternate conditions that will recreate the crash. )


Here's a .vap with the two commands.

So long as at least one command (operator's choice!) is enabled in each,  and "stop" calls "crash subcommand", it crashes.

ThePaveHawker

  • Guest
Re: Boolean Bug
« Reply #7 on: February 02, 2018, 05:32:38 PM »
Gary:

I was unable to look at your uploaded .vap. (404...)


Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4759
  • RTFM
Re: Boolean Bug
« Reply #8 on: February 02, 2018, 05:45:44 PM »
I see what you mean, however, I've verified your profile crashes v1.6.9, but it does not crash v1.6.9.7, so the bug has already been patched in the beta.

ThePaveHawker

  • Guest
Re: Boolean Bug
« Reply #9 on: February 02, 2018, 05:54:33 PM »
I'm not sure how to run the BETA because I purchased VA through my STEAM account. I downloaded the current BETA and installed it to my STEAM directory hoping it would show as registered. No dice.

Since I registered through STEAM, I have no registration email, and therefore no code to manually enter.

Looks like I have to either wait for STEAM to show an updated version available, or purchase another license directly from VA?

Now to try and figure out how to restore my registered steam version....*grin*.... Hope my profiles are still there!

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4759
  • RTFM
Re: Boolean Bug
« Reply #10 on: February 02, 2018, 05:58:30 PM »
Did you get the Steam-specific beta from this link?

You profiles shouldn't be affected.

mIRCon

  • Newbie
  • *
  • Posts: 20
Re: Boolean Bug
« Reply #11 on: February 03, 2018, 03:51:37 AM »
Sounds like the bug discussed and fixed by Gary here:
http://voiceattack.com/SMF/index.php?topic=1705.0

It's the same mechanism after all and not related to boolean per se but the whole concept of scoped vars used in a wrong way.