Gigi Labs

Please follow Gigi Labs for the latest articles.
Showing posts with label switch. Show all posts
Showing posts with label switch. 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, May 15, 2013

C# Basics: Snake Game in ASCII Art (Part 1)

Hi! :)

In recent articles, we created a game called Chase the Star (see "C#: ASCII Art Game (Part 1)" and "C#: ASCII Art Game (Part 2)"), and also learned about what The ASCII Table (C#) has to offer to us.

In today's article we're going to create a little game like the classic Snake. The first Snake game I ever played was probably Nibbles, on an ancient DOS machine. Rumour has it that in the Stone Age, when cavemen used to dance around a bonfire and chant "Ugh!", this machine was used to make a series of electronic beeps in a disco fashion. Each caveman would then walk like an Egyptian.

OK, we're going to start with something quite similar to Chase the Star. The difference is that our snake will get longer when it eats the star. Instead of storing just the location of our player, as with Chase the Star, we are going to need a whole list of locations:


Start a new console application. Since we're going to have a lot of locations, it makes sense to define a structure or struct storing X and Y, rather than keeping separate variables for them. This must be declared at the same level as class Program (ideally just before it):

    struct Location
    {
        int X;
        int Y;
    };

In Main(), we can now store the location of the head of the snake as follows:

            Location head;
            head.X = 40;
            head.Y = 12;

If you try compiling now, you'll get the following error:

'CsSnake.Location.X' is inaccessible due to its protection level (CS0122)

In order to make this work, you need to set the Location's members to public:

    struct Location
    {
        public int X;
        public int Y;
    };

Even better, instead of setting X and Y separately, we can define a constructor:

    struct Location
    {
        public int X;
        public int Y;
      
        public Location(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }
    };

In Main(), we can now declare head in one line as follows:

Location head = new Location(40, 12);

A constructor, in this case Location(int x, int y), is a method that always has the same name as the struct or class it is in (more about classes another time). It can be used to pass parameters that will be used when the object is created. In many constructors, including this one, those parameters are used to set the member variables in the struct (in this case X and Y).

In this case, we declare a new Location called head and pass 40 and 12 as parameters. Those end up in the constructor, where they are assigned to the X and Y variables within head. The this keyword simply states that we are working with the member variables of head (which is a Location).

We can now add the following at the beginning of Main():

            Console.OutputEncoding = System.Text.Encoding.GetEncoding(1252);
            Console.Title = "Snake Ranch";

...and some logic to show the head of the snake and handle movement:

            while (true)
            {
                // show head
              
                Console.Clear();
                Console.SetCursorPosition(head.X, head.Y);
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write((char) 178);
                Console.ResetColor();
              
                // handle input
              
                ConsoleKeyInfo keyInfo = Console.ReadKey(true);
              
                switch(keyInfo.Key)
                {
                    case ConsoleKey.Escape:
                        return;
                    case ConsoleKey.UpArrow:
                        if (head.Y > 0)
                            head.Y--;
                        break;
                    case ConsoleKey.DownArrow:
                        if (head.Y < 24)
                            head.Y++;
                        break;
                    case ConsoleKey.LeftArrow:
                        if (head.X > 0)
                            head.X--;
                        break;
                    case ConsoleKey.RightArrow:
                        if (head.X < 79)
                            head.X++;
                        break;
                }
            }

We now have something similar to Chase the Star, except that we don't yet have the star functionality:


We are using character number 178 from the ASCII Table to represent the head of the snake. When the user presses an arrow key, we modify the X or Y members of head. We can access them as head.X or head.Y.

Wonderful :) In this article we implemented the first part of our Snake game, and learned to use structs. Tomorrow we will learn to use Lists, which we will need to store each part of the snake. This will allow us to complete the game. :)

Check back tomorrow for more! :)

Monday, May 13, 2013

C# Basics: Command Interpreter Using Methods

Hello! :)

Today we're going to write a program similar to the Windows command prompt, Linux shell, or DOS. It's just a program that accepts commands from the user, and if it recognises the commands, it does something particular. We'll use this program to learn how to use methods, but we'll also learn a few other things as we go along. This article is admittedly longer than usual; rather that split it into two parts, I'm simply not going to post anything new tomorrow, so you can read this over two days if you like.

Let's start with this code:


            while (true)
            {
                String command = Console.ReadLine();

                if (command == "exit")
                    break;
            }

Here, we're accepting a line of text from the user and storing it in the String variable command. Instead of keeping a boolean variable to determine when to quit the loop, as in "C#: ASCII Art Game (Part 1)", we just loop forever (since the while condition is always true). When an "exit" command is received, the break statement allows us to break out of the loop anyway. Try that now.

Next, we add a little spice to our program. First, we set the console title:

            Console.Title = "Command Interpreter";

...and then, we make the user's input show up in white, by replacing the line containing Console.ReadLine() as follows:

                Console.ForegroundColor = ConsoleColor.White;
                String command = Console.ReadLine();
                Console.ResetColor();

Now, let's test what we have so far, by supporting an "echo" command. An "echo" command simply returns whatever you write after it. For example, if you write:

echo I can has cheezburger

...then you'd get:

I can has cheezburger

The only problem here is that we need to separate the command ("echo") from the parameters ("I can has cheezburger"). One way to do this is to break the command string into an array of individual words, as follows:

                String[] words = command.Split();

