Jump to content
  • Advertisement
Sign in to follow this  
  • entries
    3
  • comment
    1
  • views
    248

First instalment

duke_meister

643 views

This code is similar to that I posted in @phil67rpg 's blog but with comments and updated to remove some unnecessary stuff.

I'll finish it off by making the snake grow longer, otherwise it's not much of a challenge.

Be kind on the code, I wanted to see what I could do before bed last night :)

PS: there's a big bug in the code..

 

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleSnake
{
    /// <summary>
    /// All code written by duke_meister (Valentino Rossi)
    /// except keyboard reading technique
    /// </summary>
    class Program
    {
        // our unchanging values:
        // playfield height & width
        const int PlayfieldWidth = 80;
        const int PlayfieldHeight = 40;
        
        // timeout so game isn't too fast
        static int MillisecondsTimeout = 50;
        
        // our playfield; stores FieldVals instead of ints so we don't have to remember them
        static readonly FieldVals[,] PlayField = new FieldVals[PlayfieldWidth, PlayfieldHeight];

        // not yet used until we increase length of snake
        static int _snakeBodyLen = 4; // not including head

        // which direction (SnakdDirs enum) the snake is currently moving
        static SnakeDirs _snakeDir = SnakeDirs.Right;

        // position of the one-and-only piece of food; use our own coordinate class, Pos
        static readonly Pos FoodPos = new Pos(0, 0);

        // defines the snake; each element tells us which coordinates each snake piece is at
        static readonly Pos[] SnakeCells = { new Pos(14, 10), new Pos(13, 10), new Pos(12, 10), new Pos(11, 10), new Pos(10, 10) };

        // guess
        private static int _score = 0;

        // for randomizing things like food placement
        private static Random _rnd;

        // could've used something existing, but made a simple screen coordinate class
        public class Pos
        {
            public int X { get; set; }
            public int Y { get; set; }

            public Pos(int x, int y)
            {
                X = x;
                Y = y;
            }
        }

        // these make it easy (for the human) to know what each cell contains
        enum FieldVals { Empty = 1, SnakeHead, SnakeBody, SnakeFood }

        // these make it easy (for the human) to read snake the direction
        enum SnakeDirs { Up, Right, Down, Left }

        static void Main(string[] args)
        {
            _rnd = new Random();

            Console.Clear();

            // start with empty playfield
            for (var i = 0; i < PlayfieldWidth; i++)
            {
                for (var j = 0; j < PlayfieldHeight; j++)
                {
                    PlayField[i, j] = 0;
                }
            }

            // start with an initial piece of food
            MakeNewFood();

            // draw the border, once
            DrawBorder();

            // game loop; this was the easiest but might switch to Timer, etc.
            // function names should explain purpose
            for (;/* ever */;)
            {
                AdjustGameSpeed();
                CheckForKeyboardCommand();
                UpdatePlayfield();
                CheckForSnakeOutOfBounds();
                UpdateSnakeBodyPosition();
                CheckSnakeHasEatenFood();
            }
        }

        private static void AdjustGameSpeed()
        {
            // delay so the game isn't too fast. Halve the delay (to go faster) when going left or right
            // as the playfield isn't square
            Task.Delay( _snakeDir == SnakeDirs.Up || _snakeDir == SnakeDirs.Right ? MillisecondsTimeout / 2 : MillisecondsTimeout).Wait();
        }

        /// <summary>
        /// Check the keyboard for arrow keys
        /// I got the code off the net (see bottom of code); no point re-creating this
        /// </summary>
        private static void CheckForKeyboardCommand()
        {
            if (NativeKeyboard.IsKeyDown(KeyCode.Down)) // player hit Down arrow
            {
                // can't hit down while going up; game over
                if (_snakeDir == SnakeDirs.Up)
                    EndGame(false);

                // change snake direction to down
                _snakeDir = SnakeDirs.Down;
            }
            else if (NativeKeyboard.IsKeyDown(KeyCode.Up))
            {
                // can't hit up while going down; game over
                if (_snakeDir == SnakeDirs.Down)
                    EndGame(false);

                // change snake direction to up
                _snakeDir = SnakeDirs.Up;
            }
            else if (NativeKeyboard.IsKeyDown(KeyCode.Left))
            {
                // can't hit left while going right; game over
                if (_snakeDir == SnakeDirs.Right)
                    EndGame(false);

                // change snake direction to left
                _snakeDir = SnakeDirs.Left;
            }
            else if (NativeKeyboard.IsKeyDown(KeyCode.Right))
            {
                // can't hit right while going left; game over
                if (_snakeDir == SnakeDirs.Left)
                    EndGame(false);

                // change snake direction to right
                _snakeDir = SnakeDirs.Right;
            }
        }

        /// <summary>
        /// See if snake has eaten the food
        /// </summary>
        private static void CheckSnakeHasEatenFood()
        {
            // if snake head is in the same x,y position as the food
            // NB: First() is a Linq function; it gives me the first element in the array
            if (SnakeCells.First().X == FoodPos.X && SnakeCells.First().Y == FoodPos.Y)
            {
                IncrementScore();
                MakeNewFood();
            }
        }

        private static void IncrementScore()
        {
            ++_score;
            WriteAt( $"Score: {_score}", 0, 0);
        }

        /// <summary>
        /// Put food item at random location
        /// </summary>
        private static void MakeNewFood()
        {
            int x, y;
            do
            {
                // this ensures we're not putting the food on top of the snake, or the border
                x = _rnd.Next(1, PlayfieldWidth - 1);
                y = _rnd.Next(1, PlayfieldHeight - 1);
            } while (SnakeCells.Any(pos => pos.X == x || pos.Y == y));

            // set the food coords
            FoodPos.X = x;
            FoodPos.Y = y;

            // update the playfield position with the food value (plus the update value)
            PlayField[FoodPos.X, FoodPos.Y] = FieldVals.SnakeFood;
        }

        static void CheckForSnakeOutOfBounds()
        {
            // snake mustn't be on any border cell, or game over
            if (SnakeCells.First().Y < 1 || SnakeCells.First().X > PlayfieldWidth - 2 ||SnakeCells.First().Y > PlayfieldHeight - 2 || SnakeCells.First().X < 1)
            {
                EndGame(false);
            }
        }

        /// <summary>
        /// Move the snake pieces appropriately. I just did the simplest thing that I thought of.
        /// </summary>
        static void UpdateSnakeBodyPosition()
        {
            // Last piece of snake's tail will always become empty as the snake moves
            // NB: Last() is a Linq function; it gives me the last element in the array (end of snake tail)
            PlayField[SnakeCells.Last().X, SnakeCells.Last().Y] = FieldVals.Empty;

            // move the 'middle' section of the snake one cell along
            for (int i = SnakeCells.Length - 1; i > 0; i--)
            {
                SnakeCells[i].X = SnakeCells[i - 1].X;
                SnakeCells[i].Y = SnakeCells[i - 1].Y;
            }

            // move the snake's head, depending on direction moving
            // the body was already moved above
            switch (_snakeDir)
            {
                case SnakeDirs.Up:
                    // moved the snake head up 1 (-ve Y direction)
                    --SnakeCells.First().Y;
                    break;
                case SnakeDirs.Right:
                    // moved the snake head right 1 (+ve X direction)
                    ++SnakeCells.First().X;
                    break;
                case SnakeDirs.Down:
                    // moved the snake head up 1 (+ve Y direction)
                    ++SnakeCells.First().Y;
                    break;
                case SnakeDirs.Left:
                    // moved the snake head left 1 (-ve X direction)
                    --SnakeCells.First().X;
                    break;
            }

            // Set the playfield position at the head of the snake, to be... the snake head!
            PlayField[SnakeCells.First().X, SnakeCells.First().Y] = FieldVals.SnakeHead;
            
            // Set the positions on the playfield for the snake body cells
            // so we know to draw them
            // NB: Skip(1).Take(4) is Linq; it gives me the array left after
            // skipping the first item, then grabbing the next 4 (so in this
            // case misses the first and last).
            foreach (var cell in SnakeCells.Skip(1).Take(4))
            {
                PlayField[cell.X, cell.Y] = FieldVals.SnakeBody;
            }
        }

        /// <summary>
        /// Just show a message and exit (can only lose right now)
        /// </summary>
        /// <param name="win"></param>
        static void EndGame(bool win)
        {
            Console.Clear();
            Console.WriteLine($"YOU {( win ? "WIN" : "LOSE")}");
            Console.ReadKey();
            Environment.Exit(0);
        }

        /// <summary>
        /// Set the console size appropriately & draw the border, leaving room for the score
        /// </summary>
        static void DrawBorder()
        {
            Console.SetWindowSize(PlayfieldWidth, PlayfieldHeight + 2);

            WriteAt("+", 0, 1);
            WriteAt("+", PlayfieldWidth - 1, 1);
            WriteAt("+", 0, PlayfieldHeight);
            WriteAt("+", PlayfieldWidth - 1, PlayfieldHeight);

            for (var i = 1; i < PlayfieldWidth - 1; i++)
            {
                WriteAt("-", i, 1);
                WriteAt("-", i, PlayfieldHeight);
            }
            for (var i = 2; i < PlayfieldHeight ; i++)
            {
                WriteAt("|", 0, i);
                WriteAt("|", PlayfieldWidth - 1, i);
            }
        }

        /// <summary>
        /// Go through every element of the 2d array, only drawing a cell
        /// if it has a value (other than 0). This way we only draw the
        /// cells that need to be updated. A bit like Invalidate() in GDO.
        /// Pretty self-explanatory; if a cell has a value, draw the character
        /// appropriate for it. The space is only used to overwrite the last
        /// piece of the snake's tail.
        /// </summary>
        static void UpdatePlayfield()
        {
            for (var i = 1; i < PlayfieldWidth - 1; i++)
            {
                for (var j = 1; j < PlayfieldHeight - 1; j++)
                {
                    switch (PlayField[i, j])
                    {
                        case FieldVals.Empty:
                            WriteAt( " ", i, j + 1);
                            break;
                        case FieldVals.SnakeHead:
                            WriteAt("@", i, j + 1);
                            break;
                        case FieldVals.SnakeBody:
                            WriteAt("o", i, j + 1);
                            break;
                        case FieldVals.SnakeFood:
                            WriteAt(".", i, j + 1);
                            break;
                    }
                }
            }
        }

        // From Microsoft sample
        protected static void WriteAt(string s, int x, int y)
        {
            try
            {
                Console.SetCursorPosition(x, y);
                Console.Write(s);
            }
            catch (ArgumentOutOfRangeException e)
            {
                Console.Clear();
                Console.WriteLine(e.Message);
            }
        }
    }

    /// <summary>
    /// Codes representing keyboard keys.
    /// </summary>
    /// <remarks>
    /// Key code documentation:
    /// http://msdn.microsoft.com/en-us/library/dd375731%28v=VS.85%29.aspx
    /// </remarks>
    internal enum KeyCode
    {
        Left = 0x25,
        Up,
        Right,
        Down
    }

    /// <summary>
    /// Provides keyboard access.
    /// </summary>
    internal static class NativeKeyboard
    {
        /// <summary>
        /// A positional bit flag indicating the part of a key state denoting
        /// key pressed.
        /// </summary>
        const int KeyPressed = 0x8000;

        /// <summary>
        /// Returns a value indicating if a given key is pressed.
        /// </summary>
        /// <param name="key">The key to check.</param>
        /// <returns>
        /// <c>true</c> if the key is pressed, otherwise <c>false</c>.
        /// </returns>
        public static bool IsKeyDown(KeyCode key)
        {
            return (GetKeyState((int)key) & KeyPressed) != 0;
        }

        /// <summary>
        /// Gets the key state of a key.
        /// </summary>
        /// <param name="key">Virtual-key code for key.</param>
        /// <returns>The state of the key.</returns>
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern short GetKeyState(int key);
    }


}

 



