Conditions in depth

Conditions in depth

> LeekScript Tutorial

Great! It's time to add conditions to your programs!

For now, your code always does the same thing. Conditions will fix that.

Conditions are the first "control structure" we're going to talk about. They make your code more flexible, so that it can adapt to the situation.

So far, you've seen that your code runs linearly, one instruction after another. Now we're going to see how to run a piece of code, or not, depending on a condition.

Comparison operators

But before we get to conditions, we need to go back to operators. So far we've mostly talked about the operators used to do calculations; to tackle conditions, we'll need to look at the ones used to make comparisons.

These operators are a little different from the ones we've already seen. For example, you saw that in LeekScript, the expression "4 + 6" gives 10. The result is an integer, a number. It's a mathematical expression that represents a calculation.

Comparison operators let you write mathematical expressions that represent a relation. If we write the expression "2 ", meaning "strictly less than" and "strictly greater than". (If you're looking for them: on a QWERTY keyboard, they're on the comma and period keys, with SHIFT held down.)

In short, these operators let you test the order relation between two elements. For example:

var result = a b;

If the value of "a" is strictly greater than that of "b", the result will be true, which is assigned to the "result" variable. If it's the other way around, it will be false.

We really are talking about "strict" relations here. If a and b have the same value, the result will be false, since neither is smaller or greater than the other.

Mixed relational operators

And now, the non-strict version of the relational operators.

For the greater than or equal to operator, you just combine the equality and relational operators. That gives you the ">=" operator! The greater-than symbol followed by the equals symbol.

For example:

var result = a >= b;

If the value of "a" is greater than or equal to that of "b", the result will be true, which is assigned to the "result" variable. If it's the other way around, meaning strictly less, the result will be false.

And to express "less than or equal to", the operator is "

A B A && B

falsefalse->false

falsetrue->false

truefalse->false

truetrue->true

With this code, you can test all four pairs of values:

debug("false AND false: " + (false && false) ); // Displays: false debug("false AND true: " + (false && true) ); // Displays: false debug("true AND false: " + (true && false) ); // Displays: false debug("true AND true: " + (true && true) ); // Displays: true

OR

This is the "logical OR". There are two ways to write it in LeekScript: either by writing "or", or by using the "||" symbol (two "|" characters; on a QWERTY keyboard, "|" is SHIFT + backslash).

Let's test again with the previous code:

var A = true; var B = false; debug(A || B); // Displays: true debug(A or B); // Displays: true

This time, the result is true! The result of a logical OR is true if at least one of the two values is true.

So we have:

A B A || B

falsefalse->false

falsetrue->true

truefalse->true

truetrue->true

Once again, if you want to test it:

debug("false OR false: " + (false || false) ); // Displays: false debug("false OR true: " + (false || true) ); // Displays: true debug("true OR false: " + (true || false) ); // Displays: true debug("true OR true: " + (true || true) ); // Displays: true

NOT

This is the "logical NOT". There are two ways to write it in LeekScript: either by writing "not", or by using the "!" symbol (just a plain old exclamation mark).

Unlike the previous ones, which compared two values, this one applies to a single value. It lets you invert a boolean.

Let's test it:

var A = true; var B = false; debug(!A); // Displays: false debug(!B); // Displays: true

The result of a logical NOT is the opposite of the value. If the value is true, it becomes false, and vice versa.

Put simply, we have:

Anot A

false->true

true->false

Of course, if you want to test it:

debug("!false: " + (!false)); debug("!true: " + (!true));

Operators are really useful, trust me. Whether they're logical or arithmetic, you'll soon be using them absolutely everywhere.

Conditions

Phew, we're done with operators. Maybe you still can't quite see what they'll be useful for. That should become clearer in what follows.

The "IF"

if is the keyword that lets us write a condition. It means exactly what it says: "if". It lets you tell your leek: If this, then do that.

The syntax is as follows:

if (condition) instruction;

