Gigi Labs

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

Unity3D: Space Invaders (Part 5 - Behaviour)

Hi all! :)

In yesterday's article - Unity3D: Space Invaders (Part 4 - Collisions) - we learned how to handle collisions in Unity. At the end of the article, we listed several items that are still missing from Ranch Invaders.

Today's article deals with some of those issues, mainly relating to Player and Alien behaviour.

Player Movement

In Space Invaders, the Player's ship moves only left and right. If you've read "Unity3D: Moving an Object with Keyboard Input", you'll know that this is really easy to do. Open the Player script with MonoDevelop. You should currently have the code for shooting:

    void Update ()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Instantiate(bullet, this.transform.position, Quaternion.identity);
        }
    }

Change it as follows to allow the Player to move using the left and right arrows:

    void Update ()
    {
        Vector3 pos = this.transform.position;
       
        if (Input.GetKeyDown(KeyCode.Space))
            Instantiate(bullet, this.transform.position, Quaternion.identity);
        if (Input.GetKeyDown(KeyCode.LeftArrow))
            this.transform.position = new Vector3(pos.x - 0.1f, pos.y, pos.z);
        if (Input.GetKeyDown(KeyCode.RightArrow))
            this.transform.position = new Vector3(pos.x + 0.1f, pos.y, pos.z);
    }

Great, you can now move the Player's ship left and right. There's a problem, however. If you hold down an arrow key, the Player's ship does not continue moving. We can fix this by using Input.GetKey() instead. Also, we need to set constraints to prevent the Player's ship from leaving the screen:

    void Update ()
    {
        Vector3 pos = this.transform.position;
       
        if (Input.GetKey(KeyCode.Space))
            Instantiate(bullet, this.transform.position, Quaternion.identity);
        if (Input.GetKey(KeyCode.LeftArrow) && pos.x > -2.5f)
            this.transform.position = new Vector3(pos.x - 0.1f, pos.y, pos.z);
        if (Input.GetKey(KeyCode.RightArrow) && pos.x < 2.5f)
            this.transform.position = new Vector3(pos.x + 0.1f, pos.y, pos.z);
    }

Cooldown

As it is, the Player can fire as many bullets as it likes, with no gap between one and another. In order to implement a cooldown, we use an approach similar to this. When a bullet is fired, the time is recorded and no bullet may be fired before the cooldown period has passed. To do this, declare the following variables in the Player class:

    public float cooldown = 1.0f; // minimum time between bullets
    private float nextFire = 1.0f; // next time when a bullet can be fired

Then, adjust the firing code as follows:

        if (Input.GetKey(KeyCode.Space) && Time.time >= nextFire)
        {
            nextFire += cooldown;
            Instantiate(bullet, this.transform.position, Quaternion.identity);
        }

Then, try it out, and adjust cooldown as you like:


Then, rejoice.


Alien Movement

In Space Invaders, aliens move right, down, left, down, etc. Making this work properly takes a bit of work, so I'll just give you the code:

public class Alien : MonoBehaviour
{
    public float horDistance = 1.0f;
    public float verDistance = 0.5f;
    public float speed = 0.4f;
    private float startTime;
    private Vector3 startingPosition;
    private Vector3 target;
   
    // Use this for initialization
    void Start ()
    {
        this.startTime = Time.time;
        this.startingPosition = this.transform.position;
        this.target = this.startingPosition + new Vector3(this.horDistance, 0, 0);
    }
   
    // Update is called once per frame
    void Update ()
    {
        Vector3 currentPosition = this.transform.position;
        Vector3 newPosition = Vector3.MoveTowards(currentPosition, this.target, speed * Time.deltaTime);
        this.transform.position = newPosition;
       
        //print (this.target);
       
        if (newPosition == target)
        {
            if (newPosition.x == startingPosition.x)
            {
                if (((newPosition.y - startingPosition.y) / verDistance) % 2 == 0)
                    this.target = newPosition + new Vector3(this.horDistance, 0, 0);
                else
                    this.target = newPosition - new Vector3(0, this.verDistance, 0);
            }
            else
            {
                if (((newPosition.y - startingPosition.y) / verDistance) % 2 != 0)
                    this.target = newPosition - new Vector3(this.horDistance, 0, 0);
                else
                    this.target = newPosition - new Vector3(0, this.verDistance, 0);              
            }
        }
    }
}

