Got a question that the wiki doesn't answer? Ask on the forum (preferred), or join us on IRC.

BeastNode

CommandHelper/Staged/Logic

From EngineHub.org Wiki
Jump to: navigation, search


Many times you want to use a bit of basic logic to determine what to do. Usually you want to compare two values to see if some relationship between them is true. Based on the result of this comparison, you want to do one thing or another. This is where the logic operators come in handy. There are several functions that all act relatively the same in the BasicLogic class. Essentially, they compare certain values, then return a boolean.

Note: The examples in this tutorial primarily use operators, see the page on Operators for a refresher, if the operators are unclear.

Basic Usage

Let's take the lt() function into consideration (< operator). lt returns true if the value on the left is less than the value on the right. In a typical algebra class, when comparing two numbers, you would write something like this:

x < y

Assuming x is 3 and y is 4, this statement is true. However, if we swap the values, and make x 4 and y 3, the statement is false. Written in code, it would look like this:

1   @x < @y


All the functions in the BasicLogic class return a boolean based on certain conditions. You should be familiar with the lt (less than <), lte (less than or equals to <=), gt (greater than >), gte (greater than or equals >=) functions from a basic algebra class that covers inequalities.

Boolean logic

Some of the functions you may not have seen before if you haven't taken a logic or programming class are the boolean logic operators. For a full discussion of boolean logic, you may wish to read up on wikipedia for more information, however a basic treatment is covered below.

The three main operators: and (&&), or (||), and not (!)

are the building blocks for more complicated logic statements. 

These functions deal with the overall truth value of a construct, not individual bits (see the bit_and and bit_or functions).

and() (&&) returns true if all the parameters resolve to true:

