1

I just started learning programming couple months ago and decided to do a console snake game. Everything works great expect for one thing.

If my snake is going upwards and I press down arrow and keep it pressed, my snake just stops and stays stopped even awhile after I stop pressing the button.

Same thing happens if my snake is going right and I press right arrow too long, I lose control for some time(but snake does not stop). It happens for every way(left,right,up,down).

I tried to compare the cki to another ConsoleKeyInfo with slight delay between them, but it doesn't matter. If I keep the key pressed, my program just stays that spot and updates for a key. (atleast I think thats the problem)

Is this a "feature" of the Console.ReadKey or is there some way arount this?

Keep in mind, that I just started so I don't know about the advanced stuff, yet.

Everything works flawless as long as I don't keep the key pressed more than 1 sec.

   public void LiikutaMato() //movesnake
    {

        if (Console.KeyAvailable)
        {
                ConsoleKeyInfo cki;
                cki = Console.ReadKey(true); // <-- I believe this is where it gets stuck 

    }
Ville
  • 13
  • 1
  • 3
  • the polling rate is piling up the input on readkey and you end up having to wait for it to clear the stack before anything new is accepted... just a guess – emd Jun 03 '13 at 20:20
  • When you press it and keep it pressed, the system will (I think) start sending many keypress events to the Console. If you end up blocking due to these, (e.g. your calculations or the console redraw take more time than the time between each keypress being sent) or making the snake stop (e.g. because you don't move the snake on the instant the key is pressed to change direction normally), that might be causing your problem. – Tim S. Jun 03 '13 at 20:20
  • 1
    Just a word of advice... Writing games like that as console applications is a bit of overkill and a bit of "workarounding". You may wish to hone your general coding skills a bit further and, since you're going with C#, learn XNA. – Geeky Guy Jun 03 '13 at 20:20
  • 2
    @Renan I'd say learn MonoGame instead, since XNA is [pretty much dead](http://www.wpcentral.com/xna-dead-long-live-xna) and not even available with VS2012 unless you make some workarounds. – Pierre-Luc Pineault Jun 03 '13 at 20:36
  • You probably want to use a different method to read keyboard state. For example: http://stackoverflow.com/questions/4351258/c-sharp-arrow-key-input-for-a-console-app – Dzienny Jun 03 '13 at 22:57

1 Answers1

9

Play with this...it uses a tight loop to consume the keypresses until the set delay has expired. You also don't have to hold down the key to keep the snake moving. It was very responsive on my system: Snake Console

class Program
{

    public enum Direction
    {
        Up, 
        Down, 
        Right, 
        Left
    }

    static void Main(string[] args)
    {
        const int delay = 75;
        string snake = "O";
        char border = 'X';

        int x, y;
        int length;
        bool crashed = false;
        Direction curDirection = Direction.Up;
        Dictionary<string, bool> eaten = new Dictionary<string, bool>();
        Console.CursorVisible = false;

        ConsoleKeyInfo cki;
        bool quit = false;
        while (!quit)
        {
            Console.Clear();
            Console.Title = "Use 'a', 's', 'd' and 'w' to steer.  Hit 'q' to quit.";

            // draw border around the console:
            string row = new String(border, Console.WindowWidth);
            Console.SetCursorPosition(0, 0);
            Console.Write(row);
            Console.SetCursorPosition(0, Console.WindowHeight - 2);
            Console.Write(row);
            for (int borderY = 0; borderY < Console.WindowHeight - 2; borderY++)
            {
                Console.SetCursorPosition(0, borderY);
                Console.Write(border.ToString());
                Console.SetCursorPosition(Console.WindowWidth - 1, borderY);
                Console.Write(border.ToString());
            }

            // reset all game variables:
            length = 1;
            crashed = false;
            curDirection = Direction.Up;
            eaten.Clear();
            x = Console.WindowWidth / 2;
            y = Console.WindowHeight / 2;
            eaten.Add(x.ToString("00") + y.ToString("00"), true);

            // draw new snake:
            Console.SetCursorPosition(x, y);
            Console.Write(snake);

            // wait for initial keypress:
            while (!Console.KeyAvailable)
            {
                System.Threading.Thread.Sleep(10);
            }

            // see if intitial direction should be changed:
            cki = Console.ReadKey(true);
            switch (cki.KeyChar)
            {
                case 'w':
                    curDirection = Direction.Up;
                    break;

                case 's':
                    curDirection = Direction.Down;
                    break;

                case 'a':
                    curDirection = Direction.Left;
                    break;

                case 'd':
                    curDirection = Direction.Right;
                    break;

                case 'q':
                    quit = true;
                    break;
            }

            // main game loop:
            DateTime nextCheck = DateTime.Now.AddMilliseconds(delay);
            while (!quit && !crashed)
            {
                Console.Title = "Length: " + length.ToString();

                // consume keystrokes and change current direction until the delay has expired:
                // *The snake won't actually move until after the delay!
                while (nextCheck > DateTime.Now)
                {
                    if (Console.KeyAvailable)
                    {
                        cki = Console.ReadKey(true);
                        switch (cki.KeyChar)
                        {
                            case 'w':
                                curDirection = Direction.Up;
                                break;

                            case 's':
                                curDirection = Direction.Down;
                                break;

                            case 'a':
                                curDirection = Direction.Left;
                                break;

                            case 'd':
                                curDirection = Direction.Right;
                                break;

                            case 'q':
                                quit = true;
                                break;
                        }
                    }
                }

                // if the user didn't quit, attempt to move without hitting the border:
                if (!quit)
                {
                    string key = "";
                    switch (curDirection)
                    {
                        case Direction.Up:
                            if (y > 1)
                            {
                                y--;
                            }
                            else
                            {
                                crashed = true;
                            }
                            break;

                        case Direction.Down:
                            if (y < Console.WindowHeight - 3)
                            {
                                y++;
                            }
                            else
                            {
                                crashed = true;
                            }
                            break;

                        case Direction.Left:
                            if (x > 1)
                            {
                                x--;
                            }
                            else
                            {
                                crashed = true;
                            }

                            break;

                        case Direction.Right:
                            if (x < Console.WindowWidth - 2)
                            {
                                x++;
                            }
                            else
                            {
                                crashed = true;
                            }
                            break;
                    }

                    // if the user didn't hit the border, see if they hit the snake
                    if (!crashed)
                    {
                        key = x.ToString("00") + y.ToString("00");
                        if (!eaten.ContainsKey(key))
                        {
                            length++;
                            eaten.Add(key, true);
                            Console.SetCursorPosition(x, y);
                            Console.Write(snake);
                        }
                        else
                        {
                            crashed = true;
                        }
                    }

                    // set the next delay:
                    nextCheck = DateTime.Now.AddMilliseconds(delay);
                }
            } // end main game loop

            if (crashed)
            {
                Console.Title = "*** Crashed! *** Length: " + length.ToString() + "     Hit 'q' to quit, or 'r' to retry!";

                // wait for quit or retry:
                bool retry = false;
                while (!quit && !retry)
                {
                    if (Console.KeyAvailable)
                    {
                        cki = Console.ReadKey(true);
                        switch (cki.KeyChar)
                        {
                            case 'q':
                                quit = true;
                                break;

                            case 'r':
                                retry = true;
                                break;
                        }
                    }
                }
            } 

        } // end main program loop

    } // end Main()

}
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
  • Thank you Idle_Mind. It worked. My problem was that I was updating my keys and my snake lenght at the same time. Now I first check the key in the do while-loop, and after the timer has passed for it, then I modify my snakes coordinates. No more stopping and my snake moves at same speed all the time. – Ville Jun 04 '13 at 17:16
  • 1
    How did you never, ever, get an upvote for a cool little game? –  Jan 06 '16 at 19:24
  • Nice program. Just two comments: I found it weird that the snake always grew from the starting point, but I understand that's a tradeoff for not having things to catch, which enlarge the snake. Second: it shouldn't crash if you click backwards; it should ignore the command. If I press two keys quick to make a U turn, it crashes because it didn't react to the first turn. – Andrew Jul 11 '19 at 17:06
  • 1
    Thank for the feedback @Andrew. I'll fix those issues. – Idle_Mind Jul 11 '19 at 17:45