Gigi Labs

Please follow Gigi Labs for the latest articles.
Showing posts with label if. Show all posts
Showing posts with label if. Show all posts

Sunday, March 16, 2014

C: Rock, Scissors, Paper (using random numbers)

Hello everyone, and welcome back to the Ranch! :)

In my last C article, "C: Calculating the Average of Several Numbers", we learned how to work with numbers in C. This included using integer variables, input/output of integers, and arithmetic operations.

Today, we're going to take this further and learn how to generate random numbers in C. We'll use this to write a little Rock, Scissors, Paper game.

Pseudo-random numbers in C


So let's start off with a pretty empty program:

#include <stdio.h>

int main(int argc, char ** argv)
{
    return 0;
}

We can generate a random integer using the rand() function, which is defined in stdlib.h. This gives us a random number between 0 and a constant integer called RAND_MAX. We can output the value of RAND_MAX as we would any other integer, although its value is not guaranteed to be the same across different computers:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char ** argv)
{
    int a = rand();
    int b = rand();
    int c = rand();

    printf("RAND_MAX is: %d\n", RAND_MAX);
    printf("a is: %d\n", a);
    printf("b is: %d\n", b);
    printf("c is: %d\n", c);

    return 0;
}

Let us now run this program a few times and see what happens:


On my computer, RAND_MAX is defined as 2147483647, so each time I call rand(), it will give me a random number between 0 and 2147483647.

Another thing you'll see from the output is that each time I run the program, I get the same values for a, b and c. That isn't quite random, is it?

The reason why this is happening is that these random numbers aren't really random.  They are actually generated from a mathematical sequence, that takes a very, very long time to begin repeating itself, but which isn't truly random. These are called pseudo-random numbers. Imagine this sequence:

4, 29, 55, 12, 4, 7, 3, 97, 84, ...

This sequence seems pretty random; there isn't any pattern in it. So each time we get a random number with rand(), we'll get a different number: first 4, then 29, then 55, etc.

Our problem is that each time we run the program, we're starting from the beginning of the sequence, and this gives us the same values. So what we want to do is start from a different position each time.

We do this by seeding the pseudo-random number generator using the srand() function. Let's just add this line at the beginning of main():

    srand(100);

Let's run the program again:


You'll notice the numbers have changed, since we're using a different seed. However, each time we run the program, the same numbers are repeated. In fact we are still making the same mistake as before: we are using the same seed, and starting the pseudo-random number sequence from the same point each time.

In order to use a different seed each time we run the program, we can use the current time as the seed. We can use the time() function, defined in time.h, to give us the current time. So let's include that header file at the top...

#include <time.h>

...and replace our call to srand() with this:

srand(time(NULL));

When we run the program, things work as we expect, and we get different pseudorandom numbers each time we run:


Scaling random numbers


Great. Now, the numbers we are generating are a little bit big. For example, for Rock, Scissors, Paper, we don't need a number between 0 and 2147483647; all we need is a number between 1 and 3 (both inclusive), to represent either rock, scissors, or paper. We can limit the range of a pseudorandom number by dividing it by the maximum we want, and extracting the remainder. This is done using the modulus (%) operator, which gives us the remainder of a division:

    int a = rand() % 10;
    int b = rand() % 10;
    int c = rand() % 10;

Let's say rand() gives us 55. If we divide 55 by 10, then the remainder can be at most 9. So what we're doing above is limiting each random number to the range 0 to 9 (both inclusive). We'll need to add 1 to the number by which we're dividing, in order to make it inclusive (i.e. between 0 and 10 in this case).

What if we need a minimum value that is not zero? In short, we declare a general-purpose function that returns random integers between two integers (both inclusive) like this:

// returns a random number between min and max, both inclusive)
int randomInt(int min, int max)
{
    return min + (rand() % (max - min + 1));
}

We can then use this function from within our main() like this, to give us random numbers between 1 and 10:

    int a = randomInt(1, 10);
    int b = randomInt(1, 10);
    int c = randomInt(1, 10);

And voilĂ :


Rock, Scissors, Paper in C


OK, so now that we know how to generate pseudorandom numbers, we can write out game of Rock, Scissors, Paper. In this game, two people independently choose one of rock, scissors or paper, and state their choice at the same time. The outcome is then as follows:

  • Rock beats scissors (breaks it).
  • Scissors beats paper (cuts it).
  • Paper beats rock (wraps it).

To start off, let's throw away all our code in main(), but keep our randomInt() function:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// returns a random number between min and max, both inclusive)
int randomInt(int min, int max)
{
    return min + (rand() % (max - min + 1));
}