1   (true && true && true && true# returns true
2   (true && true && false && true# returns false


or() (||) returns true if any of the parameters are true

1   (true || false || false || false# returns true
2   (false || false || false || false# returns false


not() (!) returns the opposite truth value of the parameter:

1   (!true# returns false
2   (!false# returns true


xor() returns true if both parameters are different

1   xor(truetrue# returns false
2   xor(falsefalse# returns false
3   xor(truefalse# returns true
4   xor(falsetrue# returns true


For convenience, there are also the nand, nor, xnor functions, which are equivalent respectively to not(and()), not(or()), and not(xor()).

Bitwise Functions

The three bitwise functions bit_and, bit_or, and bit_not work similarly to and, or and not, but they only work with integers, and they work on a binary level to determine the bitwise equivalent of two or more integers, and return that integer. Again, a full discussion of how bitwise operations work is beyond the scope of this article, but some examples are below:

1   bit_and(4, 7) # binary equivalent: (100, 111) returns 100 = 4
2   bit_and(7, 5) # binary equivalent: (111, 101) returns 101 = 5
3   bit_and(1, 4) # binary equivalent: (001, 100) returns 000 = 0
4   
5   bit_or(1, 3) # binary equivalent: (001, 011) returns 011 = 3
6   bit_or(2, 4) # binary equivalent: (010, 100) returns 110 = 6
7   
8   bit_not(4) # binary equivalent: (0..100) returns 1..011 = -5


Bitwise operations are useful for storing many boolean values as a single integer, among other uses. It may be useful to note that integers are stored as 64 bit two's compliment values.

lshift, rshift, and urshift are also available. lshift(value, bitsToShift) is the general format for all three.

1   lshift(4, 2) # returns 16
2   urshift(10, 2) # returns 2
3   rshift(-10, 2) # returns -3
4   urshift(-10, 2) # returns 4611686018427387901
5   rshift(3, 1) # returns 1


if()

Now that we have established that some conditions are true or not, we may want to do different things depending. This is where the if() function comes in. We are creating "code branches" when we use if and other conditional statements, which means that sometimes code will not get run. The syntax of the if statements is as follows:

1   if(condition){
2      // code to run if true
3   } else {
4      // code to run if false
5   }


The condition and code to run if true are required, but the code to run if false ("else") is optional. If it is left off, and the condition is false, void is returned. Only one branch of code is executed here. If condition resolves to true (which may itself contain more complex code), then the second argument is evaluated and returned, otherwise the third argument is evaluated and returned. Note that the other code branch is not even evaluated, that is, it is skipped entirely.

1   if(true){
2      msg('Once');
3   } else {
4      // This "block" won't run at all
5      msg('Twice');
6   }


The "old style" of if was a fully functional syntax, though that syntax is still useful for ternary statements. In most languages, this is the condition ? true : false syntax. if technically returns a value. If the value in each block is a single, returning value, that value will be returned. As a convention, only if a ternary operator is desired should you use the functional syntax. The following code works as expected:

1   @var = if(@condition, 1, 2);


If the @condition is true, then 1 is assigned to @var, otherwise 2 is.

Truth value testing

The following are considered to evaluate to true:

  • The keyword and boolean value true
  • Any non-empty string
  • Any number different from 0

Anything else, including arrays, are considered false.

else if

if/else if/else chains can be formed as well, if multiple conditions need to be checked. Using only if/else would quickly get messy if you have multiple values to check for. Instead, you may use the else if chain.

1   if(@val1){
2      // code1
3   } else if(@val2){
4      // code2
5   } else if(@val3){
6      // code3
7   }

In this usage, the vals will be evaluated from top to bottom, and once the first val is true, the corresponding code will be run. If none of the vals are true, then nothing in the chain will run.

1   if(@val1){
2      // code1
3   } else if(@val2){
4      // code2
5   } else if(@val3){
6      // code3
7   } else {
8      // default case
9   }


In this usage, we can imagine a "default" case. If at least one of the @vals is true, then it will behave the same. However, if none of the vals are true, then the final code block will be run instead. This is the final "else" clause of the statement.

The ifelse() function is the non-brace way to do if/else if/else chains, though its usage is not recommended, as it is less readable. Internally, if/else if/else chains are converted to it still, however.

switch()

A switch statement works by simplifying comparisons. Unlike if/else if/else, switch checks to see if some value is equal to another value, then running that code, instead of just seeing if the value itself is true. Switch statements can actually be written using if chains, so to demonstrate, here is the same code written both ways:

01   @value = 'value';
02   if(@value == 'value1'){
03      // code1
04   } else if(@value == 'value2'){
05      // code2
06   } else if(@value == 'value3'){
07      // code3
08   } else {
09      // default
10   }
11   
12   switch(@value){
13      case 'value1':
14         code1();
15         break();
16      case 'value2':
17         code2();
18         break();
19      case 'value3':
20         code3();
21         break();
22      default:
23         code4();
24         break();
25   }

As you can see, it's a bit less code using the switch statement, but the results can be achieved either way. Sometimes you may want to do a different type of comparison, other than equals, so you have to use if/else if/else chains. As with if/else if/else chains, a "default" condition can be stated, in the event none of the given conditions are satisfied. When possible, however, switch statements should be used, as they can be better optimized by the compiler.

Sometimes you may want to run some code given that the value equals one of any number of values. In this case, you may specify multiple cases, and if the value equals any of the values specified, the corresponding code is run.

01   #if val equals either test2, test3, or test4, code234 is run
02   switch(@val){
03      case 'test1':
04         code1();
05         break();
06      case 'test2':
07      case 'test3':
08      case 'test4':
09         code234();
10         break();
11      default:
12         defaultCode();
13         break();
14   }


Though you can technically use non-string and non-integer values as the comparison types, it is not recommended, and in fact, arrays may not be used at all. When writing code, you should only use integers and strings whenever possible.

Slices may also be used, and if so, if the test value falls within the range of the slice, that code block is run.

1   @value = 1;
2   switch(@value){
3      case 0:
4         case0();
5         break();
6      case 1..5:
7         case1_5(); // This one will run
8         break();
9   }


Unlike many other languages, no break() is strictly necessary. Code blocks are run starting with the case statement, running until the next case. Only if a case should be ignored must you use break(), otherwise code will merge with the following case. For instance, say we want nothing to happen should the value = 2.

1   switch(@value){
2      case 1:
3         code1();
4      case 2:
5         break();
6      default:
7         defaultCode();
8   }


Had we left the break() out, case 2 and the default case would have merged together. In all other cases, break() is optional.

Note: While break() is in fact optional, it is still recommended that you use it when applicable.

Using break() is still recommeneded however, even though it is technically optional. Consider the following example.

1   switch(@v){
2      case 1:
3         msg('1');
4      case 2:
5         msg('2');
6   }


In this example, if @v is 1, we msg out 1, and if @v is 2, we message out 2. Consider however, if we comment out line 3, msg('1'). We might expect that the code simply won't message anything at all if @v is 1. This is incorrect. Due to the way fallthroughs work, it will simply change it to a combined case 1 and 2. We can add a bit more code to ensure that this won't happen:

1   switch(@v){
2      case 1:
3         msg('1');
4         break();
5      case 2:
6         msg('2');
7         break();
8   }


Now, if we comment out msg('1'), as expected, nothing happens when @v is 1.

switch also has a pure functional syntax, but it is not recommended for use in new code.



Navigation menu