Как заставить Змею проиграть при попадании на экран консоли в C# - PullRequest
0 голосов
/ 01 мая 2020

Я хочу, чтобы игрок проиграл, когда он достигнет границы экрана. Прямо сейчас моя змея может go с одной стороны и выйти с другой стороны. Я хотел бы сделать экран окна консоли в качестве стены, чтобы игрок проиграл игру, если ударит в стену.

Я не уверен, что мне следует изменить в моем коде, чтобы достичь желаемого результата. Я приложил фрагмент кода ниже моей игры со змеями.

'using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
using System.Media;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using System.Data;

namespace Snake
{
    /// <summary>
    /// Define a structure for the position for every object in the game by row and column
    /// </summary>
    struct Position
    {
        public int row;
        public int col;
        public Position(int row, int col)
        {
            this.row = row;
            this.col = col;
        }
    }


    class Program
    {
        /// <summary>
        /// function for playing background music
        /// </summary>
        public void BackgroundMusic()
        {
            //Create SoundPlayer objbect to control sound playback
            SoundPlayer backgroundMusic = new SoundPlayer();

            //Locate the SoundPlayer to the correct sound directory
            backgroundMusic.SoundLocation = AppDomain.CurrentDomain.BaseDirectory + "/SnakeBGM_1.wav";

            //Play the background music at the beginning
            backgroundMusic.PlayLooping();
        }

        /// <summary>
        /// function to play sound effect when game over
        /// </summary>
        public void LoseSoundEffect()
        {
            SoundPlayer playerLose = new SoundPlayer();
            playerLose.SoundLocation = AppDomain.CurrentDomain.BaseDirectory + "/SnakeLose_1.wav";
            playerLose.Play(); //Play the die sound effect after player died
        }
        /// <summary>
        /// function to play the sound effect when player win
        /// </summary>
        public void WinSoundEffect()
        {
            SoundPlayer playerWin = new SoundPlayer();
            playerWin.SoundLocation = AppDomain.CurrentDomain.BaseDirectory + "/SnakeWin_1.wav";
            playerWin.Play(); 
        }

        /// <summary>
        /// function to draw food in yellow "@" 
        /// </summary>
        public void DrawFood()
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.Write("@");
        }

        /// <summary>
        /// function to draw obstacle in cyan "="
        /// </summary>
        public void DrawObstacle()
        {
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.Write("=");
        }

        /// <summary>
        /// function to draw snake body in dark grey "*"
        /// </summary>
        public void DrawSnakeBody()
        {
            Console.ForegroundColor = ConsoleColor.DarkGray;
            Console.Write("*");
        }

        /// <summary>
        /// array to store directions, elements in game move depends on user direction key input
        /// </summary>
        /// <param name="directions"></param>
        public void Direction(Position[] directions)
        {

            directions[0] = new Position(0, 1);
            directions[1] = new Position(0, -1);
            directions[2] = new Position(1, 0);
            directions[3] = new Position(-1, 0);

        }

        /// <summary>
        /// function to place first 5 obstacles randomly when game start
        /// </summary>
        /// <param name="obstacles"></param>
       public void InitialRandomObstacles(List<Position>obstacles)
        {
            //Create obstacles objects and initialise certain random position of obstacles at every game play
            //The randomise obstacles will not exist in the first row at the beginning.
            Random randomNumbersGenerator = new Random();
            obstacles.Add(new Position(randomNumbersGenerator.Next(1, Console.WindowHeight), randomNumbersGenerator.Next(0, Console.WindowWidth)));
            obstacles.Add(new Position(randomNumbersGenerator.Next(1, Console.WindowHeight), randomNumbersGenerator.Next(0, Console.WindowWidth)));
            obstacles.Add(new Position(randomNumbersGenerator.Next(1, Console.WindowHeight), randomNumbersGenerator.Next(0, Console.WindowWidth)));
            obstacles.Add(new Position(randomNumbersGenerator.Next(1, Console.WindowHeight), randomNumbersGenerator.Next(0, Console.WindowWidth)));
            obstacles.Add(new Position(randomNumbersGenerator.Next(1, Console.WindowHeight), randomNumbersGenerator.Next(0, Console.WindowWidth)));

            //Show the obstacle in the windows with marking of "="
            foreach (Position obstacle in obstacles)
            {
                Console.SetCursorPosition(obstacle.col, obstacle.row);
                DrawObstacle();
            }
        }

        /// <summary>
        /// function for reading user direction key input
        /// </summary>
        /// <param name="direction"></param>
        /// <param name="right"></param>
        /// <param name="left"></param>
        /// <param name="down"></param>
        /// <param name="up"></param>
        public void CheckUserInput(ref int direction, byte right, byte left, byte down,byte up)
        {

            //User key pressed statement: depends on which direction the user want to go to get food or avoid obstacle
            if (Console.KeyAvailable)
            {
                ConsoleKeyInfo userInput = Console.ReadKey();
                if (userInput.Key == ConsoleKey.LeftArrow)
                {
                    if (direction != right) direction = left;
                }
                if (userInput.Key == ConsoleKey.RightArrow)
                {
                    if (direction != left) direction = right;
                }
                if (userInput.Key == ConsoleKey.UpArrow)
                {
                    if (direction != down) direction = up;
                }
                if (userInput.Key == ConsoleKey.DownArrow)
                {
                    if (direction != up) direction = down;
                }
            }
        }

        /// <summary>
        /// Check for Game over requirements, if snake eats itself, hit on obstacle or did not eat 3 food within 30 seconds, game over
        /// </summary>
        /// <param name="currentTime"></param>
        /// <param name="snakeElements"></param>
        /// <param name="snakeNewHead"></param>
        /// <param name="negativePoints"></param>
        /// <param name="obstacles"></param>
        /// <returns></returns>
        public int GameOverCheck(int currentTime, Queue<Position> snakeElements, Position snakeNewHead,int negativePoints, List<Position> obstacles)
        {
            if (snakeElements.Contains(snakeNewHead) || obstacles.Contains(snakeNewHead) || (Environment.TickCount-currentTime) > 30000)
            {
                LoseSoundEffect(); //this sound effect will be play if game over
                Console.SetCursorPosition(0, 0);
                Console.ForegroundColor = ConsoleColor.Red;//Text color for game over

                int userPoints = (snakeElements.Count - 4) * 100 - negativePoints;//points calculated for player
                userPoints = Math.Max(userPoints, 0); //if (userPoints < 0) userPoints = 0;

                //Display game over text and points
                PrintLinesInCenter("Game Over!", "Your points are:" + userPoints, "Press enter to exit the game!");

                SavePointsToFile(userPoints);//saving points to files

                //close only when enter key is pressed
                while (Console.ReadKey().Key != ConsoleKey.Enter) {}
                return 1;
            }
            return 0;
        }

        /// <summary>
        /// Add winning requirement, snake eat 3 food within 30 seconds to win the game
        /// </summary>
        /// <param name="snakeElements"></param>
        /// <param name="negativePoints"></param>
        /// <returns></returns>
        public int WinningCheck(Queue<Position> snakeElements, int negativePoints)
        {
            // initially snake elements has 4, increment 1 by eating 1 food, so eat 3 food to get 7 snake elements 
            if (snakeElements.Count == 7)
            {
                WinSoundEffect(); //thissound effect plays when game won  
                Console.SetCursorPosition(0, 0);
                Console.ForegroundColor = ConsoleColor.Green;//Text color for game won

                int userPoints = (snakeElements.Count - 4) * 100 - negativePoints;//points calculated for player
                userPoints = Math.Max(userPoints, 0); //if (userPoints < 0) userPoints = 0;

                //display game won text and user points
                PrintLinesInCenter("You Win!", "Your points are:" + userPoints, "Press enter to exit the game!");
                SavePointsToFile(userPoints);//saving points to files

                //close only when enter key is pressed
                while (Console.ReadKey().Key != ConsoleKey.Enter) { }
                return 1;
            }
            return 0;
        }

        /// <summary>
        /// function to generate new food randomly with conditions 
        /// </summary>
        /// <param name="food"></param>
        /// <param name="snakeElements"></param>
        /// <param name="obstacles"></param>
        public void GenerateFood(ref Position food, Queue<Position> snakeElements, List<Position> obstacles)
        {
            Random randomNumbersGenerator = new Random();
            do
            {
                food = new Position(randomNumbersGenerator.Next(0, Console.WindowHeight), //Food generated within console height
                    randomNumbersGenerator.Next(0, Console.WindowWidth)); //Food generate within console width
            }
            //a loop is created - while the program contains food and the obstacle is not hit 
            //put food on different position which is "@"
            while (snakeElements.Contains(food) || obstacles.Contains(food));
            Console.SetCursorPosition(food.col, food.row);
            DrawFood();
        }

        /// <summary>
        /// function to generate new obstacle randomly if conditions
        /// </summary>
        /// <param name="food"></param>
        /// <param name="snakeElements"></param>
        /// <param name="obstacles"></param>
        public void GenerateNewObstacle(ref Position food, Queue<Position> snakeElements, List<Position> obstacles)
        {
            Random randomNumbersGenerator = new Random();

            Position obstacle = new Position();
            do
            {
                obstacle = new Position(randomNumbersGenerator.Next(0, Console.WindowHeight),
                    randomNumbersGenerator.Next(0, Console.WindowWidth));
            }
            //if snake or obstacles are already at certain position, new obstacle will not be drawn there
            //new obstacle will not be drawn at the same row & column of food
            while (snakeElements.Contains(obstacle) || obstacles.Contains(obstacle) || (food.row == obstacle.row && food.col == obstacle.col));
            obstacles.Add(obstacle);
            Console.SetCursorPosition(obstacle.col, obstacle.row);
            DrawObstacle();
        }