The command itself is now stored in words[0], while the parameters (if they exist) are in words[1] onwards. Finally, let's change the part where the "exit" command is being handled, and change it into a switch statement. The full code so far should look like this:

            Console.Title = "Command Interpreter";

            while (true)
            {
                Console.ForegroundColor = ConsoleColor.White;
                String command = Console.ReadLine();
                Console.ResetColor();

                String[] words = command.Split();

                switch (words[0])
                {
                    case "exit":
                        return;
                    case "echo":
                        for (int i = 1; i < words.Length; i++)
                        {
                            if (i > 1)
                                Console.Write(' ');
                            Console.Write(words[i]);
                        }
                        Console.WriteLine();
                        break;
                }
            }



For the "echo" command, we used a technique similar to that used in yesterday's article, "C# Basics: Morse Code Converter Using Dictionaries". You'll notice that the break statement used by the exit command has been changed to a return statement. That's because break has a different meaning when used in a switch statement, so we can't really use it there. Instead, return allows us to leave the Main() method completely. We can now test the program:


You'll notice that the code in the Main() method is beginning to grow, and will continue growing as we add more commands. We can organise our code by using methods. Methods are also called functions or subroutines in other languages, or even prodecures if they don't return anything. Take the following code:

                Console.ForegroundColor = ConsoleColor.White;
                String command = Console.ReadLine();
                Console.ResetColor();


...and move it into a new method outside Main(), as follows:

        static String ReceiveInput()
        {
            Console.ForegroundColor = ConsoleColor.White;
            String command = Console.ReadLine();
            Console.ResetColor();
            return command;
        }


This functionality is now defined in this method called ReceiveInput() which returns a String (i.e. it returns the input from the user). In the place where this code originally was, we now need only call this method:

                String command = ReceiveInput();

You'll notice that we now have two command variables declared: one in Main(), and one in ReceiveInput(). That's actually allowed, because they are in different methods. We say they are declared in a different scope.

Now, let's organise the code for the "echo" command in a similar way. Create a new method as follows:

        static void Echo(String[] words)
        {
            for (int i = 1; i < words.Length; i++)
            {
                if (i > 1)
                    Console.Write(' ');
                Console.Write(words[i]);
            }
            Console.WriteLine();
        }

This method has a void return type: that means it doesn't return anything - it just runs the statements within its body. This method is also different from the previous one in that it takes the parameter words. Methods can take any number of parameters in order to receive input data that they will be working with.

The code for the "echo" command can now be simplified as follows:

                    case "echo":
                        Echo(words);
                        break;

If you run the program now, you'll notice it works exactly the same as before.

Now, let's support another command called "total":

        static int Total(String[] words)
        {
            int total = 0;
            try
            {
                for (int i = 1; i < words.Length; i++)
                    total += Convert.ToInt32(words[i]);
            }
            catch (Exception)
            {
                Console.WriteLine("Invalid input");
            }

            return total;
        }

...and...

                    case "total":
                        int total = Total(words);
                        Console.WriteLine(total);
                        break;

The "total" command just calculates the sum of the numbers passed as parameters. Example output:




Finally, let's now support an "average" command:

        static int Average(String[] words)
        {
            int total = Total(words);
            int average = total / (words.Length - 1);
            return average;
        }

...and...

                    case "average":
                        int average = Average(words);
                        Console.WriteLine(average);
                        break;

You'll notice we are using Total() directly to calculate the sum used by the average, instead of repeating the code in Total(). One of the great benefits of methods is that they allow code reuse and eliminate duplicate code - something that plagued my earlier article, "The ASCII Table (C#)". A best practice in programming is the Don't Repeat Yourself (DRY) principle.

Output:


You'll remember from "C# Basics: Arithmetic and Exceptions" that we aren't getting any decimal point in divisions because we are working with integers.

That's it for today! We wrote a little command interpreter, learned how to create our own methods, and learned a lot of new stuff in the process including the break statement, the return statement, and variable scoping. As I wrote earlier, since this was extra long, there will be no article tomorrow!

Here's the full code for today's article:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CsCommandInterpreter
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Title = "Command Interpreter";

            while (true)
            {
                String command = ReceiveInput();

                String[] words = command.Split();

                switch (words[0])
                {
                    case "exit":
                        return;
                    case "echo":
                        Echo(words);
                        break;
                    case "total":
                        int total = Total(words);
                        Console.WriteLine(total);
                        break;
                    case "average":
                        int average = Average(words);
                        Console.WriteLine(average);
                        break;
                }
            }
        }

        static String ReceiveInput()
        {
            Console.ForegroundColor = ConsoleColor.White;
            String command = Console.ReadLine();
            Console.ResetColor();
            return command;
        }

        static void Echo(String[] words)
        {
            for (int i = 1; i < words.Length; i++)
            {
                if (i > 1)
                    Console.Write(' ');
                Console.Write(words[i]);
            }
            Console.WriteLine();
        }

        static int Total(String[] words)
        {
            int total = 0;
            try
            {
                for (int i = 1; i < words.Length; i++)
                    total += Convert.ToInt32(words[i]);
            }
            catch (Exception)
            {
                Console.WriteLine("Invalid input");
            }

            return total;
        }

        static int Average(String[] words)
        {
            int total = Total(words);
            int average = total / (words.Length - 1);
            return average;
        }
    }
}

As an exercise, test the program above and see if you can find any bugs (there are at least two). Solve them.

Come back on Wednesday for the next article! :)