Long Conditional Tests In Fritzbot ET
From Bots-United Wiki
This topic is still under some construction and not fully verified.
Contents[hide] |
Intro to boolean math
Hopefully you may have taken a course in boolean math at one point in your education. In any case allow me to introduce the form of the equations that we will use in this topic.
Boolean: Two Values, Three Operators
In basic boolean math there are 3 possible operators to one side of the equation. Two of these operators need 2 operands, namely the operators AND and OR. In boolean math we assign the dot product (A*B = A AND B) to AND and the plus sign to OR (A+B = A OR B). For simplicity we drop the dot operator and push the two operands together for AND (A*B = A AND B = AB). The third boolean operator is what is called an unary operator since it works on just one operand. This is the NOT or inverse operator normally denoted as an overline, or a prime, or a tilde, or in our case an ! prefix (A' = ~A = !A). What about XOR you ask? Well we will show later that it is really just a combination of the first 3 operators.
Like regular math there are 2 important rules in boolean math known as distributivity and associativity. These 2 rules allow for the re-arrangement of boolean equations involving parentheses. And like other equations, terms can be moved from one side to the other by applying the same operation to both sides of the equation.
DeMorgan Duality
An important theorem (DeMorgan's) is quite useful in boolean math. The theorem proves that you can convert the OR operation to an AND operation by inverting both the operands and the result;
C = AB = !(!A + !B) or by rearrangement then !C = !A + !B
and conversely,
D = A + B = !((!A)(!B))=!(!A!B) etc.
Exclusive or...
So what about the XOR operator well it is just;
E = A x B = (A)(!B) + (!A)(B) = A!B + !AB
and with Demorgans;
E = A x B = A!B + !AB = !(!(A!B) * !(!AB)) = !((!A+!(!B)) * (!(!A)+!B)) and !! is redundant = !((!A+B) * (A+!B)) using the rules about parentheses = !(!A(A+!B) + B(A+!B)) = !(!AA+!A!B+BA+B!B) and !AA or B!B are both never true so equal zero = !(0+!A!B+BA+0) E = A x B = A!B + !AB = !(!A!B + BA)
so there are a couple of ways to calculate XOR with just AND, OR and NOT.
Writing Your Long Logic in FritzBot ET
Sum of Products Form
In hardware there is an OR functionality called wired-or where a type of hardware output can be connected to an unlimited number of similar outputs to function as a colossal OR function A1+A2+A3...An. In software there are a variety of ways to implement OR reasoning. For FritzBot ET the OR keyword can only be used on a single line condition between two condition keywords. So how then do you implement a 3 parameter condition of the form;
if( A AND (B OR C)) then command X ?
Well this can be written in boolean as A(B+C) which boolean rules allow to be rewritten as AB + AC. And since the commands in FritzBot are static and not incrementing then they can be executed on multiple lines inside the same action test block. BTW equations of the form AB + BC +... are called a sum of products because they are a long sum (like a wired-OR) of dot product (AND) pairs.
Now suppose we want to activate an action group X when one or both of 2 dynamite wall actions (B, C) are completed but that a barb wire construction A is not built. In a full featured programming language that might be written as;
if_construct_built_false A AND (if_action_true B OR if_action_true C) activateAction_group X
But FritzBot does not support 3-condition conditional statements. However activateAction_group X is a static command (i.e. executing it twice does not change its effect) and we can therefore "wire-or" together multiple identical activate commands.
So the script above in boolean can be written as X=!A(B+C)=!AB + !AC and in FritzBot ET we can program !AB + !AC as;
if_construct_built_false A AND if_action_true B //!AB activateAction_group X if_construct_built_false A AND if_action_true C //!AC activateAction_group X
So rewriting your logic into the form of the sum of AND products will allow you to program many longer conditions in FritzBot ET. (Note: to truly match the boolean equation you also need to handle the deactivateAction_group X as well.)
Sum more...
Where this technique needs more work is in cases of longer AND products like ABC = A AND B AND C. Say you want the attacking bots to move their camps forward after blowing the secondary barbwire defense and BOTH walls. Here there are 2 ways to go about it. One use DeMorgan's to convert the equation to the other form ABC = !(!A + !B + !C). This makes it in the form of the sum of products, but there is still a problem. FritzBot does not support the first NOT (!) in front of the parentheses.
But what if we transferred it to the command i.e.;
D = ABC = !(!A+!B+!C) becomes => !D = !A+!B+!C
So we now have;
if_construct_built_false A OR if_action_false B deactivateAction_group X if_action_false C deactivateAction_group X
But wait a second the idea was to activate X if A AND B AND C, so where is the activate? Well just add it unconditionally immediately before the de-activate tests;
activateAction_group X if_construct_built_false A OR if_action_false B deactivateAction_group X if_action_false C deactivateAction_group X
Now that should work, but real world can throw some curves. There could be a very small chance that in the microseconds that the aiscript activates X before deactivating it that a bot takes a decision. And maybe then the bot gets stuck. And this code will need to appear in all 3 action tests (A, B, C) it may take up too much of the limited keywords in the aiscript. And in cases of constructibles these lines are triggered more than once, possibly increasing the very small chance something unexpected could happen.
So what is the other way?
Fake Actions as Binary Accumulators
Well the if_action_true and if_action_false conditions compare the current goaltracker's goal_num to the action's game state goalNum. This is why they are used with one time actions like plain dynamite actions. Now an action activated by the aiscript gets its goalNum set to the current goal_num. And if the active forever flag is set then the action's goalNum will track the goal tracker's goal_num until the aiscript deactivates the action (when its goalNum is set to -1). This happens regardless of the type of action, so to avoid confusion you can add a fake action say hovering in the air someplace, set its objectives to -1 (invalid/none), set it to active forever 1, group 0 and goal 999 (-1?).
Then in the aiscript default block you add at the start an activateAction F (where F is the number of the fake action).
default { activateAction F //fake action number F is now true for if_action_false //... other code ... }
Now when the match starts the action will test true for if_action_false (think of it as not done/blown). When needed as a storage variable (binary) in the aiscript just deactivate the action ID for the fake action and it will test true for if_action_true (if_action_true does not differentiate for completed vs inactivated). Then to reset it, just activate it again and it will then test true for if_action_false etc.
This then allows you to write code like;
//activateAction F assumed at this point if_action_true C AND if_action_true B //F = BC deactivateAction F //fake action if_construct_built_true A AND if_action_true //F = A(BC) = ABC deactivateAction F if_construct_built_false A //add this if you want it to be used more than one time activateAction F if_action_true F //true if F deactivated. X =!F deactivateAction_group X
As well as writing equivalent code many other ways.
And since the action is invalid the bots totally ignore it. Coded properly, updated in all 3 action tests (A, B, C), the fake action can then even be used in decision making in other action test blocks, possibly saving on code and easing readability.
Warning: Given the confusing reverse logic of a fake action in the aiscript make certain you test it fully in the map walk-through!
Further Fakery
For those waypointers comfortable with scripting fake entities some savings can be made in the aiscript by avoiding the need to repeat the logic statements in all 3 (or more) action blocks. This is worthwhile to reduce the possibility for typo/confusion errors or if you have exceeded the maximum number of keywords permitted in an aiscript. It does require one extra action test block, so you must not have maxed out the aiscript in that sense. We refer to this sort of fake entity as a helper entity or function. An example of a helper entity is given in the topic FritzBot_Buttons_And_Levers.
If you decide to use a helper ent, remember that its fake func_construct has to be triggered (construct'ed) for any event (destruct, construct etc.) of any of its logic components (actions) that changes the output of the boolean equation. This is so that the related aiscript action test will be invoked at that time. (An interesting variation to this rule was implemented for the map eagles_2ways where all available TOIs were already used, but one of the already used TOIs was re-triggered every 12 seconds. Piggybacking on this TOI allowed the aiscript to be updated about other events but with an indeterminate but limited delay).
Caveats
we have shown that with additional statements long logic conditions can be handled. But be careful, FritzBot ET supports in the aiscript a limited number of keywords (256? total, 64 per action test max) and a limited number of action tests (16 (or 32 if patched with the keyword fix beta)).