int main(int argc, char ** argv)
{
    // code goes here

    return 0;
}

First, we add code to accept user input:

    int input = -1;

    while (input != 0)
    {
        printf("Choose...\n");
        printf("   1 for rock\n");
        printf("   2 for scissors\n");
        printf("   3 for paper\n");
        printf("or 0 to exit\n");

        scanf("%d", &input);
    }

The user will choose a number between 1 and 3 to represent rock, scissors, or paper respectively. A while loop repeats the game until the user enters 0 to exit the game. We have already used for loops in C: Calculating the Average of Several Numbers, but a while loop will simply keep executing the code within curly brackets as long as the condition within round brackets is true, in this case as long as input is not zero:


Once the human player has chosen, the computer player makes his choice at random:

       int computerChoice = randomInt(1, 3);

We can then display the computer's choice:

       switch(computerChoice)
       {
           case 1: printf("Computer chooses rock\n"); break;
           case 2: printf("Computer chooses scissors\n"); break;
           case 3: printf("Computer chooses paper\n"); break;
       }

A switch statement allows us to specify separate code to be executed depending on the value of a variable. In this case, if computerChoice is 1, we write that the computer has chosen rock, and similarly for the other choices. Note that each case in a switch statement should always end in a break statement; otherwise unexpected things may happen. :)

Finally, we can add the game logic to check who won:

       if (input == computerChoice)
       {
           printf("Game ends in a draw.\n");
       }
       else if (input == 1 && computerChoice == 2)
       {
          printf("Rock beats scissors! You win!\n");
       }
       else if (input == 2 && computerChoice == 3)
       {
          printf("Scissors beats paper! You win!\n");
       }
       else if (input == 3 && computerChoice == 1)
       {
          printf("Paper beats rock! You win!\n");
       }
       else if (computerChoice == 1 && input == 2)
       {
          printf("Rock beats scissors! Computer wins!\n");
       }
       else if (computerChoice == 2 && input == 3)
       {
          printf("Scissors beats paper! Computer wins!\n");
       }
       else if (computerChoice == 3 && input == 1)
       {
          printf("Paper beats rock! Computer wins!\n");
       }

Using a cascade of if statements, we compare the user input with the computer's random choice and see who won. The == operator allows us to compare two integers and determine whether they are equal. The && operator, on the other hand, is a boolean AND operator. If both conditions around the && operator are true, then the printf() statement within the curly brackets of that if statement is executed.

We can now actually play our game:


Summary


Great! :) We have a fully working Rock, Scissors, Paper game here. And in creating it, we learned about pseudo-random numbers, while loops, if statements, switch statements, and also a few operators including == (is equal to), != (is not equal to) and && (boolean AND). Below is the full code.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// returns a random number between min and max, both inclusive)
int randomInt(int min, int max)
{
    return min + (rand() % (max - min + 1));
}

int main(int argc, char ** argv)
{
    int input = -1;

    while (input != 0)
    {
        printf("Choose...\n");
        printf("   1 for rock\n");
        printf("   2 for scissors\n");
        printf("   3 for paper\n");
        printf("or 0 to exit\n");

        scanf("%d", &input);

       int computerChoice = randomInt(1, 3);

       switch(computerChoice)
       {
           case 1: printf("Computer chooses rock\n"); break;
           case 2: printf("Computer chooses scissors\n"); break;
           case 3: printf("Computer chooses paper\n"); break;
       }

       if (input == computerChoice)
       {
           printf("Game ends in a draw.\n");
       }
       else if (input == 1 && computerChoice == 2)
       {
          printf("Rock beats scissors! You win!\n");
       }
       else if (input == 2 && computerChoice == 3)
       {
          printf("Scissors beats paper! You win!\n");
       }
       else if (input == 3 && computerChoice == 1)
       {
          printf("Paper beats rock! You win!\n");
       }
       else if (computerChoice == 1 && input == 2)
       {
          printf("Rock beats scissors! Computer wins!\n");
       }
       else if (computerChoice == 2 && input == 3)
       {
          printf("Scissors beats paper! Computer wins!\n");
       }
       else if (computerChoice == 3 && input == 1)
       {
          printf("Paper beats rock! Computer wins!\n");
       }
    }

    return 0;
}

Saturday, November 30, 2013

VB .NET Basics: Conditionals, Logical Operators and Short-Circuiting

Hello again! :)

Two months ago, I wrote the article "VB .NET Basics: Input and Output", which has recently been gaining a lot of attention. In today's article, we're going to see how we can write conditional statements in VB .NET to let the program take different actions depending on user input. More importantly, we're going to see how to use logical ANDs and ORs in 'If' conditions - something that is likely to catch you off guard if you're coming from some other language.