Don't fret about what the code is doing... just use it. Tweak the public variables as you like.

More Aliens

Since we need a lot of aliens, it's a good idea to make the Alien object a prefab. To do this, drag it from the Hierarchy panel to the Assets panel. If you did it right, its listing in the Hierarchy panel should turn blue. This way, if you make any changes, you can apply them across all Aliens.

To make multiple Aliens, you can either press Ctrl+D (duplicate) to create additional objects, or else instantiate all the aliens in code using for loops. In my case, I just duplicated a handful of Aliens, just for testing:


Don't get too fussy about the Alien placement and all that. The reason we're still working with cubes and spheres is that you should first concentrate on making sure that your game mechanics work correctly. Only once that is done should you spend some time (a lot of time, actually) making it look good.

Aliens Shooting

Allowing Aliens to shoot the Player is no different from allowing the Player to shoot the Aliens. However, you now need a different bullet that moves downwards and that can destroy the player.

Once you attach a rigidbody to the Player (Component -> Physics -> Rigidbody, and remember to turn off "Use Gravity"), you will find some unexpected behaviour: at times, when the Player shoots a bullet, the Player itself is destroyed! That's because we don't yet have any code in the Bullet script that distinguishes between objects it can destroy. So far it worked correctly only because the Player didn't have a rigidbody, in which case the collision doesn't happen.

An easy way to solve this is to tag the object. Select the Player. At the top of the Inspector is a section where you can select a tag to assign to the object. For the Player, you can choose the predefined Player tag:


For the Aliens, you will need to add a new tag. From the drop-down list containing tags (shown above), select "Add Tag...".


In the resulting interface (shown above), expand the "Tags" node. Next to "Element 0", type "Alien". This creates the "Alien" tag, but does not assign it to the object.


To tag the Alien as such, select an Alien object, and select the newly available "Alien" tag from the drop-down list. Remember to click "Apply" to make this work for all Alien objects (since it's a prefab).

The Bullet script's collision code now becomes:

    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "Alien")
        {
            GameObject.Destroy(collision.gameObject);
            GameObject.Destroy(this.gameObject);
        }
    }

The Player's bullets should now destroy only Aliens.

For the aliens to shoot at the player, you will first need to create another bullet prefab (e.g. "AlienBullet") with the same script as the Bullet, except for two key differences. First, the bullet needs to go downwards instead of upwards. So:

    void Start ()
    {
        this.target = this.transform.position - new Vector3(0, 20, 0);
    }

Secondly, it needs to collide with the Player instead of the Alien:

void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Player")
{
GameObject.Destroy(collision.gameObject);
GameObject.Destroy(this.gameObject);
}
}


Finally, the code for shooting (in the Alien script) will be a little different from that of the Player. Instead of shooting when the user presses a key, the Alien will shoot with a random delay. First, declare the following variables in the Alien class:

    public GameObject bullet;
    public float minShootDelay = 1.0f;
    public float maxShootDelay = 7.0f;
    private float nextShootTime = 0.0f;

In the Unity editor, set the AlienBullet prefab in the Alien script's bullet slot.

In the Start() method, add code to initialise nextShootTime:

        this.nextShootTime = Random.Range(minShootDelay, maxShootDelay);

Unity's Random.Range() method is similar to .NET's Random.Next() method, but not quite the same.

Change the code that handles when bullets leave the game area, since these bullets are going down instead of up:

        if (this.transform.position.y < -3.0f)
            GameObject.Destroy(this.gameObject);

Finally, in the Update() method, we add code for actual shooting:

        if (Time.time > nextShootTime)
        {
            Instantiate(bullet, this.transform.position, Quaternion.identity);
            nextShootTime = Time.time + Random.Range(minShootDelay, maxShootDelay);
        }

If you try it now, things will get a little bit messy as the Aliens' bullets will collide with themselves:


