Gigi Labs

Please follow Gigi Labs for the latest articles. Programmer's Ranch no longer has its domain, so please update your bookmarks and links to programmersranch.blogspot.com.

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(4012);

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((char178);
                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! :)

No comments:

Post a Comment