First, create a new VB .NET Console Application. If you're using SharpDevelop, the project will be created with the following initial code:

Module Program
    Sub Main()
        Console.WriteLine("Hello World!")
       
        ' TODO: Implement Functionality Here
       
        Console.Write("Press any key to continue . . . ")
        Console.ReadKey(True)
    End Sub
End Module

Let's start off with a simple conditional. Replace the code in Main() wit the following:

        Console.WriteLine("Are you male or female?")
        Dim genderStr As String = Console.ReadLine();

This just asks the user about his/her gender and collects their input in a string. Now, we'll use that input as follows:

        If genderStr.Substring(0, 1).ToLower() = "m" Then
            Console.WriteLine("Hello sir!")
        End If

We're taking the first character from the user's input (thanks to the Substring() method), converting it to lowercase. That way, whether the user types "Male", "male", or "m126", it's still going to interpret that as being male.

There are a couple of things to note here. First of all, in VB .NET, the = operator (which is the same one used in assigning values to variables) is used for equality comparison. This is quite different from C-like languages which use = for assignment and == for comparison.

Secondly, VB .NET is sensitive to spacing. If you try to put the Then on the next line, like this:

        If genderStr.Substring(0, 1).ToLower() = "m"
            Then
            Console.WriteLine("Hello sir!")
        End If

...you'll get a syntax error. You can, however, stuff the conditional in one line and omit the End If, like this:

        If genderStr.Substring(0, 1).ToLower() = "m" Then Console.WriteLine("Hello sir!")

Okay, since the feminists must be grumbling at this point, let's replace our conditional with this:

        If genderStr.Substring(0, 1).ToLower() = "m" Then
            Console.WriteLine("Hello sir!")
        ElseIf genderStr.Substring(0, 1).ToLower() = "f" Then
            Console.WriteLine("Hello miss!")
        Else
            Console.WriteLine("Hello alien!")
        End If

You might be familiar with Else from other languages, but this ElseIf might possibly be new. ElseIf makes its condition (the one checking for female) run only if the initial If returned false - it's the equivalent of writing the following:

        If genderStr.Substring(0, 1).ToLower() = "m" Then
            Console.WriteLine("Hello sir!")
        Else
            If genderStr.Substring(0, 1).ToLower() = "f" Then
                Console.WriteLine("Hello miss!")
            Else
                Console.WriteLine("Hello alien!")
            End If
        End If

...but when you have lots of conditions, it saves you from having to indent indefinitely. ElseIf exists in other languages, but can vary quite a lot between them (e.g. elif in Python, elsif in Perl, or even just else if in the C-like languages).

Of course, when we are checking the same variable over different values, we know we should actually be using a Select Case statement, which is the equivalent of a switch statement in C# and other C-like languages. We can change the above conditional into a Select Case as follows:

        Select Case genderStr.Substring(0, 1).ToLower()
            Case "m":
                Console.WriteLine("Hello sir!")
            Case "f":
                Console.WriteLine("Hello miss!")
            Case Else
                Console.WriteLine("Hello alien!")
        End Select

Select Case is syntactically quite different from switch statements. In particular, there are no break statements, and Case Else is the equivalent of the default statement in C-like languages: its statements are executed if none of the earlier cases are matched.

We can now add the following to wait for user input before exiting the application:

        Console.ReadLine()

...and then test it (I used Ctrl+F5 to run, instead of debug, the application - this lets you run several instances at the same time):


Now, let's see how to write more interesting conditional statements. To do this, let us first collect the user's age:

        Dim message As String = Nothing
        Dim ageStr As String = Console.ReadLine()
        Dim age As Integer = 0
        Dim converted As Boolean = Integer.TryParse(ageStr, age)

So, first we're declaring a message variable, which we'll set in a minute. It's initially set to Nothing, which is equivalent to C# null. Then we're accepting user input, and converting it to a number. We use Integer.TryParse() so that if the conversion fails, it simply returns false and age is not set - this saves us from having to do exception handling as in "C# Basics: Arithmetic and Exceptions".

You might guess that we can logically combine conditional statements using the And and Or statements. Let's try that out:

        If converted Then
            If genderStr.Substring(0, 1).ToLower() = "m" And age < 30 Then
                message = "You are a young man!"
            ElseIf genderStr.Substring(0, 1).ToLower() = "m" And age >= 30 Then
                message = "You are an old man!"
            ElseIf genderStr.Substring(0, 1).ToLower() = "f" And age < 30 Then
                message = "You are a young lady!"
            ElseIf genderStr.Substring(0, 1).ToLower() = "f" And age >= 30 Then
                message = "You are an old lady!"
            ElseIf genderStr.Substring(0, 1).ToLower() = "a" Then
                message = "Whatever your age, you are an alien."
            End If
        End If