Here, the condition is an "expression". If this expression evaluates to true, in other words if it's true, then the instruction is executed. However, if the expression is false (if it evaluates to false), then the instruction is not executed.

But this syntax only lets you execute a single instruction.

If you want more instructions, you'll need to use this syntax:

if (condition) { instruction; instruction; instruction; }

With this syntax, you can put in as many instructions as you like. Even just one, or none at all. This syntax is also a bit more readable and less cramped than the previous one.

If "condition" is an expression whose result is true, then the block of code between the braces "{" and "}" is executed. And if the condition isn't met, and is therefore false, nothing happens.

Let's try this:

if (false) { debug("The condition is true!"); }

Nothing happens! Since the condition is false, the debug instruction isn't executed. Try replacing false with true in the condition: this time, you'll see the message appear.

The "ELSE"

else is the keyword that lets you execute code when the condition isn't met. It means "otherwise". It lets you tell your leek: If this, then do that; otherwise, do this other thing.

The syntax for using it is as follows:

if (condition) { // Instructions } else { // Other instructions }

As before, if the condition is true, the code in the braces following the IF is executed. But if the condition is not true, it's the code in the braces after the ELSE that is executed.

Let's improve the code from earlier:

if (false) { debug("The condition is true!"); } else { debug("The condition is false!"); }

And there you go! With true, you'll get the message saying that the condition is true. And with false, you'll get the message saying that the condition is false.

The "ELSE IF"

You can "merge" the if and else keywords to handle different cases. You get something like this:

if (condition) { // "condition" is true } else if (condition2) { // "condition" is false, but "condition2" is true } else { // "condition" and "condition2" are both false }

Get the picture? No? Don't panic! A few examples will make it clear. It's time to put the different operators you've discovered into practice!

Ternaries

Ternaries are another way of writing a condition. Let's take a basic condition with an if/else:

if (condition) { action1(); } else { action2(); }

The same code with a ternary looks like this:

(condition) ? action1() : action2();

You can also use one for an assignment, for example:

var myVariable = (condition) ? value1 : value2;

Ternaries have the drawback of being much less readable than the if structure. But they cost slightly fewer operations.

If you're just starting out, you don't need to optimize every single operation in your code yet. So stick with the classic if structure at first.

A few examples

Let's compare!

As you've seen, you can compare values. And what's the result of a comparison? A boolean, that's right! So you can easily do this kind of thing:

if (3 < 4) { debug("3 is less than 4!"); }

OK, it's not the discovery of the century, but you may be starting to imagine everything you could do with it. Let's make a slightly more interesting, and useful, comparison... Let's get the distance between you and your opponent!

var distance = getCellDistance(getCell(), getCell(getNearestEnemy()));

if (distance < 10) { debug("The enemy is closing in, Commander!"); }

And there you go! You can now run code depending on the distance between you and your opponent. Here, the message will only be displayed if the enemy is less than 10 cells away from you.

If you're having a bit of trouble with this line:

var distance = getCellDistance(getCell(), getCell(getNearestEnemy()));

Here's a version that's easier to understand:

var myLeekCell = getCell(); // First, get your leek's cell var nearestEnemy = getNearestEnemy(); // Then get the enemy's id var nearestEnemyCell = getCell(nearestEnemy); // Next, that enemy's cell var distance = getCellDistance(myLeekCell, nearestEnemyCell); // And finally, get the distance between the two cells

You can also nest your conditions, like this for example:

var enemy = getNearestEnemy(); var distance = getCellDistance(getCell(), getCell(enemy));

if (distance < 11) { if (distance < 8) { useWeapon(enemy); } else { useChip(CHIP_SPARK, enemy); } }

Our friends the booleans

Take the lineOfSight function: it returns true if there is a line of sight between the two cells passed as parameters.

Let's test whether your leek can see its opponent:

var enemy = getNearestEnemy(); var los = lineOfSight(getCell(), getCell(enemy));

if (los) { debug("I see it, chief! I see it!"); } else { debug("Where did it go?! The coward's hiding!"); }

And just like that, you can find out whether your leek can see its opponent, and therefore whether it can shoot at it.

Don't forget that this function returns a boolean, so there's absolutely no point in doing something like this:

var myCell = getCell(); var enemyCell = getCell(getNearestEnemy());

if (lineOfSight(myCell, enemyCell) == true) { debug("I see it, chief! I see it!"); }

The IF already checks whether the condition is true; it's as if you were asking your leek: Is the condition "there is a line of sight is true" true? It's redundant, and you're risking a slap on the wrist from me.

On the other hand, if it's the opposite condition you're interested in, you might be tempted to do something like this:

var myCell = getCell(); var enemyCell = getCell(getNearestEnemy());

if (lineOfSight(myCell, enemyCell) == false) { debug("Where did it go?! The scaredy-cat's hiding!"); }

That's no better! lineOfSight returns true or false. With that return value, your condition becomes (true == false) or (false == false). Which is pretty pointless.

So what's the right solution? Use the logical NOT!

var myCell = getCell(); var enemyCell = getCell(getNearestEnemy());

if (lineOfSight(myCell, enemyCell)) { debug("I see it, chief! I see it!"); }

if (!lineOfSight(myCell, enemyCell)) { debug("Where did it go?! The scaredy-cat's hiding!"); }

That's how you use booleans properly. No more redundancy: it's beautiful, it's clean. Good enough to eat.

You don't need the ==, !=, === and !== operators with booleans. You'll mainly use them to compare numbers and constants.

For example:

if (getWeapon() != WEAPON_PISTOL) { setWeapon(WEAPON_PISTOL); }

With this code, if you don't have a weapon in hand, or if it isn't the Pistol, your leek will equip it.

Putting it into practice

One of the first thoughts to cross your mind might be this one:

"Hmm, I could use the Pistol when I can, and otherwise use Spark." Well, why not?

You'll need to start with the conditions that must be met to use the Pistol.

Its range goes from 1 to 7. So you first need to check the distance with getCellDistance:

if (getWeapon() != WEAPON_PISTOL) { setWeapon(WEAPON_PISTOL); }

var enemy = getNearestEnemy(); var distance = getCellDistance(getCell(), getCell(enemy));

if (distance <= 7) { useWeapon(enemy); useWeapon(enemy); useWeapon(enemy); }

But that's not enough. The vast majority of weapons and chips require what's called a "line of sight". In other words, to use the Pistol, your leek must be able to see its target. To check this, you use the lineOfSight function.

if (getWeapon() != WEAPON_PISTOL) { setWeapon(WEAPON_PISTOL); }

var enemy = getNearestEnemy(); var distance = getCellDistance(getCell(), getCell(enemy)); var los = lineOfSight(getCell(), getCell(enemy));

if (distance <= 7 && los) { useWeapon(enemy); useWeapon(enemy); useWeapon(enemy); }

We have our condition! You can read it as "If the distance is less than or equal to 7 and there is a line of sight".

All that's left is to add the use of Spark.

if (getWeapon() != WEAPON_PISTOL) { setWeapon(WEAPON_PISTOL); }

var enemy = getNearestEnemy(); var distance = getCellDistance(getCell(), getCell(enemy)); var los = lineOfSight(getCell(), getCell(enemy));

if (distance <= 7 && los) { useWeapon(enemy); useWeapon(enemy); useWeapon(enemy); } else { useChip(CHIP_SPARK, enemy); useChip(CHIP_SPARK, enemy); useChip(CHIP_SPARK, enemy); }

This code will use the Pistol if it has line of sight and the target is within range; otherwise, it will use Spark. There's no need to check Spark's range. If your leek is too far away, it simply won't be able to use it.

Conclusion

From now on, you can make your code smart. You're able to make it adapt to the situation.

The next chapter is about loops, another very important concept in programming.