Explaining how to solve this requires an article in itself. Notwithstanding this, in this (long) article we have taken care of several behaviour issues with both the Player and the Aliens, and brought the game much closer to a working Space Invaders clone.

This is the last article in the Ranch Invaders series. I hope you'll return to the Ranch to read about other interesting topics! :)

Wednesday, May 15, 2013

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

Hi all! :)

In yesterday's article, C# Basics: Snake Game in ASCII Art (Part 1), we created the first part of our Snake Ranch game (a clone of Snake) and learned to use structs. Today we will learn to use lists in order to allow the snake to grow.

At the beginning of yesterday's article, we mentioned that we needed to store each part of the snake in a list, as follows:


The head of the snake would be at (1,1), for example. The remaining parts trail behind it in sequence. We can store these locations in a list, in the following fashion:


There is a List collection in .NET that allows us to do this kind of thing. As with dictionaries (see "C# Basics: Morse Code Converter Using Dictionaries", we first need to include System.Collections.Generic first:

using System.Collections.Generic;

We can now declare a new List of Location objects, and put the head as the first item:

            List<Location> snake = new List<Location>();
            snake.Add(head);

We now change the code that shows the head (from yesterday's article) to show the entire snake in the list:

                // show snake
               
                foreach (Location location in snake)
                {
                    Console.SetCursorPosition(location.X, location.Y);
                    Console.ForegroundColor = ConsoleColor.White;
                    Console.Write((char) 178);
                    Console.ResetColor();
                }

All we're doing here is going over each element in the list and showing a portion of the snake there. For now we only have the head though.

Before we make the snake grow, we first need to understand what happens when it moves. Consider this again:


Let's say the head of the snake is at (1,1), and it moves to the left. Then the head becomes (0,1). The tail (last part) of the snake goes away, and all the parts between the new head and the old tail remain unchanged. We need to make some changes to our code to handle this properly. First, before the while loop, we declare a new Location variable called next:

            Location next;

This will hold the position of the new head, based on the keypress received. In the example above, the head is currently at (1,1), but if the user presses the left arrow, then we want next to contain (0,1).

At the beginning of the while loop, we add the following:

                next = snake[0];
               
                Console.Clear();

The first line assigns next to the location of the current head, stored in the first element of the snake variable. Lists can be accessed using [] notation just like arrays.

Next, we change the input handling logic to modify next instead of head:

                switch(keyInfo.Key)
                {
                    case ConsoleKey.Escape:
                        return;
                    case ConsoleKey.UpArrow:
                        if (next.Y > 0)
                            next.Y--;
                        break;
                    case ConsoleKey.DownArrow:
                        if (next.Y < 24)
                            next.Y++;
                        break;
                    case ConsoleKey.LeftArrow:
                        if (next.X > 0)
                            next.X--;
                        break;
                    case ConsoleKey.RightArrow:
                        if (next.X < 79)
                            next.X++;
                        break;
                }

After that, we can do the following:

                snake.Insert(0, next);
                snake.RemoveAt(snake.Count - 1);

Here we are doing exactly what I explained earlier: adding a new head at the beginning of the snake, and removing the old tail. If you press F5 now, you should have functionality just like what we had at the end of yesterday's article (because we still have just one part of the snake):


All we have left to do now is to make the snake get longer when he eats a star. At the beginning, add a new variable to store the location of the star:

Location star = new Location(60, 20);

After showing the snake, add the following code to show the star:

                // show star
               
                Console.SetCursorPosition(star.X, star.Y);
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write('*');
                Console.ResetColor();

Finally, replace the last two lines in the while loop (Insert() and RemoveAt()) with the following logic:

                snake.Insert(0, next);
                if (next.X == star.X && next.Y == star.Y)
                {
                    Random random = new Random();
                    star.X = random.Next(0, 80);
                    star.Y = random.Next(0, 25);
                }
                else
                    snake.RemoveAt(snake.Count - 1);

This code is based on the code from Chase the Star (see "C#: ASCII Art Game (Part 2)"). If the snake bumps into the star, the star moves away to some other random location. Here we are using structs instead of separate starX and starY variables.

The most important thing to understand here is that if the player bumps into the star, we don't remove the snake's tail, thus allowing it to grow. We can now test it:



Awesome! :) We have just made our own variant of Snake using ASCII art. If you test it thoroughly, you'll notice there are a few problems:

  1. The snake doesn't move on its own as time goes by. You need to press an arrow key for it to move.
  2. The snake can bump into itself or the edges and nothing bad happens.
  3. If the snake moves into the edge, it sort of compresses. When you move away, it regains its former length.
  4. The star can appear over the snake.
  5. No obstacles... boring!

As you can see, it can be quite complicated to make a complete game, as there are many things you need to take care of. Making a fully blown Snake game would take many more articles, so I'll leave it at that. In this article we use generic lists to implement the Snake concept. If you're really adventurous, you might want to try and fixing some of the issues above as an exercise. Don't worry if you don't manage - just thinking about strategies to solve them will teach you how to think like a programmer.

I hope you enjoyed this, and come back for more programming articles! :)