Heh, if you're 30 or over, you're probably shaking your fists. But anyway, since we're dealing with multiple variables, we can't use a Select Case statement here. Instead, we are checking different combinations of gender and age. Note that I haven't included an Else statement, and this means that if the age conversion fails, then message remains Nothing.

Now, let's show the message. We can handle the case where message is Nothing with another conditional:

        If message IsNot Nothing And message.Length > 0 Then
            Console.WriteLine(message)
        End If

Okay. Let's begin testing the application:


That worked pretty well, didn't it? Now let's try an invalid age:


Uh oh. What just happened here? Despite the check against Nothing, accessing message.Length still caused a NullReferenceException to be thrown. Why is that? Let's look at our condition again:

         If message IsNot Nothing And message.Length > 0 Then

If message is Nothing, then the first condition resolves to false. At this point we normally expect the second expression to be ignored (which is called short circuiting), but that is not what is happening. The And operator evaluates both expressions, and only then ANDs them together.

In other words, we're using the wrong operator. We need to use AndAlso instead of And, and OrElse instead of Or. AndAlso and OrElse will short circuit (i.e. they won't evaluate both expressions if they don't have to), while And and Or are bitwise operators and mean something different (which we won't go into at this stage). If you're coming from any C-style language such as C#, then you can understand this as follows: AndAlso and OrElse are the equivalents of && and || respectively; while And and Or are the equivalents of & and | respectively.

We can now change our code to use AndAlso instead of And:

        If converted Then
            If genderStr.Substring(0, 1).ToLower() = "m" AndAlso age < 30 Then
                message = "You are a young man!"
            ElseIf genderStr.Substring(0, 1).ToLower() = "m" AndAlso age >= 30 Then
                message = "You are an old man!"
            ElseIf genderStr.Substring(0, 1).ToLower() = "f" AndAlso age < 30 Then
                message = "You are a young lady!"
            ElseIf genderStr.Substring(0, 1).ToLower() = "f" AndAlso age >= 30 Then
                message = "You are an old lady!"
            ElseIf genderStr.Substring(0, 1).ToLower() = "a" Then
                message = "Whatever your age, you are an alien."
            End If
        End If
       
        If message IsNot Nothing AndAlso message.Length > 0 Then
            Console.WriteLine(message)
        End If

And now, this code doesn't raise any exceptions (there is no message written because that's the way the logic works):


Great. In this article, we learned how to use conditional statements in Visual Basic .NET. We started by looking at simple If and Select Case statements, and then discovered how to set up more complex conditions by using Boolean operators to combine simple conditions. The main takeaway from this is that you should always use AndAlso and OrElse in your conditions, not And and Or. This will allow your conditions to short circuit, i.e. if your condition is (A AND B) and A is false, then the condition is false regardless of the value of B, so B is not evaluated. Short circuiting is very important when the second condition is dependent on the first, as we have seen in the above code.

Wednesday, September 25, 2013

Best Practices: To Always Use Braces for Conditionals and Loops... or not

Hi everyone! :)

While following a number of Pluralsight courses by Jesse Liberty (which I highly recommend), I was reminded of the long-running debate about whether one should always use braces when control flow statements (conditionals and loops) are involved. Jesse Liberty is one of many who suggest always using braces. So let's say we have the following conditional:

            if (x == 0)
                Console.WriteLine("x is zero");
            else if (x < 0)
                Console.WriteLine("x is negative");
            else if (x > 0)
                Console.WriteLine("x is positive");

The proponents of the always-use-braces camp would have us write them like this:

            if (x == 0)
            {
                Console.WriteLine("x is zero");
            }
            else if (x < 0)
            {
                Console.WriteLine("x is negative");
            }
            else if (x > 0)
            {
                Console.WriteLine("x is positive");
            }

In this article, I'm going to discuss the advantages and disadvantages of this approach, as well as my personal opinion based on my experience. I want to say from the beginning that this article is subjective and not completely factual or objective - so take it for what it is. There is no general consensus on this matter precisely because it is a matter of personal taste.

Brace styles


Braces (or curly brackets) are a feature in just about any programming language that uses the C-style syntax, including C, C++, Java, C#, JavaScript, PHP, and many others. They define a scoped block that can be executed as a single statement as part of control flow (conditionals and loops). There are many different styles in which they can be used.