        /// <summary>
        /// to get the user points and save the value to text file
        /// </summary>
        /// <param name="userPoints"></param>
        public void SavePointsToFile(int userPoints)
        {
            //declare the file path
            String filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "userPoints.txt");
            try
            {
                //if there is no such text file in the folder, it will be created before saving the points into it
                if (!File.Exists(filePath))
                {
                    File.Create(filePath).Dispose();
                    File.WriteAllText(filePath, userPoints.ToString() + Environment.NewLine);
                }
                else
                {
                    //if there are points exist in the text file, new points will be saved in next line
                    File.AppendAllText(filePath, userPoints.ToString() + Environment.NewLine);
                }
            }
            catch (Exception exception)
            {
                Console.WriteLine("{0} Exception caught.", exception);
            }
        }

        /// <summary>
        /// Read the user points from text file
        /// </summary>
        public string ReadPointsFromFile()
        {
            //declare file path 
            string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "userPoints.txt");
            //read all the contents in text file and store in an array
            string[] scoreBoard = File.ReadAllLines(filePath);
            //find the highest points from the array
            int max = scoreBoard.Select(int.Parse).Max();
            // convert integer to string 
            string highestPoint = max.ToString();
            return highestPoint;
        }

        /// <summary>
        /// function to print text in center of game screen
        /// </summary>
        /// <param name="lines"></param>
        private static void PrintLinesInCenter(params string[] lines)
        {
            int verticalStart = (Console.WindowHeight - lines.Length) / 2; //  printing the lines
            int verticalPosition = verticalStart;
            foreach (var line in lines)
            {
                // start printing the line text horizontally
                int horizontalStart = (Console.WindowWidth - line.Length) / 2;
                // start position for this line of text
                Console.SetCursorPosition(horizontalStart, verticalPosition);
                // write the text
                Console.Write(line);
                // move to the next line
                ++verticalPosition;
            }
        }

        /// <summary>
        /// To display welcome message and highest score at start of game
        /// </summary>
        public void DisplayStartScreen()
        {
            Console.ForegroundColor = ConsoleColor.Cyan; //text color for text display
            //display welcome message and highest score 
            PrintLinesInCenter("WELCOME TO SNAKE GAME", "\n", "Highest Score", ReadPointsFromFile()); 
            //start screen stay for 3 seconds
            Thread.Sleep(3000);
            //start screen clear before game start
            Console.Clear();
        }


        /// <summary>
        /// Main starts here
        /// </summary>
        //Define direction by using index number
        //Set the time taken for the food to be dissappear
        //Initialise negative points
        static void Main(string[] args)
        {

            byte right = 0;
            byte left = 1;
            byte down = 2;
            byte up = 3;
            int currentTime = Environment.TickCount;
            int lastFoodTime = 0;
            int foodDissapearTime = 10000; //food dissappears after 10 second 
            int negativePoints = 0;
            bool alive = true;

            Console.SetWindowSize(56,38);//reducing screen size 
            Console.SetBufferSize(Console.WindowWidth, Console.WindowHeight);
            Position[] directions = new Position[4];

            Program p = new Program();
            //display start screen before background music and game start 
            p.DisplayStartScreen();
            //Play background music
            p.BackgroundMusic();

            // Define direction with characteristic of index of array
            p.Direction(directions);

            // Initialised the obstacles location at the starting of the game
            List<Position> obstacles = new List<Position>();
            p.InitialRandomObstacles(obstacles);

            //Do the initialization for sleepTime (Game's Speed), Snake's direction and food timing
            //Limit the number of rows of text accessible in the console window
            double sleepTime = 100;
            int direction = right;
            Random randomNumbersGenerator = new Random();
            Console.BufferHeight = Console.WindowHeight;
            lastFoodTime = Environment.TickCount;

            //Initialise the snake position in top left corner of the windows
            //Havent draw the snake elements in the windows yet. Will be drawn in the code below
            Queue<Position> snakeElements = new Queue<Position>();
            for (int i = 0; i <= 3; i++) // Length of the snake was reduced to 3 units of *
            {
                snakeElements.Enqueue(new Position(0, i));
            }

            //To position food randomly when the program runs first time
            Position food = new Position();
            p.GenerateFood(ref food,snakeElements,obstacles);

            //while the game is running position snake on terminal with shape "*"
            foreach (Position position in snakeElements)
            {
                Console.SetCursorPosition(position.col, position.row);
                p.DrawSnakeBody();
            }

            while (true)
            {
                //negative points is initialized as 0 at the beginning of the game. As the player reaches out for food
                //negative points increment depending how far the food is
                negativePoints++;

                //Check the user input direction
                p.CheckUserInput(ref direction, right, left, down, up);

                //When the game starts the snake head is towards the end of his body with face direct to start from right.
                Position snakeHead = snakeElements.Last();
                Position nextDirection = directions[direction];

                //Snake position to go within the terminal window assigned.
                Position snakeNewHead = new Position(snakeHead.row + nextDirection.row,
                    snakeHead.col + nextDirection.col);

                if (snakeNewHead.col < 0) snakeNewHead.col = Console.WindowWidth - 1;
                if (snakeNewHead.row < 0) snakeNewHead.row = Console.WindowHeight - 1;
                if (snakeNewHead.row >= Console.WindowHeight) snakeNewHead.row = 0;
                if (snakeNewHead.col >= Console.WindowWidth) snakeNewHead.col = 0;

                //Check for GameOver Criteria
                int gameOver=p.GameOverCheck(currentTime, snakeElements, snakeNewHead, negativePoints,obstacles);
                if (gameOver == 1)
                    return;

                //Check for Winning Criteria
                int winning = p.WinningCheck(snakeElements, negativePoints);
                if (winning == 1) return;

                //The way snake head will change as the player changes his direction
                Console.SetCursorPosition(snakeHead.col, snakeHead.row);
                p.DrawSnakeBody();

                //Snake head shape when the user presses the key to change his direction
                snakeElements.Enqueue(snakeNewHead);
                Console.SetCursorPosition(snakeNewHead.col, snakeNewHead.row);
                Console.ForegroundColor = ConsoleColor.Gray;
                if (direction == right) Console.Write(">"); //Snake head when going right
                if (direction == left) Console.Write("<");//Snake head when going left
                if (direction == up) Console.Write("^");//Snake head when going up
                if (direction == down) Console.Write("v");//Snake head when going down


                // food will be positioned randomly until they are not at the same row & column as snake head
                if (snakeNewHead.col == food.col && snakeNewHead.row == food.row)
                {
                    Console.Beep();// Make a sound effect when food was eaten.
                    p.GenerateFood(ref food, snakeElements, obstacles);


                    //when the snake eat the food, the system tickcount will be set as lastFoodTime
                    //new food will be drawn, snake speed will increases
                    lastFoodTime = Environment.TickCount;
                    sleepTime--;

                    //Generate new obstacle
                    p.GenerateNewObstacle(ref food,snakeElements,obstacles);

                }
                else
                {
                    // snake is moving
                    Position last = snakeElements.Dequeue();
                    Console.SetCursorPosition(last.col, last.row);
                    Console.Write(" ");
                }

                //if snake did not eat the food before it disappears, 50 will be added to negative points
                //draw new food after the previous one disappeared
                if (Environment.TickCount - lastFoodTime >= foodDissapearTime)
                {
                    negativePoints = negativePoints + 50;
                    Console.SetCursorPosition(food.col, food.row);
                    Console.Write(" ");

                    //Generate the new food and record the system tick count
                    p.GenerateFood(ref food, snakeElements, obstacles);
                    lastFoodTime = Environment.TickCount;
                }

                //snake moving speed increased 
                sleepTime -= 0.01;

                //pause the execution thread of snake moving speed
                Thread.Sleep((int)sleepTime);
            }
        }
    }
}'

1 Ответ

0 голосов
/ 01 мая 2020

Я провел быструю проверку, и я думаю, что вам нужно только переместить свои чеки с Main на GameOverCheck.

. Это то, о чем я говорю:

....
Position snakeNewHead = new Position(snakeHead.row + nextDirection.row, snakeHead.col + nextDirection.col);

// BEGIN REMOVE
if (snakeNewHead.col < 0) snakeNewHead.col = Console.WindowWidth - 1;
if (snakeNewHead.row < 0) snakeNewHead.row = Console.WindowHeight - 1;
if (snakeNewHead.row >= Console.WindowHeight) snakeNewHead.row = 0;
if (snakeNewHead.col >= Console.WindowWidth) snakeNewHead.col = 0;
// END REMOVE

//Check for GameOver Criteria
int gameOver = p.GameOverCheck(currentTime, snakeElements, snakeNewHead, negativePoints, obstacles);
....

Затем, в GameOverCheck, добавьте эти новые условия к вашему if:

if (snakeElements.Contains(snakeNewHead) || obstacles.Contains(snakeNewHead) || (Environment.TickCount - currentTime) > 30000
// ADDED CONDITIONS
|| snakeNewHead.row >= Console.WindowHeight || snakeNewHead.col >= Console.WindowWidth || snakeNewHead.row < 0 || snakeNewHead.col < 0)

Из моих быстрых тестов игра заканчивается каждый раз, когда голова касается границ консоли.

...