Here is the full code for Snake Ranch:

using System;
using System.Collections.Generic;

namespace CsSnake
{
    struct Location
    {
        public int X;
        public int Y;
       
        public Location(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }
    };
   
    class Program
    {
        public static void Main(string[] args)
        {
            Console.OutputEncoding = System.Text.Encoding.GetEncoding(1252);
            Console.Title = "Snake Ranch";
           
            Location head = new Location(40, 12);
            List<Location> snake = new List<Location>();
            snake.Add(head);
           
            Location next;

            Location star = new Location(60, 20);
           
            while (true)
            {
                next = snake[0];
               
                Console.Clear();
               
                // show snake
               
                foreach (Location location in snake)
                {
                    Console.SetCursorPosition(location.X, location.Y);
                    Console.ForegroundColor = ConsoleColor.White;
                    Console.Write((char) 178);
                    Console.ResetColor();
                }
               
                // show star
               
                Console.SetCursorPosition(star.X, star.Y);
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write('*');
                Console.ResetColor();
               
                // handle input
               
                ConsoleKeyInfo keyInfo = Console.ReadKey(true);
               
                switch(keyInfo.Key)
                {
                    case ConsoleKey.Escape:
                        return;
                    case ConsoleKey.UpArrow:
                        if (next.Y > 0)
                            next.Y--;
                        break;
                    case ConsoleKey.DownArrow:
                        if (next.Y < 24)
                            next.Y++;
                        break;
                    case ConsoleKey.LeftArrow:
                        if (next.X > 0)
                            next.X--;
                        break;
                    case ConsoleKey.RightArrow:
                        if (next.X < 79)
                            next.X++;
                        break;
                }
               
                snake.Insert(0, next);
                if (next.X == star.X && next.Y == star.Y)
                {
                    Random random = new Random();
                    star.X = random.Next(0, 80);
                    star.Y = random.Next(0, 25);
                }
                else
                    snake.RemoveAt(snake.Count - 1);
            }
        }
    }
}

Thursday, May 9, 2013

C#: ASCII Art Game (Part 2)

Hello, and welcome to this second article about creating an ASCII-art game. This article continues where yesterday's article, C#: ASCII Art Game (Part 1), left off. So if you missed it, read it first! :) We're going to continue from the code at the end of that article.

In today's article we'll implement some basic gameplay in our console-window-based game, Chase the Star. We're going to learn about switch statements, randomness, and add a touch of colour to the game. Yippee! ;)

Let's start with that switch statement. In yesterday's code, we had a bunch of if statements that check whether the arrow keys or the ESC key was pressed. When you have a bunch of if statements checking the value of the same thing (in this case keyInfo.Key), it is more convenient to use a switch statement instead, as follows:

                ConsoleKeyInfo keyInfo = Console.ReadKey(true);
             
                switch(keyInfo.Key)
                {
                    case ConsoleKey.Escape:
                        quit = true;
                        break;
                    case ConsoleKey.UpArrow:
                        y--;
                        break;
                    case ConsoleKey.DownArrow:
                        y++;
                        break;
                    case ConsoleKey.LeftArrow:
                        x--;
                        break;
                    case ConsoleKey.RightArrow:
                        x++;
                        break;
                    default:
                        // nothing here... run along, kids
                        break;
                }

In the brackets next to switch, we put the variable whose value we are interested in. Then, we put a case statement for each value that interests us. Optionally, we can also include a default statement. This works pretty much like an else: it is used when none of the case values apply. A break statement must be placed at the end of all case and default sections.

Towards the end of yesterday's article, you might remember that we had a problem when the user runs off the screen. The console is usually 80 characters wide and 25 characters high, so we can check the player's position to ensure she never goes out of bounds:

                switch(keyInfo.Key)
                {
                    case ConsoleKey.Escape:
                        quit = true;
                        break;
                    case ConsoleKey.UpArrow:
                        if (y > 0)
                            y--;
                        break;
                    case ConsoleKey.DownArrow:
                        if (y < 24)
                            y++;
                        break;
                    case ConsoleKey.LeftArrow:
                        if (x > 0)
                            x--;
                        break;
                    case ConsoleKey.RightArrow:
                        if (x < 79)
                            x++;
                        break;
                }

Now, you'll find you can't run off the edge of the screen! If you're scratching your head a little, just remember that (0, 0) is the top-left corner. x increases as you go right, and y increases as you go down. Since both start at zero, then the highest x value we can accept is 79, not 80. Similarly, the highest y is 24.

Right now, all you can do is run around. Let's make it a little more interesting. We're going to place a star somewhere in the window, and once the user runs into it, it will appear somewhere else, Hence the name, Chase the Star. The first thing we need is a pair of new variables to store the x and y position of the star. Put these towards the beginning, before or after your x and y variables:

            int starX = 64;
            int starY = 5;

After the part where we write our "X" to the console window, let's add code to show our star:

                Console.SetCursorPosition(starX, starY);
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write("*");
                Console.ResetColor();

Here, we set the cursor position to the location where the star is. We write an asterisk to represent the star, and we write it in yellow to add a touch of colour. After that, we reset the console colour to the default light grey. If we don't, everything will be written in yellow! Here's what we've got so far:



After the switch statement, add this:

                if (x == starX && y == starY)
                {
                    Random random = new Random();
                    starX = random.Next(0, 80);
                    starY = random.Next(0, 25);
                }

This small piece of logic takes care of what happens when you run into the star. When that happens, we move the star somewhere else by setting starX and starY to some random values. In order to obtain a random number, we use Random.Next(). We can specify a range (as above) in which the random number should be generated. For starX, for example, we are requesting a random number between 0 and 79 (since the upper bound is not inclusive, the 80 doesn't count).

Awesome! You have just made your first game, and it's even a little bit of fun to play. :D

You should now be extremely proud of yourself, and with some more practice, you might even become a national hero at some point.

Here's the complete code:

            int x = 40;
            int y = 12;
            int starX = 64;
            int starY = 5;
            bool quit = false;
            Console.Title = "Chase the Star";
         
            while (quit == false)
            {
                Console.Clear();
                Console.SetCursorPosition(x, y);
                Console.Write("X");
             
                Console.SetCursorPosition(starX, starY);
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write("*");
                Console.ResetColor();
             
                ConsoleKeyInfo keyInfo = Console.ReadKey(true);
             
                switch(keyInfo.Key)
                {
                    case ConsoleKey.Escape:
                        quit = true;
                        break;
                    case ConsoleKey.UpArrow:
                        if (y > 0)
                            y--;
                        break;
                    case ConsoleKey.DownArrow:
                        if (y < 24)
                            y++;
                        break;
                    case ConsoleKey.LeftArrow:
                        if (x > 0)
                            x--;
                        break;
                    case ConsoleKey.RightArrow:
                        if (x < 79)
                            x++;
                        break;
                }

                if (x == starX && y == starY)
                {
                    Random random = new Random();
                    starX = random.Next(0, 80);
                    starY = random.Next(0, 25);
                }
            }

If you want to get your hands a bit dirtier, here's a little exercise. Keep track of the player's score, by adding 10 to it each time she hits the star. Display the score in the bottom left part of the console window. Don't allow the player to walk onto the last line (where y is 24).

There will be plenty more interesting articles in the coming days, so keep coming back for more! :)