The Java folks seem to like this kind of style:

            if (x == 0) {
                Console.WriteLine("x is zero");
            }

The .NET camp, on the other hand, seems to prefer aligning braces vertically:

            if (x == 0)
            {
                Console.WriteLine("x is zero");
            }

If you have just one statement, you can technically leave out the braces, so you can write your code like this:

            if (x == 0)
                Console.WriteLine("x is zero");

...or like this.

             if (x == 0) Console.WriteLine("x is zero");

Personally, I think the first and last options aren't the best in terms of readability (especially when code becomes complex) so I'll focus on the second and third. I normally use the second option (with braces) when I have multiple statemenents, and the third option (no braces) when I have just one. Many people recommend always using braces, and dismiss the third option as bad practice. Let's take a look at the reasons why.

If you need to add statements, you'll find the braces ready


So let's take the same example as before, where you have this statement:

             if (x == 0)
                Console.WriteLine("x is zero");

If you need to add additional statements to be executed as part of the conditional, the braces will have to be added anyway. Some people recommend always using braces so that you'll find them ready when you need to add additional statements.

In that case, I suppose, we shouldn't use empty element syntax in XML:

<RowDefinition Height="30" />

...and instead always write our tags in full:

<RowDefinition Height="30"></RowDefinition>

...simply because we might need to add something inside the element at some point. I think this is a very weak argument, because it disregards a language feature that may be very convenient, and at the same time bloats code with something that adds no meaning to the code. Take a look at the first two code snippets in this article - the one that uses braces is twice as long (in terms of lines) as the other one. And for what? Because people are too lazy to type in the braces when they are eventually needed? Wat.

Adding statements can be error-prone


Another reason why omitting braces is considered bad practice is that it may be easy to introduce logical errors when maintaining such code (see this question on Programmers StackExchange and this other one on Stack Overflow). Let's say you have this:

            if (x == 0)
                Console.WriteLine("x is zero");

Then, you add an additional statement intended to be within the conditional, and you do it like this:

             if (x == 0)
                Console.WriteLine("x is zero");
                Console.WriteLine(" which means it's neither positive nor negative");

Oops! The second Console.WriteLine() isn't actually part of the conditional, so it always gets executed, no matter what. This is a valid argument. But let's dissect it a little further.

First, let's start again from our simple single-line conditional:

             if (x == 0)
                Console.WriteLine("x is zero");

Now, if you want to add code at this point, you have two courses of action. If you want to add statements as part of the conditional, you know that there's just one statement and no braces, so adding them should be a pretty automatic response:

            if (x == 0)
            {
                Console.WriteLine("x is zero");
                Console.WriteLine(" which means it's neither positive nor negative");
            }

On the other hand, if you want to add a statement that is not a part of the conditional, you add it at the same level as the if statement:

            if (x == 0)
                Console.WriteLine("x is zero");
            Console.WriteLine(" which means it's neither positive nor negative");

Even in absence of braces, the indentation shows clearly that one statement belongs to the conditional and the other does not. So actually, when seeing this kind of code:

             if (x == 0)
                Console.WriteLine("x is zero");
                Console.WriteLine(" which means it's neither positive nor negative");

...I can't help but think that the readability problem (which results in incorrect control flow) is one of indentation, not of whether to use braces or not.

I can certainly imagine beginning programmers making this kind of mistake, but I find it hard to believe that more seasoned programmers find it hard to read basic conditionals. As one of the answers to this question states:

"I even find it implausible that this should be a common mistake: blocks are a fundamental part of programming. Block level resolution and scoping is an automatic, ingrained mental process for programmers. The brain just does it (otherwise, reasoning about programming would be much harder). There is no additional mental effort required to remember putting the braces: the programmer also remembers to indent the newly added statement correctly, after all; so the programmer has already mentally processed that a block is involved." -- Konrad Rudolph

Also, one of the sections in this article states that:

"Programmers with enough discipline to always notice the braces (and put them in when needed) don't need this idiom [always using braces].
"Auto-indent editors make it obvious whether your new statement is part of the else clause, making it unlikely you'll have the bug this idiom tries to prevent."

Summary


Personally, I think that using braces when they aren't necessary is a waste of space, resulting in a lot of unnecessary lines of code. I use them when needed, and don't use them for single statements. I find nothing wrong with omitting braces when they aren't needed. This has worked for me for many years, and you may or may not agree wtih me. Different people find themselves comfortable using different approaches, and there is no general consensus on what is best.

So find out what works best for you, and don't let anyone tell you how you should write your code based on subjective arguments. While you should definitely learn from more experienced programmers and best practices based on rational and logical arguments, be practical and don't get too religious about your code.