0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By horror_man
      Hello, I'm currently searching for additional talented and passionate members for our team that's creating a small horror game.
       
      About the game: The game would be a small sci-fi/post-apocalyptic survival horror 3D game with FPS (First person shooter) mechanics and an original setting and story based in a book (which I'm writing) scene, where a group of prisoners are left behind in an abandoned underground facility. It would play similar to Dead Space combined with Penumbra and SCP: Secret Laboratory, with the option of playing solo or multiplayer.
       
      Engine that'd be used to create the game: Unity
       
      About me: I'm a music composer with 4 years of experience and I'm fairly new in this game development world, and I'm currently leading the team that'd be creating this beautiful and horrifying game. I decided that making the book which I'm writing into a game would be really cool, and I got more motivated about doing so some time ago when I got a bunch of expensive Unity assets for a very low price. However, I researched about how to do things right in game development so I reduced the scope of it as much as I could so that's why this game is really based in a scene of the book and not the entire thing. Also I'm currently learning how to use Unity and learning how to program.
       
      Our team right now consists of: Me (Game Designer, Creator, Music Composer, Writer), 4 3D Modelers, 2 Game Programmers, 1 Sound Effect Designer, 1 Concept Artist, 1 3D Animator and 1 Community Manager.
       
      Who am I looking for: We are looking for a talented and passionate programmer that's experienced with Unity and C#.
      Right now the game is in mid-early development and you can see more information about it and follow our progress in our game jolt page here: https://gamejolt.com/games/devilspunishment/391190 . We expect to finish some sort of prototype in 3 months from now.
       
      This is a contract rev-share position
       
      If you are interested in joining, contributing or have questions about the project then let's talk. You can message me in Discord: world_creator#9524
    • By RoKabium Games
      A message from the Aliens for this week's #screenshotsaturday.
      Happy Easter to all!
      If you're looking for something to do, why not join our Alpha testing?
      Check it out at this link: http://bit.ly/sama-testing
    • By Alexander Nazarov
      Hello. I'm pretty new in Unity and I wonder how and what should I use in Unity to create smooth and physics-like tile moving in my match-3 game?
       
       
      This is example of game, where tile movements are most attractive for me 
       
    • By phil67rpg
      well I am able to draw a hexagon but I want to draw a whole screen of hexagons. here is my working code
      private void Form1_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; Pen whitePen = new Pen(Color.White, 1); float height = 50.0f; float width = (float)(4 * (height / 2 / Math.Sqrt(3))); float y = height / 2; float x = 0; float row = 0.0f; y += row * height; float col = 0.0f; if (col % 2 == 1) { y += height / 2; } x += col * (width * 0.75f); PointF pt1 = new PointF(x, y); PointF pt2 = new PointF(x + width * 0.25f, y - height / 2); PointF pt3 = new PointF(x + width * 0.75f, y - height / 2); PointF pt4 = new PointF(x + width, y); PointF pt5 = new PointF(x + width * 0.75f, y + height / 2); PointF pt6 = new PointF(x + width * 0.25f, y + height / 2); g.DrawLine(whitePen, pt1, pt2); g.DrawLine(whitePen, pt2, pt3); g.DrawLine(whitePen, pt3, pt4); g.DrawLine(whitePen, pt4, pt5); g.DrawLine(whitePen, pt5, pt6); g.DrawLine(whitePen, pt6, pt1); whitePen.Dispose(); g.Dispose(); }  
    • By RoKabium Games
      ALPHA TESTERS WANTED! Our PC game "Something Ate My Alien" is starting public testing today! If you would like to test our game, contact us or visit the link below: SAMA forums: http://bit.ly/sama-testing
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!