Skip to content

Instantly share code, notes, and snippets.

@JerryNixon
Last active September 8, 2023 16:01
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save JerryNixon/52e2821cdf888a3c492e22397c4b5dda to your computer and use it in GitHub Desktop.
Save JerryNixon/52e2821cdf888a3c492e22397c4b5dda to your computer and use it in GitHub Desktop.
The simple Snake game in C# for the Windows Console
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading;
namespace Snake
{
public enum Direction { Stop, Up, Down, Left, Right }
internal class Program
{
private static Size _size;
private static Point _head;
private static Point _fruit;
private static bool _gameOver;
private static List<Point> _tail;
private static Direction _direction;
private static readonly Random _random = new Random();
private static void Main(string[] args)
{
Start();
}
private static void Start()
{
Setup();
while (_gameOver == false)
{
Draw();
Input();
Logic();
Thread.Sleep(100);
}
End();
}
private static void Setup()
{
_gameOver = false;
_size = new Size(40, 20);
_tail = new List<Point>();
_direction = Direction.Stop;
_head = RandomPoint();
_fruit = RandomPoint();
Console.Clear();
Console.CursorVisible = false;
Console.Title = "Snake Game";
for (var i = 0; i < _size.Height; i++)
{
if (i == 0 || i == _size.Height - 1)
{
$"+{new string('-', _size.Width - 2)}+".Write(0, i);
}
else
{
$"|{new string(' ', _size.Width - 2)}|".Write(0, i);
}
}
}
private static void Draw()
{
for (var i = 1; i < _size.Height - 1; i++)
{
new string('x', _size.Width - 2).Write(1, i, ConsoleColor.Blue, ConsoleColor.Black);
}
_head.Write("0", ConsoleColor.Yellow, ConsoleColor.Black);
_tail.Write("o");
_fruit.Write("*", ConsoleColor.Cyan, ConsoleColor.Black);
$"Score: {_tail.Count()}".Write(_size.Width + 3, 5);
}
private static void Input()
{
if (!Console.KeyAvailable) { return; }
var key = Console.ReadKey(false).Key;
if (key == ConsoleKey.UpArrow) { _direction = Direction.Up; }
else if (key == ConsoleKey.DownArrow) { _direction = Direction.Down; }
else if (key == ConsoleKey.LeftArrow) { _direction = Direction.Left; }
else if (key == ConsoleKey.RightArrow) { _direction = Direction.Right; }
else if (key == ConsoleKey.Escape) { _direction = Direction.Stop; }
}
private static void Logic()
{
if (_direction == Direction.Stop) { return; }
if (_tail.Contains(_head))
{
_gameOver = true;
_direction = Direction.Stop;
return;
}
_tail.Add(_head.Copy());
if (_head == _fruit) { _fruit = RandomPoint(); }
else { _tail.RemoveFirst(); }
if (_direction == Direction.Up)
{
if (_head.Y - 1 < 1) { _head.Y = _size.Height - 2; }
else { _head.Y--; }
}
else if (_direction == Direction.Down)
{
if (_head.Y + 1 > _size.Height - 2) { _head.Y = 1; }
else { _head.Y++; }
}
else if (_direction == Direction.Left)
{
if (_head.X - 1 < 1) { _head.X = _size.Width - 2; }
else { _head.X--; }
}
else if (_direction == Direction.Right)
{
if (_head.X + 1 > _size.Width - 2) { _head.X = 1; }
else { _head.X++; }
}
}
private static void End()
{
$"GAME OVER".Write(_size.Width + 3, 3, ConsoleColor.Red, ConsoleColor.White);
$"Spacebar to play again".Write(_size.Width + 3, 4, ConsoleColor.Black, ConsoleColor.Gray);
if (Console.ReadKey(true).Key == ConsoleKey.Spacebar) { Start(); }
}
public static Point RandomPoint()
{
var x = _random.Next(1, _size.Width - 1);
var y = _random.Next(1, _size.Height - 1);
var point = new Point(x, y);
if (_tail.Contains(point) || _head == point) { return RandomPoint(); }
else { return point; }
}
}
public static class Extensions
{
public static void RemoveFirst<T>(this List<T> list)
{
if (list.Any())
{
list.Remove(list.First());
}
}
public static void RemoveLast<T>(this List<T> list)
{
if (list.Any())
{
list.Remove(list.Last());
}
}
public static Point Copy(this Point point)
{
return new Point(point.X, point.Y);
}
public static void Write(this IEnumerable<Point> points, string text)
{
foreach (var point in points)
{
Write(text, point.X, point.Y);
}
}
public static void Write(this IEnumerable<Point> points, string text, ConsoleColor foreground, ConsoleColor background)
{
foreach (var point in points)
{
Write(text, point.X, point.Y, foreground, background);
}
}
public static void Write(this Point point, string text)
{
Write(text, point.X, point.Y);
}
public static void Write(this Point point, string text, ConsoleColor foreground, ConsoleColor background)
{
Write(text, point.X, point.Y, foreground, background);
}
public static void Write(this string text, int x, int y)
{
Console.SetCursorPosition(x, y);
Console.Write(text);
}
public static void Write(this string text, int x, int y, ConsoleColor foreground, ConsoleColor background)
{
Console.SetCursorPosition(x, y);
Console.ForegroundColor = foreground;
Console.BackgroundColor = background;
Console.Write(text);
Console.ResetColor();
}
}
}
@JerryNixon
Copy link
Author

Looks a little like this.
Snake

@nguyenthanhliemfc
Copy link

Awesome, Thank you!

@M2vH
Copy link

M2vH commented Dec 5, 2019

Hi Jerry,
i really love your backwards tail list solution.
I made some tweaks on your code and posted it here https://gist.github.com/M2vH/594ddc79d5086dbc07e6c3b1c29474fd

The moves are hard-coded in an Array of Points.
To map enum to array (instead of using Dictionary<Direction,Point>) i re-arranged the enum ordering.
I implemented a GetNextPoint() function that will move the snake over the boarders.
The RemoveFirst() function will restore the background, so your Draw() gets faster.
To make use of the recovering inside RemoveFirst(), I reordered the function calls inside Start() to Input(), Logic(), Draw() to reflect a game engine procedure.

Your students are some really ones having such a teacher.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment